solidus_core 2.1.1 → 2.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of solidus_core might be problematic. Click here for more details.

Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -1
  3. data/app/assets/config/solidus_core_manifest.js +1 -0
  4. data/app/assets/javascripts/spree.js.erb +72 -0
  5. data/app/helpers/spree/store_helper.rb +5 -0
  6. data/app/jobs/spree/promotion_code_batch_job.rb +24 -0
  7. data/app/mailers/spree/promotion_code_batch_mailer.rb +13 -0
  8. data/app/models/concerns/spree/calculated_adjustments.rb +1 -1
  9. data/app/models/concerns/spree/ordered_property_value_list.rb +2 -2
  10. data/app/models/concerns/spree/user_address_book.rb +4 -4
  11. data/app/models/concerns/spree/user_methods.rb +7 -0
  12. data/app/models/concerns/spree/user_payment_source.rb +12 -5
  13. data/app/models/spree/address.rb +14 -3
  14. data/app/models/spree/adjustment.rb +13 -1
  15. data/app/models/spree/app_configuration.rb +0 -19
  16. data/app/models/spree/base.rb +2 -0
  17. data/app/models/spree/credit_card.rb +34 -43
  18. data/app/models/spree/gateway/bogus.rb +1 -1
  19. data/app/models/spree/gateway.rb +6 -4
  20. data/app/models/spree/inventory_unit.rb +3 -2
  21. data/app/models/spree/order/checkout.rb +187 -273
  22. data/app/models/spree/order.rb +137 -71
  23. data/app/models/spree/order_contents.rb +1 -1
  24. data/app/models/spree/order_inventory.rb +11 -11
  25. data/app/models/spree/order_promotion.rb +2 -0
  26. data/app/models/spree/order_update_attributes.rb +1 -8
  27. data/app/models/spree/order_updater.rb +67 -63
  28. data/app/models/spree/payment.rb +0 -1
  29. data/app/models/spree/payment_create.rb +27 -7
  30. data/app/models/spree/payment_method/store_credit.rb +3 -3
  31. data/app/models/spree/payment_method.rb +4 -1
  32. data/app/models/spree/payment_source.rb +45 -0
  33. data/app/models/spree/product/scopes.rb +24 -24
  34. data/app/models/spree/product.rb +4 -4
  35. data/app/models/spree/promotion.rb +2 -0
  36. data/app/models/spree/promotion_code/batch_builder.rb +63 -0
  37. data/app/models/spree/promotion_code.rb +1 -0
  38. data/app/models/spree/promotion_code_batch.rb +25 -0
  39. data/app/models/spree/promotion_handler/cart.rb +2 -2
  40. data/app/models/spree/promotion_handler/coupon.rb +1 -2
  41. data/app/models/spree/promotion_handler/free_shipping.rb +32 -21
  42. data/app/models/spree/promotion_handler/page.rb +1 -1
  43. data/app/models/spree/reimbursement.rb +1 -1
  44. data/app/models/spree/return_authorization.rb +0 -28
  45. data/app/models/spree/return_item.rb +1 -1
  46. data/app/models/spree/shipment.rb +4 -4
  47. data/app/models/spree/shipping_method.rb +2 -2
  48. data/app/models/spree/shipping_rate.rb +1 -1
  49. data/app/models/spree/stock/availability_validator.rb +16 -17
  50. data/app/models/spree/stock/coordinator.rb +3 -3
  51. data/app/models/spree/stock/package.rb +1 -1
  52. data/app/models/spree/stock/quantifier.rb +5 -4
  53. data/app/models/spree/stock_location.rb +2 -2
  54. data/app/models/spree/store.rb +2 -2
  55. data/app/models/spree/store_credit.rb +1 -1
  56. data/app/models/spree/tax/tax_helpers.rb +3 -3
  57. data/app/models/spree/tax_rate.rb +7 -1
  58. data/app/models/spree/taxonomy.rb +1 -1
  59. data/app/models/spree/variant/scopes.rb +5 -5
  60. data/app/models/spree/variant/vat_price_generator.rb +8 -5
  61. data/app/models/spree/variant.rb +1 -0
  62. data/app/models/spree/wallet/add_payment_sources_to_wallet.rb +19 -10
  63. data/app/models/spree/wallet/default_payment_builder.rb +6 -6
  64. data/app/models/spree/wallet.rb +71 -0
  65. data/app/models/spree/wallet_payment_source.rb +17 -0
  66. data/app/models/spree/zone.rb +1 -1
  67. data/app/views/spree/carton_mailer/shipped_email.text.erb +1 -1
  68. data/app/views/spree/promotion_code_batch_mailer/promotion_code_batch_errored.text.erb +2 -0
  69. data/app/views/spree/promotion_code_batch_mailer/promotion_code_batch_finished.text.erb +2 -0
  70. data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +0 -7
  71. data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +0 -5
  72. data/app/views/spree/shared/_error_messages.html.erb +1 -1
  73. data/app/views/spree/shipment_mailer/shipped_email.html.erb +1 -1
  74. data/config/initializers/assets.rb +1 -1
  75. data/config/initializers/friendly_id.rb +1 -1
  76. data/config/locales/en.yml +50 -12
  77. data/db/default/spree/store_credit.rb +2 -1
  78. data/db/migrate/20130826062534_add_depth_to_spree_taxons.rb +4 -6
  79. data/db/migrate/20160420044191_create_spree_wallet_payment_sources.rb +23 -0
  80. data/db/migrate/20160420181916_migrate_credit_cards_to_wallet_payment_sources.rb +26 -0
  81. data/db/migrate/20161017102621_create_spree_promotion_code_batch.rb +36 -0
  82. data/db/migrate/20161129035810_add_index_to_spree_payments_number.rb +5 -0
  83. data/db/migrate/20170223235001_remove_spree_store_credits_column.rb +5 -0
  84. data/lib/generators/spree/dummy/templates/rails/application.rb +1 -1
  85. data/lib/generators/spree/dummy/templates/rails/test.rb +1 -1
  86. data/lib/generators/spree/install/install_generator.rb +6 -5
  87. data/lib/spree/core/controller_helpers/payment_parameters.rb +54 -0
  88. data/lib/spree/core/engine.rb +6 -9
  89. data/lib/spree/core/version.rb +1 -1
  90. data/lib/spree/core.rb +0 -1
  91. data/lib/spree/money.rb +18 -0
  92. data/lib/spree/permission_sets/default_customer.rb +1 -1
  93. data/lib/spree/permitted_attributes.rb +1 -1
  94. data/lib/spree/testing_support/authorization_helpers.rb +1 -0
  95. data/lib/spree/testing_support/capybara_ext.rb +13 -0
  96. data/lib/spree/testing_support/factories/order_factory.rb +5 -1
  97. data/lib/spree/testing_support/factories/payment_factory.rb +1 -1
  98. data/lib/spree/testing_support/factories/shipment_factory.rb +0 -1
  99. data/solidus_core.gemspec +3 -3
  100. data/spec/jobs/promotion_code_batch_job_spec.rb +65 -0
  101. data/spec/lib/calculated_adjustments_spec.rb +105 -1
  102. data/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb +4 -1
  103. data/spec/lib/spree/core/testing_support/factories/payment_factory_spec.rb +8 -0
  104. data/spec/lib/spree/money_spec.rb +32 -0
  105. data/spec/lib/spree/permission_sets/default_customer_spec.rb +20 -0
  106. data/spec/mailers/promotion_code_batch_mailer_spec.rb +45 -0
  107. data/spec/models/spree/credit_card_spec.rb +86 -86
  108. data/spec/models/spree/gateway_spec.rb +3 -1
  109. data/spec/models/spree/inventory_unit_spec.rb +12 -4
  110. data/spec/models/spree/order/checkout_spec.rb +11 -32
  111. data/spec/models/spree/order/tax_spec.rb +2 -2
  112. data/spec/models/spree/order_contents_spec.rb +24 -1
  113. data/spec/models/spree/order_inventory_spec.rb +130 -83
  114. data/spec/models/spree/order_spec.rb +15 -117
  115. data/spec/models/spree/order_update_attributes_spec.rb +1 -44
  116. data/spec/models/spree/order_updater_spec.rb +10 -13
  117. data/spec/models/spree/payment_create_spec.rb +5 -1
  118. data/spec/models/spree/payment_method_spec.rb +16 -0
  119. data/spec/models/spree/payment_spec.rb +14 -8
  120. data/spec/models/spree/promotion_code/batch_builder_spec.rb +61 -0
  121. data/spec/models/spree/promotion_code_batch_spec.rb +58 -0
  122. data/spec/models/spree/promotion_code_spec.rb +4 -0
  123. data/spec/models/spree/promotion_spec.rb +3 -6
  124. data/spec/models/spree/return_authorization_spec.rb +0 -59
  125. data/spec/models/spree/shipment_spec.rb +4 -4
  126. data/spec/models/spree/stock/availability_validator_spec.rb +64 -9
  127. data/spec/models/spree/tax/item_adjuster_spec.rb +1 -2
  128. data/spec/models/spree/unit_cancel_spec.rb +0 -85
  129. data/spec/models/spree/user_spec.rb +3 -1
  130. data/spec/models/spree/variant/vat_price_generator_spec.rb +8 -2
  131. data/spec/models/spree/variant_spec.rb +16 -4
  132. data/spec/models/spree/wallet_payment_source_spec.rb +46 -0
  133. data/spec/models/spree/wallet_spec.rb +128 -0
  134. data/spec/support/concerns/payment_source.rb +64 -0
  135. metadata +51 -25
  136. data/app/assets/javascripts/spree.js.coffee.erb +0 -64
  137. data/app/models/spree/promotion_builder.rb +0 -55
  138. data/app/models/spree/promotion_code/code_builder.rb +0 -62
  139. data/config/initializers/premailer_assets.rb +0 -1
  140. data/lib/spree/core/unreturned_item_charger.rb +0 -106
  141. data/lib/tasks/exchanges.rake +0 -47
  142. data/spec/lib/spree/core/unreturned_item_charger_spec.rb +0 -126
  143. data/spec/lib/tasks/exchanges_spec.rb +0 -220
  144. data/spec/models/spree/promotion_builder_spec.rb +0 -120
  145. data/spec/models/spree/promotion_code/code_builder_spec.rb +0 -77
@@ -67,6 +67,22 @@ describe Spree::PaymentMethod, type: :model do
67
67
 
68
68
  it { is_expected.to contain_exactly(payment_method_back_display, payment_method_both_display, payment_method_nil_display)}
69
69
  end
70
+
71
+ context "when searching for payment methods available to a store" do
72
+ subject { Spree::PaymentMethod.available_to_store(store) }
73
+
74
+ context "when the store is nil" do
75
+ let(:store) { nil }
76
+ it "raises an argument error" do
77
+ expect { subject }.to raise_error(ArgumentError, "You must provide a store")
78
+ end
79
+ end
80
+
81
+ context "when the store exists" do
82
+ let(:store) { create(:store, payment_methods: [payment_method_back_display]) }
83
+ it { is_expected.to contain_exactly(payment_method_back_display) }
84
+ end
85
+ end
70
86
  end
71
87
 
72
88
  describe ".available" do
@@ -653,13 +653,12 @@ describe Spree::Payment, type: :model do
653
653
  end
654
654
 
655
655
  context "completed orders" do
656
- let(:payment_method) { create(:check_payment_method) }
657
656
  before { allow(order).to receive_messages completed?: true }
658
657
 
659
658
  it "updates payment_state and shipments" do
660
659
  expect(order.updater).to receive(:update_payment_state)
661
660
  expect(order.updater).to receive(:update_shipment_state)
662
- Spree::Payment.create!(amount: 100, order: order, payment_method: payment_method)
661
+ Spree::Payment.create(amount: 100, order: order)
663
662
  end
664
663
  end
665
664
 
@@ -743,13 +742,12 @@ describe Spree::Payment, type: :model do
743
742
  end
744
743
 
745
744
  describe "invalidating payments updates in memory objects" do
746
- let(:payment_method) { create(:check_payment_method) }
747
745
  before do
748
- Spree::PaymentCreate.new(order, amount: 1, payment_method_id: payment_method.id).build.save!
746
+ Spree::PaymentCreate.new(order, amount: 1).build.save!
749
747
  expect(order.payments.map(&:state)).to contain_exactly(
750
748
  'checkout'
751
749
  )
752
- Spree::PaymentCreate.new(order, amount: 2, payment_method_id: payment_method.id).build.save!
750
+ Spree::PaymentCreate.new(order, amount: 2).build.save!
753
751
  end
754
752
 
755
753
  it 'should not have stale payments' do
@@ -808,11 +806,12 @@ describe Spree::Payment, type: :model do
808
806
  let(:order) { create(:order, user: user) }
809
807
  let(:user) { create(:user) }
810
808
  let!(:credit_card) { create(:credit_card, user_id: order.user_id) }
809
+ let!(:wallet_payment_source) { user.wallet.add(credit_card) }
811
810
 
812
811
  let(:params) do
813
812
  {
814
813
  source_attributes: {
815
- existing_card_id: credit_card.id,
814
+ wallet_payment_source_id: wallet_payment_source.id,
816
815
  verification_value: '321'
817
816
  }
818
817
  }
@@ -849,14 +848,21 @@ describe Spree::Payment, type: :model do
849
848
 
850
849
  context 'the credit card belongs to a different user' do
851
850
  let(:other_user) { create(:user) }
852
- before { credit_card.update!(user_id: other_user.id) }
851
+ before do
852
+ credit_card.update!(user_id: other_user.id)
853
+ user.wallet.remove(credit_card)
854
+ other_user.wallet.add(credit_card)
855
+ end
853
856
  it 'errors' do
854
857
  expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
855
858
  end
856
859
  end
857
860
 
858
861
  context 'the credit card has no user' do
859
- before { credit_card.update!(user_id: nil) }
862
+ before do
863
+ credit_card.update!(user_id: nil)
864
+ user.wallet.remove(credit_card)
865
+ end
860
866
  it 'errors' do
861
867
  expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
862
868
  end
@@ -0,0 +1,61 @@
1
+ require "spec_helper"
2
+
3
+ describe Spree::PromotionCode::BatchBuilder do
4
+ let(:promotion) { create(:promotion) }
5
+ let(:base_code) { "abc" }
6
+ let(:promotion_code_batch) do
7
+ Spree::PromotionCodeBatch.create!(
8
+ promotion_id: promotion.id,
9
+ base_code: base_code,
10
+ number_of_codes: 10,
11
+ email: "test@email.com"
12
+ )
13
+ end
14
+
15
+ subject { described_class.new promotion_code_batch }
16
+
17
+ describe "#build_promotion_codes" do
18
+ context "with a failed build" do
19
+ before do
20
+ allow(subject).to receive(:generate_random_codes).and_raise "Error"
21
+
22
+ expect { subject.build_promotion_codes }.to raise_error RuntimeError
23
+ end
24
+
25
+ it "updates the error and state on promotion batch" do
26
+ expect(promotion_code_batch.reload.error).to eq("#<RuntimeError: Error>")
27
+ expect(promotion_code_batch.reload.state).to eq("failed")
28
+ end
29
+ end
30
+ context "with a successful build" do
31
+ before do
32
+ allow(Spree::PromotionCodeBatchMailer)
33
+ .to receive(:promotion_code_batch_finished)
34
+ .and_call_original
35
+
36
+ subject.build_promotion_codes
37
+ end
38
+
39
+ it "update the promotion codes count for the batch" do
40
+ expect(promotion_code_batch.promotion_codes.count).to eq(10)
41
+ end
42
+
43
+ it "builds the correct number of codes" do
44
+ expect(subject.promotion.codes.size).to eq(10)
45
+ end
46
+
47
+ it "builds codes with distinct values" do
48
+ expect(subject.promotion.codes.map(&:value).uniq.size).to eq(10)
49
+ end
50
+
51
+ it "builds codes with the same base prefix" do
52
+ values = subject.promotion.codes.map(&:value)
53
+ expect(values.all? { |val| val.starts_with?("#{base_code}_") }).to be true
54
+ end
55
+
56
+ it "updates the promotion code batch state to completed" do
57
+ expect(promotion_code_batch.state).to eq("completed")
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ describe Spree::PromotionCodeBatch, type: :model do
4
+ include ActiveJob::TestHelper
5
+
6
+ subject do
7
+ described_class.create!(
8
+ promotion_id: create(:promotion).id,
9
+ base_code: "abc",
10
+ number_of_codes: 1,
11
+ error: nil,
12
+ email: "test@email.com"
13
+ )
14
+ end
15
+
16
+ describe "#process" do
17
+ context "with a pending code batch" do
18
+ it "should call the worker" do
19
+ ActiveJob::Base.queue_adapter = :test
20
+
21
+ expect { subject.process }
22
+ .to have_enqueued_job(Spree::PromotionCodeBatchJob)
23
+
24
+ clear_enqueued_jobs
25
+ end
26
+
27
+ it "should update the state to processing" do
28
+ subject.process
29
+
30
+ expect(subject.state).to eq("processing")
31
+ end
32
+ end
33
+
34
+ context "with a processing batch" do
35
+ before { subject.update_attribute(:state, "processing") }
36
+
37
+ it "should raise an error" do
38
+ expect{ subject.process }.to raise_error Spree::PromotionCodeBatch::CantProcessStartedBatch
39
+ end
40
+ end
41
+
42
+ context "with a completed batch" do
43
+ before { subject.update_attribute(:state, "completed") }
44
+
45
+ it "should raise an error" do
46
+ expect{ subject.process }.to raise_error Spree::PromotionCodeBatch::CantProcessStartedBatch
47
+ end
48
+ end
49
+
50
+ context "with a failed batch" do
51
+ before { subject.update_attribute(:state, "failed") }
52
+
53
+ it "should raise an error" do
54
+ expect{ subject.process }.to raise_error Spree::PromotionCodeBatch::CantProcessStartedBatch
55
+ end
56
+ end
57
+ end
58
+ end
@@ -164,21 +164,25 @@ RSpec.describe Spree::PromotionCode do
164
164
  order.complete!
165
165
  end
166
166
  end
167
+
167
168
  it "makes the promotion ineligible" do
168
169
  expect{
169
170
  order.complete
170
171
  }.to change{ promo_adjustment.reload.eligible }.to(false)
171
172
  end
173
+
172
174
  it "adjusts the promo_total" do
173
175
  expect{
174
176
  order.complete
175
177
  }.to change(order, :promo_total).by(10)
176
178
  end
179
+
177
180
  it "increases the total to remove the promo" do
178
181
  expect{
179
182
  order.complete
180
183
  }.to change(order, :total).from(30).to(40)
181
184
  end
185
+
182
186
  it "resets the state of the order" do
183
187
  expect{
184
188
  order.complete
@@ -597,8 +597,7 @@ describe Spree::Promotion, type: :model do
597
597
  allow(rule2).to receive_messages(eligible?: true, applicable?: true)
598
598
 
599
599
  promotion.promotion_rules = [rule1, rule2]
600
- allow(promotion).to receive_message_chain(:rules, :none?).and_return(false)
601
- allow(promotion).to receive_message_chain(:rules, :for).and_return(promotion.promotion_rules)
600
+ allow(promotion.promotion_rules).to receive(:for).and_return(promotion.promotion_rules)
602
601
  end
603
602
  it "returns the eligible rules" do
604
603
  expect(promotion.eligible_rules(promotable)).to eq [rule1, rule2]
@@ -616,8 +615,7 @@ describe Spree::Promotion, type: :model do
616
615
  allow(rule2).to receive_messages(eligible?: false, applicable?: true, eligibility_errors: errors)
617
616
 
618
617
  promotion.promotion_rules = [rule1, rule2]
619
- allow(promotion).to receive_message_chain(:rules, :none?).and_return(false)
620
- allow(promotion).to receive_message_chain(:rules, :for).and_return(promotion.promotion_rules)
618
+ allow(promotion.promotion_rules).to receive(:for).and_return(promotion.promotion_rules)
621
619
  end
622
620
  it "returns nil" do
623
621
  expect(promotion.eligible_rules(promotable)).to be_nil
@@ -649,8 +647,7 @@ describe Spree::Promotion, type: :model do
649
647
  allow(rule).to receive_messages(eligible?: false, applicable?: true, eligibility_errors: errors)
650
648
 
651
649
  promotion.promotion_rules = [rule]
652
- allow(promotion).to receive_message_chain(:rules, :for).and_return(promotion.promotion_rules)
653
- allow(promotion).to receive_message_chain(:rules, :none?).and_return(false)
650
+ allow(promotion.promotion_rules).to receive(:for).and_return(promotion.promotion_rules)
654
651
  end
655
652
  it "returns nil" do
656
653
  expect(promotion.eligible_rules(promotable)).to be_nil
@@ -44,65 +44,6 @@ describe Spree::ReturnAuthorization, type: :model do
44
44
  expect(return_authorization.errors['base'].size).to eq 0
45
45
  end
46
46
  end
47
-
48
- context "expedited exchanges are configured" do
49
- let(:order) { create(:shipped_order, line_items_count: 2) }
50
- let(:exchange_return_item) { build(:exchange_return_item, inventory_unit: order.inventory_units.first) }
51
- let(:return_item) { build(:return_item, inventory_unit: order.inventory_units.last) }
52
- subject { create(:return_authorization, order: order, return_items: [exchange_return_item, return_item]) }
53
-
54
- before do
55
- Spree::Config[:expedited_exchanges] = true
56
- @pre_exchange_hooks = subject.class.pre_expedited_exchange_hooks
57
- end
58
-
59
- after do
60
- subject.class.pre_expedited_exchange_hooks = @pre_exchange_hooks
61
- end
62
-
63
- context "no items to exchange" do
64
- subject { create(:return_authorization, order: order) }
65
-
66
- it "does not create a reimbursement" do
67
- expect{ subject.save }.to_not change { Spree::Reimbursement.count }
68
- end
69
- end
70
-
71
- context "items to exchange" do
72
- it "calls pre_expedited_exchange hooks with the return items to exchange" do
73
- hook = double(:as_null_object)
74
- expect(hook).to receive(:call).with [exchange_return_item]
75
- subject.class.pre_expedited_exchange_hooks = [hook]
76
- subject.save
77
- end
78
-
79
- it "attempts to accept all return items requiring exchange" do
80
- expect(exchange_return_item).to receive :attempt_accept
81
- expect(return_item).not_to receive :attempt_accept
82
- subject.save
83
- end
84
-
85
- it "performs an exchange reimbursement for the exchange return items" do
86
- subject.save
87
- reimbursement = Spree::Reimbursement.last
88
- expect(reimbursement.order).to eq subject.order
89
- expect(reimbursement.return_items).to eq [exchange_return_item]
90
- expect(exchange_return_item.reload.exchange_shipment).to be_present
91
- end
92
-
93
- context "the reimbursement fails" do
94
- before do
95
- allow_any_instance_of(Spree::Reimbursement).to receive(:save) { false }
96
- allow_any_instance_of(Spree::Reimbursement).to receive(:errors) { double(full_messages: "foo") }
97
- end
98
-
99
- it "puts errors on the return authorization" do
100
- subject.save
101
- expect(subject.errors[:base]).to include "foo"
102
- end
103
- end
104
- end
105
- end
106
47
  end
107
48
 
108
49
  describe ".before_create" do
@@ -724,11 +724,10 @@ describe Spree::Shipment, type: :model do
724
724
  expect { shipment.reload }.to raise_error(ActiveRecord::RecordNotFound)
725
725
  end
726
726
 
727
- it "cannot be destroyed when ready" do
727
+ it "can be destroyed when ready" do
728
728
  shipment = create(:shipment, state: "ready")
729
- expect(shipment.destroy).to eq false
730
- expect(shipment.errors.full_messages.join).to match /Cannot destroy/
731
- expect { shipment.reload }.not_to raise_error
729
+ expect(shipment.destroy).to be_truthy
730
+ expect { shipment.reload }.to raise_error(ActiveRecord::RecordNotFound)
732
731
  end
733
732
 
734
733
  it "cannot be destroyed when shipped" do
@@ -753,6 +752,7 @@ describe Spree::Shipment, type: :model do
753
752
  before do
754
753
  stock_item.set_count_on_hand(10)
755
754
  stock_item.update_attributes!(backorderable: false)
755
+ inventory_unit.update_attributes!(pending: true)
756
756
  end
757
757
 
758
758
  subject { shipment.finalize! }
@@ -26,6 +26,7 @@ module Spree
26
26
  end
27
27
 
28
28
  it "doesn't add a validation error" do
29
+ subject
29
30
  expect(line_item.errors).to be_empty
30
31
  end
31
32
  end
@@ -53,28 +54,82 @@ module Spree
53
54
  context "line_item is part of a shipment" do
54
55
  let!(:order) { create(:order_with_line_items) }
55
56
 
56
- context "has stock in all stock locations" do
57
+ context "has stock in one stock location" do
57
58
  let(:line_item) { order.line_items.first }
58
59
 
59
60
  before do
60
- variant_ids = order.line_items.map(&:variant_id)
61
- Spree::StockItem.where(variant_id: variant_ids).update_all(count_on_hand: 10, backorderable: false)
61
+ line_item.variant.stock_items.update_all(count_on_hand: 10, backorderable: false)
62
62
  end
63
63
 
64
64
  include_examples "passes validation"
65
65
  end
66
66
 
67
- context "doesn't have stock in a particular stock location" do
68
- let(:variant) { create(:variant) }
69
- let(:line_item) { order.line_items.find_by(variant_id: variant.id) }
67
+ context "with stock in multiple locations" do
68
+ let(:line_item) { order.line_items.first }
69
+ let(:variant) { line_item.variant }
70
70
  let!(:stock_location_1) { create(:stock_location, name: "Test Warehouse", active: false) }
71
71
 
72
72
  before do
73
- order.contents.add(variant, 1, stock_location_quantities: { stock_location_1.id => 1 })
74
- order.contents.advance
75
- stock_location_1.stock_items.update_all(count_on_hand: 0, backorderable: false)
73
+ shipment = order.shipments.create(stock_location: stock_location_1)
74
+ order.contents.add(variant, 1, shipment: shipment)
75
+ end
76
+
77
+ context "but no stock in either location" do
78
+ before do
79
+ variant.stock_items.update_all(count_on_hand: 0, backorderable: false)
80
+ end
81
+ include_examples "fails validation"
82
+ end
83
+
84
+ context "but no stock in one location" do
85
+ before do
86
+ stock_location_1.stock_items.update_all(count_on_hand: 0, backorderable: false)
87
+ end
88
+
89
+ include_examples "fails validation"
90
+ end
91
+
92
+ context "with enough stock only across locations" do
93
+ before do
94
+ variant.stock_items.update_all(count_on_hand: 1, backorderable: false)
95
+ end
96
+ include_examples "passes validation"
97
+ end
98
+
99
+ context "but inventory units are finalized" do
100
+ before do
101
+ order.inventory_units.update_all(pending: false)
102
+ end
103
+
104
+ include_examples "passes validation"
76
105
  end
106
+ end
107
+ end
108
+
109
+ context "line_item is split across two shipments" do
110
+ let!(:order) { create(:order_with_line_items) }
111
+ let(:line_item) { order.line_items.first }
112
+ let(:variant) { line_item.variant }
113
+ let(:stock_location) { order.shipments.first.stock_location }
114
+
115
+ before do
116
+ shipment2 = order.shipments.create!(stock_location: order.shipments.first.stock_location)
117
+ order.contents.add(variant, 1, shipment: shipment2)
118
+ variant.stock_items.first.update_columns(count_on_hand: count_on_hand, backorderable: false)
119
+ end
120
+
121
+ context "and there is just enough stock" do
122
+ let(:count_on_hand) { 2 }
123
+ include_examples "passes validation"
124
+ end
125
+
126
+ context "and there is not enough stock" do
127
+ let(:count_on_hand) { 1 }
128
+ include_examples "fails validation"
129
+ end
77
130
 
131
+ context "and there is no available stock" do
132
+ let(:count_on_hand) { 0 }
78
133
  include_examples "fails validation"
79
134
  end
80
135
  end
@@ -65,13 +65,12 @@ RSpec.describe Spree::Tax::ItemAdjuster do
65
65
 
66
66
  context 'when there are matching rates for the zone' do
67
67
  context 'and all rates have the same tax category as the item' do
68
+ let(:item) { build_stubbed :line_item, order: order, tax_category: item_tax_category }
68
69
  let(:item_tax_category) { build_stubbed(:tax_category) }
69
70
  let(:rate_1) { create :tax_rate, tax_category: item_tax_category }
70
71
  let(:rate_2) { create :tax_rate }
71
72
  let(:rates_for_order_zone) { [rate_1, rate_2] }
72
73
 
73
- before { allow(item).to receive(:tax_category).and_return(item_tax_category) }
74
-
75
74
  it 'creates an adjustment for every matching rate' do
76
75
  adjuster.adjust!
77
76
  expect(tax_adjustments.length).to eq(1)
@@ -55,90 +55,5 @@ describe Spree::UnitCancel do
55
55
  expect { subject }.to raise_error RuntimeError, "Adjustable does not match line item"
56
56
  end
57
57
  end
58
-
59
- context "when exchanges are present" do
60
- let!(:order) { create(:order, ship_address: create(:address)) }
61
- let!(:product) { create(:product, price: 10.00) }
62
- let!(:variant) do
63
- create(:variant, price: 10, product: product, track_inventory: false)
64
- end
65
- let!(:shipping_method) { create(:free_shipping_method) }
66
- let(:exchange_variant) do
67
- create(:variant, product: variant.product, price: 10, track_inventory: false)
68
- end
69
-
70
- before do
71
- Spree::Config[:expedited_exchanges] = true
72
- end
73
-
74
- # This sets up an order with one shipped inventory unit, one unshipped
75
- # inventory unit, and one unshipped exchange inventory unit.
76
- before do
77
- # Complete an order with 1 line item with quantity=2
78
- order.contents.add(variant, 2)
79
- order.contents.advance
80
- create(:payment, order: order, amount: order.total)
81
- order.payments.reload
82
- order.contents.advance
83
- order.complete!
84
- order.reload
85
-
86
- # Ship _one_ of the inventory units
87
- @shipment = order.shipments.first
88
- @shipped_inventory_unit = order.inventory_units[0]
89
- @unshipped_inventory_unit = order.inventory_units[1]
90
- order.shipping.ship(
91
- inventory_units: [@shipped_inventory_unit],
92
- stock_location: @shipment.stock_location,
93
- address: order.ship_address,
94
- shipping_method: @shipment.shipping_method
95
- )
96
-
97
- # Create an expedited exchange for the shipped inventory unit.
98
- # This generates a new inventory unit attached to the existing line item.
99
- Spree::ReturnAuthorization.create!(
100
- order: order,
101
- stock_location: @shipment.stock_location,
102
- reason: create(:return_reason),
103
- return_items: [
104
- Spree::ReturnItem.new(
105
- inventory_unit: @shipped_inventory_unit,
106
- exchange_variant: exchange_variant
107
- )
108
- ]
109
- )
110
- @exchange_inventory_unit = order.inventory_units.reload[2]
111
- end
112
-
113
- context 'when canceling an unshipped inventory unit from the original order' do
114
- subject do
115
- unit_cancel.compute_amount(@unshipped_inventory_unit.line_item)
116
- end
117
-
118
- let(:unit_cancel) do
119
- Spree::UnitCancel.create!(
120
- inventory_unit: @unshipped_inventory_unit,
121
- reason: Spree::UnitCancel::SHORT_SHIP
122
- )
123
- end
124
-
125
- it { is_expected.to eq(-10.00) }
126
- end
127
-
128
- context 'when canceling an unshipped exchange inventory unit' do
129
- subject do
130
- unit_cancel.compute_amount(@exchange_inventory_unit.line_item)
131
- end
132
-
133
- let(:unit_cancel) do
134
- Spree::UnitCancel.create!(
135
- inventory_unit: @exchange_inventory_unit,
136
- reason: Spree::UnitCancel::SHORT_SHIP
137
- )
138
- end
139
-
140
- it { is_expected.to eq(-10.00) }
141
- end
142
- end
143
58
  end
144
59
  end
@@ -92,7 +92,9 @@ describe Spree::LegacyUser, type: :model do
92
92
  end
93
93
 
94
94
  it "has payment sources" do
95
- expect(user.payment_sources.first.gateway_customer_profile_id).not_to be_empty
95
+ Spree::Deprecation.silence do
96
+ expect(user.payment_sources.first.gateway_customer_profile_id).not_to be_empty
97
+ end
96
98
  end
97
99
  end
98
100
  end
@@ -30,10 +30,16 @@ describe Spree::Variant::VatPriceGenerator do
30
30
  end
31
31
 
32
32
  it "will not build prices that are already present" do
33
- variant.prices.build(amount: 11, country_iso: "FR")
34
- variant.prices.build(amount: 11, country_iso: nil)
35
33
  expect { subject }.not_to change { variant.prices.length }
36
34
  end
35
+
36
+ # We need to remove the price for FR from the database so it is created in memory, and then run VatPriceGenerator twice to trigger the duplicate price issue.
37
+ it "will not build duplicate prices on multiple runs" do
38
+ variant.prices.where(country_iso: "FR").destroy_all
39
+ variant.reload
40
+ described_class.new(variant).run
41
+ expect { subject }.not_to change { variant.prices.size }
42
+ end
37
43
  end
38
44
 
39
45
  context "with no default admin country" do
@@ -66,17 +66,19 @@ describe Spree::Variant, type: :model do
66
66
  let!(:high_vat) { create(:tax_rate, included_in_price: true, amount: 0.25, zone: high_vat_zone, tax_category: tax_category) }
67
67
  let!(:low_vat) { create(:tax_rate, included_in_price: true, amount: 0.15, zone: low_vat_zone, tax_category: tax_category) }
68
68
 
69
- let(:product) { create(:product, tax_category: tax_category) }
69
+ let(:product) { build(:product, tax_category: tax_category) }
70
70
 
71
- subject(:new_variant) { create(:variant, price: 15, product: product) }
71
+ subject(:new_variant) { build(:variant, price: 15) }
72
72
 
73
73
  it "creates the appropriate prices for them" do
74
- # default price + FR, DE, DK
75
- expect { new_variant }.to change { Spree::Price.count }.by(4)
74
+ product.variants << new_variant
75
+ product.save!
76
76
  expect(new_variant.prices.find_by(country_iso: "FR").amount).to eq(17.25)
77
77
  expect(new_variant.prices.find_by(country_iso: "DE").amount).to eq(18.75)
78
78
  expect(new_variant.prices.find_by(country_iso: "DK").amount).to eq(18.75)
79
79
  expect(new_variant.prices.find_by(country_iso: nil).amount).to eq(15.00)
80
+ # default price + FR, DE, DK
81
+ expect(new_variant.prices.count).to eq(4)
80
82
  end
81
83
 
82
84
  context "when the products price changes" do
@@ -561,6 +563,16 @@ describe Spree::Variant, type: :model do
561
563
  end
562
564
  end
563
565
  end
566
+
567
+ describe "cache clearing on update" do
568
+ it "correctly reports after updating track_inventory" do
569
+ variant.stock_items.first.set_count_on_hand 0
570
+ expect(variant).not_to be_in_stock
571
+
572
+ variant.update!(track_inventory: false)
573
+ expect(variant).to be_in_stock
574
+ end
575
+ end
564
576
  end
565
577
 
566
578
  describe '#is_backorderable' do