solidus_core 1.0.2 → 1.0.3

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -0
  3. data/Gemfile +3 -0
  4. data/Rakefile +16 -0
  5. data/script/rails +9 -0
  6. data/solidus_core.gemspec +48 -0
  7. data/spec/fixtures/thinking-cat.jpg +0 -0
  8. data/spec/helpers/base_helper_spec.rb +173 -0
  9. data/spec/helpers/order_helper_spec.rb +12 -0
  10. data/spec/helpers/products_helper_spec.rb +220 -0
  11. data/spec/helpers/taxons_helper_spec.rb +17 -0
  12. data/spec/lib/calculated_adjustments_spec.rb +7 -0
  13. data/spec/lib/i18n_spec.rb +123 -0
  14. data/spec/lib/search/base_spec.rb +86 -0
  15. data/spec/lib/search/variant_spec.rb +92 -0
  16. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +66 -0
  17. data/spec/lib/spree/core/controller_helpers/order_spec.rb +92 -0
  18. data/spec/lib/spree/core/controller_helpers/search_spec.rb +17 -0
  19. data/spec/lib/spree/core/controller_helpers/store_spec.rb +16 -0
  20. data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +39 -0
  21. data/spec/lib/spree/core/current_store_spec.rb +36 -0
  22. data/spec/lib/spree/core/delegate_belongs_to_spec.rb +22 -0
  23. data/spec/lib/spree/core/importer/order_spec.rb +431 -0
  24. data/spec/lib/spree/core/role_configuration_spec.rb +138 -0
  25. data/spec/lib/spree/core/validators/email_spec.rb +48 -0
  26. data/spec/lib/spree/localized_number_spec.rb +38 -0
  27. data/spec/lib/spree/migrations_spec.rb +36 -0
  28. data/spec/lib/spree/money_spec.rb +127 -0
  29. data/spec/lib/tasks/exchanges_spec.rb +231 -0
  30. data/spec/lib/tasks/migrations/copy_shipped_shipments_to_cartons_spec.rb +115 -0
  31. data/spec/lib/tasks/order_capturing_spec.rb +56 -0
  32. data/spec/mailers/carton_mailer_spec.rb +43 -0
  33. data/spec/mailers/order_mailer_spec.rb +122 -0
  34. data/spec/mailers/reimbursement_mailer_spec.rb +40 -0
  35. data/spec/mailers/test_mailer_spec.rb +15 -0
  36. data/spec/models/spree/ability_spec.rb +276 -0
  37. data/spec/models/spree/address_spec.rb +250 -0
  38. data/spec/models/spree/adjustment_reason_spec.rb +13 -0
  39. data/spec/models/spree/adjustment_spec.rb +177 -0
  40. data/spec/models/spree/app_configuration_spec.rb +20 -0
  41. data/spec/models/spree/asset_spec.rb +24 -0
  42. data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
  43. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
  44. data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
  45. data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
  46. data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
  47. data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
  48. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +51 -0
  49. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
  50. data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
  51. data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
  52. data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
  53. data/spec/models/spree/calculator/shipping/price_sack_spec.rb +30 -0
  54. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +36 -0
  55. data/spec/models/spree/calculator/tiered_percent_spec.rb +47 -0
  56. data/spec/models/spree/calculator_spec.rb +36 -0
  57. data/spec/models/spree/carton_spec.rb +133 -0
  58. data/spec/models/spree/classification_spec.rb +15 -0
  59. data/spec/models/spree/concerns/display_money_spec.rb +43 -0
  60. data/spec/models/spree/concerns/user_methods_spec.rb +41 -0
  61. data/spec/models/spree/credit_card_spec.rb +334 -0
  62. data/spec/models/spree/customer_return_spec.rb +276 -0
  63. data/spec/models/spree/exchange_spec.rb +79 -0
  64. data/spec/models/spree/gateway/bogus_simple.rb +20 -0
  65. data/spec/models/spree/gateway/bogus_spec.rb +13 -0
  66. data/spec/models/spree/gateway_spec.rb +82 -0
  67. data/spec/models/spree/inventory_unit_spec.rb +307 -0
  68. data/spec/models/spree/item_adjustments_spec.rb +256 -0
  69. data/spec/models/spree/line_item_spec.rb +191 -0
  70. data/spec/models/spree/option_type_spec.rb +14 -0
  71. data/spec/models/spree/option_value_spec.rb +22 -0
  72. data/spec/models/spree/order/address_spec.rb +50 -0
  73. data/spec/models/spree/order/adjustments_spec.rb +39 -0
  74. data/spec/models/spree/order/callbacks_spec.rb +42 -0
  75. data/spec/models/spree/order/checkout_spec.rb +902 -0
  76. data/spec/models/spree/order/currency_updater_spec.rb +32 -0
  77. data/spec/models/spree/order/finalizing_spec.rb +111 -0
  78. data/spec/models/spree/order/payment_spec.rb +210 -0
  79. data/spec/models/spree/order/risk_assessment_spec.rb +68 -0
  80. data/spec/models/spree/order/state_machine_spec.rb +221 -0
  81. data/spec/models/spree/order/tax_spec.rb +84 -0
  82. data/spec/models/spree/order/totals_spec.rb +24 -0
  83. data/spec/models/spree/order/updating_spec.rb +18 -0
  84. data/spec/models/spree/order/validations_spec.rb +15 -0
  85. data/spec/models/spree/order_cancellations_spec.rb +120 -0
  86. data/spec/models/spree/order_capturing_spec.rb +116 -0
  87. data/spec/models/spree/order_contents_spec.rb +265 -0
  88. data/spec/models/spree/order_inventory_spec.rb +228 -0
  89. data/spec/models/spree/order_mutex_spec.rb +85 -0
  90. data/spec/models/spree/order_promotion_spec.rb +31 -0
  91. data/spec/models/spree/order_shipping_spec.rb +247 -0
  92. data/spec/models/spree/order_spec.rb +1412 -0
  93. data/spec/models/spree/order_stock_location_spec.rb +18 -0
  94. data/spec/models/spree/order_updater_spec.rb +299 -0
  95. data/spec/models/spree/payment_method/store_credit_spec.rb +294 -0
  96. data/spec/models/spree/payment_method_spec.rb +96 -0
  97. data/spec/models/spree/payment_spec.rb +1044 -0
  98. data/spec/models/spree/permission_sets/base_spec.rb +12 -0
  99. data/spec/models/spree/permission_sets/configuration_display.rb +82 -0
  100. data/spec/models/spree/permission_sets/configuration_management_spec.rb +50 -0
  101. data/spec/models/spree/permission_sets/dashboard_display_spec.rb +22 -0
  102. data/spec/models/spree/permission_sets/order_display_spec.rb +49 -0
  103. data/spec/models/spree/permission_sets/order_management_spec.rb +36 -0
  104. data/spec/models/spree/permission_sets/product_display_spec.rb +60 -0
  105. data/spec/models/spree/permission_sets/product_management_spec.rb +40 -0
  106. data/spec/models/spree/permission_sets/promotion_display_spec.rb +34 -0
  107. data/spec/models/spree/permission_sets/promotion_management_spec.rb +26 -0
  108. data/spec/models/spree/permission_sets/report_display_spec.rb +24 -0
  109. data/spec/models/spree/permission_sets/restricted_transfer_management_spec.rb +132 -0
  110. data/spec/models/spree/permission_sets/stock_display_spec.rb +26 -0
  111. data/spec/models/spree/permission_sets/stock_management_spec.rb +24 -0
  112. data/spec/models/spree/permission_sets/user_display_spec.rb +36 -0
  113. data/spec/models/spree/permission_sets/user_management_spec.rb +28 -0
  114. data/spec/models/spree/preference_spec.rb +80 -0
  115. data/spec/models/spree/preferences/configuration_spec.rb +30 -0
  116. data/spec/models/spree/preferences/preferable_spec.rb +294 -0
  117. data/spec/models/spree/preferences/scoped_store_spec.rb +58 -0
  118. data/spec/models/spree/preferences/static_model_preferences_spec.rb +78 -0
  119. data/spec/models/spree/preferences/statically_configurable_spec.rb +60 -0
  120. data/spec/models/spree/preferences/store_spec.rb +39 -0
  121. data/spec/models/spree/price_spec.rb +42 -0
  122. data/spec/models/spree/product/scopes_spec.rb +148 -0
  123. data/spec/models/spree/product_duplicator_spec.rb +103 -0
  124. data/spec/models/spree/product_filter_spec.rb +26 -0
  125. data/spec/models/spree/product_property_spec.rb +20 -0
  126. data/spec/models/spree/product_spec.rb +437 -0
  127. data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +96 -0
  128. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +165 -0
  129. data/spec/models/spree/promotion/actions/create_quantity_adjustments_spec.rb +115 -0
  130. data/spec/models/spree/promotion/actions/free_shipping_spec.rb +40 -0
  131. data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
  132. data/spec/models/spree/promotion/rules/item_total_spec.rb +67 -0
  133. data/spec/models/spree/promotion/rules/nth_order_spec.rb +70 -0
  134. data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
  135. data/spec/models/spree/promotion/rules/option_value_spec.rb +94 -0
  136. data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
  137. data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
  138. data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
  139. data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
  140. data/spec/models/spree/promotion_builder_spec.rb +118 -0
  141. data/spec/models/spree/promotion_category_spec.rb +17 -0
  142. data/spec/models/spree/promotion_code/code_builder_spec.rb +79 -0
  143. data/spec/models/spree/promotion_code_spec.rb +187 -0
  144. data/spec/models/spree/promotion_handler/cart_spec.rb +114 -0
  145. data/spec/models/spree/promotion_handler/coupon_spec.rb +335 -0
  146. data/spec/models/spree/promotion_handler/free_shipping_spec.rb +47 -0
  147. data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
  148. data/spec/models/spree/promotion_rule_spec.rb +28 -0
  149. data/spec/models/spree/promotion_spec.rb +767 -0
  150. data/spec/models/spree/refund_spec.rb +204 -0
  151. data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
  152. data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
  153. data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
  154. data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
  155. data/spec/models/spree/reimbursement_spec.rb +231 -0
  156. data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
  157. data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
  158. data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
  159. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +107 -0
  160. data/spec/models/spree/reimbursement_type/store_credit_spec.rb +97 -0
  161. data/spec/models/spree/return_authorization_spec.rb +290 -0
  162. data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
  163. data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
  164. data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +85 -0
  165. data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
  166. data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
  167. data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
  168. data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
  169. data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
  170. data/spec/models/spree/return_item_spec.rb +775 -0
  171. data/spec/models/spree/returns_calculator_spec.rb +14 -0
  172. data/spec/models/spree/shipment_spec.rb +709 -0
  173. data/spec/models/spree/shipping_calculator_spec.rb +45 -0
  174. data/spec/models/spree/shipping_method_spec.rb +88 -0
  175. data/spec/models/spree/shipping_rate_spec.rb +142 -0
  176. data/spec/models/spree/state_spec.rb +14 -0
  177. data/spec/models/spree/stock/availability_validator_spec.rb +83 -0
  178. data/spec/models/spree/stock/coordinator_spec.rb +116 -0
  179. data/spec/models/spree/stock/differentiator_spec.rb +39 -0
  180. data/spec/models/spree/stock/estimator_spec.rb +146 -0
  181. data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
  182. data/spec/models/spree/stock/package_spec.rb +163 -0
  183. data/spec/models/spree/stock/packer_spec.rb +91 -0
  184. data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
  185. data/spec/models/spree/stock/quantifier_spec.rb +115 -0
  186. data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
  187. data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
  188. data/spec/models/spree/stock/splitter/shipping_category_spec.rb +50 -0
  189. data/spec/models/spree/stock/splitter/weight_spec.rb +29 -0
  190. data/spec/models/spree/stock_item_spec.rb +426 -0
  191. data/spec/models/spree/stock_location_spec.rb +279 -0
  192. data/spec/models/spree/stock_movement_spec.rb +56 -0
  193. data/spec/models/spree/stock_transfer_spec.rb +290 -0
  194. data/spec/models/spree/store_credit_category_spec.rb +17 -0
  195. data/spec/models/spree/store_credit_event_spec.rb +314 -0
  196. data/spec/models/spree/store_credit_spec.rb +876 -0
  197. data/spec/models/spree/store_spec.rb +55 -0
  198. data/spec/models/spree/tax_category_spec.rb +27 -0
  199. data/spec/models/spree/tax_rate_spec.rb +378 -0
  200. data/spec/models/spree/taxon_spec.rb +74 -0
  201. data/spec/models/spree/taxonomy_spec.rb +18 -0
  202. data/spec/models/spree/tracker_spec.rb +21 -0
  203. data/spec/models/spree/transfer_item_spec.rb +264 -0
  204. data/spec/models/spree/unit_cancel_spec.rb +148 -0
  205. data/spec/models/spree/user_spec.rb +223 -0
  206. data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +23 -0
  207. data/spec/models/spree/variant/scopes_spec.rb +55 -0
  208. data/spec/models/spree/variant_spec.rb +546 -0
  209. data/spec/models/spree/zone_spec.rb +305 -0
  210. data/spec/spec_helper.rb +78 -0
  211. data/spec/support/big_decimal.rb +5 -0
  212. data/spec/support/concerns/default_price.rb +34 -0
  213. data/spec/support/dummy_ability.rb +4 -0
  214. data/spec/support/test_gateway.rb +2 -0
  215. metadata +229 -3
  216. data/lib/spree/testing_support/rspec-activemodel-mocks_patch.rb +0 -8
@@ -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,115 @@
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 eq true
6
+ expect(subject.can_supply?(101)).to eq true
7
+ expect(subject.can_supply?(100_001)).to eq true
8
+ end
9
+ end
10
+
11
+ module Spree
12
+ module Stock
13
+ describe Quantifier, :type => :model do
14
+
15
+ let(:target_stock_location) { nil }
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, target_stock_location) }
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
+ specify { expect(subject.backorderable?).to be true }
46
+
47
+ it_should_behave_like 'unlimited supply'
48
+ end
49
+
50
+ context 'when stock item prevents backordering' do
51
+ before { stock_item.update_attributes(backorderable: false) }
52
+
53
+ specify { expect(subject.backorderable?).to be false }
54
+
55
+ it 'can_supply? only upto total_on_hand' do
56
+ expect(subject.can_supply?(1)).to be true
57
+ expect(subject.can_supply?(10)).to be true
58
+ expect(subject.can_supply?(11)).to be false
59
+ end
60
+ end
61
+
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
+
95
+ end
96
+
97
+ context 'with a specific stock location' do
98
+ let!(:stock_location_2) { create :stock_location }
99
+ let!(:stock_location_3) { create :stock_location, active: false }
100
+ let(:target_stock_location) { stock_location_3 }
101
+
102
+ before do
103
+ Spree::StockItem.update_all(count_on_hand: 0, backorderable: false)
104
+ stock_location_3.stock_items.where(variant_id: stock_item.variant).update_all(count_on_hand: 5, backorderable: false)
105
+ end
106
+
107
+ it 'can_supply? only upto total_on_hand' do
108
+ expect(subject.can_supply?(5)).to eq true
109
+ expect(subject.can_supply?(6)).to eq false
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ 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,50 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ module Splitter
6
+ describe ShippingCategory, :type => :model do
7
+
8
+ let(:order) { create(:order_with_line_items, line_items_count: 1) }
9
+ let(:line_item) { order.line_items.first }
10
+ let(:variant1) { build(:variant) }
11
+ let(:variant2) { build(:variant) }
12
+ let(:shipping_category_1) { create(:shipping_category, name: 'A') }
13
+ let(:shipping_category_2) { create(:shipping_category, name: 'B') }
14
+
15
+ def inventory_unit1
16
+ build(:inventory_unit, variant: variant1, order: order, line_item: line_item).tap do |inventory_unit|
17
+ inventory_unit.variant.product.shipping_category = shipping_category_1
18
+ end
19
+ end
20
+
21
+ def inventory_unit2
22
+ build(:inventory_unit, variant: variant2, order: order, line_item: line_item).tap do |inventory_unit|
23
+ inventory_unit.variant.product.shipping_category = shipping_category_2
24
+ end
25
+ end
26
+
27
+ let(:packer) { build(:stock_packer) }
28
+
29
+ subject { ShippingCategory.new(packer) }
30
+
31
+ it 'splits each package by shipping category' do
32
+ package1 = Package.new(packer.stock_location)
33
+ 4.times { package1.add inventory_unit1 }
34
+ 8.times { package1.add inventory_unit2 }
35
+
36
+ package2 = Package.new(packer.stock_location)
37
+ 6.times { package2.add inventory_unit1 }
38
+ 9.times { package2.add inventory_unit2, :backordered }
39
+
40
+ packages = subject.split([package1, package2])
41
+ expect(packages[0].quantity).to eq 4
42
+ expect(packages[1].quantity).to eq 8
43
+ expect(packages[2].quantity).to eq 6
44
+ expect(packages[3].quantity).to eq 9
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,29 @@
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(:variant) { build(:base_variant, :weight => 100) }
9
+
10
+ subject { Weight.new(packer) }
11
+
12
+ it 'splits and keeps splitting until all packages are underweight' do
13
+ package = Package.new(packer.stock_location)
14
+ 4.times { package.add build(:inventory_unit, variant: variant) }
15
+ packages = subject.split([package])
16
+ expect(packages.size).to eq 4
17
+ end
18
+
19
+ it 'handles packages that can not be reduced' do
20
+ package = Package.new(packer.stock_location)
21
+ allow(variant).to receive_messages(:weight => 200)
22
+ 2.times { package.add build(:inventory_unit, variant: variant) }
23
+ packages = subject.split([package])
24
+ expect(packages.size).to eq 2
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,426 @@
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 ActiveRecord::RecordInvalid
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
+ before do
180
+ Spree::Config.binary_inventory_cache = binary_inventory_cache
181
+ end
182
+
183
+ context "binary_inventory_cache is set to false (default)" do
184
+ let(:binary_inventory_cache) { false }
185
+
186
+ context "in_stock? changes" do
187
+ it "touches its variant" do
188
+ expect do
189
+ subject.set_count_on_hand(0)
190
+ end.to change { subject.variant.updated_at }
191
+ end
192
+ end
193
+
194
+ context "in_stock? does not change" do
195
+ it "touches its variant" do
196
+ expect do
197
+ subject.set_count_on_hand(-1)
198
+ end.to change { subject.variant.updated_at }
199
+ end
200
+ end
201
+ end
202
+
203
+ context "binary_inventory_cache is set to true" do
204
+ let(:binary_inventory_cache) { true }
205
+
206
+ context "in_stock? changes" do
207
+ it "touches its variant" do
208
+ expect do
209
+ subject.set_count_on_hand(0)
210
+ end.to change { subject.variant.updated_at }
211
+ end
212
+
213
+ # Regression spec
214
+ context "stock changes to below zero" do
215
+ it "touches its variant" do
216
+ expect do
217
+ subject.set_count_on_hand(-1)
218
+ end.to change { subject.variant.updated_at }
219
+ end
220
+ end
221
+ end
222
+
223
+ context "in_stock? does not change" do
224
+ it "does not touch its variant" do
225
+ expect do
226
+ subject.set_count_on_hand(1)
227
+ end.not_to change { subject.reload.variant.updated_at }
228
+ end
229
+ end
230
+
231
+ context "when a new stock location is added" do
232
+ it "touches its variant" do
233
+ expect do
234
+ create(:stock_location)
235
+ end.to change { subject.variant.reload.updated_at }
236
+ end
237
+ end
238
+ end
239
+ end
240
+
241
+ describe "#after_touch" do
242
+ it "touches its variant" do
243
+ expect do
244
+ subject.touch
245
+ end.to change { subject.variant.updated_at }
246
+ end
247
+ end
248
+
249
+ # Regression test for #4651
250
+ context "variant" do
251
+ it "can be found even if the variant is deleted" do
252
+ subject.variant.destroy
253
+ expect(subject.reload.variant).not_to be_nil
254
+ end
255
+ end
256
+
257
+ describe 'validations' do
258
+ describe 'count_on_hand' do
259
+ shared_examples_for 'valid count_on_hand' do
260
+ before(:each) do
261
+ subject.save
262
+ end
263
+
264
+ it 'has :no errors_on' do
265
+ expect(subject.errors_on(:count_on_hand).size).to eq(0)
266
+ end
267
+ end
268
+
269
+ shared_examples_for 'not valid count_on_hand' do
270
+ before(:each) do
271
+ subject.save
272
+ end
273
+
274
+ it 'has 1 error_on' do
275
+ expect(subject.error_on(:count_on_hand).size).to eq(1)
276
+ end
277
+ it { expect(subject.errors[:count_on_hand]).to include 'must be greater than or equal to 0' }
278
+ end
279
+
280
+ context 'when count_on_hand not changed' do
281
+ context 'when not backorderable' do
282
+ before(:each) do
283
+ subject.backorderable = false
284
+ end
285
+ it_should_behave_like 'valid count_on_hand'
286
+ end
287
+
288
+ context 'when backorderable' do
289
+ before(:each) do
290
+ subject.backorderable = true
291
+ end
292
+ it_should_behave_like 'valid count_on_hand'
293
+ end
294
+ end
295
+
296
+ context 'when count_on_hand changed' do
297
+ context 'when backorderable' do
298
+ before(:each) do
299
+ subject.backorderable = true
300
+ end
301
+ context 'when both count_on_hand and count_on_hand_was are positive' do
302
+ context 'when count_on_hand is greater than count_on_hand_was' do
303
+ before(:each) do
304
+ subject.update_column(:count_on_hand, 3)
305
+ subject.send(:count_on_hand=, subject.count_on_hand + 3)
306
+ end
307
+ it_should_behave_like 'valid count_on_hand'
308
+ end
309
+
310
+ context 'when count_on_hand is smaller than count_on_hand_was' do
311
+ before(:each) do
312
+ subject.update_column(:count_on_hand, 3)
313
+ subject.send(:count_on_hand=, subject.count_on_hand - 2)
314
+ end
315
+
316
+ it_should_behave_like 'valid count_on_hand'
317
+ end
318
+ end
319
+
320
+ context 'when both count_on_hand and count_on_hand_was are negative' do
321
+ context 'when count_on_hand is greater than count_on_hand_was' do
322
+ before(:each) do
323
+ subject.update_column(:count_on_hand, -3)
324
+ subject.send(:count_on_hand=, subject.count_on_hand + 2)
325
+ end
326
+ it_should_behave_like 'valid count_on_hand'
327
+ end
328
+
329
+ context 'when count_on_hand is smaller than count_on_hand_was' do
330
+ before(:each) do
331
+ subject.update_column(:count_on_hand, 3)
332
+ subject.send(:count_on_hand=, subject.count_on_hand - 3)
333
+ end
334
+
335
+ it_should_behave_like 'valid count_on_hand'
336
+ end
337
+ end
338
+
339
+ context 'when both count_on_hand is positive and count_on_hand_was is negative' do
340
+ context 'when count_on_hand is greater than count_on_hand_was' do
341
+ before(:each) do
342
+ subject.update_column(:count_on_hand, -3)
343
+ subject.send(:count_on_hand=, subject.count_on_hand + 6)
344
+ end
345
+ it_should_behave_like 'valid count_on_hand'
346
+ end
347
+ end
348
+
349
+ context 'when both count_on_hand is negative and count_on_hand_was is 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 - 6)
354
+ end
355
+ it_should_behave_like 'valid count_on_hand'
356
+ end
357
+ end
358
+ end
359
+
360
+ context 'when not backorderable' do
361
+ before(:each) do
362
+ subject.backorderable = false
363
+ end
364
+
365
+ context 'when both count_on_hand and count_on_hand_was are positive' do
366
+ context 'when count_on_hand is greater than count_on_hand_was' do
367
+ before(:each) do
368
+ subject.update_column(:count_on_hand, 3)
369
+ subject.send(:count_on_hand=, subject.count_on_hand + 3)
370
+ end
371
+ it_should_behave_like 'valid count_on_hand'
372
+ end
373
+
374
+ context 'when count_on_hand is smaller than count_on_hand_was' do
375
+ before(:each) do
376
+ subject.update_column(:count_on_hand, 3)
377
+ subject.send(:count_on_hand=, subject.count_on_hand - 2)
378
+ end
379
+
380
+ it_should_behave_like 'valid count_on_hand'
381
+ end
382
+ end
383
+
384
+ context 'when both count_on_hand and count_on_hand_was are negative' do
385
+ context 'when count_on_hand is greater than count_on_hand_was' do
386
+ before(:each) do
387
+ subject.update_column(:count_on_hand, -3)
388
+ subject.send(:count_on_hand=, subject.count_on_hand + 2)
389
+ end
390
+ it_should_behave_like 'valid count_on_hand'
391
+ end
392
+
393
+ context 'when count_on_hand is smaller than count_on_hand_was' do
394
+ before(:each) do
395
+ subject.update_column(:count_on_hand, -3)
396
+ subject.send(:count_on_hand=, subject.count_on_hand - 3)
397
+ end
398
+
399
+ it_should_behave_like 'not valid count_on_hand'
400
+ end
401
+ end
402
+
403
+ context 'when both count_on_hand is positive and count_on_hand_was is negative' do
404
+ context 'when count_on_hand is greater than count_on_hand_was' do
405
+ before(:each) do
406
+ subject.update_column(:count_on_hand, -3)
407
+ subject.send(:count_on_hand=, subject.count_on_hand + 6)
408
+ end
409
+ it_should_behave_like 'valid count_on_hand'
410
+ end
411
+ end
412
+
413
+ context 'when both count_on_hand is negative and count_on_hand_was is positive' do
414
+ context 'when count_on_hand is greater than count_on_hand_was' do
415
+ before(:each) do
416
+ subject.update_column(:count_on_hand, 3)
417
+ subject.send(:count_on_hand=, subject.count_on_hand - 6)
418
+ end
419
+ it_should_behave_like 'not valid count_on_hand'
420
+ end
421
+ end
422
+ end
423
+ end
424
+ end
425
+ end
426
+ end