active_shipping 1.0.0.pre4 → 1.0.0
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 +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 = {})
|