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,307 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::InventoryUnit, :type => :model do
4
+ let(:stock_location) { create(:stock_location_with_items) }
5
+ let(:stock_item) { stock_location.stock_items.order(:id).first }
6
+ let(:line_item) { create(:line_item, variant: stock_item.variant) }
7
+
8
+ context "#backordered_for_stock_item" do
9
+ let(:order) do
10
+ order = create(:order, state: 'complete', ship_address: create(:ship_address))
11
+ order.completed_at = Time.now
12
+ create(:shipment, order: order, stock_location: stock_location)
13
+ order.shipments.reload
14
+ create(:line_item, order: order, variant: stock_item.variant)
15
+ order.line_items.reload
16
+ order.tap(&:save!)
17
+ end
18
+
19
+ let(:shipment) do
20
+ order.shipments.first
21
+ end
22
+
23
+ let(:shipping_method) do
24
+ shipment.shipping_methods.first
25
+ end
26
+
27
+ let!(:unit) do
28
+ unit = shipment.inventory_units.first
29
+ unit.state = 'backordered'
30
+ unit.variant_id = stock_item.variant.id
31
+ unit.order_id = order.id
32
+ unit.line_item = line_item
33
+ unit.tap(&:save!)
34
+ end
35
+
36
+ before do
37
+ stock_item.set_count_on_hand(-2)
38
+ end
39
+
40
+ # Regression for #3066
41
+ it "returns modifiable objects" do
42
+ units = Spree::InventoryUnit.backordered_for_stock_item(stock_item)
43
+ expect { units.first.save! }.to_not raise_error
44
+ end
45
+
46
+ it "finds inventory units from its stock location when the unit's variant matches the stock item's variant" do
47
+ expect(Spree::InventoryUnit.backordered_for_stock_item(stock_item)).to match_array([unit])
48
+ end
49
+
50
+ it "does not find inventory units that aren't backordered" do
51
+ on_hand_unit = shipment.inventory_units.build
52
+ on_hand_unit.state = 'on_hand'
53
+ on_hand_unit.order_id = order.id
54
+ on_hand_unit.line_item = line_item
55
+ on_hand_unit.variant = stock_item.variant
56
+ on_hand_unit.save!
57
+
58
+ expect(Spree::InventoryUnit.backordered_for_stock_item(stock_item)).not_to include(on_hand_unit)
59
+ end
60
+
61
+ it "does not find inventory units that don't match the stock item's variant" do
62
+ other_variant_unit = shipment.inventory_units.build
63
+ other_variant_unit.state = 'backordered'
64
+ other_variant_unit.order_id = order.id
65
+ other_variant_unit.line_item = line_item
66
+ other_variant_unit.variant = create(:variant)
67
+ other_variant_unit.save!
68
+
69
+ expect(Spree::InventoryUnit.backordered_for_stock_item(stock_item)).not_to include(other_variant_unit)
70
+ end
71
+
72
+ it "does not change shipping cost when fulfilling the order" do
73
+ current_shipment_cost = shipment.cost
74
+ shipping_method.calculator.set_preference(:amount, current_shipment_cost + 5.0)
75
+ stock_item.set_count_on_hand(0)
76
+ expect(shipment.reload.cost).to eq(current_shipment_cost)
77
+ end
78
+
79
+ context "other shipments" do
80
+ let(:other_order) do
81
+ order = create(:order)
82
+ order.state = 'payment'
83
+ order.completed_at = nil
84
+ order.tap(&:save!)
85
+ end
86
+
87
+ let(:other_shipment) do
88
+ shipment = Spree::Shipment.new
89
+ shipment.stock_location = stock_location
90
+ shipment.shipping_methods << create(:shipping_method)
91
+ shipment.order = other_order
92
+ # We don't care about this in this test
93
+ allow(shipment).to receive(:ensure_correct_adjustment)
94
+ shipment.tap(&:save!)
95
+ end
96
+
97
+ let!(:other_unit) do
98
+ unit = other_shipment.inventory_units.build
99
+ unit.state = 'backordered'
100
+ unit.variant_id = stock_item.variant.id
101
+ unit.order_id = other_order.id
102
+ unit.line_item = line_item
103
+ unit.tap(&:save!)
104
+ end
105
+
106
+ it "does not find inventory units belonging to incomplete orders" do
107
+ expect(Spree::InventoryUnit.backordered_for_stock_item(stock_item)).not_to include(other_unit)
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+
114
+ context "variants deleted" do
115
+ let!(:unit) { create(:inventory_unit) }
116
+
117
+ it "can still fetch variant" do
118
+ unit.variant.destroy
119
+ expect(unit.reload.variant).to be_a Spree::Variant
120
+ end
121
+
122
+ it "can still fetch variants by eager loading (remove default_scope)" do
123
+ skip "find a way to remove default scope when eager loading associations"
124
+ unit.variant.destroy
125
+ expect(Spree::InventoryUnit.joins(:variant).includes(:variant).first.variant).to be_a Spree::Variant
126
+ end
127
+ end
128
+
129
+ context "#finalize_units!" do
130
+ let!(:stock_location) { create(:stock_location) }
131
+ let(:variant) { create(:variant) }
132
+ let(:inventory_units) { [
133
+ create(:inventory_unit, variant: variant),
134
+ create(:inventory_unit, variant: variant)
135
+ ] }
136
+
137
+ it "should create a stock movement" do
138
+ Spree::InventoryUnit.finalize_units!(inventory_units)
139
+ expect(inventory_units.any?(&:pending)).to be false
140
+ end
141
+ end
142
+
143
+ describe "#current_or_new_return_item" do
144
+ before { allow(inventory_unit).to receive_messages(pre_tax_amount: 100.0) }
145
+
146
+ subject { inventory_unit.current_or_new_return_item }
147
+
148
+ context "associated with a return item" do
149
+ let(:return_item) { create(:return_item) }
150
+ let(:inventory_unit) { return_item.inventory_unit }
151
+
152
+ it "returns a persisted return item" do
153
+ expect(subject).to be_persisted
154
+ end
155
+
156
+ it "returns it's associated return_item" do
157
+ expect(subject).to eq return_item
158
+ end
159
+ end
160
+
161
+ context "no associated return item" do
162
+ let(:inventory_unit) { create(:inventory_unit) }
163
+
164
+ it "returns a new return item" do
165
+ expect(subject).to_not be_persisted
166
+ end
167
+
168
+ it "associates itself to the new return_item" do
169
+ expect(subject.inventory_unit).to eq inventory_unit
170
+ end
171
+ end
172
+ end
173
+
174
+ describe '#additional_tax_total' do
175
+ let(:quantity) { 2 }
176
+ let(:line_item_additional_tax_total) { 10.00 }
177
+ let(:line_item) do
178
+ build(:line_item, {
179
+ quantity: quantity,
180
+ additional_tax_total: line_item_additional_tax_total,
181
+ })
182
+ end
183
+
184
+ subject do
185
+ build(:inventory_unit, line_item: line_item)
186
+ end
187
+
188
+ it 'is the correct amount' do
189
+ expect(subject.additional_tax_total).to eq line_item_additional_tax_total / quantity
190
+ end
191
+ end
192
+
193
+ describe '#included_tax_total' do
194
+ let(:quantity) { 2 }
195
+ let(:line_item_included_tax_total) { 10.00 }
196
+ let(:line_item) do
197
+ build(:line_item, {
198
+ quantity: quantity,
199
+ included_tax_total: line_item_included_tax_total,
200
+ })
201
+ end
202
+
203
+ subject do
204
+ build(:inventory_unit, line_item: line_item)
205
+ end
206
+
207
+ it 'is the correct amount' do
208
+ expect(subject.included_tax_total).to eq line_item_included_tax_total / quantity
209
+ end
210
+ end
211
+
212
+ describe '#additional_tax_total' do
213
+ let(:quantity) { 2 }
214
+ let(:line_item_additional_tax_total) { 10.00 }
215
+ let(:line_item) do
216
+ build(:line_item, {
217
+ quantity: quantity,
218
+ additional_tax_total: line_item_additional_tax_total,
219
+ })
220
+ end
221
+
222
+ subject do
223
+ build(:inventory_unit, line_item: line_item)
224
+ end
225
+
226
+ it 'is the correct amount' do
227
+ expect(subject.additional_tax_total).to eq line_item_additional_tax_total / quantity
228
+ end
229
+ end
230
+
231
+ describe '#included_tax_total' do
232
+ let(:quantity) { 2 }
233
+ let(:line_item_included_tax_total) { 10.00 }
234
+ let(:line_item) do
235
+ build(:line_item, {
236
+ quantity: quantity,
237
+ included_tax_total: line_item_included_tax_total,
238
+ })
239
+ end
240
+
241
+ subject do
242
+ build(:inventory_unit, line_item: line_item)
243
+ end
244
+
245
+ it 'is the correct amount' do
246
+ expect(subject.included_tax_total).to eq line_item_included_tax_total / quantity
247
+ end
248
+ end
249
+
250
+ describe "#exchange_requested?" do
251
+
252
+ subject { inventory_unit.exchange_requested? }
253
+
254
+ context "return item contains inventory unit and was for an exchange" do
255
+ let(:exchange_return_item) { create(:exchange_return_item) }
256
+ let(:inventory_unit) { exchange_return_item.inventory_unit }
257
+ it { is_expected.to eq true }
258
+ end
259
+
260
+ context "return item does not contain inventory unit" do
261
+ let(:inventory_unit) { create(:inventory_unit) }
262
+ it { is_expected.to eq false }
263
+ end
264
+ end
265
+
266
+ context "destroy prevention" do
267
+ it "can be destroyed when on hand" do
268
+ inventory_unit = create(:inventory_unit, state: "on_hand")
269
+ expect(inventory_unit.destroy).to be_truthy
270
+ expect { inventory_unit.reload }.to raise_error(ActiveRecord::RecordNotFound)
271
+ end
272
+
273
+ it "can be destroyed when backordered" do
274
+ inventory_unit = create(:inventory_unit, state: "backordered")
275
+ expect(inventory_unit.destroy).to be_truthy
276
+ expect { inventory_unit.reload }.to raise_error(ActiveRecord::RecordNotFound)
277
+ end
278
+
279
+ it "cannot be destroyed when shipped" do
280
+ inventory_unit = create(:inventory_unit, state: "shipped")
281
+ expect(inventory_unit.destroy).to eq false
282
+ expect(inventory_unit.errors.full_messages.join).to match /Cannot destroy/
283
+ expect { inventory_unit.reload }.not_to raise_error
284
+ end
285
+
286
+ it "cannot be destroyed when returned" do
287
+ inventory_unit = create(:inventory_unit, state: "returned")
288
+ expect(inventory_unit.destroy).to eq false
289
+ expect(inventory_unit.errors.full_messages.join).to match /Cannot destroy/
290
+ expect { inventory_unit.reload }.not_to raise_error
291
+ end
292
+
293
+ it "cannot be destroyed if its shipment is ready" do
294
+ inventory_unit = create(:order_ready_to_ship).inventory_units.first
295
+ expect(inventory_unit.destroy).to eq false
296
+ expect(inventory_unit.errors.full_messages.join).to match /Cannot destroy/
297
+ expect { inventory_unit.reload }.not_to raise_error
298
+ end
299
+
300
+ it "cannot be destroyed if its shipment is shipped" do
301
+ inventory_unit = create(:shipped_order).inventory_units.first
302
+ expect(inventory_unit.destroy).to eq false
303
+ expect(inventory_unit.errors.full_messages.join).to match /Cannot destroy/
304
+ expect { inventory_unit.reload }.not_to raise_error
305
+ end
306
+ end
307
+ end
@@ -0,0 +1,256 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe ItemAdjustments, :type => :model do
5
+ let(:order) { create :order_with_line_items, line_items_count: 1 }
6
+ let(:line_item) { order.line_items.first }
7
+
8
+ let(:subject) { ItemAdjustments.new(line_item) }
9
+
10
+ context '#update' do
11
+ it "updates a linked adjustment" do
12
+ tax_rate = create(:tax_rate, :amount => 0.05)
13
+ adjustment = create(:adjustment, order: order, source: tax_rate, adjustable: line_item)
14
+ line_item.price = 10
15
+ line_item.tax_category = tax_rate.tax_category
16
+
17
+ subject.update
18
+ expect(line_item.adjustment_total).to eq(0.5)
19
+ expect(line_item.additional_tax_total).to eq(0.5)
20
+ end
21
+ end
22
+
23
+ context "taxes and promotions" do
24
+ let!(:tax_rate) do
25
+ create(:tax_rate, :amount => 0.05)
26
+ end
27
+
28
+ let!(:promotion) do
29
+ Spree::Promotion.create(:name => "$10 off")
30
+ end
31
+
32
+ let!(:promotion_action) do
33
+ calculator = Calculator::FlatRate.new(:preferred_amount => 10)
34
+ Promotion::Actions::CreateItemAdjustments.create calculator: calculator, promotion: promotion
35
+ end
36
+
37
+ before do
38
+ line_item.price = 20
39
+ line_item.tax_category = tax_rate.tax_category
40
+ line_item.save
41
+ create(:adjustment, order: order, source: promotion_action, adjustable: line_item)
42
+ end
43
+
44
+ context "tax included in price" do
45
+ before do
46
+ create(:adjustment,
47
+ :source => tax_rate,
48
+ :adjustable => line_item,
49
+ :order => order,
50
+ :included => true
51
+ )
52
+ end
53
+
54
+ it "tax has no bearing on final price" do
55
+ subject.update
56
+ line_item.reload
57
+ expect(line_item.included_tax_total).to eq(0.5)
58
+ expect(line_item.additional_tax_total).to eq(0)
59
+ expect(line_item.promo_total).to eq(-10)
60
+ expect(line_item.adjustment_total).to eq(-10)
61
+ end
62
+
63
+ it "tax linked to order" do
64
+ order.update!
65
+ order.reload
66
+ expect(order.included_tax_total).to eq(0.5)
67
+ expect(order.additional_tax_total).to eq(00)
68
+ end
69
+ end
70
+
71
+ context "tax excluded from price" do
72
+ before do
73
+ create(:adjustment,
74
+ :source => tax_rate,
75
+ :adjustable => line_item,
76
+ :order => order,
77
+ :included => false
78
+ )
79
+ end
80
+
81
+ it "tax applies to line item" do
82
+ subject.update
83
+ line_item.reload
84
+ # Taxable amount is: $20 (base) - $10 (promotion) = $10
85
+ # Tax rate is 5% (of $10).
86
+ expect(line_item.included_tax_total).to eq(0)
87
+ expect(line_item.additional_tax_total).to eq(0.5)
88
+ expect(line_item.promo_total).to eq(-10)
89
+ expect(line_item.adjustment_total).to eq(-9.5)
90
+ end
91
+
92
+ it "tax linked to order" do
93
+ order.update!
94
+ expect(order.included_tax_total).to eq(0)
95
+ expect(order.additional_tax_total).to eq(0.5)
96
+ end
97
+ end
98
+ end
99
+
100
+ context "best promotion is always applied" do
101
+ let(:calculator) { Calculator::FlatRate.new(:preferred_amount => 10) }
102
+
103
+ let(:source) do
104
+ Promotion::Actions::CreateItemAdjustments.create!(
105
+ calculator: calculator,
106
+ promotion: promotion,
107
+ )
108
+ end
109
+ let(:promotion) { create(:promotion) }
110
+
111
+ def create_adjustment(label, amount)
112
+ create(:adjustment, :order => order,
113
+ :adjustable => line_item,
114
+ :source => source,
115
+ :amount => amount,
116
+ :state => "closed",
117
+ :label => label,
118
+ :mandatory => false)
119
+ end
120
+
121
+ it "should make all but the most valuable promotion adjustment ineligible, leaving non promotion adjustments alone" do
122
+ create_adjustment("Promotion A", -100)
123
+ create_adjustment("Promotion B", -200)
124
+ create_adjustment("Promotion C", -300)
125
+ create(:adjustment, :order => order,
126
+ :adjustable => line_item,
127
+ :source => nil,
128
+ :amount => -500,
129
+ :state => "closed",
130
+ :label => "Some other credit")
131
+ line_item.adjustments.each {|a| a.update_column(:eligible, true)}
132
+
133
+ subject.update
134
+
135
+ expect(line_item.adjustments.promotion.eligible.count).to eq(1)
136
+ expect(line_item.adjustments.promotion.eligible.first.label).to eq('Promotion C')
137
+ end
138
+
139
+ it "should choose the most recent promotion adjustment when amounts are equal" do
140
+ # Using Timecop is a regression test
141
+ Timecop.freeze do
142
+ create_adjustment("Promotion A", -200)
143
+ create_adjustment("Promotion B", -200)
144
+ end
145
+ line_item.adjustments.each {|a| a.update_column(:eligible, true)}
146
+
147
+ subject.update
148
+
149
+ expect(line_item.adjustments.promotion.eligible.count).to eq(1)
150
+ expect(line_item.adjustments.promotion.eligible.first.label).to eq('Promotion B')
151
+ end
152
+
153
+ it "should choose the most recent promotion adjustment when amounts are equal" do
154
+ # Using Timecop is a regression test
155
+ Timecop.freeze do
156
+ create_adjustment("Promotion A", -200)
157
+ create_adjustment("Promotion B", -200)
158
+ end
159
+ line_item.adjustments.each {|a| a.update_column(:eligible, true)}
160
+
161
+ subject.update
162
+
163
+ expect(line_item.adjustments.promotion.eligible.count).to eq(1)
164
+ expect(line_item.adjustments.promotion.eligible.first.label).to eq('Promotion B')
165
+ end
166
+
167
+ context "when previously ineligible promotions become available" do
168
+ let(:order_promo1) { create(:promotion, :with_order_adjustment, :with_item_total_rule, weighted_order_adjustment_amount: 5, item_total_threshold_amount: 10) }
169
+ let(:order_promo2) { create(:promotion, :with_order_adjustment, :with_item_total_rule, weighted_order_adjustment_amount: 10, item_total_threshold_amount: 20) }
170
+ let(:order_promos) { [ order_promo1, order_promo2 ] }
171
+ let(:line_item_promo1) { create(:promotion, :with_line_item_adjustment, :with_item_total_rule, adjustment_rate: 2.5, item_total_threshold_amount: 10) }
172
+ let(:line_item_promo2) { create(:promotion, :with_line_item_adjustment, :with_item_total_rule, adjustment_rate: 5, item_total_threshold_amount: 20) }
173
+ let(:line_item_promos) { [ line_item_promo1, line_item_promo2 ] }
174
+ let(:order) { create(:order_with_line_items, line_items_count: 1) }
175
+
176
+ # Apply promotions in different sequences. Results should be the same.
177
+ promo_sequences = [
178
+ [ 0, 1 ],
179
+ [ 1, 0 ]
180
+ ]
181
+
182
+ promo_sequences.each do |promo_sequence|
183
+ it "should pick the best order-level promo according to current eligibility" do
184
+ # apply both promos to the order, even though only promo1 is eligible
185
+ order_promos[promo_sequence[0]].activate order: order
186
+ order_promos[promo_sequence[1]].activate order: order
187
+
188
+ order.reload
189
+ expect(order.all_adjustments.count).to eq(2), "Expected two adjustments (using sequence #{promo_sequence})"
190
+ expect(order.all_adjustments.eligible.count).to eq(1), "Expected one elegible adjustment (using sequence #{promo_sequence})"
191
+ expect(order.all_adjustments.eligible.first.source.promotion).to eq(order_promo1), "Expected promo1 to be used (using sequence #{promo_sequence})"
192
+
193
+ order.contents.add create(:variant, price: 10), 1
194
+ order.save
195
+
196
+ order.reload
197
+ expect(order.all_adjustments.count).to eq(2), "Expected two adjustments (using sequence #{promo_sequence})"
198
+ expect(order.all_adjustments.eligible.count).to eq(1), "Expected one elegible adjustment (using sequence #{promo_sequence})"
199
+ expect(order.all_adjustments.eligible.first.source.promotion).to eq(order_promo2), "Expected promo2 to be used (using sequence #{promo_sequence})"
200
+ end
201
+ end
202
+
203
+ promo_sequences.each do |promo_sequence|
204
+ it "should pick the best line-item-level promo according to current eligibility" do
205
+ # apply both promos to the order, even though only promo1 is eligible
206
+ line_item_promos[promo_sequence[0]].activate order: order
207
+ line_item_promos[promo_sequence[1]].activate order: order
208
+
209
+ order.reload
210
+ expect(order.all_adjustments.count).to eq(1), "Expected one adjustment (using sequence #{promo_sequence})"
211
+ expect(order.all_adjustments.eligible.count).to eq(1), "Expected one elegible adjustment (using sequence #{promo_sequence})"
212
+ # line_item_promo1 is the only one that has thus far met the order total threshold, it is the only promo which should be applied.
213
+ expect(order.all_adjustments.first.source.promotion).to eq(line_item_promo1), "Expected line_item_promo1 to be used (using sequence #{promo_sequence})"
214
+
215
+ order.contents.add create(:variant, price: 10), 1
216
+ order.save
217
+
218
+ order.reload
219
+ expect(order.all_adjustments.count).to eq(4), "Expected four adjustments (using sequence #{promo_sequence})"
220
+ expect(order.all_adjustments.eligible.count).to eq(2), "Expected two elegible adjustments (using sequence #{promo_sequence})"
221
+ order.all_adjustments.eligible.each do |adjustment|
222
+ expect(adjustment.source.promotion).to eq(line_item_promo2), "Expected line_item_promo2 to be used (using sequence #{promo_sequence})"
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ context "multiple adjustments and the best one is not eligible" do
229
+ let!(:promo_a) { create_adjustment("Promotion A", -100) }
230
+ let!(:promo_c) { create_adjustment("Promotion C", -300) }
231
+
232
+ before do
233
+ promo_a.update_column(:eligible, true)
234
+ promo_c.update_column(:eligible, false)
235
+ end
236
+
237
+ # regression for #3274
238
+ it "still makes the previous best eligible adjustment valid" do
239
+ subject.update
240
+ expect(line_item.adjustments.promotion.eligible.first.label).to eq('Promotion A')
241
+ end
242
+ end
243
+
244
+ it "should only leave one adjustment even if 2 have the same amount" do
245
+ create_adjustment("Promotion A", -100)
246
+ create_adjustment("Promotion B", -200)
247
+ create_adjustment("Promotion C", -200)
248
+
249
+ subject.update
250
+
251
+ expect(line_item.adjustments.promotion.eligible.count).to eq(1)
252
+ expect(line_item.adjustments.promotion.eligible.first.amount.to_i).to eq(-200)
253
+ end
254
+ end
255
+ end
256
+ end