spree_core 2.1.12 → 2.2.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.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/base_helper.rb +4 -7
  3. data/app/helpers/spree/products_helper.rb +11 -9
  4. data/app/helpers/spree/store_helper.rb +5 -0
  5. data/app/models/spree/ability.rb +4 -0
  6. data/app/models/spree/address.rb +6 -6
  7. data/app/models/spree/adjustment.rb +40 -61
  8. data/app/models/spree/app_configuration.rb +1 -14
  9. data/app/models/spree/calculator.rb +12 -4
  10. data/app/models/spree/calculator/default_tax.rb +42 -38
  11. data/app/models/spree/calculator/flat_percent_item_total.rb +2 -4
  12. data/app/models/spree/calculator/free_shipping.rb +5 -2
  13. data/app/models/spree/calculator/percent_on_line_item.rb +15 -0
  14. data/app/models/spree/calculator/percent_per_item.rb +3 -0
  15. data/app/models/spree/classification.rb +3 -2
  16. data/app/models/spree/credit_card.rb +7 -25
  17. data/app/models/spree/gateway/bogus.rb +5 -5
  18. data/app/models/spree/gateway/bogus_simple.rb +0 -8
  19. data/app/models/spree/image.rb +0 -9
  20. data/app/models/spree/inventory_unit.rb +10 -4
  21. data/app/models/spree/item_adjustments.rb +65 -0
  22. data/app/models/spree/legacy_user.rb +1 -0
  23. data/app/models/spree/line_item.rb +33 -13
  24. data/app/models/spree/option_type.rb +2 -2
  25. data/app/models/spree/option_value.rb +1 -1
  26. data/app/models/spree/order.rb +109 -89
  27. data/app/models/spree/order/checkout.rb +48 -0
  28. data/app/models/spree/order_contents.rb +72 -37
  29. data/app/models/spree/order_inventory.rb +65 -68
  30. data/app/models/spree/order_populator.rb +3 -17
  31. data/app/models/spree/order_updater.rb +63 -44
  32. data/app/models/spree/payment.rb +20 -5
  33. data/app/models/spree/payment/processing.rb +19 -25
  34. data/app/models/spree/payment_capture_event.rb +9 -0
  35. data/app/models/spree/payment_method/check.rb +0 -2
  36. data/app/models/spree/price.rb +1 -1
  37. data/app/models/spree/product.rb +14 -16
  38. data/app/models/spree/product/scopes.rb +4 -6
  39. data/app/models/spree/product_option_type.rb +2 -2
  40. data/app/models/spree/product_property.rb +2 -2
  41. data/app/models/spree/promotion.rb +71 -50
  42. data/app/models/spree/promotion/actions/create_adjustment.rb +31 -32
  43. data/app/models/spree/promotion/actions/create_item_adjustments.rb +83 -0
  44. data/app/models/spree/promotion/actions/free_shipping.rb +36 -0
  45. data/app/models/spree/promotion/rules/first_order.rb +4 -0
  46. data/app/models/spree/promotion/rules/item_total.rb +5 -1
  47. data/app/models/spree/promotion/rules/product.rb +4 -0
  48. data/app/models/spree/promotion/rules/user.rb +5 -6
  49. data/app/models/spree/promotion/rules/user_logged_in.rb +4 -0
  50. data/app/models/spree/promotion_action.rb +1 -5
  51. data/app/models/spree/promotion_handler/cart.rb +38 -0
  52. data/app/models/spree/promotion_handler/coupon.rb +76 -0
  53. data/app/models/spree/promotion_handler/free_shipping.rb +31 -0
  54. data/app/models/spree/promotion_handler/page.rb +24 -0
  55. data/app/models/spree/promotion_rule.rb +15 -7
  56. data/app/models/spree/property.rb +1 -1
  57. data/app/models/spree/return_authorization.rb +7 -1
  58. data/app/models/spree/shipment.rb +113 -49
  59. data/app/models/spree/shipping_calculator.rb +4 -5
  60. data/app/models/spree/shipping_category.rb +2 -2
  61. data/app/models/spree/shipping_method.rb +12 -6
  62. data/app/models/spree/shipping_rate.rb +27 -7
  63. data/app/models/spree/stock/availability_validator.rb +1 -1
  64. data/app/models/spree/stock/estimator.rb +13 -1
  65. data/app/models/spree/stock/package.rb +11 -7
  66. data/app/models/spree/stock/packer.rb +3 -3
  67. data/app/models/spree/stock/quantifier.rb +9 -1
  68. data/app/models/spree/stock_item.rb +11 -6
  69. data/app/models/spree/stock_movement.rb +1 -2
  70. data/app/models/spree/tax_category.rb +6 -1
  71. data/app/models/spree/tax_rate.rb +57 -49
  72. data/app/models/spree/taxon.rb +10 -5
  73. data/app/models/spree/taxonomy.rb +5 -2
  74. data/app/models/spree/variant.rb +33 -16
  75. data/app/models/spree/zone.rb +24 -24
  76. data/app/views/spree/shared/_routes.html.erb +3 -0
  77. data/config/locales/en.yml +42 -26
  78. data/db/migrate/20130213191427_create_default_stock.rb +3 -3
  79. data/db/migrate/20130413230529_add_name_to_spree_credit_cards.rb +5 -0
  80. data/db/migrate/20130414000512_update_name_fields_on_spree_credit_cards.rb +13 -0
  81. data/db/migrate/20130417120035_update_adjustment_states.rb +2 -2
  82. data/db/migrate/20130417123427_add_shipping_rates_to_shipments.rb +1 -1
  83. data/db/migrate/20130509115210_add_number_to_stock_transfer.rb +1 -1
  84. data/db/migrate/20130611054351_rename_shipping_methods_zones_to_spree_shipping_methods_zones.rb +0 -5
  85. data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +7 -5
  86. data/db/migrate/20130807024301_upgrade_adjustments.rb +39 -0
  87. data/db/migrate/20130807024302_rename_adjustment_fields.rb +17 -0
  88. data/db/migrate/20130813004002_add_shipment_total_to_spree_orders.rb +5 -0
  89. data/db/migrate/20130813232134_rename_activators_to_promotions.rb +5 -0
  90. data/db/migrate/20130815000406_add_adjustment_total_to_line_items.rb +5 -0
  91. data/db/migrate/20130815024413_add_adjustment_total_to_shipments.rb +5 -0
  92. data/db/migrate/20130828234942_add_tax_total_to_line_items_shipments_and_orders.rb +8 -0
  93. data/db/migrate/20130830001159_migrate_old_shipping_calculators.rb +1 -1
  94. data/db/migrate/20130903183026_add_code_to_spree_promotion_rules.rb +5 -0
  95. data/db/migrate/20130917024658_remove_promotions_event_name_field.rb +5 -0
  96. data/db/migrate/20130924040529_add_promo_total_to_line_items_and_shipments_and_orders.rb +7 -0
  97. data/db/migrate/20131001013410_remove_unused_credit_card_fields.rb +7 -3
  98. data/db/migrate/20131107132123_add_tax_category_to_variants.rb +6 -0
  99. data/db/migrate/20131118043959_add_included_to_adjustments.rb +5 -0
  100. data/db/migrate/20131118050234_rename_tax_total_fields.rb +11 -0
  101. data/db/migrate/20131118183431_add_line_item_id_to_spree_inventory_units.rb +21 -0
  102. data/db/migrate/20131127001002_add_position_to_classifications.rb +5 -0
  103. data/db/migrate/20131211112807_create_spree_orders_promotions.rb +8 -0
  104. data/db/migrate/20131218054603_add_item_count_to_spree_orders.rb +5 -0
  105. data/db/migrate/20140106224208_rename_permalink_to_slug_for_products.rb +5 -0
  106. data/db/migrate/20140124023232_rename_activator_id_in_rules_and_actions_to_promotion_id.rb +6 -0
  107. data/db/migrate/20140203161722_add_approver_id_and_approved_at_to_orders.rb +6 -0
  108. data/db/migrate/20140204115338_add_confirmation_delivered_to_spree_orders.rb +5 -0
  109. data/db/migrate/20140205120320_create_spree_payment_capture_events.rb +12 -0
  110. data/db/migrate/20140205144710_add_uncaptured_amount_to_payments.rb +5 -0
  111. data/db/migrate/20140207085910_add_tax_category_id_to_shipping_methods.rb +5 -0
  112. data/db/migrate/20140207093021_add_tax_rate_id_to_shipping_rates.rb +5 -0
  113. data/db/migrate/20140211040159_add_pre_tax_amount_to_line_items_and_shipments.rb +6 -0
  114. data/db/migrate/20140213184916_add_more_indexes.rb +13 -0
  115. data/db/migrate/20140219060952_add_considered_risky_to_orders.rb +5 -0
  116. data/lib/generators/spree/dummy/dummy_generator.rb +1 -6
  117. data/lib/generators/spree/install/install_generator.rb +6 -6
  118. data/lib/generators/spree/install/templates/{app/assets/javascripts/admin → vendor/assets/javascripts/spree/backend}/all.js +3 -3
  119. data/lib/generators/spree/install/templates/{app/assets/javascripts/store → vendor/assets/javascripts/spree/frontend}/all.js +3 -3
  120. data/lib/generators/spree/install/templates/{app/assets/stylesheets/store → vendor/assets/stylesheets/spree/backend}/all.css +3 -3
  121. data/lib/generators/spree/install/templates/{app/assets/stylesheets/admin → vendor/assets/stylesheets/spree/frontend}/all.css +3 -3
  122. data/lib/spree/core.rb +21 -8
  123. data/lib/spree/core/calculated_adjustments.rb +0 -40
  124. data/lib/spree/core/controller_helpers.rb +5 -0
  125. data/lib/spree/core/controller_helpers/auth.rb +2 -2
  126. data/lib/spree/core/controller_helpers/common.rb +0 -5
  127. data/lib/spree/core/controller_helpers/order.rb +8 -9
  128. data/lib/spree/core/engine.rb +10 -17
  129. data/lib/spree/core/permalinks.rb +1 -1
  130. data/lib/spree/core/product_duplicator.rb +3 -8
  131. data/lib/spree/core/user_address.rb +1 -1
  132. data/lib/spree/core/validators/email.rb +23 -1
  133. data/lib/spree/core/version.rb +1 -1
  134. data/lib/spree/money.rb +1 -1
  135. data/lib/spree/permitted_attributes.rb +2 -2
  136. data/lib/spree/testing_support/caching.rb +47 -0
  137. data/lib/spree/testing_support/factories/adjustment_factory.rb +11 -2
  138. data/lib/spree/testing_support/factories/credit_card_factory.rb +2 -1
  139. data/lib/spree/testing_support/factories/order_factory.rb +10 -5
  140. data/lib/spree/testing_support/factories/payment_factory.rb +2 -2
  141. data/lib/spree/testing_support/factories/payment_method_factory.rb +3 -3
  142. data/lib/spree/testing_support/factories/promotion_factory.rb +16 -1
  143. data/lib/spree/testing_support/factories/shipment_factory.rb +8 -4
  144. data/lib/spree/testing_support/factories/shipping_method_factory.rb +1 -3
  145. data/lib/spree/testing_support/factories/stock_factory.rb +1 -1
  146. data/lib/spree/testing_support/factories/tax_rate_factory.rb +2 -2
  147. data/lib/spree/testing_support/order_walkthrough.rb +1 -1
  148. data/lib/tasks/core.rake +2 -2
  149. data/vendor/assets/fonts/FontAwesome.otf +0 -0
  150. data/vendor/assets/fonts/fontawesome-webfont.eot +0 -0
  151. data/vendor/assets/fonts/fontawesome-webfont.svg +399 -0
  152. data/vendor/assets/fonts/fontawesome-webfont.ttf +0 -0
  153. data/vendor/assets/fonts/fontawesome-webfont.woff +0 -0
  154. data/vendor/assets/stylesheets/font-awesome.scss +1475 -0
  155. metadata +73 -44
  156. data/app/assets/javascripts/admin/handlebar_extensions.js +0 -9
  157. data/app/helpers/spree/admin/adjustments_helper.rb +0 -26
  158. data/app/helpers/spree/admin/images_helper.rb +0 -18
  159. data/app/helpers/spree/promotion_rules_helper.rb +0 -13
  160. data/app/models/spree/activator.rb +0 -29
  161. data/app/models/spree/calculator/per_item.rb +0 -41
  162. data/app/models/spree/stock/remaining_packer.rb +0 -22
  163. data/app/views/spree/payments/_payment.html.erb +0 -18
  164. data/db/migrate/20131118041203_add_tax_total_to_spree_orders.rb +0 -5
  165. data/db/migrate/20131118043021_add_order_id_to_spree_adjustments.rb +0 -6
  166. data/db/migrate/20131118074808_add_included_to_spree_adjustments.rb +0 -5
  167. data/db/migrate/20140415041315_add_user_id_created_by_id_index_to_order.rb +0 -5
  168. data/lib/spree/core/gateway_error.rb +0 -5
  169. data/lib/spree/core/preference_rescue.rb +0 -25
  170. data/lib/spree/core/s3_support.rb +0 -25
  171. data/lib/spree/promo/coupon_applicator.rb +0 -71
  172. data/lib/spree/testing_support/factories/activator_factory.rb +0 -8
@@ -84,11 +84,22 @@ module Spree
84
84
  if states[:delivery]
85
85
  before_transition :to => :delivery, :do => :create_proposed_shipments
86
86
  before_transition :to => :delivery, :do => :ensure_available_shipping_rates
87
+ before_transition :from => :delivery, :do => :apply_free_shipping_promotions
88
+ end
89
+
90
+ if states[:payment]
91
+ before_transition :to => :payment, :do => :set_shipments_cost
92
+ before_transition :to => :payment, :do => :create_tax_charge!
87
93
  end
88
94
 
89
95
  after_transition :to => :complete, :do => :finalize!
90
96
  after_transition :to => :resumed, :do => :after_resume
91
97
  after_transition :to => :canceled, :do => :after_cancel
98
+
99
+ after_transition :from => any - :cart, :to => any - [:confirm, :complete] do |order|
100
+ order.update_totals
101
+ order.persist_totals
102
+ end
92
103
  end
93
104
  end
94
105
 
@@ -190,6 +201,43 @@ module Spree
190
201
  return false unless has_checkout_step?(self.state) && has_checkout_step?(state)
191
202
  checkout_step_index(state) > checkout_step_index(self.state)
192
203
  end
204
+
205
+ define_callbacks :updating_from_params, terminator: 'result == false'
206
+
207
+ set_callback :updating_from_params, :before, :update_params_payment_source
208
+
209
+ def update_from_params(params, permitted_params)
210
+ success = false
211
+ @updating_params = params
212
+ run_callbacks :updating_from_params do
213
+ attributes = @updating_params[:order] ? @updating_params[:order].permit(permitted_params) : {}
214
+ success = self.update_attributes(attributes)
215
+ end
216
+ @updating_params = nil
217
+ success
218
+ end
219
+
220
+ private
221
+ # For payment step, filter order parameters to produce the expected nested
222
+ # attributes for a single payment and its source, discarding attributes
223
+ # for payment methods other than the one selected
224
+ def update_params_payment_source
225
+ # respond_to check is necessary due to issue described in #2910
226
+ if has_checkout_step?("payment") && self.payment?
227
+ if @updating_params[:payment_source].present?
228
+ source_params = @updating_params.delete(:payment_source)[@updating_params[:order][:payments_attributes].first[:payment_method_id].underscore]
229
+
230
+ if source_params
231
+ @updating_params[:order][:payments_attributes].first[:source_attributes] = source_params
232
+ end
233
+ end
234
+
235
+ if (@updating_params[:order][:payments_attributes])
236
+ @updating_params[:order][:payments_attributes].first[:amount] = self.total
237
+ end
238
+ end
239
+ end
240
+
193
241
  end
194
242
  end
195
243
  end
@@ -6,62 +6,97 @@ module Spree
6
6
  @order = order
7
7
  end
8
8
 
9
- # Get current line item for variant if exists
10
- # Add variant qty to line_item
11
9
  def add(variant, quantity = 1, currency = nil, shipment = nil)
12
- line_item = order.find_line_item_by_variant(variant)
13
- add_to_line_item(line_item, variant, quantity, currency, shipment)
10
+ line_item = add_to_line_item(variant, quantity, currency, shipment)
11
+ reload_totals
12
+ PromotionHandler::Cart.new(order, line_item).activate
13
+ ItemAdjustments.new(line_item).update
14
+ reload_totals
15
+ line_item
14
16
  end
15
17
 
16
- # Get current line item for variant
17
- # Remove variant qty from line_item
18
18
  def remove(variant, quantity = 1, shipment = nil)
19
- line_item = order.find_line_item_by_variant(variant)
19
+ line_item = remove_from_line_item(variant, quantity, shipment)
20
+ reload_totals
21
+ PromotionHandler::Cart.new(order, line_item).activate
22
+ ItemAdjustments.new(line_item).update
23
+ reload_totals
24
+ line_item
25
+ end
20
26
 
21
- unless line_item
22
- raise ActiveRecord::RecordNotFound, "Line item not found for variant #{variant.sku}"
27
+ def update_cart(params)
28
+ if order.update_attributes(params)
29
+ order.line_items = order.line_items.select {|li| li.quantity > 0 }
30
+ # Update totals, then check if the order is eligible for any cart promotions.
31
+ # If we do not update first, then the item total will be wrong and ItemTotal
32
+ # promotion rules would not be triggered.
33
+ reload_totals
34
+ PromotionHandler::Cart.new(order).activate
35
+ order.ensure_updated_shipments
36
+ reload_totals
37
+ true
38
+ else
39
+ false
23
40
  end
24
-
25
- remove_from_line_item(line_item, variant, quantity, shipment)
26
41
  end
27
42
 
28
43
  private
44
+ def order_updater
45
+ @updater ||= OrderUpdater.new(order)
46
+ end
29
47
 
30
- def add_to_line_item(line_item, variant, quantity, currency=nil, shipment=nil)
31
- if line_item
32
- line_item.target_shipment = shipment
33
- line_item.quantity += quantity.to_i
34
- line_item.currency = currency unless currency.nil?
35
- else
36
- line_item = order.line_items.new(quantity: quantity, variant: variant)
37
- line_item.target_shipment = shipment
38
- if currency
48
+ def reload_totals
49
+ order_updater.update_item_count
50
+ order_updater.update_item_total
51
+ order_updater.update_adjustment_total
52
+ order_updater.persist_totals
53
+ order.reload
54
+ end
55
+
56
+ def add_to_line_item(variant, quantity, currency=nil, shipment=nil)
57
+ line_item = grab_line_item_by_variant(variant)
58
+
59
+ if line_item
60
+ line_item.target_shipment = shipment
61
+ line_item.quantity += quantity.to_i
39
62
  line_item.currency = currency unless currency.nil?
40
- line_item.price = variant.price_in(currency).amount
41
63
  else
42
- line_item.price = variant.price
64
+ line_item = order.line_items.new(quantity: quantity, variant: variant)
65
+ line_item.target_shipment = shipment
66
+ if currency
67
+ line_item.currency = currency
68
+ line_item.price = variant.price_in(currency).amount
69
+ else
70
+ line_item.price = variant.price
71
+ end
43
72
  end
73
+
74
+ line_item.save
75
+ line_item
44
76
  end
45
77
 
46
- line_item.save
47
- order.reload
48
- line_item
49
- end
78
+ def remove_from_line_item(variant, quantity, shipment=nil)
79
+ line_item = grab_line_item_by_variant(variant, true)
80
+ line_item.quantity += -quantity
81
+ line_item.target_shipment= shipment
50
82
 
51
- def remove_from_line_item(line_item, variant, quantity, shipment=nil)
52
- line_item.quantity += -quantity
53
- line_item.target_shipment= shipment
83
+ if line_item.quantity == 0
84
+ line_item.destroy
85
+ else
86
+ line_item.save!
87
+ end
54
88
 
55
- if line_item.quantity == 0
56
- Spree::OrderInventory.new(order).verify(line_item, shipment)
57
- line_item.destroy
58
- else
59
- line_item.save!
89
+ line_item
60
90
  end
61
91
 
62
- order.reload
63
- line_item
64
- end
92
+ def grab_line_item_by_variant(variant, raise_error = false)
93
+ line_item = order.find_line_item_by_variant(variant)
65
94
 
95
+ if !line_item.present? && raise_error
96
+ raise ActiveRecord::RecordNotFound, "Line item not found for variant #{variant.sku}"
97
+ end
98
+
99
+ line_item
100
+ end
66
101
  end
67
102
  end
@@ -1,9 +1,11 @@
1
1
  module Spree
2
2
  class OrderInventory
3
- attr_accessor :order
3
+ attr_accessor :order, :line_item, :variant
4
4
 
5
- def initialize(order)
5
+ def initialize(order, line_item)
6
6
  @order = order
7
+ @line_item = line_item
8
+ @variant = line_item.variant
7
9
  end
8
10
 
9
11
  # Only verify inventory for completed orders (as orders in frontend checkout
@@ -13,98 +15,93 @@ module Spree
13
15
  # In case shipment is passed the stock location should only unstock or
14
16
  # restock items if the order is completed. That is so because stock items
15
17
  # are always unstocked when the order is completed through +shipment.finalize+
16
- def verify(line_item, shipment = nil)
18
+ def verify(shipment = nil)
17
19
  if order.completed? || shipment.present?
18
20
 
19
- variant_units = inventory_units_for(line_item.variant)
21
+ if inventory_units.size < line_item.quantity
22
+ quantity = line_item.quantity - inventory_units.size
20
23
 
21
- if variant_units.size < line_item.quantity
22
- quantity = line_item.quantity - variant_units.size
23
-
24
- shipment = determine_target_shipment(line_item.variant) unless shipment
25
- add_to_shipment(shipment, line_item.variant, quantity)
26
- elsif variant_units.size > line_item.quantity
27
- remove(line_item, variant_units, shipment)
24
+ shipment = determine_target_shipment unless shipment
25
+ add_to_shipment(shipment, quantity)
26
+ elsif inventory_units.size > line_item.quantity
27
+ remove(inventory_units, shipment)
28
28
  end
29
- else
30
- true
31
29
  end
32
30
  end
33
31
 
34
- def inventory_units_for(variant)
35
- units = order.shipments.collect{|s| s.inventory_units.to_a}.flatten
36
- units.group_by(&:variant_id)[variant.id] || []
32
+ def inventory_units
33
+ line_item.inventory_units
37
34
  end
38
35
 
39
36
  private
40
- def remove(line_item, variant_units, shipment = nil)
41
- quantity = variant_units.size - line_item.quantity
42
-
43
- if shipment.present?
44
- remove_from_shipment(shipment, line_item.variant, quantity)
45
- else
46
- order.shipments.each do |shipment|
47
- break if quantity == 0
48
- quantity -= remove_from_shipment(shipment, line_item.variant, quantity)
37
+ def remove(item_units, shipment = nil)
38
+ quantity = item_units.size - line_item.quantity
39
+
40
+ if shipment.present?
41
+ remove_from_shipment(shipment, quantity)
42
+ else
43
+ order.shipments.each do |shipment|
44
+ break if quantity == 0
45
+ quantity -= remove_from_shipment(shipment, quantity)
46
+ end
49
47
  end
50
48
  end
51
- end
52
49
 
53
- # Returns either one of the shipment:
54
- #
55
- # first unshipped that already includes this variant
56
- # first unshipped that's leaving from a stock_location that stocks this variant
57
- def determine_target_shipment(variant)
58
- shipment = order.shipments.detect do |shipment|
59
- (shipment.ready? || shipment.pending?) && shipment.include?(variant)
60
- end
50
+ # Returns either one of the shipment:
51
+ #
52
+ # first unshipped that already includes this variant
53
+ # first unshipped that's leaving from a stock_location that stocks this variant
54
+ def determine_target_shipment
55
+ shipment = order.shipments.detect do |shipment|
56
+ shipment.ready_or_pending? && shipment.include?(variant)
57
+ end
61
58
 
62
- shipment ||= order.shipments.detect do |shipment|
63
- (shipment.ready? || shipment.pending?) && variant.stock_location_ids.include?(shipment.stock_location_id)
59
+ shipment ||= order.shipments.detect do |shipment|
60
+ shipment.ready_or_pending? && variant.stock_location_ids.include?(shipment.stock_location_id)
61
+ end
64
62
  end
65
- end
66
63
 
67
- def add_to_shipment(shipment, variant, quantity)
68
- if variant.should_track_inventory?
69
- on_hand, back_order = shipment.stock_location.fill_status(variant, quantity)
64
+ def add_to_shipment(shipment, quantity)
65
+ if variant.should_track_inventory?
66
+ on_hand, back_order = shipment.stock_location.fill_status(variant, quantity)
70
67
 
71
- on_hand.times { shipment.set_up_inventory('on_hand', variant, order) }
72
- back_order.times { shipment.set_up_inventory('backordered', variant, order) }
73
- else
74
- quantity.times { shipment.set_up_inventory('on_hand', variant, order) }
75
- end
68
+ on_hand.times { shipment.set_up_inventory('on_hand', variant, order, line_item) }
69
+ back_order.times { shipment.set_up_inventory('backordered', variant, order, line_item) }
70
+ else
71
+ quantity.times { shipment.set_up_inventory('on_hand', variant, order, line_item) }
72
+ end
76
73
 
77
- # adding to this shipment, and removing from stock_location
78
- if order.completed?
79
- shipment.stock_location.unstock(variant, quantity, shipment)
74
+ # adding to this shipment, and removing from stock_location
75
+ if order.completed?
76
+ shipment.stock_location.unstock(variant, quantity, shipment)
77
+ end
78
+
79
+ quantity
80
80
  end
81
81
 
82
- quantity
83
- end
82
+ def remove_from_shipment(shipment, quantity)
83
+ return 0 if quantity == 0 || shipment.shipped?
84
84
 
85
- def remove_from_shipment(shipment, variant, quantity)
86
- return 0 if quantity == 0 || shipment.shipped?
85
+ shipment_units = shipment.inventory_units_for_item(line_item, variant).reject do |variant_unit|
86
+ variant_unit.state == 'shipped'
87
+ end.sort_by(&:state)
87
88
 
88
- shipment_units = shipment.inventory_units_for(variant).reject do |variant_unit|
89
- variant_unit.state == 'shipped'
90
- end.sort_by(&:state)
89
+ removed_quantity = 0
91
90
 
92
- removed_quantity = 0
91
+ shipment_units.each do |inventory_unit|
92
+ break if removed_quantity == quantity
93
+ inventory_unit.destroy
94
+ removed_quantity += 1
95
+ end
93
96
 
94
- shipment_units.each do |inventory_unit|
95
- break if removed_quantity == quantity
96
- inventory_unit.destroy
97
- removed_quantity += 1
98
- end
97
+ shipment.destroy if shipment.inventory_units.count == 0
99
98
 
100
- shipment.destroy if shipment.inventory_units.count == 0
99
+ # removing this from shipment, and adding to stock_location
100
+ if order.completed?
101
+ shipment.stock_location.restock variant, removed_quantity, shipment
102
+ end
101
103
 
102
- # removing this from shipment, and adding to stock_location
103
- if order.completed?
104
- shipment.stock_location.restock variant, removed_quantity, shipment
104
+ removed_quantity
105
105
  end
106
-
107
- removed_quantity
108
- end
109
106
  end
110
107
  end
@@ -9,23 +9,9 @@ module Spree
9
9
  @errors = ActiveModel::Errors.new(self)
10
10
  end
11
11
 
12
- #
13
- # Parameters can be passed using the following possible parameter configurations:
14
- #
15
- # * Single variant/quantity pairing
16
- # +:variants => { variant_id => quantity }+
17
- #
18
- # * Multiple products at once
19
- # +:products => { product_id => variant_id, product_id => variant_id }, :quantity => quantity+
20
- def populate(from_hash)
21
- from_hash[:products].each do |product_id,variant_id|
22
- attempt_cart_add(variant_id, from_hash[:quantity])
23
- end if from_hash[:products]
24
-
25
- from_hash[:variants].each do |variant_id, quantity|
26
- attempt_cart_add(variant_id, quantity)
27
- end if from_hash[:variants]
28
-
12
+
13
+ def populate(variant_id, quantity)
14
+ attempt_cart_add(variant_id, quantity)
29
15
  valid?
30
16
  end
31
17
 
@@ -16,35 +16,23 @@ module Spree
16
16
  # associations try to save and then in turn try to call +update!+ again.)
17
17
  def update
18
18
  update_totals
19
-
20
19
  if order.completed?
21
20
  update_payment_state
22
-
23
- # give each of the shipments a chance to update themselves
24
- shipments.each { |shipment| shipment.update!(order) }
21
+ update_shipments
25
22
  update_shipment_state
26
23
  end
27
-
28
- update_adjustments
29
- # update totals a second time in case updated adjustments have an effect on the total
30
- update_totals
31
-
32
- order.update_columns({
33
- payment_state: order.payment_state,
34
- shipment_state: order.shipment_state,
35
- item_total: order.item_total,
36
- adjustment_total: order.adjustment_total,
37
- payment_total: order.payment_total,
38
- total: order.total
39
- })
40
-
41
24
  run_hooks
25
+ persist_totals
42
26
  end
43
27
 
44
28
  def run_hooks
45
29
  update_hooks.each { |hook| order.send hook }
46
30
  end
47
31
 
32
+ def recalculate_adjustments
33
+ adjustments.includes(:source).each { |adjustment| adjustment.update! order }
34
+ end
35
+
48
36
  # Updates the following Order total values:
49
37
  #
50
38
  # +payment_total+ The total value of all finalized Payments (NOTE: non-finalized Payments are excluded)
@@ -52,11 +40,61 @@ module Spree
52
40
  # +adjustment_total+ The total value of all adjustments (promotions, credits, etc.)
53
41
  # +total+ The so-called "order total." This is equivalent to +item_total+ plus +adjustment_total+.
54
42
  def update_totals
55
- order.payment_total = payments.completed.map(&:amount).sum
43
+ order.payment_total = payments.completed.sum(:amount)
44
+ update_item_total
45
+ update_shipment_total
46
+ update_adjustment_total
47
+ end
48
+
49
+
50
+ # give each of the shipments a chance to update themselves
51
+ def update_shipments
52
+ shipments.each { |shipment| shipment.update!(order) }
53
+ end
54
+
55
+ def update_shipment_total
56
+ order.shipment_total = shipments.sum("cost + promo_total")
57
+ update_order_total
58
+ end
59
+
60
+ def update_order_total
61
+ order.total = order.item_total + order.shipment_total + order.adjustment_total
62
+ end
63
+
64
+ def update_adjustment_total
65
+ recalculate_adjustments
66
+ order.adjustment_total = line_items.sum(:adjustment_total) +
67
+ shipments.sum(:adjustment_total) +
68
+ adjustments.eligible.sum(:amount)
69
+ order.included_tax_total = line_items.sum(:included_tax_total) + shipments.sum(:included_tax_total)
70
+ order.additional_tax_total = line_items.sum(:additional_tax_total) + shipments.sum(:additional_tax_total)
71
+
72
+ update_order_total
73
+ end
74
+
75
+ def update_item_count
76
+ order.item_count = line_items.sum(:quantity)
77
+ end
78
+
79
+ def update_item_total
56
80
  order.item_total = line_items.map(&:amount).sum
57
- order.adjustment_total = adjustments.eligible.map(&:amount).sum
58
- order.tax_total = order.all_adjustments.tax.map(&:amount).sum
59
- order.total = order.item_total + order.adjustment_total
81
+ update_order_total
82
+ end
83
+
84
+ def persist_totals
85
+ order.update_columns(
86
+ payment_state: order.payment_state,
87
+ shipment_state: order.shipment_state,
88
+ item_total: order.item_total,
89
+ item_count: order.item_count,
90
+ adjustment_total: order.adjustment_total,
91
+ included_tax_total: order.included_tax_total,
92
+ additional_tax_total: order.additional_tax_total,
93
+ payment_total: order.payment_total,
94
+ shipment_total: order.shipment_total,
95
+ total: order.total,
96
+ updated_at: Time.now,
97
+ )
60
98
  end
61
99
 
62
100
  # Updates the +shipment_state+ attribute according to the following logic:
@@ -90,6 +128,7 @@ module Spree
90
128
  end
91
129
 
92
130
  order.state_changed('shipment')
131
+ order.shipment_state
93
132
  end
94
133
 
95
134
  # Updates the +payment_state+ attribute according to the following logic:
@@ -107,6 +146,8 @@ module Spree
107
146
  if payments.present?
108
147
  if payments.last.state == 'failed'
109
148
  order.payment_state = 'failed'
149
+ elsif payments.last.state == 'checkout'
150
+ order.payment_state = 'pending'
110
151
  elsif payments.last.state == 'completed'
111
152
  order.payment_state = 'credit_owed'
112
153
  else
@@ -124,30 +165,8 @@ module Spree
124
165
  order.state_changed('payment')
125
166
  end
126
167
 
127
- # Updates each of the Order adjustments.
128
- #
129
- # This is intended to be called from an Observer so that the Order can
130
- # respond to external changes to LineItem, Shipment, other Adjustments, etc.
131
- #
132
- # Adjustments will check if they are still eligible. Ineligible adjustments
133
- # are preserved but not counted towards adjustment_total.
134
- def update_adjustments
135
- order.adjustments.reload.each { |adjustment| adjustment.update!(order) }
136
- choose_best_promotion_adjustment
137
- end
138
-
139
168
  private
140
169
 
141
- # Picks one (and only one) promotion to be eligible for this order
142
- # This promotion provides the most discount, and if two promotions
143
- # have the same amount, then it will pick the latest one.
144
- def choose_best_promotion_adjustment
145
- if best_promotion_adjustment = order.adjustments.promotion.eligible.reorder("amount ASC, created_at DESC").first
146
- other_promotions = order.adjustments.promotion.where("id NOT IN (?)", best_promotion_adjustment.id)
147
- other_promotions.update_all(eligible: false)
148
- end
149
- end
150
-
151
170
  def round_money(n)
152
171
  (n * 100).round / 100.0
153
172
  end