spree_core 2.3.13 → 2.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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