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,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::ReturnItem::EligibilityValidator::InventoryShipped do
4
+ let(:return_item) { create(:return_item) }
5
+ let(:validator) { Spree::ReturnItem::EligibilityValidator::InventoryShipped.new(return_item) }
6
+
7
+ describe "#eligible_for_return?" do
8
+ before { allow(return_item.inventory_unit).to receive(:shipped?).and_return(true) }
9
+
10
+ subject { validator.eligible_for_return? }
11
+
12
+ context "the associated inventory unit is shipped" do
13
+ it "returns true" do
14
+ expect(subject).to eq true
15
+ end
16
+ end
17
+
18
+ context "the associated inventory unit is not shipped" do
19
+ before { allow(return_item.inventory_unit).to receive(:shipped?).and_return(false) }
20
+
21
+ it "returns false" do
22
+ expect(subject).to eq false
23
+ end
24
+
25
+ it "sets an error" do
26
+ subject
27
+ expect(validator.errors[:inventory_unit_shipped]).to eq Spree.t('return_item_inventory_unit_ineligible')
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "#requires_manual_intervention?" do
33
+ subject { validator.requires_manual_intervention? }
34
+
35
+ context "not eligible for return" do
36
+ before do
37
+ allow(return_item.inventory_unit).to receive(:shipped?).and_return(false)
38
+ validator.eligible_for_return?
39
+ end
40
+
41
+ it 'returns true if errors were added' do
42
+ expect(subject).to eq true
43
+ end
44
+ end
45
+
46
+ context "eligible for return" do
47
+ before do
48
+ allow(return_item.inventory_unit).to receive(:shipped?).and_return(true)
49
+ validator.eligible_for_return?
50
+ end
51
+
52
+ it 'returns false if no errors were added' do
53
+ expect(subject).to eq false
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::ReturnItem::EligibilityValidator::NoReimbursements do
4
+ let(:validator) { Spree::ReturnItem::EligibilityValidator::NoReimbursements.new(return_item) }
5
+
6
+ describe "#eligible_for_return?" do
7
+
8
+ subject { validator.eligible_for_return? }
9
+
10
+ context "inventory unit has already been reimbursed" do
11
+ let(:reimbursement) { create(:reimbursement) }
12
+ let(:return_item) { reimbursement.return_items.last }
13
+
14
+ it "returns false" do
15
+ expect(subject).to eq false
16
+ end
17
+
18
+ it "sets an error" do
19
+ subject
20
+ expect(validator.errors[:inventory_unit_reimbursed]).to eq Spree.t('return_item_inventory_unit_reimbursed')
21
+ end
22
+
23
+ context "but the return item has been expired" do
24
+ before { return_item.expired }
25
+
26
+ it "returns true" do
27
+ expect(subject).to eq true
28
+ end
29
+ end
30
+
31
+ context "but the return item has been canceled" do
32
+ before { return_item.cancel }
33
+
34
+ it "returns true" do
35
+ expect(subject).to eq true
36
+ end
37
+ end
38
+
39
+ context "but the return item has been unexchanged" do
40
+ before { return_item.unexchange }
41
+
42
+ it "returns true" do
43
+ expect(subject).to eq true
44
+ end
45
+ end
46
+ end
47
+
48
+ context "inventory unit has not been reimbursed" do
49
+ let(:return_item) { create(:return_item) }
50
+
51
+ it "returns true" do
52
+ expect(subject).to eq true
53
+ end
54
+ end
55
+ end
56
+
57
+ describe "#requires_manual_intervention?" do
58
+ subject { validator.requires_manual_intervention? }
59
+
60
+ context "not eligible for return" do
61
+ let(:reimbursement) { create(:reimbursement) }
62
+ let(:return_item) { reimbursement.return_items.last }
63
+
64
+ before do
65
+ validator.eligible_for_return?
66
+ end
67
+
68
+ it 'returns true if errors were added' do
69
+ expect(subject).to eq true
70
+ end
71
+ end
72
+
73
+ context "eligible for return" do
74
+ let(:return_item) { create(:return_item) }
75
+
76
+ before do
77
+ validator.eligible_for_return?
78
+ end
79
+
80
+ it 'returns false if no errors were added' do
81
+ expect(subject).to eq false
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::ReturnItem::EligibilityValidator::OrderCompleted do
4
+ let(:inventory_unit) { create(:inventory_unit, order: order) }
5
+ let(:return_item) { create(:return_item, inventory_unit: inventory_unit) }
6
+ let(:validator) { Spree::ReturnItem::EligibilityValidator::OrderCompleted.new(return_item) }
7
+
8
+ describe "#eligible_for_return?" do
9
+ subject { validator.eligible_for_return? }
10
+
11
+ context "the order was completed" do
12
+ let(:order) { create(:completed_order_with_totals) }
13
+
14
+ it "returns true" do
15
+ expect(subject).to be true
16
+ end
17
+ end
18
+
19
+ context "the order is not completed" do
20
+ let(:order) { create(:order) }
21
+
22
+ it "returns false" do
23
+ expect(subject).to be false
24
+ end
25
+
26
+ it "sets an error" do
27
+ subject
28
+ expect(validator.errors[:order_not_completed]).to eq Spree.t('return_item_order_not_completed')
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::ReturnItem::EligibilityValidator::RMARequired, :type => :model do
4
+ let(:return_item) { create(:return_item) }
5
+ let(:validator) { Spree::ReturnItem::EligibilityValidator::RMARequired.new(return_item) }
6
+
7
+ describe "#eligible_for_return?" do
8
+ subject { validator.eligible_for_return? }
9
+
10
+ context "there is an rma on the return item" do
11
+ it "returns true" do
12
+ expect(subject).to be true
13
+ end
14
+ end
15
+
16
+ context "there is no rma on the return item" do
17
+ before { allow(return_item).to receive(:return_authorization).and_return(nil) }
18
+
19
+ it "returns false" do
20
+ expect(subject).to be false
21
+ end
22
+
23
+ it "sets an error" do
24
+ subject
25
+ expect(validator.errors[:rma_required]).to eq Spree.t('return_item_rma_ineligible')
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::ReturnItem::EligibilityValidator::TimeSincePurchase, :type => :model do
4
+ let(:inventory_unit) { create(:inventory_unit, order: create(:shipped_order)) }
5
+ let(:return_item) { create(:return_item, inventory_unit: inventory_unit) }
6
+ let(:validator) { Spree::ReturnItem::EligibilityValidator::TimeSincePurchase.new(return_item) }
7
+
8
+ describe "#eligible_for_return?" do
9
+ subject { validator.eligible_for_return? }
10
+
11
+ context "it is within the return timeframe" do
12
+ it "returns true" do
13
+ completed_at = return_item.inventory_unit.order.completed_at - (Spree::Config[:return_eligibility_number_of_days].days / 2)
14
+ return_item.inventory_unit.order.update_attributes(completed_at: completed_at)
15
+ expect(subject).to be true
16
+ end
17
+ end
18
+
19
+ context "it is past the return timeframe" do
20
+ before do
21
+ completed_at = return_item.inventory_unit.order.completed_at - Spree::Config[:return_eligibility_number_of_days].days - 1.day
22
+ return_item.inventory_unit.order.update_attributes(completed_at: completed_at)
23
+ end
24
+
25
+ it "returns false" do
26
+ expect(subject).to be false
27
+ end
28
+
29
+ it "sets an error" do
30
+ subject
31
+ expect(validator.errors[:number_of_days]).to eq Spree.t('return_item_time_period_ineligible')
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module ReturnItem::ExchangeVariantEligibility
5
+ describe SameOptionValue, :type => :model do
6
+ describe ".eligible_variants" do
7
+ let(:color_option_type) { create(:option_type, name: "color") }
8
+ let(:waist_option_type) { create(:option_type, name: "waist") }
9
+ let(:inseam_option_type) { create(:option_type, name: "inseam") }
10
+
11
+ let(:blue_option_value) { create(:option_value, name: "blue", option_type: color_option_type) }
12
+ let(:red_option_value) { create(:option_value, name: "red", option_type: color_option_type) }
13
+
14
+ let(:three_two_waist_option_value) { create(:option_value, name: 32, option_type: waist_option_type) }
15
+ let(:three_four_waist_option_value) { create(:option_value, name: 34, option_type: waist_option_type) }
16
+
17
+ let(:three_zero_inseam_option_value) { create(:option_value, name: 30, option_type: inseam_option_type) }
18
+ let(:three_one_inseam_option_value) { create(:option_value, name: 31, option_type: inseam_option_type) }
19
+
20
+ let(:product) { create(:product, option_types: [color_option_type, waist_option_type, inseam_option_type]) }
21
+
22
+ let!(:variant) { create(:variant, product: product, option_values: [blue_option_value, three_two_waist_option_value, three_zero_inseam_option_value]) }
23
+ let!(:same_option_values_variant) { create(:variant, product: product, option_values: [blue_option_value, three_two_waist_option_value, three_one_inseam_option_value]) }
24
+ let!(:different_color_option_value_variant) { create(:variant, product: product, option_values: [red_option_value, three_two_waist_option_value, three_one_inseam_option_value]) }
25
+ let!(:different_waist_option_value_variant) { create(:variant, product: product, option_values: [blue_option_value, three_four_waist_option_value, three_one_inseam_option_value]) }
26
+
27
+ before do
28
+ @original_option_type_restrictions = SameOptionValue.option_type_restrictions
29
+ SameOptionValue.option_type_restrictions = ["color", "waist"]
30
+ end
31
+
32
+ after { SameOptionValue.option_type_restrictions = @original_option_type_restrictions }
33
+
34
+ subject { SameOptionValue.eligible_variants(variant.reload) }
35
+
36
+ it "returns all other variants for the same product with the same option value for the specified option type" do
37
+ Spree::StockItem.update_all(count_on_hand: 10)
38
+
39
+ expect(subject.sort).to eq [variant, same_option_values_variant].sort
40
+ end
41
+
42
+ it "does not return variants for another product" do
43
+ other_product_variant = create(:variant)
44
+ expect(subject).not_to include other_product_variant
45
+ end
46
+
47
+ context "no option value restrictions are specified" do
48
+ before do
49
+ @original_option_type_restrictions = SameOptionValue.option_type_restrictions
50
+ SameOptionValue.option_type_restrictions = []
51
+ end
52
+
53
+ after { SameOptionValue.option_type_restrictions = @original_option_type_restrictions }
54
+
55
+ it "returns all variants for the product" do
56
+ Spree::StockItem.update_all(count_on_hand: 10)
57
+
58
+ expect(subject.sort).to eq [variant, same_option_values_variant, different_waist_option_value_variant, different_color_option_value_variant].sort
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module ReturnItem::ExchangeVariantEligibility
5
+ describe SameProduct, :type => :model do
6
+ describe ".eligible_variants" do
7
+
8
+ context "product has no variants" do
9
+ it "returns the master variant for the same product" do
10
+ product = create(:product)
11
+ product.master.stock_items.first.update_column(:count_on_hand, 10)
12
+
13
+ expect(SameProduct.eligible_variants(product.master)).to eq [product.master]
14
+ end
15
+ end
16
+
17
+ context "product has variants" do
18
+ it "returns all variants for the same product" do
19
+ product = create(:product, variants: 3.times.map { create(:variant) })
20
+ product.variants.map { |v| v.stock_items.first.update_column(:count_on_hand, 10) }
21
+
22
+ expect(SameProduct.eligible_variants(product.variants.first).sort).to eq product.variants.sort
23
+ end
24
+ end
25
+
26
+ it "does not return variants for another product" do
27
+ variant = create(:variant)
28
+ other_product_variant = create(:variant)
29
+ expect(SameProduct.eligible_variants(variant)).not_to include other_product_variant
30
+ end
31
+
32
+ it "only returns variants that are on hand" do
33
+ product = create(:product, variants: 2.times.map { create(:variant) })
34
+ in_stock_variant = product.variants.first
35
+
36
+ in_stock_variant.stock_items.first.update_column(:count_on_hand, 10)
37
+ expect(SameProduct.eligible_variants(in_stock_variant)).to eq [in_stock_variant]
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,775 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples "an invalid state transition" do |status, expected_status|
4
+ let(:status) { status }
5
+
6
+ it "cannot transition to #{expected_status}" do
7
+ expect { subject }.to raise_error(StateMachines::InvalidTransition)
8
+ end
9
+ end
10
+
11
+ describe Spree::ReturnItem, :type => :model do
12
+
13
+ all_reception_statuses = Spree::ReturnItem.state_machines[:reception_status].states.map(&:name).map(&:to_s)
14
+ all_acceptance_statuses = Spree::ReturnItem.state_machines[:acceptance_status].states.map(&:name).map(&:to_s)
15
+
16
+ before do
17
+ allow_any_instance_of(Spree::Order).to receive(:return!).and_return(true)
18
+ end
19
+
20
+ describe '#receive!' do
21
+ let(:now) { Time.now }
22
+ let(:order) { create(:shipped_order)}
23
+ let(:inventory_unit) { create(:inventory_unit, order: order,state: 'shipped') }
24
+ let!(:customer_return) { create(:customer_return_without_return_items, return_items: [return_item], stock_location_id: inventory_unit.shipment.stock_location_id) }
25
+ let(:return_item) { create(:return_item, inventory_unit: inventory_unit) }
26
+
27
+ before do
28
+ inventory_unit.update_attributes!(state: 'shipped')
29
+ return_item.update_attributes!(reception_status: 'awaiting')
30
+ allow(return_item).to receive(:eligible_for_return?).and_return(true)
31
+ end
32
+
33
+ subject { return_item.receive! }
34
+
35
+ it 'returns the inventory unit' do
36
+ subject
37
+ expect(inventory_unit.reload.state).to eq 'returned'
38
+ end
39
+
40
+ it 'attempts to accept the return item' do
41
+ expect(return_item).to receive(:attempt_accept)
42
+ subject
43
+ end
44
+
45
+ context 'when there is a received return item with the same inventory unit' do
46
+ let!(:return_item_with_dupe_inventory_unit) { create(:return_item, inventory_unit: inventory_unit, reception_status: 'received') }
47
+
48
+ before do
49
+ assert_raises(StateMachines::InvalidTransition) { subject }
50
+ end
51
+
52
+ it 'does not receive the return item' do
53
+ expect(return_item.reception_status).to eq 'awaiting'
54
+ end
55
+
56
+ it 'adds an error to the return item' do
57
+ expect(return_item.errors[:inventory_unit]).to include "#{return_item.inventory_unit_id} has already been taken by return item #{return_item_with_dupe_inventory_unit.id}"
58
+ end
59
+ end
60
+
61
+ context 'when the received item is actually the exchange (aka customer changed mind about exchange)' do
62
+ let(:exchange_inventory_unit) { create(:inventory_unit, order: order,state: 'shipped') }
63
+ let!(:return_item_with_exchange) { create(:return_item, inventory_unit: inventory_unit, exchange_inventory_unit: exchange_inventory_unit) }
64
+ let!(:return_item_in_lieu) { create(:return_item, inventory_unit: exchange_inventory_unit)}
65
+
66
+ it 'unexchanges original return item' do
67
+ return_item_in_lieu.receive!
68
+
69
+ return_item_with_exchange.reload
70
+ return_item_in_lieu.reload
71
+ expect(return_item_with_exchange.reception_status).to eq 'unexchanged'
72
+ expect(return_item_in_lieu.reception_status).to eq 'received'
73
+ expect(return_item_in_lieu.pre_tax_amount).to eq 0
74
+ expect(inventory_unit.reload.state).to eq 'shipped'
75
+ expect(exchange_inventory_unit.reload.state).to eq 'returned'
76
+ end
77
+ end
78
+
79
+ context 'with a stock location' do
80
+ let(:stock_location) { customer_return.stock_location }
81
+ let(:stock_item) { stock_location.stock_item(inventory_unit.variant) }
82
+
83
+ before do
84
+ inventory_unit.update_attributes!(state: 'shipped')
85
+ return_item.update_attributes!(reception_status: 'awaiting')
86
+ stock_location.update_attributes!(restock_inventory: true)
87
+ end
88
+
89
+ it 'increases the count on hand' do
90
+ expect { subject }.to change { stock_item.reload.count_on_hand }.by(1)
91
+ end
92
+
93
+ context 'when variant does not track inventory' do
94
+ before do
95
+ inventory_unit.update_attributes!(state: 'shipped')
96
+ inventory_unit.variant.update_attributes!(track_inventory: false)
97
+ return_item.update_attributes!(reception_status: 'awaiting')
98
+ end
99
+
100
+ it 'does not increase the count on hand' do
101
+ expect { subject }.to_not change { stock_item.reload.count_on_hand }
102
+ end
103
+ end
104
+
105
+ context "when the stock location's restock_inventory is false" do
106
+ before do
107
+ stock_location.update_attributes!(restock_inventory: false)
108
+ end
109
+
110
+ it 'does not increase the count on hand' do
111
+ expect { subject }.to_not change { stock_item.reload.count_on_hand }
112
+ end
113
+ end
114
+
115
+ context "when the inventory unit's variant does not yet have a stock item for the stock location it was returned to" do
116
+ before { inventory_unit.variant.stock_items.destroy_all }
117
+
118
+ it "creates a new stock item for the inventory unit with a count of 1" do
119
+ expect { subject }.to change(Spree::StockItem, :count).by(1)
120
+ stock_item = Spree::StockItem.last
121
+ expect(stock_item.variant).to eq inventory_unit.variant
122
+ expect(stock_item.count_on_hand).to eq 1
123
+ end
124
+
125
+ end
126
+
127
+ Spree::ReturnItem::INTERMEDIATE_RECEPTION_STATUSES.each do |status|
128
+ context "when the item was #{status}" do
129
+ before { return_item.update_attributes!(reception_status: status) }
130
+
131
+ it 'processes the inventory unit' do
132
+ subject
133
+ expect(return_item.inventory_unit.reload.state).to eq('returned')
134
+ end
135
+
136
+ it 'return remains accepted' do
137
+ subject
138
+ expect(return_item.acceptance_status).to eq('accepted')
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+
146
+ describe "#display_pre_tax_amount" do
147
+ let(:pre_tax_amount) { 21.22 }
148
+ let(:return_item) { build(:return_item, pre_tax_amount: pre_tax_amount) }
149
+
150
+ it "returns a Spree::Money" do
151
+ expect(return_item.display_pre_tax_amount).to eq Spree::Money.new(pre_tax_amount)
152
+ end
153
+ end
154
+
155
+ describe ".default_refund_amount_calculator" do
156
+ it "defaults to the default refund amount calculator" do
157
+ expect(Spree::ReturnItem.refund_amount_calculator).to eq Spree::Calculator::Returns::DefaultRefundAmount
158
+ end
159
+ end
160
+
161
+ describe "pre_tax_amount calculations on create" do
162
+ let(:inventory_unit) { build(:inventory_unit) }
163
+ before { subject.save! }
164
+
165
+ context "pre tax amount is not specified" do
166
+ subject { build(:return_item, inventory_unit: inventory_unit) }
167
+
168
+ context "not an exchange" do
169
+ it { expect(subject.pre_tax_amount).to eq Spree::Calculator::Returns::DefaultRefundAmount.new.compute(subject) }
170
+ end
171
+
172
+ context "an exchange" do
173
+ subject { build(:exchange_return_item) }
174
+
175
+ it { expect(subject.pre_tax_amount).to eq 0.0 }
176
+ end
177
+ end
178
+
179
+ context "pre tax amount is specified" do
180
+ subject { build(:return_item, inventory_unit: inventory_unit, pre_tax_amount: 100) }
181
+
182
+ it { expect(subject.pre_tax_amount).to eq 100 }
183
+ end
184
+ end
185
+
186
+ describe ".from_inventory_unit" do
187
+ let(:inventory_unit) { build(:inventory_unit) }
188
+
189
+ subject { Spree::ReturnItem.from_inventory_unit(inventory_unit) }
190
+
191
+ context "with a cancelled return item" do
192
+ let!(:return_item) { create(:return_item, inventory_unit: inventory_unit, reception_status: 'cancelled') }
193
+
194
+ it { is_expected.not_to be_persisted }
195
+ end
196
+
197
+ context 'with a expired item' do
198
+ let!(:return_item) { create(:return_item, inventory_unit: inventory_unit, reception_status: 'expired') }
199
+
200
+ it { is_expected.not_to be_persisted }
201
+ end
202
+
203
+ context "with a non-cancelled return item" do
204
+ let!(:return_item) { create(:return_item, inventory_unit: inventory_unit) }
205
+
206
+ it { is_expected.to be_persisted }
207
+ end
208
+ end
209
+
210
+ describe "reception_status state_machine" do
211
+ subject(:return_item) { create(:return_item) }
212
+
213
+ it "starts off in the awaiting state" do
214
+ expect(return_item).to be_awaiting
215
+ end
216
+ end
217
+
218
+ describe "acceptance_status state_machine" do
219
+ subject(:return_item) { create(:return_item) }
220
+
221
+ it "starts off in the pending state" do
222
+ expect(return_item).to be_pending
223
+ end
224
+ end
225
+
226
+ describe "#receive" do
227
+ let(:inventory_unit) { create(:inventory_unit, order: create(:shipped_order)) }
228
+ let(:return_item) { create(:return_item, reception_status: status, inventory_unit: inventory_unit) }
229
+
230
+ subject { return_item.receive! }
231
+
232
+ context "awaiting status" do
233
+ let(:status) { 'awaiting' }
234
+
235
+ before do
236
+ expect(return_item.inventory_unit).to receive(:return!)
237
+ end
238
+
239
+ before { subject }
240
+
241
+ it "transitions successfully" do
242
+ expect(return_item).to be_received
243
+ end
244
+ end
245
+
246
+ context "return_item has a reception status of cancelled" do
247
+ it_behaves_like "an invalid state transition", 'cancelled', 'received'
248
+ end
249
+ end
250
+
251
+ describe "#cancel" do
252
+ let(:return_item) { create(:return_item, reception_status: status) }
253
+
254
+ subject { return_item.cancel! }
255
+
256
+ context "awaiting status" do
257
+ let(:status) { 'awaiting' }
258
+
259
+ before { subject }
260
+
261
+ it "transitions successfully" do
262
+ expect(return_item).to be_cancelled
263
+ end
264
+ end
265
+
266
+ (all_reception_statuses - ['awaiting']).each do |invalid_transition_status|
267
+ context "return_item has a reception status of #{invalid_transition_status}" do
268
+ it_behaves_like "an invalid state transition", invalid_transition_status, 'cancelled'
269
+ end
270
+ end
271
+ end
272
+
273
+ {
274
+ give: 'given_to_customer',
275
+ lost: 'lost_in_transit',
276
+ wrong_item_shipped: 'shipped_wrong_item',
277
+ short_shipped: 'short_shipped'
278
+ }.each do |transition, status|
279
+ describe "##{transition}" do
280
+ let(:return_item) { create(:return_item, reception_status: status, inventory_unit: inventory_unit) }
281
+ let(:inventory_unit) { create(:inventory_unit, state: 'shipped') }
282
+ subject { return_item.public_send("#{transition}!") }
283
+ context "awaiting status" do
284
+ before do
285
+ return_item.update_attributes!(reception_status: 'awaiting')
286
+ allow(return_item).to receive(:eligible_for_return?).and_return(true)
287
+ end
288
+
289
+ it 'accepts the return' do
290
+ subject
291
+ expect(return_item.acceptance_status).to eq('accepted')
292
+ end
293
+
294
+ it 'does not decrease inventory' do
295
+ expect(return_item).to_not receive(:process_inventory_unit)
296
+ subject
297
+ end
298
+
299
+ it "transitions successfully" do
300
+ subject
301
+ expect(return_item.reception_status).to eq status
302
+ end
303
+ end
304
+
305
+ it_behaves_like "an invalid state transition", 'cancelled', status
306
+ end
307
+ end
308
+
309
+ describe "#attempt_accept" do
310
+ let(:return_item) { create(:return_item, acceptance_status: status) }
311
+ let(:validator_errors) { {} }
312
+ let(:validator_double) { double(errors: validator_errors) }
313
+
314
+ subject { return_item.attempt_accept! }
315
+
316
+ before do
317
+ allow(return_item).to receive(:validator).and_return(validator_double)
318
+ end
319
+
320
+ context "pending status" do
321
+ let(:status) { 'pending' }
322
+
323
+ before do
324
+ allow(return_item).to receive(:eligible_for_return?).and_return(true)
325
+ subject
326
+ end
327
+
328
+ it "transitions successfully" do
329
+ expect(return_item).to be_accepted
330
+ end
331
+
332
+ it "has no acceptance status errors" do
333
+ expect(return_item.acceptance_status_errors).to be_empty
334
+ end
335
+ end
336
+
337
+ (all_acceptance_statuses - ['accepted', 'pending']).each do |invalid_transition_status|
338
+ context "return_item has an acceptance status of #{invalid_transition_status}" do
339
+ it_behaves_like "an invalid state transition", invalid_transition_status, 'accepted'
340
+ end
341
+ end
342
+
343
+ context "not eligible for return" do
344
+ let(:status) { 'pending' }
345
+ let(:validator_errors) { { number_of_days: "Return Item is outside the eligible time period" } }
346
+
347
+ before do
348
+ allow(return_item).to receive(:eligible_for_return?).and_return(false)
349
+ end
350
+
351
+ context "manual intervention required" do
352
+ before do
353
+ allow(return_item).to receive(:requires_manual_intervention?).and_return(true)
354
+ subject
355
+ end
356
+
357
+ it "transitions to manual intervention required" do
358
+ expect(return_item).to be_manual_intervention_required
359
+ end
360
+
361
+ it "sets the acceptance status errors" do
362
+ expect(return_item.acceptance_status_errors).to eq validator_errors
363
+ end
364
+ end
365
+
366
+ context "manual intervention not required" do
367
+ before do
368
+ allow(return_item).to receive(:requires_manual_intervention?).and_return(false)
369
+ subject
370
+ end
371
+
372
+ it "transitions to rejected" do
373
+ expect(return_item).to be_rejected
374
+ end
375
+
376
+ it "sets the acceptance status errors" do
377
+ expect(return_item.acceptance_status_errors).to eq validator_errors
378
+ end
379
+ end
380
+ end
381
+ end
382
+
383
+ describe "#reject" do
384
+ let(:return_item) { create(:return_item, acceptance_status: status) }
385
+
386
+ subject { return_item.reject! }
387
+
388
+ context "pending status" do
389
+ let(:status) { 'pending' }
390
+
391
+ before { subject }
392
+
393
+ it "transitions successfully" do
394
+ expect(return_item).to be_rejected
395
+ end
396
+
397
+ it "has no acceptance status errors" do
398
+ expect(return_item.acceptance_status_errors).to be_empty
399
+ end
400
+ end
401
+
402
+ (all_acceptance_statuses - ['accepted', 'pending', 'manual_intervention_required']).each do |invalid_transition_status|
403
+ context "return_item has an acceptance status of #{invalid_transition_status}" do
404
+ it_behaves_like "an invalid state transition", invalid_transition_status, 'rejected'
405
+ end
406
+ end
407
+ end
408
+
409
+ describe "#accept" do
410
+ let(:return_item) { create(:return_item, acceptance_status: status) }
411
+
412
+ subject { return_item.accept! }
413
+
414
+ context "pending status" do
415
+ let(:status) { 'pending' }
416
+
417
+ before { subject }
418
+
419
+ it "transitions successfully" do
420
+ expect(return_item).to be_accepted
421
+ end
422
+
423
+ it "has no acceptance status errors" do
424
+ expect(return_item.acceptance_status_errors).to be_empty
425
+ end
426
+ end
427
+
428
+ (all_acceptance_statuses - ['accepted', 'pending', 'manual_intervention_required']).each do |invalid_transition_status|
429
+ context "return_item has an acceptance status of #{invalid_transition_status}" do
430
+ it_behaves_like "an invalid state transition", invalid_transition_status, 'accepted'
431
+ end
432
+ end
433
+ end
434
+
435
+ describe "#require_manual_intervention" do
436
+ let(:return_item) { create(:return_item, acceptance_status: status) }
437
+
438
+ subject { return_item.require_manual_intervention! }
439
+
440
+ context "pending status" do
441
+ let(:status) { 'pending' }
442
+
443
+ before { subject }
444
+
445
+ it "transitions successfully" do
446
+ expect(return_item).to be_manual_intervention_required
447
+ end
448
+
449
+ it "has no acceptance status errors" do
450
+ expect(return_item.acceptance_status_errors).to be_empty
451
+ end
452
+ end
453
+
454
+ (all_acceptance_statuses - ['accepted', 'pending', 'manual_intervention_required']).each do |invalid_transition_status|
455
+ context "return_item has an acceptance status of #{invalid_transition_status}" do
456
+ it_behaves_like "an invalid state transition", invalid_transition_status, 'manual_intervention_required'
457
+ end
458
+ end
459
+ end
460
+
461
+ describe 'validity for reimbursements' do
462
+ let(:return_item) { create(:return_item, acceptance_status: acceptance_status) }
463
+ let(:acceptance_status) { 'pending' }
464
+
465
+ before { return_item.reimbursement = build(:reimbursement) }
466
+
467
+ subject { return_item }
468
+
469
+ context 'when acceptance_status is accepted' do
470
+ let(:acceptance_status) { 'accepted' }
471
+
472
+ it 'is valid' do
473
+ expect(subject).to be_valid
474
+ end
475
+ end
476
+
477
+ context 'when acceptance_status is accepted' do
478
+ let(:acceptance_status) { 'pending' }
479
+
480
+ it 'is valid' do
481
+ expect(subject).to_not be_valid
482
+ expect(subject.errors.messages).to eq({reimbursement: [I18n.t(:cannot_be_associated_unless_accepted, scope: 'activerecord.errors.models.spree/return_item.attributes.reimbursement')]})
483
+ end
484
+ end
485
+ end
486
+
487
+ describe "#part_of_exchange?" do
488
+ context "exchange variant exists, unexchanged sibling does not" do
489
+ before { allow(subject).to receive(:exchange_variant).and_return(mock_model(Spree::Variant)) }
490
+ it { expect(subject.part_of_exchange?).to eq true }
491
+ end
492
+ context "exchange variant does not exist, but unexchagned sibling does" do
493
+ before { expect(subject).to receive(:sibling_intended_for_exchange).with('unexchanged').and_return(true) }
494
+ it { expect(subject.part_of_exchange?).to eq true }
495
+ end
496
+ context "neither exchange variant nor unexchanged sibling exist" do
497
+ before { expect(subject).to receive(:sibling_intended_for_exchange).with('unexchanged').and_return(false) }
498
+ it { expect(subject.part_of_exchange?).to eq false }
499
+ end
500
+ end
501
+
502
+ describe "#exchange_requested?" do
503
+ context "exchange variant exists" do
504
+ before { allow(subject).to receive(:exchange_variant).and_return(mock_model(Spree::Variant)) }
505
+ it { expect(subject.exchange_requested?).to eq true }
506
+ end
507
+ context "exchange variant does not exist" do
508
+ before { allow(subject).to receive(:exchange_variant).and_return(nil) }
509
+ it { expect(subject.exchange_requested?).to eq false }
510
+ end
511
+ end
512
+
513
+ describe "#exchange_processed?" do
514
+ context "exchange inventory unit exists" do
515
+ before { allow(subject).to receive(:exchange_inventory_unit).and_return(mock_model(Spree::InventoryUnit)) }
516
+ it { expect(subject.exchange_processed?).to eq true }
517
+ end
518
+ context "exchange inventory unit does not exist" do
519
+ before { allow(subject).to receive(:exchange_inventory_unit).and_return(nil) }
520
+ it { expect(subject.exchange_processed?).to eq false }
521
+ end
522
+ end
523
+
524
+ describe "#exchange_required?" do
525
+ context "exchange has been requested and not yet processed" do
526
+ before do
527
+ allow(subject).to receive(:exchange_requested?).and_return(true)
528
+ allow(subject).to receive(:exchange_processed?).and_return(false)
529
+ end
530
+
531
+ it { expect(subject.exchange_required?).to be true }
532
+ end
533
+
534
+ context "exchange has not been requested" do
535
+ before { allow(subject).to receive(:exchange_requested?).and_return(false) }
536
+ it { expect(subject.exchange_required?).to be false }
537
+ end
538
+
539
+ context "exchange has been requested and processed" do
540
+ before do
541
+ allow(subject).to receive(:exchange_requested?).and_return(true)
542
+ allow(subject).to receive(:exchange_processed?).and_return(true)
543
+ end
544
+ it { expect(subject.exchange_required?).to be false }
545
+ end
546
+ end
547
+
548
+ describe "#eligible_exchange_variants" do
549
+ it "uses the exchange variant calculator to compute possible variants to exchange for" do
550
+ return_item = build(:return_item)
551
+ expect(Spree::ReturnItem.exchange_variant_engine).to receive(:eligible_variants).with(return_item.variant)
552
+ return_item.eligible_exchange_variants
553
+ end
554
+ end
555
+
556
+ describe ".exchange_variant_engine" do
557
+ it "defaults to the same product calculator" do
558
+ expect(Spree::ReturnItem.exchange_variant_engine).to eq Spree::ReturnItem::ExchangeVariantEligibility::SameProduct
559
+ end
560
+ end
561
+
562
+ describe "exchange pre_tax_amount" do
563
+ let(:return_item) { build(:return_item) }
564
+
565
+ context "the return item is intended to be exchanged" do
566
+ before do
567
+ return_item.inventory_unit.variant.update_column(:track_inventory, false)
568
+ return_item.exchange_variant = return_item.inventory_unit.variant
569
+ end
570
+
571
+ it do
572
+ return_item.pre_tax_amount = 5.0
573
+ return_item.save!
574
+ expect(return_item.reload.pre_tax_amount).to eq 0.0
575
+ end
576
+ end
577
+
578
+ context "the return item is not intended to be exchanged" do
579
+ it do
580
+ return_item.pre_tax_amount = 5.0
581
+ return_item.save!
582
+ expect(return_item.reload.pre_tax_amount).to eq 5.0
583
+ end
584
+ end
585
+ end
586
+
587
+ describe "#build_exchange_inventory_unit" do
588
+ let(:return_item) { build(:return_item) }
589
+ subject { return_item.build_exchange_inventory_unit }
590
+
591
+ context "the return item is intended to be exchanged" do
592
+ before { allow(return_item).to receive(:exchange_variant).and_return(mock_model(Spree::Variant)) }
593
+
594
+ context "an exchange inventory unit already exists" do
595
+ before { allow(return_item).to receive(:exchange_inventory_unit).and_return(mock_model(Spree::InventoryUnit)) }
596
+ it { expect(subject).to be_nil }
597
+ end
598
+
599
+ context "no exchange inventory unit exists" do
600
+ it "builds a pending inventory unit with references to the return item, variant, and previous inventory unit" do
601
+ expect(subject.variant).to eq return_item.exchange_variant
602
+ expect(subject.pending).to eq true
603
+ expect(subject).not_to be_persisted
604
+ expect(subject.original_return_item).to eq return_item
605
+ expect(subject.line_item).to eq return_item.inventory_unit.line_item
606
+ expect(subject.order).to eq return_item.inventory_unit.order
607
+ end
608
+ end
609
+ end
610
+
611
+ context "the return item is not intended to be exchanged" do
612
+ it { expect(subject).to be_nil }
613
+ end
614
+ end
615
+
616
+ describe "#exchange_shipment" do
617
+ it "returns the exchange inventory unit's shipment" do
618
+ inventory_unit = build(:inventory_unit)
619
+ subject.exchange_inventory_unit = inventory_unit
620
+ expect(subject.exchange_shipment).to eq inventory_unit.shipment
621
+ end
622
+ end
623
+
624
+ describe "#shipment" do
625
+ it "returns the inventory unit's shipment" do
626
+ inventory_unit = build(:inventory_unit)
627
+ subject.inventory_unit = inventory_unit
628
+ expect(subject.shipment).to eq inventory_unit.shipment
629
+ end
630
+ end
631
+
632
+ describe 'inventory_unit uniqueness' do
633
+ let!(:old_return_item) { create(:return_item, reception_status: old_reception_status) }
634
+ let(:old_reception_status) { 'awaiting' }
635
+
636
+ subject do
637
+ build(:return_item, {
638
+ return_authorization: old_return_item.return_authorization,
639
+ inventory_unit: old_return_item.inventory_unit,
640
+ })
641
+ end
642
+
643
+ context 'with other awaiting return items exist for the same inventory unit' do
644
+ let(:old_reception_status) { 'awaiting' }
645
+
646
+ it 'cancels the others' do
647
+ expect {
648
+ subject.save!
649
+ }.to change { old_return_item.reload.reception_status }.from('awaiting').to('cancelled')
650
+ end
651
+
652
+ it 'does not cancel itself' do
653
+ subject.save!
654
+ expect(subject).to be_awaiting
655
+ end
656
+ end
657
+
658
+ context 'with other cancelled return items exist for the same inventory unit' do
659
+ let(:old_reception_status) { 'cancelled' }
660
+
661
+ it 'succeeds' do
662
+ expect { subject.save! }.to_not raise_error
663
+ end
664
+ end
665
+
666
+ context 'with other received return items exist for the same inventory unit' do
667
+ let(:old_reception_status) { 'received' }
668
+
669
+ it 'is invalid' do
670
+ expect(subject).to_not be_valid
671
+ expect(subject.errors.to_a).to eq ["Inventory unit #{subject.inventory_unit_id} has already been taken by return item #{old_return_item.id}"]
672
+ end
673
+ end
674
+
675
+ context 'with other given_to_customer return items exist for the same inventory unit' do
676
+ let(:old_reception_status) { 'given_to_customer' }
677
+
678
+ it 'is invalid' do
679
+ expect(subject).to_not be_valid
680
+ expect(subject.errors.to_a).to eq ["Inventory unit #{subject.inventory_unit_id} has already been taken by return item #{old_return_item.id}"]
681
+ end
682
+ end
683
+ end
684
+
685
+ describe "included tax in total" do
686
+ let(:inventory_unit) { create(:inventory_unit, state: 'shipped') }
687
+ let(:return_item) do
688
+ create(
689
+ :return_item,
690
+ inventory_unit: inventory_unit,
691
+ included_tax_total: 10
692
+ )
693
+ end
694
+
695
+ it 'includes included tax total' do
696
+ expect(return_item.pre_tax_amount).to eq 10
697
+ expect(return_item.included_tax_total).to eq 10
698
+ expect(return_item.total).to eq 20
699
+ end
700
+ end
701
+
702
+ describe "valid exchange variant" do
703
+ subject { return_item }
704
+
705
+ before { subject.save }
706
+
707
+ context "return item doesn't have an exchange variant" do
708
+ let(:return_item) { create(:return_item) }
709
+
710
+ it "is valid" do
711
+ expect(subject).to be_valid
712
+ end
713
+ end
714
+
715
+ context "return item has an exchange variant" do
716
+ let(:return_item) { create(:exchange_return_item) }
717
+ let(:exchange_variant) { create(:on_demand_variant, product: return_item.inventory_unit.variant.product) }
718
+
719
+ context "the exchange variant is eligible" do
720
+ before { return_item.exchange_variant = exchange_variant }
721
+
722
+ it "is valid" do
723
+ expect(subject).to be_valid
724
+ end
725
+ end
726
+
727
+ context "the exchange variant is not eligible" do
728
+ context "new return item" do
729
+ let(:return_item) { build(:return_item) }
730
+ let(:exchange_variant) { create(:variant, product: return_item.inventory_unit.variant.product) }
731
+
732
+ before { return_item.exchange_variant = exchange_variant }
733
+
734
+ it "is invalid" do
735
+ expect(subject).to_not be_valid
736
+ end
737
+
738
+ it "adds an error message about the invalid exchange variant" do
739
+ subject.valid?
740
+ expect(subject.errors.to_a).to eq ["Invalid exchange variant."]
741
+ end
742
+ end
743
+
744
+ context "the exchange variant has been updated" do
745
+ before do
746
+ other_variant = create(:variant)
747
+ return_item.exchange_variant_id = other_variant.id
748
+ subject.valid?
749
+ end
750
+
751
+ it "is invalid" do
752
+ expect(subject).to_not be_valid
753
+ end
754
+
755
+ it "adds an error message about the invalid exchange variant" do
756
+ expect(subject.errors.to_a).to eq ["Invalid exchange variant."]
757
+ end
758
+ end
759
+
760
+ context "the exchange variant has not been updated" do
761
+ before do
762
+ other_variant = create(:variant)
763
+ return_item.update_column(:exchange_variant_id, other_variant.id)
764
+ return_item.reload
765
+ subject.valid?
766
+ end
767
+
768
+ it "is valid" do
769
+ expect(subject).to be_valid
770
+ end
771
+ end
772
+ end
773
+ end
774
+ end
775
+ end