omniship 0.1.0 → 0.3.2.2
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.
- data/README.markdown +1 -2
- data/lib/omniship/address.rb +40 -37
- data/lib/omniship/carriers/ups.rb +330 -193
- data/lib/omniship/rate_estimate.rb +1 -1
- data/lib/omniship/version.rb +2 -2
- data/lib/omniship.rb +20 -16
- metadata +175 -152
@@ -1,150 +1,170 @@
|
|
1
1
|
module Omniship
|
2
2
|
class UPS < Carrier
|
3
3
|
self.retry_safe = true
|
4
|
-
|
4
|
+
|
5
5
|
cattr_accessor :default_options
|
6
6
|
cattr_reader :name
|
7
7
|
@@name = "UPS"
|
8
|
-
|
8
|
+
|
9
9
|
TEST_URL = 'https://wwwcie.ups.com'
|
10
10
|
LIVE_URL = 'https://onlinetools.ups.com'
|
11
|
-
|
11
|
+
|
12
12
|
RESOURCES = {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
:rates => 'ups.app/xml/Rate',
|
14
|
+
:track => 'ups.app/xml/Track',
|
15
|
+
:shipconfirm => 'ups.app/xml/ShipConfirm',
|
16
|
+
:shipaccept => 'ups.app/xml/ShipAccept',
|
17
|
+
:shipvoid => 'ups.app/xml/Void',
|
18
|
+
:valid_address => 'ups.app/xml/XAV'
|
18
19
|
}
|
19
|
-
|
20
|
+
|
20
21
|
PICKUP_CODES = HashWithIndifferentAccess.new({
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
:daily_pickup => "01",
|
23
|
+
:customer_counter => "03",
|
24
|
+
:one_time_pickup => "06",
|
25
|
+
:on_call_air => "07",
|
26
|
+
:suggested_retail_rates => "11",
|
27
|
+
:letter_center => "19",
|
28
|
+
:air_service_center => "20"
|
29
|
+
})
|
30
|
+
|
30
31
|
CUSTOMER_CLASSIFICATIONS = HashWithIndifferentAccess.new({
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
:wholesale => "01",
|
33
|
+
:occasional => "03",
|
34
|
+
:retail => "04"
|
35
|
+
})
|
36
|
+
|
36
37
|
# these are the defaults described in the UPS API docs,
|
37
38
|
# but they don't seem to apply them under all circumstances,
|
38
39
|
# so we need to take matters into our own hands
|
39
|
-
DEFAULT_CUSTOMER_CLASSIFICATIONS = Hash.new do |hash,key|
|
40
|
+
DEFAULT_CUSTOMER_CLASSIFICATIONS = Hash.new do |hash, key|
|
40
41
|
hash[key] = case key.to_sym
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
when :daily_pickup then
|
43
|
+
:wholesale
|
44
|
+
when :customer_counter then
|
45
|
+
:retail
|
46
|
+
else
|
47
|
+
:occasional
|
48
|
+
end
|
46
49
|
end
|
47
|
-
|
50
|
+
|
48
51
|
DEFAULT_SERVICES = {
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
52
|
+
"01" => "UPS Next Day Air",
|
53
|
+
"02" => "UPS Second Day Air",
|
54
|
+
"03" => "UPS Ground",
|
55
|
+
"07" => "UPS Worldwide Express",
|
56
|
+
"08" => "UPS Worldwide Expedited",
|
57
|
+
"11" => "UPS Standard",
|
58
|
+
"12" => "UPS Three-Day Select",
|
59
|
+
"13" => "UPS Next Day Air Saver",
|
60
|
+
"14" => "UPS Next Day Air Early A.M.",
|
61
|
+
"54" => "UPS Worldwide Express Plus",
|
62
|
+
"59" => "UPS Second Day Air A.M.",
|
63
|
+
"65" => "UPS Saver",
|
64
|
+
"82" => "UPS Today Standard",
|
65
|
+
"83" => "UPS Today Dedicated Courier",
|
66
|
+
"84" => "UPS Today Intercity",
|
67
|
+
"85" => "UPS Today Express",
|
68
|
+
"86" => "UPS Today Express Saver"
|
66
69
|
}
|
67
|
-
|
70
|
+
|
68
71
|
CANADA_ORIGIN_SERVICES = {
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
+
"01" => "UPS Express",
|
73
|
+
"02" => "UPS Expedited",
|
74
|
+
"14" => "UPS Express Early A.M."
|
72
75
|
}
|
73
|
-
|
76
|
+
|
74
77
|
MEXICO_ORIGIN_SERVICES = {
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
+
"07" => "UPS Express",
|
79
|
+
"08" => "UPS Expedited",
|
80
|
+
"54" => "UPS Express Plus"
|
78
81
|
}
|
79
|
-
|
82
|
+
|
80
83
|
EU_ORIGIN_SERVICES = {
|
81
|
-
|
82
|
-
|
84
|
+
"07" => "UPS Express",
|
85
|
+
"08" => "UPS Expedited"
|
83
86
|
}
|
84
|
-
|
87
|
+
|
85
88
|
OTHER_NON_US_ORIGIN_SERVICES = {
|
86
|
-
|
89
|
+
"07" => "UPS Express"
|
87
90
|
}
|
88
|
-
|
91
|
+
|
89
92
|
# From http://en.wikipedia.org/w/index.php?title=European_Union&oldid=174718707 (Current as of November 30, 2007)
|
90
93
|
EU_COUNTRY_CODES = ["GB", "AT", "BE", "BG", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE"]
|
91
|
-
|
94
|
+
|
92
95
|
US_TERRITORIES_TREATED_AS_COUNTRIES = ["AS", "FM", "GU", "MH", "MP", "PW", "PR", "VI"]
|
93
|
-
|
96
|
+
|
94
97
|
def requirements
|
95
98
|
[:key, :login, :password]
|
96
99
|
end
|
97
|
-
|
100
|
+
|
98
101
|
def find_rates(origin, destination, packages, options={})
|
99
102
|
origin, destination = upsified_location(origin), upsified_location(destination)
|
100
103
|
options = @options.merge(options)
|
104
|
+
options[:test] = options[:test].nil? ? false : options[:test]
|
101
105
|
packages = Array(packages)
|
102
106
|
access_request = build_access_request
|
103
107
|
rate_request = build_rate_request(origin, destination, packages, options)
|
104
|
-
response = commit(:rates, save_request(access_request.gsub("\n","") + rate_request.gsub("\n","")),
|
108
|
+
response = commit(:rates, save_request(access_request.gsub("\n", "") + rate_request.gsub("\n", "")), options[:test])
|
105
109
|
parse_rate_response(origin, destination, packages, response, options)
|
106
110
|
end
|
107
|
-
|
111
|
+
|
108
112
|
def find_tracking_info(tracking_number, options={})
|
109
113
|
options = @options.update(options)
|
114
|
+
options[:test] = options[:test].nil? ? false : options[:test]
|
110
115
|
access_request = build_access_request
|
111
116
|
tracking_request = build_tracking_request(tracking_number, options)
|
112
|
-
response = commit(:track, save_request(access_request.gsub("\n","") + tracking_request.gsub("\n","")),
|
117
|
+
response = commit(:track, save_request(access_request.gsub("\n", "") + tracking_request.gsub("\n", "")), options[:test])
|
113
118
|
parse_tracking_response(response, options)
|
114
119
|
end
|
115
120
|
|
116
121
|
# Creating shipping functionality for UPS
|
117
122
|
def create_shipment(origin, destination, packages, options={})
|
123
|
+
@options = @options.merge(options)
|
118
124
|
origin, destination = upsified_location(origin), upsified_location(destination)
|
119
125
|
options = @options.merge(options)
|
126
|
+
options[:test] = options[:test].nil? ? true : options[:test]
|
120
127
|
packages = Array(packages)
|
121
128
|
access_request = build_access_request
|
122
129
|
ship_confirm_request = build_ship_confirm(origin, destination, packages, options)
|
123
|
-
response = commit(:shipconfirm, save_request(access_request.gsub("\n","") + ship_confirm_request.gsub("\n","")),
|
130
|
+
response = commit(:shipconfirm, save_request(access_request.gsub("\n", "") + ship_confirm_request.gsub("\n", "")), options[:test])
|
124
131
|
parse_ship_confirm_response(origin, destination, packages, response, options)
|
125
132
|
end
|
126
133
|
|
127
134
|
def accept_shipment(digest, options={})
|
135
|
+
options[:test] = options[:test].nil? ? true : options[:test]
|
128
136
|
access_request = build_access_request
|
129
137
|
ship_accept_request = build_ship_accept(digest)
|
130
|
-
response = commit(:shipaccept, save_request(access_request.gsub("\n","") + ship_accept_request.gsub("\n","")),
|
138
|
+
response = commit(:shipaccept, save_request(access_request.gsub("\n", "") + ship_accept_request.gsub("\n", "")), options[:test])
|
131
139
|
parse_ship_accept_response(response, options)
|
132
140
|
end
|
133
141
|
|
134
|
-
def void_shipment(tracking_number, options={})
|
142
|
+
def void_shipment(ups_shipment_id,tracking_number, options={})
|
143
|
+
@options = @options.merge(options)
|
135
144
|
options = @options.merge(options)
|
145
|
+
options[:test] = options[:test].nil? ? true : options[:test]
|
136
146
|
access_request = build_access_request
|
137
|
-
ship_void_request = build_void_request(tracking_number)
|
138
|
-
response = commit(:shipvoid, save_request(access_request.gsub("\n","") + ship_void_request.gsub("\n","")),
|
147
|
+
ship_void_request = build_void_request(ups_shipment_id,tracking_number)
|
148
|
+
response = commit(:shipvoid, save_request(access_request.gsub("\n", "") + ship_void_request.gsub("\n", "")), options[:test])
|
139
149
|
parse_ship_void_response(response, options)
|
140
150
|
end
|
141
151
|
|
152
|
+
def validate_address(address,city,state,zip_code,country_code, options={})
|
153
|
+
@options = @options.merge(options)
|
154
|
+
access_request = build_access_request
|
155
|
+
validate_address_request = build_valid_address_request(address,city,state,zip_code,country_code)
|
156
|
+
options[:test] = options[:test].nil? ? true : options[:test]
|
157
|
+
response = commit(:valid_address, save_request(access_request.gsub("\n", "") + validate_address_request.gsub("\n", "")), options[:test])
|
158
|
+
parse_response = parse_ship_valid_address(response)
|
159
|
+
parse_response
|
160
|
+
end
|
161
|
+
|
142
162
|
protected
|
143
|
-
|
163
|
+
|
144
164
|
def upsified_location(location)
|
145
165
|
if location.country_code == 'US' && US_TERRITORIES_TREATED_AS_COUNTRIES.include?(location.state)
|
146
166
|
atts = {:country => location.state}
|
147
|
-
[:zip, :city, :address1, :address2, :address3, :phone, :fax, :address_type].each do |att|
|
167
|
+
[:zip, :city, :address1, :address2, :address3, :phone, :fax, :address_type, :attention_name].each do |att|
|
148
168
|
atts[att] = location.send(att)
|
149
169
|
end
|
150
170
|
Address.new(atts)
|
@@ -152,7 +172,8 @@ module Omniship
|
|
152
172
|
location
|
153
173
|
end
|
154
174
|
end
|
155
|
-
|
175
|
+
|
176
|
+
|
156
177
|
def build_access_request
|
157
178
|
builder = Nokogiri::XML::Builder.new do |xml|
|
158
179
|
xml.AccessRequest {
|
@@ -171,6 +192,15 @@ module Omniship
|
|
171
192
|
|
172
193
|
# Build the ship_confirm XML request
|
173
194
|
def build_ship_confirm(origin, destination, packages, options={})
|
195
|
+
|
196
|
+
#Return Service types:
|
197
|
+
#‘2’ = UPS Print and Mail (PNM)
|
198
|
+
#‘3’ = UPS Return Service 1- Attempt (RS1)
|
199
|
+
#‘5’ = UPS Return Service 3-Attempt (RS3)
|
200
|
+
#‘8’ = UPS Electronic Return Label (ERL)
|
201
|
+
#‘9’ = UPS Print Return Label (PRL)
|
202
|
+
|
203
|
+
|
174
204
|
packages = Array(packages)
|
175
205
|
builder = Nokogiri::XML::Builder.new do |xml|
|
176
206
|
xml.ShipmentConfirmRequest {
|
@@ -196,9 +226,22 @@ module Omniship
|
|
196
226
|
}
|
197
227
|
xml.ShipmentServiceOptions {
|
198
228
|
xml.SaturdayDelivery if options[:saturday] == true
|
229
|
+
if options[:delivery_confirmation_type].present?
|
230
|
+
xml.DeliveryConfirmation {
|
231
|
+
xml.DCISType options[:delivery_confirmation_type]
|
232
|
+
}
|
233
|
+
end
|
199
234
|
}
|
235
|
+
if options[:return_service_code].present?
|
236
|
+
xml.ReturnService {
|
237
|
+
xml.Code options[:return_service_code]
|
238
|
+
if options[:return_service_description].present?
|
239
|
+
xml.Description options[:return_service_description]
|
240
|
+
end
|
241
|
+
}
|
242
|
+
end
|
200
243
|
packages.each do |package|
|
201
|
-
imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
|
244
|
+
imperial = ['US', 'LR', 'MM'].include?(origin.country_code(:alpha2))
|
202
245
|
xml.Package {
|
203
246
|
xml.PackagingType {
|
204
247
|
xml.Code package.options[:package_type]
|
@@ -207,9 +250,9 @@ module Omniship
|
|
207
250
|
xml.UnitOfMeasurement {
|
208
251
|
xml.Code imperial ? 'IN' : 'CM'
|
209
252
|
}
|
210
|
-
[:length
|
253
|
+
[:length, :width, :height].each do |axis|
|
211
254
|
value = ((imperial ? package.inches(axis) : package.cm(axis)).to_f*1000).round/1000.0 # 3 decimals
|
212
|
-
xml.send axis.to_s.gsub(/^[a-z]|\s+[-z]/) { |a| a.upcase }, [value,0.1].max
|
255
|
+
xml.send axis.to_s.gsub(/^[a-z]|\s+[-z]/) { |a| a.upcase }, [value, 0.1].max
|
213
256
|
end
|
214
257
|
}
|
215
258
|
xml.PackageWeight {
|
@@ -217,7 +260,15 @@ module Omniship
|
|
217
260
|
xml.Code imperial ? 'LBS' : 'KGS'
|
218
261
|
}
|
219
262
|
value = ((imperial ? package.lbs : package.kgs).to_f*1000).round/1000.0 # decimals
|
220
|
-
xml.Weight [value,0.1].max
|
263
|
+
xml.Weight [value, 0.1].max
|
264
|
+
}
|
265
|
+
xml.Description package.options[:package_description] if package.options[:package_description].present?
|
266
|
+
xml.PackageServiceOptions {
|
267
|
+
if package.options[:delivery_confirmation_type].present?
|
268
|
+
xml.DeliveryConfirmation {
|
269
|
+
xml.DCISType package.options[:delivery_confirmation_type]
|
270
|
+
}
|
271
|
+
end
|
221
272
|
}
|
222
273
|
}
|
223
274
|
end
|
@@ -247,19 +298,40 @@ module Omniship
|
|
247
298
|
builder.to_xml
|
248
299
|
end
|
249
300
|
|
250
|
-
def build_void_request(tracking_number)
|
301
|
+
def build_void_request(ups_shipment_id,tracking_number)
|
251
302
|
builder = Nokogiri::XML::Builder.new do |xml|
|
252
|
-
xml.VoidShipmentRequest {
|
303
|
+
xml.VoidShipmentRequest {
|
253
304
|
xml.Request {
|
254
305
|
xml.RequestAction 'Void'
|
255
306
|
}
|
256
307
|
xml.ExpandedVoidShipment {
|
257
|
-
xml.ShipmentIdentificationNumber
|
308
|
+
xml.ShipmentIdentificationNumber ups_shipment_id
|
309
|
+
xml.TrackingNumber tracking_number
|
258
310
|
}
|
259
311
|
}
|
260
312
|
end
|
261
313
|
builder.to_xml
|
262
314
|
end
|
315
|
+
|
316
|
+
def build_valid_address_request(address,city,state,zip_code,country_code)
|
317
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
318
|
+
xml.AddressValidationRequest {
|
319
|
+
xml.Request{
|
320
|
+
xml.RequestAction 'XAV'
|
321
|
+
xml.RequestOption 3
|
322
|
+
}
|
323
|
+
|
324
|
+
xml.AddressKeyFormat{
|
325
|
+
xml.AddressLine address
|
326
|
+
xml.PoliticalDivision2 city
|
327
|
+
xml.PoliticalDivision1 state
|
328
|
+
xml.PostcodePrimaryLow zip_code
|
329
|
+
xml.CountryCode country_code
|
330
|
+
}
|
331
|
+
}
|
332
|
+
end
|
333
|
+
builder.to_xml
|
334
|
+
end
|
263
335
|
|
264
336
|
def build_rate_request(origin, destination, packages, options={})
|
265
337
|
packages = Array(packages)
|
@@ -271,88 +343,95 @@ module Omniship
|
|
271
343
|
}
|
272
344
|
# not implemented: 'Rate' RequestOption to specify a single service query
|
273
345
|
# request << XmlNode.new('RequestOption', ((options[:service].nil? or options[:service] == :all) ? 'Shop' : 'Rate'))
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
346
|
+
pickup_type = options[:pickup_type] || :daily_pickup
|
347
|
+
xml.PickupType {
|
348
|
+
xml.Code PICKUP_CODES[pickup_type]
|
349
|
+
# not implemented: PickupType/PickupDetails element
|
350
|
+
}
|
351
|
+
cc = options[:customer_classification] || DEFAULT_CUSTOMER_CLASSIFICATIONS[pickup_type]
|
352
|
+
xml.CustomerClassification {
|
353
|
+
xml.Code CUSTOMER_CLASSIFICATIONS[cc]
|
354
|
+
}
|
355
|
+
xml.Shipment {
|
356
|
+
build_location_node(['Shipper'], (options[:shipper] || origin), options, xml)
|
357
|
+
build_location_node(['ShipTo'], destination, options, xml)
|
358
|
+
if options[:shipper] && options[:shipper] != origin
|
359
|
+
build_location_node(['ShipFrom'], origin, options, xml)
|
360
|
+
end
|
289
361
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
}
|
305
|
-
xml.Dimensions {
|
306
|
-
xml.UnitOfMeasurement {
|
307
|
-
xml.Code imperial ? 'IN' : 'CM'
|
362
|
+
# not implemented: * Shipment/ShipmentWeight element
|
363
|
+
# * Shipment/ReferenceNumber element
|
364
|
+
# * Shipment/Service element
|
365
|
+
# * Shipment/PickupDate element
|
366
|
+
# * Shipment/ScheduledDeliveryDate element
|
367
|
+
# * Shipment/ScheduledDeliveryTime element
|
368
|
+
# * Shipment/AlternateDeliveryTime element
|
369
|
+
# * Shipment/DocumentsOnly element
|
370
|
+
|
371
|
+
packages.each do |package|
|
372
|
+
imperial = ['US', 'LR', 'MM'].include?(origin.country_code(:alpha2))
|
373
|
+
xml.Package {
|
374
|
+
xml.PackagingType {
|
375
|
+
xml.Code '02'
|
308
376
|
}
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
377
|
+
xml.Dimensions {
|
378
|
+
xml.UnitOfMeasurement {
|
379
|
+
xml.Code imperial ? 'IN' : 'CM'
|
380
|
+
}
|
381
|
+
[:length, :width, :height].each do |axis|
|
382
|
+
value = ((imperial ? package.inches(axis) : package.cm(axis)).to_f*1000).round/1000.0 # 3 decimals
|
383
|
+
xml.send axis.to_s.gsub(/^[a-z]|\s+[-z]/) { |a| a.upcase }, [value, 0.1].max
|
384
|
+
end
|
317
385
|
}
|
318
|
-
|
319
|
-
|
386
|
+
xml.PackageWeight {
|
387
|
+
xml.UnitOfMeasurement {
|
388
|
+
xml.Code imperial ? 'LBS' : 'KGS'
|
389
|
+
}
|
390
|
+
value = ((imperial ? package.lbs : package.kgs).to_f*1000).round/1000.0 # 3 decimals
|
391
|
+
xml.Weight [value, 0.1].max
|
392
|
+
}
|
393
|
+
xml.PackageServiceOptions {
|
394
|
+
if package.options[:delivery_confirmation_type].present?
|
395
|
+
xml.DeliveryConfirmation {
|
396
|
+
xml.DCISType package.options[:delivery_confirmation_type]
|
397
|
+
}
|
398
|
+
end
|
399
|
+
}
|
400
|
+
# not implemented: * Shipment/Package/LargePackageIndicator element
|
401
|
+
# * Shipment/Package/ReferenceNumber element
|
402
|
+
# * Shipment/Package/AdditionalHandling element
|
320
403
|
}
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
end
|
327
|
-
# not implemented: * Shipment/ShipmentServiceOptions element
|
328
|
-
# * Shipment/RateInformation element
|
329
|
-
}
|
330
|
-
}
|
404
|
+
end
|
405
|
+
# not implemented: * Shipment/ShipmentServiceOptions element
|
406
|
+
# * Shipment/RateInformation element
|
407
|
+
}
|
408
|
+
}
|
331
409
|
end
|
332
410
|
builder.to_xml
|
333
411
|
end
|
334
|
-
|
412
|
+
|
335
413
|
def build_tracking_request(tracking_number, options={})
|
336
|
-
|
414
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
337
415
|
xml.TrackRequest {
|
338
416
|
xml.Request {
|
339
417
|
xml.RequestAction 'Track'
|
340
|
-
xml.RequestOption '
|
341
|
-
}
|
342
|
-
xml.TrackingNumber tracking_number.to_s
|
418
|
+
xml.RequestOption '7'
|
419
|
+
}
|
420
|
+
xml.TrackingNumber tracking_number.to_s
|
421
|
+
xml.TrackingOption '02'
|
343
422
|
}
|
344
423
|
end
|
345
424
|
builder.to_xml
|
346
425
|
end
|
347
|
-
|
348
|
-
def build_location_node(name,location,options={},xml)
|
349
|
-
for name in name
|
426
|
+
|
427
|
+
def build_location_node(name, location, options={}, xml)
|
428
|
+
for name in name
|
350
429
|
xml.send(name) {
|
351
430
|
xml.Name location.name unless location.name.blank?
|
352
431
|
xml.AttentionName location.attention_name unless location.attention_name.blank?
|
353
432
|
xml.CompanyName location.company_name unless location.company_name.blank?
|
354
|
-
xml.PhoneNumber location.phone.gsub(/[^\d]/,'') unless location.phone.blank?
|
355
|
-
xml.FaxNumber location.fax.gsub(/[^\d]/,'') unless location.fax.blank?
|
433
|
+
xml.PhoneNumber location.phone.gsub(/[^\d]/, '') unless location.phone.blank?
|
434
|
+
xml.FaxNumber location.fax.gsub(/[^\d]/, '') unless location.fax.blank?
|
356
435
|
|
357
436
|
if name =='Shipper' and (origin_account = @options[:origin_account] || options[:origin_account])
|
358
437
|
xml.ShipperNumber origin_account
|
@@ -368,7 +447,7 @@ module Omniship
|
|
368
447
|
xml.StateProvinceCode location.province unless location.province.blank?
|
369
448
|
xml.PostalCode location.postal_code unless location.postal_code.blank?
|
370
449
|
xml.CountryCode location.country_code unless location.country_code.blank?
|
371
|
-
xml.
|
450
|
+
xml.ResidentialAddress unless location.commercial?
|
372
451
|
}
|
373
452
|
}
|
374
453
|
end
|
@@ -376,49 +455,82 @@ module Omniship
|
|
376
455
|
|
377
456
|
def parse_rate_response(origin, destination, packages, response, options={})
|
378
457
|
rates = []
|
379
|
-
|
458
|
+
|
380
459
|
xml = Nokogiri::XML(response)
|
381
460
|
success = response_success?(xml)
|
382
461
|
message = response_message(xml)
|
383
462
|
|
384
463
|
if success
|
385
464
|
rate_estimates = []
|
386
|
-
|
465
|
+
|
387
466
|
xml.xpath('/*/RatedShipment').each do |rated_shipment|
|
388
467
|
service_code = rated_shipment.xpath('Service/Code').text.to_s
|
389
468
|
days_to_delivery = rated_shipment.xpath('GuaranteedDaysToDelivery').text.to_s.to_i
|
390
|
-
delivery_date
|
469
|
+
delivery_date = days_to_delivery >= 1 ? days_to_delivery.days.from_now.strftime("%Y-%m-%d") : nil
|
391
470
|
|
392
471
|
rate_estimates << RateEstimate.new(origin, destination, @@name,
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
472
|
+
service_name_for(origin, service_code),
|
473
|
+
:total_price => rated_shipment.xpath('TotalCharges/MonetaryValue').text.to_s.to_f,
|
474
|
+
:currency => rated_shipment.xpath('TotalCharges/CurrencyCode').text.to_s,
|
475
|
+
:service_code => service_code,
|
476
|
+
:packages => packages,
|
477
|
+
:delivery_range => [delivery_date])
|
399
478
|
end
|
400
479
|
end
|
401
480
|
RateResponse.new(success, message, Hash.from_xml(response).values.first, :rates => rate_estimates, :xml => response, :request => last_request)
|
402
481
|
end
|
403
|
-
|
404
|
-
def parse_tracking_response(response, options={})
|
405
|
-
#TODO
|
482
|
+
|
483
|
+
def parse_tracking_response(response, options={})
|
406
484
|
xml = Nokogiri::XML(response)
|
407
485
|
success = response_success?(xml)
|
408
486
|
message = response_message(xml)
|
409
487
|
|
488
|
+
puts "response :" + xml.to_s
|
489
|
+
|
410
490
|
if success
|
411
491
|
tracking_number, origin, destination = nil
|
412
|
-
|
492
|
+
shipment_details = Hash.new
|
413
493
|
|
494
|
+
tracking_number = xml.xpath('/*/Shipment/Package/TrackingNumber').text
|
495
|
+
shipment_details[:tracking_number] = tracking_number
|
496
|
+
|
497
|
+
estimated_delivery_date = xml.xpath('/*/Shipment/ScheduledDeliveryDate').text
|
498
|
+
if !estimated_delivery_date.blank?
|
499
|
+
estimated_delivery_date = Date.strptime(estimated_delivery_date,'%Y%m%d')
|
500
|
+
shipment_details[:estimated_delivery_date] = estimated_delivery_date
|
501
|
+
else
|
502
|
+
reschedule_delivery_date = xml.xpath('/*/Shipment/Package/RescheduledDeliveryDate').text
|
503
|
+
if !reschedule_delivery_date.blank?
|
504
|
+
reschedule_delivery_date = Date.strptime(reschedule_delivery_date,'%Y%m%d')
|
505
|
+
shipment_details[:estimated_delivery_date] = reschedule_delivery_date
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
puts 'tracking_number: ' + tracking_number
|
510
|
+
puts 'estimated_delivery_date: ' + estimated_delivery_date.to_s
|
511
|
+
|
512
|
+
activities = []
|
513
|
+
xml.xpath('/*/Shipment/Package/Activity').each do |activity|
|
514
|
+
status_code = activity.xpath('Status/StatusCode').text
|
515
|
+
status_dsc = activity.xpath('Status/StatusType/Description').text
|
516
|
+
date = activity.xpath('Date').text
|
517
|
+
time = activity.xpath('Time').text
|
518
|
+
hour, minute, second = time.scan(/\d{2}/)
|
519
|
+
year, month, day = date[0..3], date[4..5], date[6..7]
|
520
|
+
date_time = Time.utc(year, month, day, hour, minute, second)
|
521
|
+
location = activity.xpath('ActivityLocation/Address/City').text + ' ' + activity.xpath('ActivityLocation/Address/StateProvinceCode').text + ' ' + activity.xpath('ActivityLocation/Address/CountryCode').text
|
522
|
+
activities << {:status_code => status_code, :status_dsc => status_dsc, :date => date_time, :location => location}
|
523
|
+
end
|
524
|
+
shipment_details[:activities] = activities
|
525
|
+
|
414
526
|
#first_shipment = xml.gelements['/*/Shipment']
|
415
527
|
#first_package = first_shipment.elements['Package']
|
416
528
|
#tracking_number = first_shipment.get_text('ShipmentIdentificationNumber | Package/TrackingNumber').to_s
|
417
|
-
|
529
|
+
|
418
530
|
#origin, destination = %w{Shipper ShipTo}.map do |location|
|
419
531
|
# location_from_address_node(first_shipment.elements["#{location}/Address"])
|
420
532
|
#end
|
421
|
-
|
533
|
+
|
422
534
|
#activities = first_package.get_elements('Activity')
|
423
535
|
#unless activities.empty?
|
424
536
|
# shipment_events = activities.map do |activity|
|
@@ -459,15 +571,15 @@ module Omniship
|
|
459
571
|
# :origin => origin,
|
460
572
|
# :destination => destination,
|
461
573
|
# :tracking_number => tracking_number)
|
462
|
-
return
|
574
|
+
return shipment_details
|
463
575
|
end
|
464
|
-
|
576
|
+
|
465
577
|
def parse_ship_confirm_response(origin, destination, packages, response, options={})
|
466
578
|
xml = Nokogiri::XML(response)
|
467
579
|
success = response_success?(xml)
|
468
|
-
|
580
|
+
|
469
581
|
if success
|
470
|
-
@response_text = xml.xpath('//*/ShipmentDigest').text
|
582
|
+
@response_text = xml.xpath('//*/ShipmentDigest').text
|
471
583
|
else
|
472
584
|
@response_text = {}
|
473
585
|
@response_text[:status] = xml.xpath('/*/Response/ResponseStatusDescription').text
|
@@ -481,24 +593,24 @@ module Omniship
|
|
481
593
|
def parse_ship_accept_response(response, options={})
|
482
594
|
xml = Nokogiri::XML(response)
|
483
595
|
success = response_success?(xml)
|
484
|
-
@response_text = {}
|
485
|
-
|
596
|
+
@response_text = {}
|
597
|
+
|
486
598
|
if success
|
487
599
|
tracking_number = []
|
488
|
-
label
|
600
|
+
label = []
|
489
601
|
|
490
|
-
@response_text[:charges]
|
602
|
+
@response_text[:charges] = xml.xpath('/*/ShipmentResults/*/TotalCharges/MonetaryValue').text
|
491
603
|
@response_text[:shipment_id] = xml.xpath('/*/ShipmentResults/ShipmentIdentificationNumber').text
|
492
|
-
|
604
|
+
|
493
605
|
xml.xpath('/*/ShipmentResults/*/TrackingNumber').each do |track|
|
494
606
|
tracking_number << track.text
|
495
607
|
end
|
496
|
-
@response_text[:tracking_number] = tracking_number
|
608
|
+
@response_text[:tracking_number] = tracking_number
|
497
609
|
|
498
610
|
xml.xpath('/*/ShipmentResults/*/LabelImage/GraphicImage').each do |image|
|
499
611
|
label << image.text
|
500
612
|
end
|
501
|
-
@response_text[:label]
|
613
|
+
@response_text[:label] = label
|
502
614
|
@response_text[:success] = true
|
503
615
|
else
|
504
616
|
@response_text[:status] = xml.xpath('/*/Response/ResponseStatusDescription').text
|
@@ -511,54 +623,79 @@ module Omniship
|
|
511
623
|
|
512
624
|
def parse_ship_void_response(response, options={})
|
513
625
|
xml = Nokogiri::XML(response)
|
626
|
+
puts "Void response: " + xml.to_s
|
514
627
|
success = response_success?(xml)
|
515
628
|
if success
|
516
629
|
@void = "Shipment successfully voided!"
|
517
630
|
else
|
518
631
|
@void = "Voiding shipment failed!"
|
519
632
|
end
|
520
|
-
|
633
|
+
|
521
634
|
return @void
|
522
635
|
end
|
636
|
+
|
637
|
+
def parse_ship_valid_address(response, options={})
|
638
|
+
xml = Nokogiri::XML(response)
|
639
|
+
success = response_success?(xml)
|
640
|
+
suggested_addresses = Array.new
|
641
|
+
if success
|
642
|
+
addresses = xml.xpath('/*/AddressKeyFormat').each do |address_data|
|
643
|
+
address_hash = Hash.new
|
644
|
+
address_hash[:address] = address_data.xpath('AddressLine').text
|
645
|
+
address_hash[:city] = address_data.xpath('PoliticalDivision2').text
|
646
|
+
address_hash[:state] = address_data.xpath('PoliticalDivision1').text
|
647
|
+
address_hash[:zip_code] = address_data.xpath('PostcodePrimaryLow').text
|
648
|
+
address_hash[:country_code] = address_data.xpath('CountryCode').text
|
649
|
+
suggested_addresses << address_hash
|
650
|
+
end
|
651
|
+
else
|
652
|
+
return "Address validation failed!"
|
653
|
+
end
|
654
|
+
|
655
|
+
return suggested_addresses
|
656
|
+
end
|
523
657
|
|
524
658
|
def location_from_address_node(address)
|
525
659
|
return nil unless address
|
526
660
|
Address.new(
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
661
|
+
:country => node_text_or_nil(address.elements['CountryCode']),
|
662
|
+
:postal_code => node_text_or_nil(address.elements['PostalCode']),
|
663
|
+
:province => node_text_or_nil(address.elements['StateProvinceCode']),
|
664
|
+
:city => node_text_or_nil(address.elements['City']),
|
665
|
+
:address1 => node_text_or_nil(address.elements['AddressLine1']),
|
666
|
+
:address2 => node_text_or_nil(address.elements['AddressLine2']),
|
667
|
+
:address3 => node_text_or_nil(address.elements['AddressLine3'])
|
668
|
+
)
|
535
669
|
end
|
536
|
-
|
670
|
+
|
537
671
|
def response_success?(xml)
|
538
672
|
xml.xpath('/*/Response/ResponseStatusCode').text == '1'
|
539
673
|
end
|
540
|
-
|
674
|
+
|
541
675
|
def response_message(xml)
|
542
676
|
xml.xpath('/*/Response/Error/ErrorDescription | /*/Response/ResponseStatusDescription').text
|
543
677
|
end
|
544
|
-
|
678
|
+
|
545
679
|
def commit(action, request, test = false)
|
546
680
|
ssl_post("#{test ? TEST_URL : LIVE_URL}/#{RESOURCES[action]}", request)
|
547
681
|
end
|
548
|
-
|
549
|
-
|
682
|
+
|
683
|
+
|
550
684
|
def service_name_for(origin, code)
|
551
685
|
origin = origin.country_code(:alpha2)
|
552
|
-
|
686
|
+
|
553
687
|
name = case origin
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
688
|
+
when "CA" then
|
689
|
+
CANADA_ORIGIN_SERVICES[code]
|
690
|
+
when "MX" then
|
691
|
+
MEXICO_ORIGIN_SERVICES[code]
|
692
|
+
when *EU_COUNTRY_CODES then
|
693
|
+
EU_ORIGIN_SERVICES[code]
|
694
|
+
end
|
695
|
+
|
559
696
|
name ||= OTHER_NON_US_ORIGIN_SERVICES[code] unless name == 'US'
|
560
697
|
name ||= DEFAULT_SERVICES[code]
|
561
698
|
end
|
562
|
-
|
699
|
+
|
563
700
|
end
|
564
701
|
end
|