solidus_core 2.7.4 → 2.8.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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -17
  3. data/app/assets/images/logo/solidus.svg +18 -0
  4. data/app/assets/javascripts/spree.js.erb +2 -2
  5. data/app/helpers/spree/base_helper.rb +1 -7
  6. data/app/helpers/spree/taxons_helper.rb +2 -2
  7. data/app/mailers/spree/carton_mailer.rb +4 -4
  8. data/app/models/spree/calculator/flat_percent_item_total.rb +4 -1
  9. data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +5 -3
  10. data/app/models/spree/calculator/tiered_percent.rb +2 -1
  11. data/app/models/spree/country.rb +8 -0
  12. data/app/models/spree/fulfilment_changer.rb +9 -1
  13. data/app/models/spree/gallery/product_gallery.rb +18 -0
  14. data/app/models/spree/gallery/variant_gallery.rb +21 -0
  15. data/app/models/spree/image.rb +11 -1
  16. data/app/models/spree/inventory_unit.rb +8 -0
  17. data/app/models/spree/line_item.rb +1 -1
  18. data/app/models/spree/order.rb +0 -4
  19. data/app/models/spree/order_cancellations.rb +31 -10
  20. data/app/models/spree/order_contents.rb +1 -5
  21. data/app/models/spree/order_inventory.rb +5 -4
  22. data/app/models/spree/product.rb +9 -0
  23. data/app/models/spree/product/scopes.rb +3 -4
  24. data/app/models/spree/promotion.rb +1 -6
  25. data/app/models/spree/promotion_handler/coupon.rb +15 -0
  26. data/app/models/spree/reimbursement.rb +21 -6
  27. data/app/models/spree/reimbursement_performer.rb +12 -6
  28. data/app/models/spree/reimbursement_type.rb +1 -1
  29. data/app/models/spree/reimbursement_type/credit.rb +5 -2
  30. data/app/models/spree/reimbursement_type/exchange.rb +1 -1
  31. data/app/models/spree/reimbursement_type/original_payment.rb +1 -1
  32. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +6 -6
  33. data/app/models/spree/reimbursement_type/store_credit.rb +18 -3
  34. data/app/models/spree/shipment.rb +11 -11
  35. data/app/models/spree/stock/allocator/base.rb +19 -0
  36. data/app/models/spree/stock/allocator/on_hand_first.rb +42 -0
  37. data/app/models/spree/stock/content_item.rb +1 -1
  38. data/app/models/spree/stock/inventory_units_finalizer.rb +47 -0
  39. data/app/models/spree/stock/location_sorter/base.rb +38 -0
  40. data/app/models/spree/stock/location_sorter/default_first.rb +15 -0
  41. data/app/models/spree/stock/location_sorter/unsorted.rb +14 -0
  42. data/app/models/spree/stock/simple_coordinator.rb +24 -10
  43. data/app/models/spree/stock_location.rb +2 -0
  44. data/app/models/spree/store_credit.rb +4 -12
  45. data/app/models/spree/store_credit_event.rb +2 -2
  46. data/app/models/spree/store_credit_reason.rb +11 -0
  47. data/app/models/spree/taxon.rb +8 -3
  48. data/app/models/spree/unit_cancel.rb +3 -0
  49. data/app/models/spree/variant.rb +18 -7
  50. data/config/initializers/money.rb +5 -0
  51. data/config/locales/en.yml +661 -527
  52. data/db/default/spree/store_credit.rb +1 -1
  53. data/db/default/spree/zones.rb +2 -2
  54. data/db/migrate/20180710170104_create_spree_store_credit_reasons_table.rb +42 -0
  55. data/db/migrate/20190106184413_remove_code_from_spree_promotions.rb +41 -0
  56. data/lib/generators/spree/dummy/templates/rails/test.rb +0 -3
  57. data/lib/generators/spree/install/install_generator.rb +2 -2
  58. data/lib/generators/spree/install/templates/config/initializers/spree.rb.tt +2 -2
  59. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/backend/all.js +1 -1
  60. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/frontend/all.js +1 -1
  61. data/lib/solidus/migrations/promotions_with_code_handlers.rb +66 -0
  62. data/lib/spree/app_configuration.rb +20 -4
  63. data/lib/spree/core/controller_helpers/common.rb +1 -1
  64. data/lib/spree/core/stock_configuration.rb +12 -0
  65. data/lib/spree/core/version.rb +1 -1
  66. data/lib/spree/i18n.rb +4 -0
  67. data/lib/spree/money.rb +13 -11
  68. data/lib/spree/permission_sets.rb +0 -1
  69. data/lib/spree/permission_sets/promotion_management.rb +1 -0
  70. data/lib/spree/testing_support/dummy_app.rb +13 -2
  71. data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/backend/all.js +1 -1
  72. data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/frontend/all.js +1 -1
  73. data/lib/spree/testing_support/factories/store_credit_event_factory.rb +5 -5
  74. data/lib/spree/testing_support/factories/{store_credit_update_reason_factory.rb → store_credit_reason_factory.rb} +1 -1
  75. data/lib/spree/testing_support/partial_double_verification.rb +13 -0
  76. data/lib/spree/testing_support/shared_examples/gallery.rb +18 -0
  77. data/spec/helpers/products_helper_spec.rb +10 -8
  78. data/spec/helpers/taxons_helper_spec.rb +3 -1
  79. data/spec/lib/i18n_spec.rb +5 -0
  80. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +6 -2
  81. data/spec/lib/spree/core/stock_configuration_spec.rb +19 -0
  82. data/spec/lib/spree/core/testing_support/factories/store_credit_reason_factory_spec.rb +14 -0
  83. data/spec/lib/spree/money_spec.rb +12 -13
  84. data/spec/migrate/20190106184413_remove_code_from_spree_promotions_spec.rb +136 -0
  85. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +10 -1
  86. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +18 -6
  87. data/spec/models/spree/calculator/tiered_percent_spec.rb +7 -1
  88. data/spec/models/spree/country_spec.rb +76 -0
  89. data/spec/models/spree/customer_return_spec.rb +2 -1
  90. data/spec/models/spree/fulfilment_changer_spec.rb +33 -0
  91. data/spec/models/spree/gallery/product_gallery_spec.rb +21 -0
  92. data/spec/models/spree/gallery/variant_gallery_spec.rb +21 -0
  93. data/spec/models/spree/inventory_unit_spec.rb +1 -4
  94. data/spec/models/spree/order/checkout_spec.rb +6 -6
  95. data/spec/models/spree/order/finalizing_spec.rb +1 -20
  96. data/spec/models/spree/order/outstanding_balance_integration_spec.rb +2 -1
  97. data/spec/models/spree/order/updating_spec.rb +1 -1
  98. data/spec/models/spree/order_cancellations_spec.rb +55 -14
  99. data/spec/models/spree/order_inventory_spec.rb +12 -5
  100. data/spec/models/spree/order_merger_spec.rb +6 -3
  101. data/spec/models/spree/order_spec.rb +3 -7
  102. data/spec/models/spree/order_updater_spec.rb +3 -8
  103. data/spec/models/spree/payment/cancellation_spec.rb +4 -3
  104. data/spec/models/spree/payment_spec.rb +1 -17
  105. data/spec/models/spree/permission_sets/promotion_management_spec.rb +2 -0
  106. data/spec/models/spree/product_spec.rb +10 -1
  107. data/spec/models/spree/promotion_handler/coupon_spec.rb +62 -8
  108. data/spec/models/spree/promotion_spec.rb +24 -10
  109. data/spec/models/spree/refund_spec.rb +2 -1
  110. data/spec/models/spree/reimbursement_performer_spec.rb +5 -4
  111. data/spec/models/spree/reimbursement_spec.rb +26 -2
  112. data/spec/models/spree/reimbursement_type/credit_spec.rb +2 -1
  113. data/spec/models/spree/reimbursement_type/exchange_spec.rb +2 -1
  114. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +2 -1
  115. data/spec/models/spree/reimbursement_type/store_credit_spec.rb +14 -2
  116. data/spec/models/spree/return_item_spec.rb +1 -1
  117. data/spec/models/spree/shipment_spec.rb +20 -13
  118. data/spec/models/spree/shipping_calculator_spec.rb +13 -3
  119. data/spec/models/spree/stock/allocator/on_hand_first_spec.rb +146 -0
  120. data/spec/models/spree/stock/content_item_spec.rb +70 -0
  121. data/spec/models/spree/stock/estimator_spec.rb +5 -2
  122. data/spec/models/spree/stock/inventory_units_finalizer_spec.rb +34 -0
  123. data/spec/models/spree/stock/location_sorter/default_first_spec.rb +20 -0
  124. data/spec/models/spree/stock/location_sorter/unsorted_spec.rb +19 -0
  125. data/spec/models/spree/stock/simple_coordinator_spec.rb +17 -0
  126. data/spec/models/spree/store_credit_event_spec.rb +12 -12
  127. data/spec/models/spree/store_credit_spec.rb +2 -2
  128. data/spec/models/spree/unit_cancel_spec.rb +20 -1
  129. data/spec/models/spree/variant_spec.rb +46 -24
  130. data/spec/spec_helper.rb +1 -0
  131. metadata +30 -9
  132. data/.yardopts +0 -1
  133. data/app/models/spree/store_credit_update_reason.rb +0 -4
  134. data/lib/spree/permission_sets/report_display.rb +0 -11
  135. data/spec/lib/spree/core/testing_support/factories/store_credit_update_reason_factory_spec.rb +0 -14
  136. data/spec/models/spree/permission_sets/report_display_spec.rb +0 -25
@@ -10,13 +10,14 @@ module Spree
10
10
  let(:simulate) { false }
11
11
  let!(:default_refund_reason) { Spree::RefundReason.find_or_create_by!(name: Spree::RefundReason::RETURN_PROCESSING_REASON, mutable: false) }
12
12
  let(:creditable) { DummyCreditable.new(amount: 99.99) }
13
+ let(:created_by_user) { create(:user, email: 'user@email.com') }
13
14
 
14
15
  class DummyCreditable < Spree::Base
15
16
  attr_accessor :amount
16
17
  self.table_name = 'spree_payments' # Your creditable class should not use this table
17
18
  end
18
19
 
19
- subject { Spree::ReimbursementType::Credit.reimburse(reimbursement, [return_item], simulate) }
20
+ subject { Spree::ReimbursementType::Credit.reimburse(reimbursement, [return_item], simulate, created_by: created_by_user) }
20
21
 
21
22
  before do
22
23
  reimbursement.update!(total: reimbursement.calculated_total)
@@ -9,8 +9,9 @@ module Spree
9
9
  let(:return_items) { reimbursement.return_items }
10
10
  let(:new_exchange) { double("Exchange") }
11
11
  let(:simulate) { true }
12
+ let(:created_by_user) { create(:user, email: 'user@email.com') }
12
13
 
13
- subject { Spree::ReimbursementType::Exchange.reimburse(reimbursement, return_items, simulate) }
14
+ subject { Spree::ReimbursementType::Exchange.reimburse(reimbursement, return_items, simulate, created_by: created_by_user) }
14
15
 
15
16
  context 'return items are supplied' do
16
17
  before do
@@ -9,8 +9,9 @@ module Spree
9
9
  let(:payment) { reimbursement.order.payments.first }
10
10
  let(:simulate) { false }
11
11
  let!(:default_refund_reason) { Spree::RefundReason.find_or_create_by!(name: Spree::RefundReason::RETURN_PROCESSING_REASON, mutable: false) }
12
+ let(:created_by_user) { create(:user, email: 'user@email.com') }
12
13
 
13
- subject { Spree::ReimbursementType::OriginalPayment.reimburse(reimbursement, [return_item], simulate) }
14
+ subject { Spree::ReimbursementType::OriginalPayment.reimburse(reimbursement, [return_item], simulate, created_by: created_by_user) }
14
15
 
15
16
  before { reimbursement.update!(total: reimbursement.calculated_total) }
16
17
 
@@ -12,10 +12,10 @@ module Spree
12
12
  let!(:default_refund_reason) { Spree::RefundReason.find_or_create_by!(name: Spree::RefundReason::RETURN_PROCESSING_REASON, mutable: false) }
13
13
 
14
14
  let!(:primary_credit_type) { create(:primary_credit_type) }
15
- let!(:created_by_user) { create(:user, email: Spree::StoreCredit::DEFAULT_CREATED_BY_EMAIL) }
15
+ let(:created_by_user) { create(:user, email: 'user@email.com') }
16
16
  let!(:default_reimbursement_category) { create(:store_credit_category) }
17
17
 
18
- subject { Spree::ReimbursementType::StoreCredit.reimburse(reimbursement, [return_item, return_item2], simulate) }
18
+ subject { Spree::ReimbursementType::StoreCredit.reimburse(reimbursement, [return_item, return_item2], simulate, created_by: created_by_user) }
19
19
 
20
20
  before do
21
21
  reimbursement.update!(total: reimbursement.calculated_total)
@@ -92,6 +92,18 @@ module Spree
92
92
  expect { subject }.to change { Spree::StoreCredit.count }.by(1)
93
93
  expect(Spree::StoreCredit.last.currency).to eq reimbursement.order.currency
94
94
  end
95
+
96
+ context 'without a user with email address "spree@example.com" in the database' do
97
+ before do
98
+ default_user = Spree::LegacyUser.find_by(email: "spree@example.com")
99
+ default_user.destroy if default_user
100
+ end
101
+
102
+ it "creates a store credit with the same currency as the reimbursement's order" do
103
+ expect { subject }.to change { Spree::StoreCredit.count }.by(1)
104
+ expect(Spree::StoreCredit.last.currency).to eq reimbursement.order.currency
105
+ end
106
+ end
95
107
  end
96
108
  end
97
109
  end
@@ -294,7 +294,7 @@ RSpec.describe Spree::ReturnItem, type: :model do
294
294
  end
295
295
 
296
296
  it 'does not decrease inventory' do
297
- expect(return_item).to_not receive(:process_inventory_unit)
297
+ expect(return_item).to_not receive(:process_inventory_unit!)
298
298
  subject
299
299
  end
300
300
 
@@ -267,8 +267,8 @@ RSpec.describe Spree::Shipment, type: :model do
267
267
 
268
268
  before do
269
269
  allow(line_item).to receive(:order) { order }
270
- allow(shipment).to receive(:inventory_units) { inventory_units }
271
- allow(inventory_units).to receive_message_chain(:includes, :joins).and_return inventory_units
270
+ shipment.inventory_units = inventory_units
271
+ allow(shipment.inventory_units).to receive_message_chain(:includes, :joins).and_return inventory_units
272
272
  end
273
273
 
274
274
  it 'should use symbols for states when adding contents to package' do
@@ -413,7 +413,9 @@ RSpec.describe Spree::Shipment, type: :model do
413
413
  allow(shipment.order).to receive(:update!)
414
414
 
415
415
  shipment.state = 'pending'
416
- expect(shipment).to receive(:after_cancel)
416
+ without_partial_double_verification do
417
+ expect(shipment).to receive(:after_cancel)
418
+ end
417
419
  shipment.cancel!
418
420
  expect(shipment.state).to eq 'canceled'
419
421
  end
@@ -512,7 +514,6 @@ RSpec.describe Spree::Shipment, type: :model do
512
514
  let(:subject) { shipment_with_inventory_units.ship! }
513
515
  before do
514
516
  allow(order).to receive(:update!)
515
- allow(shipment_with_inventory_units).to receive_messages(require_inventory: false, update_order: true)
516
517
  end
517
518
 
518
519
  it 'unstocks them items' do
@@ -525,7 +526,7 @@ RSpec.describe Spree::Shipment, type: :model do
525
526
  context "from #{state}" do
526
527
  before do
527
528
  allow(order).to receive(:update!)
528
- allow(shipment).to receive_messages(require_inventory: false, update_order: true, state: state)
529
+ allow(shipment).to receive_messages(state: state)
529
530
  end
530
531
 
531
532
  it "finalizes adjustments" do
@@ -760,8 +761,12 @@ RSpec.describe Spree::Shipment, type: :model do
760
761
  describe "#finalize!" do
761
762
  let(:inventory_unit) { shipment.inventory_units.first }
762
763
  let(:stock_item) { inventory_unit.variant.stock_items.find_by(stock_location: stock_location) }
764
+ let(:inventory_unit_finalizer) { double(:inventory_unit_finalizer, run!: [true]) }
763
765
 
764
766
  before do
767
+ allow(Spree::Stock::InventoryUnitsFinalizer)
768
+ .to receive(:new).and_return(inventory_unit_finalizer)
769
+
765
770
  stock_item.set_count_on_hand(10)
766
771
  stock_item.update_attributes!(backorderable: false)
767
772
  inventory_unit.update_attributes!(pending: true)
@@ -769,18 +774,20 @@ RSpec.describe Spree::Shipment, type: :model do
769
774
 
770
775
  subject { shipment.finalize! }
771
776
 
772
- it "updates the associated inventory units" do
773
- inventory_unit.update_columns(updated_at: 1.hour.ago)
774
- expect { subject }.to change { inventory_unit.reload.updated_at }
775
- end
777
+ it "call run! on Spree::Stock::InventoryUnitsFinalizer" do
778
+ expect(inventory_unit_finalizer).to receive(:run!)
776
779
 
777
- it "unstocks the variant" do
778
- expect { subject }.to change { stock_item.reload.count_on_hand }.from(10).to(9)
780
+ subject
779
781
  end
780
782
 
781
783
  context "inventory unit already finalized" do
782
- before do
783
- inventory_unit.update_attributes!(pending: false)
784
+ let(:inventory_unit) { build(:inventory_unit, pending: false) }
785
+
786
+ it "doesn't pass the inventory unit" do
787
+ expect(Spree::Stock::InventoryUnitsFinalizer)
788
+ .to receive(:new).with([])
789
+
790
+ subject
784
791
  end
785
792
 
786
793
  it "doesn't update the associated inventory units" do
@@ -4,11 +4,21 @@ require 'rails_helper'
4
4
 
5
5
  module Spree
6
6
  RSpec.describe ShippingCalculator, type: :model do
7
- let(:variant1) { build(:variant, price: 10) }
8
- let(:variant2) { build(:variant, price: 20) }
7
+ let(:line_item1) { build(:line_item, price: 10) }
8
+ let(:line_item2) { build(:line_item, price: 20) }
9
+
10
+ let(:inventory_unit1) { build(:inventory_unit, line_item: line_item1) }
11
+ let(:inventory_unit2) { build(:inventory_unit, line_item: line_item2) }
9
12
 
10
13
  let(:package) do
11
- build(:stock_package, variants_contents: { variant1 => 2, variant2 => 1 })
14
+ build(
15
+ :stock_package,
16
+ contents: [
17
+ Spree::Stock::ContentItem.new(inventory_unit1),
18
+ Spree::Stock::ContentItem.new(inventory_unit1),
19
+ Spree::Stock::ContentItem.new(inventory_unit2)
20
+ ]
21
+ )
12
22
  end
13
23
 
14
24
  subject { ShippingCalculator.new }
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ module Spree
6
+ module Stock
7
+ module Allocator
8
+ RSpec.describe OnHandFirst, type: :model do
9
+ subject { described_class.new(availability) }
10
+
11
+ let(:availability) { double(Spree::Stock::Availability) }
12
+
13
+ let!(:default_stock_location) { create(:stock_location, default: true, backorderable_default: false) }
14
+ let!(:backorderable_stock_location) { create(:stock_location) }
15
+
16
+ let(:first_variant) { create(:variant) }
17
+ let(:second_variant) { create(:variant) }
18
+
19
+ let(:desired_quantities) do
20
+ quantities = {}
21
+ quantities[first_variant] = first_variant_desired
22
+ quantities[second_variant] = second_variant_desired
23
+ quantities
24
+ end
25
+
26
+ let(:desired) { Spree::StockQuantities.new(desired_quantities) }
27
+
28
+ describe '#allocate_inventory' do
29
+ let(:default_on_hand_availability) do
30
+ quantities = {}
31
+ quantities[first_variant] = first_variant_default_availability
32
+ quantities[second_variant] = second_variant_default_availability
33
+ quantities
34
+ end
35
+
36
+ let(:dropship_on_hand_availability) do
37
+ quantities = {}
38
+ quantities[first_variant] = first_variant_dropship_availability
39
+ quantities[second_variant] = second_variant_dropship_availability
40
+ quantities
41
+ end
42
+
43
+ let(:on_hand_by_stock_location_id) do
44
+ availability = {}
45
+ availability[default_stock_location.id] = Spree::StockQuantities.new(default_on_hand_availability)
46
+ availability[backorderable_stock_location.id] = Spree::StockQuantities.new(dropship_on_hand_availability)
47
+ availability
48
+ end
49
+
50
+ let(:dropship_backorderable_availability) do
51
+ quantities = {}
52
+ quantities[first_variant] = Float::INFINITY
53
+ quantities[second_variant] = Float::INFINITY
54
+ quantities
55
+ end
56
+
57
+ let(:backorderable_by_stock_location_id) do
58
+ availability = {}
59
+ availability[backorderable_stock_location.id] = Spree::StockQuantities.new(dropship_backorderable_availability)
60
+ availability
61
+ end
62
+
63
+ before do
64
+ allow(availability).to receive(:on_hand_by_stock_location_id)
65
+ .and_return(on_hand_by_stock_location_id)
66
+
67
+ allow(availability).to receive(:backorderable_by_stock_location_id)
68
+ .and_return(backorderable_by_stock_location_id)
69
+ end
70
+
71
+ context 'when default stock location has enough items' do
72
+ let(:first_variant_default_availability) { 100 }
73
+ let(:second_variant_default_availability) { 100 }
74
+ let(:first_variant_dropship_availability) { 0 }
75
+ let(:second_variant_dropship_availability) { 0 }
76
+ let(:first_variant_desired) { 30 }
77
+ let(:second_variant_desired) { 5 }
78
+
79
+ it 'allocates all the desired units on the default stock location' do
80
+ on_hand_packages, backordered_packages, leftover = subject.allocate_inventory(desired)
81
+
82
+ expect(on_hand_packages[default_stock_location.id][first_variant]).to eq(30)
83
+ expect(on_hand_packages[default_stock_location.id][second_variant]).to eq(5)
84
+ expect(on_hand_packages[backorderable_stock_location.id][first_variant]).to eq(0)
85
+ expect(on_hand_packages[backorderable_stock_location.id][second_variant]).to eq(0)
86
+
87
+ expect(backordered_packages[backorderable_stock_location.id][first_variant]).to eq(0)
88
+ expect(backordered_packages[backorderable_stock_location.id][second_variant]).to eq(0)
89
+
90
+ expect(leftover[first_variant]).to eq(0)
91
+ expect(leftover[second_variant]).to eq(0)
92
+ end
93
+ end
94
+
95
+ context 'when default stock location hasn\'t enough items' do
96
+ let(:first_variant_default_availability) { 10 }
97
+ let(:second_variant_default_availability) { 10 }
98
+
99
+ let(:first_variant_desired) { 15 }
100
+ let(:second_variant_desired) { 5 }
101
+
102
+ context 'when dropship stock location has enough items' do
103
+ let(:first_variant_dropship_availability) { 20 }
104
+ let(:second_variant_dropship_availability) { 0 }
105
+
106
+ it 'allocates all the desired units on the stock locations while stocks last' do
107
+ on_hand_packages, backordered_packages, leftover = subject.allocate_inventory(desired)
108
+
109
+ expect(on_hand_packages[default_stock_location.id][first_variant]).to eq(10)
110
+ expect(on_hand_packages[default_stock_location.id][second_variant]).to eq(5)
111
+ expect(on_hand_packages[backorderable_stock_location.id][first_variant]).to eq(5)
112
+ expect(on_hand_packages[backorderable_stock_location.id][second_variant]).to eq(0)
113
+
114
+ expect(backordered_packages[backorderable_stock_location.id][first_variant]).to eq(0)
115
+ expect(backordered_packages[backorderable_stock_location.id][second_variant]).to eq(0)
116
+
117
+ expect(leftover[first_variant]).to eq(0)
118
+ expect(leftover[second_variant]).to eq(0)
119
+ end
120
+ end
121
+
122
+ context 'when dropship stock location hasn\'t enough items' do
123
+ let(:first_variant_dropship_availability) { 2 }
124
+ let(:second_variant_dropship_availability) { 0 }
125
+
126
+ it 'allocates all the desired units on the stock locations while stocks last' do
127
+ on_hand_packages, backordered_packages, leftover = subject.allocate_inventory(desired)
128
+
129
+ expect(on_hand_packages[default_stock_location.id][first_variant]).to eq(10)
130
+ expect(on_hand_packages[default_stock_location.id][second_variant]).to eq(5)
131
+ expect(on_hand_packages[backorderable_stock_location.id][first_variant]).to eq(2)
132
+ expect(on_hand_packages[backorderable_stock_location.id][second_variant]).to eq(0)
133
+
134
+ expect(backordered_packages[backorderable_stock_location.id][first_variant]).to eq(3)
135
+ expect(backordered_packages[backorderable_stock_location.id][second_variant]).to eq(0)
136
+
137
+ expect(leftover[first_variant]).to eq(0)
138
+ expect(leftover[second_variant]).to eq(0)
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ module Spree
6
+ module Stock
7
+ RSpec.describe ContentItem, type: :model do
8
+ let(:instance) { ContentItem.new(inventory_unit, state) }
9
+ let(:inventory_unit) { build(:inventory_unit) }
10
+ let(:state) { :on_hand }
11
+
12
+ describe '#variant' do
13
+ subject { instance.variant }
14
+ it { is_expected.to eq(inventory_unit.variant) }
15
+ end
16
+
17
+ describe '#weight' do
18
+ subject { instance.weight }
19
+ it { is_expected.to eq(0.to_d) }
20
+ end
21
+
22
+ describe '#line_item' do
23
+ subject { instance.line_item }
24
+ it { is_expected.to eq(inventory_unit.line_item) }
25
+ end
26
+
27
+ describe '#on_hand?' do
28
+ subject { instance.on_hand? }
29
+
30
+ context 'the state is on hand' do
31
+ it { is_expected.to eq(true) }
32
+ end
33
+
34
+ context 'the state is not on hand' do
35
+ let(:state) { 'foo' }
36
+ it { is_expected.to eq(false) }
37
+ end
38
+ end
39
+
40
+ describe '#backordered?' do
41
+ subject { instance.backordered? }
42
+
43
+ context 'the state is not backordered' do
44
+ let(:state) { 'foo' }
45
+ it { is_expected.to eq(false) }
46
+ end
47
+
48
+ context 'the state is backordered' do
49
+ let(:state) { :backordered }
50
+ it { is_expected.to eq(true) }
51
+ end
52
+ end
53
+
54
+ describe '#price' do
55
+ subject { instance.price }
56
+ it { is_expected.to eq(10.to_d) }
57
+ end
58
+
59
+ describe '#amount' do
60
+ subject { instance.amount }
61
+ it { is_expected.to eq(10.to_d) }
62
+ end
63
+
64
+ describe '#quantity' do
65
+ subject { instance.quantity }
66
+ it { is_expected.to eq(1) }
67
+ end
68
+ end
69
+ end
70
+ end
@@ -205,11 +205,14 @@ module Spree
205
205
  end
206
206
 
207
207
  it 'uses the configured shipping rate sorter' do
208
- class Spree::Stock::TestSorter; end;
208
+ class Spree::Stock::TestSorter
209
+ def initialize(_rates)
210
+ end
211
+ end
209
212
  Spree::Config.shipping_rate_sorter_class = Spree::Stock::TestSorter
210
213
 
211
214
  sorter = double(:sorter, sort: nil)
212
- allow(Spree::Stock::TestSorter).to receive(:new).and_return(sorter)
215
+ allow(Spree::Stock::TestSorter).to receive(:new) { sorter }
213
216
 
214
217
  subject.shipping_rates(package)
215
218
 
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ module Spree
6
+ module Stock
7
+ RSpec.describe InventoryUnitsFinalizer, type: :model do
8
+ let(:order) { build(:order_with_line_items) }
9
+ let(:inventory_unit) { build(:inventory_unit, order: order, variant: order.line_items.first.variant, shipment: order.shipments.first) }
10
+ let(:stock_item) { inventory_unit.variant.stock_items.first }
11
+
12
+ before do
13
+ stock_item.set_count_on_hand(10)
14
+ stock_item.update_attributes!(backorderable: false)
15
+ inventory_unit.update_attributes!(pending: true)
16
+ end
17
+
18
+ subject { described_class.new([inventory_unit]).run! }
19
+
20
+ it "updates the associated inventory units" do
21
+ inventory_unit.update_columns(updated_at: 1.hour.ago)
22
+ expect { subject }.to change { inventory_unit.reload.updated_at }
23
+ end
24
+
25
+ it "updates the inventory units to not be pending" do
26
+ expect { subject }.to change { inventory_unit.reload.pending }.to(false)
27
+ end
28
+
29
+ it "unstocks the variant" do
30
+ expect { subject }.to change { stock_item.reload.count_on_hand }.from(10).to(9)
31
+ end
32
+ end
33
+ end
34
+ end