Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
romul committed Oct 30, 2009
0 parents commit 7a6d852
Show file tree
Hide file tree
Showing 19 changed files with 526 additions and 0 deletions.
3 changes: 3 additions & 0 deletions README.markdown
@@ -0,0 +1,3 @@
= Product Assembly

Description goes here
41 changes: 41 additions & 0 deletions Rakefile
@@ -0,0 +1,41 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

desc 'Default: run unit tests.'
task :default => :test

desc 'Test the product_assembly extension.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end

namespace :test do
desc 'Functional test the product_assembly extension.'
Rake::TestTask.new(:functionals) do |t|
t.libs << 'lib'
t.pattern = 'test/functional/*_test.rb'
t.verbose = true
end

desc 'Unit test the product_assembly extension.'
Rake::TestTask.new(:units) do |t|
t.libs << 'lib'
t.pattern = 'test/unit/*_test.rb'
t.verbose = true
end
end

desc 'Generate documentation for the product_assembly extension.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'ProductAssemblyExtension'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README.markdown')
rdoc.rdoc_files.include('lib/**/*.rb')
end

# Load any custom rakefiles for extension
Dir[File.dirname(__FILE__) + '/lib/tasks/*.rake'].sort.each { |f| require f }
47 changes: 47 additions & 0 deletions app/controllers/admin/parts_controller.rb
@@ -0,0 +1,47 @@
class Admin::PartsController < Admin::BaseController
helper :products
before_filter :find_product

def index
@parts = @product.parts
end

def remove
@part = Variant.find(params[:id])
@product.remove_part(@part)
render :update do |page|
page.replace_html :product_parts, :partial => "parts_table",
:locals => {:parts => @product.parts}
end
end

def available
if params[:q].blank?
@available_products = []
else
@available_products = Product.find(:all,
:conditions => ['lower(name) LIKE ? AND can_be_part = ?', "%#{params[:q].downcase}%", true])
end
respond_to do |format|
format.html
format.js {render :layout => false}
end
end

def create
@part = Variant.find(params[:part_id])
qty = params[:part_count].to_i
@product.add_part(@part, qty) if qty > 0
render :update do |page|
page.replace_html :product_parts, :partial => "parts_table",
:locals => {:parts => @product.parts}
page.hide :search_hits
end
end

private
def find_product
@product = Product.find_by_permalink(params[:product_id])
@product_admin_tabs << { :name => "Parts", :url => "admin_product_parts_url" }
end
end
2 changes: 2 additions & 0 deletions app/helpers/admin/parts_helper.rb
@@ -0,0 +1,2 @@
module Admin::PartsHelper
end
5 changes: 5 additions & 0 deletions app/models/assemblies_part.rb
@@ -0,0 +1,5 @@
class AssembliesPart < ActiveRecord::Base
set_primary_keys :assembly_id, :part_id
belongs_to :assembly, :class_name => "Product", :foreign_key => "assembly_id"
belongs_to :part, :class_name => "Variant", :foreign_key => "part_id"
end
32 changes: 32 additions & 0 deletions app/views/admin/parts/_parts_table.html.erb
@@ -0,0 +1,32 @@
<table class="index">
<thead>
<tr>
<th><%= t("sku") %></th>
<th><%= t("name") %></th>
<th><%= t("options") %></th>
<th><%= t("qty") %></th>
<th></th>
</tr>
</thead>
<tbody>
<% parts.each do |part| %>
<tr id="<%= dom_id(part, :sel)%>">
<td><%= part.sku %></td>
<td><%= part.product.name %></td>
<td><%= variant_options part %></td>
<td><%= @product.count_of part %></td>
<td class="actions">
<%= image_tag "spinner.gif", :style => "display:none", :id => "#{dom_id(part, :rem_spinner)}" %>
<%= link_to_remote icon('delete') + ' ' + t('remove'),
{:url => remove_admin_product_part_url(@product, part),
:loading => "jQuery('##{dom_id(part, :rem_spinner)}').show()",
:complete => "jQuery('##{dom_id(part, :rem_spinner)}').hide();"},
:class => 'iconlink' %>
</td>
</tr>
<% end %>
<% if parts.empty? %>
<tr><td colspan="3"><%= t('none') %>.</td></tr>
<% end %>
</tbody>
</table>
51 changes: 51 additions & 0 deletions app/views/admin/parts/available.js.erb
@@ -0,0 +1,51 @@
<script type="text/javascript">
function displayRow(){
var row = document.getElementById("captionRow");
if (row.style.display == '') row.style.display = 'none';
else row.style.display = '';
}
</script>
<h4><%= t('available_parts') %></h4>
<% # admin_product_parts_path(@product)
%>
<table class="index">
<thead>
<tr>
<th><%= t("name") %></th>
<th><%= t("options") %></th>
<th><%= t("qty") %></th>
<th></th>
</tr>
</thead>
<tbody>
<% @available_products.each do |product| %>
<tr id="<%= dom_id(product) %>">
<td><%= product.name %></td>
<td>
<% if product.has_variants? %>
<%= select_tag "part[id]",
options_for_select(product.variants.map { |v| [variant_options(v), v.id] }) %>
<% else %>
<%= hidden_field_tag "part[id]", product.master.id %>
<%= t :no_variants %>
<% end %>
</td>
<td><%= text_field_tag "part[count]", 1 %></td>
<td class="actions">
<%= image_tag "spinner.gif", :style => "display:none", :id => "#{dom_id(product, :spinner)}" %>
<%= link_to_remote icon('add') + ' ' + t('select'),
{:url => admin_product_parts_path(@product),
:loading => "jQuery('##{dom_id(product, :spinner)}').show()",
:complete => "jQuery('##{dom_id(product, :spinner)}').hide();",
:with => "'part_count=' + $('#part_count', $(this).parent().parent()).val() + '&part_id=' + ($('#part_id option:selected', $(this).parent().parent()).val() || $('#part_id', $(this).parent().parent()).val()) "
},
:class => 'iconlink' %>
</td>
</tr>
<% end %>
<% if @available_products.empty? %>
<tr><td colspan="3"><%= t('no_match_found') %>.</td></tr>
<% end %>
</tbody>
</table>
42 changes: 42 additions & 0 deletions app/views/admin/parts/index.html.erb
@@ -0,0 +1,42 @@
<%= render :partial => 'admin/shared/product_sub_menu' %>
<%= render :partial => 'admin/shared/product_tabs', :locals => {:current => "Parts"} %>
<div id="product_parts">
<%= render :partial => "parts_table", :locals => {:parts => @parts} %>
</div>


<% form_tag('#') do %>
<label><%= t('search') %>:</label>
<input id="searchtext" size="25">
<% end %>
<% javascript_tag do %>
function search_for_parts(){
jQuery.ajax({
data: {q: jQuery("#searchtext").val() },
success: function(request){
jQuery('#search_hits').html(request);
},
type: 'post',
url: '<%= available_admin_product_parts_url(@product) %>'
});
}

jQuery("#searchtext").keypress(function (e) {
if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
search_for_parts();

return false;
} else {
return true;
}
});

jQuery("#searchtext").delayedObserver(0.75, function(element, value) {
search_for_parts();
})
<% end %>
<br/>
<div id="search_hits"></div>

95 changes: 95 additions & 0 deletions app/views/admin/products/_form.html.erb
@@ -0,0 +1,95 @@
<div class="yui-gc">
<div class="yui-u first">

<% f.field_container :name do %>
<%= f.label :name, t("name") %> <span class="required">*</span><br />
<%= f.text_field :name, :class => 'fullwidth title' %>
<%= f.error_message_on :name %>
<% end %>
<% f.field_container :description do %>
<%= f.label :description, t("description")%><br />
<%= f.text_area :description, {:cols => 60, :rows => 4, :class => 'fullwidth'} %>
<%= f.error_message_on :description %>
<% end %>

</div>
<div class="yui-u">

<% f.field_container :price do %>
<%= f.label :price, t("master_price")%> <span class="required">*</span><br />
<%= f.text_field :price %>
<%= f.error_message_on :price %>
<% end %>

<p>
<%= f.label :available_on, t("available_on") %><br />
<%= f.error_message_on :available_on %>
<%= f.unobtrusive_date_picker :available_on %>
</p>

<p>
<%= f.label :sku, t("sku") %><br />
<%= f.text_field :sku, :size => 16 %>
</p>
<% unless @product.has_variants? %>
<p>
<%= f.label :on_hand, t("on_hand")%><br />
<%= f.text_field :on_hand, :size => 4 %>
</p>
<ul id="shipping-specs">
<li>
<%= f.label :weight, t("weight")%>
<%= f.text_field :weight, :size => 4 %>
</li>
<li>
<%= f.label :height, t("height")%>
<%= f.text_field :height, :size => 4 %>
</li>
<li>
<%= f.label :width, t("width")%>
<%= f.text_field :width, :size => 4 %>
</li>
<li>
<%= f.label :depth, t("depth")%>
<%= f.text_field :depth, :size => 4 %>
</li>
</ul>
<% end %>
<p>
<%= f.label :shipping_category_id, t("shipping_categories")%><br />
<%= f.collection_select(:shipping_category_id, @shipping_categories, :id, :name, {:include_blank => true}, {"style" => "width:200px"}) %>
<%= f.error_message_on :shipping_category %>
</p>

<p>
<%= f.label :tax_category_id, t("tax_category")%><br />
<%= f.collection_select(:tax_category_id, @tax_categories, :id, :name, {:include_blank => true}, {"style" => "width:200px"}) %>
<%= f.error_message_on :tax_category%>
</p>
<p>
<%= f.label :can_be_part, t("can_be_part")%><br />
<%= f.check_box(:can_be_part) %>
</p>
<p>
<%= f.label :individual_sale, t("individual_sale")%><br />
<%= f.check_box(:individual_sale) %>
</p>
</div>
</div>

<h2><%= t("metadata") %></h2>
<p>
<%= f.label :meta_keywords, t("meta_keywords")%><br />
<%= f.text_field :meta_keywords, :class => 'fullwidth' %>
</p>
<p>
<%= f.label :meta_description, t("meta_description")%><br />
<%= f.text_field :meta_description, :class => 'fullwidth' %>
</p>

<%#= render 'properties_form', :f => f %>
<% Variant.additional_fields.select{|af| af[:only].nil? || af[:only].include?(:product) }.each do |field| %>
<%= render :partial => "admin/shared/additional_field", :locals => {:field => field, :f => f} %>
<% end %>
10 changes: 10 additions & 0 deletions config/routes.rb
@@ -0,0 +1,10 @@
# Put your extension routes here.

map.namespace :admin do |admin|
admin.resources :products, :member => {:clone => :get}, :has_many => [:product_properties, :images] do |product|
product.resources :variants
product.resources :option_types, :member => { :select => :get, :remove => :get}, :collection => {:available => :get, :selected => :get}
product.resources :taxons, :member => {:select => :post, :remove => :post}, :collection => {:available => :post, :selected => :get}
product.resources :parts, :member => {:select => :post, :remove => :post}, :collection => {:available => :post, :selected => :get}
end
end
13 changes: 13 additions & 0 deletions db/migrate/20091028152124_add_many_to_many_relation_to_products.rb
@@ -0,0 +1,13 @@
class AddManyToManyRelationToProducts < ActiveRecord::Migration
def self.up
create_table :assemblies_parts, :id => false do |t|
t.integer "assembly_id", :null => false
t.integer "part_id", :null => false
t.integer "count", :null => false, :default => 1
end
end

def self.down
drop_table :assemblies_parts
end
end
15 changes: 15 additions & 0 deletions db/migrate/20091029165620_add_parts_fields_to_products.rb
@@ -0,0 +1,15 @@
class AddPartsFieldsToProducts < ActiveRecord::Migration
def self.up
change_table(:products) do |t|
t.column :can_be_part, :boolean, :default => false, :null => false
t.column :individual_sale, :boolean, :default => true, :null => false
end
end

def self.down
change_table(:products) do |t|
t.remove :can_be_part
t.remove :individual_sale
end
end
end
2 changes: 2 additions & 0 deletions db/seeds.rb
@@ -0,0 +1,2 @@
# Use this file to load your own seed data from extensions.
# See the db/seeds.rb file in the Spree core for some ideas on what you can do here.

0 comments on commit 7a6d852

Please sign in to comment.