lib/active_shipping/carriers/usps.rb in active_shipping-1.1.3 vs lib/active_shipping/carriers/usps.rb in active_shipping-1.2.0
- old
+ new
@@ -167,13 +167,20 @@
def find_tracking_info(tracking_number, options = {})
options = @options.update(options)
tracking_request = build_tracking_request(tracking_number, options)
response = commit(:track, tracking_request, options[:test] || false)
- parse_tracking_response(response, options)
+ parse_tracking_response(response).first
end
+ def batch_find_tracking_info(tracking_infos, options = {})
+ options = @options.update(options)
+ tracking_request = build_tracking_batch_request(tracking_infos, options)
+ response = commit(:track, tracking_request, options[:test] || false)
+ parse_tracking_response(response, fault_tolerant: true)
+ end
+
def self.size_code_for(package)
if package.inches(:max) <= 12
'REGULAR'
else
'LARGE'
@@ -228,11 +235,11 @@
description = node.at('Event').text.upcase
if prefix = ONLY_PREFIX_EVENTS.find { |p| description.starts_with?(p) }
description = prefix
end
-
+
timestamp = "#{node.at('EventDate').text}, #{node.at('EventTime').text}"
event_code = node.at('EventCode').text
city = node.at('EventCity').try(:text)
state = node.at('EventState').try(:text)
zip_code = node.at('EventZIPCode').try(:text)
@@ -248,20 +255,30 @@
end
protected
def build_tracking_request(tracking_number, options = {})
+ build_tracking_batch_request([{
+ number: tracking_number,
+ destination_zip: options[:destination_zip],
+ mailing_date: options[:mailing_date]
+ }], options)
+ end
+
+ def build_tracking_batch_request(tracking_infos, options)
xml_builder = Nokogiri::XML::Builder.new do |xml|
- xml.TrackFieldRequest('USERID' => @options[:login]) do
+ xml.TrackFieldRequest('USERID' => options[:login]) do
xml.Revision { xml.text('1') }
- xml.ClientIp { xml.text(@options[:client_ip] || '127.0.0.1') }
- xml.SourceId { xml.text(@options[:source_id] || 'active_shipping') }
- xml.TrackID('ID' => tracking_number) do
- xml.DestinationZipCode { xml.text(strip_zip(@options[:destination_zip]))} if @options[:destination_zip]
- if @options[:mailing_date]
- formatted_date = @options[:mailing_date].strftime('%Y-%m-%d')
- xml.MailingDate { xml.text(formatted_date)}
+ xml.ClientIp { xml.text(options[:client_ip] || '127.0.0.1') }
+ xml.SourceId { xml.text(options[:source_id] || 'active_shipping') }
+ tracking_infos.each do |info|
+ xml.TrackID('ID' => info[:number]) do
+ xml.DestinationZipCode { xml.text(strip_zip(info[:destination_zip]))} if info[:destination_zip]
+ if info[:mailing_date]
+ formatted_date = info[:mailing_date].strftime('%Y-%m-%d')
+ xml.MailingDate { xml.text(formatted_date)}
+ end
end
end
end
end
xml_builder.to_xml
@@ -534,25 +551,48 @@
(dimensions[:length_plus_width_plus_height].nil? or
dimensions[:length_plus_width_plus_height].to_f >=
package.inches(:length) + package.inches(:width) + package.inches(:height)))
end
- def parse_tracking_response(response, options)
- actual_delivery_date, status = nil
+ def parse_tracking_response(response, options = {})
xml = Nokogiri.XML(response)
- success = response_success?(xml)
- message = response_message(xml)
+ if has_error?(xml)
+ message = error_description_node(xml).text
+ # actually raises instead of returning by nature of TrackingResponse#initialize
+ return TrackingResponse.new(false, message, Hash.from_xml(response),
+ carrier: @@name, xml: response, request: last_request)
+ end
+ # Responses are always returned in the order originally given.
+ if options[:fault_tolerant]
+ xml.root.xpath('TrackInfo').map do |info|
+ # Don't let one failure wreck the whole batch
+ begin
+ parse_tracking_info(response, info)
+ rescue ResponseError => e
+ e.response
+ end
+ end
+ else
+ xml.root.xpath('TrackInfo').map { |info| parse_tracking_info(response, info) }
+ end
+ end
+
+
+ def parse_tracking_info(response, node)
+ success = !has_error?(node)
+ message = response_message(node)
+
if success
destination = nil
shipment_events = []
- tracking_details = xml.root.xpath('TrackInfo/TrackDetail')
- tracking_details << xml.root.at('TrackInfo/TrackSummary')
+ tracking_details = node.xpath('TrackDetail')
+ tracking_details << node.at('TrackSummary')
- tracking_number = xml.root.at('TrackInfo').attributes['ID'].value
- prediction_node = xml.root.at('PredictedDeliveryDate') || xml.root.at('ExpectedDeliveryDate')
+ tracking_number = node.attributes['ID'].value
+ prediction_node = node.at('PredictedDeliveryDate') || node.at('ExpectedDeliveryDate')
scheduled_delivery = prediction_node ? Time.parse(prediction_node.text) : nil
tracking_details.each do |event|
details = extract_event_details(event)
if details.location
@@ -580,33 +620,19 @@
:actual_delivery_date => actual_delivery_date,
:scheduled_delivery_date => scheduled_delivery
)
end
- def track_summary_node(document)
- document.root.xpath('TrackInfo/StatusSummary')
+ def error_description_node(node)
+ node.xpath('//Error/Description')
end
- def error_description_node(document)
- document.xpath('//Error/Description')
+ def response_status_node(node)
+ node.at('StatusSummary') || error_description_node(node)
end
- def response_status_node(document)
- summary = track_summary_node(document)
- return summary unless summary.empty?
- error_description_node(document)
- end
-
- def has_error?(document)
- !document.at('Error').nil?
- end
-
- def tracking_info_error?(document)
- !document.root.at('TrackInfo/Error').nil?
- end
-
- def response_success?(document)
- !(has_error?(document) || tracking_info_error?(document))
+ def has_error?(node)
+ node.xpath('Error').length > 0
end
def response_message(document)
response_status_node(document).text
end