solidus_core 1.1.0 → 1.1.1

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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Rakefile +16 -0
  4. data/script/rails +9 -0
  5. data/solidus_core.gemspec +48 -0
  6. data/spec/fixtures/thinking-cat.jpg +0 -0
  7. data/spec/helpers/base_helper_spec.rb +173 -0
  8. data/spec/helpers/order_helper_spec.rb +12 -0
  9. data/spec/helpers/products_helper_spec.rb +208 -0
  10. data/spec/helpers/taxons_helper_spec.rb +17 -0
  11. data/spec/lib/calculated_adjustments_spec.rb +7 -0
  12. data/spec/lib/i18n_spec.rb +106 -0
  13. data/spec/lib/search/base_spec.rb +86 -0
  14. data/spec/lib/search/variant_spec.rb +112 -0
  15. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +66 -0
  16. data/spec/lib/spree/core/controller_helpers/order_spec.rb +92 -0
  17. data/spec/lib/spree/core/controller_helpers/payment_parameters_spec.rb +80 -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 +24 -0
  23. data/spec/lib/spree/core/importer/order_spec.rb +431 -0
  24. data/spec/lib/spree/core/role_configuration_spec.rb +156 -0
  25. data/spec/lib/spree/core/unreturned_item_charger_spec.rb +130 -0
  26. data/spec/lib/spree/core/validators/email_spec.rb +48 -0
  27. data/spec/lib/spree/localized_number_spec.rb +38 -0
  28. data/spec/lib/spree/migrations_spec.rb +36 -0
  29. data/spec/lib/spree/money_spec.rb +127 -0
  30. data/spec/lib/tasks/exchanges_spec.rb +231 -0
  31. data/spec/lib/tasks/migrations/copy_shipped_shipments_to_cartons_spec.rb +115 -0
  32. data/spec/lib/tasks/order_capturing_spec.rb +56 -0
  33. data/spec/mailers/carton_mailer_spec.rb +55 -0
  34. data/spec/mailers/order_mailer_spec.rb +135 -0
  35. data/spec/mailers/reimbursement_mailer_spec.rb +40 -0
  36. data/spec/mailers/test_mailer_spec.rb +15 -0
  37. data/spec/models/spree/ability_spec.rb +276 -0
  38. data/spec/models/spree/address_spec.rb +376 -0
  39. data/spec/models/spree/adjustment_reason_spec.rb +13 -0
  40. data/spec/models/spree/adjustment_spec.rb +169 -0
  41. data/spec/models/spree/app_configuration_spec.rb +24 -0
  42. data/spec/models/spree/asset_spec.rb +24 -0
  43. data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
  44. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
  45. data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
  46. data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
  47. data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
  48. data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
  49. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +60 -0
  50. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
  51. data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
  52. data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
  53. data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
  54. data/spec/models/spree/calculator/shipping/price_sack_spec.rb +30 -0
  55. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +36 -0
  56. data/spec/models/spree/calculator/tiered_percent_spec.rb +47 -0
  57. data/spec/models/spree/calculator_spec.rb +36 -0
  58. data/spec/models/spree/carton_spec.rb +133 -0
  59. data/spec/models/spree/classification_spec.rb +93 -0
  60. data/spec/models/spree/concerns/display_money_spec.rb +43 -0
  61. data/spec/models/spree/concerns/ordered_property_value_list_spec.rb +25 -0
  62. data/spec/models/spree/concerns/user_address_book_spec.rb +332 -0
  63. data/spec/models/spree/concerns/user_methods_spec.rb +41 -0
  64. data/spec/models/spree/credit_card_spec.rb +341 -0
  65. data/spec/models/spree/customer_return_spec.rb +276 -0
  66. data/spec/models/spree/exchange_spec.rb +79 -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 +104 -0
  70. data/spec/models/spree/inventory_unit_spec.rb +307 -0
  71. data/spec/models/spree/item_adjustments_spec.rb +275 -0
  72. data/spec/models/spree/line_item_spec.rb +199 -0
  73. data/spec/models/spree/option_type_spec.rb +14 -0
  74. data/spec/models/spree/option_value_spec.rb +45 -0
  75. data/spec/models/spree/order/address_spec.rb +50 -0
  76. data/spec/models/spree/order/adjustments_spec.rb +27 -0
  77. data/spec/models/spree/order/callbacks_spec.rb +42 -0
  78. data/spec/models/spree/order/checkout_spec.rb +884 -0
  79. data/spec/models/spree/order/currency_updater_spec.rb +32 -0
  80. data/spec/models/spree/order/finalizing_spec.rb +110 -0
  81. data/spec/models/spree/order/payment_spec.rb +243 -0
  82. data/spec/models/spree/order/risk_assessment_spec.rb +68 -0
  83. data/spec/models/spree/order/state_machine_spec.rb +209 -0
  84. data/spec/models/spree/order/tax_spec.rb +84 -0
  85. data/spec/models/spree/order/totals_spec.rb +24 -0
  86. data/spec/models/spree/order/updating_spec.rb +18 -0
  87. data/spec/models/spree/order/validations_spec.rb +15 -0
  88. data/spec/models/spree/order_cancellations_spec.rb +120 -0
  89. data/spec/models/spree/order_capturing_spec.rb +150 -0
  90. data/spec/models/spree/order_contents_spec.rb +307 -0
  91. data/spec/models/spree/order_inventory_spec.rb +228 -0
  92. data/spec/models/spree/order_mutex_spec.rb +85 -0
  93. data/spec/models/spree/order_promotion_spec.rb +31 -0
  94. data/spec/models/spree/order_shipping_spec.rb +241 -0
  95. data/spec/models/spree/order_spec.rb +1482 -0
  96. data/spec/models/spree/order_stock_location_spec.rb +18 -0
  97. data/spec/models/spree/order_updater_spec.rb +283 -0
  98. data/spec/models/spree/payment_method/store_credit_spec.rb +294 -0
  99. data/spec/models/spree/payment_method_spec.rb +147 -0
  100. data/spec/models/spree/payment_spec.rb +1087 -0
  101. data/spec/models/spree/permission_sets/base_spec.rb +12 -0
  102. data/spec/models/spree/permission_sets/configuration_display.rb +82 -0
  103. data/spec/models/spree/permission_sets/configuration_management_spec.rb +50 -0
  104. data/spec/models/spree/permission_sets/dashboard_display_spec.rb +22 -0
  105. data/spec/models/spree/permission_sets/order_display_spec.rb +55 -0
  106. data/spec/models/spree/permission_sets/order_management_spec.rb +42 -0
  107. data/spec/models/spree/permission_sets/product_display_spec.rb +60 -0
  108. data/spec/models/spree/permission_sets/product_management_spec.rb +40 -0
  109. data/spec/models/spree/permission_sets/promotion_display_spec.rb +40 -0
  110. data/spec/models/spree/permission_sets/promotion_management_spec.rb +26 -0
  111. data/spec/models/spree/permission_sets/report_display_spec.rb +24 -0
  112. data/spec/models/spree/permission_sets/restricted_stock_display_spec.rb +41 -0
  113. data/spec/models/spree/permission_sets/restricted_stock_management_spec.rb +41 -0
  114. data/spec/models/spree/permission_sets/restricted_stock_transfer_display_spec.rb +50 -0
  115. data/spec/models/spree/permission_sets/restricted_stock_transfer_management_spec.rb +160 -0
  116. data/spec/models/spree/permission_sets/stock_display_spec.rb +24 -0
  117. data/spec/models/spree/permission_sets/stock_management_spec.rb +22 -0
  118. data/spec/models/spree/permission_sets/stock_transfer_display_spec.rb +24 -0
  119. data/spec/models/spree/permission_sets/stock_transfer_management_spec.rb +25 -0
  120. data/spec/models/spree/permission_sets/user_display_spec.rb +38 -0
  121. data/spec/models/spree/permission_sets/user_management_spec.rb +48 -0
  122. data/spec/models/spree/preference_spec.rb +80 -0
  123. data/spec/models/spree/preferences/configuration_spec.rb +30 -0
  124. data/spec/models/spree/preferences/preferable_spec.rb +294 -0
  125. data/spec/models/spree/preferences/scoped_store_spec.rb +60 -0
  126. data/spec/models/spree/preferences/static_model_preferences_spec.rb +78 -0
  127. data/spec/models/spree/preferences/statically_configurable_spec.rb +60 -0
  128. data/spec/models/spree/preferences/store_spec.rb +39 -0
  129. data/spec/models/spree/price_spec.rb +42 -0
  130. data/spec/models/spree/product/scopes_spec.rb +116 -0
  131. data/spec/models/spree/product_duplicator_spec.rb +103 -0
  132. data/spec/models/spree/product_filter_spec.rb +26 -0
  133. data/spec/models/spree/product_property_spec.rb +18 -0
  134. data/spec/models/spree/product_spec.rb +504 -0
  135. data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +96 -0
  136. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +165 -0
  137. data/spec/models/spree/promotion/actions/create_quantity_adjustments_spec.rb +115 -0
  138. data/spec/models/spree/promotion/actions/free_shipping_spec.rb +40 -0
  139. data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
  140. data/spec/models/spree/promotion/rules/first_repeat_purchase_since_spec.rb +69 -0
  141. data/spec/models/spree/promotion/rules/item_total_spec.rb +67 -0
  142. data/spec/models/spree/promotion/rules/nth_order_spec.rb +70 -0
  143. data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
  144. data/spec/models/spree/promotion/rules/option_value_spec.rb +94 -0
  145. data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
  146. data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
  147. data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
  148. data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
  149. data/spec/models/spree/promotion_builder_spec.rb +118 -0
  150. data/spec/models/spree/promotion_category_spec.rb +17 -0
  151. data/spec/models/spree/promotion_code/code_builder_spec.rb +79 -0
  152. data/spec/models/spree/promotion_code_spec.rb +187 -0
  153. data/spec/models/spree/promotion_handler/cart_spec.rb +130 -0
  154. data/spec/models/spree/promotion_handler/coupon_spec.rb +335 -0
  155. data/spec/models/spree/promotion_handler/free_shipping_spec.rb +47 -0
  156. data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
  157. data/spec/models/spree/promotion_rule_spec.rb +28 -0
  158. data/spec/models/spree/promotion_spec.rb +776 -0
  159. data/spec/models/spree/refund_spec.rb +192 -0
  160. data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
  161. data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
  162. data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
  163. data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
  164. data/spec/models/spree/reimbursement_spec.rb +231 -0
  165. data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
  166. data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
  167. data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
  168. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +107 -0
  169. data/spec/models/spree/reimbursement_type/store_credit_spec.rb +97 -0
  170. data/spec/models/spree/return_authorization_spec.rb +290 -0
  171. data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
  172. data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
  173. data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +85 -0
  174. data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
  175. data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
  176. data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
  177. data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
  178. data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
  179. data/spec/models/spree/return_item_spec.rb +776 -0
  180. data/spec/models/spree/returns_calculator_spec.rb +14 -0
  181. data/spec/models/spree/shipment_spec.rb +753 -0
  182. data/spec/models/spree/shipping_calculator_spec.rb +45 -0
  183. data/spec/models/spree/shipping_manifest_spec.rb +94 -0
  184. data/spec/models/spree/shipping_method_spec.rb +88 -0
  185. data/spec/models/spree/shipping_rate_spec.rb +142 -0
  186. data/spec/models/spree/state_spec.rb +14 -0
  187. data/spec/models/spree/stock/availability_validator_spec.rb +83 -0
  188. data/spec/models/spree/stock/coordinator_spec.rb +116 -0
  189. data/spec/models/spree/stock/differentiator_spec.rb +39 -0
  190. data/spec/models/spree/stock/estimator_spec.rb +146 -0
  191. data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
  192. data/spec/models/spree/stock/package_spec.rb +163 -0
  193. data/spec/models/spree/stock/packer_spec.rb +91 -0
  194. data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
  195. data/spec/models/spree/stock/quantifier_spec.rb +115 -0
  196. data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
  197. data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
  198. data/spec/models/spree/stock/splitter/shipping_category_spec.rb +50 -0
  199. data/spec/models/spree/stock/splitter/weight_spec.rb +29 -0
  200. data/spec/models/spree/stock_item_spec.rb +444 -0
  201. data/spec/models/spree/stock_location_spec.rb +279 -0
  202. data/spec/models/spree/stock_movement_spec.rb +56 -0
  203. data/spec/models/spree/stock_transfer_spec.rb +290 -0
  204. data/spec/models/spree/store_credit_category_spec.rb +17 -0
  205. data/spec/models/spree/store_credit_event_spec.rb +314 -0
  206. data/spec/models/spree/store_credit_spec.rb +876 -0
  207. data/spec/models/spree/store_spec.rb +55 -0
  208. data/spec/models/spree/tax_category_spec.rb +27 -0
  209. data/spec/models/spree/tax_rate_spec.rb +378 -0
  210. data/spec/models/spree/taxon_spec.rb +74 -0
  211. data/spec/models/spree/taxonomy_spec.rb +18 -0
  212. data/spec/models/spree/tracker_spec.rb +21 -0
  213. data/spec/models/spree/transfer_item_spec.rb +264 -0
  214. data/spec/models/spree/unit_cancel_spec.rb +149 -0
  215. data/spec/models/spree/user_spec.rb +246 -0
  216. data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +23 -0
  217. data/spec/models/spree/variant/scopes_spec.rb +55 -0
  218. data/spec/models/spree/variant_property_rule_condition_spec.rb +15 -0
  219. data/spec/models/spree/variant_property_rule_spec.rb +83 -0
  220. data/spec/models/spree/variant_property_rule_value_spec.rb +18 -0
  221. data/spec/models/spree/variant_spec.rb +601 -0
  222. data/spec/models/spree/zone_spec.rb +305 -0
  223. data/spec/spec_helper.rb +80 -0
  224. data/spec/support/big_decimal.rb +5 -0
  225. data/spec/support/concerns/default_price.rb +34 -0
  226. data/spec/support/dummy_ability.rb +4 -0
  227. data/spec/support/test_gateway.rb +2 -0
  228. metadata +242 -2
@@ -0,0 +1,444 @@
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
+ context "inventory_cache_threshold is not set (default)" do
180
+ context "in_stock? changes" do
181
+ it "touches its variant" do
182
+ expect do
183
+ subject.set_count_on_hand(0)
184
+ end.to change { subject.variant.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.set_count_on_hand(-1)
192
+ end.to change { subject.variant.updated_at }
193
+ end
194
+ end
195
+ end
196
+
197
+ context "inventory_cache_threshold is set" do
198
+ before do
199
+ Spree::Config.inventory_cache_threshold = inventory_cache_threshold
200
+ end
201
+
202
+ let(:inventory_cache_threshold) { 5 }
203
+
204
+ it "count on hand falls below threshold" do
205
+ expect do
206
+ subject.set_count_on_hand(3)
207
+ end.to change { subject.variant.updated_at }
208
+ end
209
+
210
+ it "count on hand rises above threshold" do
211
+ subject.set_count_on_hand(2)
212
+ expect do
213
+ subject.set_count_on_hand(7)
214
+ end.to change { subject.variant.updated_at }
215
+ end
216
+
217
+ it "count on hand stays below threshold" do
218
+ subject.set_count_on_hand(2)
219
+ expect do
220
+ subject.set_count_on_hand(3)
221
+ end.to change { subject.variant.updated_at }
222
+ end
223
+
224
+ it "count on hand stays above threshold" do
225
+ expect do
226
+ subject.set_count_on_hand(8)
227
+ end.not_to change { subject.variant.updated_at }
228
+ end
229
+ end
230
+
231
+ context "when deprecated binary_inventory_cache is used" do
232
+ before do
233
+ Spree::Config.binary_inventory_cache = binary_inventory_cache
234
+ allow(ActiveSupport::Deprecation).to receive(:warn)
235
+ subject.set_count_on_hand(9)
236
+ end
237
+
238
+ context "binary_inventory_cache is set to true" do
239
+ let(:binary_inventory_cache) { true }
240
+
241
+ it "logs a deprecation warning" do
242
+ expect(ActiveSupport::Deprecation).to have_received(:warn)
243
+ end
244
+ end
245
+
246
+ context "binary_inventory_cache is set to false" do
247
+ let(:binary_inventory_cache) { false }
248
+ it "inventory_cache_threshold remains nil" do
249
+ expect(Spree::Config.inventory_cache_threshold).to be_nil
250
+ end
251
+
252
+ it "does not log a deprecation warning" do
253
+ expect(ActiveSupport::Deprecation).not_to have_received(:warn)
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ describe "#after_touch" do
260
+ it "touches its variant" do
261
+ expect do
262
+ subject.touch
263
+ end.to change { subject.variant.updated_at }
264
+ end
265
+ end
266
+
267
+ # Regression test for #4651
268
+ context "variant" do
269
+ it "can be found even if the variant is deleted" do
270
+ subject.variant.destroy
271
+ expect(subject.reload.variant).not_to be_nil
272
+ end
273
+ end
274
+
275
+ describe 'validations' do
276
+ describe 'count_on_hand' do
277
+ shared_examples_for 'valid count_on_hand' do
278
+ before(:each) do
279
+ subject.save
280
+ end
281
+
282
+ it 'has :no errors_on' do
283
+ expect(subject.errors_on(:count_on_hand).size).to eq(0)
284
+ end
285
+ end
286
+
287
+ shared_examples_for 'not valid count_on_hand' do
288
+ before(:each) do
289
+ subject.save
290
+ end
291
+
292
+ it 'has 1 error_on' do
293
+ expect(subject.error_on(:count_on_hand).size).to eq(1)
294
+ end
295
+ it { expect(subject.errors[:count_on_hand]).to include 'must be greater than or equal to 0' }
296
+ end
297
+
298
+ context 'when count_on_hand not changed' do
299
+ context 'when not backorderable' do
300
+ before(:each) do
301
+ subject.backorderable = false
302
+ end
303
+ it_should_behave_like 'valid count_on_hand'
304
+ end
305
+
306
+ context 'when backorderable' do
307
+ before(:each) do
308
+ subject.backorderable = true
309
+ end
310
+ it_should_behave_like 'valid count_on_hand'
311
+ end
312
+ end
313
+
314
+ context 'when count_on_hand changed' do
315
+ context 'when backorderable' do
316
+ before(:each) do
317
+ subject.backorderable = true
318
+ end
319
+ context 'when both count_on_hand and count_on_hand_was are positive' do
320
+ context 'when count_on_hand is greater than count_on_hand_was' do
321
+ before(:each) do
322
+ subject.update_column(:count_on_hand, 3)
323
+ subject.send(:count_on_hand=, subject.count_on_hand + 3)
324
+ end
325
+ it_should_behave_like 'valid count_on_hand'
326
+ end
327
+
328
+ context 'when count_on_hand is smaller than count_on_hand_was' do
329
+ before(:each) do
330
+ subject.update_column(:count_on_hand, 3)
331
+ subject.send(:count_on_hand=, subject.count_on_hand - 2)
332
+ end
333
+
334
+ it_should_behave_like 'valid count_on_hand'
335
+ end
336
+ end
337
+
338
+ context 'when both count_on_hand and count_on_hand_was are negative' do
339
+ context 'when count_on_hand is greater than count_on_hand_was' do
340
+ before(:each) do
341
+ subject.update_column(:count_on_hand, -3)
342
+ subject.send(:count_on_hand=, subject.count_on_hand + 2)
343
+ end
344
+ it_should_behave_like 'valid count_on_hand'
345
+ end
346
+
347
+ context 'when count_on_hand is smaller than count_on_hand_was' do
348
+ before(:each) do
349
+ subject.update_column(:count_on_hand, 3)
350
+ subject.send(:count_on_hand=, subject.count_on_hand - 3)
351
+ end
352
+
353
+ it_should_behave_like 'valid count_on_hand'
354
+ end
355
+ end
356
+
357
+ context 'when both count_on_hand is positive and count_on_hand_was is negative' do
358
+ context 'when count_on_hand is greater 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 + 6)
362
+ end
363
+ it_should_behave_like 'valid count_on_hand'
364
+ end
365
+ end
366
+
367
+ context 'when both count_on_hand is negative and count_on_hand_was is positive' do
368
+ context 'when count_on_hand is greater than count_on_hand_was' do
369
+ before(:each) do
370
+ subject.update_column(:count_on_hand, 3)
371
+ subject.send(:count_on_hand=, subject.count_on_hand - 6)
372
+ end
373
+ it_should_behave_like 'valid count_on_hand'
374
+ end
375
+ end
376
+ end
377
+
378
+ context 'when not backorderable' do
379
+ before(:each) do
380
+ subject.backorderable = false
381
+ end
382
+
383
+ context 'when both count_on_hand and count_on_hand_was are positive' do
384
+ context 'when count_on_hand is greater than count_on_hand_was' do
385
+ before(:each) do
386
+ subject.update_column(:count_on_hand, 3)
387
+ subject.send(:count_on_hand=, subject.count_on_hand + 3)
388
+ end
389
+ it_should_behave_like 'valid count_on_hand'
390
+ end
391
+
392
+ context 'when count_on_hand is smaller than count_on_hand_was' do
393
+ before(:each) do
394
+ subject.update_column(:count_on_hand, 3)
395
+ subject.send(:count_on_hand=, subject.count_on_hand - 2)
396
+ end
397
+
398
+ it_should_behave_like 'valid count_on_hand'
399
+ end
400
+ end
401
+
402
+ context 'when both count_on_hand and count_on_hand_was are negative' do
403
+ context 'when count_on_hand is greater than count_on_hand_was' do
404
+ before(:each) do
405
+ subject.update_column(:count_on_hand, -3)
406
+ subject.send(:count_on_hand=, subject.count_on_hand + 2)
407
+ end
408
+ it_should_behave_like 'valid count_on_hand'
409
+ end
410
+
411
+ context 'when count_on_hand is smaller than count_on_hand_was' do
412
+ before(:each) do
413
+ subject.update_column(:count_on_hand, -3)
414
+ subject.send(:count_on_hand=, subject.count_on_hand - 3)
415
+ end
416
+
417
+ it_should_behave_like 'not valid count_on_hand'
418
+ end
419
+ end
420
+
421
+ context 'when both count_on_hand is positive and count_on_hand_was is negative' do
422
+ context 'when count_on_hand is greater than count_on_hand_was' do
423
+ before(:each) do
424
+ subject.update_column(:count_on_hand, -3)
425
+ subject.send(:count_on_hand=, subject.count_on_hand + 6)
426
+ end
427
+ it_should_behave_like 'valid count_on_hand'
428
+ end
429
+ end
430
+
431
+ context 'when both count_on_hand is negative and count_on_hand_was is positive' do
432
+ context 'when count_on_hand is greater than count_on_hand_was' do
433
+ before(:each) do
434
+ subject.update_column(:count_on_hand, 3)
435
+ subject.send(:count_on_hand=, subject.count_on_hand - 6)
436
+ end
437
+ it_should_behave_like 'not valid count_on_hand'
438
+ end
439
+ end
440
+ end
441
+ end
442
+ end
443
+ end
444
+ end
@@ -0,0 +1,279 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe StockLocation, :type => :model do
5
+ subject { create(:stock_location_with_items, backorderable_default: true) }
6
+ let(:stock_item) { subject.stock_items.order(:id).first }
7
+ let(:variant) { stock_item.variant }
8
+
9
+ it 'creates stock_items for all variants' do
10
+ expect(subject.stock_items.count).to eq Variant.count
11
+ end
12
+
13
+ context "handling stock items" do
14
+ let!(:variant) { create(:variant) }
15
+
16
+ context "given a variant" do
17
+ subject { StockLocation.create(name: "testing", propagate_all_variants: false) }
18
+
19
+ context "set up" do
20
+ it "creates stock item" do
21
+ expect(subject).to receive(:propagate_variant)
22
+ subject.set_up_stock_item(variant)
23
+ end
24
+
25
+ context "stock item exists" do
26
+ let!(:stock_item) { subject.propagate_variant(variant) }
27
+
28
+ it "returns existing stock item" do
29
+ expect(subject.set_up_stock_item(variant)).to eq(stock_item)
30
+ end
31
+ end
32
+ end
33
+
34
+ context "propagate variants" do
35
+ let(:stock_item) { subject.propagate_variant(variant) }
36
+
37
+ it "creates a new stock item" do
38
+ expect {
39
+ subject.propagate_variant(variant)
40
+ }.to change{ StockItem.count }.by(1)
41
+ end
42
+
43
+ context "passes backorderable default config" do
44
+ context "true" do
45
+ before { subject.backorderable_default = true }
46
+ it { expect(stock_item.backorderable).to be true }
47
+ end
48
+
49
+ context "false" do
50
+ before { subject.backorderable_default = false }
51
+ it { expect(stock_item.backorderable).to be false }
52
+ end
53
+ end
54
+ end
55
+
56
+ context "propagate all variants" do
57
+ subject { StockLocation.new(name: "testing") }
58
+
59
+ context "true" do
60
+ before { subject.propagate_all_variants = true }
61
+
62
+ specify do
63
+ expect(subject).to receive(:propagate_variant).at_least(:once)
64
+ subject.save!
65
+ end
66
+ end
67
+
68
+ context "false" do
69
+ before { subject.propagate_all_variants = false }
70
+
71
+ specify do
72
+ expect(subject).not_to receive(:propagate_variant)
73
+ subject.save!
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ it 'finds a stock_item for a variant' do
81
+ stock_item = subject.stock_item(variant)
82
+ expect(stock_item.count_on_hand).to eq 10
83
+ end
84
+
85
+ it 'finds a stock_item for a variant by id' do
86
+ stock_item = subject.stock_item(variant.id)
87
+ expect(stock_item.variant).to eq variant
88
+ end
89
+
90
+ it 'returns nil when stock_item is not found for variant' do
91
+ stock_item = subject.stock_item(100)
92
+ expect(stock_item).to be_nil
93
+ end
94
+
95
+ describe '#stock_item_or_create' do
96
+ before do
97
+ variant = create(:variant)
98
+ variant.stock_items.destroy_all
99
+ variant.save
100
+ end
101
+
102
+ it 'creates a stock_item if not found for a variant' do
103
+ stock_item = subject.stock_item_or_create(variant)
104
+ expect(stock_item.variant).to eq variant
105
+ end
106
+
107
+ it 'creates a stock_item if not found for a variant_id' do
108
+ stock_item = subject.stock_item_or_create(variant.id)
109
+ expect(stock_item.variant).to eq variant
110
+ end
111
+ end
112
+
113
+ it 'finds a count_on_hand for a variant' do
114
+ expect(subject.count_on_hand(variant)).to eq 10
115
+ end
116
+
117
+ it 'finds determines if you a variant is backorderable' do
118
+ expect(subject.backorderable?(variant)).to be true
119
+ end
120
+
121
+ it 'restocks a variant with a positive stock movement' do
122
+ originator = double
123
+ expect(subject).to receive(:move).with(variant, 5, originator)
124
+ subject.restock(variant, 5, originator)
125
+ end
126
+
127
+ it 'unstocks a variant with a negative stock movement' do
128
+ originator = double
129
+ expect(subject).to receive(:move).with(variant, -5, originator)
130
+ subject.unstock(variant, 5, originator)
131
+ end
132
+
133
+ it 'it creates a stock_movement' do
134
+ expect {
135
+ subject.move variant, 5
136
+ }.to change { subject.stock_movements.where(stock_item_id: stock_item).count }.by(1)
137
+ end
138
+
139
+ it 'can be deactivated' do
140
+ create(:stock_location, :active => true)
141
+ create(:stock_location, :active => false)
142
+ expect(Spree::StockLocation.active.count).to eq 1
143
+ end
144
+
145
+ it 'ensures only one stock location is default at a time' do
146
+ first = create(:stock_location, :active => true, :default => true)
147
+ second = create(:stock_location, :active => true, :default => true)
148
+
149
+ expect(first.reload.default).to eq false
150
+ expect(second.reload.default).to eq true
151
+
152
+ first.default = true
153
+ first.save!
154
+
155
+ expect(first.reload.default).to eq true
156
+ expect(second.reload.default).to eq false
157
+ end
158
+
159
+ context 'fill_status' do
160
+ it 'all on_hand with no backordered' do
161
+ on_hand, backordered = subject.fill_status(variant, 5)
162
+ expect(on_hand).to eq 5
163
+ expect(backordered).to eq 0
164
+ end
165
+
166
+ it 'some on_hand with some backordered' do
167
+ on_hand, backordered = subject.fill_status(variant, 20)
168
+ expect(on_hand).to eq 10
169
+ expect(backordered).to eq 10
170
+ end
171
+
172
+ it 'zero on_hand with all backordered' do
173
+ zero_stock_item = mock_model(StockItem,
174
+ count_on_hand: 0,
175
+ backorderable?: true)
176
+ expect(subject).to receive(:stock_item).with(variant).and_return(zero_stock_item)
177
+
178
+ on_hand, backordered = subject.fill_status(variant, 20)
179
+ expect(on_hand).to eq 0
180
+ expect(backordered).to eq 20
181
+ end
182
+
183
+ context 'when backordering is not allowed' do
184
+ before do
185
+ @stock_item = mock_model(StockItem, backorderable?: false)
186
+ expect(subject).to receive(:stock_item).with(variant).and_return(@stock_item)
187
+ end
188
+
189
+ it 'all on_hand' do
190
+ allow(@stock_item).to receive_messages(count_on_hand: 10)
191
+
192
+ on_hand, backordered = subject.fill_status(variant, 5)
193
+ expect(on_hand).to eq 5
194
+ expect(backordered).to eq 0
195
+ end
196
+
197
+ it 'some on_hand' do
198
+ allow(@stock_item).to receive_messages(count_on_hand: 10)
199
+
200
+ on_hand, backordered = subject.fill_status(variant, 20)
201
+ expect(on_hand).to eq 10
202
+ expect(backordered).to eq 0
203
+ end
204
+
205
+ it 'zero on_hand' do
206
+ allow(@stock_item).to receive_messages(count_on_hand: 0)
207
+
208
+ on_hand, backordered = subject.fill_status(variant, 20)
209
+ expect(on_hand).to eq 0
210
+ expect(backordered).to eq 0
211
+ end
212
+ end
213
+
214
+ context 'without stock_items' do
215
+ subject { create(:stock_location) }
216
+ let(:variant) { create(:base_variant) }
217
+
218
+ it 'zero on_hand and backordered' do
219
+ subject
220
+ variant.stock_items.destroy_all
221
+ on_hand, backordered = subject.fill_status(variant, 1)
222
+ expect(on_hand).to eq 0
223
+ expect(backordered).to eq 0
224
+ end
225
+ end
226
+ end
227
+
228
+ context '#state_text' do
229
+ context 'state is blank' do
230
+ subject { StockLocation.create(name: "testing", state: nil, state_name: 'virginia') }
231
+ specify { expect(subject.state_text).to eq('virginia') }
232
+ end
233
+
234
+ context 'both name and abbr is present' do
235
+ let(:state) { stub_model(Spree::State, name: 'virginia', abbr: 'va') }
236
+ subject { StockLocation.create(name: "testing", state: state, state_name: nil) }
237
+ specify { expect(subject.state_text).to eq('va') }
238
+ end
239
+
240
+ context 'only name is present' do
241
+ let(:state) { stub_model(Spree::State, name: 'virginia', abbr: nil) }
242
+ subject { StockLocation.create(name: "testing", state: state, state_name: nil) }
243
+ specify { expect(subject.state_text).to eq('virginia') }
244
+ end
245
+ end
246
+
247
+
248
+ describe "#move" do
249
+ let!(:variant) { create(:variant) }
250
+ def move
251
+ subject.move(variant, quantity)
252
+ end
253
+
254
+ context "no stock item exists" do
255
+ before { subject.stock_items.destroy_all }
256
+ context "positive movement" do
257
+ let(:quantity) { 1 }
258
+ it "creates a stock item" do
259
+ expect { move }.to change { subject.stock_items.count }.by 1
260
+ end
261
+ end
262
+
263
+ # We should not be creating stock items that do not exist
264
+ # for the sake of a negative movement.
265
+ context "negative movement" do
266
+ let(:quantity) { -1 }
267
+ it "raises an error" do
268
+ expect {
269
+ expect {
270
+ move
271
+ }.to raise_error StockLocation::InvalidMovementError
272
+ }.not_to change { subject.stock_items.count }
273
+ end
274
+ end
275
+
276
+ end
277
+ end
278
+ end
279
+ end