active_shipping 0.12.6 → 1.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -0
- data.tar.gz.sig +0 -0
- data/{CHANGELOG → CHANGELOG.md} +6 -2
- data/CONTRIBUTING.md +32 -0
- data/{README.markdown → README.md} +45 -61
- data/lib/active_shipping.rb +20 -28
- data/lib/active_shipping/carrier.rb +82 -0
- data/lib/active_shipping/carriers.rb +33 -0
- data/lib/active_shipping/carriers/benchmark_carrier.rb +31 -0
- data/lib/active_shipping/carriers/bogus_carrier.rb +12 -0
- data/lib/active_shipping/carriers/canada_post.rb +253 -0
- data/lib/active_shipping/carriers/canada_post_pws.rb +870 -0
- data/lib/active_shipping/carriers/fedex.rb +579 -0
- data/lib/active_shipping/carriers/kunaki.rb +164 -0
- data/lib/active_shipping/carriers/new_zealand_post.rb +262 -0
- data/lib/active_shipping/carriers/shipwire.rb +181 -0
- data/lib/active_shipping/carriers/stamps.rb +861 -0
- data/lib/active_shipping/carriers/ups.rb +648 -0
- data/lib/active_shipping/carriers/usps.rb +642 -0
- data/lib/active_shipping/errors.rb +7 -0
- data/lib/active_shipping/label_response.rb +23 -0
- data/lib/active_shipping/location.rb +149 -0
- data/lib/active_shipping/package.rb +241 -0
- data/lib/active_shipping/rate_estimate.rb +64 -0
- data/lib/active_shipping/rate_response.rb +13 -0
- data/lib/active_shipping/response.rb +41 -0
- data/lib/active_shipping/shipment_event.rb +17 -0
- data/lib/active_shipping/shipment_packer.rb +73 -0
- data/lib/active_shipping/shipping_response.rb +12 -0
- data/lib/active_shipping/tracking_response.rb +52 -0
- data/lib/active_shipping/version.rb +1 -1
- data/lib/vendor/quantified/test/length_test.rb +2 -2
- data/lib/vendor/xml_node/test/test_parsing.rb +1 -1
- metadata +58 -36
- metadata.gz.sig +0 -0
- data/lib/active_shipping/shipping/base.rb +0 -13
- data/lib/active_shipping/shipping/carrier.rb +0 -84
- data/lib/active_shipping/shipping/carriers.rb +0 -23
- data/lib/active_shipping/shipping/carriers/benchmark_carrier.rb +0 -33
- data/lib/active_shipping/shipping/carriers/bogus_carrier.rb +0 -14
- data/lib/active_shipping/shipping/carriers/canada_post.rb +0 -257
- data/lib/active_shipping/shipping/carriers/canada_post_pws.rb +0 -874
- data/lib/active_shipping/shipping/carriers/fedex.rb +0 -581
- data/lib/active_shipping/shipping/carriers/kunaki.rb +0 -166
- data/lib/active_shipping/shipping/carriers/new_zealand_post.rb +0 -262
- data/lib/active_shipping/shipping/carriers/shipwire.rb +0 -184
- data/lib/active_shipping/shipping/carriers/stamps.rb +0 -864
- data/lib/active_shipping/shipping/carriers/ups.rb +0 -650
- data/lib/active_shipping/shipping/carriers/usps.rb +0 -649
- data/lib/active_shipping/shipping/errors.rb +0 -9
- data/lib/active_shipping/shipping/label_response.rb +0 -25
- data/lib/active_shipping/shipping/location.rb +0 -152
- data/lib/active_shipping/shipping/package.rb +0 -243
- data/lib/active_shipping/shipping/rate_estimate.rb +0 -66
- data/lib/active_shipping/shipping/rate_response.rb +0 -15
- data/lib/active_shipping/shipping/response.rb +0 -43
- data/lib/active_shipping/shipping/shipment_event.rb +0 -19
- data/lib/active_shipping/shipping/shipment_packer.rb +0 -75
- data/lib/active_shipping/shipping/shipping_response.rb +0 -14
- data/lib/active_shipping/shipping/tracking_response.rb +0 -54
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
module ActiveShipping
|
4
|
+
class Kunaki < Carrier
|
5
|
+
self.retry_safe = true
|
6
|
+
|
7
|
+
cattr_reader :name
|
8
|
+
@@name = "Kunaki"
|
9
|
+
|
10
|
+
URL = 'https://Kunaki.com/XMLService.ASP'
|
11
|
+
|
12
|
+
CARRIERS = ["UPS", "USPS", "FedEx", "Royal Mail", "Parcelforce", "Pharos", "Eurotrux", "Canada Post", "DHL"]
|
13
|
+
|
14
|
+
COUNTRIES = {
|
15
|
+
'AR' => 'Argentina',
|
16
|
+
'AU' => 'Australia',
|
17
|
+
'AT' => 'Austria',
|
18
|
+
'BE' => 'Belgium',
|
19
|
+
'BR' => 'Brazil',
|
20
|
+
'BG' => 'Bulgaria',
|
21
|
+
'CA' => 'Canada',
|
22
|
+
'CN' => 'China',
|
23
|
+
'CY' => 'Cyprus',
|
24
|
+
'CZ' => 'Czech Republic',
|
25
|
+
'DK' => 'Denmark',
|
26
|
+
'EE' => 'Estonia',
|
27
|
+
'FI' => 'Finland',
|
28
|
+
'FR' => 'France',
|
29
|
+
'DE' => 'Germany',
|
30
|
+
'GI' => 'Gibraltar',
|
31
|
+
'GR' => 'Greece',
|
32
|
+
'GL' => 'Greenland',
|
33
|
+
'HK' => 'Hong Kong',
|
34
|
+
'HU' => 'Hungary',
|
35
|
+
'IS' => 'Iceland',
|
36
|
+
'IE' => 'Ireland',
|
37
|
+
'IL' => 'Israel',
|
38
|
+
'IT' => 'Italy',
|
39
|
+
'JP' => 'Japan',
|
40
|
+
'LV' => 'Latvia',
|
41
|
+
'LI' => 'Liechtenstein',
|
42
|
+
'LT' => 'Lithuania',
|
43
|
+
'LU' => 'Luxembourg',
|
44
|
+
'MX' => 'Mexico',
|
45
|
+
'NL' => 'Netherlands',
|
46
|
+
'NZ' => 'New Zealand',
|
47
|
+
'NO' => 'Norway',
|
48
|
+
'PL' => 'Poland',
|
49
|
+
'PT' => 'Portugal',
|
50
|
+
'RO' => 'Romania',
|
51
|
+
'RU' => 'Russia',
|
52
|
+
'SG' => 'Singapore',
|
53
|
+
'SK' => 'Slovakia',
|
54
|
+
'SI' => 'Slovenia',
|
55
|
+
'ES' => 'Spain',
|
56
|
+
'SE' => 'Sweden',
|
57
|
+
'CH' => 'Switzerland',
|
58
|
+
'TW' => 'Taiwan',
|
59
|
+
'TR' => 'Turkey',
|
60
|
+
'UA' => 'Ukraine',
|
61
|
+
'GB' => 'United Kingdom',
|
62
|
+
'US' => 'United States',
|
63
|
+
'VA' => 'Vatican City',
|
64
|
+
'RS' => 'Yugoslavia',
|
65
|
+
'ME' => 'Yugoslavia'
|
66
|
+
}
|
67
|
+
|
68
|
+
def find_rates(origin, destination, packages, options = {})
|
69
|
+
requires!(options, :items)
|
70
|
+
commit(origin, destination, options)
|
71
|
+
end
|
72
|
+
|
73
|
+
def valid_credentials?
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def build_request(destination, options)
|
80
|
+
xml = Builder::XmlMarkup.new
|
81
|
+
xml.instruct!
|
82
|
+
xml.tag! 'ShippingOptions' do
|
83
|
+
xml.tag! 'AddressInfo' do
|
84
|
+
xml.tag! 'Country', COUNTRIES[destination.country_code]
|
85
|
+
|
86
|
+
state = %w(US CA).include?(destination.country_code.to_s) ? destination.state : ''
|
87
|
+
|
88
|
+
xml.tag! 'State_Province', state
|
89
|
+
xml.tag! 'PostalCode', destination.zip
|
90
|
+
end
|
91
|
+
|
92
|
+
options[:items].each do |item|
|
93
|
+
xml.tag! 'Product' do
|
94
|
+
xml.tag! 'ProductId', item[:sku]
|
95
|
+
xml.tag! 'Quantity', item[:quantity]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
xml.target!
|
100
|
+
end
|
101
|
+
|
102
|
+
def commit(origin, destination, options)
|
103
|
+
request = build_request(destination, options)
|
104
|
+
|
105
|
+
response = parse( ssl_post(URL, request, "Content-Type" => "text/xml") )
|
106
|
+
|
107
|
+
RateResponse.new(success?(response), message_from(response), response,
|
108
|
+
:rates => build_rate_estimates(response, origin, destination)
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def build_rate_estimates(response, origin, destination)
|
113
|
+
response["Options"].collect do |quote|
|
114
|
+
RateEstimate.new(origin, destination, carrier_for(quote["Description"]), quote["Description"],
|
115
|
+
:total_price => quote["Price"],
|
116
|
+
:currency => "USD"
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def carrier_for(service)
|
122
|
+
CARRIERS.dup.find { |carrier| service.to_s =~ /^#{carrier}/i } || service.to_s.split(" ").first
|
123
|
+
end
|
124
|
+
|
125
|
+
def parse(xml)
|
126
|
+
response = {}
|
127
|
+
response["Options"] = []
|
128
|
+
|
129
|
+
document = REXML::Document.new(sanitize(xml))
|
130
|
+
|
131
|
+
response["ErrorCode"] = parse_child_text(document.root, "ErrorCode")
|
132
|
+
response["ErrorText"] = parse_child_text(document.root, "ErrorText")
|
133
|
+
|
134
|
+
document.root.elements.each("Option") do |e|
|
135
|
+
rate = {}
|
136
|
+
rate["Description"] = parse_child_text(e, "Description")
|
137
|
+
rate["Price"] = parse_child_text(e, "Price")
|
138
|
+
response["Options"] << rate
|
139
|
+
end
|
140
|
+
response
|
141
|
+
end
|
142
|
+
|
143
|
+
def sanitize(response)
|
144
|
+
result = response.to_s
|
145
|
+
result.gsub!("\r\n", "")
|
146
|
+
result.gsub!(/<(\/)?(BODY|HTML)>/, '')
|
147
|
+
result
|
148
|
+
end
|
149
|
+
|
150
|
+
def parse_child_text(parent, name)
|
151
|
+
if element = parent.elements[name]
|
152
|
+
element.text
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def success?(response)
|
157
|
+
response["ErrorCode"] == "0"
|
158
|
+
end
|
159
|
+
|
160
|
+
def message_from(response)
|
161
|
+
response["ErrorText"]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,262 @@
|
|
1
|
+
require 'active_support/core_ext/object/to_query'
|
2
|
+
|
3
|
+
module ActiveShipping
|
4
|
+
class NewZealandPost < Carrier
|
5
|
+
cattr_reader :name
|
6
|
+
@@name = "New Zealand Post"
|
7
|
+
|
8
|
+
URL = "http://api.nzpost.co.nz/ratefinder"
|
9
|
+
|
10
|
+
def requirements
|
11
|
+
[:key]
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_rates(origin, destination, packages, options = {})
|
15
|
+
options = @options.merge(options)
|
16
|
+
request = RateRequest.from(origin, destination, packages, options)
|
17
|
+
request.raw_responses = commit(request.urls) if request.new_zealand_origin?
|
18
|
+
request.rate_response
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def commit(urls)
|
24
|
+
save_request(urls).map { |url| ssl_get(url) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.default_location
|
28
|
+
Location.new(
|
29
|
+
:country => "NZ",
|
30
|
+
:city => "Wellington",
|
31
|
+
:address1 => "22 Waterloo Quay",
|
32
|
+
:address2 => "Pipitea",
|
33
|
+
:postal_code => "6011"
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
class NewZealandPostRateResponse < RateResponse
|
38
|
+
attr_reader :raw_responses
|
39
|
+
|
40
|
+
def initialize(success, message, params = {}, options = {})
|
41
|
+
@raw_responses = options[:raw_responses]
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class RateRequest
|
47
|
+
attr_reader :urls
|
48
|
+
attr_writer :raw_responses
|
49
|
+
|
50
|
+
def self.from(*args)
|
51
|
+
return International.new(*args) unless domestic?(args[0..1])
|
52
|
+
Domestic.new(*args)
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(origin, destination, packages, options)
|
56
|
+
@origin = Location.from(origin)
|
57
|
+
@destination = Location.from(destination)
|
58
|
+
@packages = Array(packages).map { |package| NewZealandPostPackage.new(package, api) }
|
59
|
+
@params = { :format => "json", :api_key => options[:key] }
|
60
|
+
@test = options[:test]
|
61
|
+
@rates = @responses = @raw_responses = []
|
62
|
+
@urls = @packages.map { |package| url(package) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def rate_response
|
66
|
+
@rates = rates
|
67
|
+
NewZealandPostRateResponse.new(true, "success", response_params, response_options)
|
68
|
+
rescue => error
|
69
|
+
NewZealandPostRateResponse.new(false, error.message, response_params, response_options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def new_zealand_origin?
|
73
|
+
self.class.new_zealand?(@origin)
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def self.new_zealand?(location)
|
79
|
+
['NZ', nil].include?(Location.from(location).country_code)
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.domestic?(locations)
|
83
|
+
locations.select { |location| new_zealand?(location) }.size == 2
|
84
|
+
end
|
85
|
+
|
86
|
+
def response_options
|
87
|
+
{
|
88
|
+
:rates => @rates,
|
89
|
+
:raw_responses => @raw_responses,
|
90
|
+
:request => @urls,
|
91
|
+
:test => @test
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def response_params
|
96
|
+
{ :responses => @responses }
|
97
|
+
end
|
98
|
+
|
99
|
+
def rate_options(products)
|
100
|
+
{
|
101
|
+
:total_price => products.sum { |product| price(product) },
|
102
|
+
:currency => "NZD",
|
103
|
+
:service_code => products.first["code"]
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
def rates
|
108
|
+
rates_hash.map do |service, products|
|
109
|
+
RateEstimate.new(@origin, @destination, NewZealandPost.name, service, rate_options(products))
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def rates_hash
|
114
|
+
products_hash.select { |_service, products| products.size == @packages.size }
|
115
|
+
end
|
116
|
+
|
117
|
+
def products_hash
|
118
|
+
product_arrays.flatten.group_by { |product| service_name(product) }
|
119
|
+
end
|
120
|
+
|
121
|
+
def product_arrays
|
122
|
+
responses.map do |response|
|
123
|
+
raise(response["message"]) unless response["status"] == "success"
|
124
|
+
response["products"]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def responses
|
129
|
+
@responses = @raw_responses.map { |response| parse_response(response) }
|
130
|
+
end
|
131
|
+
|
132
|
+
def parse_response(response)
|
133
|
+
JSON.parse(response)
|
134
|
+
end
|
135
|
+
|
136
|
+
def url(package)
|
137
|
+
"#{URL}/#{api}?#{params(package).to_query}"
|
138
|
+
end
|
139
|
+
|
140
|
+
def params(package)
|
141
|
+
@params.merge(api_params).merge(package.params)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class Domestic < RateRequest
|
146
|
+
def service_name(product)
|
147
|
+
[product["service_group_description"], product["description"]].join(" ")
|
148
|
+
end
|
149
|
+
|
150
|
+
def api
|
151
|
+
:domestic
|
152
|
+
end
|
153
|
+
|
154
|
+
def api_params
|
155
|
+
{
|
156
|
+
:postcode_src => @origin.postal_code,
|
157
|
+
:postcode_dest => @destination.postal_code,
|
158
|
+
:carrier => "all"
|
159
|
+
}
|
160
|
+
end
|
161
|
+
|
162
|
+
def price(product)
|
163
|
+
product["cost"].to_f
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
class International < RateRequest
|
168
|
+
def rates
|
169
|
+
raise "New Zealand Post packages must originate in New Zealand" unless new_zealand_origin?
|
170
|
+
super
|
171
|
+
end
|
172
|
+
|
173
|
+
def service_name(product)
|
174
|
+
[product["group"], product["name"]].join(" ")
|
175
|
+
end
|
176
|
+
|
177
|
+
def api
|
178
|
+
:international
|
179
|
+
end
|
180
|
+
|
181
|
+
def api_params
|
182
|
+
{ :country_code => @destination.country_code }
|
183
|
+
end
|
184
|
+
|
185
|
+
def price(product)
|
186
|
+
product["price"].to_f
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class NewZealandPostPackage
|
191
|
+
def initialize(package, api)
|
192
|
+
@package = package
|
193
|
+
@api = api
|
194
|
+
@params = { :weight => weight, :length => length }
|
195
|
+
end
|
196
|
+
|
197
|
+
def params
|
198
|
+
@params.merge(api_params).merge(shape_params)
|
199
|
+
end
|
200
|
+
|
201
|
+
protected
|
202
|
+
|
203
|
+
def weight
|
204
|
+
@package.kg
|
205
|
+
end
|
206
|
+
|
207
|
+
def length
|
208
|
+
mm(:length)
|
209
|
+
end
|
210
|
+
|
211
|
+
def height
|
212
|
+
mm(:height)
|
213
|
+
end
|
214
|
+
|
215
|
+
def width
|
216
|
+
mm(:width)
|
217
|
+
end
|
218
|
+
|
219
|
+
def shape
|
220
|
+
return :cylinder if @package.cylinder?
|
221
|
+
:cuboid
|
222
|
+
end
|
223
|
+
|
224
|
+
def api_params
|
225
|
+
send("#{@api}_params")
|
226
|
+
end
|
227
|
+
|
228
|
+
def international_params
|
229
|
+
{ :value => value }
|
230
|
+
end
|
231
|
+
|
232
|
+
def domestic_params
|
233
|
+
{}
|
234
|
+
end
|
235
|
+
|
236
|
+
def shape_params
|
237
|
+
send("#{shape}_params")
|
238
|
+
end
|
239
|
+
|
240
|
+
def cuboid_params
|
241
|
+
{ :height => height, :thickness => width }
|
242
|
+
end
|
243
|
+
|
244
|
+
def cylinder_params
|
245
|
+
{ :diameter => width }
|
246
|
+
end
|
247
|
+
|
248
|
+
def mm(measurement)
|
249
|
+
@package.cm(measurement) * 10
|
250
|
+
end
|
251
|
+
|
252
|
+
def value
|
253
|
+
return 0 unless @package.value && currency == "NZD"
|
254
|
+
@package.value / 100
|
255
|
+
end
|
256
|
+
|
257
|
+
def currency
|
258
|
+
@package.currency || "NZD"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
module ActiveShipping
|
4
|
+
class Shipwire < Carrier
|
5
|
+
self.retry_safe = true
|
6
|
+
|
7
|
+
cattr_reader :name
|
8
|
+
@@name = "Shipwire"
|
9
|
+
|
10
|
+
URL = 'https://api.shipwire.com/exec/RateServices.php'
|
11
|
+
SCHEMA_URL = 'http://www.shipwire.com/exec/download/RateRequest.dtd'
|
12
|
+
WAREHOUSES = { 'CHI' => 'Chicago',
|
13
|
+
'LAX' => 'Los Angeles',
|
14
|
+
'REN' => 'Reno',
|
15
|
+
'VAN' => 'Vancouver',
|
16
|
+
'TOR' => 'Toronto',
|
17
|
+
'UK' => 'United Kingdom'
|
18
|
+
}
|
19
|
+
|
20
|
+
CARRIERS = ["UPS", "USPS", "FedEx", "Royal Mail", "Parcelforce", "Pharos", "Eurotrux", "Canada Post", "DHL"]
|
21
|
+
|
22
|
+
SUCCESS = "OK"
|
23
|
+
SUCCESS_MESSAGE = "Successfully received the shipping rates"
|
24
|
+
NO_RATES_MESSAGE = "No shipping rates could be found for the destination address"
|
25
|
+
REQUIRED_OPTIONS = [:login, :password].freeze
|
26
|
+
|
27
|
+
def find_rates(origin, destination, packages, options = {})
|
28
|
+
requires!(options, :items)
|
29
|
+
commit(origin, destination, options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def valid_credentials?
|
33
|
+
location = self.class.default_location
|
34
|
+
find_rates(location, location, Package.new(100, [5, 15, 30]),
|
35
|
+
:items => [{ :sku => '', :quantity => 1 }]
|
36
|
+
)
|
37
|
+
rescue ActiveShipping::ResponseError
|
38
|
+
true
|
39
|
+
rescue ActiveUtils::ResponseError => e
|
40
|
+
e.response.code != '401'
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def requirements
|
46
|
+
REQUIRED_OPTIONS
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_request(destination, options)
|
50
|
+
xml = Builder::XmlMarkup.new
|
51
|
+
xml.instruct!
|
52
|
+
xml.declare! :DOCTYPE, :RateRequest, :SYSTEM, SCHEMA_URL
|
53
|
+
xml.tag! 'RateRequest' do
|
54
|
+
add_credentials(xml)
|
55
|
+
add_order(xml, destination, options)
|
56
|
+
end
|
57
|
+
xml.target!
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_credentials(xml)
|
61
|
+
xml.tag! 'EmailAddress', @options[:login]
|
62
|
+
xml.tag! 'Password', @options[:password]
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_order(xml, destination, options)
|
66
|
+
xml.tag! 'Order', :id => options[:order_id] do
|
67
|
+
xml.tag! 'Warehouse', options[:warehouse] || '00'
|
68
|
+
|
69
|
+
add_address(xml, destination)
|
70
|
+
Array(options[:items]).each_with_index do |line_item, index|
|
71
|
+
add_item(xml, line_item, index)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_address(xml, destination)
|
77
|
+
xml.tag! 'AddressInfo', :type => 'Ship' do
|
78
|
+
if destination.name.present?
|
79
|
+
xml.tag! 'Name' do
|
80
|
+
xml.tag! 'Full', destination.name
|
81
|
+
end
|
82
|
+
end
|
83
|
+
xml.tag! 'Address1', destination.address1
|
84
|
+
xml.tag! 'Address2', destination.address2 unless destination.address2.blank?
|
85
|
+
xml.tag! 'Address3', destination.address3 unless destination.address3.blank?
|
86
|
+
xml.tag! 'Company', destination.company unless destination.company.blank?
|
87
|
+
xml.tag! 'City', destination.city
|
88
|
+
xml.tag! 'State', destination.state unless destination.state.blank?
|
89
|
+
xml.tag! 'Country', destination.country_code
|
90
|
+
xml.tag! 'Zip', destination.zip unless destination.zip.blank?
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Code is limited to 12 characters
|
95
|
+
def add_item(xml, item, index)
|
96
|
+
xml.tag! 'Item', :num => index do
|
97
|
+
xml.tag! 'Code', item[:sku]
|
98
|
+
xml.tag! 'Quantity', item[:quantity]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def commit(origin, destination, options)
|
103
|
+
request = build_request(destination, options)
|
104
|
+
save_request(request)
|
105
|
+
|
106
|
+
response = parse( ssl_post(URL, "RateRequestXML=#{CGI.escape(request)}") )
|
107
|
+
|
108
|
+
RateResponse.new(response["success"], response["message"], response,
|
109
|
+
:xml => response,
|
110
|
+
:rates => build_rate_estimates(response, origin, destination),
|
111
|
+
:request => last_request
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
115
|
+
def build_rate_estimates(response, origin, destination)
|
116
|
+
response["rates"].collect do |quote|
|
117
|
+
RateEstimate.new(origin, destination, carrier_for(quote["service"]), quote["service"],
|
118
|
+
:service_code => quote["method"],
|
119
|
+
:total_price => quote["cost"],
|
120
|
+
:currency => quote["currency"],
|
121
|
+
:delivery_range => [timestamp_from_business_day(quote["delivery_min"]),
|
122
|
+
timestamp_from_business_day(quote["delivery_max"])]
|
123
|
+
)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def carrier_for(service)
|
128
|
+
CARRIERS.dup.find { |carrier| service.to_s =~ /^#{carrier}/i } || service.to_s.split(" ").first
|
129
|
+
end
|
130
|
+
|
131
|
+
def parse(xml)
|
132
|
+
response = {}
|
133
|
+
response["rates"] = []
|
134
|
+
|
135
|
+
document = REXML::Document.new(xml)
|
136
|
+
|
137
|
+
response["status"] = parse_child_text(document.root, "Status")
|
138
|
+
|
139
|
+
document.root.elements.each("Order/Quotes/Quote") do |e|
|
140
|
+
rate = {}
|
141
|
+
rate["method"] = e.attributes["method"]
|
142
|
+
rate["warehouse"] = parse_child_text(e, "Warehouse")
|
143
|
+
rate["service"] = parse_child_text(e, "Service")
|
144
|
+
rate["cost"] = parse_child_text(e, "Cost")
|
145
|
+
rate["currency"] = parse_child_attribute(e, "Cost", "currency")
|
146
|
+
if delivery_estimate = e.elements["DeliveryEstimate"]
|
147
|
+
rate["delivery_min"] = parse_child_text(delivery_estimate, "Minimum").to_i
|
148
|
+
rate["delivery_max"] = parse_child_text(delivery_estimate, "Maximum").to_i
|
149
|
+
end
|
150
|
+
response["rates"] << rate
|
151
|
+
end
|
152
|
+
|
153
|
+
if response["status"] == SUCCESS && response["rates"].any?
|
154
|
+
response["success"] = true
|
155
|
+
response["message"] = SUCCESS_MESSAGE
|
156
|
+
elsif response["status"] == SUCCESS && response["rates"].empty?
|
157
|
+
response["success"] = false
|
158
|
+
response["message"] = NO_RATES_MESSAGE
|
159
|
+
else
|
160
|
+
response["success"] = false
|
161
|
+
response["message"] = parse_child_text(document.root, "ErrorMessage")
|
162
|
+
end
|
163
|
+
|
164
|
+
response
|
165
|
+
rescue NoMethodError => e
|
166
|
+
raise ActiveShipping::ResponseContentError.new(e, xml)
|
167
|
+
end
|
168
|
+
|
169
|
+
def parse_child_text(parent, name)
|
170
|
+
if element = parent.elements[name]
|
171
|
+
element.text
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def parse_child_attribute(parent, name, attribute)
|
176
|
+
if element = parent.elements[name]
|
177
|
+
element.attributes[attribute]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|