spree_core 3.0.5 → 3.0.6

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 (194) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +3 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/javascripts/spree.js.coffee.erb +1 -1
  6. data/app/models/spree/ability.rb +1 -1
  7. data/app/models/spree/base.rb +3 -1
  8. data/app/models/spree/order_updater.rb +2 -1
  9. data/app/models/spree/price.rb +7 -12
  10. data/app/models/spree/product.rb +3 -2
  11. data/app/models/spree/reimbursement.rb +1 -1
  12. data/app/models/spree/state.rb +2 -0
  13. data/app/models/spree/zone.rb +1 -1
  14. data/lib/spree/core/version.rb +1 -1
  15. data/lib/spree/testing_support/shoulda_matcher_configuration.rb +6 -0
  16. data/script/rails +9 -0
  17. data/spec/fixtures/thinking-cat.jpg +0 -0
  18. data/spec/helpers/base_helper_spec.rb +137 -0
  19. data/spec/helpers/products_helper_spec.rb +224 -0
  20. data/spec/lib/calculated_adjustments_spec.rb +7 -0
  21. data/spec/lib/i18n_spec.rb +123 -0
  22. data/spec/lib/search/base_spec.rb +86 -0
  23. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +101 -0
  24. data/spec/lib/spree/core/controller_helpers/order_spec.rb +95 -0
  25. data/spec/lib/spree/core/controller_helpers/search_spec.rb +17 -0
  26. data/spec/lib/spree/core/controller_helpers/store_spec.rb +16 -0
  27. data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +39 -0
  28. data/spec/lib/spree/core/delegate_belongs_to_spec.rb +22 -0
  29. data/spec/lib/spree/core/importer/order_spec.rb +502 -0
  30. data/spec/lib/spree/core/validators/email_spec.rb +53 -0
  31. data/spec/lib/spree/localized_number_spec.rb +38 -0
  32. data/spec/lib/spree/migrations_spec.rb +34 -0
  33. data/spec/lib/spree/money_spec.rb +122 -0
  34. data/spec/lib/tasks/exchanges_spec.rb +136 -0
  35. data/spec/mailers/order_mailer_spec.rb +124 -0
  36. data/spec/mailers/reimbursement_mailer_spec.rb +47 -0
  37. data/spec/mailers/shipment_mailer_spec.rb +63 -0
  38. data/spec/mailers/test_mailer_spec.rb +24 -0
  39. data/spec/models/spree/ability_spec.rb +246 -0
  40. data/spec/models/spree/address_spec.rb +291 -0
  41. data/spec/models/spree/adjustable/adjustments_updater_spec.rb +286 -0
  42. data/spec/models/spree/adjustment_spec.rb +163 -0
  43. data/spec/models/spree/app_configuration_spec.rb +23 -0
  44. data/spec/models/spree/asset_spec.rb +25 -0
  45. data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
  46. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
  47. data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
  48. data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
  49. data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
  50. data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
  51. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +51 -0
  52. data/spec/models/spree/calculator/shipping.rb +8 -0
  53. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
  54. data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
  55. data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
  56. data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
  57. data/spec/models/spree/calculator/shipping/price_sack_spec.rb +29 -0
  58. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +40 -0
  59. data/spec/models/spree/calculator/tiered_percent_spec.rb +51 -0
  60. data/spec/models/spree/calculator_spec.rb +69 -0
  61. data/spec/models/spree/classification_spec.rb +93 -0
  62. data/spec/models/spree/concerns/display_money_spec.rb +43 -0
  63. data/spec/models/spree/country_spec.rb +18 -0
  64. data/spec/models/spree/credit_card_spec.rb +324 -0
  65. data/spec/models/spree/customer_return_spec.rb +262 -0
  66. data/spec/models/spree/exchange_spec.rb +75 -0
  67. data/spec/models/spree/gateway/bogus_simple.rb +20 -0
  68. data/spec/models/spree/gateway/bogus_spec.rb +13 -0
  69. data/spec/models/spree/gateway_spec.rb +54 -0
  70. data/spec/models/spree/image_spec.rb +5 -0
  71. data/spec/models/spree/inventory_unit_spec.rb +242 -0
  72. data/spec/models/spree/line_item_spec.rb +267 -0
  73. data/spec/models/spree/option_type_spec.rb +14 -0
  74. data/spec/models/spree/option_value_spec.rb +13 -0
  75. data/spec/models/spree/order/address_spec.rb +50 -0
  76. data/spec/models/spree/order/adjustments_spec.rb +29 -0
  77. data/spec/models/spree/order/callbacks_spec.rb +42 -0
  78. data/spec/models/spree/order/checkout_spec.rb +764 -0
  79. data/spec/models/spree/order/currency_updater_spec.rb +32 -0
  80. data/spec/models/spree/order/finalizing_spec.rb +117 -0
  81. data/spec/models/spree/order/helpers_spec.rb +5 -0
  82. data/spec/models/spree/order/payment_spec.rb +214 -0
  83. data/spec/models/spree/order/risk_assessment_spec.rb +84 -0
  84. data/spec/models/spree/order/shipments_spec.rb +43 -0
  85. data/spec/models/spree/order/state_machine_spec.rb +216 -0
  86. data/spec/models/spree/order/tax_spec.rb +84 -0
  87. data/spec/models/spree/order/totals_spec.rb +24 -0
  88. data/spec/models/spree/order/updating_spec.rb +18 -0
  89. data/spec/models/spree/order/validations_spec.rb +15 -0
  90. data/spec/models/spree/order_contents_spec.rb +256 -0
  91. data/spec/models/spree/order_inventory_spec.rb +228 -0
  92. data/spec/models/spree/order_merger_spec.rb +133 -0
  93. data/spec/models/spree/order_spec.rb +954 -0
  94. data/spec/models/spree/order_updater_spec.rb +283 -0
  95. data/spec/models/spree/payment/gateway_options_spec.rb +119 -0
  96. data/spec/models/spree/payment_method_spec.rb +95 -0
  97. data/spec/models/spree/payment_spec.rb +926 -0
  98. data/spec/models/spree/preference_spec.rb +80 -0
  99. data/spec/models/spree/preferences/configuration_spec.rb +30 -0
  100. data/spec/models/spree/preferences/preferable_spec.rb +348 -0
  101. data/spec/models/spree/preferences/scoped_store_spec.rb +58 -0
  102. data/spec/models/spree/preferences/store_spec.rb +46 -0
  103. data/spec/models/spree/price_spec.rb +42 -0
  104. data/spec/models/spree/product/scopes_spec.rb +148 -0
  105. data/spec/models/spree/product_duplicator_spec.rb +103 -0
  106. data/spec/models/spree/product_filter_spec.rb +26 -0
  107. data/spec/models/spree/product_option_type_spec.rb +5 -0
  108. data/spec/models/spree/product_property_spec.rb +11 -0
  109. data/spec/models/spree/product_spec.rb +474 -0
  110. data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +50 -0
  111. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +148 -0
  112. data/spec/models/spree/promotion/actions/create_line_items_spec.rb +86 -0
  113. data/spec/models/spree/promotion/actions/free_shipping_spec.rb +36 -0
  114. data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
  115. data/spec/models/spree/promotion/rules/item_total_spec.rb +282 -0
  116. data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
  117. data/spec/models/spree/promotion/rules/option_value_spec.rb +90 -0
  118. data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
  119. data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
  120. data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
  121. data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
  122. data/spec/models/spree/promotion_action_spec.rb +10 -0
  123. data/spec/models/spree/promotion_category_spec.rb +17 -0
  124. data/spec/models/spree/promotion_handler/cart_spec.rb +102 -0
  125. data/spec/models/spree/promotion_handler/coupon_spec.rb +323 -0
  126. data/spec/models/spree/promotion_handler/free_shipping_spec.rb +48 -0
  127. data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
  128. data/spec/models/spree/promotion_rule_spec.rb +29 -0
  129. data/spec/models/spree/promotion_spec.rb +603 -0
  130. data/spec/models/spree/property_spec.rb +5 -0
  131. data/spec/models/spree/prototype_spec.rb +5 -0
  132. data/spec/models/spree/refund_spec.rb +195 -0
  133. data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
  134. data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
  135. data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
  136. data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
  137. data/spec/models/spree/reimbursement_spec.rb +215 -0
  138. data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
  139. data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
  140. data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
  141. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +55 -0
  142. data/spec/models/spree/return_authorization_spec.rb +250 -0
  143. data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
  144. data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
  145. data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +61 -0
  146. data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
  147. data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
  148. data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
  149. data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
  150. data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
  151. data/spec/models/spree/return_item_spec.rb +682 -0
  152. data/spec/models/spree/returns_calculator_spec.rb +14 -0
  153. data/spec/models/spree/shipment_spec.rb +740 -0
  154. data/spec/models/spree/shipping_calculator_spec.rb +45 -0
  155. data/spec/models/spree/shipping_category_spec.rb +5 -0
  156. data/spec/models/spree/shipping_method_spec.rb +88 -0
  157. data/spec/models/spree/shipping_rate_spec.rb +141 -0
  158. data/spec/models/spree/state_spec.rb +18 -0
  159. data/spec/models/spree/stock/availability_validator_spec.rb +36 -0
  160. data/spec/models/spree/stock/content_item_spec.rb +22 -0
  161. data/spec/models/spree/stock/coordinator_spec.rb +51 -0
  162. data/spec/models/spree/stock/differentiator_spec.rb +39 -0
  163. data/spec/models/spree/stock/estimator_spec.rb +154 -0
  164. data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
  165. data/spec/models/spree/stock/package_spec.rb +194 -0
  166. data/spec/models/spree/stock/packer_spec.rb +70 -0
  167. data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
  168. data/spec/models/spree/stock/quantifier_spec.rb +97 -0
  169. data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
  170. data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
  171. data/spec/models/spree/stock/splitter/shipping_category_spec.rb +47 -0
  172. data/spec/models/spree/stock/splitter/weight_spec.rb +32 -0
  173. data/spec/models/spree/stock_item_spec.rb +410 -0
  174. data/spec/models/spree/stock_location_spec.rb +243 -0
  175. data/spec/models/spree/stock_movement_spec.rb +56 -0
  176. data/spec/models/spree/stock_transfer_spec.rb +50 -0
  177. data/spec/models/spree/store_spec.rb +50 -0
  178. data/spec/models/spree/tax_category_spec.rb +27 -0
  179. data/spec/models/spree/tax_rate_spec.rb +382 -0
  180. data/spec/models/spree/taxon_spec.rb +74 -0
  181. data/spec/models/spree/taxonomy_spec.rb +18 -0
  182. data/spec/models/spree/tracker_spec.rb +21 -0
  183. data/spec/models/spree/user_spec.rb +130 -0
  184. data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +24 -0
  185. data/spec/models/spree/variant_spec.rb +523 -0
  186. data/spec/models/spree/zone_spec.rb +444 -0
  187. data/spec/spec_helper.rb +74 -0
  188. data/spec/support/big_decimal.rb +5 -0
  189. data/spec/support/concerns/adjustment_source_spec.rb +23 -0
  190. data/spec/support/concerns/default_price_spec.rb +28 -0
  191. data/spec/support/rake.rb +13 -0
  192. data/spec/support/test_gateway.rb +2 -0
  193. data/spree_core.gemspec +48 -0
  194. metadata +185 -4
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ describe Prioritizer, :type => :model do
6
+ let(:order) { mock_model(Order) }
7
+ let(:stock_location) { build(:stock_location) }
8
+ let(:variant) { build(:variant) }
9
+
10
+ def inventory_units
11
+ @inventory_units ||= []
12
+ end
13
+
14
+ def build_inventory_unit
15
+ mock_model(InventoryUnit, variant: variant).tap do |unit|
16
+ inventory_units << unit
17
+ end
18
+ end
19
+
20
+ def pack
21
+ package = Package.new(order)
22
+ yield(package) if block_given?
23
+ package
24
+ end
25
+
26
+ it 'keeps a single package' do
27
+ package1 = pack do |package|
28
+ package.add build_inventory_unit
29
+ package.add build_inventory_unit
30
+ end
31
+
32
+ packages = [package1]
33
+ prioritizer = Prioritizer.new(inventory_units, packages)
34
+ packages = prioritizer.prioritized_packages
35
+ expect(packages.size).to eq 1
36
+ end
37
+
38
+ it 'removes duplicate packages' do
39
+ package1 = pack do |package|
40
+ package.add build_inventory_unit
41
+ package.add build_inventory_unit
42
+ end
43
+
44
+ package2 = pack do |package|
45
+ package.add inventory_units.first
46
+ package.add inventory_units.last
47
+ end
48
+
49
+ packages = [package1, package2]
50
+ prioritizer = Prioritizer.new(inventory_units, packages)
51
+ packages = prioritizer.prioritized_packages
52
+ expect(packages.size).to eq 1
53
+ end
54
+
55
+ it 'split over 2 packages' do
56
+ package1 = pack do |package|
57
+ package.add build_inventory_unit
58
+ end
59
+ package2 = pack do |package|
60
+ package.add build_inventory_unit
61
+ end
62
+
63
+ packages = [package1, package2]
64
+ prioritizer = Prioritizer.new(inventory_units, packages)
65
+ packages = prioritizer.prioritized_packages
66
+ expect(packages.size).to eq 2
67
+ end
68
+
69
+ it '1st has some, 2nd has remaining' do
70
+ 5.times { build_inventory_unit }
71
+
72
+ package1 = pack do |package|
73
+ 2.times { |i| package.add inventory_units[i] }
74
+ end
75
+ package2 = pack do |package|
76
+ 5.times { |i| package.add inventory_units[i] }
77
+ end
78
+
79
+ packages = [package1, package2]
80
+ prioritizer = Prioritizer.new(inventory_units, packages)
81
+ packages = prioritizer.prioritized_packages
82
+ expect(packages.count).to eq 2
83
+ expect(packages[0].quantity).to eq 2
84
+ expect(packages[1].quantity).to eq 3
85
+ end
86
+
87
+ it '1st has backorder, 2nd has some' do
88
+ 5.times { build_inventory_unit }
89
+
90
+ package1 = pack do |package|
91
+ 5.times { |i| package.add inventory_units[i], :backordered }
92
+ end
93
+ package2 = pack do |package|
94
+ 2.times { |i| package.add inventory_units[i] }
95
+ end
96
+
97
+ packages = [package1, package2]
98
+ prioritizer = Prioritizer.new(inventory_units, packages)
99
+ packages = prioritizer.prioritized_packages
100
+
101
+ expect(packages[0].quantity(:backordered)).to eq 3
102
+ expect(packages[1].quantity(:on_hand)).to eq 2
103
+ end
104
+
105
+ it '1st has backorder, 2nd has all' do
106
+ 5.times { build_inventory_unit }
107
+
108
+ package1 = pack do |package|
109
+ 3.times { |i| package.add inventory_units[i], :backordered }
110
+ end
111
+ package2 = pack do |package|
112
+ 5.times { |i| package.add inventory_units[i] }
113
+ end
114
+
115
+ packages = [package1, package2]
116
+ prioritizer = Prioritizer.new(inventory_units, packages)
117
+ packages = prioritizer.prioritized_packages
118
+ expect(packages[0]).to eq package2
119
+ expect(packages[1]).to be_nil
120
+ expect(packages[0].quantity(:backordered)).to eq 0
121
+ expect(packages[0].quantity(:on_hand)).to eq 5
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'unlimited supply' do
4
+ it 'can_supply? any amount' do
5
+ expect(subject.can_supply?(1)).to be true
6
+ expect(subject.can_supply?(101)).to be true
7
+ expect(subject.can_supply?(100_001)).to be true
8
+ end
9
+ end
10
+
11
+ module Spree
12
+ module Stock
13
+ describe Quantifier, type: :model do
14
+ before(:all) { Spree::StockLocation.destroy_all } # FIXME leaky database
15
+
16
+ let!(:stock_location) { create :stock_location_with_items }
17
+ let!(:stock_item) { stock_location.stock_items.order(:id).first }
18
+
19
+ subject { described_class.new(stock_item.variant) }
20
+
21
+ specify { expect(subject.stock_items).to eq([stock_item]) }
22
+
23
+ context 'with a single stock location/item' do
24
+ it 'total_on_hand should match stock_item' do
25
+ expect(subject.total_on_hand).to eq(stock_item.count_on_hand)
26
+ end
27
+
28
+ context 'when track_inventory_levels is false' do
29
+ before { configure_spree_preferences { |config| config.track_inventory_levels = false } }
30
+
31
+ specify { expect(subject.total_on_hand).to eq(Float::INFINITY) }
32
+
33
+ it_should_behave_like 'unlimited supply'
34
+ end
35
+
36
+ context 'when variant inventory tracking is off' do
37
+ before { stock_item.variant.track_inventory = false }
38
+
39
+ specify { expect(subject.total_on_hand).to eq(Float::INFINITY) }
40
+
41
+ it_should_behave_like 'unlimited supply'
42
+ end
43
+
44
+ context 'when stock item allows backordering' do
45
+
46
+ specify { expect(subject.backorderable?).to be true }
47
+
48
+ it_should_behave_like 'unlimited supply'
49
+ end
50
+
51
+ context 'when stock item prevents backordering' do
52
+ before { stock_item.update_attributes(backorderable: false) }
53
+
54
+ specify { expect(subject.backorderable?).to be false }
55
+
56
+ it 'can_supply? only upto total_on_hand' do
57
+ expect(subject.can_supply?(1)).to be true
58
+ expect(subject.can_supply?(10)).to be true
59
+ expect(subject.can_supply?(11)).to be false
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'with multiple stock locations/items' do
65
+ let!(:stock_location_2) { create :stock_location }
66
+ let!(:stock_location_3) { create :stock_location, active: false }
67
+
68
+ before do
69
+ stock_location_2.stock_items.where(variant_id: stock_item.variant).update_all(count_on_hand: 5, backorderable: false)
70
+ stock_location_3.stock_items.where(variant_id: stock_item.variant).update_all(count_on_hand: 5, backorderable: false)
71
+ end
72
+
73
+ it 'total_on_hand should total all active stock_items' do
74
+ expect(subject.total_on_hand).to eq(15)
75
+ end
76
+
77
+ context 'when any stock item allows backordering' do
78
+ specify { expect(subject.backorderable?).to be true }
79
+
80
+ it_should_behave_like 'unlimited supply'
81
+ end
82
+
83
+ context 'when all stock items prevent backordering' do
84
+ before { stock_item.update_attributes(backorderable: false) }
85
+
86
+ specify { expect(subject.backorderable?).to be false }
87
+
88
+ it 'can_supply? upto total_on_hand' do
89
+ expect(subject.can_supply?(1)).to be true
90
+ expect(subject.can_supply?(15)).to be true
91
+ expect(subject.can_supply?(16)).to be false
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ module Splitter
6
+ describe Backordered, :type => :model do
7
+ let(:variant) { build(:variant) }
8
+
9
+ let(:packer) { build(:stock_packer) }
10
+
11
+ subject { Backordered.new(packer) }
12
+
13
+ it 'splits packages by status' do
14
+ package = Package.new(packer.stock_location)
15
+ 4.times { package.add build(:inventory_unit, variant: variant) }
16
+ 5.times { package.add build(:inventory_unit, variant: variant), :backordered }
17
+
18
+ packages = subject.split([package])
19
+ expect(packages.count).to eq 2
20
+ expect(packages.first.quantity).to eq 4
21
+ expect(packages.first.on_hand.count).to eq 4
22
+ expect(packages.first.backordered.count).to eq 0
23
+
24
+ expect(packages[1].contents.count).to eq 5
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ module Splitter
6
+ describe Base, :type => :model do
7
+ let(:packer) { build(:stock_packer) }
8
+
9
+ it 'continues to splitter chain' do
10
+ splitter1 = Base.new(packer)
11
+ splitter2 = Base.new(packer, splitter1)
12
+ packages = []
13
+
14
+ expect(splitter1).to receive(:split).with(packages)
15
+ splitter2.split(packages)
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ module Splitter
6
+ describe ShippingCategory, :type => :model do
7
+
8
+ let(:variant1) { build(:variant) }
9
+ let(:variant2) { build(:variant) }
10
+ let(:shipping_category_1) { create(:shipping_category, name: 'A') }
11
+ let(:shipping_category_2) { create(:shipping_category, name: 'B') }
12
+
13
+ def inventory_unit1
14
+ InventoryUnit.new(variant: variant1).tap do |inventory_unit|
15
+ inventory_unit.variant.product.shipping_category = shipping_category_1
16
+ end
17
+ end
18
+
19
+ def inventory_unit2
20
+ InventoryUnit.new(variant: variant2).tap do |inventory_unit|
21
+ inventory_unit.variant.product.shipping_category = shipping_category_2
22
+ end
23
+ end
24
+
25
+ let(:packer) { build(:stock_packer) }
26
+
27
+ subject { ShippingCategory.new(packer) }
28
+
29
+ it 'splits each package by shipping category' do
30
+ package1 = Package.new(packer.stock_location)
31
+ 4.times { package1.add inventory_unit1 }
32
+ 8.times { package1.add inventory_unit2 }
33
+
34
+ package2 = Package.new(packer.stock_location)
35
+ 6.times { package2.add inventory_unit1 }
36
+ 9.times { package2.add inventory_unit2, :backordered }
37
+
38
+ packages = subject.split([package1, package2])
39
+ expect(packages[0].quantity).to eq 4
40
+ expect(packages[1].quantity).to eq 8
41
+ expect(packages[2].quantity).to eq 6
42
+ expect(packages[3].quantity).to eq 9
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ module Splitter
6
+ describe Weight, type: :model do
7
+ let(:packer) { build(:stock_packer) }
8
+ let(:heavy_variant) { build(:base_variant, weight: 100) }
9
+ let(:variant) { build(:base_variant, weight: 49) }
10
+
11
+ subject { Weight.new(packer) }
12
+
13
+ it 'splits and keeps splitting until all packages are underweight' do
14
+ package = Package.new(packer.stock_location)
15
+ 2.times { package.add build(:inventory_unit, variant: heavy_variant) }
16
+ 4.times { package.add build(:inventory_unit, variant: variant) }
17
+ 2.times { package.add build(:inventory_unit, variant: heavy_variant) }
18
+ packages = subject.split([package])
19
+ expect(packages.size).to eq 4
20
+ end
21
+
22
+ it 'handles packages that can not be reduced' do
23
+ package = Package.new(packer.stock_location)
24
+ allow(variant).to receive_messages(weight: 200)
25
+ 2.times { package.add build(:inventory_unit, variant: variant) }
26
+ packages = subject.split([package])
27
+ expect(packages.size).to eq 2
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,410 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::StockItem, :type => :model do
4
+ let(:stock_location) { create(:stock_location_with_items) }
5
+
6
+ subject { stock_location.stock_items.order(:id).first }
7
+
8
+ it 'maintains the count on hand for a variant' do
9
+ expect(subject.count_on_hand).to eq 10
10
+ end
11
+
12
+ it "can return the stock item's variant's name" do
13
+ expect(subject.variant_name).to eq(subject.variant.name)
14
+ end
15
+
16
+ context "available to be included in shipment" do
17
+ context "has stock" do
18
+ it { expect(subject).to be_available }
19
+ end
20
+
21
+ context "backorderable" do
22
+ before { subject.backorderable = true }
23
+ it { expect(subject).to be_available }
24
+ end
25
+
26
+ context "no stock and not backorderable" do
27
+ before do
28
+ subject.backorderable = false
29
+ allow(subject).to receive_messages(count_on_hand: 0)
30
+ end
31
+
32
+ it { expect(subject).not_to be_available }
33
+ end
34
+ end
35
+
36
+ describe 'reduce_count_on_hand_to_zero' do
37
+ context 'when count_on_hand > 0' do
38
+ before(:each) do
39
+ subject.update_column('count_on_hand', 4)
40
+ subject.reduce_count_on_hand_to_zero
41
+ end
42
+
43
+ it { expect(subject.count_on_hand).to eq(0) }
44
+ end
45
+
46
+ context 'when count_on_hand > 0' do
47
+ before(:each) do
48
+ subject.update_column('count_on_hand', -4)
49
+ @count_on_hand = subject.count_on_hand
50
+ subject.reduce_count_on_hand_to_zero
51
+ end
52
+
53
+ it { expect(subject.count_on_hand).to eq(@count_on_hand) }
54
+ end
55
+ end
56
+
57
+ context "adjust count_on_hand" do
58
+ let!(:current_on_hand) { subject.count_on_hand }
59
+
60
+ it 'is updated pessimistically' do
61
+ copy = Spree::StockItem.find(subject.id)
62
+
63
+ subject.adjust_count_on_hand(5)
64
+ expect(subject.count_on_hand).to eq(current_on_hand + 5)
65
+
66
+ expect(copy.count_on_hand).to eq(current_on_hand)
67
+ copy.adjust_count_on_hand(5)
68
+ expect(copy.count_on_hand).to eq(current_on_hand + 10)
69
+ end
70
+
71
+ context "item out of stock (by two items)" do
72
+ let(:inventory_unit) { double('InventoryUnit') }
73
+ let(:inventory_unit_2) { double('InventoryUnit2') }
74
+
75
+ before do
76
+ allow(subject).to receive_messages(:backordered_inventory_units => [inventory_unit, inventory_unit_2])
77
+ subject.update_column(:count_on_hand, -2)
78
+ end
79
+
80
+ # Regression test for #3755
81
+ it "processes existing backorders, even with negative stock" do
82
+ expect(inventory_unit).to receive(:fill_backorder)
83
+ expect(inventory_unit_2).not_to receive(:fill_backorder)
84
+ subject.adjust_count_on_hand(1)
85
+ expect(subject.count_on_hand).to eq(-1)
86
+ end
87
+
88
+ # Test for #3755
89
+ it "does not process backorders when stock is adjusted negatively" do
90
+ expect(inventory_unit).not_to receive(:fill_backorder)
91
+ expect(inventory_unit_2).not_to receive(:fill_backorder)
92
+ subject.adjust_count_on_hand(-1)
93
+ expect(subject.count_on_hand).to eq(-3)
94
+ end
95
+
96
+ context "adds new items" do
97
+ before { allow(subject).to receive_messages(:backordered_inventory_units => [inventory_unit, inventory_unit_2]) }
98
+
99
+ it "fills existing backorders" do
100
+ expect(inventory_unit).to receive(:fill_backorder)
101
+ expect(inventory_unit_2).to receive(:fill_backorder)
102
+
103
+ subject.adjust_count_on_hand(3)
104
+ expect(subject.count_on_hand).to eq(1)
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ context "set count_on_hand" do
111
+ let!(:current_on_hand) { subject.count_on_hand }
112
+
113
+ it 'is updated pessimistically' do
114
+ copy = Spree::StockItem.find(subject.id)
115
+
116
+ subject.set_count_on_hand(5)
117
+ expect(subject.count_on_hand).to eq(5)
118
+
119
+ expect(copy.count_on_hand).to eq(current_on_hand)
120
+ copy.set_count_on_hand(10)
121
+ expect(copy.count_on_hand).to eq(current_on_hand)
122
+ end
123
+
124
+ context "item out of stock (by two items)" do
125
+ let(:inventory_unit) { double('InventoryUnit') }
126
+ let(:inventory_unit_2) { double('InventoryUnit2') }
127
+
128
+ before { subject.set_count_on_hand(-2) }
129
+
130
+ it "doesn't process backorders" do
131
+ expect(subject).not_to receive(:backordered_inventory_units)
132
+ end
133
+
134
+ context "adds new items" do
135
+ before { allow(subject).to receive_messages(:backordered_inventory_units => [inventory_unit, inventory_unit_2]) }
136
+
137
+ it "fills existing backorders" do
138
+ expect(inventory_unit).to receive(:fill_backorder)
139
+ expect(inventory_unit_2).to receive(:fill_backorder)
140
+
141
+ subject.set_count_on_hand(1)
142
+ expect(subject.count_on_hand).to eq(1)
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ context "with stock movements" do
149
+ before { Spree::StockMovement.create(stock_item: subject, quantity: 1) }
150
+
151
+ it "doesnt raise ReadOnlyRecord error" do
152
+ expect { subject.destroy }.not_to raise_error
153
+ end
154
+ end
155
+
156
+ context "destroyed" do
157
+ before { subject.destroy }
158
+
159
+ it "recreates stock item just fine" do
160
+ expect {
161
+ stock_location.stock_items.create!(variant: subject.variant)
162
+ }.not_to raise_error
163
+ end
164
+
165
+ it "doesnt allow recreating more than one stock item at once" do
166
+ stock_location.stock_items.create!(variant: subject.variant)
167
+
168
+ expect {
169
+ stock_location.stock_items.create!(variant: subject.variant)
170
+ }.to raise_error
171
+ end
172
+ end
173
+
174
+ describe "#after_save" do
175
+ before do
176
+ subject.variant.update_column(:updated_at, 1.day.ago)
177
+ end
178
+
179
+ context "binary_inventory_cache is set to false (default)" do
180
+ context "in_stock? changes" do
181
+ it "touches its variant" do
182
+ expect do
183
+ subject.adjust_count_on_hand(subject.count_on_hand * -1)
184
+ end.to change { subject.variant.reload.updated_at }
185
+ end
186
+ end
187
+
188
+ context "in_stock? does not change" do
189
+ it "touches its variant" do
190
+ expect do
191
+ subject.adjust_count_on_hand((subject.count_on_hand * -1) + 1)
192
+ end.to change { subject.variant.reload.updated_at }
193
+ end
194
+ end
195
+ end
196
+
197
+ context "binary_inventory_cache is set to true" do
198
+ before { Spree::Config.binary_inventory_cache = true }
199
+ context "in_stock? changes" do
200
+ it "touches its variant" do
201
+ expect do
202
+ subject.adjust_count_on_hand(subject.count_on_hand * -1)
203
+ end.to change { subject.variant.reload.updated_at }
204
+ end
205
+ end
206
+
207
+ context "in_stock? does not change" do
208
+ it "does not touch its variant" do
209
+ expect do
210
+ subject.adjust_count_on_hand((subject.count_on_hand * -1) + 1)
211
+ end.not_to change { subject.variant.reload.updated_at }
212
+ end
213
+ end
214
+
215
+ context "when a new stock location is added" do
216
+ it "touches its variant" do
217
+ expect do
218
+ create(:stock_location)
219
+ end.to change { subject.variant.reload.updated_at }
220
+ end
221
+ end
222
+ end
223
+ end
224
+
225
+ describe "#after_touch" do
226
+ it "touches its variant" do
227
+ expect do
228
+ subject.touch
229
+ end.to change { subject.variant.updated_at }
230
+ end
231
+ end
232
+
233
+ # Regression test for #4651
234
+ context "variant" do
235
+ it "can be found even if the variant is deleted" do
236
+ subject.variant.destroy
237
+ expect(subject.reload.variant).not_to be_nil
238
+ end
239
+ end
240
+
241
+ describe 'validations' do
242
+ describe 'count_on_hand' do
243
+ shared_examples_for 'valid count_on_hand' do
244
+ before(:each) do
245
+ subject.save
246
+ end
247
+
248
+ it 'has :no errors_on' do
249
+ expect(subject.errors_on(:count_on_hand).size).to eq(0)
250
+ end
251
+ end
252
+
253
+ shared_examples_for 'not valid count_on_hand' do
254
+ before(:each) do
255
+ subject.save
256
+ end
257
+
258
+ it 'has 1 error_on' do
259
+ expect(subject.error_on(:count_on_hand).size).to eq(1)
260
+ end
261
+ it { expect(subject.errors[:count_on_hand]).to include 'must be greater than or equal to 0' }
262
+ end
263
+
264
+ context 'when count_on_hand not changed' do
265
+ context 'when not backorderable' do
266
+ before(:each) do
267
+ subject.backorderable = false
268
+ end
269
+ it_should_behave_like 'valid count_on_hand'
270
+ end
271
+
272
+ context 'when backorderable' do
273
+ before(:each) do
274
+ subject.backorderable = true
275
+ end
276
+ it_should_behave_like 'valid count_on_hand'
277
+ end
278
+ end
279
+
280
+ context 'when count_on_hand changed' do
281
+ context 'when backorderable' do
282
+ before(:each) do
283
+ subject.backorderable = true
284
+ end
285
+ context 'when both count_on_hand and count_on_hand_was are positive' do
286
+ context 'when count_on_hand is greater than count_on_hand_was' do
287
+ before(:each) do
288
+ subject.update_column(:count_on_hand, 3)
289
+ subject.send(:count_on_hand=, subject.count_on_hand + 3)
290
+ end
291
+ it_should_behave_like 'valid count_on_hand'
292
+ end
293
+
294
+ context 'when count_on_hand is smaller than count_on_hand_was' do
295
+ before(:each) do
296
+ subject.update_column(:count_on_hand, 3)
297
+ subject.send(:count_on_hand=, subject.count_on_hand - 2)
298
+ end
299
+
300
+ it_should_behave_like 'valid count_on_hand'
301
+ end
302
+ end
303
+
304
+ context 'when both count_on_hand and count_on_hand_was are negative' do
305
+ context 'when count_on_hand is greater than count_on_hand_was' do
306
+ before(:each) do
307
+ subject.update_column(:count_on_hand, -3)
308
+ subject.send(:count_on_hand=, subject.count_on_hand + 2)
309
+ end
310
+ it_should_behave_like 'valid count_on_hand'
311
+ end
312
+
313
+ context 'when count_on_hand is smaller than count_on_hand_was' do
314
+ before(:each) do
315
+ subject.update_column(:count_on_hand, 3)
316
+ subject.send(:count_on_hand=, subject.count_on_hand - 3)
317
+ end
318
+
319
+ it_should_behave_like 'valid count_on_hand'
320
+ end
321
+ end
322
+
323
+ context 'when both count_on_hand is positive and count_on_hand_was is negative' do
324
+ context 'when count_on_hand is greater than count_on_hand_was' do
325
+ before(:each) do
326
+ subject.update_column(:count_on_hand, -3)
327
+ subject.send(:count_on_hand=, subject.count_on_hand + 6)
328
+ end
329
+ it_should_behave_like 'valid count_on_hand'
330
+ end
331
+ end
332
+
333
+ context 'when both count_on_hand is negative and count_on_hand_was is positive' do
334
+ context 'when count_on_hand is greater than count_on_hand_was' do
335
+ before(:each) do
336
+ subject.update_column(:count_on_hand, 3)
337
+ subject.send(:count_on_hand=, subject.count_on_hand - 6)
338
+ end
339
+ it_should_behave_like 'valid count_on_hand'
340
+ end
341
+ end
342
+ end
343
+
344
+ context 'when not backorderable' do
345
+ before(:each) do
346
+ subject.backorderable = false
347
+ end
348
+
349
+ context 'when both count_on_hand and count_on_hand_was are positive' do
350
+ context 'when count_on_hand is greater than count_on_hand_was' do
351
+ before(:each) do
352
+ subject.update_column(:count_on_hand, 3)
353
+ subject.send(:count_on_hand=, subject.count_on_hand + 3)
354
+ end
355
+ it_should_behave_like 'valid count_on_hand'
356
+ end
357
+
358
+ context 'when count_on_hand is smaller than count_on_hand_was' do
359
+ before(:each) do
360
+ subject.update_column(:count_on_hand, 3)
361
+ subject.send(:count_on_hand=, subject.count_on_hand - 2)
362
+ end
363
+
364
+ it_should_behave_like 'valid count_on_hand'
365
+ end
366
+ end
367
+
368
+ context 'when both count_on_hand and count_on_hand_was are negative' do
369
+ context 'when count_on_hand is greater than count_on_hand_was' do
370
+ before(:each) do
371
+ subject.update_column(:count_on_hand, -3)
372
+ subject.send(:count_on_hand=, subject.count_on_hand + 2)
373
+ end
374
+ it_should_behave_like 'valid count_on_hand'
375
+ end
376
+
377
+ context 'when count_on_hand is smaller than count_on_hand_was' do
378
+ before(:each) do
379
+ subject.update_column(:count_on_hand, -3)
380
+ subject.send(:count_on_hand=, subject.count_on_hand - 3)
381
+ end
382
+
383
+ it_should_behave_like 'not valid count_on_hand'
384
+ end
385
+ end
386
+
387
+ context 'when both count_on_hand is positive and count_on_hand_was is negative' do
388
+ context 'when count_on_hand is greater than count_on_hand_was' do
389
+ before(:each) do
390
+ subject.update_column(:count_on_hand, -3)
391
+ subject.send(:count_on_hand=, subject.count_on_hand + 6)
392
+ end
393
+ it_should_behave_like 'valid count_on_hand'
394
+ end
395
+ end
396
+
397
+ context 'when both count_on_hand is negative and count_on_hand_was is positive' do
398
+ context 'when count_on_hand is greater than count_on_hand_was' do
399
+ before(:each) do
400
+ subject.update_column(:count_on_hand, 3)
401
+ subject.send(:count_on_hand=, subject.count_on_hand - 6)
402
+ end
403
+ it_should_behave_like 'not valid count_on_hand'
404
+ end
405
+ end
406
+ end
407
+ end
408
+ end
409
+ end
410
+ end