solidus_core 2.0.3 → 2.1.0.beta1

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +62 -3
  3. data/app/assets/javascripts/spree.js.coffee.erb +4 -1
  4. data/app/helpers/spree/base_helper.rb +7 -48
  5. data/app/models/spree/address.rb +5 -1
  6. data/app/models/spree/adjustment.rb +3 -3
  7. data/app/models/spree/app_configuration.rb +13 -0
  8. data/app/models/spree/calculator.rb +3 -2
  9. data/app/models/spree/calculator/default_tax.rb +6 -10
  10. data/app/models/spree/calculator/flat_percent_item_total.rb +0 -4
  11. data/app/models/spree/calculator/flat_rate.rb +0 -4
  12. data/app/models/spree/calculator/flexi_rate.rb +0 -4
  13. data/app/models/spree/calculator/free_shipping.rb +0 -3
  14. data/app/models/spree/calculator/percent_on_line_item.rb +0 -4
  15. data/app/models/spree/calculator/percent_per_item.rb +0 -4
  16. data/app/models/spree/calculator/price_sack.rb +0 -4
  17. data/app/models/spree/calculator/returns/default_refund_amount.rb +0 -3
  18. data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +0 -4
  19. data/app/models/spree/calculator/shipping/flat_rate.rb +0 -4
  20. data/app/models/spree/calculator/shipping/flexi_rate.rb +0 -4
  21. data/app/models/spree/calculator/shipping/per_item.rb +0 -4
  22. data/app/models/spree/calculator/shipping/price_sack.rb +0 -4
  23. data/app/models/spree/calculator/tiered_flat_rate.rb +0 -4
  24. data/app/models/spree/calculator/tiered_percent.rb +0 -4
  25. data/app/models/spree/credit_card.rb +27 -14
  26. data/app/models/spree/gateway.rb +4 -0
  27. data/app/models/spree/inventory_unit.rb +2 -0
  28. data/app/models/spree/line_item.rb +31 -26
  29. data/app/models/spree/option_type.rb +0 -3
  30. data/app/models/spree/order.rb +28 -31
  31. data/app/models/spree/order/checkout.rb +0 -2
  32. data/app/models/spree/order_contents.rb +0 -45
  33. data/app/models/spree/order_merger.rb +6 -6
  34. data/app/models/spree/order_update_attributes.rb +0 -2
  35. data/app/models/spree/order_updater.rb +91 -13
  36. data/app/models/spree/payment.rb +9 -2
  37. data/app/models/spree/payment/processing.rb +15 -9
  38. data/app/models/spree/payment_method.rb +48 -5
  39. data/app/models/spree/price.rb +7 -9
  40. data/app/models/spree/product.rb +1 -25
  41. data/app/models/spree/promotion.rb +22 -14
  42. data/app/models/spree/promotion/actions/create_adjustment.rb +12 -1
  43. data/app/models/spree/promotion/actions/create_item_adjustments.rb +15 -1
  44. data/app/models/spree/promotion/actions/create_quantity_adjustments.rb +5 -3
  45. data/app/models/spree/promotion/actions/free_shipping.rb +14 -0
  46. data/app/models/spree/promotion/rules/taxon.rb +7 -2
  47. data/app/models/spree/promotion/rules/user_role.rb +43 -0
  48. data/app/models/spree/promotion_action.rb +19 -2
  49. data/app/models/spree/promotion_handler/coupon.rb +1 -4
  50. data/app/models/spree/promotion_handler/free_shipping.rb +22 -17
  51. data/app/models/spree/promotion_rule_role.rb +6 -0
  52. data/app/models/spree/property.rb +0 -3
  53. data/app/models/spree/return_authorization.rb +2 -0
  54. data/app/models/spree/shipment.rb +5 -21
  55. data/app/models/spree/shipping_method.rb +23 -2
  56. data/app/models/spree/shipping_rate.rb +3 -0
  57. data/app/models/spree/stock/estimator.rb +1 -1
  58. data/app/models/spree/stock_location.rb +3 -0
  59. data/app/models/spree/store.rb +7 -0
  60. data/app/models/spree/tax/item_adjuster.rb +27 -12
  61. data/app/models/spree/tax/order_adjuster.rb +2 -5
  62. data/app/models/spree/tax/tax_helpers.rb +4 -8
  63. data/app/models/spree/tax_rate.rb +1 -15
  64. data/app/models/spree/taxon.rb +0 -3
  65. data/app/models/spree/transfer_item.rb +1 -1
  66. data/app/models/spree/user_class_handle.rb +14 -9
  67. data/app/models/spree/variant/pricing_options.rb +1 -1
  68. data/app/models/spree/wallet/add_payment_sources_to_wallet.rb +1 -1
  69. data/app/models/spree/zone.rb +20 -13
  70. data/config/locales/en.yml +144 -62
  71. data/db/migrate/20120831092320_spree_one_two.rb +0 -7
  72. data/db/migrate/20150723224133_remove_unnecessary_indexes.rb +0 -2
  73. data/db/migrate/20160924135758_remove_is_default_from_prices.rb +5 -0
  74. data/db/migrate/20161009141333_remove_currency_from_line_items.rb +5 -0
  75. data/db/migrate/20161014221052_add_available_to_columns_and_remove_display_on_from_payment_methods.rb +28 -0
  76. data/db/migrate/20161123154034_add_available_to_users_and_remove_display_on_from_shipping_methods.rb +20 -0
  77. data/lib/generators/spree/custom_user/templates/authentication_helpers.rb.tt +4 -0
  78. data/lib/generators/spree/dummy/dummy_generator.rb +0 -2
  79. data/lib/spree/core.rb +0 -5
  80. data/lib/spree/core/controller_helpers/pricing.rb +2 -1
  81. data/lib/spree/core/engine.rb +14 -0
  82. data/lib/spree/core/version.rb +1 -1
  83. data/lib/spree/deprecation.rb +1 -1
  84. data/lib/spree/localized_number.rb +3 -2
  85. data/lib/spree/permission_sets/configuration_display.rb +0 -1
  86. data/lib/spree/permission_sets/configuration_management.rb +0 -1
  87. data/lib/spree/permission_sets/product_display.rb +0 -1
  88. data/lib/spree/permission_sets/product_management.rb +0 -1
  89. data/lib/spree/permission_sets/user_management.rb +2 -4
  90. data/lib/spree/permitted_attributes.rb +3 -2
  91. data/lib/spree/testing_support/capybara_ext.rb +0 -12
  92. data/lib/spree/testing_support/factories/address_factory.rb +1 -1
  93. data/lib/spree/testing_support/factories/line_item_factory.rb +0 -1
  94. data/lib/spree/testing_support/factories/payment_factory.rb +4 -0
  95. data/lib/spree/testing_support/factories/payment_method_factory.rb +8 -1
  96. data/lib/spree/testing_support/factories/user_factory.rb +2 -2
  97. data/solidus_core.gemspec +4 -3
  98. data/spec/helpers/base_helper_spec.rb +0 -40
  99. data/spec/lib/spree/core/controller_helpers/pricing_spec.rb +16 -0
  100. data/spec/lib/spree/core/importer/order_spec.rb +27 -18
  101. data/spec/lib/spree/core/price_migrator_spec.rb +3 -1
  102. data/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb +16 -0
  103. data/spec/lib/spree/core/unreturned_item_charger_spec.rb +0 -2
  104. data/spec/lib/tasks/exchanges_spec.rb +4 -2
  105. data/spec/lib/tasks/migrations/create_vat_prices_spec.rb +5 -3
  106. data/spec/models/spree/adjustment_spec.rb +136 -0
  107. data/spec/models/spree/calculator/default_tax_spec.rb +13 -7
  108. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +3 -0
  109. data/spec/models/spree/calculator/flat_rate_spec.rb +3 -0
  110. data/spec/models/spree/calculator/flexi_rate_spec.rb +3 -0
  111. data/spec/models/spree/calculator/free_shipping_spec.rb +6 -0
  112. data/spec/models/spree/calculator/percent_on_line_item_spec.rb +9 -4
  113. data/spec/models/spree/calculator/percent_per_item_spec.rb +10 -0
  114. data/spec/models/spree/calculator/price_sack_spec.rb +3 -0
  115. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +3 -0
  116. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +3 -0
  117. data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +3 -0
  118. data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +3 -0
  119. data/spec/models/spree/calculator/shipping/per_item_spec.rb +3 -0
  120. data/spec/models/spree/calculator/shipping/price_sack_spec.rb +4 -1
  121. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +3 -0
  122. data/spec/models/spree/calculator/tiered_percent_spec.rb +3 -0
  123. data/spec/models/spree/credit_card_spec.rb +27 -1
  124. data/spec/models/spree/line_item_spec.rb +58 -65
  125. data/spec/models/spree/order/checkout_spec.rb +2 -1
  126. data/spec/models/spree/order/payment_spec.rb +9 -10
  127. data/spec/models/spree/order/tax_spec.rb +22 -7
  128. data/spec/models/spree/order/updating_spec.rb +1 -3
  129. data/spec/models/spree/order_cancellations_spec.rb +6 -4
  130. data/spec/models/spree/order_contents_spec.rb +34 -50
  131. data/spec/models/spree/order_inventory_spec.rb +3 -5
  132. data/spec/models/spree/order_merger_spec.rb +20 -0
  133. data/spec/models/spree/order_spec.rb +28 -64
  134. data/spec/models/spree/order_update_attributes_spec.rb +1 -5
  135. data/spec/models/spree/order_updater_spec.rb +251 -0
  136. data/spec/models/spree/payment_method_spec.rb +178 -28
  137. data/spec/models/spree/payment_spec.rb +35 -19
  138. data/spec/models/spree/permission_sets/configuration_display.rb +0 -4
  139. data/spec/models/spree/permission_sets/configuration_management_spec.rb +0 -2
  140. data/spec/models/spree/permission_sets/product_display_spec.rb +0 -4
  141. data/spec/models/spree/permission_sets/product_management_spec.rb +0 -2
  142. data/spec/models/spree/permission_sets/user_management_spec.rb +9 -2
  143. data/spec/models/spree/price_spec.rb +16 -1
  144. data/spec/models/spree/product_spec.rb +0 -75
  145. data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +20 -0
  146. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +39 -15
  147. data/spec/models/spree/promotion/actions/create_quantity_adjustments_spec.rb +203 -22
  148. data/spec/models/spree/promotion/actions/free_shipping_spec.rb +22 -3
  149. data/spec/models/spree/promotion/rules/taxon_spec.rb +26 -0
  150. data/spec/models/spree/promotion/rules/user_role_spec.rb +86 -0
  151. data/spec/models/spree/promotion_action_spec.rb +38 -0
  152. data/spec/models/spree/promotion_handler/coupon_spec.rb +36 -33
  153. data/spec/models/spree/promotion_handler/free_shipping_spec.rb +21 -22
  154. data/spec/models/spree/promotion_spec.rb +46 -6
  155. data/spec/models/spree/reimbursement_spec.rb +1 -1
  156. data/spec/models/spree/reimbursement_tax_calculator_spec.rb +2 -2
  157. data/spec/models/spree/shipment_spec.rb +68 -50
  158. data/spec/models/spree/shipping_method_spec.rb +41 -0
  159. data/spec/models/spree/shipping_rate_spec.rb +9 -3
  160. data/spec/models/spree/stock/estimator_spec.rb +4 -2
  161. data/spec/models/spree/store_credit_spec.rb +3 -3
  162. data/spec/models/spree/tax/item_adjuster_spec.rb +31 -21
  163. data/spec/models/spree/tax/order_adjuster_spec.rb +6 -10
  164. data/spec/models/spree/tax/taxation_integration_spec.rb +19 -0
  165. data/spec/models/spree/tax_rate_spec.rb +5 -26
  166. data/spec/models/spree/transfer_item_spec.rb +11 -0
  167. data/spec/models/spree/variant/pricing_options_spec.rb +7 -17
  168. data/spec/models/spree/variant_spec.rb +2 -4
  169. data/spec/models/spree/zone_spec.rb +60 -20
  170. data/spec/shared_examples/calculator_shared_examples.rb +8 -0
  171. metadata +19 -24
  172. data/app/models/spree/item_adjustments.rb +0 -89
  173. data/app/models/spree/option_type_prototype.rb +0 -6
  174. data/app/models/spree/property_prototype.rb +0 -6
  175. data/app/models/spree/prototype.rb +0 -14
  176. data/app/models/spree/prototype_taxon.rb +0 -6
  177. data/app/models/spree/tracker.rb +0 -8
  178. data/db/migrate/20150128032538_remove_environment_from_tracker.rb +0 -6
  179. data/lib/generators/spree/dummy/templates/initializers/custom_user.rb +0 -1
  180. data/lib/spree/core/delegate_belongs_to.rb +0 -94
  181. data/lib/spree/testing_support/factories/prototype_factory.rb +0 -8
  182. data/lib/spree/testing_support/factories/tracker_factory.rb +0 -6
  183. data/spec/lib/spree/core/delegate_belongs_to_spec.rb +0 -24
  184. data/spec/lib/spree/core/testing_support/factories/prototype_factory_spec.rb +0 -12
  185. data/spec/lib/spree/core/testing_support/factories/tracker_factory_spec.rb +0 -12
  186. data/spec/models/spree/item_adjustments_spec.rb +0 -306
  187. data/spec/models/spree/tracker_spec.rb +0 -21
@@ -1,11 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Spree::Order, type: :model do
4
- let(:order) { stub_model(Spree::Order) }
4
+ let(:order) { create(:order) }
5
5
 
6
6
  context "#update!" do
7
- let(:line_items) { [mock_model(Spree::LineItem, amount: 5)] }
8
-
9
7
  context "when there are update hooks" do
10
8
  before { Spree::Order.register_update_hook :foo }
11
9
  after { Spree::Order.update_hooks.clear }
@@ -134,17 +134,19 @@ describe Spree::OrderCancellations do
134
134
  let(:line_item) { order.line_items.to_a.first }
135
135
  let(:inventory_unit_1) { line_item.inventory_units[0] }
136
136
  let(:inventory_unit_2) { line_item.inventory_units[1] }
137
+ let(:promotion) { create(:promotion, :with_line_item_adjustment) }
138
+ let(:promotion_action) { promotion.actions[0] }
137
139
 
138
140
  before do
139
141
  order.contents.add(line_item.variant)
140
142
 
141
143
  # make the total $1.67 so it divides unevenly
142
144
  line_item.adjustments.create!(
143
- source_type: 'Spree::TaxRate',
144
145
  order: order,
145
146
  amount: 0.01,
146
- label: 'some fake tax',
147
- finalized: true
147
+ label: 'some promo',
148
+ source: promotion_action,
149
+ finalized: true,
148
150
  )
149
151
  order.update!
150
152
  end
@@ -154,7 +156,7 @@ describe Spree::OrderCancellations do
154
156
  order.cancellations.short_ship([inventory_unit_2])
155
157
  expect(line_item.adjustments.map(&:amount)).to match_array(
156
158
  [
157
- 0.01, # tax adjustment
159
+ 0.01, # promo adjustment
158
160
  -0.84, # short ship 1
159
161
  -0.83, # short ship 2
160
162
  ]
@@ -2,12 +2,12 @@ require 'spec_helper'
2
2
 
3
3
  describe Spree::OrderContents, type: :model do
4
4
  let!(:store) { create :store }
5
- let(:order) { Spree::Order.create }
5
+ let(:order) { create(:order) }
6
6
  let(:variant) { create(:variant) }
7
7
  let!(:stock_location) { variant.stock_locations.first }
8
8
  let(:stock_location_2) { create(:stock_location) }
9
9
 
10
- subject { described_class.new(order) }
10
+ subject(:order_contents) { described_class.new(order) }
11
11
 
12
12
  context "#add" do
13
13
  context 'given quantity is not explicitly provided' do
@@ -96,6 +96,38 @@ describe Spree::OrderContents, type: :model do
96
96
  include_context "discount changes order total"
97
97
  end
98
98
  end
99
+
100
+ describe 'tax calculations' do
101
+ let!(:zone) { create(:global_zone) }
102
+ let!(:tax_rate) do
103
+ create(:tax_rate, zone: zone, tax_category: variant.tax_category)
104
+ end
105
+
106
+ context 'when the order has a taxable address' do
107
+ before do
108
+ expect(order.tax_address.country_id).to be_present
109
+ end
110
+
111
+ it 'creates a tax adjustment' do
112
+ order_contents.add(variant)
113
+ line_item = order.find_line_item_by_variant(variant)
114
+ expect(line_item.adjustments.tax.count).to eq(1)
115
+ end
116
+ end
117
+
118
+ context 'when the order does not have a taxable address' do
119
+ before do
120
+ order.update_attributes!(ship_address: nil, bill_address: nil)
121
+ expect(order.tax_address.country_id).to be_nil
122
+ end
123
+
124
+ it 'creates a tax adjustment' do
125
+ order_contents.add(variant)
126
+ line_item = order.find_line_item_by_variant(variant)
127
+ expect(line_item.adjustments.tax.count).to eq(0)
128
+ end
129
+ end
130
+ end
99
131
  end
100
132
 
101
133
  context "#remove" do
@@ -224,54 +256,6 @@ describe Spree::OrderContents, type: :model do
224
256
  }.to change { subject.order.total }
225
257
  end
226
258
 
227
- context "given an order with existing addresses" do
228
- let(:default_address) { create :address, state_code: "NY", zipcode: "17402" }
229
- let(:order_with_address ) { create :order, ship_address: default_address, bill_address: default_address }
230
-
231
- subject { described_class.new(order_with_address) }
232
-
233
- context "when an address in a potentially different tax zone is supplied " do
234
- let(:updated_address) { build :address, state_code: "AL", zipcode: "64092" }
235
-
236
- let(:params) do
237
- { ship_address_attributes: updated_address.value_attributes, bill_address_attributes: updated_address.value_attributes }
238
- end
239
-
240
- it "updates tax adjustments" do
241
- expect(subject.order).to receive(:create_tax_charge!)
242
- subject.update_cart params
243
- end
244
- end
245
-
246
- context "when an address in potentially the same tax zone is supplied" do
247
- let(:updated_address) { build :address, state_code: "NY", zipcode: "17402", firstname: 'Robert' }
248
-
249
- let(:params) do
250
- { ship_address_attributes: updated_address.value_attributes, bill_address_attributes: updated_address.value_attributes }
251
- end
252
-
253
- it "does not updates tax adjustments" do
254
- expect(subject.order).not_to receive(:create_tax_charge!)
255
- subject.update_cart params
256
- end
257
- end
258
- end
259
-
260
- context "given an order with no existing addresses" do
261
- context "when an address is supplied" do
262
- let(:updated_address) { build :address, state_code: "CA", zipcode: "14902" }
263
-
264
- let(:params) do
265
- { ship_address_attributes: updated_address.attributes, bill_address_attributes: updated_address.value_attributes }
266
- end
267
-
268
- it "does not updates tax adjustments" do
269
- expect(subject.order).not_to receive(:create_tax_charge!)
270
- subject.update_cart params
271
- end
272
- end
273
- end
274
-
275
259
  context "submits item quantity 0" do
276
260
  let(:params) do
277
261
  { line_items_attributes: {
@@ -201,12 +201,10 @@ describe Spree::OrderInventory, type: :model do
201
201
  end
202
202
 
203
203
  it 'should destroy self if not inventory units remain' do
204
- shipment.inventory_units[1...999].each(&:destroy)
205
- shipment.inventory_units.reload
204
+ allow(shipment.inventory_units).to receive_messages(count: 0)
205
+ expect(shipment).to receive(:destroy)
206
206
 
207
- expect {
208
- expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
209
- }.to change{ order.shipments.count }.from(1).to(0)
207
+ expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
210
208
  end
211
209
 
212
210
  context "inventory unit line item and variant points to different products" do
@@ -43,6 +43,26 @@ module Spree
43
43
  end
44
44
  end
45
45
 
46
+ context 'merging together two orders with multiple currencies line items' do
47
+ let(:order_2) { Spree::Order.create(currency: 'JPY') }
48
+ let(:variant_2) { create(:variant) }
49
+
50
+ before do
51
+ Spree::Price.create(variant: variant_2, amount: 10, currency: 'JPY')
52
+ order_1.contents.add(variant, 1)
53
+ order_2.contents.add(variant_2.reload, 1)
54
+ end
55
+
56
+ it 'rejects other order line items' do
57
+ subject.merge!(order_2, user)
58
+ expect(order_1.line_items.count).to eq(1)
59
+
60
+ line_item = order_1.line_items.first
61
+ expect(line_item.quantity).to eq(1)
62
+ expect(line_item.variant_id).to eq(variant.id)
63
+ end
64
+ end
65
+
46
66
  context "merging using extension-specific line_item_comparison_hooks" do
47
67
  before do
48
68
  Spree::Order.register_line_item_comparison_hook(:foos_match)
@@ -1,11 +1,5 @@
1
1
  require 'spec_helper'
2
2
 
3
- class FakeCalculator < Spree::Calculator
4
- def compute(_computable)
5
- 5
6
- end
7
- end
8
-
9
3
  describe Spree::Order, type: :model do
10
4
  let(:store) { build_stubbed(:store) }
11
5
  let(:user) { stub_model(Spree::LegacyUser, email: "spree@example.com") }
@@ -516,7 +510,8 @@ describe Spree::Order, type: :model do
516
510
  payment_method = Spree::PaymentMethod.create!({
517
511
  name: "Fake",
518
512
  active: true,
519
- display_on: "front_end"
513
+ available_to_users: true,
514
+ available_to_admin: false
520
515
  })
521
516
  expect(order.available_payment_methods).to include(payment_method)
522
517
  end
@@ -525,16 +520,18 @@ describe Spree::Order, type: :model do
525
520
  payment_method = Spree::PaymentMethod.create!({
526
521
  name: "Fake",
527
522
  active: true,
528
- display_on: "both"
523
+ available_to_users: true,
524
+ available_to_admin: true
529
525
  })
530
526
  expect(order.available_payment_methods).to include(payment_method)
531
527
  end
532
528
 
533
- it "does not include a payment method twice if display_on is blank" do
529
+ it "does not include a payment method twice" do
534
530
  payment_method = Spree::PaymentMethod.create!({
535
531
  name: "Fake",
536
532
  active: true,
537
- display_on: "both"
533
+ available_to_users: true,
534
+ available_to_admin: true
538
535
  })
539
536
  expect(order.available_payment_methods.count).to eq(1)
540
537
  expect(order.available_payment_methods).to include(payment_method)
@@ -543,8 +540,10 @@ describe Spree::Order, type: :model do
543
540
  context "with more than one payment method" do
544
541
  subject { order.available_payment_methods }
545
542
 
546
- let!(:first_method) { FactoryGirl.create(:payment_method, display_on: :both) }
547
- let!(:second_method) { FactoryGirl.create(:payment_method, display_on: :both) }
543
+ let!(:first_method) { FactoryGirl.create(:payment_method, available_to_users: true,
544
+ available_to_admin: true) }
545
+ let!(:second_method) { FactoryGirl.create(:payment_method, available_to_users: true,
546
+ available_to_admin: true) }
548
547
 
549
548
  before do
550
549
  second_method.move_to_top
@@ -575,6 +574,23 @@ describe Spree::Order, type: :model do
575
574
  [payment_method_with_store]
576
575
  )
577
576
  end
577
+
578
+ context 'and the store has an extra payment method unavailable to users' do
579
+ let!(:admin_only_payment_method) do create(:payment_method,
580
+ available_to_users: false,
581
+ available_to_admin: true)
582
+ end
583
+
584
+ before do
585
+ store_with_payment_methods.payment_methods << admin_only_payment_method
586
+ end
587
+
588
+ it 'returns only the payment methods available to users for that store' do
589
+ expect(order.available_payment_methods).to match_array(
590
+ [payment_method_with_store]
591
+ )
592
+ end
593
+ end
578
594
  end
579
595
 
580
596
  context 'when the store does not have payment methods' do
@@ -1488,56 +1504,4 @@ describe Spree::Order, type: :model do
1488
1504
  end
1489
1505
  end
1490
1506
  end
1491
-
1492
- describe "#validate_payments_attributes" do
1493
- let(:attributes) { [ActionController::Parameters.new(payment_method_id: payment_method.id)] }
1494
- subject do
1495
- order.validate_payments_attributes(attributes)
1496
- end
1497
-
1498
- context "with empty array" do
1499
- let(:attributes) { [] }
1500
- it "doesn't error" do
1501
- subject
1502
- end
1503
- end
1504
-
1505
- context "with no payment method specified" do
1506
- let(:attributes) { [ActionController::Parameters.new({})] }
1507
- it "doesn't error" do
1508
- subject
1509
- end
1510
- end
1511
-
1512
- context "with valid payment method" do
1513
- let(:payment_method) { create(:check_payment_method) }
1514
- it "doesn't error" do
1515
- subject
1516
- end
1517
- end
1518
-
1519
- context "with inactive payment method" do
1520
- let(:payment_method) { create(:check_payment_method, active: false) }
1521
-
1522
- it "raises RecordNotFound" do
1523
- expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
1524
- end
1525
- end
1526
-
1527
- context "with unavailable payment method" do
1528
- let(:payment_method) { create(:check_payment_method, display_on: "back_end") }
1529
-
1530
- it "raises RecordNotFound" do
1531
- expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
1532
- end
1533
- end
1534
-
1535
- context "with soft-deleted payment method" do
1536
- let(:payment_method) { create(:check_payment_method, deleted_at: Time.current) }
1537
-
1538
- it "raises RecordNotFound" do
1539
- expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
1540
- end
1541
- end
1542
- end
1543
1507
  end
@@ -3,7 +3,6 @@ require 'spec_helper'
3
3
  module Spree
4
4
  RSpec.describe OrderUpdateAttributes do
5
5
  let(:order) { create(:order) }
6
- let(:payment_method) { create(:payment_method) }
7
6
  let(:request_env) { nil }
8
7
  let(:update) { described_class.new(order, attributes, request_env: request_env) }
9
8
 
@@ -26,10 +25,7 @@ module Spree
26
25
  let(:attributes) do
27
26
  {
28
27
  payments_attributes: [
29
- {
30
- payment_method_id: payment_method.id,
31
- source_attributes: attributes_for(:credit_card)
32
- }
28
+ { source_attributes: attributes_for(:credit_card) }
33
29
  ]
34
30
  }
35
31
  end
@@ -67,6 +67,257 @@ module Spree
67
67
  end
68
68
  end
69
69
 
70
+ describe '#recalculate_adjustments ' do
71
+ describe 'promotion recalculation' do
72
+ let(:order) { create(:order_with_line_items, line_items_count: 1, line_items_price: 10) }
73
+ let(:line_item) { order.line_items[0] }
74
+
75
+ context 'when the item quantity has changed' do
76
+ let(:promotion) { create(:promotion, promotion_actions: [promotion_action]) }
77
+ let(:promotion_action) { Spree::Promotion::Actions::CreateItemAdjustments.new(calculator: calculator) }
78
+ let(:calculator) { Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) }
79
+
80
+ before do
81
+ promotion.activate(order: order)
82
+ order.update!
83
+ line_item.update!(quantity: 2)
84
+ end
85
+
86
+ it 'updates the promotion amount' do
87
+ expect {
88
+ order.update!
89
+ }.to change {
90
+ line_item.promo_total
91
+ }.from(-1).to(-2)
92
+ end
93
+ end
94
+
95
+ context 'promotion chooser customization' do
96
+ before do
97
+ class Spree::TestPromotionChooser
98
+ def initialize(_adjustments)
99
+ raise 'Custom promotion chooser'
100
+ end
101
+ end
102
+
103
+ Spree::Config.promotion_chooser_class = Spree::TestPromotionChooser
104
+ end
105
+
106
+ it 'uses the defined promotion chooser' do
107
+ expect { order.update! }.to raise_error('Custom promotion chooser')
108
+ end
109
+ end
110
+
111
+ context 'default promotion chooser (best promotion is always applied)' do
112
+ let(:calculator) { Calculator::FlatRate.new(preferred_amount: 10) }
113
+
114
+ let(:source) do
115
+ Promotion::Actions::CreateItemAdjustments.create!(
116
+ calculator: calculator,
117
+ promotion: promotion,
118
+ )
119
+ end
120
+ let(:promotion) { create(:promotion) }
121
+
122
+ def create_adjustment(label, amount)
123
+ create(
124
+ :adjustment,
125
+ order: order,
126
+ adjustable: line_item,
127
+ source: source,
128
+ amount: amount,
129
+ finalized: true,
130
+ label: label,
131
+ )
132
+ end
133
+
134
+ it 'should make all but the most valuable promotion adjustment ineligible, leaving non promotion adjustments alone' do
135
+ create_adjustment('Promotion A', -100)
136
+ create_adjustment('Promotion B', -200)
137
+ create_adjustment('Promotion C', -300)
138
+ create(:adjustment, order: order,
139
+ adjustable: line_item,
140
+ source: nil,
141
+ amount: -500,
142
+ finalized: true,
143
+ label: 'Some other credit')
144
+ line_item.adjustments.each { |a| a.update_column(:eligible, true) }
145
+
146
+ order.update!
147
+
148
+ expect(line_item.adjustments.promotion.eligible.count).to eq(1)
149
+ expect(line_item.adjustments.promotion.eligible.first.label).to eq('Promotion C')
150
+ end
151
+
152
+ it 'should choose the most recent promotion adjustment when amounts are equal' do
153
+ # Using Timecop is a regression test
154
+ Timecop.freeze do
155
+ create_adjustment('Promotion A', -200)
156
+ create_adjustment('Promotion B', -200)
157
+ end
158
+ line_item.adjustments.each { |a| a.update_column(:eligible, true) }
159
+
160
+ order.update!
161
+
162
+ expect(line_item.adjustments.promotion.eligible.count).to eq(1)
163
+ expect(line_item.adjustments.promotion.eligible.first.label).to eq('Promotion B')
164
+ end
165
+
166
+ it 'should choose the most recent promotion adjustment when amounts are equal' do
167
+ # Using Timecop is a regression test
168
+ Timecop.freeze do
169
+ create_adjustment('Promotion A', -200)
170
+ create_adjustment('Promotion B', -200)
171
+ end
172
+ line_item.adjustments.each { |a| a.update_column(:eligible, true) }
173
+
174
+ order.update!
175
+
176
+ expect(line_item.adjustments.promotion.eligible.count).to eq(1)
177
+ expect(line_item.adjustments.promotion.eligible.first.label).to eq('Promotion B')
178
+ end
179
+
180
+ context 'when previously ineligible promotions become available' do
181
+ let(:order_promo1) { create(:promotion, :with_order_adjustment, :with_item_total_rule, weighted_order_adjustment_amount: 5, item_total_threshold_amount: 10) }
182
+ let(:order_promo2) { create(:promotion, :with_order_adjustment, :with_item_total_rule, weighted_order_adjustment_amount: 10, item_total_threshold_amount: 20) }
183
+ let(:order_promos) { [order_promo1, order_promo2] }
184
+ let(:line_item_promo1) { create(:promotion, :with_line_item_adjustment, :with_item_total_rule, adjustment_rate: 2.5, item_total_threshold_amount: 10, apply_automatically: true) }
185
+ let(:line_item_promo2) { create(:promotion, :with_line_item_adjustment, :with_item_total_rule, adjustment_rate: 5, item_total_threshold_amount: 20, apply_automatically: true) }
186
+ let(:line_item_promos) { [line_item_promo1, line_item_promo2] }
187
+ let(:order) { create(:order_with_line_items, line_items_count: 1) }
188
+
189
+ # Apply promotions in different sequences. Results should be the same.
190
+ promo_sequences = [
191
+ [0, 1],
192
+ [1, 0],
193
+ ]
194
+
195
+ promo_sequences.each do |promo_sequence|
196
+ context "with promo sequence #{promo_sequence}" do
197
+ it 'should pick the best order-level promo according to current eligibility' do
198
+ # apply both promos to the order, even though only promo1 is eligible
199
+ order_promos[promo_sequence[0]].activate order: order
200
+ order_promos[promo_sequence[1]].activate order: order
201
+
202
+ order.update!
203
+ order.reload
204
+ expect(order.all_adjustments.count).to eq(2), 'Expected two adjustments'
205
+ expect(order.all_adjustments.eligible.count).to eq(1), 'Expected one elegible adjustment'
206
+ expect(order.all_adjustments.eligible.first.source.promotion).to eq(order_promo1), 'Expected promo1 to be used'
207
+
208
+ order.contents.add create(:variant, price: 10), 1
209
+ order.save
210
+
211
+ order.reload
212
+ expect(order.all_adjustments.count).to eq(2), 'Expected two adjustments'
213
+ expect(order.all_adjustments.eligible.count).to eq(1), 'Expected one elegible adjustment'
214
+ expect(order.all_adjustments.eligible.first.source.promotion).to eq(order_promo2), 'Expected promo2 to be used'
215
+ end
216
+
217
+ it 'should pick the best line-item-level promo according to current eligibility' do
218
+ # apply both promos to the order, even though only promo1 is eligible
219
+ line_item_promos[promo_sequence[0]].activate order: order
220
+ line_item_promos[promo_sequence[1]].activate order: order
221
+
222
+ order.reload
223
+ expect(order.all_adjustments.count).to eq(1), 'Expected one adjustment'
224
+ expect(order.all_adjustments.eligible.count).to eq(1), 'Expected one elegible adjustment'
225
+ # line_item_promo1 is the only one that has thus far met the order total threshold, it is the only promo which should be applied.
226
+ expect(order.all_adjustments.first.source.promotion).to eq(line_item_promo1), 'Expected line_item_promo1 to be used'
227
+
228
+ order.contents.add create(:variant, price: 10), 1
229
+ order.save
230
+
231
+ order.reload
232
+ expect(order.all_adjustments.count).to eq(4), 'Expected four adjustments'
233
+ expect(order.all_adjustments.eligible.count).to eq(2), 'Expected two elegible adjustments'
234
+ order.all_adjustments.eligible.each do |adjustment|
235
+ expect(adjustment.source.promotion).to eq(line_item_promo2), 'Expected line_item_promo2 to be used'
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ context 'multiple adjustments and the best one is not eligible' do
243
+ let!(:promo_a) { create_adjustment('Promotion A', -100) }
244
+ let!(:promo_c) { create_adjustment('Promotion C', -300) }
245
+
246
+ before do
247
+ promo_a.update_column(:eligible, true)
248
+ promo_c.update_column(:eligible, false)
249
+ end
250
+
251
+ # regression for https://github.com/spree/spree/issues/3274
252
+ it 'still makes the previous best eligible adjustment valid' do
253
+ order.update!
254
+ expect(line_item.adjustments.promotion.eligible.first.label).to eq('Promotion A')
255
+ end
256
+ end
257
+
258
+ it 'should only leave one adjustment even if 2 have the same amount' do
259
+ create_adjustment('Promotion A', -100)
260
+ create_adjustment('Promotion B', -200)
261
+ create_adjustment('Promotion C', -200)
262
+
263
+ order.update!
264
+
265
+ expect(line_item.adjustments.promotion.eligible.count).to eq(1)
266
+ expect(line_item.adjustments.promotion.eligible.first.amount.to_i).to eq(-200)
267
+ end
268
+ end
269
+ end
270
+
271
+ describe 'tax recalculation' do
272
+ let!(:ship_address) { create(:address) }
273
+ let!(:tax_zone) { create(:global_zone) } # will include the above address
274
+ let!(:tax_rate) { create(:tax_rate, zone: tax_zone, tax_category: tax_category) }
275
+
276
+ let(:order) do
277
+ create(
278
+ :order_with_line_items,
279
+ line_items_attributes: [{ price: 10, variant: variant }],
280
+ ship_address: ship_address,
281
+ )
282
+ end
283
+ let(:line_item) { order.line_items[0] }
284
+
285
+ let(:variant) { create(:variant, tax_category: tax_category) }
286
+ let(:tax_category) { create(:tax_category) }
287
+
288
+ context 'when the item quantity has changed' do
289
+ before do
290
+ line_item.update!(quantity: 2)
291
+ end
292
+
293
+ it 'updates the promotion amount' do
294
+ expect {
295
+ order.update!
296
+ }.to change {
297
+ line_item.additional_tax_total
298
+ }.from(1).to(2)
299
+ end
300
+ end
301
+
302
+ context 'with a custom tax_adjuster_class' do
303
+ let(:custom_adjuster_class) { double }
304
+ let(:custom_adjuster_instance) { double }
305
+
306
+ before do
307
+ order # generate this first so we can expect it
308
+ Spree::Config.tax_adjuster_class = custom_adjuster_class
309
+ end
310
+
311
+ it 'uses the configured class' do
312
+ expect(custom_adjuster_class).to receive(:new).with(order).at_least(:once).and_return(custom_adjuster_instance)
313
+ expect(custom_adjuster_instance).to receive(:adjust!).at_least(:once)
314
+
315
+ order.update!
316
+ end
317
+ end
318
+ end
319
+ end
320
+
70
321
  context "updating shipment state" do
71
322
  before do
72
323
  allow(order).to receive_messages backordered?: false