solidus_active_shipping 1.0.0
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 +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
|