solidus_active_shipping 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.simplecov +5 -0
- data/.travis.yml +21 -0
- data/Gemfile +18 -0
- data/README.md +192 -0
- data/Rakefile +21 -0
- data/app/assets/javascripts/admin/product_packages/edit.js.coffee +4 -0
- data/app/assets/javascripts/admin/product_packages/index.js.coffee +28 -0
- data/app/assets/javascripts/admin/product_packages/new.js.coffee +7 -0
- data/app/assets/javascripts/spree/backend/solidus_active_shipping.js +0 -0
- data/app/assets/javascripts/spree/frontend/solidus_active_shipping.js +0 -0
- data/app/assets/stylesheets/spree/backend/solidus_active_shipping.css +0 -0
- data/app/assets/stylesheets/spree/frontend/solidus_active_shipping.css +0 -0
- data/app/controllers/spree/admin/active_shipping_settings_controller.rb +27 -0
- data/app/controllers/spree/admin/product_packages_controller.rb +17 -0
- data/app/controllers/spree/admin/products_controller_decorator.rb +12 -0
- data/app/controllers/spree/checkout_controller_decorator.rb +11 -0
- data/app/controllers/spree/orders_controller_decorator.rb +11 -0
- data/app/models/spree/calculator/shipping/active_shipping/base.rb +208 -0
- data/app/models/spree/calculator/shipping/canada_post/base.rb +17 -0
- data/app/models/spree/calculator/shipping/canada_post/expedited.rb +11 -0
- data/app/models/spree/calculator/shipping/canada_post/parcel_surface.rb +11 -0
- data/app/models/spree/calculator/shipping/canada_post/priority_worldwide_intl.rb +12 -0
- data/app/models/spree/calculator/shipping/canada_post/regular.rb +11 -0
- data/app/models/spree/calculator/shipping/canada_post/small_packets_air.rb +11 -0
- data/app/models/spree/calculator/shipping/canada_post/small_packets_surface.rb +11 -0
- data/app/models/spree/calculator/shipping/canada_post/xpresspost.rb +11 -0
- data/app/models/spree/calculator/shipping/canada_post/xpresspost_international.rb +11 -0
- data/app/models/spree/calculator/shipping/fedex/base.rb +21 -0
- data/app/models/spree/calculator/shipping/fedex/express_saver.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/first_overnight.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/ground.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/ground_home_delivery.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/international_economy.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/international_economy_freight.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/international_first.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/international_ground.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/international_priority.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/international_priority_freight.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/international_priority_saturday_delivery.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/one_day_freight.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/one_day_freight_saturday_delivery.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/priority_overnight.rb +13 -0
- data/app/models/spree/calculator/shipping/fedex/priority_overnight_saturday_delivery.rb +11 -0
- data/app/models/spree/calculator/shipping/fedex/saver.rb +11 -0
- data/app/models/spree/calculator/shipping/fedex/standard_overnight.rb +11 -0
- data/app/models/spree/calculator/shipping/fedex/three_day_freight.rb +11 -0
- data/app/models/spree/calculator/shipping/fedex/three_day_freight_saturday_delivery.rb +11 -0
- data/app/models/spree/calculator/shipping/fedex/two_day.rb +11 -0
- data/app/models/spree/calculator/shipping/fedex/two_day_freight.rb +11 -0
- data/app/models/spree/calculator/shipping/fedex/two_day_freight_saturday_delivery.rb +11 -0
- data/app/models/spree/calculator/shipping/fedex/two_day_saturday_delivery.rb +11 -0
- data/app/models/spree/calculator/shipping/ups/base.rb +30 -0
- data/app/models/spree/calculator/shipping/ups/express.rb +11 -0
- data/app/models/spree/calculator/shipping/ups/ground.rb +11 -0
- data/app/models/spree/calculator/shipping/ups/next_day_air.rb +11 -0
- data/app/models/spree/calculator/shipping/ups/next_day_air_early_am.rb +11 -0
- data/app/models/spree/calculator/shipping/ups/next_day_air_saver.rb +11 -0
- data/app/models/spree/calculator/shipping/ups/saver.rb +11 -0
- data/app/models/spree/calculator/shipping/ups/second_day_air.rb +11 -0
- data/app/models/spree/calculator/shipping/ups/standard.rb +11 -0
- data/app/models/spree/calculator/shipping/ups/three_day_select.rb +11 -0
- data/app/models/spree/calculator/shipping/ups/worldwide_expedited.rb +11 -0
- data/app/models/spree/calculator/shipping/usps/base.rb +84 -0
- data/app/models/spree/calculator/shipping/usps/express_mail.rb +19 -0
- data/app/models/spree/calculator/shipping/usps/express_mail_international.rb +46 -0
- data/app/models/spree/calculator/shipping/usps/first_class_mail_international.rb +31 -0
- data/app/models/spree/calculator/shipping/usps/first_class_mail_international_large_envelope.rb +31 -0
- data/app/models/spree/calculator/shipping/usps/first_class_mail_parcel.rb +28 -0
- data/app/models/spree/calculator/shipping/usps/first_class_package_international.rb +49 -0
- data/app/models/spree/calculator/shipping/usps/global_express_guaranteed.rb +48 -0
- data/app/models/spree/calculator/shipping/usps/media_mail.rb +19 -0
- data/app/models/spree/calculator/shipping/usps/priority_mail.rb +19 -0
- data/app/models/spree/calculator/shipping/usps/priority_mail_flat_rate_envelope.rb +19 -0
- data/app/models/spree/calculator/shipping/usps/priority_mail_international.rb +49 -0
- data/app/models/spree/calculator/shipping/usps/priority_mail_international_large_flat_rate_box.rb +43 -0
- data/app/models/spree/calculator/shipping/usps/priority_mail_international_medium_flat_rate_box.rb +43 -0
- data/app/models/spree/calculator/shipping/usps/priority_mail_international_small_flat_rate_box.rb +43 -0
- data/app/models/spree/calculator/shipping/usps/priority_mail_large_flat_rate_box.rb +19 -0
- data/app/models/spree/calculator/shipping/usps/priority_mail_medium_flat_rate_box.rb +19 -0
- data/app/models/spree/calculator/shipping/usps/priority_mail_small_flat_rate_box.rb +19 -0
- data/app/models/spree/calculator/shipping/usps/standard_post.rb +19 -0
- data/app/models/spree/content_item_decorator.rb +7 -0
- data/app/models/spree/line_item_decorator.rb +4 -0
- data/app/models/spree/package_builder.rb +114 -0
- data/app/models/spree/product_decorator.rb +10 -0
- data/app/models/spree/product_package.rb +10 -0
- data/app/models/spree/stock_location_decorator.rb +10 -0
- data/app/models/spree/variant_decorator.rb +3 -0
- data/app/overrides/spree/admin/shared/_configuration_menu/add_active_shipping_settings_tab.html.erb.deface +3 -0
- data/app/overrides/spree/admin/shared/_product_tabs/add_product_packages_tab.html.erb.deface +7 -0
- data/app/views/spree/admin/active_shipping_settings/edit.html.erb +92 -0
- data/app/views/spree/admin/product_packages/_form.html.erb +24 -0
- data/app/views/spree/admin/product_packages/edit.html.erb +15 -0
- data/app/views/spree/admin/product_packages/index.html.erb +46 -0
- data/app/views/spree/admin/product_packages/new.html.erb +15 -0
- data/config/locales/en.yml +93 -0
- data/config/locales/fr.yml +12 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20130107030221_create_product_packages.rb +12 -0
- data/lib/solidus_active_shipping.rb +3 -0
- data/lib/solidus_active_shipping/engine.rb +44 -0
- data/lib/spree/active_shipping_configuration.rb +24 -0
- data/lib/spree/shipping_error.rb +3 -0
- data/solidus_active_shipping.gemspec +33 -0
- data/spec/cassettes/Checkout/with_valid_shipping_address/does_not_break_the_per-item_shipping_method_calculator.yml +134 -0
- data/spec/cassettes/FedEx_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Fedex_Ground/1_1_1_1.yml +48 -0
- data/spec/cassettes/FedEx_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Fedex_InternationalEconomy/1_1_2_1.yml +48 -0
- data/spec/cassettes/FedEx_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Fedex_InternationalFirst/1_1_3_1.yml +48 -0
- data/spec/cassettes/FedEx_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Fedex_InternationalPriority/1_1_4_1.yml +48 -0
- data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_ExpressSaver/1_2_5_1.yml +48 -0
- data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_FirstOvernight/1_2_1_1.yml +48 -0
- data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_GroundHomeDelivery/1_2_6_1.yml +48 -0
- data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_PriorityOvernight/1_2_2_1.yml +48 -0
- data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_StandardOvernight/1_2_3_1.yml +48 -0
- data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_TwoDay/1_2_4_1.yml +48 -0
- data/spec/cassettes/UPS_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Ups_Express/1_1_1_1.yml +103 -0
- data/spec/cassettes/UPS_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Ups_Saver/1_1_3_1.yml +103 -0
- data/spec/cassettes/UPS_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Ups_Standard/1_1_4_1.yml +103 -0
- data/spec/cassettes/UPS_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Ups_ThreeDaySelect/1_1_5_1.yml +103 -0
- data/spec/cassettes/UPS_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Ups_WorldwideExpedited/1_1_2_1.yml +103 -0
- data/spec/cassettes/UPS_calculators/with_US_origin_address/Spree_Calculator_Shipping_Ups_Ground/1_2_1_1.yml +103 -0
- data/spec/cassettes/UPS_calculators/with_US_origin_address/Spree_Calculator_Shipping_Ups_NextDayAir/1_2_2_1.yml +103 -0
- data/spec/cassettes/UPS_calculators/with_US_origin_address/Spree_Calculator_Shipping_Ups_NextDayAirEarlyAm/1_2_3_1.yml +103 -0
- data/spec/cassettes/UPS_calculators/with_US_origin_address/Spree_Calculator_Shipping_Ups_NextDayAirSaver/1_2_4_1.yml +103 -0
- data/spec/cassettes/UPS_calculators/with_US_origin_address/Spree_Calculator_Shipping_Ups_SecondDayAir/1_2_5_1.yml +103 -0
- data/spec/controllers/admin/active_shipping_settings_controller_spec.rb +38 -0
- data/spec/controllers/admin/product_packages_controller_spec.rb +36 -0
- data/spec/factories/order_factory_override.rb +46 -0
- data/spec/factories/product_package_factory.rb +9 -0
- data/spec/factories/state_factory_override.rb +28 -0
- data/spec/features/checkout_spec.rb +37 -0
- data/spec/fixtures/normal_rates_request.xml +2 -0
- data/spec/integrations/calculators/fedex_spec.rb +56 -0
- data/spec/integrations/calculators/ups_spec.rb +56 -0
- data/spec/lib/spree/active_shipping/bogus_carrier.rb +18 -0
- data/spec/lib/spree/calculator/shipping/bogus_calculator.rb +17 -0
- data/spec/models/active_shipping_calculator_spec.rb +182 -0
- data/spec/models/carriers/usps_calculator_spec.rb +72 -0
- data/spec/models/package_builder_spec.rb +173 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +80 -0
- data/spec/support/capybara.rb +24 -0
- data/spec/support/checkout_helper.rb +12 -0
- data/spec/support/feature_helper.rb +7 -0
- data/spec/support/package_helper.rb +9 -0
- data/spec/support/shared_contexts/checkout_setup.rb +12 -0
- data/spec/support/shared_contexts/package_setup.rb +34 -0
- data/spec/support/shared_contexts/shipping_carriers/fedex.rb +15 -0
- data/spec/support/shared_contexts/shipping_carriers/ups.rb +14 -0
- data/spec/support/shared_contexts/stock_location_setup.rb +26 -0
- data/spec/support/web_fixtures.rb +6 -0
- metadata +444 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'UPS calculators', :vcr do
|
4
|
+
include_context 'UPS setup'
|
5
|
+
include_context 'package setup'
|
6
|
+
|
7
|
+
subject { described_class.new.compute_package(package) }
|
8
|
+
|
9
|
+
context 'with Canadian origin address' do
|
10
|
+
include_context 'Canada stock location'
|
11
|
+
|
12
|
+
describe Spree::Calculator::Shipping::Ups::Express do
|
13
|
+
it { is_expected.to eq(139.1) }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Spree::Calculator::Shipping::Ups::WorldwideExpedited do
|
17
|
+
it { is_expected.to eq(97.15) }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe Spree::Calculator::Shipping::Ups::Saver do
|
21
|
+
it { is_expected.to eq(132.25) }
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Spree::Calculator::Shipping::Ups::Standard do
|
25
|
+
it { is_expected.to eq(35.34) }
|
26
|
+
end
|
27
|
+
|
28
|
+
describe Spree::Calculator::Shipping::Ups::ThreeDaySelect do
|
29
|
+
it { is_expected.to eq(89.7) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with US origin address' do
|
34
|
+
include_context 'US stock location'
|
35
|
+
|
36
|
+
describe Spree::Calculator::Shipping::Ups::Ground do
|
37
|
+
it { is_expected.to eq(14.2) }
|
38
|
+
end
|
39
|
+
|
40
|
+
describe Spree::Calculator::Shipping::Ups::NextDayAir do
|
41
|
+
it { is_expected.to eq(79.47) }
|
42
|
+
end
|
43
|
+
|
44
|
+
describe Spree::Calculator::Shipping::Ups::NextDayAirEarlyAm do
|
45
|
+
it { is_expected.to eq(110.29) }
|
46
|
+
end
|
47
|
+
|
48
|
+
describe Spree::Calculator::Shipping::Ups::NextDayAirSaver do
|
49
|
+
it { is_expected.to eq(77.46) }
|
50
|
+
end
|
51
|
+
|
52
|
+
describe Spree::Calculator::Shipping::Ups::SecondDayAir do
|
53
|
+
it { is_expected.to eq(27.97) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Spree
|
2
|
+
module ActiveShipping
|
3
|
+
# Bogus carrier useful for testing. For some reasons the plugin version of this class does not work
|
4
|
+
# properly (it fails to return a RateResponse)
|
5
|
+
class BogusCarrier < ::ActiveShipping::Carrier
|
6
|
+
include RSpec::Mocks::ExampleMethods
|
7
|
+
|
8
|
+
def name
|
9
|
+
"BogusCarrier"
|
10
|
+
end
|
11
|
+
|
12
|
+
# We're stubbing the rates because actual RateEstimate are too elaborate
|
13
|
+
def find_rates(origin, destination, packages, options = {})
|
14
|
+
::ActiveShipping::RateResponse.new(true, "success!", {}, :rates => [instance_double('rate', service_name: 'Bogus Calculator', price: 999)], :xml => "")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
module Calculator::Shipping
|
5
|
+
module ActiveShipping
|
6
|
+
class BogusCalculator < Spree::Calculator::Shipping::ActiveShipping::Base
|
7
|
+
def carrier
|
8
|
+
@carrier ||= Spree::ActiveShipping::BogusCarrier.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.description
|
12
|
+
"Bogus Calculator"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spree::Calculator::Shipping do
|
4
|
+
# NOTE: All specs will use the bogus calculator
|
5
|
+
# (no login information needed)
|
6
|
+
|
7
|
+
let(:address) { FactoryGirl.create(:address) }
|
8
|
+
let(:variant_1) { FactoryGirl.create(:variant, weight: 1) }
|
9
|
+
let(:variant_2) { FactoryGirl.create(:variant, weight: 2) }
|
10
|
+
let!(:order) do
|
11
|
+
FactoryGirl.create(:order_with_line_items, ship_address: address, line_items_count: 2,
|
12
|
+
line_items_attributes: [{ quantity: 2, variant: variant_1 }, { quantity: 2, variant: variant_2 }])
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:calculator) { Spree::Calculator::Shipping::ActiveShipping::BogusCalculator.new }
|
16
|
+
let(:package) { order.shipments.first.to_package }
|
17
|
+
|
18
|
+
before(:each) do
|
19
|
+
order.create_proposed_shipments
|
20
|
+
Spree::ActiveShipping::Config.set(units: 'imperial')
|
21
|
+
Spree::ActiveShipping::Config.set(unit_multiplier: 1)
|
22
|
+
Spree::ActiveShipping::Config.set(handling_fee: 0)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'available' do
|
26
|
+
context 'when rates are available' do
|
27
|
+
it 'should return true' do
|
28
|
+
expect(calculator.available?(package)).to eq true
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should use zero as a valid weight for service' do
|
32
|
+
allow(calculator.carrier).to receive(:max_weight_for_country).and_return(0)
|
33
|
+
expect(calculator.available?(package)).to eq true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when rates are not available' do
|
38
|
+
let(:invalid_response) do
|
39
|
+
::ActiveShipping::RateResponse.new(true, 'success!', {}, rates: [], xml: '')
|
40
|
+
end
|
41
|
+
|
42
|
+
before do
|
43
|
+
allow(calculator.carrier).to receive(:find_rates).and_return(invalid_response)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should return false' do
|
47
|
+
expect(calculator.available?(package)).to eq false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when there is an error retrieving the rates' do
|
52
|
+
before do
|
53
|
+
allow(calculator.carrier).to receive(:find_rates).and_raise(::ActiveShipping::ResponseError)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should return false' do
|
57
|
+
expect(calculator.available?(package)).to eq false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'available?' do
|
63
|
+
# regression test for #164 and #171
|
64
|
+
it 'should not return rates if the weight requirements for the destination country are not met' do
|
65
|
+
# if max_weight_for_country is nil -> the carrier does not ship to that country
|
66
|
+
# if max_weight_for_country is 0 -> the carrier does not have weight restrictions to that country
|
67
|
+
allow(calculator).to receive(:max_weight_for_country).and_return(nil)
|
68
|
+
expect(calculator).to receive(:is_package_shippable?).and_raise(Spree::ShippingError)
|
69
|
+
expect(calculator.available?(package)).to eq false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'compute' do
|
74
|
+
subject { calculator.compute(package) }
|
75
|
+
|
76
|
+
context 'when the cache is warm' do
|
77
|
+
it 'should check the cache first before finding rates' do
|
78
|
+
# Since the cache is cleared between the tests, cache.fetch will return a miss,
|
79
|
+
# but by passing a block { Hash.new }, the return value of the block will be
|
80
|
+
# written under the given cache key so we simulate a warm cache
|
81
|
+
Rails.cache.fetch(calculator.send(:cache_key, package)) { Hash.new }
|
82
|
+
expect(calculator.carrier).not_to receive(:find_rates)
|
83
|
+
subject
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'when the cache is empty' do
|
88
|
+
before do
|
89
|
+
# We're stubbing the carrier method because we
|
90
|
+
# need to check that a specific instance of carrier
|
91
|
+
# is receiving or not the function call (otherwise test will
|
92
|
+
# pass but only because carrier we're watching and the carrier
|
93
|
+
# used by the calculator are different)
|
94
|
+
allow(calculator.carrier).to receive(:find_rates).and_call_original
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should call .find_rates' do
|
98
|
+
expect(calculator.carrier).to receive(:find_rates)
|
99
|
+
subject
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'with valid response' do
|
104
|
+
it "should return rate based on calculator's service_name" do
|
105
|
+
expect(subject).to eq 9.99
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should include handling_fee when configured' do
|
109
|
+
Spree::ActiveShipping::Config.set(handling_fee: 100)
|
110
|
+
expect(subject).to eq 10.99
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should return nil if service_name is not found in rate_hash' do
|
114
|
+
allow(calculator.class).to receive(:description) { 'invalid service_name' }
|
115
|
+
expect(subject).to be_nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'with invalid response' do
|
120
|
+
before do
|
121
|
+
allow(calculator.carrier).to receive(:find_rates).and_raise(::ActiveShipping::ResponseError)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should raise a Spree::ShippingError' do
|
125
|
+
expect { subject }.to raise_exception(Spree::ShippingError)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'service_name' do
|
131
|
+
it 'should return description when not defined' do
|
132
|
+
expect(calculator.class.service_name).to eq calculator.description
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# We make an exception and tests this the private method because max_weight values
|
137
|
+
# are difficult to tests conclusively through the
|
138
|
+
describe 'get_max_weight' do
|
139
|
+
include_context 'US stock location'
|
140
|
+
include_context 'package setup'
|
141
|
+
|
142
|
+
context 'when the max_weight from the calculator is non-zero and max_weight_per_package is zero' do
|
143
|
+
before do
|
144
|
+
allow(calculator).to receive(:max_weight_for_country).and_return(1)
|
145
|
+
allow(calculator).to receive(:max_weight_per_package).and_return(0)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'uses the max_weight_for_country as a max_weight' do
|
149
|
+
expect(calculator.send(:get_max_weight, package)).to eq calculator.send(:max_weight_for_country)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context 'when the max_weight from the calculator is zero and max_weight_per_package is non-zero' do
|
154
|
+
before do
|
155
|
+
allow(calculator).to receive(:max_weight_for_country).and_return(0)
|
156
|
+
allow(calculator).to receive(:max_weight_per_package).and_return(1)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'uses the max_weight_per_package as a max_weight' do
|
160
|
+
expect(calculator.send(:get_max_weight, package)).to eq calculator.send(:max_weight_per_package)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context 'when the max_weight from the calculator is non-zero and max_weight_per_package is non-zero' do
|
165
|
+
before do
|
166
|
+
allow(calculator).to receive(:max_weight_per_package).and_return(SecureRandom.random_number(19) + 1)
|
167
|
+
allow(calculator).to receive(:max_weight_for_country).and_return(SecureRandom.random_number(19) + 1)
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'uses the lesser one of the two values' do
|
171
|
+
min = [calculator.send(:max_weight_for_country), calculator.send(:max_weight_per_package)].min
|
172
|
+
expect(calculator.send(:get_max_weight, package)).to eq min
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'when the max_weight is zero and max_weight_per_package is zero' do
|
177
|
+
it 'uses 0 as a max_eight' do
|
178
|
+
expect(calculator.send(:get_max_weight, package)).to be_zero
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spree::Calculator::Shipping::Usps do
|
4
|
+
let(:address) { FactoryGirl.create(:address) }
|
5
|
+
let(:variant_1) { FactoryGirl.create(:variant, weight: 1) }
|
6
|
+
let(:variant_2) { FactoryGirl.create(:variant, weight: 2) }
|
7
|
+
let!(:order) do
|
8
|
+
FactoryGirl.create(:order_with_line_items, ship_address: address, line_items_count: 2,
|
9
|
+
line_items_attributes: [{ quantity: 2, variant: variant_1}, { quantity: 2, variant: variant_2 }] )
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:carrier) { ActiveShipping::USPS.new(login: 'FAKEFAKEFAKE') }
|
13
|
+
let(:calculator) { Spree::Calculator::Shipping::Usps::ExpressMail.new }
|
14
|
+
let(:package) { order.shipments.first.to_package }
|
15
|
+
|
16
|
+
before(:each) do
|
17
|
+
order.create_proposed_shipments
|
18
|
+
Spree::ActiveShipping::Config.set(units: 'imperial')
|
19
|
+
Spree::ActiveShipping::Config.set(unit_multiplier: 1)
|
20
|
+
Spree::ActiveShipping::Config.set(handling_fee: 0)
|
21
|
+
|
22
|
+
stub_request(:get, %r{http:\/\/production.shippingapis.com\/ShippingAPI.dll.*})
|
23
|
+
.to_return(body: fixture(:normal_rates_request))
|
24
|
+
|
25
|
+
# Since the response can be cached, we explicitly clear cache
|
26
|
+
# so each test can be run from a clean slate
|
27
|
+
Rails.cache.delete(calculator.send(:cache_key, package))
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'compute' do
|
31
|
+
subject { calculator.compute(package) }
|
32
|
+
|
33
|
+
it 'should use the carrier supplied in the initializer' do
|
34
|
+
expect(subject).to eq 14.1
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with valid response' do
|
38
|
+
it "should return rate based on calculator's service_code" do
|
39
|
+
allow(calculator.class).to receive(:service_code) { 'dom:3' }
|
40
|
+
expect(subject).to eq 14.1
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should include handling_fee when configured' do
|
44
|
+
Spree::ActiveShipping::Config.set(handling_fee: 100)
|
45
|
+
allow(calculator.class).to receive(:service_code) { 'dom:3' }
|
46
|
+
expect(subject).to eq 15.1
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should return nil if service_code is not found in rate_hash' do
|
50
|
+
allow(calculator.class).to receive(:service_code) { 'invalid service_code' }
|
51
|
+
expect(subject).to be_nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with invalid response' do
|
56
|
+
before do
|
57
|
+
allow(calculator).to receive(:carrier).and_return(carrier)
|
58
|
+
allow(carrier).to receive(:find_rates).and_raise(::ActiveShipping::ResponseError)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should raise a Spree::ShippingError' do
|
62
|
+
expect{ subject }.to raise_exception(Spree::ShippingError)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'service_name' do
|
68
|
+
it 'should return description when not defined' do
|
69
|
+
expect(calculator.class.service_name).to eq calculator.description
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spree::PackageBuilder do
|
4
|
+
include_context 'Canada stock location'
|
5
|
+
include PackageHelper
|
6
|
+
|
7
|
+
let(:shipping_calculator) do
|
8
|
+
Spree::Calculator::Shipping::ActiveShipping::BogusCalculator.new
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:package_builder) { Spree::PackageBuilder.new }
|
12
|
+
|
13
|
+
before do
|
14
|
+
# We disable the multiplier so it's simpler to test
|
15
|
+
allow(package_builder).to receive(:multiplier).and_return(1)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'process' do
|
19
|
+
include_context 'package setup'
|
20
|
+
|
21
|
+
let(:max_weight) { 0 }
|
22
|
+
|
23
|
+
subject { package_builder.process(package, max_weight) }
|
24
|
+
|
25
|
+
it 'returns an array of ActiveShipping::Package' do
|
26
|
+
expect(subject.map(&:class).uniq).to match_array([ActiveShipping::Package])
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'uses the unit multiplier in the calculations' do
|
30
|
+
allow(package_builder).to receive(:multiplier).and_return(2)
|
31
|
+
expect(subject.sum(&:weight)).to eq (package.weight * 2)
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when one product has a zero weight' do
|
35
|
+
let(:variant_1) { FactoryGirl.create(:variant, weight: 0) }
|
36
|
+
|
37
|
+
it 'ignores products with nil weight' do
|
38
|
+
expect(subject.sum(&:weight)).to eq (variant_2.weight * 2)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when one product has a nil weight' do
|
43
|
+
let(:variant_1) { FactoryGirl.create(:variant, weight: nil) }
|
44
|
+
let(:variant_2) { FactoryGirl.create(:variant, weight: nil) }
|
45
|
+
let(:default_weight) { Spree::ActiveShipping::Config[:default_weight] }
|
46
|
+
|
47
|
+
it 'use the default_weight as a weight value' do
|
48
|
+
expect(subject.sum(&:weight)).to eq( default_weight * 4 )
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'with an order containing only products with associated product_packages' do
|
53
|
+
let(:product_weight) { 20 }
|
54
|
+
let(:product_package1) { FactoryGirl.create(:product_package, weight: product_weight) }
|
55
|
+
let(:product_package2) { FactoryGirl.create(:product_package, weight: product_weight) }
|
56
|
+
let(:product_with_packages) do
|
57
|
+
build(
|
58
|
+
:variant,
|
59
|
+
weight: product_weight,
|
60
|
+
product: build(
|
61
|
+
:product,
|
62
|
+
product_packages: [product_package1, product_package2]
|
63
|
+
)
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
let(:package) do
|
68
|
+
build(:stock_package,
|
69
|
+
stock_location: stock_location,
|
70
|
+
contents: [build_content_items(product_with_packages, 2, order),
|
71
|
+
build_content_items(product_with_packages, 1, order)].flatten)
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when there is non-zero max_weight' do
|
75
|
+
context 'when the product_package weight exceeds the allowed max_weight' do
|
76
|
+
let(:max_weight) { product_weight - 1 }
|
77
|
+
|
78
|
+
it 'raise a Spree::Shipping error' do
|
79
|
+
expect { subject }.to raise_error(Spree::ShippingError)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when the product_packages weight does not exceed the allowed max_weight' do
|
84
|
+
let(:max_weight) { 1000 }
|
85
|
+
|
86
|
+
it 'return a ActiveShipping::Package for each ProductPackage of each ContentItem' do
|
87
|
+
# Each individual item in the Order as a 1-1 association with a ContentItem
|
88
|
+
expected_size = package.contents.sum { |item| item.variant.product.product_packages.size }
|
89
|
+
expect(subject.size).to eq expected_size
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when there is no max weight (0)' do
|
95
|
+
it 'return a ActiveShipping::Package for each ProductPackage of each ContentItem' do
|
96
|
+
# Each individual item in the Order as a 1-1 association with a ContentItem
|
97
|
+
expected_size = package.contents.sum { |item| item.variant.product.product_packages.size }
|
98
|
+
expect(subject.size).to eq expected_size
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'with an order containing only products without any associated product_packages' do
|
104
|
+
include_context 'package setup'
|
105
|
+
|
106
|
+
context 'when there is non-zero max_weight' do
|
107
|
+
context 'and their combined weight is lower than the max_weight' do
|
108
|
+
let(:max_weight) { package.weight + 1 }
|
109
|
+
|
110
|
+
it 'will combine items into the same package' do
|
111
|
+
expect(subject.size).to eq 1
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'and their combined weight is higher than the max_weight' do
|
116
|
+
let(:max_weight) { package.weight - 1 }
|
117
|
+
|
118
|
+
it 'will split items into multiple packages' do
|
119
|
+
expect(subject.size).to eq 2
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'and the weight of a single item is higher than the max_weight' do
|
124
|
+
let(:max_weight) { variant_2.weight - 1 }
|
125
|
+
it 'will raise an Spree::ShippingError' do
|
126
|
+
expect { subject }.to raise_error(Spree::ShippingError)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when there is no max_weight' do
|
132
|
+
it 'will combine all items into the same package' do
|
133
|
+
expect(subject.size).to eq 1
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'with an order containing some products with product_packages and some products without' do
|
139
|
+
let(:product_weight) { 20 }
|
140
|
+
let(:product_package1) { FactoryGirl.create(:product_package, weight: product_weight) }
|
141
|
+
let(:product_package2) { FactoryGirl.create(:product_package, weight: product_weight) }
|
142
|
+
|
143
|
+
let(:product_no_packages) { FactoryGirl.create(:variant, weight: 5) }
|
144
|
+
let(:product_with_packages) do
|
145
|
+
build(
|
146
|
+
:variant,
|
147
|
+
weight: product_weight,
|
148
|
+
product: build(
|
149
|
+
:product,
|
150
|
+
product_packages: [product_package1, product_package2]
|
151
|
+
)
|
152
|
+
)
|
153
|
+
end
|
154
|
+
|
155
|
+
let(:package) do
|
156
|
+
build(:stock_package,
|
157
|
+
stock_location: stock_location,
|
158
|
+
contents: [build_content_items(product_with_packages, 1, order),
|
159
|
+
build_content_items(product_no_packages, 1, order),
|
160
|
+
build_content_items(product_no_packages, 1, order)].flatten)
|
161
|
+
end
|
162
|
+
|
163
|
+
let(:max_weight) { 1000 }
|
164
|
+
|
165
|
+
it 'products with product_packages will not be combined with product with no packages' do
|
166
|
+
active_shipping_packages = subject
|
167
|
+
# First package in the array is the "default package" who should
|
168
|
+
# only include product_with_no_packages x2
|
169
|
+
expect(active_shipping_packages[0].weight).to eq (product_no_packages.weight * 2)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|