active_shipping 1.0.0.pre4 → 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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.yardopts +0 -1
- data/CHANGELOG.md +17 -0
- data/CONTRIBUTING.md +2 -2
- data/Gemfile.activesupport32 +1 -0
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/active_shipping.gemspec +2 -2
- data/lib/active_shipping.rb +2 -3
- data/lib/active_shipping/carriers.rb +1 -0
- data/lib/active_shipping/carriers/canada_post_pws.rb +281 -266
- data/lib/active_shipping/carriers/correios.rb +285 -0
- data/lib/active_shipping/carriers/fedex.rb +205 -199
- data/lib/active_shipping/carriers/stamps.rb +218 -219
- data/lib/active_shipping/carriers/ups.rb +287 -48
- data/lib/active_shipping/carriers/usps.rb +3 -3
- data/lib/active_shipping/delivery_date_estimate.rb +21 -0
- data/lib/active_shipping/delivery_date_estimates_response.rb +11 -0
- data/lib/active_shipping/errors.rb +20 -1
- data/lib/active_shipping/location.rb +0 -5
- data/lib/active_shipping/response.rb +0 -15
- data/lib/active_shipping/version.rb +1 -1
- data/test/credentials.yml +11 -3
- data/test/fixtures/xml/correios/book_response.xml +13 -0
- data/test/fixtures/xml/correios/book_response_invalid.xml +13 -0
- data/test/fixtures/xml/correios/clothes_response.xml +43 -0
- data/test/fixtures/xml/correios/poster_response.xml +23 -0
- data/test/fixtures/xml/correios/shoes_response.xml +43 -0
- data/test/fixtures/xml/fedex/tracking_request.xml +13 -11
- data/test/fixtures/xml/fedex/tracking_response_bad_tracking_number.xml +20 -0
- data/test/fixtures/xml/fedex/tracking_response_delivered_at_door.xml +254 -0
- data/test/fixtures/xml/fedex/tracking_response_delivered_at_facility.xml +403 -0
- data/test/fixtures/xml/fedex/tracking_response_delivered_with_signature.xml +269 -0
- data/test/fixtures/xml/fedex/tracking_response_in_transit.xml +127 -0
- data/test/fixtures/xml/fedex/tracking_response_multiple_results.xml +100 -0
- data/test/fixtures/xml/fedex/tracking_response_not_found.xml +52 -0
- data/test/fixtures/xml/fedex/tracking_response_shipment_exception.xml +209 -0
- data/test/fixtures/xml/ups/delivery_dates_response.xml +140 -0
- data/test/fixtures/xml/ups/package_exceeds_maximum_length.xml +12 -0
- data/test/fixtures/xml/ups/rate_single_service.xml +54 -0
- data/test/fixtures/xml/ups/rescheduled_shipment.xml +204 -0
- data/test/fixtures/xml/ups/test_real_home_as_residential_destination_response.xml +290 -1
- data/test/fixtures/xml/usps/delivered_extended_tracking_response.xml +11 -0
- data/test/remote/canada_post_pws_platform_test.rb +35 -22
- data/test/remote/canada_post_pws_test.rb +32 -40
- data/test/remote/correios_test.rb +83 -0
- data/test/remote/fedex_test.rb +95 -13
- data/test/remote/stamps_test.rb +1 -0
- data/test/remote/ups_test.rb +77 -40
- data/test/remote/usps_test.rb +13 -1
- data/test/test_helper.rb +12 -2
- data/test/unit/carriers/canada_post_pws_rating_test.rb +66 -59
- data/test/unit/carriers/canada_post_pws_shipping_test.rb +34 -23
- data/test/unit/carriers/correios_test.rb +244 -0
- data/test/unit/carriers/fedex_test.rb +161 -156
- data/test/unit/carriers/ups_test.rb +193 -1
- data/test/unit/carriers/usps_test.rb +14 -0
- data/test/unit/location_test.rb +0 -10
- metadata +63 -46
- metadata.gz.sig +0 -0
- data/lib/vendor/test_helper.rb +0 -6
- data/lib/vendor/xml_node/README +0 -36
- data/lib/vendor/xml_node/Rakefile +0 -21
- data/lib/vendor/xml_node/benchmark/bench_generation.rb +0 -30
- data/lib/vendor/xml_node/init.rb +0 -1
- data/lib/vendor/xml_node/lib/xml_node.rb +0 -221
- data/lib/vendor/xml_node/test/test_generating.rb +0 -89
- data/lib/vendor/xml_node/test/test_parsing.rb +0 -40
- data/test/fixtures/xml/fedex/tracking_response.xml +0 -151
- data/test/fixtures/xml/fedex/tracking_response_empty_destination.xml +0 -76
- data/test/fixtures/xml/fedex/tracking_response_no_destination.xml +0 -139
- data/test/fixtures/xml/fedex/tracking_response_no_ship_time.xml +0 -150
- data/test/fixtures/xml/fedex/tracking_response_with_estimated_delivery_date.xml +0 -95
- data/test/fixtures/xml/fedex/tracking_response_with_shipper_address.xml +0 -71
@@ -1,12 +1,8 @@
|
|
1
|
-
require 'builder'
|
2
|
-
|
3
1
|
module ActiveShipping
|
4
2
|
# Stamps.com integration for rating, tracking, address validation, and label generation
|
5
3
|
# Integration ID can be requested from Stamps.com
|
6
4
|
|
7
5
|
class Stamps < Carrier
|
8
|
-
self.ssl_version = :SSLv3
|
9
|
-
|
10
6
|
cattr_reader :name
|
11
7
|
@@name = 'Stamps'
|
12
8
|
|
@@ -225,27 +221,27 @@ module ActiveShipping
|
|
225
221
|
end
|
226
222
|
|
227
223
|
def build_header
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
224
|
+
Nokogiri::XML::Builder.new do |xml|
|
225
|
+
xml['soap'].Envelope(
|
226
|
+
'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
|
227
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
228
|
+
'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
|
229
|
+
'xmlns:tns' => 'http://stamps.com/xml/namespace/2014/01/swsim/swsimv34'
|
230
|
+
) do
|
231
|
+
xml['soap'].Body do
|
232
|
+
yield(xml)
|
233
|
+
end
|
238
234
|
end
|
239
|
-
end
|
235
|
+
end.to_xml
|
240
236
|
end
|
241
237
|
|
242
238
|
def build_authenticate_user_request
|
243
239
|
build_header do |xml|
|
244
|
-
xml.
|
245
|
-
xml.
|
246
|
-
xml.
|
247
|
-
xml.
|
248
|
-
xml.
|
240
|
+
xml['tns'].AuthenticateUser do
|
241
|
+
xml['tns'].Credentials do
|
242
|
+
xml['tns'].IntegrationID(@options[:integration_id])
|
243
|
+
xml['tns'].Username(@options[:username])
|
244
|
+
xml['tns'].Password(@options[:password])
|
249
245
|
end
|
250
246
|
end
|
251
247
|
end
|
@@ -253,67 +249,67 @@ module ActiveShipping
|
|
253
249
|
|
254
250
|
def build_get_account_info_request
|
255
251
|
build_header do |xml|
|
256
|
-
xml.
|
257
|
-
xml.
|
252
|
+
xml['tns'].GetAccountInfo do
|
253
|
+
xml['tns'].Authenticator(authenticator)
|
258
254
|
end
|
259
255
|
end
|
260
256
|
end
|
261
257
|
|
262
258
|
def build_purchase_postage_request(purchase_amount, control_total)
|
263
259
|
build_header do |xml|
|
264
|
-
xml.
|
265
|
-
xml.
|
266
|
-
xml.
|
267
|
-
xml.
|
260
|
+
xml['tns'].PurchasePostage do
|
261
|
+
xml['tns'].Authenticator(authenticator)
|
262
|
+
xml['tns'].PurchaseAmount(purchase_amount)
|
263
|
+
xml['tns'].ControlTotal(control_total)
|
268
264
|
end
|
269
265
|
end
|
270
266
|
end
|
271
267
|
|
272
268
|
def build_get_purchase_status(transaction_id)
|
273
269
|
build_header do |xml|
|
274
|
-
xml.
|
275
|
-
xml.
|
276
|
-
xml.
|
270
|
+
xml['tns'].GetPurchaseStatus do
|
271
|
+
xml['tns'].Authenticator(authenticator)
|
272
|
+
xml['tns'].TransactionID(transaction_id)
|
277
273
|
end
|
278
274
|
end
|
279
275
|
end
|
280
276
|
|
281
277
|
def build_cleanse_address_request(address)
|
282
278
|
build_header do |xml|
|
283
|
-
xml.
|
284
|
-
xml.
|
279
|
+
xml['tns'].CleanseAddress do
|
280
|
+
xml['tns'].Authenticator(authenticator)
|
285
281
|
add_address(xml, address)
|
286
282
|
end
|
287
283
|
end
|
288
284
|
end
|
289
285
|
|
290
286
|
def add_address(xml, address, object_type = :Address)
|
291
|
-
xml.
|
292
|
-
xml.
|
293
|
-
xml.
|
294
|
-
xml.
|
295
|
-
xml.
|
296
|
-
xml.
|
297
|
-
xml.
|
287
|
+
xml['tns'].public_send(object_type) do
|
288
|
+
xml['tns'].FullName( address.name) unless address.name.blank?
|
289
|
+
xml['tns'].Company( address.company) unless address.company.blank?
|
290
|
+
xml['tns'].Address1( address.address1)
|
291
|
+
xml['tns'].Address2( address.address2) unless address.address2.blank?
|
292
|
+
xml['tns'].Address3( address.address3) unless address.address3.blank?
|
293
|
+
xml['tns'].City( address.city) unless address.city.blank?
|
298
294
|
if domestic?(address)
|
299
|
-
xml.
|
295
|
+
xml['tns'].State( address.state) unless address.state.blank?
|
300
296
|
|
301
297
|
zip = (address.postal_code || '').match(/^(\d{5})?-?(\d{4})?$/)
|
302
|
-
xml.
|
303
|
-
xml.
|
298
|
+
xml['tns'].ZIPCode( zip[1]) unless zip[1].nil?
|
299
|
+
xml['tns'].ZIPCodeAddOn(zip[2]) unless zip[2].nil?
|
304
300
|
else
|
305
|
-
xml.
|
306
|
-
xml.
|
301
|
+
xml['tns'].Province( address.province) unless address.province.blank?
|
302
|
+
xml['tns'].PostalCode( address.postal_code) unless address.postal_code.blank?
|
307
303
|
end
|
308
|
-
xml.
|
309
|
-
xml.
|
304
|
+
xml['tns'].Country( address.country_code) unless address.country_code.blank?
|
305
|
+
xml['tns'].PhoneNumber( address.phone) unless address.phone.blank?
|
310
306
|
end
|
311
307
|
end
|
312
308
|
|
313
309
|
def build_rate_request(origin, destination, package, options)
|
314
310
|
build_header do |xml|
|
315
|
-
xml.
|
316
|
-
xml.
|
311
|
+
xml['tns'].GetRates do
|
312
|
+
xml['tns'].Authenticator(authenticator)
|
317
313
|
add_rate(xml, origin, destination, package, options)
|
318
314
|
end
|
319
315
|
end
|
@@ -324,21 +320,21 @@ module ActiveShipping
|
|
324
320
|
options[:insured_value] ||= value
|
325
321
|
options[:declared_value] ||= value if international?(destination)
|
326
322
|
|
327
|
-
xml.
|
328
|
-
xml.
|
329
|
-
xml.
|
330
|
-
xml.
|
331
|
-
xml.
|
332
|
-
xml.
|
333
|
-
xml.
|
334
|
-
xml.
|
335
|
-
xml.
|
336
|
-
xml.
|
337
|
-
xml.
|
338
|
-
xml.
|
339
|
-
xml.
|
340
|
-
xml.
|
341
|
-
xml.
|
323
|
+
xml['tns'].Rate do
|
324
|
+
xml['tns'].FromZIPCode( origin.postal_code) unless origin.postal_code.blank?
|
325
|
+
xml['tns'].ToZIPCode( destination.postal_code) unless destination.postal_code.blank?
|
326
|
+
xml['tns'].ToCountry( destination.country_code) unless destination.country_code.blank?
|
327
|
+
xml['tns'].ServiceType( options[:service]) unless options[:service].blank?
|
328
|
+
xml['tns'].PrintLayout( options[:print_layout]) unless options[:print_layout].blank?
|
329
|
+
xml['tns'].WeightOz( [package.ounces, 1].max)
|
330
|
+
xml['tns'].PackageType( options[:package_type] || 'Package')
|
331
|
+
xml['tns'].Length( package.inches(:length)) if package.inches(:length)
|
332
|
+
xml['tns'].Width( package.inches(:width)) if package.inches(:width)
|
333
|
+
xml['tns'].Height( package.inches(:height)) if package.inches(:height)
|
334
|
+
xml['tns'].ShipDate( options[:ship_date] || Date.today)
|
335
|
+
xml['tns'].InsuredValue( options[:insured_value]) unless options[:insured_value].blank?
|
336
|
+
xml['tns'].CODValue( options[:cod_value]) unless options[:cod_value].blank?
|
337
|
+
xml['tns'].DeclaredValue( options[:declared_value]) unless options[:declared_value].blank?
|
342
338
|
|
343
339
|
machinable = if package.options.has_key?(:machinable)
|
344
340
|
package.options[:machinable] ? true : false
|
@@ -346,54 +342,54 @@ module ActiveShipping
|
|
346
342
|
USPS.package_machinable?(package)
|
347
343
|
end
|
348
344
|
|
349
|
-
xml.
|
345
|
+
xml['tns'].NonMachinable( true) unless machinable
|
350
346
|
|
351
|
-
xml.
|
352
|
-
xml.
|
347
|
+
xml['tns'].RectangularShaped(!package.cylinder?)
|
348
|
+
xml['tns'].GEMNotes( options[:gem_notes]) unless options[:gem_notes].blank?
|
353
349
|
|
354
350
|
add_ons = Array(options[:add_ons])
|
355
351
|
unless add_ons.empty?
|
356
|
-
xml.
|
352
|
+
xml['tns'].AddOns do
|
357
353
|
add_ons.each do |add_on|
|
358
|
-
xml.
|
359
|
-
xml.
|
354
|
+
xml['tns'].AddOnV5 do
|
355
|
+
xml['tns'].AddOnType(add_on)
|
360
356
|
end
|
361
357
|
end
|
362
358
|
end
|
363
359
|
end
|
364
360
|
|
365
|
-
xml.
|
361
|
+
xml['tns'].ToState(destination.province) unless destination.province.blank?
|
366
362
|
end
|
367
363
|
end
|
368
364
|
|
369
365
|
def build_create_indicium_request(origin, destination, package, line_items, options)
|
370
366
|
build_header do |xml|
|
371
|
-
xml.
|
372
|
-
xml.
|
373
|
-
xml.
|
367
|
+
xml['tns'].CreateIndicium do
|
368
|
+
xml['tns'].Authenticator( authenticator)
|
369
|
+
xml['tns'].IntegratorTxID( options[:integrator_tx_id] || SecureRandom::uuid)
|
374
370
|
|
375
371
|
add_rate(xml, origin, destination, package, options)
|
376
372
|
add_address(xml, origin, :From)
|
377
373
|
add_address(xml, destination, :To)
|
378
374
|
add_customs(xml, line_items, options) unless options[:content_type].blank?
|
379
375
|
|
380
|
-
xml.
|
381
|
-
xml.
|
382
|
-
xml.
|
383
|
-
xml.
|
384
|
-
xml.
|
376
|
+
xml['tns'].SampleOnly( options[:sample_only]) unless options[:sample_only].blank?
|
377
|
+
xml['tns'].ImageType( options[:image_type]) unless options[:image_type].blank?
|
378
|
+
xml['tns'].EltronPrinterDPIType( options[:label_resolution]) unless options[:label_resolution].blank?
|
379
|
+
xml['tns'].memo( options[:memo]) unless options[:memo].blank?
|
380
|
+
xml['tns'].deliveryNotification( options[:delivery_notification]) unless options[:delivery_notification].blank?
|
385
381
|
|
386
382
|
add_shipment_notification(xml, options) unless options[:email].blank?
|
387
383
|
|
388
|
-
xml.
|
389
|
-
xml.
|
390
|
-
xml.
|
391
|
-
xml.
|
392
|
-
xml.
|
393
|
-
xml.
|
394
|
-
xml.
|
395
|
-
xml.
|
396
|
-
xml.
|
384
|
+
xml['tns'].horizontalOffset( options[:horizontal_offset]) unless options[:horizontal_offest].blank?
|
385
|
+
xml['tns'].verticalOffset( options[:vertical_offset]) unless options[:vertical_offest].blank?
|
386
|
+
xml['tns'].printDensity( options[:print_density]) unless options[:print_density].blank?
|
387
|
+
xml['tns'].rotationDegrees( options[:rotation]) unless options[:rotation].blank?
|
388
|
+
xml['tns'].printMemo( options[:print_memo]) unless options[:print_memo].blank?
|
389
|
+
xml['tns'].printInstructions( options[:print_instructions]) unless options[:print_instructions].blank?
|
390
|
+
xml['tns'].ReturnImageData( options[:return_image_data]) unless options[:return_image_data].blank?
|
391
|
+
xml['tns'].InternalTransactionNumber(options[:internal_transaction_number]) unless options[:internal_transaction_number].blank?
|
392
|
+
xml['tns'].PaperSize( options[:paper_size]) unless options[:paper_size].blank?
|
397
393
|
|
398
394
|
add_label_recipient_info(xml, options) unless options[:label_email_address].blank?
|
399
395
|
end
|
@@ -401,32 +397,32 @@ module ActiveShipping
|
|
401
397
|
end
|
402
398
|
|
403
399
|
def add_shipment_notification(xml, options)
|
404
|
-
xml.
|
405
|
-
xml.
|
406
|
-
xml.
|
407
|
-
xml.
|
408
|
-
xml.
|
400
|
+
xml['tns'].ShipmentNotification do
|
401
|
+
xml['tns'].Email( options[:email])
|
402
|
+
xml['tns'].CCToAccountHolder( options[:cc_to_account_holder]) unless options[:cc_to_account_holder].blank?
|
403
|
+
xml['tns'].UseCompanyNameInFromLine(options[:use_company_name_in_from_name]) unless options[:use_company_name_in_from_line].blank?
|
404
|
+
xml['tns'].UseCompanyNameInSubject( options[:use_company_name_in_subject]) unless options[:use_company_name_in_subject].blank?
|
409
405
|
end
|
410
406
|
end
|
411
407
|
|
412
408
|
def add_customs(xml, line_items, options)
|
413
|
-
xml.
|
414
|
-
xml.
|
415
|
-
xml.
|
416
|
-
xml.
|
417
|
-
xml.
|
418
|
-
xml.
|
419
|
-
xml.
|
420
|
-
|
421
|
-
xml.
|
409
|
+
xml['tns'].Customs do
|
410
|
+
xml['tns'].ContentType( options[:content_type])
|
411
|
+
xml['tns'].Comments( options[:comments]) unless options[:comments].blank?
|
412
|
+
xml['tns'].LicenseNumber( options[:license_number]) unless options[:license_number].blank?
|
413
|
+
xml['tns'].CertificateNumber(options[:certificate_number]) unless options[:certificate_number].blank?
|
414
|
+
xml['tns'].InvoiceNumber( options[:invoice_number]) unless options[:invoice_number].blank?
|
415
|
+
xml['tns'].OtherDescribe( options[:other_describe]) unless options[:other_describe].blank?
|
416
|
+
|
417
|
+
xml['tns'].CustomsLines do
|
422
418
|
line_items.each do |customs_line|
|
423
|
-
xml.
|
424
|
-
xml.
|
425
|
-
xml.
|
426
|
-
xml.
|
427
|
-
xml.
|
428
|
-
xml.
|
429
|
-
xml.
|
419
|
+
xml['tns'].CustomsLine do
|
420
|
+
xml['tns'].Description( customs_line.name)
|
421
|
+
xml['tns'].Quantity( customs_line.quantity)
|
422
|
+
xml['tns'].Value( '%.2f' % (customs_line.value.to_f / 100))
|
423
|
+
xml['tns'].WeightOz( customs_line.ounces) unless customs_line.ounces.blank?
|
424
|
+
xml['tns'].HSTariffNumber( customs_line.hs_code.tr('.', '')[0..5]) unless customs_line.hs_code.blank?
|
425
|
+
xml['tns'].CountryOfOrigin(customs_line.options[:country]) unless customs_line.options[:country].blank?
|
430
426
|
end
|
431
427
|
end
|
432
428
|
end
|
@@ -434,19 +430,19 @@ module ActiveShipping
|
|
434
430
|
end
|
435
431
|
|
436
432
|
def add_label_recipient_info(xml, options)
|
437
|
-
xml.
|
438
|
-
xml.
|
439
|
-
xml.
|
440
|
-
xml.
|
441
|
-
xml.
|
433
|
+
xml['tns'].LabelRecipientInfo do
|
434
|
+
xml['tns'].EmailAddress( options[:label_email_address])
|
435
|
+
xml['tns'].Name( options[:name]) unless options[:name].blank?
|
436
|
+
xml['tns'].Note( options[:note]) unless options[:note].blank?
|
437
|
+
xml['tns'].CopyToOriginator(options[:copy_to_originator]) unless options[:copy_to_originator].blank?
|
442
438
|
end
|
443
439
|
end
|
444
440
|
|
445
441
|
def build_track_shipment_request(shipment_id, options)
|
446
442
|
build_header do |xml|
|
447
|
-
xml.
|
448
|
-
xml.
|
449
|
-
xml.
|
443
|
+
xml['tns'].TrackShipment do
|
444
|
+
xml['tns'].Authenticator(authenticator)
|
445
|
+
xml['tns'].public_send(options[:stamps_tx_id] ? :StampsTxID : :TrackingNumber, shipment_id)
|
450
446
|
end
|
451
447
|
end
|
452
448
|
end
|
@@ -473,10 +469,11 @@ module ActiveShipping
|
|
473
469
|
response_options[:request] = last_request
|
474
470
|
response_options[:test] = test_mode?
|
475
471
|
|
476
|
-
document =
|
477
|
-
child_element = document.
|
472
|
+
document = Nokogiri.XML(xml)
|
473
|
+
child_element = document.at_xpath('/soap:Envelope/soap:Body/*')
|
478
474
|
parse_method = 'parse_' + child_element.name.underscore
|
479
475
|
if respond_to?(parse_method, true)
|
476
|
+
child_element.document.remove_namespaces!
|
480
477
|
send(parse_method, child_element, response_options)
|
481
478
|
else
|
482
479
|
Response.new(false, "Unknown response object #{child_element.name}", response_options)
|
@@ -484,22 +481,22 @@ module ActiveShipping
|
|
484
481
|
end
|
485
482
|
|
486
483
|
def parse_fault(fault, response_options)
|
487
|
-
@authenticator = fault.
|
484
|
+
@authenticator = fault.at('detail/authenticator').text if fault.at('detail/authenticator')
|
488
485
|
|
489
|
-
error_code = if fault.
|
490
|
-
fault.
|
491
|
-
elsif fault.
|
492
|
-
fault.
|
486
|
+
error_code = if fault.at('detail/stamps_exception')
|
487
|
+
fault.at('detail/stamps_exception')['code']
|
488
|
+
elsif fault.at('detail/sdcerror')
|
489
|
+
fault.at('detail/sdcerror')['code']
|
493
490
|
else
|
494
491
|
nil
|
495
492
|
end
|
496
493
|
|
497
494
|
# Renew the Authenticator if it has expired and retry the request
|
498
|
-
if error_code
|
495
|
+
if error_code && error_code.downcase == '002b0202'
|
499
496
|
request = renew_authenticator(last_request)
|
500
497
|
commit(last_swsim_method, request)
|
501
498
|
else
|
502
|
-
raise ResponseError.new(fault.
|
499
|
+
raise ResponseError.new(fault.at('faultstring').text)
|
503
500
|
end
|
504
501
|
end
|
505
502
|
|
@@ -508,37 +505,37 @@ module ActiveShipping
|
|
508
505
|
end
|
509
506
|
|
510
507
|
def parse_authenticator(response)
|
511
|
-
@authenticator = response.
|
508
|
+
@authenticator = response.at_xpath('Authenticator').text
|
512
509
|
end
|
513
510
|
|
514
511
|
def parse_get_account_info_response(account_info_response, response_options)
|
515
512
|
parse_authenticator(account_info_response)
|
516
513
|
|
517
|
-
account_info = account_info_response.
|
518
|
-
response_options[:customer_id] = account_info.
|
519
|
-
response_options[:meter_number] = account_info.
|
520
|
-
response_options[:user_id] = account_info.
|
521
|
-
response_options[:max_postage_balance] = account_info.
|
522
|
-
response_options[:lpo_city] = account_info.
|
523
|
-
response_options[:lpo_state] = account_info.
|
524
|
-
response_options[:lpo_zip] = account_info.
|
525
|
-
|
526
|
-
postage_balance_node = account_info.
|
527
|
-
response_options[:available_postage] = postage_balance_node.
|
528
|
-
response_options[:control_total] = postage_balance_node.
|
529
|
-
|
530
|
-
capabilities_node = account_info.
|
531
|
-
response_options[:can_print_shipping] = capabilities_node.
|
532
|
-
response_options[:can_use_cost_codes] = capabilities_node.
|
533
|
-
response_options[:can_use_hidden_postage] = capabilities_node.
|
534
|
-
response_options[:can_purchase_sdc_insurance] = capabilities_node.
|
535
|
-
response_options[:can_print_memo] = capabilities_node.
|
536
|
-
response_options[:can_print_international] = capabilities_node.
|
537
|
-
response_options[:can_purchase_postage] = capabilities_node.
|
538
|
-
response_options[:can_edit_cost_codes] = capabilities_node.
|
539
|
-
response_options[:must_use_cost_codes] = capabilities_node.
|
540
|
-
response_options[:can_view_online_reports] = capabilities_node.
|
541
|
-
response_options[:per_print_limit] = capabilities_node.
|
514
|
+
account_info = account_info_response.at('AccountInfo')
|
515
|
+
response_options[:customer_id] = account_info.at('CustomerID').text
|
516
|
+
response_options[:meter_number] = account_info.at('MeterNumber').text
|
517
|
+
response_options[:user_id] = account_info.at('UserID').text
|
518
|
+
response_options[:max_postage_balance] = account_info.at('MaxPostageBalance').text
|
519
|
+
response_options[:lpo_city] = account_info.at('LPOCity').text
|
520
|
+
response_options[:lpo_state] = account_info.at('LPOState').text
|
521
|
+
response_options[:lpo_zip] = account_info.at('LPOZip').text
|
522
|
+
|
523
|
+
postage_balance_node = account_info.at('PostageBalance')
|
524
|
+
response_options[:available_postage] = postage_balance_node.at('AvailablePostage').text
|
525
|
+
response_options[:control_total] = postage_balance_node.at('ControlTotal').text
|
526
|
+
|
527
|
+
capabilities_node = account_info.at('Capabilities')
|
528
|
+
response_options[:can_print_shipping] = capabilities_node.at('CanPrintShipping').text == 'true'
|
529
|
+
response_options[:can_use_cost_codes] = capabilities_node.at('CanUseCostCodes').text == 'true'
|
530
|
+
response_options[:can_use_hidden_postage] = capabilities_node.at('CanUseHiddenPostage').text == 'true'
|
531
|
+
response_options[:can_purchase_sdc_insurance] = capabilities_node.at('CanPurchaseSDCInsurance').text == 'true'
|
532
|
+
response_options[:can_print_memo] = capabilities_node.at('CanPrintMemoOnShippingLabel').text == 'true'
|
533
|
+
response_options[:can_print_international] = capabilities_node.at('CanPrintInternational').text == 'true'
|
534
|
+
response_options[:can_purchase_postage] = capabilities_node.at('CanPurchasePostage').text == 'true'
|
535
|
+
response_options[:can_edit_cost_codes] = capabilities_node.at('CanEditCostCodes').text == 'true'
|
536
|
+
response_options[:must_use_cost_codes] = capabilities_node.at('MustUseCostCodes').text == 'true'
|
537
|
+
response_options[:can_view_online_reports] = capabilities_node.at('CanViewOnlineReports').text == 'true'
|
538
|
+
response_options[:per_print_limit] = capabilities_node.at('PerPrintLimit').text
|
542
539
|
|
543
540
|
StampsAccountInfoResponse.new(true, '', {}, response_options)
|
544
541
|
end
|
@@ -546,13 +543,13 @@ module ActiveShipping
|
|
546
543
|
def parse_purchase_postage_response(postage, response_options)
|
547
544
|
parse_authenticator(postage)
|
548
545
|
|
549
|
-
response_options[:purchase_status] = postage.
|
550
|
-
response_options[:rejection_reason] = postage
|
551
|
-
response_options[:transaction_id] = postage
|
546
|
+
response_options[:purchase_status] = postage.at('PurchaseStatus').text
|
547
|
+
response_options[:rejection_reason] = parse_content(postage, 'RejectionReason')
|
548
|
+
response_options[:transaction_id] = parse_content(postage, 'TransactionID')
|
552
549
|
|
553
|
-
balance = postage.
|
554
|
-
response_options[:available_postage] = balance.
|
555
|
-
response_options[:control_total] = balance
|
550
|
+
balance = postage.at('PostageBalance')
|
551
|
+
response_options[:available_postage] = balance.at('AvailablePostage').text
|
552
|
+
response_options[:control_total] = parse_content(balance, 'ControlTotal')
|
556
553
|
|
557
554
|
StampsPurchasePostageResponse.new(true, '', {}, response_options)
|
558
555
|
end
|
@@ -561,19 +558,18 @@ module ActiveShipping
|
|
561
558
|
def parse_cleanse_address_response(cleanse_address, response_options)
|
562
559
|
parse_authenticator(cleanse_address)
|
563
560
|
|
564
|
-
response_options[:address_match] = cleanse_address.
|
565
|
-
response_options[:city_state_zip_ok] = cleanse_address.
|
561
|
+
response_options[:address_match] = cleanse_address.at('AddressMatch').text == 'true'
|
562
|
+
response_options[:city_state_zip_ok] = cleanse_address.at('CityStateZipOK').text == 'true'
|
566
563
|
|
567
|
-
address = cleanse_address.
|
568
|
-
response_options[:cleanse_hash] = address
|
569
|
-
response_options[:override_hash] = address
|
564
|
+
address = cleanse_address.at('Address')
|
565
|
+
response_options[:cleanse_hash] = parse_content(address, 'CleanseHash')
|
566
|
+
response_options[:override_hash] = parse_content(address, 'OverrideHash')
|
570
567
|
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
response_options[:address] = parse_address(address_node, indicator_node, po_box_node)
|
568
|
+
indicator_node = cleanse_address.at('ResidentialDeliveryIndicatorType')
|
569
|
+
po_box_node = cleanse_address.at('IsPOBox')
|
570
|
+
response_options[:address] = parse_address(address, indicator_node, po_box_node)
|
575
571
|
|
576
|
-
candidate_addresses = cleanse_address.
|
572
|
+
candidate_addresses = cleanse_address.xpath('CandidateAddresses/Address')
|
577
573
|
response_options[:candidate_addresses] = candidate_addresses.map do |candidate_address|
|
578
574
|
parse_address(candidate_address)
|
579
575
|
end
|
@@ -584,23 +580,23 @@ module ActiveShipping
|
|
584
580
|
def parse_address(address_node, residential_indicator_node = nil, po_box_node = nil)
|
585
581
|
address = {}
|
586
582
|
|
587
|
-
address[:name] = address_node
|
588
|
-
address[:company] = address_node
|
589
|
-
address[:address1] = address_node
|
590
|
-
address[:address2] = address_node
|
591
|
-
address[:address3] = address_node
|
592
|
-
address[:city] = address_node
|
593
|
-
address[:country] = address_node
|
594
|
-
address[:phone] = address_node
|
583
|
+
address[:name] = parse_content(address_node, 'FullName')
|
584
|
+
address[:company] = parse_content(address_node, 'Company')
|
585
|
+
address[:address1] = parse_content(address_node, 'Address1')
|
586
|
+
address[:address2] = parse_content(address_node, 'Address2')
|
587
|
+
address[:address3] = parse_content(address_node, 'Address3')
|
588
|
+
address[:city] = parse_content(address_node, 'City')
|
589
|
+
address[:country] = parse_content(address_node, 'Country')
|
590
|
+
address[:phone] = parse_content(address_node, 'PhoneNumber')
|
595
591
|
|
596
592
|
if address[:country] == 'US' || address[:country].nil?
|
597
|
-
address[:state] = address_node
|
593
|
+
address[:state] = parse_content(address_node, 'State')
|
598
594
|
|
599
|
-
address[:postal_code] = address_node
|
600
|
-
address[:postal_code] += '-' + address_node
|
595
|
+
address[:postal_code] = parse_content(address_node, 'ZIPCode')
|
596
|
+
address[:postal_code] += '-' + parse_content(address_node, 'ZIPCodeAddOn')
|
601
597
|
else
|
602
|
-
address[:province] = address_node
|
603
|
-
address[:postal_code] = address_node
|
598
|
+
address[:province] = parse_content(address_node, 'Province')
|
599
|
+
address[:postal_code] = parse_content(address_node, 'PostalCode')
|
604
600
|
end
|
605
601
|
|
606
602
|
address[:address_type] = if residential_indicator_node == 'Yes'
|
@@ -619,7 +615,7 @@ module ActiveShipping
|
|
619
615
|
def parse_get_rates_response(get_rates, response_options)
|
620
616
|
parse_authenticator(get_rates)
|
621
617
|
|
622
|
-
response_options[:estimates] = get_rates.
|
618
|
+
response_options[:estimates] = get_rates.xpath('Rates/Rate').map do |rate|
|
623
619
|
parse_rate(rate)
|
624
620
|
end
|
625
621
|
|
@@ -629,33 +625,33 @@ module ActiveShipping
|
|
629
625
|
def parse_rate(rate)
|
630
626
|
rate_options = {}
|
631
627
|
|
632
|
-
origin = Location.new(zip: rate.
|
628
|
+
origin = Location.new(zip: rate.at('FromZIPCode').text)
|
633
629
|
|
634
630
|
location_values = {}
|
635
|
-
location_values[:zip] = rate
|
636
|
-
location_values[:country] = rate
|
631
|
+
location_values[:zip] = parse_content(rate, 'ToZIPCode')
|
632
|
+
location_values[:country] = parse_content(rate, 'ToCountry')
|
637
633
|
destination = Location.new(location_values)
|
638
634
|
|
639
|
-
service_name = SERVICE_TYPES[rate.
|
635
|
+
service_name = SERVICE_TYPES[rate.at('ServiceType').text]
|
640
636
|
|
641
|
-
rate_options[:service_code] = rate.
|
637
|
+
rate_options[:service_code] = rate.at('ServiceType').text
|
642
638
|
rate_options[:currency] = 'USD'
|
643
|
-
rate_options[:shipping_date] = Date.parse(rate.
|
639
|
+
rate_options[:shipping_date] = Date.parse(rate.at('ShipDate').text)
|
644
640
|
|
645
|
-
if delivery_days = rate.
|
646
|
-
delivery_days = delivery_days.
|
641
|
+
if delivery_days = rate.at('DeliverDays')
|
642
|
+
delivery_days = delivery_days.text.split('-')
|
647
643
|
rate_options[:delivery_range] = delivery_days.map { |day| rate_options[:shipping_date] + day.to_i.days }
|
648
644
|
end
|
649
645
|
|
650
|
-
rate_options[:total_price] = rate.
|
646
|
+
rate_options[:total_price] = rate.at('Amount').text
|
651
647
|
|
652
648
|
rate_options[:add_ons] = parse_add_ons(rate)
|
653
649
|
rate_options[:packages] = parse_package(rate)
|
654
650
|
|
655
651
|
add_ons = rate_options[:add_ons]
|
656
|
-
if add_ons['SC-A-INS']
|
652
|
+
if add_ons['SC-A-INS'] && add_ons['SC-A-INS'][:amount]
|
657
653
|
rate_options[:insurance_price] = add_ons['SC-A-INS'][:amount]
|
658
|
-
elsif add_ons['US-A-INS']
|
654
|
+
elsif add_ons['US-A-INS'] && add_ons['US-A-INS'][:amount]
|
659
655
|
rate_options[:insurance_price] = add_ons['US-A-INS'][:amount]
|
660
656
|
end
|
661
657
|
|
@@ -664,14 +660,14 @@ module ActiveShipping
|
|
664
660
|
|
665
661
|
def parse_add_ons(rate)
|
666
662
|
add_ons = {}
|
667
|
-
rate.
|
668
|
-
add_on_type = add_on.
|
663
|
+
rate.xpath('AddOns/AddOnV5').each do |add_on|
|
664
|
+
add_on_type = add_on.at('AddOnType').text
|
669
665
|
|
670
666
|
add_on_details = {}
|
671
|
-
add_on_details[:missing_data] = add_on
|
672
|
-
add_on_details[:amount] = add_on
|
667
|
+
add_on_details[:missing_data] = parse_content(add_on, 'MissingData') if add_on.at('MissingData')
|
668
|
+
add_on_details[:amount] = parse_content(add_on, 'Amount') if add_on.at('Amount')
|
673
669
|
|
674
|
-
prohibited_with = add_on.
|
670
|
+
prohibited_with = add_on.xpath('ProhibitedWithAnyOf/AddOnTypeV5').map(&:text)
|
675
671
|
add_on_details[:prohibited_with] = prohibited_with unless prohibited_with.empty?
|
676
672
|
|
677
673
|
add_ons[add_on_type] = add_on_details
|
@@ -681,17 +677,17 @@ module ActiveShipping
|
|
681
677
|
end
|
682
678
|
|
683
679
|
def parse_package(rate)
|
684
|
-
weight = rate.
|
680
|
+
weight = rate.at('WeightOz').text.to_f
|
685
681
|
|
686
682
|
dimensions = %w(Length Width Height).map do |dim|
|
687
|
-
rate.
|
683
|
+
rate.at(dim) ? rate.at(dim).text.to_f : nil
|
688
684
|
end
|
689
685
|
dimensions.compact!
|
690
686
|
|
691
687
|
package_options = { units: :imperial }
|
692
688
|
|
693
|
-
if value = rate.
|
694
|
-
package_options[:value] = value.
|
689
|
+
if value = rate.at('InsuredValue') || rate.at('DeclaredValue')
|
690
|
+
package_options[:value] = value.text.to_f
|
695
691
|
package_options[:currency] = 'USD'
|
696
692
|
end
|
697
693
|
|
@@ -701,14 +697,14 @@ module ActiveShipping
|
|
701
697
|
def parse_create_indicium_response(indicium, response_options)
|
702
698
|
parse_authenticator(indicium)
|
703
699
|
|
704
|
-
response_options[:shipping_id] = indicium.
|
705
|
-
response_options[:tracking_number] = indicium
|
706
|
-
response_options[:stamps_tx_id] = indicium.
|
707
|
-
response_options[:label_url] = indicium
|
708
|
-
response_options[:available_postage] = indicium.
|
709
|
-
response_options[:control_total] = indicium.
|
710
|
-
response_options[:image_data] = Base64.decode64(indicium.
|
711
|
-
response_options[:rate] = parse_rate(indicium.
|
700
|
+
response_options[:shipping_id] = indicium.at('IntegratorTxID').text
|
701
|
+
response_options[:tracking_number] = parse_content(indicium, 'TrackingNumber')
|
702
|
+
response_options[:stamps_tx_id] = indicium.at('StampsTxID').text
|
703
|
+
response_options[:label_url] = parse_content(indicium, 'URL')
|
704
|
+
response_options[:available_postage] = indicium.at('PostageBalance/AvailablePostage').text
|
705
|
+
response_options[:control_total] = indicium.at('PostageBalance/ControlTotal').text
|
706
|
+
response_options[:image_data] = Base64.decode64(indicium.at('ImageData/base64Binary').text) if indicium.at('ImageData/base64Binary')
|
707
|
+
response_options[:rate] = parse_rate(indicium.at('Rate'))
|
712
708
|
|
713
709
|
StampsShippingResponse.new(true, '', {}, response_options)
|
714
710
|
end
|
@@ -718,27 +714,27 @@ module ActiveShipping
|
|
718
714
|
|
719
715
|
response_options[:carrier] = @@name
|
720
716
|
|
721
|
-
shipment_events = track_shipment.
|
717
|
+
shipment_events = track_shipment.xpath('TrackingEvents/TrackingEvent').map do |event|
|
722
718
|
unless response_options[:status]
|
723
|
-
response_options[:status_code] = event.
|
719
|
+
response_options[:status_code] = event.at('TrackingEventType').text
|
724
720
|
response_options[:status] = response_options[:status_code].underscore.to_sym
|
725
721
|
end
|
726
722
|
|
727
|
-
response_options[:delivery_signature] = event
|
723
|
+
response_options[:delivery_signature] = parse_content(event, 'SignedBy')
|
728
724
|
|
729
|
-
description = event.
|
725
|
+
description = event.at('Event').text
|
730
726
|
|
731
|
-
timestamp = event.
|
727
|
+
timestamp = event.at('Timestamp').text
|
732
728
|
date, time = timestamp.split('T')
|
733
729
|
year, month, day = date.split('-')
|
734
730
|
hour, minute, second = time.split(':')
|
735
731
|
zoneless_time = Time.utc(year, month, day, hour, minute, second)
|
736
732
|
|
737
733
|
location = Location.new(
|
738
|
-
city: event.
|
739
|
-
state: event.
|
740
|
-
zip: event.
|
741
|
-
country: event.
|
734
|
+
city: event.at('City').text,
|
735
|
+
state: event.at('State').text,
|
736
|
+
zip: event.at('Zip').text,
|
737
|
+
country: event.at('Country').text
|
742
738
|
)
|
743
739
|
|
744
740
|
ShipmentEvent.new(description, zoneless_time, location)
|
@@ -749,6 +745,11 @@ module ActiveShipping
|
|
749
745
|
|
750
746
|
TrackingResponse.new(true, '', {}, response_options)
|
751
747
|
end
|
748
|
+
|
749
|
+
def parse_content(node, child)
|
750
|
+
return unless node.at(child) && node.at(child).text != ''
|
751
|
+
node.at(child).text
|
752
|
+
end
|
752
753
|
end
|
753
754
|
|
754
755
|
class StampsAccountInfoResponse < Response
|
@@ -840,8 +841,6 @@ module ActiveShipping
|
|
840
841
|
class StampsShippingResponse < ShippingResponse
|
841
842
|
include ActiveUtils::PostsData
|
842
843
|
|
843
|
-
self.ssl_version = :SSLv3
|
844
|
-
|
845
844
|
attr_reader :rate, :stamps_tx_id, :label_url, :available_postage, :control_total
|
846
845
|
|
847
846
|
def initialize(success, message, params = {}, options = {})
|