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,228 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Spree::OrderInventory, :type => :model do
|
|
4
|
+
let(:order) { create :completed_order_with_totals }
|
|
5
|
+
let(:line_item) { order.line_items.first }
|
|
6
|
+
|
|
7
|
+
subject { described_class.new(order, line_item) }
|
|
8
|
+
|
|
9
|
+
context "when order is missing inventory units" do
|
|
10
|
+
before { line_item.update_column(:quantity, 2) }
|
|
11
|
+
|
|
12
|
+
it 'creates the proper number of inventory units' do
|
|
13
|
+
subject.verify
|
|
14
|
+
expect(subject.inventory_units.count).to eq 2
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context "#add_to_shipment" do
|
|
19
|
+
let(:shipment) { order.shipments.first }
|
|
20
|
+
|
|
21
|
+
context "order is not completed" do
|
|
22
|
+
before { allow(order).to receive_messages completed?: false }
|
|
23
|
+
|
|
24
|
+
it "doesn't unstock items" do
|
|
25
|
+
expect(shipment.stock_location).not_to receive(:unstock)
|
|
26
|
+
expect(subject.send(:add_to_shipment, shipment, 5)).to eq(5)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context "inventory units state" do
|
|
31
|
+
before { shipment.inventory_units.destroy_all }
|
|
32
|
+
|
|
33
|
+
it 'sets inventory_units state as per stock location availability' do
|
|
34
|
+
expect(shipment.stock_location).to receive(:fill_status).with(subject.variant, 5).and_return([3, 2])
|
|
35
|
+
|
|
36
|
+
expect(subject.send(:add_to_shipment, shipment, 5)).to eq(5)
|
|
37
|
+
|
|
38
|
+
units = shipment.inventory_units_for(subject.variant).group_by(&:state)
|
|
39
|
+
expect(units['backordered'].size).to eq(2)
|
|
40
|
+
expect(units['on_hand'].size).to eq(3)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context "store doesnt track inventory" do
|
|
45
|
+
let(:variant) { create(:variant) }
|
|
46
|
+
|
|
47
|
+
before { Spree::Config.track_inventory_levels = false }
|
|
48
|
+
|
|
49
|
+
it "creates only on hand inventory units" do
|
|
50
|
+
variant.stock_items.destroy_all
|
|
51
|
+
|
|
52
|
+
# The before_save callback in LineItem would verify inventory
|
|
53
|
+
line_item = order.contents.add variant, 1, shipment: shipment
|
|
54
|
+
|
|
55
|
+
units = shipment.inventory_units_for(line_item.variant)
|
|
56
|
+
expect(units.count).to eq 1
|
|
57
|
+
expect(units.first).to be_on_hand
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
context "variant doesnt track inventory" do
|
|
62
|
+
let(:variant) { create(:variant) }
|
|
63
|
+
before { variant.track_inventory = false }
|
|
64
|
+
|
|
65
|
+
it "creates only on hand inventory units" do
|
|
66
|
+
variant.stock_items.destroy_all
|
|
67
|
+
|
|
68
|
+
line_item = order.contents.add variant, 1
|
|
69
|
+
subject.verify(shipment)
|
|
70
|
+
|
|
71
|
+
units = shipment.inventory_units_for(line_item.variant)
|
|
72
|
+
expect(units.count).to eq 1
|
|
73
|
+
expect(units.first).to be_on_hand
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'should create stock_movement' do
|
|
78
|
+
expect(subject.send(:add_to_shipment, shipment, 5)).to eq(5)
|
|
79
|
+
|
|
80
|
+
stock_item = shipment.stock_location.stock_item(subject.variant)
|
|
81
|
+
movement = stock_item.stock_movements.last
|
|
82
|
+
# movement.originator.should == shipment
|
|
83
|
+
expect(movement.quantity).to eq(-5)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
context "#determine_target_shipment" do
|
|
88
|
+
let(:stock_location) { create :stock_location }
|
|
89
|
+
let(:variant) { line_item.variant }
|
|
90
|
+
|
|
91
|
+
before do
|
|
92
|
+
subject.verify
|
|
93
|
+
|
|
94
|
+
order.shipments.create(:stock_location_id => stock_location.id, :cost => 5)
|
|
95
|
+
|
|
96
|
+
shipped = order.shipments.create(:stock_location_id => order.shipments.first.stock_location.id, :cost => 10)
|
|
97
|
+
shipped.update_column(:state, 'shipped')
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'should select first non-shipped shipment that already contains given variant' do
|
|
101
|
+
shipment = subject.send(:determine_target_shipment)
|
|
102
|
+
expect(shipment.shipped?).to be false
|
|
103
|
+
expect(shipment.inventory_units_for(variant)).not_to be_empty
|
|
104
|
+
|
|
105
|
+
expect(variant.stock_location_ids.include?(shipment.stock_location_id)).to be true
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
context "when no shipments already contain this varint" do
|
|
109
|
+
before do
|
|
110
|
+
subject.line_item.reload
|
|
111
|
+
subject.inventory_units.destroy_all
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'selects first non-shipped shipment that leaves from same stock_location' do
|
|
115
|
+
shipment = subject.send(:determine_target_shipment)
|
|
116
|
+
shipment.reload
|
|
117
|
+
expect(shipment.shipped?).to be false
|
|
118
|
+
expect(shipment.inventory_units_for(variant)).to be_empty
|
|
119
|
+
expect(variant.stock_location_ids.include?(shipment.stock_location_id)).to be true
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
context 'when order has too many inventory units' do
|
|
125
|
+
before do
|
|
126
|
+
line_item.quantity = 3
|
|
127
|
+
line_item.save!
|
|
128
|
+
|
|
129
|
+
line_item.update_column(:quantity, 2)
|
|
130
|
+
subject.line_item.reload
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'should be a messed up order' do
|
|
134
|
+
expect(order.shipments.first.inventory_units_for(line_item.variant).size).to eq(3)
|
|
135
|
+
expect(line_item.quantity).to eq(2)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'should decrease the number of inventory units' do
|
|
139
|
+
subject.verify
|
|
140
|
+
expect(subject.inventory_units.count).to eq 2
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
context '#remove_from_shipment' do
|
|
144
|
+
let(:shipment) { order.shipments.first }
|
|
145
|
+
let(:variant) { subject.variant }
|
|
146
|
+
|
|
147
|
+
context "order is not completed" do
|
|
148
|
+
before { allow(order).to receive_messages completed?: false }
|
|
149
|
+
|
|
150
|
+
it "doesn't restock items" do
|
|
151
|
+
expect(shipment.stock_location).not_to receive(:restock)
|
|
152
|
+
expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'should create stock_movement' do
|
|
157
|
+
expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
|
|
158
|
+
|
|
159
|
+
stock_item = shipment.stock_location.stock_item(variant)
|
|
160
|
+
movement = stock_item.stock_movements.last
|
|
161
|
+
# movement.originator.should == shipment
|
|
162
|
+
expect(movement.quantity).to eq(1)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'should destroy backordered units first' do
|
|
166
|
+
allow(shipment).to receive_messages(inventory_units_for_item: [
|
|
167
|
+
mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'backordered'),
|
|
168
|
+
mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'on_hand'),
|
|
169
|
+
mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'backordered')
|
|
170
|
+
])
|
|
171
|
+
|
|
172
|
+
expect(shipment.inventory_units_for_item[0]).to receive(:destroy)
|
|
173
|
+
expect(shipment.inventory_units_for_item[1]).not_to receive(:destroy)
|
|
174
|
+
expect(shipment.inventory_units_for_item[2]).to receive(:destroy)
|
|
175
|
+
|
|
176
|
+
expect(subject.send(:remove_from_shipment, shipment, 2)).to eq(2)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'should destroy unshipped units first' do
|
|
180
|
+
allow(shipment).to receive_messages(inventory_units_for_item: [
|
|
181
|
+
mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'shipped'),
|
|
182
|
+
mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'on_hand')
|
|
183
|
+
])
|
|
184
|
+
|
|
185
|
+
expect(shipment.inventory_units_for_item[0]).not_to receive(:destroy)
|
|
186
|
+
expect(shipment.inventory_units_for_item[1]).to receive(:destroy)
|
|
187
|
+
|
|
188
|
+
expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it 'only attempts to destroy as many units as are eligible, and return amount destroyed' do
|
|
192
|
+
allow(shipment).to receive_messages(inventory_units_for_item: [
|
|
193
|
+
mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'shipped'),
|
|
194
|
+
mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'on_hand')
|
|
195
|
+
])
|
|
196
|
+
|
|
197
|
+
expect(shipment.inventory_units_for_item[0]).not_to receive(:destroy)
|
|
198
|
+
expect(shipment.inventory_units_for_item[1]).to receive(:destroy)
|
|
199
|
+
|
|
200
|
+
expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it 'should destroy self if not inventory units remain' do
|
|
204
|
+
allow(shipment.inventory_units).to receive_messages(:count => 0)
|
|
205
|
+
expect(shipment).to receive(:destroy)
|
|
206
|
+
|
|
207
|
+
expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
context "inventory unit line item and variant points to different products" do
|
|
211
|
+
let(:different_line_item) { create(:line_item) }
|
|
212
|
+
|
|
213
|
+
let!(:different_inventory) do
|
|
214
|
+
shipment.set_up_inventory("on_hand", variant, order, different_line_item)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
context "completed order" do
|
|
218
|
+
before { order.touch :completed_at }
|
|
219
|
+
|
|
220
|
+
it "removes only units that match both line item and variant" do
|
|
221
|
+
subject.send(:remove_from_shipment, shipment, shipment.inventory_units.count)
|
|
222
|
+
expect(different_inventory.reload).to be_persisted
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Spree::OrderMutex do
|
|
4
|
+
let(:order) { create(:order) }
|
|
5
|
+
|
|
6
|
+
context "without an existing lock" do
|
|
7
|
+
it "executes the block" do
|
|
8
|
+
expect { |b|
|
|
9
|
+
Spree::OrderMutex.with_lock!(order, &b)
|
|
10
|
+
}.to yield_control.once
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "releases the lock for subsequent calls" do
|
|
14
|
+
expect { |b|
|
|
15
|
+
Spree::OrderMutex.with_lock!(order, &b)
|
|
16
|
+
Spree::OrderMutex.with_lock!(order, &b)
|
|
17
|
+
}.to yield_control.twice
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "returns the value of the block" do
|
|
21
|
+
expect(Spree::OrderMutex.with_lock!(order) { 'yay for spree' }).to eq 'yay for spree'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context "with an existing lock on the same order" do
|
|
26
|
+
it "raises a LockFailed error and then releases the lock" do
|
|
27
|
+
Spree::OrderMutex.with_lock!(order) do
|
|
28
|
+
expect {
|
|
29
|
+
expect { |b|
|
|
30
|
+
Spree::OrderMutex.with_lock!(order, &b)
|
|
31
|
+
}.not_to yield_control
|
|
32
|
+
}.to raise_error(Spree::OrderMutex::LockFailed)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
expect { |b|
|
|
36
|
+
Spree::OrderMutex.with_lock!(order, &b)
|
|
37
|
+
}.to yield_control.once
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context "with an expired existing lock on the same order" do
|
|
42
|
+
around do |example|
|
|
43
|
+
Spree::OrderMutex.with_lock!(order) do
|
|
44
|
+
future = Spree::Config[:order_mutex_max_age].seconds.from_now + 1.second
|
|
45
|
+
Timecop.freeze(future) do
|
|
46
|
+
example.run
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "executes the block" do
|
|
52
|
+
expect { |b|
|
|
53
|
+
Spree::OrderMutex.with_lock!(order, &b)
|
|
54
|
+
}.to yield_control.once
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context "with an existing lock on a different order" do
|
|
59
|
+
let(:order2) { create(:order) }
|
|
60
|
+
|
|
61
|
+
around do |example|
|
|
62
|
+
Spree::OrderMutex.with_lock!(order2) { example.run }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "executes the block" do
|
|
66
|
+
expect { |b|
|
|
67
|
+
Spree::OrderMutex.with_lock!(order, &b)
|
|
68
|
+
}.to yield_control.once
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context "when an unrelated RecordNotUnique error occurs" do
|
|
73
|
+
def raise_record_not_unique
|
|
74
|
+
raise ActiveRecord::RecordNotUnique.new("Testing")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "does not rescue the unrelated error" do
|
|
78
|
+
expect {
|
|
79
|
+
Spree::OrderMutex.with_lock!(order) do
|
|
80
|
+
raise_record_not_unique
|
|
81
|
+
end
|
|
82
|
+
}.to raise_error(ActiveRecord::RecordNotUnique)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Spree::OrderPromotion do
|
|
4
|
+
subject do
|
|
5
|
+
order_promotion
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:order_promotion) { build(:order_promotion) }
|
|
9
|
+
|
|
10
|
+
describe "promotion code presence error" do
|
|
11
|
+
subject do
|
|
12
|
+
order_promotion.valid?
|
|
13
|
+
order_promotion.errors[:promotion_code]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
let(:order_promotion) { build(:order_promotion) }
|
|
17
|
+
let(:promotion) { order_promotion.promotion }
|
|
18
|
+
|
|
19
|
+
context "when the promotion does not have a code" do
|
|
20
|
+
it { is_expected.to be_blank }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context "when the promotion has a code" do
|
|
24
|
+
let!(:promotion_code) do
|
|
25
|
+
promotion.codes << build(:promotion_code, promotion: promotion)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it { is_expected.to include("can't be blank") }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Spree::OrderShipping do
|
|
4
|
+
let(:order) { create(:order_ready_to_ship, line_items_count: 1) }
|
|
5
|
+
|
|
6
|
+
def emails
|
|
7
|
+
ActionMailer::Base.deliveries
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
shared_examples 'shipment shipping' do
|
|
11
|
+
|
|
12
|
+
it "marks the inventory units as shipped" do
|
|
13
|
+
expect { subject }.to change { order.inventory_units.reload.map(&:state) }.from(['on_hand']).to(['shipped'])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "creates a carton with the shipment's inventory units" do
|
|
17
|
+
expect { subject }.to change { order.cartons.count }.by(1)
|
|
18
|
+
expect(subject.inventory_units).to match_array(shipment.inventory_units)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe "shipment email" do
|
|
22
|
+
before { with_test_mail { subject } }
|
|
23
|
+
|
|
24
|
+
it "should send a shipment email" do
|
|
25
|
+
expect(emails.size).to eq(1)
|
|
26
|
+
expect(emails.first.subject).to eq("#{order.store.name} Shipment Notification ##{order.number}")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "updates the order shipment state" do
|
|
31
|
+
expect { subject }.to change { order.reload.shipment_state }.from('ready').to('shipped')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "updates shipment.shipped_at" do
|
|
35
|
+
Timecop.freeze do |now|
|
|
36
|
+
expect { subject }.to change { shipment.shipped_at }.from(nil).to(now)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "updates order.updated_at" do
|
|
41
|
+
future = 1.minute.from_now
|
|
42
|
+
expect do
|
|
43
|
+
Timecop.freeze(future) do
|
|
44
|
+
subject
|
|
45
|
+
end
|
|
46
|
+
end.to change { order.updated_at }.from(order.updated_at).to(future)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe "#ship" do
|
|
52
|
+
subject do
|
|
53
|
+
order.shipping.ship(
|
|
54
|
+
inventory_units: inventory_units,
|
|
55
|
+
stock_location: stock_location,
|
|
56
|
+
address: address,
|
|
57
|
+
shipping_method: shipping_method,
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
let(:shipment) { order.shipments.to_a.first }
|
|
62
|
+
let(:inventory_units) { shipment.inventory_units }
|
|
63
|
+
let(:stock_location) { shipment.stock_location }
|
|
64
|
+
let(:address) { shipment.address }
|
|
65
|
+
let(:shipping_method) { shipment.shipping_method }
|
|
66
|
+
|
|
67
|
+
it_behaves_like 'shipment shipping'
|
|
68
|
+
|
|
69
|
+
context "with an external_number" do
|
|
70
|
+
subject do
|
|
71
|
+
order.shipping.ship(
|
|
72
|
+
inventory_units: inventory_units,
|
|
73
|
+
stock_location: stock_location,
|
|
74
|
+
address: address,
|
|
75
|
+
shipping_method: shipping_method,
|
|
76
|
+
external_number: 'some-external-number',
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "sets the external_number" do
|
|
81
|
+
expect(subject.external_number).to eq 'some-external-number'
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context "with a tracking number" do
|
|
86
|
+
subject do
|
|
87
|
+
order.shipping.ship(
|
|
88
|
+
inventory_units: inventory_units,
|
|
89
|
+
stock_location: stock_location,
|
|
90
|
+
address: address,
|
|
91
|
+
shipping_method: shipping_method,
|
|
92
|
+
tracking_number: 'tracking-number',
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "sets the tracking-number" do
|
|
97
|
+
expect(subject.tracking).to eq 'tracking-number'
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context "when told to suppress the mailer" do
|
|
102
|
+
before { with_test_mail { subject } }
|
|
103
|
+
|
|
104
|
+
subject do
|
|
105
|
+
order.shipping.ship(
|
|
106
|
+
inventory_units: inventory_units,
|
|
107
|
+
stock_location: stock_location,
|
|
108
|
+
address: address,
|
|
109
|
+
shipping_method: shipping_method,
|
|
110
|
+
suppress_mailer: true,
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "does not send a shipment email" do
|
|
115
|
+
expect(emails.size).to eq(0)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
describe "#ship_shipment" do
|
|
121
|
+
subject { order.shipping.ship_shipment(shipment) }
|
|
122
|
+
|
|
123
|
+
let(:shipment) { order.shipments.to_a.first }
|
|
124
|
+
|
|
125
|
+
it_behaves_like 'shipment shipping'
|
|
126
|
+
|
|
127
|
+
context "when not all units are shippable" do
|
|
128
|
+
let(:order) { create(:order_ready_to_ship, line_items_count: 2) }
|
|
129
|
+
let(:shippable_line_item) { order.line_items.first }
|
|
130
|
+
let(:unshippable_line_item) { order.line_items.last }
|
|
131
|
+
|
|
132
|
+
before do
|
|
133
|
+
unshippable_line_item.inventory_units.each(&:cancel!)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "only ships the shippable ones" do
|
|
137
|
+
expect(subject.inventory_units).to match_array(shippable_line_item.inventory_units)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
context "when all units are canceled or shipped" do
|
|
142
|
+
let(:order) { create(:order_ready_to_ship, line_items_count: 2) }
|
|
143
|
+
|
|
144
|
+
before { Spree::OrderCancellations.new(order).short_ship([order.inventory_units.first]) }
|
|
145
|
+
|
|
146
|
+
it "updates the order shipment state" do
|
|
147
|
+
expect { subject }.to change { order.reload.shipment_state }.from('ready').to('shipped')
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
context "with an external_number" do
|
|
152
|
+
subject do
|
|
153
|
+
order.shipping.ship_shipment(
|
|
154
|
+
shipment,
|
|
155
|
+
external_number: 'some-external-number',
|
|
156
|
+
)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "sets the external_number" do
|
|
160
|
+
expect(subject.external_number).to eq 'some-external-number'
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
context "with a tracking number" do
|
|
165
|
+
subject do
|
|
166
|
+
order.shipping.ship_shipment(
|
|
167
|
+
shipment,
|
|
168
|
+
tracking_number: 'tracking-number',
|
|
169
|
+
)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "sets the tracking-number" do
|
|
173
|
+
expect(subject.tracking).to eq 'tracking-number'
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# TODO: We can remove this once Shipment#ship! is called by
|
|
178
|
+
# OrderShipping#ship rather than vice versa
|
|
179
|
+
context "when the tracking number is already on the shipment" do
|
|
180
|
+
before do
|
|
181
|
+
shipment.update_attributes!(tracking: 'tracking-number')
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "sets the tracking-number" do
|
|
185
|
+
expect(subject.tracking).to eq 'tracking-number'
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
context "when the shipment has been partially shipped previously" do
|
|
190
|
+
let(:order) { create(:order_ready_to_ship, line_items_count: 2) }
|
|
191
|
+
let(:inventory_units) { shipment.inventory_units.to_a }
|
|
192
|
+
let(:shipped_inventory) { [inventory_units.first] }
|
|
193
|
+
let(:unshipped_inventory) { [inventory_units.last] }
|
|
194
|
+
|
|
195
|
+
before do
|
|
196
|
+
order.shipping.ship(
|
|
197
|
+
inventory_units: shipped_inventory,
|
|
198
|
+
stock_location: shipment.stock_location,
|
|
199
|
+
address: shipment.address,
|
|
200
|
+
shipping_method: shipment.shipping_method,
|
|
201
|
+
)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "marks the inventory units as shipped" do
|
|
205
|
+
expect { subject }.to change { unshipped_inventory.map(&:reload).map(&:state) }.from(['on_hand']).to(['shipped'])
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "creates a carton with the shipment's inventory units" do
|
|
209
|
+
expect { subject }.to change { order.cartons.count }.by(1)
|
|
210
|
+
expect(subject.inventory_units).to match_array(unshipped_inventory)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
context "when told to suppress the mailer" do
|
|
215
|
+
before { with_test_mail { subject } }
|
|
216
|
+
|
|
217
|
+
subject do
|
|
218
|
+
order.shipping.ship_shipment(
|
|
219
|
+
shipment,
|
|
220
|
+
suppress_mailer: true,
|
|
221
|
+
)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it "does not send a shipment email" do
|
|
225
|
+
expect(emails.size).to eq(0)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
context "with stale inventory units (regression test)" do
|
|
230
|
+
let(:order) { FactoryGirl.create(:order_ready_to_ship, line_items_count: 1) }
|
|
231
|
+
let(:shipment) do
|
|
232
|
+
FactoryGirl.create(
|
|
233
|
+
:shipment,
|
|
234
|
+
order: order,
|
|
235
|
+
address: FactoryGirl.create(:address)
|
|
236
|
+
)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
before { shipment.ready! }
|
|
240
|
+
|
|
241
|
+
it "updates the state to shipped" do
|
|
242
|
+
order.shipping.ship_shipment(shipment)
|
|
243
|
+
expect(shipment.reload.state).to eq "shipped"
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|