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