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.
Files changed (155) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +1 -0
  4. data/.simplecov +5 -0
  5. data/.travis.yml +21 -0
  6. data/Gemfile +18 -0
  7. data/README.md +192 -0
  8. data/Rakefile +21 -0
  9. data/app/assets/javascripts/admin/product_packages/edit.js.coffee +4 -0
  10. data/app/assets/javascripts/admin/product_packages/index.js.coffee +28 -0
  11. data/app/assets/javascripts/admin/product_packages/new.js.coffee +7 -0
  12. data/app/assets/javascripts/spree/backend/solidus_active_shipping.js +0 -0
  13. data/app/assets/javascripts/spree/frontend/solidus_active_shipping.js +0 -0
  14. data/app/assets/stylesheets/spree/backend/solidus_active_shipping.css +0 -0
  15. data/app/assets/stylesheets/spree/frontend/solidus_active_shipping.css +0 -0
  16. data/app/controllers/spree/admin/active_shipping_settings_controller.rb +27 -0
  17. data/app/controllers/spree/admin/product_packages_controller.rb +17 -0
  18. data/app/controllers/spree/admin/products_controller_decorator.rb +12 -0
  19. data/app/controllers/spree/checkout_controller_decorator.rb +11 -0
  20. data/app/controllers/spree/orders_controller_decorator.rb +11 -0
  21. data/app/models/spree/calculator/shipping/active_shipping/base.rb +208 -0
  22. data/app/models/spree/calculator/shipping/canada_post/base.rb +17 -0
  23. data/app/models/spree/calculator/shipping/canada_post/expedited.rb +11 -0
  24. data/app/models/spree/calculator/shipping/canada_post/parcel_surface.rb +11 -0
  25. data/app/models/spree/calculator/shipping/canada_post/priority_worldwide_intl.rb +12 -0
  26. data/app/models/spree/calculator/shipping/canada_post/regular.rb +11 -0
  27. data/app/models/spree/calculator/shipping/canada_post/small_packets_air.rb +11 -0
  28. data/app/models/spree/calculator/shipping/canada_post/small_packets_surface.rb +11 -0
  29. data/app/models/spree/calculator/shipping/canada_post/xpresspost.rb +11 -0
  30. data/app/models/spree/calculator/shipping/canada_post/xpresspost_international.rb +11 -0
  31. data/app/models/spree/calculator/shipping/fedex/base.rb +21 -0
  32. data/app/models/spree/calculator/shipping/fedex/express_saver.rb +13 -0
  33. data/app/models/spree/calculator/shipping/fedex/first_overnight.rb +13 -0
  34. data/app/models/spree/calculator/shipping/fedex/ground.rb +13 -0
  35. data/app/models/spree/calculator/shipping/fedex/ground_home_delivery.rb +13 -0
  36. data/app/models/spree/calculator/shipping/fedex/international_economy.rb +13 -0
  37. data/app/models/spree/calculator/shipping/fedex/international_economy_freight.rb +13 -0
  38. data/app/models/spree/calculator/shipping/fedex/international_first.rb +13 -0
  39. data/app/models/spree/calculator/shipping/fedex/international_ground.rb +13 -0
  40. data/app/models/spree/calculator/shipping/fedex/international_priority.rb +13 -0
  41. data/app/models/spree/calculator/shipping/fedex/international_priority_freight.rb +13 -0
  42. data/app/models/spree/calculator/shipping/fedex/international_priority_saturday_delivery.rb +13 -0
  43. data/app/models/spree/calculator/shipping/fedex/one_day_freight.rb +13 -0
  44. data/app/models/spree/calculator/shipping/fedex/one_day_freight_saturday_delivery.rb +13 -0
  45. data/app/models/spree/calculator/shipping/fedex/priority_overnight.rb +13 -0
  46. data/app/models/spree/calculator/shipping/fedex/priority_overnight_saturday_delivery.rb +11 -0
  47. data/app/models/spree/calculator/shipping/fedex/saver.rb +11 -0
  48. data/app/models/spree/calculator/shipping/fedex/standard_overnight.rb +11 -0
  49. data/app/models/spree/calculator/shipping/fedex/three_day_freight.rb +11 -0
  50. data/app/models/spree/calculator/shipping/fedex/three_day_freight_saturday_delivery.rb +11 -0
  51. data/app/models/spree/calculator/shipping/fedex/two_day.rb +11 -0
  52. data/app/models/spree/calculator/shipping/fedex/two_day_freight.rb +11 -0
  53. data/app/models/spree/calculator/shipping/fedex/two_day_freight_saturday_delivery.rb +11 -0
  54. data/app/models/spree/calculator/shipping/fedex/two_day_saturday_delivery.rb +11 -0
  55. data/app/models/spree/calculator/shipping/ups/base.rb +30 -0
  56. data/app/models/spree/calculator/shipping/ups/express.rb +11 -0
  57. data/app/models/spree/calculator/shipping/ups/ground.rb +11 -0
  58. data/app/models/spree/calculator/shipping/ups/next_day_air.rb +11 -0
  59. data/app/models/spree/calculator/shipping/ups/next_day_air_early_am.rb +11 -0
  60. data/app/models/spree/calculator/shipping/ups/next_day_air_saver.rb +11 -0
  61. data/app/models/spree/calculator/shipping/ups/saver.rb +11 -0
  62. data/app/models/spree/calculator/shipping/ups/second_day_air.rb +11 -0
  63. data/app/models/spree/calculator/shipping/ups/standard.rb +11 -0
  64. data/app/models/spree/calculator/shipping/ups/three_day_select.rb +11 -0
  65. data/app/models/spree/calculator/shipping/ups/worldwide_expedited.rb +11 -0
  66. data/app/models/spree/calculator/shipping/usps/base.rb +84 -0
  67. data/app/models/spree/calculator/shipping/usps/express_mail.rb +19 -0
  68. data/app/models/spree/calculator/shipping/usps/express_mail_international.rb +46 -0
  69. data/app/models/spree/calculator/shipping/usps/first_class_mail_international.rb +31 -0
  70. data/app/models/spree/calculator/shipping/usps/first_class_mail_international_large_envelope.rb +31 -0
  71. data/app/models/spree/calculator/shipping/usps/first_class_mail_parcel.rb +28 -0
  72. data/app/models/spree/calculator/shipping/usps/first_class_package_international.rb +49 -0
  73. data/app/models/spree/calculator/shipping/usps/global_express_guaranteed.rb +48 -0
  74. data/app/models/spree/calculator/shipping/usps/media_mail.rb +19 -0
  75. data/app/models/spree/calculator/shipping/usps/priority_mail.rb +19 -0
  76. data/app/models/spree/calculator/shipping/usps/priority_mail_flat_rate_envelope.rb +19 -0
  77. data/app/models/spree/calculator/shipping/usps/priority_mail_international.rb +49 -0
  78. data/app/models/spree/calculator/shipping/usps/priority_mail_international_large_flat_rate_box.rb +43 -0
  79. data/app/models/spree/calculator/shipping/usps/priority_mail_international_medium_flat_rate_box.rb +43 -0
  80. data/app/models/spree/calculator/shipping/usps/priority_mail_international_small_flat_rate_box.rb +43 -0
  81. data/app/models/spree/calculator/shipping/usps/priority_mail_large_flat_rate_box.rb +19 -0
  82. data/app/models/spree/calculator/shipping/usps/priority_mail_medium_flat_rate_box.rb +19 -0
  83. data/app/models/spree/calculator/shipping/usps/priority_mail_small_flat_rate_box.rb +19 -0
  84. data/app/models/spree/calculator/shipping/usps/standard_post.rb +19 -0
  85. data/app/models/spree/content_item_decorator.rb +7 -0
  86. data/app/models/spree/line_item_decorator.rb +4 -0
  87. data/app/models/spree/package_builder.rb +114 -0
  88. data/app/models/spree/product_decorator.rb +10 -0
  89. data/app/models/spree/product_package.rb +10 -0
  90. data/app/models/spree/stock_location_decorator.rb +10 -0
  91. data/app/models/spree/variant_decorator.rb +3 -0
  92. data/app/overrides/spree/admin/shared/_configuration_menu/add_active_shipping_settings_tab.html.erb.deface +3 -0
  93. data/app/overrides/spree/admin/shared/_product_tabs/add_product_packages_tab.html.erb.deface +7 -0
  94. data/app/views/spree/admin/active_shipping_settings/edit.html.erb +92 -0
  95. data/app/views/spree/admin/product_packages/_form.html.erb +24 -0
  96. data/app/views/spree/admin/product_packages/edit.html.erb +15 -0
  97. data/app/views/spree/admin/product_packages/index.html.erb +46 -0
  98. data/app/views/spree/admin/product_packages/new.html.erb +15 -0
  99. data/config/locales/en.yml +93 -0
  100. data/config/locales/fr.yml +12 -0
  101. data/config/routes.rb +9 -0
  102. data/db/migrate/20130107030221_create_product_packages.rb +12 -0
  103. data/lib/solidus_active_shipping.rb +3 -0
  104. data/lib/solidus_active_shipping/engine.rb +44 -0
  105. data/lib/spree/active_shipping_configuration.rb +24 -0
  106. data/lib/spree/shipping_error.rb +3 -0
  107. data/solidus_active_shipping.gemspec +33 -0
  108. data/spec/cassettes/Checkout/with_valid_shipping_address/does_not_break_the_per-item_shipping_method_calculator.yml +134 -0
  109. data/spec/cassettes/FedEx_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Fedex_Ground/1_1_1_1.yml +48 -0
  110. data/spec/cassettes/FedEx_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Fedex_InternationalEconomy/1_1_2_1.yml +48 -0
  111. data/spec/cassettes/FedEx_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Fedex_InternationalFirst/1_1_3_1.yml +48 -0
  112. data/spec/cassettes/FedEx_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Fedex_InternationalPriority/1_1_4_1.yml +48 -0
  113. data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_ExpressSaver/1_2_5_1.yml +48 -0
  114. data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_FirstOvernight/1_2_1_1.yml +48 -0
  115. data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_GroundHomeDelivery/1_2_6_1.yml +48 -0
  116. data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_PriorityOvernight/1_2_2_1.yml +48 -0
  117. data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_StandardOvernight/1_2_3_1.yml +48 -0
  118. data/spec/cassettes/FedEx_calculators/with_US_origin_address/Spree_Calculator_Shipping_Fedex_TwoDay/1_2_4_1.yml +48 -0
  119. data/spec/cassettes/UPS_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Ups_Express/1_1_1_1.yml +103 -0
  120. data/spec/cassettes/UPS_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Ups_Saver/1_1_3_1.yml +103 -0
  121. data/spec/cassettes/UPS_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Ups_Standard/1_1_4_1.yml +103 -0
  122. data/spec/cassettes/UPS_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Ups_ThreeDaySelect/1_1_5_1.yml +103 -0
  123. data/spec/cassettes/UPS_calculators/with_Canadian_origin_address/Spree_Calculator_Shipping_Ups_WorldwideExpedited/1_1_2_1.yml +103 -0
  124. data/spec/cassettes/UPS_calculators/with_US_origin_address/Spree_Calculator_Shipping_Ups_Ground/1_2_1_1.yml +103 -0
  125. data/spec/cassettes/UPS_calculators/with_US_origin_address/Spree_Calculator_Shipping_Ups_NextDayAir/1_2_2_1.yml +103 -0
  126. data/spec/cassettes/UPS_calculators/with_US_origin_address/Spree_Calculator_Shipping_Ups_NextDayAirEarlyAm/1_2_3_1.yml +103 -0
  127. data/spec/cassettes/UPS_calculators/with_US_origin_address/Spree_Calculator_Shipping_Ups_NextDayAirSaver/1_2_4_1.yml +103 -0
  128. data/spec/cassettes/UPS_calculators/with_US_origin_address/Spree_Calculator_Shipping_Ups_SecondDayAir/1_2_5_1.yml +103 -0
  129. data/spec/controllers/admin/active_shipping_settings_controller_spec.rb +38 -0
  130. data/spec/controllers/admin/product_packages_controller_spec.rb +36 -0
  131. data/spec/factories/order_factory_override.rb +46 -0
  132. data/spec/factories/product_package_factory.rb +9 -0
  133. data/spec/factories/state_factory_override.rb +28 -0
  134. data/spec/features/checkout_spec.rb +37 -0
  135. data/spec/fixtures/normal_rates_request.xml +2 -0
  136. data/spec/integrations/calculators/fedex_spec.rb +56 -0
  137. data/spec/integrations/calculators/ups_spec.rb +56 -0
  138. data/spec/lib/spree/active_shipping/bogus_carrier.rb +18 -0
  139. data/spec/lib/spree/calculator/shipping/bogus_calculator.rb +17 -0
  140. data/spec/models/active_shipping_calculator_spec.rb +182 -0
  141. data/spec/models/carriers/usps_calculator_spec.rb +72 -0
  142. data/spec/models/package_builder_spec.rb +173 -0
  143. data/spec/spec.opts +6 -0
  144. data/spec/spec_helper.rb +80 -0
  145. data/spec/support/capybara.rb +24 -0
  146. data/spec/support/checkout_helper.rb +12 -0
  147. data/spec/support/feature_helper.rb +7 -0
  148. data/spec/support/package_helper.rb +9 -0
  149. data/spec/support/shared_contexts/checkout_setup.rb +12 -0
  150. data/spec/support/shared_contexts/package_setup.rb +34 -0
  151. data/spec/support/shared_contexts/shipping_carriers/fedex.rb +15 -0
  152. data/spec/support/shared_contexts/shipping_carriers/ups.rb +14 -0
  153. data/spec/support/shared_contexts/stock_location_setup.rb +26 -0
  154. data/spec/support/web_fixtures.rb +6 -0
  155. 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