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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -0
- data/Gemfile +3 -0
- data/Rakefile +16 -0
- data/script/rails +9 -0
- data/solidus_core.gemspec +48 -0
- data/spec/fixtures/thinking-cat.jpg +0 -0
- data/spec/helpers/base_helper_spec.rb +173 -0
- data/spec/helpers/order_helper_spec.rb +12 -0
- data/spec/helpers/products_helper_spec.rb +220 -0
- data/spec/helpers/taxons_helper_spec.rb +17 -0
- data/spec/lib/calculated_adjustments_spec.rb +7 -0
- data/spec/lib/i18n_spec.rb +123 -0
- data/spec/lib/search/base_spec.rb +86 -0
- data/spec/lib/search/variant_spec.rb +92 -0
- data/spec/lib/spree/core/controller_helpers/auth_spec.rb +66 -0
- data/spec/lib/spree/core/controller_helpers/order_spec.rb +92 -0
- data/spec/lib/spree/core/controller_helpers/search_spec.rb +17 -0
- data/spec/lib/spree/core/controller_helpers/store_spec.rb +16 -0
- data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +39 -0
- data/spec/lib/spree/core/current_store_spec.rb +36 -0
- data/spec/lib/spree/core/delegate_belongs_to_spec.rb +22 -0
- data/spec/lib/spree/core/importer/order_spec.rb +431 -0
- data/spec/lib/spree/core/role_configuration_spec.rb +138 -0
- data/spec/lib/spree/core/validators/email_spec.rb +48 -0
- data/spec/lib/spree/localized_number_spec.rb +38 -0
- data/spec/lib/spree/migrations_spec.rb +36 -0
- data/spec/lib/spree/money_spec.rb +127 -0
- data/spec/lib/tasks/exchanges_spec.rb +231 -0
- data/spec/lib/tasks/migrations/copy_shipped_shipments_to_cartons_spec.rb +115 -0
- data/spec/lib/tasks/order_capturing_spec.rb +56 -0
- data/spec/mailers/carton_mailer_spec.rb +43 -0
- data/spec/mailers/order_mailer_spec.rb +122 -0
- data/spec/mailers/reimbursement_mailer_spec.rb +40 -0
- data/spec/mailers/test_mailer_spec.rb +15 -0
- data/spec/models/spree/ability_spec.rb +276 -0
- data/spec/models/spree/address_spec.rb +250 -0
- data/spec/models/spree/adjustment_reason_spec.rb +13 -0
- data/spec/models/spree/adjustment_spec.rb +177 -0
- data/spec/models/spree/app_configuration_spec.rb +20 -0
- data/spec/models/spree/asset_spec.rb +24 -0
- data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
- data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
- data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
- data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
- data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
- data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
- data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +51 -0
- data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
- data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
- data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
- data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
- data/spec/models/spree/calculator/shipping/price_sack_spec.rb +30 -0
- data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +36 -0
- data/spec/models/spree/calculator/tiered_percent_spec.rb +47 -0
- data/spec/models/spree/calculator_spec.rb +36 -0
- data/spec/models/spree/carton_spec.rb +133 -0
- data/spec/models/spree/classification_spec.rb +15 -0
- data/spec/models/spree/concerns/display_money_spec.rb +43 -0
- data/spec/models/spree/concerns/user_methods_spec.rb +41 -0
- data/spec/models/spree/credit_card_spec.rb +334 -0
- data/spec/models/spree/customer_return_spec.rb +276 -0
- data/spec/models/spree/exchange_spec.rb +79 -0
- data/spec/models/spree/gateway/bogus_simple.rb +20 -0
- data/spec/models/spree/gateway/bogus_spec.rb +13 -0
- data/spec/models/spree/gateway_spec.rb +82 -0
- data/spec/models/spree/inventory_unit_spec.rb +307 -0
- data/spec/models/spree/item_adjustments_spec.rb +256 -0
- data/spec/models/spree/line_item_spec.rb +191 -0
- data/spec/models/spree/option_type_spec.rb +14 -0
- data/spec/models/spree/option_value_spec.rb +22 -0
- data/spec/models/spree/order/address_spec.rb +50 -0
- data/spec/models/spree/order/adjustments_spec.rb +39 -0
- data/spec/models/spree/order/callbacks_spec.rb +42 -0
- data/spec/models/spree/order/checkout_spec.rb +902 -0
- data/spec/models/spree/order/currency_updater_spec.rb +32 -0
- data/spec/models/spree/order/finalizing_spec.rb +111 -0
- data/spec/models/spree/order/payment_spec.rb +210 -0
- data/spec/models/spree/order/risk_assessment_spec.rb +68 -0
- data/spec/models/spree/order/state_machine_spec.rb +221 -0
- data/spec/models/spree/order/tax_spec.rb +84 -0
- data/spec/models/spree/order/totals_spec.rb +24 -0
- data/spec/models/spree/order/updating_spec.rb +18 -0
- data/spec/models/spree/order/validations_spec.rb +15 -0
- data/spec/models/spree/order_cancellations_spec.rb +120 -0
- data/spec/models/spree/order_capturing_spec.rb +116 -0
- data/spec/models/spree/order_contents_spec.rb +265 -0
- data/spec/models/spree/order_inventory_spec.rb +228 -0
- data/spec/models/spree/order_mutex_spec.rb +85 -0
- data/spec/models/spree/order_promotion_spec.rb +31 -0
- data/spec/models/spree/order_shipping_spec.rb +247 -0
- data/spec/models/spree/order_spec.rb +1412 -0
- data/spec/models/spree/order_stock_location_spec.rb +18 -0
- data/spec/models/spree/order_updater_spec.rb +299 -0
- data/spec/models/spree/payment_method/store_credit_spec.rb +294 -0
- data/spec/models/spree/payment_method_spec.rb +96 -0
- data/spec/models/spree/payment_spec.rb +1044 -0
- data/spec/models/spree/permission_sets/base_spec.rb +12 -0
- data/spec/models/spree/permission_sets/configuration_display.rb +82 -0
- data/spec/models/spree/permission_sets/configuration_management_spec.rb +50 -0
- data/spec/models/spree/permission_sets/dashboard_display_spec.rb +22 -0
- data/spec/models/spree/permission_sets/order_display_spec.rb +49 -0
- data/spec/models/spree/permission_sets/order_management_spec.rb +36 -0
- data/spec/models/spree/permission_sets/product_display_spec.rb +60 -0
- data/spec/models/spree/permission_sets/product_management_spec.rb +40 -0
- data/spec/models/spree/permission_sets/promotion_display_spec.rb +34 -0
- data/spec/models/spree/permission_sets/promotion_management_spec.rb +26 -0
- data/spec/models/spree/permission_sets/report_display_spec.rb +24 -0
- data/spec/models/spree/permission_sets/restricted_transfer_management_spec.rb +132 -0
- data/spec/models/spree/permission_sets/stock_display_spec.rb +26 -0
- data/spec/models/spree/permission_sets/stock_management_spec.rb +24 -0
- data/spec/models/spree/permission_sets/user_display_spec.rb +36 -0
- data/spec/models/spree/permission_sets/user_management_spec.rb +28 -0
- data/spec/models/spree/preference_spec.rb +80 -0
- data/spec/models/spree/preferences/configuration_spec.rb +30 -0
- data/spec/models/spree/preferences/preferable_spec.rb +294 -0
- data/spec/models/spree/preferences/scoped_store_spec.rb +58 -0
- data/spec/models/spree/preferences/static_model_preferences_spec.rb +78 -0
- data/spec/models/spree/preferences/statically_configurable_spec.rb +60 -0
- data/spec/models/spree/preferences/store_spec.rb +39 -0
- data/spec/models/spree/price_spec.rb +42 -0
- data/spec/models/spree/product/scopes_spec.rb +148 -0
- data/spec/models/spree/product_duplicator_spec.rb +103 -0
- data/spec/models/spree/product_filter_spec.rb +26 -0
- data/spec/models/spree/product_property_spec.rb +20 -0
- data/spec/models/spree/product_spec.rb +437 -0
- data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +96 -0
- data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +165 -0
- data/spec/models/spree/promotion/actions/create_quantity_adjustments_spec.rb +115 -0
- data/spec/models/spree/promotion/actions/free_shipping_spec.rb +40 -0
- data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
- data/spec/models/spree/promotion/rules/item_total_spec.rb +67 -0
- data/spec/models/spree/promotion/rules/nth_order_spec.rb +70 -0
- data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
- data/spec/models/spree/promotion/rules/option_value_spec.rb +94 -0
- data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
- data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
- data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
- data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
- data/spec/models/spree/promotion_builder_spec.rb +118 -0
- data/spec/models/spree/promotion_category_spec.rb +17 -0
- data/spec/models/spree/promotion_code/code_builder_spec.rb +79 -0
- data/spec/models/spree/promotion_code_spec.rb +187 -0
- data/spec/models/spree/promotion_handler/cart_spec.rb +114 -0
- data/spec/models/spree/promotion_handler/coupon_spec.rb +335 -0
- data/spec/models/spree/promotion_handler/free_shipping_spec.rb +47 -0
- data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
- data/spec/models/spree/promotion_rule_spec.rb +28 -0
- data/spec/models/spree/promotion_spec.rb +767 -0
- data/spec/models/spree/refund_spec.rb +204 -0
- data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
- data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
- data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
- data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
- data/spec/models/spree/reimbursement_spec.rb +231 -0
- data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
- data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
- data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
- data/spec/models/spree/reimbursement_type/original_payment_spec.rb +107 -0
- data/spec/models/spree/reimbursement_type/store_credit_spec.rb +97 -0
- data/spec/models/spree/return_authorization_spec.rb +290 -0
- data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
- data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
- data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +85 -0
- data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
- data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
- data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
- data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
- data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
- data/spec/models/spree/return_item_spec.rb +775 -0
- data/spec/models/spree/returns_calculator_spec.rb +14 -0
- data/spec/models/spree/shipment_spec.rb +709 -0
- data/spec/models/spree/shipping_calculator_spec.rb +45 -0
- data/spec/models/spree/shipping_method_spec.rb +88 -0
- data/spec/models/spree/shipping_rate_spec.rb +142 -0
- data/spec/models/spree/state_spec.rb +14 -0
- data/spec/models/spree/stock/availability_validator_spec.rb +83 -0
- data/spec/models/spree/stock/coordinator_spec.rb +116 -0
- data/spec/models/spree/stock/differentiator_spec.rb +39 -0
- data/spec/models/spree/stock/estimator_spec.rb +146 -0
- data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
- data/spec/models/spree/stock/package_spec.rb +163 -0
- data/spec/models/spree/stock/packer_spec.rb +91 -0
- data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
- data/spec/models/spree/stock/quantifier_spec.rb +115 -0
- data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
- data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
- data/spec/models/spree/stock/splitter/shipping_category_spec.rb +50 -0
- data/spec/models/spree/stock/splitter/weight_spec.rb +29 -0
- data/spec/models/spree/stock_item_spec.rb +426 -0
- data/spec/models/spree/stock_location_spec.rb +279 -0
- data/spec/models/spree/stock_movement_spec.rb +56 -0
- data/spec/models/spree/stock_transfer_spec.rb +290 -0
- data/spec/models/spree/store_credit_category_spec.rb +17 -0
- data/spec/models/spree/store_credit_event_spec.rb +314 -0
- data/spec/models/spree/store_credit_spec.rb +876 -0
- data/spec/models/spree/store_spec.rb +55 -0
- data/spec/models/spree/tax_category_spec.rb +27 -0
- data/spec/models/spree/tax_rate_spec.rb +378 -0
- data/spec/models/spree/taxon_spec.rb +74 -0
- data/spec/models/spree/taxonomy_spec.rb +18 -0
- data/spec/models/spree/tracker_spec.rb +21 -0
- data/spec/models/spree/transfer_item_spec.rb +264 -0
- data/spec/models/spree/unit_cancel_spec.rb +148 -0
- data/spec/models/spree/user_spec.rb +223 -0
- data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +23 -0
- data/spec/models/spree/variant/scopes_spec.rb +55 -0
- data/spec/models/spree/variant_spec.rb +546 -0
- data/spec/models/spree/zone_spec.rb +305 -0
- data/spec/spec_helper.rb +78 -0
- data/spec/support/big_decimal.rb +5 -0
- data/spec/support/concerns/default_price.rb +34 -0
- data/spec/support/dummy_ability.rb +4 -0
- data/spec/support/test_gateway.rb +2 -0
- metadata +229 -3
- 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
|