spree_core 2.3.13 → 2.4.0.rc1

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.
Files changed (232) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/spree.js.coffee.erb +1 -5
  3. data/app/helpers/spree/base_helper.rb +22 -11
  4. data/app/helpers/spree/products_helper.rb +8 -7
  5. data/app/mailers/spree/base_mailer.rb +1 -0
  6. data/app/mailers/spree/reimbursement_mailer.rb +10 -0
  7. data/app/mailers/spree/test_mailer.rb +2 -3
  8. data/app/models/concerns/spree/adjustment_source.rb +24 -0
  9. data/app/models/concerns/spree/calculated_adjustments.rb +33 -0
  10. data/app/models/concerns/spree/named_type.rb +12 -0
  11. data/app/models/concerns/spree/user_address.rb +30 -0
  12. data/app/models/concerns/spree/user_payment_source.rb +19 -0
  13. data/app/models/spree/address.rb +13 -6
  14. data/app/models/spree/adjustment.rb +5 -5
  15. data/app/models/spree/app_configuration.rb +8 -4
  16. data/app/models/spree/asset.rb +1 -1
  17. data/app/models/spree/base.rb +0 -3
  18. data/app/models/spree/calculator/flat_rate.rb +1 -5
  19. data/app/models/spree/calculator/returns/default_refund_amount.rb +36 -0
  20. data/app/models/spree/classification.rb +1 -1
  21. data/app/models/spree/credit_card.rb +18 -22
  22. data/app/models/spree/customer_return.rb +70 -0
  23. data/app/models/spree/exchange.rb +42 -0
  24. data/app/models/spree/gateway/bogus.rb +3 -3
  25. data/app/models/spree/image.rb +1 -1
  26. data/app/models/spree/inventory_unit.rb +32 -8
  27. data/app/models/spree/item_adjustments.rb +7 -11
  28. data/app/models/spree/legacy_user.rb +2 -2
  29. data/app/models/spree/line_item.rb +25 -12
  30. data/app/models/spree/option_type.rb +1 -1
  31. data/app/models/spree/option_value.rb +1 -8
  32. data/app/models/spree/order.rb +163 -145
  33. data/app/models/spree/order/checkout.rb +35 -23
  34. data/app/models/spree/order/payments.rb +66 -0
  35. data/app/models/spree/order_contents.rb +34 -24
  36. data/app/models/spree/order_populator.rb +6 -4
  37. data/app/models/spree/order_updater.rb +10 -1
  38. data/app/models/spree/payment.rb +19 -16
  39. data/app/models/spree/payment/processing.rb +40 -72
  40. data/app/models/spree/payment_method.rb +1 -1
  41. data/app/models/spree/payment_method/check.rb +0 -2
  42. data/app/models/spree/preference.rb +1 -1
  43. data/app/models/spree/preferences/preferable.rb +20 -0
  44. data/app/models/spree/price.rb +13 -3
  45. data/app/models/spree/product.rb +24 -29
  46. data/app/models/spree/product_property.rb +0 -2
  47. data/app/models/spree/promotion.rb +66 -24
  48. data/app/models/spree/promotion/actions/create_adjustment.rb +2 -2
  49. data/app/models/spree/promotion/actions/create_item_adjustments.rb +15 -11
  50. data/app/models/spree/promotion/actions/create_line_items.rb +2 -12
  51. data/app/models/spree/promotion/rules/first_order.rb +6 -2
  52. data/app/models/spree/promotion/rules/item_total.rb +42 -4
  53. data/app/models/spree/promotion/rules/one_use_per_user.rb +24 -0
  54. data/app/models/spree/promotion/rules/product.rb +13 -11
  55. data/app/models/spree/promotion/rules/taxon.rb +61 -0
  56. data/app/models/spree/promotion/rules/user.rb +1 -1
  57. data/app/models/spree/promotion/rules/user_logged_in.rb +4 -1
  58. data/app/models/spree/promotion_category.rb +6 -0
  59. data/app/models/spree/promotion_handler/cart.rb +14 -18
  60. data/app/models/spree/promotion_handler/coupon.rb +25 -16
  61. data/app/models/spree/promotion_rule.rb +13 -0
  62. data/app/models/spree/property.rb +1 -3
  63. data/app/models/spree/refund.rb +91 -0
  64. data/app/models/spree/refund_reason.rb +13 -0
  65. data/app/models/spree/reimbursement.rb +148 -0
  66. data/app/models/spree/reimbursement/credit.rb +25 -0
  67. data/app/models/spree/reimbursement/reimbursement_type_engine.rb +56 -0
  68. data/app/models/spree/reimbursement/reimbursement_type_validator.rb +12 -0
  69. data/app/models/spree/reimbursement_performer.rb +43 -0
  70. data/app/models/spree/reimbursement_tax_calculator.rb +38 -0
  71. data/app/models/spree/reimbursement_type.rb +16 -0
  72. data/app/models/spree/reimbursement_type/credit.rb +13 -0
  73. data/app/models/spree/reimbursement_type/exchange.rb +9 -0
  74. data/app/models/spree/reimbursement_type/original_payment.rb +13 -0
  75. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +50 -0
  76. data/app/models/spree/return_authorization.rb +52 -68
  77. data/app/models/spree/return_authorization_reason.rb +7 -0
  78. data/app/models/spree/return_item.rb +230 -0
  79. data/app/models/spree/return_item/default_eligibility_validator.rb +27 -0
  80. data/app/models/spree/return_item/eligibility_validator/base_validator.rb +24 -0
  81. data/app/models/spree/return_item/eligibility_validator/rma_required.rb +17 -0
  82. data/app/models/spree/return_item/eligibility_validator/time_since_purchase.rb +16 -0
  83. data/app/models/spree/return_item/exchange_variant_eligibility/same_option_value.rb +34 -0
  84. data/app/models/spree/return_item/exchange_variant_eligibility/same_product.rb +10 -0
  85. data/app/models/spree/returns_calculator.rb +8 -0
  86. data/app/models/spree/shipment.rb +209 -154
  87. data/app/models/spree/shipment_handler.rb +43 -0
  88. data/app/models/spree/shipping_calculator.rb +1 -1
  89. data/app/models/spree/shipping_category.rb +2 -2
  90. data/app/models/spree/shipping_method.rb +1 -1
  91. data/app/models/spree/shipping_method_category.rb +1 -1
  92. data/app/models/spree/shipping_rate.rb +4 -0
  93. data/app/models/spree/stock/adjuster.rb +10 -11
  94. data/app/models/spree/stock/availability_validator.rb +6 -10
  95. data/app/models/spree/stock/content_item.rb +48 -0
  96. data/app/models/spree/stock/coordinator.rb +14 -7
  97. data/app/models/spree/stock/estimator.rb +1 -1
  98. data/app/models/spree/stock/inventory_unit_builder.rb +21 -0
  99. data/app/models/spree/stock/package.rb +38 -51
  100. data/app/models/spree/stock/packer.rb +13 -11
  101. data/app/models/spree/stock/prioritizer.rb +7 -7
  102. data/app/models/spree/stock/splitter/base.rb +2 -2
  103. data/app/models/spree/stock_item.rb +6 -9
  104. data/app/models/spree/stock_location.rb +17 -25
  105. data/app/models/spree/stock_movement.rb +1 -8
  106. data/app/models/spree/stock_transfer.rb +0 -2
  107. data/app/models/spree/store.rb +1 -1
  108. data/app/models/spree/tax_category.rb +2 -2
  109. data/app/models/spree/tax_rate.rb +16 -22
  110. data/app/models/spree/taxon.rb +1 -1
  111. data/app/models/spree/variant.rb +45 -23
  112. data/app/models/spree/zone.rb +17 -17
  113. data/app/models/spree/zone_member.rb +1 -1
  114. data/app/views/layouts/spree/base_mailer.html.erb +784 -0
  115. data/app/views/spree/order_mailer/cancel_email.html.erb +45 -0
  116. data/app/views/spree/order_mailer/cancel_email.text.erb +2 -2
  117. data/app/views/spree/order_mailer/confirm_email.html.erb +84 -0
  118. data/app/views/spree/order_mailer/confirm_email.text.erb +2 -2
  119. data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +22 -0
  120. data/app/views/spree/shared/_base_mailer_footer.html.erb +20 -0
  121. data/app/views/spree/shared/_base_mailer_header.html.erb +31 -0
  122. data/app/views/spree/shipment_mailer/shipped_email.html.erb +34 -0
  123. data/app/views/spree/test_mailer/test_email.html.erb +40 -0
  124. data/app/views/spree/test_mailer/test_email.text.erb +2 -2
  125. data/config/initializers/friendly_id.rb +88 -0
  126. data/config/initializers/premailer_assets.rb +1 -0
  127. data/config/initializers/user_class_extensions.rb +9 -22
  128. data/config/locales/en.yml +180 -12
  129. data/db/default/spree/states.rb +73 -55
  130. data/db/migrate/20130213191427_create_default_stock.rb +1 -0
  131. data/db/migrate/20130807024301_upgrade_adjustments.rb +4 -5
  132. data/db/migrate/20140309033438_create_store_from_preferences.rb +0 -7
  133. data/db/migrate/20140318191500_create_spree_taxons_promotion_rules.rb +8 -0
  134. data/db/migrate/20140530024945_move_order_token_from_tokenized_permission.rb +1 -1
  135. data/db/migrate/20140601011216_set_shipment_total_for_users_upgrading.rb +5 -3
  136. data/db/migrate/20140625214618_create_spree_refunds.rb +12 -0
  137. data/db/migrate/20140702140656_create_spree_return_authorization_inventory_unit.rb +12 -0
  138. data/db/migrate/20140707125621_rename_return_authorization_inventory_unit_to_return_items.rb +5 -0
  139. data/db/migrate/20140709160534_backfill_line_item_pre_tax_amount.rb +10 -0
  140. data/db/migrate/20140710041921_recreate_spree_return_authorizations.rb +55 -0
  141. data/db/migrate/20140710181204_add_amount_fields_to_return_items.rb +7 -0
  142. data/db/migrate/20140710190048_drop_return_authorization_amount.rb +5 -0
  143. data/db/migrate/20140713140455_create_spree_return_authorization_reasons.rb +28 -0
  144. data/db/migrate/20140713140527_create_spree_refund_reasons.rb +14 -0
  145. data/db/migrate/20140713142214_rename_return_authorization_reason.rb +5 -0
  146. data/db/migrate/20140715182625_create_spree_promotion_categories.rb +11 -0
  147. data/db/migrate/20140716204111_drop_received_at_on_return_items.rb +9 -0
  148. data/db/migrate/20140716212330_add_reception_and_acceptance_status_to_return_items.rb +6 -0
  149. data/db/migrate/20140717155155_create_default_refund_reason.rb +9 -0
  150. data/db/migrate/20140717185932_add_default_to_spree_stock_locations.rb +5 -0
  151. data/db/migrate/20140718133010_create_spree_customer_returns.rb +9 -0
  152. data/db/migrate/20140718133349_add_customer_return_id_to_return_item.rb +6 -0
  153. data/db/migrate/20140718195325_create_friendly_id_slugs.rb +15 -0
  154. data/db/migrate/20140723004419_rename_spree_refund_return_authorization_id.rb +5 -0
  155. data/db/migrate/20140723152808_increase_return_item_pre_tax_amount_precision.rb +13 -0
  156. data/db/migrate/20140723214541_copy_product_slugs_to_slug_history.rb +15 -0
  157. data/db/migrate/20140725131539_create_spree_reimbursements.rb +21 -0
  158. data/db/migrate/20140728225422_add_promotionable_to_spree_products.rb +5 -0
  159. data/db/migrate/20140729133613_add_exchange_inventory_unit_foreign_keys.rb +7 -0
  160. data/db/migrate/20140730155938_add_acceptance_status_errors_to_return_item.rb +5 -0
  161. data/db/migrate/20140731150017_create_spree_reimbursement_types.rb +20 -0
  162. data/db/migrate/20140805171035_add_default_to_spree_credit_cards.rb +5 -0
  163. data/db/migrate/20140805171219_make_existing_credit_cards_default.rb +10 -0
  164. data/db/migrate/20140806144901_add_type_to_reimbursement_type.rb +9 -0
  165. data/db/migrate/20140808184039_create_spree_reimbursement_credits.rb +10 -0
  166. data/db/migrate/20140827170513_add_meta_title_to_spree_products.rb +7 -0
  167. data/db/migrate/20140924164824_add_code_to_spree_tax_categories.rb +5 -0
  168. data/db/migrate/20141002191113_add_code_to_spree_shipping_methods.rb +5 -0
  169. data/db/migrate/20141007230328_add_cancel_audit_fields_to_spree_orders.rb +6 -0
  170. data/db/migrate/20141009204607_add_store_id_to_orders.rb +8 -0
  171. data/lib/generators/spree/install/install_generator.rb +7 -3
  172. data/lib/spree/core.rb +11 -10
  173. data/lib/spree/core/controller_helpers/common.rb +3 -10
  174. data/lib/spree/core/controller_helpers/order.rb +15 -12
  175. data/lib/spree/core/controller_helpers/strong_parameters.rb +9 -9
  176. data/lib/spree/core/engine.rb +8 -1
  177. data/lib/spree/core/importer/order.rb +5 -17
  178. data/lib/spree/core/search/base.rb +1 -1
  179. data/lib/spree/core/validators/email.rb +1 -1
  180. data/lib/spree/core/version.rb +1 -1
  181. data/lib/spree/instrumentation.rb +41 -0
  182. data/lib/spree/migrations.rb +3 -7
  183. data/lib/spree/money.rb +2 -2
  184. data/lib/spree/permitted_attributes.rb +10 -9
  185. data/lib/spree/testing_support/ability_helpers.rb +25 -25
  186. data/lib/spree/testing_support/authorization_helpers.rb +3 -5
  187. data/lib/spree/testing_support/capybara_ext.rb +2 -2
  188. data/lib/spree/testing_support/factories/calculator_factory.rb +0 -8
  189. data/lib/spree/testing_support/factories/credit_card_factory.rb +1 -1
  190. data/lib/spree/testing_support/factories/customer_return_factory.rb +31 -0
  191. data/lib/spree/testing_support/factories/inventory_unit_factory.rb +1 -0
  192. data/lib/spree/testing_support/factories/line_item_factory.rb +2 -1
  193. data/lib/spree/testing_support/factories/order_factory.rb +11 -6
  194. data/lib/spree/testing_support/factories/promotion_category_factory.rb +6 -0
  195. data/lib/spree/testing_support/factories/promotion_factory.rb +9 -7
  196. data/lib/spree/testing_support/factories/refund_factory.rb +14 -0
  197. data/lib/spree/testing_support/factories/reimbursement_factory.rb +16 -0
  198. data/lib/spree/testing_support/factories/reimbursement_type_factory.rb +7 -0
  199. data/lib/spree/testing_support/factories/return_authorization_factory.rb +9 -3
  200. data/lib/spree/testing_support/factories/return_item_factory.rb +10 -0
  201. data/lib/spree/testing_support/factories/shipment_factory.rb +1 -0
  202. data/lib/spree/testing_support/factories/shipping_method_factory.rb +3 -2
  203. data/lib/spree/testing_support/factories/stock_factory.rb +12 -11
  204. data/lib/spree/testing_support/flash.rb +2 -2
  205. data/lib/tasks/email.rake +7 -0
  206. data/lib/tasks/exchanges.rake +70 -0
  207. data/vendor/assets/javascripts/jquery.validate/localization/messages_et.js +23 -0
  208. data/vendor/assets/javascripts/jquery.validate/localization/messages_eu.js +25 -0
  209. data/vendor/assets/javascripts/jquery.validate/localization/messages_hr.js +25 -0
  210. data/vendor/assets/javascripts/jquery.validate/localization/messages_ka.js +25 -0
  211. data/vendor/assets/javascripts/jquery.validate/localization/messages_ko.js +25 -0
  212. data/vendor/assets/javascripts/jquery.validate/localization/messages_my.js +25 -0
  213. data/vendor/assets/javascripts/jquery.validate/localization/messages_pt_BR.js +26 -0
  214. data/vendor/assets/javascripts/jquery.validate/localization/messages_pt_PT.js +26 -0
  215. data/vendor/assets/javascripts/jquery.validate/localization/messages_sl.js +25 -0
  216. data/vendor/assets/javascripts/jquery.validate/localization/messages_sv.js +23 -0
  217. data/vendor/assets/javascripts/jquery.validate/localization/messages_uk.js +25 -0
  218. data/vendor/assets/javascripts/jquery.validate/localization/messages_zh.js +25 -0
  219. data/vendor/assets/javascripts/jquery.validate/localization/messages_zh_TW.js +26 -0
  220. metadata +163 -47
  221. data/app/models/concerns/spree/ransackable_attributes.rb +0 -19
  222. data/db/migrate/20141021194502_add_state_lock_version_to_order.rb +0 -5
  223. data/db/migrate/20141101231208_fix_adjustment_order_presence.rb +0 -13
  224. data/db/migrate/20141105213646_update_classifications_positions.rb +0 -9
  225. data/db/migrate/20141120135441_add_guest_token_index_to_spree_orders.rb +0 -5
  226. data/db/migrate/20150515211137_fix_adjustment_order_id.rb +0 -70
  227. data/lib/spree/core/adjustment_source.rb +0 -26
  228. data/lib/spree/core/calculated_adjustments.rb +0 -35
  229. data/lib/spree/core/controller_helpers.rb +0 -20
  230. data/lib/spree/core/user_address.rb +0 -32
  231. data/lib/spree/core/user_payment_source.rb +0 -20
  232. data/lib/spree/localized_number.rb +0 -20
@@ -0,0 +1,43 @@
1
+ module Spree
2
+ class ShipmentHandler
3
+ class << self
4
+ def factory(shipment)
5
+ # Do we have a specialized shipping-method-specific handler? e.g:
6
+ # Given shipment.shipping_method = Spree::ShippingMethod::DigitalDownload
7
+ # do we have Spree::ShipmentHandler::DigitalDownload?
8
+ if sm_handler = "Spree::ShipmentHandler::#{shipment.shipping_method.name.split('::').last}".constantize rescue false
9
+ sm_handler.new(shipment)
10
+ else
11
+ new(shipment)
12
+ end
13
+ end
14
+ end
15
+
16
+ def initialize(shipment)
17
+ @shipment = shipment
18
+ end
19
+
20
+ def perform
21
+ @shipment.inventory_units.each &:ship!
22
+ @shipment.process_order_payments if Spree::Config[:auto_capture_on_dispatch]
23
+ send_shipped_email
24
+ @shipment.touch :shipped_at
25
+ update_order_shipment_state
26
+ end
27
+
28
+ private
29
+ def send_shipped_email
30
+ ShipmentMailer.shipped_email(@shipment.id).deliver
31
+ end
32
+
33
+ def update_order_shipment_state
34
+ order = @shipment.order
35
+
36
+ new_state = OrderUpdater.new(order).update_shipment_state
37
+ order.update_columns(
38
+ shipment_state: new_state,
39
+ updated_at: Time.now,
40
+ )
41
+ end
42
+ end
43
+ end
@@ -15,7 +15,7 @@ module Spree
15
15
 
16
16
  private
17
17
  def total(content_items)
18
- content_items.sum { |item| item.quantity * item.variant.price }
18
+ content_items.map(&:amount).sum
19
19
  end
20
20
  end
21
21
  end
@@ -2,7 +2,7 @@ module Spree
2
2
  class ShippingCategory < Spree::Base
3
3
  validates :name, presence: true
4
4
  has_many :products, inverse_of: :shipping_category
5
- has_many :shipping_method_categories, inverse_of: :shipping_method
5
+ has_many :shipping_method_categories
6
6
  has_many :shipping_methods, through: :shipping_method_categories
7
7
  end
8
- end
8
+ end
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class ShippingMethod < Spree::Base
3
3
  acts_as_paranoid
4
- include Spree::Core::CalculatedAdjustments
4
+ include Spree::CalculatedAdjustments
5
5
  DISPLAY = [:both, :front_end, :back_end]
6
6
 
7
7
  default_scope -> { where(deleted_at: nil) }
@@ -1,6 +1,6 @@
1
1
  module Spree
2
2
  class ShippingMethodCategory < Spree::Base
3
3
  belongs_to :shipping_method, class_name: 'Spree::ShippingMethod'
4
- belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :shipping_method_categories
4
+ belongs_to :shipping_category, class_name: 'Spree::ShippingCategory'
5
5
  end
6
6
  end
@@ -46,6 +46,10 @@ module Spree
46
46
  Spree::ShippingMethod.unscoped { super }
47
47
  end
48
48
 
49
+ def shipping_method_code
50
+ shipping_method.code
51
+ end
52
+
49
53
  def tax_rate
50
54
  Spree::TaxRate.unscoped { super }
51
55
  end
@@ -3,25 +3,24 @@
3
3
  module Spree
4
4
  module Stock
5
5
  class Adjuster
6
- attr_accessor :variant, :need, :status
6
+ attr_accessor :inventory_unit, :status, :fulfilled
7
7
 
8
- def initialize(variant, quantity, status)
9
- @variant = variant
10
- @need = quantity
8
+ def initialize(inventory_unit, status)
9
+ @inventory_unit = inventory_unit
11
10
  @status = status
11
+ @fulfilled = false
12
12
  end
13
13
 
14
- def adjust(item)
15
- if item.quantity >= need
16
- item.quantity = need
17
- @need = 0
18
- elsif item.quantity < need
19
- @need -= item.quantity
14
+ def adjust(package)
15
+ if fulfilled?
16
+ package.remove(inventory_unit)
17
+ else
18
+ self.fulfilled = true
20
19
  end
21
20
  end
22
21
 
23
22
  def fulfilled?
24
- @need == 0
23
+ fulfilled
25
24
  end
26
25
  end
27
26
  end
@@ -5,20 +5,16 @@ module Spree
5
5
  unit_count = line_item.inventory_units.size
6
6
  return if unit_count >= line_item.quantity
7
7
  quantity = line_item.quantity - unit_count
8
- return if quantity.zero?
9
8
 
10
9
  quantifier = Stock::Quantifier.new(line_item.variant)
11
10
 
12
- return if quantifier.can_supply?(quantity)
11
+ unless quantifier.can_supply? quantity
12
+ variant = line_item.variant
13
+ display_name = %Q{#{variant.name}}
14
+ display_name += %Q{ (#{variant.options_text})} unless variant.options_text.blank?
13
15
 
14
- variant = line_item.variant
15
- display_name = "#{variant.name}"
16
- display_name += " (#{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
- )
16
+ line_item.errors[:quantity] << Spree.t(:selected_quantity_not_available, :scope => :order_populator, :item => display_name.inspect)
17
+ end
22
18
  end
23
19
  end
24
20
  end
@@ -0,0 +1,48 @@
1
+ module Spree
2
+ module Stock
3
+ class ContentItem
4
+ attr_accessor :inventory_unit, :state
5
+
6
+ def initialize(inventory_unit, state = :on_hand)
7
+ @inventory_unit = inventory_unit
8
+ @state = state
9
+ end
10
+
11
+ def variant
12
+ inventory_unit.variant
13
+ end
14
+
15
+ def weight
16
+ variant.weight * quantity
17
+ end
18
+
19
+ def line_item
20
+ inventory_unit.line_item
21
+ end
22
+
23
+ def on_hand?
24
+ state.to_s == "on_hand"
25
+ end
26
+
27
+ def backordered?
28
+ state.to_s == "backordered"
29
+ end
30
+
31
+ def price
32
+ variant.price
33
+ end
34
+
35
+ def amount
36
+ price * quantity
37
+ end
38
+
39
+ def quantity
40
+ # Since inventory units don't have a quantity,
41
+ # make this always 1 for now, leaving ourselves
42
+ # open to a different possibility in the future,
43
+ # but this massively simplifies things for now
44
+ 1
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,10 +1,17 @@
1
1
  module Spree
2
2
  module Stock
3
3
  class Coordinator
4
- attr_reader :order
4
+ attr_reader :order, :inventory_units
5
5
 
6
- def initialize(order)
6
+ def initialize(order, inventory_units = nil)
7
7
  @order = order
8
+ @inventory_units = inventory_units || InventoryUnitBuilder.new(order).units
9
+ end
10
+
11
+ def shipments
12
+ packages.map do |package|
13
+ package.to_shipment.tap { |s| s.address = order.ship_address }
14
+ end
8
15
  end
9
16
 
10
17
  def packages
@@ -24,9 +31,9 @@ module Spree
24
31
  # Returns an array of Package instances
25
32
  def build_packages(packages = Array.new)
26
33
  StockLocation.active.each do |stock_location|
27
- next unless stock_location.stock_items.where(:variant_id => order.line_items.pluck(:variant_id)).exists?
34
+ next unless stock_location.stock_items.where(:variant_id => inventory_units.map(&:variant_id)).exists?
28
35
 
29
- packer = build_packer(stock_location, order)
36
+ packer = build_packer(stock_location, inventory_units)
30
37
  packages += packer.packages
31
38
  end
32
39
  packages
@@ -34,7 +41,7 @@ module Spree
34
41
 
35
42
  private
36
43
  def prioritize_packages(packages)
37
- prioritizer = Prioritizer.new(order, packages)
44
+ prioritizer = Prioritizer.new(inventory_units, packages)
38
45
  prioritizer.prioritized_packages
39
46
  end
40
47
 
@@ -46,8 +53,8 @@ module Spree
46
53
  packages
47
54
  end
48
55
 
49
- def build_packer(stock_location, order)
50
- Packer.new(stock_location, order, splitters(stock_location))
56
+ def build_packer(stock_location, inventory_units)
57
+ Packer.new(stock_location, inventory_units, splitters(stock_location))
51
58
  end
52
59
 
53
60
  def splitters(stock_location)
@@ -35,7 +35,7 @@ module Spree
35
35
  # If the rate's zone matches the order's zone, a positive adjustment will be applied.
36
36
  # If the rate is from the default tax zone, then a negative adjustment will be applied.
37
37
  # See the tests in shipping_rate_spec.rb for an example of this.d
38
- rate.zone == package.order.tax_zone || rate.zone.default_tax?
38
+ rate.zone == order.tax_zone || rate.zone.default_tax?
39
39
  end
40
40
  end
41
41
 
@@ -0,0 +1,21 @@
1
+ module Spree
2
+ module Stock
3
+ class InventoryUnitBuilder
4
+ def initialize(order)
5
+ @order = order
6
+ end
7
+
8
+ def units
9
+ @order.line_items.flat_map do |line_item|
10
+ line_item.quantity.times.map do |i|
11
+ @order.inventory_units.build(
12
+ pending: true,
13
+ variant: line_item.variant,
14
+ line_item: line_item
15
+ )
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,53 +1,50 @@
1
1
  module Spree
2
2
  module Stock
3
3
  class Package
4
- ContentItem = Struct.new(:line_item, :variant, :quantity, :state)
5
-
6
- attr_reader :stock_location, :order, :contents
4
+ attr_reader :stock_location, :contents
7
5
  attr_accessor :shipping_rates
8
6
 
9
- def initialize(stock_location, order, contents=[])
7
+ def initialize(stock_location, contents=[])
10
8
  @stock_location = stock_location
11
- @order = order
12
9
  @contents = contents
13
10
  @shipping_rates = Array.new
14
11
  end
15
12
 
16
- def add(line_item, quantity, state = :on_hand, variant = nil)
17
- contents << ContentItem.new(line_item, variant || line_item.variant, quantity, state)
13
+ def add(inventory_unit, state = :on_hand)
14
+ contents << ContentItem.new(inventory_unit, state) unless find_item(inventory_unit)
15
+ end
16
+
17
+ def add_multiple(inventory_units, state = :on_hand)
18
+ inventory_units.each { |inventory_unit| add(inventory_unit, state) }
19
+ end
20
+
21
+ def remove(inventory_unit)
22
+ item = find_item(inventory_unit)
23
+ @contents -= [item] if item
18
24
  end
19
25
 
20
26
  def weight
21
- contents.sum { |item| item.variant.weight * item.quantity }
27
+ contents.sum(&:weight)
22
28
  end
23
29
 
24
30
  def on_hand
25
- contents.select { |item| item.state == :on_hand }
31
+ contents.select(&:on_hand?)
26
32
  end
27
33
 
28
34
  def backordered
29
- contents.select { |item| item.state == :backordered }
35
+ contents.select(&:backordered?)
30
36
  end
31
37
 
32
- # Consider extensions and applications might create a inventory unit
33
- # where the variant and the line_item might not refer to the same product
34
- def find_item(variant, state = :on_hand, line_item = nil)
35
- contents.select do |item|
36
- item.variant == variant &&
37
- item.state == state &&
38
- (line_item.nil? || line_item == item.line_item)
39
- end.first
38
+ def find_item(inventory_unit, state = nil)
39
+ contents.detect do |item|
40
+ item.inventory_unit == inventory_unit &&
41
+ (!state || item.state.to_s == state.to_s)
42
+ end
40
43
  end
41
44
 
42
- def quantity(state=nil)
43
- case state
44
- when :on_hand
45
- on_hand.sum { |item| item.quantity }
46
- when :backordered
47
- backordered.sum { |item| item.quantity }
48
- else
49
- contents.sum { |item| item.quantity }
50
- end
45
+ def quantity(state = nil)
46
+ matched_contents = state.nil? ? contents : contents.select { |c| c.state.to_s == state.to_s }
47
+ matched_contents.map(&:quantity).sum
51
48
  end
52
49
 
53
50
  def empty?
@@ -55,7 +52,7 @@ module Spree
55
52
  end
56
53
 
57
54
  def currency
58
- order.currency
55
+ #TODO calculate from first variant?
59
56
  end
60
57
 
61
58
  def shipping_categories
@@ -67,32 +64,22 @@ module Spree
67
64
  end
68
65
 
69
66
  def inspect
70
- out = "#{order} - "
71
- out << contents.map do |content_item|
72
- "#{content_item.variant.name} #{content_item.quantity} #{content_item.state}"
73
- end.join('/')
74
- out
67
+ contents.map do |content_item|
68
+ "#{content_item.variant.name} #{content_item.state}"
69
+ end.join(' / ')
75
70
  end
76
71
 
77
72
  def to_shipment
78
- shipment = Spree::Shipment.new
79
- shipment.address = order.ship_address
80
- shipment.order = order
81
- shipment.stock_location = stock_location
82
- shipment.shipping_rates = shipping_rates
83
-
84
- contents.each do |item|
85
- item.quantity.times do |n|
86
- unit = shipment.inventory_units.build
87
- unit.pending = true
88
- unit.order = order
89
- unit.variant = item.variant
90
- unit.line_item = item.line_item
91
- unit.state = item.state.to_s
92
- end
93
- end
94
-
95
- shipment
73
+ # At this point we should only have one content item per inventory unit
74
+ # across the entire set of inventory units to be shipped, which has been
75
+ # taken care of by the Prioritizer
76
+ contents.each { |content_item| content_item.inventory_unit.state = content_item.state.to_s }
77
+
78
+ Spree::Shipment.new(
79
+ stock_location: stock_location,
80
+ shipping_rates: shipping_rates,
81
+ inventory_units: contents.map(&:inventory_unit)
82
+ )
96
83
  end
97
84
  end
98
85
  end
@@ -1,11 +1,11 @@
1
1
  module Spree
2
2
  module Stock
3
3
  class Packer
4
- attr_reader :stock_location, :order, :splitters
4
+ attr_reader :stock_location, :inventory_units, :splitters
5
5
 
6
- def initialize(stock_location, order, splitters=[Splitter::Base])
6
+ def initialize(stock_location, inventory_units, splitters=[Splitter::Base])
7
7
  @stock_location = stock_location
8
- @order = order
8
+ @inventory_units = inventory_units
9
9
  @splitters = splitters
10
10
  end
11
11
 
@@ -18,17 +18,19 @@ module Spree
18
18
  end
19
19
 
20
20
  def default_package
21
- package = Package.new(stock_location, order)
22
- order.line_items.each do |line_item|
23
- if line_item.should_track_inventory?
24
- next unless stock_location.stock_item(line_item.variant)
21
+ package = Package.new(stock_location)
22
+ inventory_units.group_by(&:variant).each do |variant, variant_inventory_units|
23
+ units = variant_inventory_units.clone
24
+ if variant.should_track_inventory?
25
+ next unless stock_location.stock_item(variant)
25
26
 
26
- on_hand, backordered = stock_location.fill_status(line_item.variant, line_item.quantity)
27
- package.add line_item, on_hand, :on_hand if on_hand > 0
28
- package.add line_item, backordered, :backordered if backordered > 0
27
+ on_hand, backordered = stock_location.fill_status(variant, units.count)
28
+ package.add_multiple units.slice!(0, on_hand), :on_hand if on_hand > 0
29
+ package.add_multiple units.slice!(0, backordered), :backordered if backordered > 0
29
30
  else
30
- package.add line_item, line_item.quantity, :on_hand
31
+ package.add_multiple units
31
32
  end
33
+
32
34
  end
33
35
  package
34
36
  end