solidus_product_bundle 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +3 -0
- data/.travis.yml +11 -0
- data/Gemfile +9 -0
- data/LICENSE.md +13 -0
- data/README.markdown +95 -0
- data/README.md +2 -0
- data/Rakefile +15 -0
- data/app/assets/images/spinner.gif +0 -0
- data/app/assets/javascripts/spree/backend/spree_product_assembly/index.js.coffee +82 -0
- data/app/assets/javascripts/spree/backend/spree_product_assembly/translations.js.erb +3 -0
- data/app/assets/javascripts/spree/frontend/spree_product_assembly.js +1 -0
- data/app/assets/stylesheets/spree/backend/spree_product_assembly.css +3 -0
- data/app/assets/stylesheets/spree/frontend/spree_product_assembly.css +3 -0
- data/app/controllers/spree/admin/parts_controller.rb +63 -0
- data/app/controllers/spree/checkout_controller_decorator.rb +9 -0
- data/app/helpers/spree/admin/orders_helper_decorator.rb +9 -0
- data/app/models/spree/assemblies_part.rb +33 -0
- data/app/models/spree/assign_part_to_bundle_form.rb +64 -0
- data/app/models/spree/inventory_unit_decorator.rb +13 -0
- data/app/models/spree/line_item_decorator.rb +65 -0
- data/app/models/spree/order_contents_decorator.rb +35 -0
- data/app/models/spree/order_inventory_assembly.rb +54 -0
- data/app/models/spree/part_line_item.rb +6 -0
- data/app/models/spree/product_decorator.rb +37 -0
- data/app/models/spree/shipment_decorator.rb +50 -0
- data/app/models/spree/stock/availability_validator.rb +27 -0
- data/app/models/spree/stock/inventory_unit_builder_decorator.rb +30 -0
- data/app/models/spree/variant_decorator.rb +13 -0
- data/app/overrides/add_admin_product_form_fields.rb +5 -0
- data/app/overrides/add_admin_tabs.rb +5 -0
- data/app/overrides/add_line_item_description.rb +5 -0
- data/app/overrides/spree/admin/orders/_form/inject_product_assemblies.html.erb.deface +3 -0
- data/app/overrides/spree/admin/orders/_shipment/stock_contents.html.erb.deface +2 -0
- data/app/overrides/spree/checkout/_delivery/remove_unshippable_markup.html.erb.deface +1 -0
- data/app/overrides/spree/checkout/_delivery/render_line_item_manifest.html.erb.deface +3 -0
- data/app/overrides/spree/products/add_links_to_parts.rb +6 -0
- data/app/overrides/spree/products/show/remove_add_to_cart_button_for_non_individual_sale_products.html.erb.deface +4 -0
- data/app/overrides/spree/shared/_order_details/part_description.html.erb.deface +16 -0
- data/app/serializers/spree/wombat/assembly_shipment_serializer.rb +37 -0
- data/app/views/spree/admin/orders/_assemblies.html.erb +62 -0
- data/app/views/spree/admin/orders/_stock_contents.html.erb +69 -0
- data/app/views/spree/admin/orders/_stock_item.html.erb +46 -0
- data/app/views/spree/admin/parts/_parts_table.html.erb +33 -0
- data/app/views/spree/admin/parts/available.html.erb +43 -0
- data/app/views/spree/admin/parts/index.html.erb +19 -0
- data/app/views/spree/admin/parts/update_parts_table.js.erb +2 -0
- data/app/views/spree/admin/products/_product_assembly_fields.html.erb +23 -0
- data/app/views/spree/admin/shared/_product_assembly_product_tabs.html.erb +3 -0
- data/app/views/spree/checkout/_line_item_manifest.html.erb +17 -0
- data/app/views/spree/orders/_cart_description.html.erb +16 -0
- data/app/views/spree/products/show/_parts.html.erb +38 -0
- data/bin/rails +7 -0
- data/config/locales/en.yml +15 -0
- data/config/locales/fr.yml +12 -0
- data/config/locales/ru.yml +12 -0
- data/config/locales/sv.yml +12 -0
- data/config/routes.rb +19 -0
- data/db/migrate/20091028152124_add_many_to_many_relation_to_products.rb +13 -0
- data/db/migrate/20091029165620_add_parts_fields_to_products.rb +27 -0
- data/db/migrate/20120316141830_namespace_product_assembly_for_spree_one.rb +9 -0
- data/db/migrate/20140620223938_add_id_to_spree_assemblies_parts.rb +9 -0
- data/db/migrate/20150219192418_add_variant_selection_deferred_to_assemblies_parts.rb +5 -0
- data/db/migrate/20150303105615_create_part_line_items.rb +9 -0
- data/lib/generators/spree_product_assembly/install/install_generator.rb +24 -0
- data/lib/spree_product_assembly/engine.rb +21 -0
- data/lib/spree_product_assembly.rb +4 -0
- data/lib/tasks/spree2_upgrade.rake +29 -0
- data/solidus_product_bundle.gemspec +33 -0
- data/spec/features/adding_items_to_cart_spec.rb +203 -0
- data/spec/features/admin/orders_spec.rb +29 -0
- data/spec/features/admin/parts_spec.rb +183 -0
- data/spec/features/checkout_spec.rb +249 -0
- data/spec/features/updating_items_in_cart_spec.rb +199 -0
- data/spec/models/spree/assemblies_part_spec.rb +18 -0
- data/spec/models/spree/assign_part_to_bundle_form_spec.rb +51 -0
- data/spec/models/spree/inventory_unit_spec.rb +32 -0
- data/spec/models/spree/line_item_spec.rb +88 -0
- data/spec/models/spree/order_contents_spec.rb +82 -0
- data/spec/models/spree/order_inventory_assembly_spec.rb +321 -0
- data/spec/models/spree/order_inventory_spec.rb +34 -0
- data/spec/models/spree/product_spec.rb +40 -0
- data/spec/models/spree/shipment_spec.rb +113 -0
- data/spec/models/spree/stock/availability_validator_spec.rb +71 -0
- data/spec/models/spree/stock/coordinator_spec.rb +46 -0
- data/spec/models/spree/stock/inventory_unit_builder_spec.rb +36 -0
- data/spec/models/spree/variant_spec.rb +28 -0
- data/spec/serializers/spree/wombat/assembly_shipment_serializer_spec.rb +36 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/support/factories/assemblies_part_factory.rb +10 -0
- data/spec/support/factories/part_line_item_factory.rb +9 -0
- data/spec/support/factories/variant_factory.rb +15 -0
- data/spec/support/shared_contexts/order_with_bundle.rb +13 -0
- metadata +374 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
Shipment.class_eval do
|
|
3
|
+
# Overriden from Spree core as a product bundle part should not be put
|
|
4
|
+
# together with an individual product purchased (even though they're the
|
|
5
|
+
# very same variant) That is so we can tell the store admin which units
|
|
6
|
+
# were purchased individually and which ones as parts of the bundle
|
|
7
|
+
#
|
|
8
|
+
# Account for situations where we can't track the line_item for a variant.
|
|
9
|
+
# This should avoid exceptions when users upgrade from spree 1.3
|
|
10
|
+
#
|
|
11
|
+
# TODO Can possibly be removed as well. We already override the manifest
|
|
12
|
+
# partial so we can get the product there
|
|
13
|
+
def manifest
|
|
14
|
+
items = []
|
|
15
|
+
inventory_units.joins(:variant).includes(:variant, :line_item).group_by(&:variant).each do |variant, units|
|
|
16
|
+
|
|
17
|
+
units.group_by(&:line_item).each do |line_item, units|
|
|
18
|
+
states = {}
|
|
19
|
+
units.group_by(&:state).each { |state, iu| states[state] = iu.count }
|
|
20
|
+
line_item ||= order.find_line_item_by_variant(variant)
|
|
21
|
+
|
|
22
|
+
part = line_item ? line_item.product.assembly? : false
|
|
23
|
+
items << OpenStruct.new(part: part,
|
|
24
|
+
product: line_item.try(:product),
|
|
25
|
+
line_item: line_item,
|
|
26
|
+
variant: variant,
|
|
27
|
+
quantity: units.length,
|
|
28
|
+
states: states)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
items
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# There might be scenarios where we don't want to display every single
|
|
35
|
+
# variant on the shipment. e.g. when ordering a product bundle that includes
|
|
36
|
+
# 5 other parts. Frontend users should only see the product bundle as a
|
|
37
|
+
# single item to ship
|
|
38
|
+
def line_item_manifest
|
|
39
|
+
inventory_units.includes(:line_item, :variant).group_by(&:line_item).map do |line_item, units|
|
|
40
|
+
states = {}
|
|
41
|
+
units.group_by(&:state).each { |state, iu| states[state] = iu.count }
|
|
42
|
+
OpenStruct.new(line_item: line_item, variant: line_item.variant, quantity: units.length, states: states)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def inventory_units_for_item(line_item, variant)
|
|
47
|
+
inventory_units.where(line_item_id: line_item.id, variant_id: variant.id)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Stock
|
|
3
|
+
# Overridden from spree core to make it also check for assembly parts stock
|
|
4
|
+
class AvailabilityValidator < ActiveModel::Validator
|
|
5
|
+
def validate(line_item)
|
|
6
|
+
line_item.quantity_by_variant.each do |variant, variant_quantity|
|
|
7
|
+
inventory_units = line_item.inventory_units.where(variant: variant).count
|
|
8
|
+
quantity = variant_quantity - inventory_units
|
|
9
|
+
|
|
10
|
+
next if quantity <= 0
|
|
11
|
+
|
|
12
|
+
quantifier = Stock::Quantifier.new(variant)
|
|
13
|
+
|
|
14
|
+
unless quantifier.can_supply? quantity
|
|
15
|
+
display_name = %Q{#{variant.name}}
|
|
16
|
+
display_name += %Q{ (#{variant.options_text})} unless variant.options_text.blank?
|
|
17
|
+
|
|
18
|
+
line_item.errors[:quantity] << Spree.t(
|
|
19
|
+
:selected_quantity_not_available,
|
|
20
|
+
item: display_name.inspect
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Stock
|
|
3
|
+
InventoryUnitBuilder.class_eval do
|
|
4
|
+
def units
|
|
5
|
+
@order.line_items.flat_map do |line_item|
|
|
6
|
+
line_item.quantity_by_variant.flat_map do |variant, quantity|
|
|
7
|
+
quantity.times.map { build_inventory_unit(variant, line_item) }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def build_inventory_unit(variant, line_item)
|
|
13
|
+
@order.inventory_units.includes(
|
|
14
|
+
variant: {
|
|
15
|
+
product: {
|
|
16
|
+
shipping_category: {
|
|
17
|
+
shipping_methods: [:calculator, { zones: :zone_members }]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
).build(
|
|
22
|
+
pending: true,
|
|
23
|
+
variant: variant,
|
|
24
|
+
line_item: line_item,
|
|
25
|
+
order: @order
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Spree::Variant.class_eval do
|
|
2
|
+
has_and_belongs_to_many :assemblies, :class_name => "Spree::Product",
|
|
3
|
+
:join_table => "spree_assemblies_parts",
|
|
4
|
+
:foreign_key => "part_id", :association_foreign_key => "assembly_id"
|
|
5
|
+
|
|
6
|
+
def assemblies_for(products)
|
|
7
|
+
assemblies.where(id: products)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def part?
|
|
11
|
+
assemblies.exists?
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Deface::Override.new(:virtual_path => "spree/admin/products/_form",
|
|
2
|
+
:name => "product_assembly_admin_product_form_right",
|
|
3
|
+
:insert_after => "[data-hook='admin_product_form_right'], #admin_product_form_right[data-hook]",
|
|
4
|
+
:partial => "spree/admin/products/product_assembly_fields",
|
|
5
|
+
:disabled => false)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!-- remove '.unshippable' -->
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!-- insert_bottom 'td[data-hook=order_item_description]' -->
|
|
2
|
+
<% if item.product.assembly? %>
|
|
3
|
+
<ul class='assembly_parts'>
|
|
4
|
+
<% item.part_line_items.each do |v| %>
|
|
5
|
+
<% pli_variant = v.variant %>
|
|
6
|
+
<li>
|
|
7
|
+
(<%= item.quantity * v.quantity %>)
|
|
8
|
+
<%= link_to pli_variant.name, product_path(pli_variant.product) %>
|
|
9
|
+
<% unless pli_variant.is_master? %>
|
|
10
|
+
(<%= pli_variant.options_text %>)
|
|
11
|
+
<% end %>
|
|
12
|
+
(<%= pli_variant.sku %>)
|
|
13
|
+
</li>
|
|
14
|
+
<% end %>
|
|
15
|
+
</ul>
|
|
16
|
+
<% end %>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Wombat
|
|
3
|
+
class AssemblyShipmentSerializer < ShipmentSerializer
|
|
4
|
+
|
|
5
|
+
attributes :items
|
|
6
|
+
|
|
7
|
+
def items
|
|
8
|
+
i = []
|
|
9
|
+
object.line_items.each do |li|
|
|
10
|
+
next if li.nil?
|
|
11
|
+
|
|
12
|
+
hsh = {
|
|
13
|
+
product_id: li.variant.sku,
|
|
14
|
+
name: li.name,
|
|
15
|
+
quantity: li.quantity,
|
|
16
|
+
price: li.price.to_f
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if li.parts.present?
|
|
20
|
+
bundled_items = []
|
|
21
|
+
li.parts.each do |part|
|
|
22
|
+
bundled_items << {
|
|
23
|
+
product_id: part.sku,
|
|
24
|
+
name: part.name,
|
|
25
|
+
quantity: li.count_of(part),
|
|
26
|
+
price: part.price.to_f
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
hsh[:bundled_items] = bundled_items
|
|
30
|
+
end
|
|
31
|
+
i << hsh
|
|
32
|
+
end
|
|
33
|
+
i
|
|
34
|
+
end
|
|
35
|
+
end if defined?(ShipmentSerializer)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<% if order.line_items.assemblies.any? %>
|
|
2
|
+
<fieldset class="no-border-bottom">
|
|
3
|
+
<legend align="center" class="" data-hook="">
|
|
4
|
+
<%= Spree.t(:product_bundles) %>
|
|
5
|
+
</legend>
|
|
6
|
+
</fieldset>
|
|
7
|
+
|
|
8
|
+
<table class="product-bundles" data-hook="">
|
|
9
|
+
<colgroup>
|
|
10
|
+
<col style="width: 10%;" />
|
|
11
|
+
<col style="width: 30%;" />
|
|
12
|
+
<col style="width: 15%;" />
|
|
13
|
+
<col style="width: 15%;" />
|
|
14
|
+
<col style="width: 15%;" />
|
|
15
|
+
<col style="width: 15%;" />
|
|
16
|
+
</colgroup>
|
|
17
|
+
|
|
18
|
+
<thead>
|
|
19
|
+
<th colspan="2"><%= Spree.t(:item_description) %></th>
|
|
20
|
+
<th><%= Spree.t(:price) %></th>
|
|
21
|
+
<th><%= Spree.t(:quantity) %></th>
|
|
22
|
+
<th><%= Spree.t(:total) %></th>
|
|
23
|
+
<th class="orders-actions actions" data-hook="admin_order_form_line_items_header_actions"></th>
|
|
24
|
+
</thead>
|
|
25
|
+
|
|
26
|
+
<% order.line_items.assemblies.each do |item| %>
|
|
27
|
+
<tbody>
|
|
28
|
+
<tr class="" data-item-quantity="<%= item.quantity %>">
|
|
29
|
+
<td class="item-image"><%= mini_image(item.variant) %></td>
|
|
30
|
+
<td class="item-name">
|
|
31
|
+
<%= item.product.name %> - <%= item.variant.sku %> <br />
|
|
32
|
+
<%= "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? %>
|
|
33
|
+
</td>
|
|
34
|
+
<td class="item-price align-center">
|
|
35
|
+
<%= item.single_money.to_html %>
|
|
36
|
+
</td>
|
|
37
|
+
<td class="line-item-qty-show align-center">
|
|
38
|
+
<%= item.quantity %>
|
|
39
|
+
</td>
|
|
40
|
+
|
|
41
|
+
<td class="line-item-qty-edit hidden">
|
|
42
|
+
<%= number_field_tag :quantity, item.quantity, :min => 0, class: "line_item_quantity", :size => 5 %>
|
|
43
|
+
</td>
|
|
44
|
+
|
|
45
|
+
<td class="item-total align-center">
|
|
46
|
+
<%= item.display_amount.to_html %>
|
|
47
|
+
</td>
|
|
48
|
+
<td class="cart-item-delete actions" data-hook="cart_item_delete">
|
|
49
|
+
<% unless item.any_units_shipped? %>
|
|
50
|
+
<% if can? :update, item %>
|
|
51
|
+
<%= link_to '', '#', :class => 'save-line-item fa fa-ok no-text with-tip', :data => { 'line-item-id' => item.id, :action => 'save'}, :title => Spree.t('actions.save'), :style => 'display: none' %>
|
|
52
|
+
<%= link_to '', '#', :class => 'cancel-line-item fa fa-cancel no-text with-tip', :data => {:action => 'cancel'}, :title => Spree.t('actions.cancel'), :style => 'display: none' %>
|
|
53
|
+
<%= link_to '', '#', :class => 'edit-line-item fa fa-edit no-text with-tip', :data => {:action => 'edit'}, :title => Spree.t('edit') %>
|
|
54
|
+
<%= link_to '', '#', :class => 'delete-line-item fa fa-trash no-text with-tip', :data => { 'line-item-id' => item.id, :action => 'remove'}, :title => Spree.t('delete') %>
|
|
55
|
+
<% end %>
|
|
56
|
+
<% end %>
|
|
57
|
+
</td>
|
|
58
|
+
</tr>
|
|
59
|
+
</tbody>
|
|
60
|
+
<% end %>
|
|
61
|
+
</table>
|
|
62
|
+
<% end %>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<%= render 'stock_item', shipment: shipment %>
|
|
2
|
+
|
|
3
|
+
<% unless shipment.shipped? %>
|
|
4
|
+
<tr class="edit-method hidden total">
|
|
5
|
+
<td colspan="5">
|
|
6
|
+
<div class="field alpha five columns">
|
|
7
|
+
<%= label_tag 'selected_shipping_rate_id', Spree.t(:shipping_method) %>
|
|
8
|
+
<%= select_tag :selected_shipping_rate_id,
|
|
9
|
+
options_for_select(shipment.shipping_rates.map {|sr| ["#{sr.name} #{sr.display_price}", sr.id] }, shipment.selected_shipping_rate_id),
|
|
10
|
+
{:class => 'select2 fullwidth', :data => {'shipment-number' => shipment.number } } %>
|
|
11
|
+
</div>
|
|
12
|
+
</td>
|
|
13
|
+
<td class="actions">
|
|
14
|
+
<% if can? :update, shipment %>
|
|
15
|
+
<%= link_to '', '#', :class => 'save-method fa fa-ok no-text with-tip',
|
|
16
|
+
:data => {'shipment-number' => shipment.number, :action => 'save'}, title: Spree.t('actions.save') %>
|
|
17
|
+
<%= link_to '', '#', :class => 'cancel-method fa fa-cancel no-text with-tip',
|
|
18
|
+
:data => {:action => 'cancel'}, :title => Spree.t('actions.cancel') %>
|
|
19
|
+
<% end %>
|
|
20
|
+
</td>
|
|
21
|
+
</tr>
|
|
22
|
+
<% end %>
|
|
23
|
+
|
|
24
|
+
<tr class="show-method total">
|
|
25
|
+
<% if shipment.shipping_method %>
|
|
26
|
+
<td colspan="4">
|
|
27
|
+
<strong><%= shipment.shipping_method.name %></strong>
|
|
28
|
+
</td>
|
|
29
|
+
<td class="total" align="center">
|
|
30
|
+
<span><%= shipment.display_cost %></span>
|
|
31
|
+
</td>
|
|
32
|
+
<% else %>
|
|
33
|
+
<td colspan='5'><%= Spree.t(:no_shipping_method_selected) %></td>
|
|
34
|
+
<% end %>
|
|
35
|
+
|
|
36
|
+
<td class="actions">
|
|
37
|
+
<% if can? :update, shipment %>
|
|
38
|
+
<%= link_to '', '#', :class => 'edit-method fa fa-edit no-text with-tip', :data => {:action => 'edit'}, :title => Spree.t('edit') %>
|
|
39
|
+
<% end %>
|
|
40
|
+
</td>
|
|
41
|
+
</tr>
|
|
42
|
+
|
|
43
|
+
<tr class="edit-tracking hidden total">
|
|
44
|
+
<td colspan="5">
|
|
45
|
+
<label><%= Spree.t(:tracking_number) %>:</label>
|
|
46
|
+
<%= text_field_tag :tracking, shipment.tracking %>
|
|
47
|
+
</td>
|
|
48
|
+
<td class="actions">
|
|
49
|
+
<% if can? :update, shipment %>
|
|
50
|
+
<%= link_to '', '#', :class => 'save-tracking fa fa-ok no-text with-tip', :data => {'shipment-number' => shipment.number, :action => 'save'}, :title => Spree.t('actions.save') %>
|
|
51
|
+
<%= link_to '', '#', :class => 'cancel-tracking fa fa-cancel no-text with-tip', :data => {:action => 'cancel'}, :title => Spree.t('actions.cancel') %>
|
|
52
|
+
<% end %>
|
|
53
|
+
</td>
|
|
54
|
+
</tr>
|
|
55
|
+
|
|
56
|
+
<tr class="show-tracking total">
|
|
57
|
+
<td colspan="5">
|
|
58
|
+
<% if shipment.tracking.present? %>
|
|
59
|
+
<strong><%= Spree.t(:tracking) %>:</strong> <%= shipment.tracking %>
|
|
60
|
+
<% else %>
|
|
61
|
+
<%= Spree.t(:no_tracking_present) %>
|
|
62
|
+
<% end %>
|
|
63
|
+
</td>
|
|
64
|
+
<td class="actions">
|
|
65
|
+
<% if can? :update, shipment %>
|
|
66
|
+
<%= link_to '', '#', :class => 'edit-tracking fa fa-edit no-text with-tip', :data => {:action => 'edit'}, :title => Spree.t('edit') %>
|
|
67
|
+
<% end %>
|
|
68
|
+
</td>
|
|
69
|
+
</tr>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<% shipment.manifest.each do |item| %>
|
|
2
|
+
<tr class="stock-item" data-item-quantity="<%= item.quantity %>">
|
|
3
|
+
<td class="item-image"><%= mini_image(item.variant) %></td>
|
|
4
|
+
<td class="item-name">
|
|
5
|
+
<%= item.variant.product.name %><br><%= "(" + variant_options(item.variant) + ")" unless item.variant.option_values.empty? %>
|
|
6
|
+
<% if item.part && item.line_item %>
|
|
7
|
+
<i><%= Spree.t(:part_of_bundle, sku: item.product.sku) %></i>
|
|
8
|
+
<% end %>
|
|
9
|
+
</td>
|
|
10
|
+
<td class="item-price align-center">
|
|
11
|
+
<% if item.part %>
|
|
12
|
+
---
|
|
13
|
+
<% else %>
|
|
14
|
+
<%= item.line_item.single_money.to_html if item.line_item %>
|
|
15
|
+
<% end %>
|
|
16
|
+
</td>
|
|
17
|
+
<td class="item-qty-show align-center">
|
|
18
|
+
<% item.states.each do |state,count| %>
|
|
19
|
+
<%= count %> x <%= state.humanize.downcase %>
|
|
20
|
+
<% end %>
|
|
21
|
+
</td>
|
|
22
|
+
<% unless shipment.shipped? %>
|
|
23
|
+
<td class="item-qty-edit hidden">
|
|
24
|
+
<%= number_field_tag :quantity, item.quantity, :min => 0, :class => "line_item_quantity", :size => 5 %>
|
|
25
|
+
</td>
|
|
26
|
+
<% end %>
|
|
27
|
+
<td class="item-total align-center">
|
|
28
|
+
<% if item.part %>
|
|
29
|
+
---
|
|
30
|
+
<% else %>
|
|
31
|
+
<%= line_item_shipment_price(item.line_item, item.quantity) if item.line_item %>
|
|
32
|
+
<% end %>
|
|
33
|
+
</td>
|
|
34
|
+
<td class="cart-item-delete actions" data-hook="cart_item_delete">
|
|
35
|
+
<% if !shipment.shipped? && !item.part %>
|
|
36
|
+
<% if can? :update, item %>
|
|
37
|
+
<%= link_to '', '#', :class => 'save-item fa fa-ok no-text with-tip', :data => {'shipment-number' => shipment.number, 'variant-id' => item.variant.id, :action => 'save'}, :title => Spree.t('actions.save'), :style => 'display: none' %>
|
|
38
|
+
<%= link_to '', '#', :class => 'cancel-item fa fa-cancel no-text with-tip', :data => {:action => 'cancel'}, :title => Spree.t('actions.cancel'), :style => 'display: none' %>
|
|
39
|
+
<%= link_to '', '#', :class => 'edit-item fa fa-edit no-text with-tip', :data => {:action => 'edit'}, :title => Spree.t('edit') %>
|
|
40
|
+
<%= link_to '', '#', :class => 'split-item fa fa-arrows-h no-text with-tip', :data => {:action => 'split', 'variant-id' => item.variant.id}, :title => Spree.t('split') %>
|
|
41
|
+
<%= link_to '', '#', :class => 'delete-item fa fa-trash no-text with-tip', :data => {'shipment-number' => shipment.number, 'variant-id' => item.variant.id, :action => 'remove'}, :title => Spree.t('delete') %>
|
|
42
|
+
<% end %>
|
|
43
|
+
<% end %>
|
|
44
|
+
</td>
|
|
45
|
+
</tr>
|
|
46
|
+
<% end %>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<table class="index">
|
|
2
|
+
<thead>
|
|
3
|
+
<tr>
|
|
4
|
+
<th><%= Spree.t(:sku) %></th>
|
|
5
|
+
<th><%= Spree.t(:name) %></th>
|
|
6
|
+
<th><%= Spree.t(:options) %></th>
|
|
7
|
+
<th><%= Spree.t(:qty) %></th>
|
|
8
|
+
</tr>
|
|
9
|
+
</thead>
|
|
10
|
+
<tbody>
|
|
11
|
+
<% parts.each do |part| %>
|
|
12
|
+
<tr id="<%= dom_id(part, :sel)%>">
|
|
13
|
+
<td><%= part.sku %></td>
|
|
14
|
+
<td><%= part.name %></td>
|
|
15
|
+
<td><%= variant_options part %></td>
|
|
16
|
+
<td><%= text_field_tag :count, part.count %></td>
|
|
17
|
+
<td class="actions">
|
|
18
|
+
<%= image_tag "spinner.gif", :style => "display:none", :class => "spinner" %>
|
|
19
|
+
<%= link_to(icon('edit'),
|
|
20
|
+
set_count_admin_product_part_url(product, part),
|
|
21
|
+
:class => "set_count_admin_product_part_link fa fa-ok no-text") %>
|
|
22
|
+
|
|
23
|
+
<%= link_to(icon('delete'),
|
|
24
|
+
remove_admin_product_part_url(product, part),
|
|
25
|
+
:class => "remove_admin_product_part_link fa fa-trash no-text") %>
|
|
26
|
+
</td>
|
|
27
|
+
</tr>
|
|
28
|
+
<% end %>
|
|
29
|
+
<% if parts.empty? %>
|
|
30
|
+
<tr><td colspan="5"><%= Spree.t(:none) %>.</td></tr>
|
|
31
|
+
<% end %>
|
|
32
|
+
</tbody>
|
|
33
|
+
</table>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<h4><%= Spree.t(:available_parts) %></h4>
|
|
2
|
+
<table class="index">
|
|
3
|
+
<thead>
|
|
4
|
+
<tr>
|
|
5
|
+
<th><%= Spree.t(:name) %></th>
|
|
6
|
+
<th><%= Spree.t(:options) %></th>
|
|
7
|
+
<th><%= Spree.t(:qty) %></th>
|
|
8
|
+
<th></th>
|
|
9
|
+
</tr>
|
|
10
|
+
</thead>
|
|
11
|
+
<tbody>
|
|
12
|
+
<% @available_products.each do |part| %>
|
|
13
|
+
<tr id="<%= dom_id(part) %>"
|
|
14
|
+
class="<%= "with-variants" if part.has_variants? %>">
|
|
15
|
+
|
|
16
|
+
<td><%= part.name %></td>
|
|
17
|
+
<td>
|
|
18
|
+
<% if part.has_variants? %>
|
|
19
|
+
<%- opts = part.variants.map {|v| [variant_options(v), v.id] } %>
|
|
20
|
+
<%= select_tag "part[id]",
|
|
21
|
+
options_for_select(opts),
|
|
22
|
+
include_blank: Spree.t(:user_selectable)
|
|
23
|
+
%>
|
|
24
|
+
<% else %>
|
|
25
|
+
<%= hidden_field_tag "part[id]", part.master.id %>
|
|
26
|
+
<%= Spree.t(:no_variants) %>
|
|
27
|
+
<% end %>
|
|
28
|
+
</td>
|
|
29
|
+
<td><%= text_field_tag "part[count]", 1 %></td>
|
|
30
|
+
<td class="actions">
|
|
31
|
+
<%= image_tag "spinner.gif", :style => "display:none", :class => "spinner" %>
|
|
32
|
+
<%= link_to(icon('add') + ' ' + Spree.t(:select),
|
|
33
|
+
admin_product_parts_path(product),
|
|
34
|
+
:data => { 'target' => dom_id(part), 'master-variant-id' => part.master.id},
|
|
35
|
+
:class => "add_product_part_link") %>
|
|
36
|
+
</td>
|
|
37
|
+
</tr>
|
|
38
|
+
<% end %>
|
|
39
|
+
<% if @available_products.empty? %>
|
|
40
|
+
<tr><td colspan="3"><%= Spree.t(:no_match_found) %>.</td></tr>
|
|
41
|
+
<% end %>
|
|
42
|
+
</tbody>
|
|
43
|
+
</table>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%- content_for :javascript do %>
|
|
2
|
+
<%= javascript_tag "backend/spree_product_assembly" %>
|
|
3
|
+
<%- end %>
|
|
4
|
+
|
|
5
|
+
<%= render :partial => 'spree/admin/shared/product_sub_menu' %>
|
|
6
|
+
|
|
7
|
+
<%= render :partial => 'spree/admin/shared/product_tabs', :locals => {:current => "Parts"} %>
|
|
8
|
+
<div id="product_parts" data-product-slug="<%= product.slug %>">
|
|
9
|
+
<%= render :partial => "parts_table", :locals => {:parts => @parts} %>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<%= form_tag('#') do %>
|
|
13
|
+
<label><%= Spree.t(:search) %>:</label>
|
|
14
|
+
<input id="searchtext" size="25">
|
|
15
|
+
<button id="search_parts_button" class="fa fa-search button" name="button">Search</button>
|
|
16
|
+
<% end %>
|
|
17
|
+
|
|
18
|
+
<br/>
|
|
19
|
+
<div id="search_hits"></div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<div class="left six columns alpha">
|
|
2
|
+
<div class="field">
|
|
3
|
+
<%= f.label :can_be_part, Spree.t(:can_be_part)%>
|
|
4
|
+
<%= f.check_box(:can_be_part) %>
|
|
5
|
+
</div>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="right six columns omega">
|
|
8
|
+
<div class="field">
|
|
9
|
+
<%= f.label :individual_sale, Spree.t(:individual_sale)%>
|
|
10
|
+
<%= f.check_box(:individual_sale) %>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<% if @product.assembly? %>
|
|
15
|
+
<% content_for :head do %>
|
|
16
|
+
<script type="text/javascript">
|
|
17
|
+
jQuery(document).ready(function(){
|
|
18
|
+
$("input[id='product_on_hand']").attr('disabled',true).parent().hide();
|
|
19
|
+
$("input#product_can_be_part").attr('disabled',true);
|
|
20
|
+
});
|
|
21
|
+
</script>
|
|
22
|
+
<% end %>
|
|
23
|
+
<% end %>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<% ship_form.object.line_item_manifest.each do |item| %>
|
|
2
|
+
<tr class="stock-item">
|
|
3
|
+
<td class="item-image"><%= mini_image(item.variant) %></td>
|
|
4
|
+
<td class="item-name">
|
|
5
|
+
<%= item.variant.name %>
|
|
6
|
+
<%= render 'spree/orders/cart_description', variant: item.variant, line_item: item.line_item %>
|
|
7
|
+
</td>
|
|
8
|
+
<td class="item-qty">
|
|
9
|
+
<% if item.line_item.product.assembly? %>
|
|
10
|
+
<%= item.line_item.quantity %>
|
|
11
|
+
<% else %>
|
|
12
|
+
<%= item.quantity %>
|
|
13
|
+
<% end %>
|
|
14
|
+
</td>
|
|
15
|
+
<td class="item-price"><%= item.line_item.single_money %></td>
|
|
16
|
+
</tr>
|
|
17
|
+
<% end %>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<% product = variant.product %>
|
|
2
|
+
<% if product.assembly? %>
|
|
3
|
+
<ul class='assembly_parts'>
|
|
4
|
+
<% line_item.part_line_items.each do |pli| %>
|
|
5
|
+
<% pli_variant = pli.variant %>
|
|
6
|
+
<li>
|
|
7
|
+
(<%= line_item.quantity * pli.quantity %>)
|
|
8
|
+
<%= link_to pli_variant.name, product_path(pli_variant.product) %>
|
|
9
|
+
<% unless pli_variant.is_master? %>
|
|
10
|
+
(<%= pli_variant.options_text %>)
|
|
11
|
+
<% end %>
|
|
12
|
+
(<%= pli_variant.sku %>)
|
|
13
|
+
</li>
|
|
14
|
+
<% end %>
|
|
15
|
+
</ul>
|
|
16
|
+
<% end %>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<% if @product.assemblies_parts.any? %>
|
|
2
|
+
<div class="columns">
|
|
3
|
+
<h6><%= Spree.t(:parts_included) %></h6>
|
|
4
|
+
|
|
5
|
+
<ul id="products" class="inline product-listing" data-hook>
|
|
6
|
+
<% @product.assemblies_parts.each do |part| %>
|
|
7
|
+
<%- variant = part.part %>
|
|
8
|
+
|
|
9
|
+
<li id="product_<%= variant.product.id %>" class="columns three <%= cycle("alpha", "secondary", "", "omega secondary") %>" data-hook="products_list_item" itemscope itemtype="http://schema.org/Product">
|
|
10
|
+
<div class="product-image">
|
|
11
|
+
<%= link_to small_image(variant.product, itemprop: "name"), variant.product %>
|
|
12
|
+
</div>
|
|
13
|
+
<%= link_to truncate(variant.product.name, length: 50), variant.product, class: 'info', itemprop: "name", title: variant.product.name %>
|
|
14
|
+
|
|
15
|
+
<%- if !variant.in_stock? && !part.variant_selection_deferred? %>
|
|
16
|
+
<%- if variant.is_backorderable? %>
|
|
17
|
+
<div class="on-backorder"><%= Spree.t(:backorderable) %></div>
|
|
18
|
+
<%- else %>
|
|
19
|
+
<div class="out-of-stock"><%= Spree.t(:out_of_stock) %></div>
|
|
20
|
+
<%- end %>
|
|
21
|
+
<%- end %>
|
|
22
|
+
|
|
23
|
+
<%- if part.variant_selection_deferred? %>
|
|
24
|
+
<div class="variant-selection-deferred">
|
|
25
|
+
<%- product = variant.product %>
|
|
26
|
+
<%= label_tag "options_selected_variants_#{part.id}", Spree.t(:variant) %>
|
|
27
|
+
|
|
28
|
+
<%- opts = product.variants.map { |v| [variant_options(v), v.id] } %>
|
|
29
|
+
<%- opts_disabled = product.variants.each_with_object([]) { |v, o| o << v.id if !v.in_stock? && !v.is_backorderable? } %>
|
|
30
|
+
|
|
31
|
+
<%= select_tag "options[selected_variants][#{part.id}]", options_for_select(opts, disabled: opts_disabled)%>
|
|
32
|
+
</div>
|
|
33
|
+
<%- end %>
|
|
34
|
+
</li>
|
|
35
|
+
<% end %>
|
|
36
|
+
</ul>
|
|
37
|
+
</div>
|
|
38
|
+
<% end %>
|
data/bin/rails
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
en:
|
|
3
|
+
spree:
|
|
4
|
+
available_parts: Available parts
|
|
5
|
+
can_be_part: Can be part
|
|
6
|
+
individual_sale: Individual sale
|
|
7
|
+
no_variants: No variants
|
|
8
|
+
parts: Parts
|
|
9
|
+
part_quantity_invalid: Part quantity must be a positive integer
|
|
10
|
+
assembly_cannot_be_part: assembly can't be part
|
|
11
|
+
product_bundles: Product Bundles
|
|
12
|
+
parts_included: Parts included
|
|
13
|
+
part_of_bundle: 'Part of bundle %{sku}'
|
|
14
|
+
selected_quantity_not_available: Selected quantity not available
|
|
15
|
+
user_selectable: User Selectable
|