active_shipping 1.1.3 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5868e8cfd35acb6845aa850d2a8e12999cc0a71b
4
- data.tar.gz: 998bba416cc03877b6da20f141fff0272b5f9543
3
+ metadata.gz: 167a59476f3013546de9f351d4d62b045ce18695
4
+ data.tar.gz: 5a8d406aad880b511e8102fe462a3e344a1660d7
5
5
  SHA512:
6
- metadata.gz: 8ecbdea4269a2eeb54d54e45219dbbddd4518fcc65a88cee70575d0c8c40814ec1dbd99986bc715e6bb10e66ab16d3da3ee534e209b4a94ea0557053f5eeb372
7
- data.tar.gz: 4b27e759684cea1e91286dfc062cc5ab167ae9696053b0b252e8c4b0804d7aee04c5ba2f1bf7cbf6bd3a8bc7d63ddc9e37c4317c58c70e9cfb1f84ffaa0f53ee
6
+ metadata.gz: 0a21b4a2467b1ead50736c57cd9b3da5494090e6f4866e78d9f91a8cbeff1e89ac1dd7ff82187677ee6f897f718cc4fabd48fb7f8e49b998b5e6e7e51f045fab
7
+ data.tar.gz: fce0b6be9e67015c62b6ed7651243ad7e09791ca20502509e29c72138a47c400dfca855f4f6203a5f27796916d09467b2931690f5d6440652913d5b13068c019
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # ActiveShipping CHANGELOG
2
2
 
3
+ ### v1.2.0
4
+
5
+ - Added support for buying labels with FedEx
6
+ - Added support for batched tracking requests with USPS
7
+
8
+ ### v1.1.3
9
+
10
+ - Handle ZIP+4 numbers in USPS tracking
11
+ - Add a field for rate estimate references
12
+ - Add support for UPS mail innovations tracking option
13
+
14
+ ### v1.1.2
15
+
16
+ - Fix finding of error descriptions for USPS tracking
17
+
3
18
  ### v1.1.1
4
19
 
5
20
  - Fix bug with USPS tracking not handling optional fields being absent.
data/README.md CHANGED
@@ -5,6 +5,7 @@ This library interfaces with the web services of various shipping carriers. The
5
5
  - Finding shipping rates
6
6
  - Registering shipments
7
7
  - Tracking shipments
8
+ - Purchasing shipping labels
8
9
 
9
10
  Active Shipping is currently being used and improved in a production environment for [Shopify][]. Development is being done by the Shopify integrations team (<integrations-team@shopify.com>). Discussion is welcome in the [Active Merchant Google Group][discuss].
10
11
 
@@ -36,6 +36,7 @@ require 'active_shipping/tracking_response'
36
36
  require 'active_shipping/delivery_date_estimates_response'
37
37
  require 'active_shipping/shipping_response'
38
38
  require 'active_shipping/label_response'
39
+ require 'active_shipping/label'
39
40
  require 'active_shipping/package'
40
41
  require 'active_shipping/location'
41
42
  require 'active_shipping/rate_estimate'
@@ -68,6 +68,14 @@ module ActiveShipping
68
68
  'station' => 'STATION'
69
69
  }
70
70
 
71
+ SIGNATURE_OPTION_CODES = {
72
+ adult: 'ADULT', # 21 years plus
73
+ direct: 'DIRECT', # A person at the delivery address
74
+ indirect: 'INDIRECT', # A person at the delivery address, or a neighbor, or a signed note for fedex on the door
75
+ none_required: 'NO_SIGNATURE_REQUIRED',
76
+ default_for_service: 'SERVICE_DEFAULT'
77
+ }
78
+
71
79
  PAYMENT_TYPES = {
72
80
  'sender' => 'SENDER',
73
81
  'recipient' => 'RECIPIENT',
@@ -153,10 +161,127 @@ module ActiveShipping
153
161
  parse_tracking_response(xml, options)
154
162
  end
155
163
 
164
+
165
+ # Get Shipping labels
166
+ def create_shipment(origin, destination, packages, options = {})
167
+ options = @options.update(options)
168
+ packages = Array(packages)
169
+ raise Error, "Multiple packages are not supported yet." if packages.length > 1
170
+
171
+ request = build_shipment_request(origin, destination, packages, options)
172
+ logger.debug(request) if logger
173
+
174
+ logger.debug(confirm_response) if logger
175
+
176
+ response = commit(save_request(request), (options[:test] || false))
177
+ parse_ship_response(response)
178
+ end
179
+
156
180
  protected
157
181
 
182
+ def build_shipment_request(origin, destination, packages, options = {})
183
+ imperial = location_uses_imperial(origin)
184
+
185
+ xml_builder = Nokogiri::XML::Builder.new do |xml|
186
+ xml.ProcessShipmentRequest(xmlns: 'http://fedex.com/ws/ship/v13') do
187
+ build_request_header(xml)
188
+ build_version_node(xml, 'ship', 13, 0 ,0)
189
+
190
+ xml.RequestedShipment do
191
+ xml.ShipTimestamp(ship_timestamp(options[:turn_around_time]).iso8601(0))
192
+ xml.DropoffType('REGULAR_PICKUP')
193
+ xml.ServiceType(options[:service_type] || 'FEDEX_GROUND')
194
+ xml.PackagingType('YOUR_PACKAGING')
195
+
196
+ xml.Shipper do
197
+ build_contact_address_nodes(xml, options[:shipper] || origin)
198
+ end
199
+
200
+ xml.Recipient do
201
+ build_contact_address_nodes(xml, destination)
202
+ end
203
+
204
+ xml.Origin do
205
+ build_contact_address_nodes(xml, origin)
206
+ end
207
+
208
+ xml.ShippingChargesPayment do
209
+ xml.PaymentType('SENDER')
210
+ xml.Payor do
211
+ build_shipment_responsible_party_node(xml, options[:shipper] || origin)
212
+ end
213
+ end
214
+
215
+ xml.LabelSpecification do
216
+ xml.LabelFormatType('COMMON2D')
217
+ xml.ImageType('PNG')
218
+ xml.LabelStockType('PAPER_7X4.75')
219
+ end
220
+
221
+ xml.RateRequestTypes('ACCOUNT')
222
+
223
+ xml.PackageCount(packages.size)
224
+ packages.each do |package|
225
+ xml.RequestedPackageLineItems do
226
+ xml.GroupPackageCount(1)
227
+ build_package_weight_node(xml, package, imperial)
228
+ build_package_dimensions_node(xml, package, imperial)
229
+
230
+ # Reference Numbers
231
+ reference_numbers = Array(package.options[:reference_numbers])
232
+ if reference_numbers.size > 0
233
+ xml.CustomerReferences do
234
+ reference_numbers.each do |reference_number_info|
235
+ xml.CustomerReferenceType(reference_number_info[:type] || "CUSTOMER_REFERENCE")
236
+ xml.Value(reference_number_info[:value])
237
+ end
238
+ end
239
+ end
240
+
241
+ xml.SpecialServicesRequested do
242
+ xml.SpecialServiceTypes("SIGNATURE_OPTION")
243
+ xml.SignatureOptionDetail do
244
+ xml.OptionType(SIGNATURE_OPTION_CODES[package.options[:signature_option] || :default_for_service])
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+ xml_builder.to_xml
253
+ end
254
+
255
+ def build_contact_address_nodes(xml, location)
256
+ xml.Contact do
257
+ xml.PersonName(location.name)
258
+ xml.CompanyName(location.company)
259
+ xml.PhoneNumber(location.phone)
260
+ end
261
+ xml.Address do
262
+ xml.StreetLines(location.address1) if location.address1
263
+ xml.StreetLines(location.address2) if location.address2
264
+ xml.City(location.city) if location.city
265
+ xml.StateOrProvinceCode(location.state)
266
+ xml.PostalCode(location.postal_code)
267
+ xml.CountryCode(location.country_code(:alpha2))
268
+ xml.Residential('true') if location.residential?
269
+ end
270
+ end
271
+
272
+ def build_shipment_responsible_party_node(xml, origin)
273
+ xml.ResponsibleParty do
274
+ xml.AccountNumber(@options[:account])
275
+ xml.Contact do
276
+ xml.PersonName(origin.name)
277
+ xml.CompanyName(origin.company)
278
+ xml.PhoneNumber(origin.phone)
279
+ end
280
+ end
281
+ end
282
+
158
283
  def build_rate_request(origin, destination, packages, options = {})
159
- imperial = %w(US LR MM).include?(origin.country_code(:alpha2))
284
+ imperial = location_uses_imperial(origin)
160
285
 
161
286
  xml_builder = Nokogiri::XML::Builder.new do |xml|
162
287
  xml.RateRequest(xmlns: 'http://fedex.com/ws/rate/v13') do
@@ -341,31 +466,35 @@ module ActiveShipping
341
466
  success = response_success?(xml)
342
467
  message = response_message(xml)
343
468
 
344
- rate_estimates = xml.root.css('> RateReplyDetails').map do |rated_shipment|
345
- service_code = rated_shipment.at('ServiceType').text
346
- is_saturday_delivery = rated_shipment.at('AppliedOptions').try(:text) == 'SATURDAY_DELIVERY'
347
- service_type = is_saturday_delivery ? "#{service_code}_SATURDAY_DELIVERY" : service_code
348
-
349
- transit_time = rated_shipment.at('TransitTime').text if service_code == "FEDEX_GROUND"
350
- max_transit_time = rated_shipment.at('MaximumTransitTime').try(:text) if service_code == "FEDEX_GROUND"
351
-
352
- delivery_timestamp = rated_shipment.at('DeliveryTimestamp').try(:text)
353
-
354
- delivery_range = delivery_range_from(transit_time, max_transit_time, delivery_timestamp, options)
355
-
356
- currency = rated_shipment.at('RatedShipmentDetails/ShipmentRateDetail/TotalNetCharge/Currency').text
357
- RateEstimate.new(origin, destination, @@name,
358
- self.class.service_name_for_code(service_type),
359
- :service_code => service_code,
360
- :total_price => rated_shipment.at('RatedShipmentDetails/ShipmentRateDetail/TotalNetCharge/Amount').text.to_f,
361
- :currency => currency,
362
- :packages => packages,
363
- :delivery_range => delivery_range)
364
- end
469
+ if success
470
+ rate_estimates = xml.root.css('> RateReplyDetails').map do |rated_shipment|
471
+ service_code = rated_shipment.at('ServiceType').text
472
+ is_saturday_delivery = rated_shipment.at('AppliedOptions').try(:text) == 'SATURDAY_DELIVERY'
473
+ service_type = is_saturday_delivery ? "#{service_code}_SATURDAY_DELIVERY" : service_code
474
+
475
+ transit_time = rated_shipment.at('TransitTime').text if service_code == "FEDEX_GROUND"
476
+ max_transit_time = rated_shipment.at('MaximumTransitTime').try(:text) if service_code == "FEDEX_GROUND"
477
+
478
+ delivery_timestamp = rated_shipment.at('DeliveryTimestamp').try(:text)
479
+
480
+ delivery_range = delivery_range_from(transit_time, max_transit_time, delivery_timestamp, options)
481
+
482
+ currency = rated_shipment.at('RatedShipmentDetails/ShipmentRateDetail/TotalNetCharge/Currency').text
483
+ RateEstimate.new(origin, destination, @@name,
484
+ self.class.service_name_for_code(service_type),
485
+ :service_code => service_code,
486
+ :total_price => rated_shipment.at('RatedShipmentDetails/ShipmentRateDetail/TotalNetCharge/Amount').text.to_f,
487
+ :currency => currency,
488
+ :packages => packages,
489
+ :delivery_range => delivery_range)
490
+ end
365
491
 
366
- if rate_estimates.empty?
367
- success = false
368
- message = "No shipping rates could be found for the destination address" if message.blank?
492
+ if rate_estimates.empty?
493
+ success = false
494
+ message = "No shipping rates could be found for the destination address" if message.blank?
495
+ end
496
+ else
497
+ rate_estimates = []
369
498
  end
370
499
 
371
500
  RateResponse.new(success, message, Hash.from_xml(response), :rates => rate_estimates, :xml => response, :request => last_request, :log_xml => options[:log_xml])
@@ -383,6 +512,19 @@ module ActiveShipping
383
512
  delivery_range
384
513
  end
385
514
 
515
+ def parse_ship_response(response)
516
+ xml = build_document(response, 'ProcessShipmentReply')
517
+ success = response_success?(xml)
518
+ message = response_message(xml)
519
+
520
+ response_info = Hash.from_xml(response)
521
+ tracking_number = xml.css("CompletedPackageDetails TrackingIds TrackingNumber").text
522
+ base_64_image = xml.css("Label Image").text
523
+
524
+ labels = [Label.new(tracking_number, Base64.decode64(base_64_image))]
525
+ LabelResponse.new(success, message, response_info, {labels: labels})
526
+ end
527
+
386
528
  def business_days_from(date, days)
387
529
  future_date = date
388
530
  count = 0
@@ -577,5 +719,9 @@ module ActiveShipping
577
719
  rescue Nokogiri::XML::SyntaxError => e
578
720
  raise ActiveShipping::ResponseContentError.new(e, xml)
579
721
  end
722
+
723
+ def location_uses_imperial(location)
724
+ %w(US LR MM).include?(location.country_code(:alpha2))
725
+ end
580
726
  end
581
727
  end
@@ -156,39 +156,32 @@ module ActiveShipping
156
156
  packages = Array(packages)
157
157
  access_request = build_access_request
158
158
 
159
- begin
159
+ # STEP 1: Confirm. Validation step, important for verifying price.
160
+ confirm_request = build_shipment_request(origin, destination, packages, options)
161
+ logger.debug(confirm_request) if logger
160
162
 
161
- # STEP 1: Confirm. Validation step, important for verifying price.
162
- confirm_request = build_shipment_request(origin, destination, packages, options)
163
- logger.debug(confirm_request) if logger
163
+ confirm_response = commit(:ship_confirm, save_request(access_request + confirm_request), (options[:test] || false))
164
+ logger.debug(confirm_response) if logger
164
165
 
165
- confirm_response = commit(:ship_confirm, save_request(access_request + confirm_request), (options[:test] || false))
166
- logger.debug(confirm_response) if logger
167
-
168
- # ... now, get the digest, it's needed to get the label. In theory,
169
- # one could make decisions based on the price or some such to avoid
170
- # surprises. This also has *no* error handling yet.
171
- xml = parse_ship_confirm(confirm_response)
172
- success = response_success?(xml)
173
- message = response_message(xml)
174
- raise message unless success
175
- digest = response_digest(xml)
176
-
177
- # STEP 2: Accept. Use shipment digest in first response to get the actual label.
178
- accept_request = build_accept_request(digest, options)
179
- logger.debug(accept_request) if logger
180
-
181
- accept_response = commit(:ship_accept, save_request(access_request + accept_request), (options[:test] || false))
182
- logger.debug(accept_response) if logger
166
+ # ... now, get the digest, it's needed to get the label. In theory,
167
+ # one could make decisions based on the price or some such to avoid
168
+ # surprises. This also has *no* error handling yet.
169
+ xml = parse_ship_confirm(confirm_response)
170
+ success = response_success?(xml)
171
+ message = response_message(xml)
172
+ raise message unless success
173
+ digest = response_digest(xml)
183
174
 
184
- # ...finally, build a map from the response that contains
185
- # the label data and tracking information.
186
- parse_ship_accept(accept_response)
175
+ # STEP 2: Accept. Use shipment digest in first response to get the actual label.
176
+ accept_request = build_accept_request(digest, options)
177
+ logger.debug(accept_request) if logger
187
178
 
188
- rescue RuntimeError => e
189
- raise "Could not obtain shipping label. #{e.message}."
179
+ accept_response = commit(:ship_accept, save_request(access_request + accept_request), (options[:test] || false))
180
+ logger.debug(accept_response) if logger
190
181
 
191
- end
182
+ # ...finally, build a map from the response that contains
183
+ # the label data and tracking information.
184
+ parse_ship_accept(accept_response)
192
185
  end
193
186
 
194
187
  def get_delivery_date_estimates(origin, destination, packages, pickup_date=Date.current, options = {})
@@ -855,7 +848,14 @@ module ActiveShipping
855
848
  success = response_success?(xml)
856
849
  message = response_message(xml)
857
850
 
858
- LabelResponse.new(success, message, Hash.from_xml(response).values.first)
851
+ response_info = Hash.from_xml(response).values.first
852
+ packages = response_info["ShipmentResults"]["PackageResults"]
853
+ packages = [packages] if Hash === packages
854
+ labels = packages.map do |package|
855
+ Label.new(package["TrackingNumber"], Base64.decode64(package["LabelImage"]["GraphicImage"]))
856
+ end
857
+
858
+ LabelResponse.new(success, message, response_info, {labels: labels})
859
859
  end
860
860
 
861
861
  def commit(action, request, test = false)
@@ -169,7 +169,14 @@ module ActiveShipping
169
169
  options = @options.update(options)
170
170
  tracking_request = build_tracking_request(tracking_number, options)
171
171
  response = commit(:track, tracking_request, options[:test] || false)
172
- parse_tracking_response(response, options)
172
+ parse_tracking_response(response).first
173
+ end
174
+
175
+ def batch_find_tracking_info(tracking_infos, options = {})
176
+ options = @options.update(options)
177
+ tracking_request = build_tracking_batch_request(tracking_infos, options)
178
+ response = commit(:track, tracking_request, options[:test] || false)
179
+ parse_tracking_response(response, fault_tolerant: true)
173
180
  end
174
181
 
175
182
  def self.size_code_for(package)
@@ -230,7 +237,7 @@ module ActiveShipping
230
237
  if prefix = ONLY_PREFIX_EVENTS.find { |p| description.starts_with?(p) }
231
238
  description = prefix
232
239
  end
233
-
240
+
234
241
  timestamp = "#{node.at('EventDate').text}, #{node.at('EventTime').text}"
235
242
  event_code = node.at('EventCode').text
236
243
  city = node.at('EventCity').try(:text)
@@ -250,16 +257,26 @@ module ActiveShipping
250
257
  protected
251
258
 
252
259
  def build_tracking_request(tracking_number, options = {})
260
+ build_tracking_batch_request([{
261
+ number: tracking_number,
262
+ destination_zip: options[:destination_zip],
263
+ mailing_date: options[:mailing_date]
264
+ }], options)
265
+ end
266
+
267
+ def build_tracking_batch_request(tracking_infos, options)
253
268
  xml_builder = Nokogiri::XML::Builder.new do |xml|
254
- xml.TrackFieldRequest('USERID' => @options[:login]) do
269
+ xml.TrackFieldRequest('USERID' => options[:login]) do
255
270
  xml.Revision { xml.text('1') }
256
- xml.ClientIp { xml.text(@options[:client_ip] || '127.0.0.1') }
257
- xml.SourceId { xml.text(@options[:source_id] || 'active_shipping') }
258
- xml.TrackID('ID' => tracking_number) do
259
- xml.DestinationZipCode { xml.text(strip_zip(@options[:destination_zip]))} if @options[:destination_zip]
260
- if @options[:mailing_date]
261
- formatted_date = @options[:mailing_date].strftime('%Y-%m-%d')
262
- xml.MailingDate { xml.text(formatted_date)}
271
+ xml.ClientIp { xml.text(options[:client_ip] || '127.0.0.1') }
272
+ xml.SourceId { xml.text(options[:source_id] || 'active_shipping') }
273
+ tracking_infos.each do |info|
274
+ xml.TrackID('ID' => info[:number]) do
275
+ xml.DestinationZipCode { xml.text(strip_zip(info[:destination_zip]))} if info[:destination_zip]
276
+ if info[:mailing_date]
277
+ formatted_date = info[:mailing_date].strftime('%Y-%m-%d')
278
+ xml.MailingDate { xml.text(formatted_date)}
279
+ end
263
280
  end
264
281
  end
265
282
  end
@@ -536,21 +553,44 @@ module ActiveShipping
536
553
  package.inches(:length) + package.inches(:width) + package.inches(:height)))
537
554
  end
538
555
 
539
- def parse_tracking_response(response, options)
540
- actual_delivery_date, status = nil
556
+ def parse_tracking_response(response, options = {})
541
557
  xml = Nokogiri.XML(response)
542
558
 
543
- success = response_success?(xml)
544
- message = response_message(xml)
559
+ if has_error?(xml)
560
+ message = error_description_node(xml).text
561
+ # actually raises instead of returning by nature of TrackingResponse#initialize
562
+ return TrackingResponse.new(false, message, Hash.from_xml(response),
563
+ carrier: @@name, xml: response, request: last_request)
564
+ end
565
+
566
+ # Responses are always returned in the order originally given.
567
+ if options[:fault_tolerant]
568
+ xml.root.xpath('TrackInfo').map do |info|
569
+ # Don't let one failure wreck the whole batch
570
+ begin
571
+ parse_tracking_info(response, info)
572
+ rescue ResponseError => e
573
+ e.response
574
+ end
575
+ end
576
+ else
577
+ xml.root.xpath('TrackInfo').map { |info| parse_tracking_info(response, info) }
578
+ end
579
+ end
580
+
581
+
582
+ def parse_tracking_info(response, node)
583
+ success = !has_error?(node)
584
+ message = response_message(node)
545
585
 
546
586
  if success
547
587
  destination = nil
548
588
  shipment_events = []
549
- tracking_details = xml.root.xpath('TrackInfo/TrackDetail')
550
- tracking_details << xml.root.at('TrackInfo/TrackSummary')
589
+ tracking_details = node.xpath('TrackDetail')
590
+ tracking_details << node.at('TrackSummary')
551
591
 
552
- tracking_number = xml.root.at('TrackInfo').attributes['ID'].value
553
- prediction_node = xml.root.at('PredictedDeliveryDate') || xml.root.at('ExpectedDeliveryDate')
592
+ tracking_number = node.attributes['ID'].value
593
+ prediction_node = node.at('PredictedDeliveryDate') || node.at('ExpectedDeliveryDate')
554
594
  scheduled_delivery = prediction_node ? Time.parse(prediction_node.text) : nil
555
595
 
556
596
  tracking_details.each do |event|
@@ -582,30 +622,16 @@ module ActiveShipping
582
622
  )
583
623
  end
584
624
 
585
- def track_summary_node(document)
586
- document.root.xpath('TrackInfo/StatusSummary')
587
- end
588
-
589
- def error_description_node(document)
590
- document.xpath('//Error/Description')
591
- end
592
-
593
- def response_status_node(document)
594
- summary = track_summary_node(document)
595
- return summary unless summary.empty?
596
- error_description_node(document)
597
- end
598
-
599
- def has_error?(document)
600
- !document.at('Error').nil?
625
+ def error_description_node(node)
626
+ node.xpath('//Error/Description')
601
627
  end
602
628
 
603
- def tracking_info_error?(document)
604
- !document.root.at('TrackInfo/Error').nil?
629
+ def response_status_node(node)
630
+ node.at('StatusSummary') || error_description_node(node)
605
631
  end
606
632
 
607
- def response_success?(document)
608
- !(has_error?(document) || tracking_info_error?(document))
633
+ def has_error?(node)
634
+ node.xpath('Error').length > 0
609
635
  end
610
636
 
611
637
  def response_message(document)
@@ -0,0 +1,10 @@
1
+ module ActiveShipping
2
+ class Label
3
+ attr_reader :tracking_number, :img_data
4
+
5
+ def initialize(tracking_number, img_data)
6
+ @tracking_number = tracking_number
7
+ @img_data = img_data
8
+ end
9
+ end
10
+ end
@@ -1,23 +1,10 @@
1
1
  module ActiveShipping
2
-
3
- # This is UPS specific for now; the hash is not at all generic
4
- # or common between carriers.
5
2
  class LabelResponse < Response
6
- attr :params # maybe?
3
+ attr_reader :labels
7
4
 
8
5
  def initialize(success, message, params = {}, options = {})
9
- @params = params
6
+ @labels = options[:labels]
10
7
  super
11
8
  end
12
-
13
- def labels
14
- return @labels if @labels
15
- packages = params["ShipmentResults"]["PackageResults"]
16
- packages = [packages] if Hash === packages
17
- @labels = packages.map do |package|
18
- { :tracking_number => package["TrackingNumber"],
19
- :image => package["LabelImage"] }
20
- end
21
- end
22
9
  end
23
10
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveShipping
2
- VERSION = "1.1.3"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <v13:ProcessShipmentReply xmlns:v13="http://fedex.com/ws/ship/v13"><v13:HighestSeverity>SUCCESS</v13:HighestSeverity><v13:Notifications><v13:Severity>SUCCESS</v13:Severity><v13:Source>ship</v13:Source><v13:Code>0000</v13:Code><v13:Message>Success</v13:Message><v13:LocalizedMessage>Success</v13:LocalizedMessage></v13:Notifications><v13:TransactionDetail> <v13:CustomerTransactionId>ActiveShipping</v13:CustomerTransactionId> </v13:TransactionDetail><v13:Version> <v13:ServiceId>ship</v13:ServiceId> <v13:Major>13</v13:Major> <v13:Intermediate>0</v13:Intermediate> <v13:Minor>0</v13:Minor> </v13:Version><v13:JobId>2cbe617200571fq127edj036951</v13:JobId><v13:CompletedShipmentDetail><v13:UsDomestic>true</v13:UsDomestic><v13:CarrierCode>FDXG</v13:CarrierCode><v13:PackagingDescription>YOUR_PACKAGING</v13:PackagingDescription><v13:OperationalDetail><v13:OriginLocationNumber>915</v13:OriginLocationNumber><v13:DestinationLocationNumber>100</v13:DestinationLocationNumber><v13:TransitTime>FOUR_DAYS</v13:TransitTime><v13:IneligibleForMoneyBackGuarantee>false</v13:IneligibleForMoneyBackGuarantee><v13:DeliveryEligibilities>SATURDAY_DELIVERY</v13:DeliveryEligibilities><v13:ServiceCode>92</v13:ServiceCode></v13:OperationalDetail><v13:ShipmentRating><v13:ActualRateType>PAYOR_ACCOUNT_PACKAGE</v13:ActualRateType><v13:ShipmentRateDetails><v13:RateType>PAYOR_ACCOUNT_PACKAGE</v13:RateType><v13:RateZone>8</v13:RateZone><v13:RatedWeightMethod>ACTUAL</v13:RatedWeightMethod><v13:DimDivisor>0</v13:DimDivisor><v13:FuelSurchargePercent>4.5</v13:FuelSurchargePercent><v13:TotalBillingWeight><v13:Units>LB</v13:Units><v13:Value>8.0</v13:Value></v13:TotalBillingWeight><v13:TotalBaseCharge><v13:Currency>USD</v13:Currency><v13:Amount>12.79</v13:Amount></v13:TotalBaseCharge><v13:TotalFreightDiscounts><v13:Currency>USD</v13:Currency><v13:Amount>0.0</v13:Amount></v13:TotalFreightDiscounts><v13:TotalNetFreight><v13:Currency>USD</v13:Currency><v13:Amount>12.79</v13:Amount></v13:TotalNetFreight><v13:TotalSurcharges><v13:Currency>USD</v13:Currency><v13:Amount>0.58</v13:Amount></v13:TotalSurcharges><v13:TotalNetFedExCharge><v13:Currency>USD</v13:Currency><v13:Amount>13.37</v13:Amount></v13:TotalNetFedExCharge><v13:TotalTaxes><v13:Currency>USD</v13:Currency><v13:Amount>0.0</v13:Amount></v13:TotalTaxes><v13:TotalNetCharge><v13:Currency>USD</v13:Currency><v13:Amount>13.37</v13:Amount></v13:TotalNetCharge><v13:TotalRebates><v13:Currency>USD</v13:Currency><v13:Amount>0.0</v13:Amount></v13:TotalRebates><v13:Surcharges><v13:SurchargeType>FUEL</v13:SurchargeType><v13:Level>PACKAGE</v13:Level><v13:Description>FedEx Ground Fuel</v13:Description><v13:Amount><v13:Currency>USD</v13:Currency><v13:Amount>0.58</v13:Amount></v13:Amount></v13:Surcharges></v13:ShipmentRateDetails></v13:ShipmentRating><v13:CompletedPackageDetails><v13:SequenceNumber>1</v13:SequenceNumber><v13:TrackingIds><v13:TrackingIdType>FEDEX</v13:TrackingIdType><v13:TrackingNumber>794637052920</v13:TrackingNumber></v13:TrackingIds><v13:GroupNumber>0</v13:GroupNumber><v13:PackageRating><v13:ActualRateType>PAYOR_ACCOUNT_PACKAGE</v13:ActualRateType><v13:PackageRateDetails><v13:RateType>PAYOR_ACCOUNT_PACKAGE</v13:RateType><v13:RatedWeightMethod>ACTUAL</v13:RatedWeightMethod><v13:BillingWeight><v13:Units>LB</v13:Units><v13:Value>8.0</v13:Value></v13:BillingWeight><v13:BaseCharge><v13:Currency>USD</v13:Currency><v13:Amount>12.79</v13:Amount></v13:BaseCharge><v13:TotalFreightDiscounts><v13:Currency>USD</v13:Currency><v13:Amount>0.0</v13:Amount></v13:TotalFreightDiscounts><v13:NetFreight><v13:Currency>USD</v13:Currency><v13:Amount>12.79</v13:Amount></v13:NetFreight><v13:TotalSurcharges><v13:Currency>USD</v13:Currency><v13:Amount>0.58</v13:Amount></v13:TotalSurcharges><v13:NetFedExCharge><v13:Currency>USD</v13:Currency><v13:Amount>13.37</v13:Amount></v13:NetFedExCharge><v13:TotalTaxes><v13:Currency>USD</v13:Currency><v13:Amount>0.0</v13:Amount></v13:TotalTaxes><v13:NetCharge><v13:Currency>USD</v13:Currency><v13:Amount>13.37</v13:Amount></v13:NetCharge><v13:TotalRebates><v13:Currency>USD</v13:Currency><v13:Amount>0.0</v13:Amount></v13:TotalRebates><v13:Surcharges><v13:SurchargeType>FUEL</v13:SurchargeType><v13:Level>PACKAGE</v13:Level><v13:Description>FedEx Ground Fuel</v13:Description><v13:Amount><v13:Currency>USD</v13:Currency><v13:Amount>0.58</v13:Amount></v13:Amount></v13:Surcharges></v13:PackageRateDetails></v13:PackageRating><v13:OperationalDetail><v13:OperationalInstructions><v13:Number>2</v13:Number><v13:Content>TRK#</v13:Content></v13:OperationalInstructions><v13:OperationalInstructions><v13:Number>7</v13:Number><v13:Content>9622001900008000296000794637052920</v13:Content></v13:OperationalInstructions><v13:OperationalInstructions><v13:Number>8</v13:Number><v13:Content>537J1/25E2/EE4B</v13:Content></v13:OperationalInstructions><v13:OperationalInstructions><v13:Number>10</v13:Number><v13:Content>7946 3705 2920</v13:Content></v13:OperationalInstructions><v13:OperationalInstructions><v13:Number>15</v13:Number><v13:Content>10017</v13:Content></v13:OperationalInstructions><v13:OperationalInstructions><v13:Number>18</v13:Number><v13:Content>9622 0019 0 (000 800 0296) 0 00 7946 3705 2920</v13:Content></v13:OperationalInstructions><v13:Barcodes><v13:BinaryBarcodes><v13:Type>COMMON_2D</v13:Type><v13:Value>Wyk+HjAxHTAyMTAwMTcdODQwHTAxOR03OTQ2MzcwNTI5MjAdRkRFRx0wMDA4MDAwMjk2HTEyMB0dMS8xHTcuNTBMQh1OHTc4MCAzcmQgQXZlbnVlHU5ldyBZb3JrHU5ZHUJvYiBCb2JzZW4eMDYdMTBaR0QwMDcdMTJaMTIzMTIzMTIzNB0xNFpTdWl0ZSAgMjYwMR0yMFocHTMxWjk2MjIwMDE5MDAwMDgwMDAyOTYwMDA3OTQ2MzcwNTI5MjAdMzRaMDEdHgQ=</v13:Value></v13:BinaryBarcodes><v13:StringBarcodes><v13:Type>FEDEX_1D</v13:Type><v13:Value>9622001900008000296000794637052920</v13:Value></v13:StringBarcodes></v13:Barcodes><v13:GroundServiceCode>019</v13:GroundServiceCode></v13:OperationalDetail><v13:Label><v13:Type>OUTBOUND_LABEL</v13:Type><v13:ShippingDocumentDisposition>RETURNED</v13:ShippingDocumentDisposition><v13:ImageType>PNG</v13:ImageType><v13:Resolution>200</v13:Resolution><v13:CopiesToPrint>1</v13:CopiesToPrint><v13:Parts><v13:DocumentPartSequenceNumber>1</v13:DocumentPartSequenceNumber><v13:Image>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</v13:Image></v13:Parts></v13:Label><v13:SignatureOption>SERVICE_DEFAULT</v13:SignatureOption></v13:CompletedPackageDetails></v13:CompletedShipmentDetail></v13:ProcessShipmentReply>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0"?>
2
+ <TrackFieldRequest USERID="login">
3
+ <Revision>1</Revision>
4
+ <ClientIp>127.0.0.1</ClientIp>
5
+ <SourceId>active_shipping</SourceId>
6
+ <TrackID ID="9102901000462189604217">
7
+ <DestinationZipCode>12345</DestinationZipCode>
8
+ <MailingDate>2010-01-30</MailingDate>
9
+ </TrackID>
10
+ <TrackID ID="5555555555555555555555"/>
11
+ <TrackID ID="9405510ee200828613653750"/>
12
+ </TrackFieldRequest>
@@ -0,0 +1,223 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <TrackResponse>
3
+ <TrackInfo ID="9102901000462189604217">
4
+ <Class>First-Class Package Service</Class>
5
+ <ClassOfMailCode>FC</ClassOfMailCode>
6
+ <DestinationCity>HANNA CITY</DestinationCity>
7
+ <DestinationState>IL</DestinationState>
8
+ <DestinationZip>61536</DestinationZip>
9
+ <EmailEnabled>true</EmailEnabled>
10
+ <ExpectedDeliveryDate>April 28, 2015</ExpectedDeliveryDate>
11
+ <KahalaIndicator>false</KahalaIndicator>
12
+ <MailTypeCode>DM</MailTypeCode>
13
+ <MPDATE>2015-04-24 02:08:04.000000</MPDATE>
14
+ <MPSUFFIX>495865558</MPSUFFIX>
15
+ <OriginCity>ARGYLE</OriginCity>
16
+ <OriginState>TX</OriginState>
17
+ <OriginZip>76226</OriginZip>
18
+ <PodEnabled>false</PodEnabled>
19
+ <PredictedDeliveryDate>April 28, 2015</PredictedDeliveryDate>
20
+ <RestoreEnabled>false</RestoreEnabled>
21
+ <RramEnabled>false</RramEnabled>
22
+ <RreEnabled>false</RreEnabled>
23
+ <Service>USPS Tracking&lt;SUP&gt;&amp;#153;&lt;/SUP&gt;</Service>
24
+ <ServiceTypeCode>001</ServiceTypeCode>
25
+ <Status>Delivered, In/At Mailbox</Status>
26
+ <StatusCategory>Delivered</StatusCategory>
27
+ <StatusSummary>Your item was delivered in or at the mailbox at 9:01 am on April 28, 2015 in HANNA CITY, IL 61536.</StatusSummary>
28
+ <TABLECODE>T</TABLECODE>
29
+ <TrackSummary>
30
+ <EventTime>9:01 am</EventTime>
31
+ <EventDate>April 28, 2015</EventDate>
32
+ <Event>Delivered, In/At Mailbox</Event>
33
+ <EventCity>HANNA CITY</EventCity>
34
+ <EventState>IL</EventState>
35
+ <EventZIPCode>61536</EventZIPCode>
36
+ <EventCountry/>
37
+ <FirmName/>
38
+ <Name/>
39
+ <AuthorizedAgent>false</AuthorizedAgent>
40
+ <EventCode>01</EventCode>
41
+ <DeliveryAttributeCode>01</DeliveryAttributeCode>
42
+ </TrackSummary>
43
+ <TrackDetail>
44
+ <EventTime>8:29 am</EventTime>
45
+ <EventDate>April 28, 2015</EventDate>
46
+ <Event>Out for Delivery</Event>
47
+ <EventCity>HANNA CITY</EventCity>
48
+ <EventState>IL</EventState>
49
+ <EventZIPCode>61536</EventZIPCode>
50
+ <EventCountry/>
51
+ <FirmName/>
52
+ <Name/>
53
+ <AuthorizedAgent>false</AuthorizedAgent>
54
+ <EventCode>OF</EventCode>
55
+ </TrackDetail>
56
+ <TrackDetail>
57
+ <EventTime>8:19 am</EventTime>
58
+ <EventDate>April 28, 2015</EventDate>
59
+ <Event>Sorting Complete</Event>
60
+ <EventCity>HANNA CITY</EventCity>
61
+ <EventState>IL</EventState>
62
+ <EventZIPCode>61536</EventZIPCode>
63
+ <EventCountry/>
64
+ <FirmName/>
65
+ <Name/>
66
+ <AuthorizedAgent>false</AuthorizedAgent>
67
+ <EventCode>PC</EventCode>
68
+ </TrackDetail>
69
+ <TrackDetail>
70
+ <EventTime>7:03 am</EventTime>
71
+ <EventDate>April 28, 2015</EventDate>
72
+ <Event>Arrived at Post Office</Event>
73
+ <EventCity>HANNA CITY</EventCity>
74
+ <EventState>IL</EventState>
75
+ <EventZIPCode>61536</EventZIPCode>
76
+ <EventCountry/>
77
+ <FirmName/>
78
+ <Name/>
79
+ <AuthorizedAgent>false</AuthorizedAgent>
80
+ <EventCode>07</EventCode>
81
+ </TrackDetail>
82
+ <TrackDetail>
83
+ <EventTime>4:05 am</EventTime>
84
+ <EventDate>April 28, 2015</EventDate>
85
+ <Event>Departed USPS Facility</Event>
86
+ <EventCity>HAZELWOOD</EventCity>
87
+ <EventState>MO</EventState>
88
+ <EventZIPCode>63042</EventZIPCode>
89
+ <EventCountry/>
90
+ <FirmName/>
91
+ <Name/>
92
+ <AuthorizedAgent>false</AuthorizedAgent>
93
+ <EventCode>EF</EventCode>
94
+ </TrackDetail>
95
+ <TrackDetail>
96
+ <EventTime>4:04 pm</EventTime>
97
+ <EventDate>April 27, 2015</EventDate>
98
+ <Event>Arrived at USPS Facility</Event>
99
+ <EventCity>HAZELWOOD</EventCity>
100
+ <EventState>MO</EventState>
101
+ <EventZIPCode>63042</EventZIPCode>
102
+ <EventCountry/>
103
+ <FirmName/>
104
+ <Name/>
105
+ <AuthorizedAgent>false</AuthorizedAgent>
106
+ <EventCode>10</EventCode>
107
+ </TrackDetail>
108
+ <TrackDetail>
109
+ <EventTime>12:18 am</EventTime>
110
+ <EventDate>April 26, 2015</EventDate>
111
+ <Event>Departed USPS Facility</Event>
112
+ <EventCity>COPPELL</EventCity>
113
+ <EventState>TX</EventState>
114
+ <EventZIPCode>75099</EventZIPCode>
115
+ <EventCountry/>
116
+ <FirmName/>
117
+ <Name/>
118
+ <AuthorizedAgent>false</AuthorizedAgent>
119
+ <EventCode>EF</EventCode>
120
+ </TrackDetail>
121
+ <TrackDetail>
122
+ <EventTime>7:19 pm</EventTime>
123
+ <EventDate>April 25, 2015</EventDate>
124
+ <Event>Arrived at USPS Origin Facility</Event>
125
+ <EventCity>COPPELL</EventCity>
126
+ <EventState>TX</EventState>
127
+ <EventZIPCode>75099</EventZIPCode>
128
+ <EventCountry/>
129
+ <FirmName/>
130
+ <Name/>
131
+ <AuthorizedAgent>false</AuthorizedAgent>
132
+ <EventCode>10</EventCode>
133
+ </TrackDetail>
134
+ <TrackDetail>
135
+ <EventTime>6:04 pm</EventTime>
136
+ <EventDate>April 25, 2015</EventDate>
137
+ <Event>Accepted at USPS Origin Sort Facility</Event>
138
+ <EventCity>ARGYLE</EventCity>
139
+ <EventState>TX</EventState>
140
+ <EventZIPCode>76226</EventZIPCode>
141
+ <EventCountry/>
142
+ <FirmName/>
143
+ <Name/>
144
+ <AuthorizedAgent>false</AuthorizedAgent>
145
+ <EventCode>OA</EventCode>
146
+ </TrackDetail>
147
+ <TrackDetail>
148
+ <EventTime>11:36 pm</EventTime>
149
+ <EventDate>April 23, 2015</EventDate>
150
+ <Event>Shipping Label Created</Event>
151
+ <EventCity>ARGYLE</EventCity>
152
+ <EventState>TX</EventState>
153
+ <EventZIPCode>76226</EventZIPCode>
154
+ <EventCountry/>
155
+ <FirmName/>
156
+ <Name/>
157
+ <AuthorizedAgent>false</AuthorizedAgent>
158
+ <EventCode>GX</EventCode>
159
+ </TrackDetail>
160
+ </TrackInfo>
161
+ <TrackInfo ID="5555555555555555555555">
162
+ <Class>First-Class Package Service</Class>
163
+ <ClassOfMailCode>FC</ClassOfMailCode>
164
+ <DestinationCity>HANNA CITY</DestinationCity>
165
+ <DestinationState>IL</DestinationState>
166
+ <DestinationZip>61536</DestinationZip>
167
+ <EmailEnabled>true</EmailEnabled>
168
+ <ExpectedDeliveryDate>April 28, 2015</ExpectedDeliveryDate>
169
+ <KahalaIndicator>false</KahalaIndicator>
170
+ <MailTypeCode>DM</MailTypeCode>
171
+ <MPDATE>2015-04-24 02:08:04.000000</MPDATE>
172
+ <MPSUFFIX>495865558</MPSUFFIX>
173
+ <OriginCity>ARGYLE</OriginCity>
174
+ <OriginState>TX</OriginState>
175
+ <OriginZip>76226</OriginZip>
176
+ <PodEnabled>false</PodEnabled>
177
+ <PredictedDeliveryDate>April 28, 2015</PredictedDeliveryDate>
178
+ <RestoreEnabled>false</RestoreEnabled>
179
+ <RramEnabled>false</RramEnabled>
180
+ <RreEnabled>false</RreEnabled>
181
+ <Service>USPS Tracking&lt;SUP&gt;&amp;#153;&lt;/SUP&gt;</Service>
182
+ <ServiceTypeCode>001</ServiceTypeCode>
183
+ <Status>Delivered, In/At Mailbox</Status>
184
+ <StatusCategory>Delivered</StatusCategory>
185
+ <StatusSummary>Your item was delivered in or at the mailbox at 9:01 am on April 28, 2015 in HANNA CITY, IL 61536.</StatusSummary>
186
+ <TABLECODE>T</TABLECODE>
187
+ <TrackSummary>
188
+ <EventTime>9:01 am</EventTime>
189
+ <EventDate>April 28, 2015</EventDate>
190
+ <Event>Delivered, In/At Mailbox</Event>
191
+ <EventCity>HANNA CITY</EventCity>
192
+ <EventState>IL</EventState>
193
+ <EventZIPCode>61536</EventZIPCode>
194
+ <EventCountry/>
195
+ <FirmName/>
196
+ <Name/>
197
+ <AuthorizedAgent>false</AuthorizedAgent>
198
+ <EventCode>01</EventCode>
199
+ <DeliveryAttributeCode>01</DeliveryAttributeCode>
200
+ </TrackSummary>
201
+ <TrackDetail>
202
+ <EventTime>11:36 pm</EventTime>
203
+ <EventDate>April 23, 2015</EventDate>
204
+ <Event>Shipping Label Created</Event>
205
+ <EventCity>ARGYLE</EventCity>
206
+ <EventState>TX</EventState>
207
+ <EventZIPCode>76226</EventZIPCode>
208
+ <EventCountry/>
209
+ <FirmName/>
210
+ <Name/>
211
+ <AuthorizedAgent>false</AuthorizedAgent>
212
+ <EventCode>GX</EventCode>
213
+ </TrackDetail>
214
+ </TrackInfo>
215
+ <TrackInfo ID="9405510ee200828613653750">
216
+ <Error>
217
+ <Number>-2147219302</Number>
218
+ <Description>The Postal Service could not locate the tracking information for your request. Please verify your tracking number and try again later.</Description>
219
+ <HelpFile/>
220
+ <HelpContext/>
221
+ </Error>
222
+ </TrackInfo>
223
+ </TrackResponse>
@@ -297,4 +297,68 @@ class RemoteFedExTest < Minitest::Test
297
297
  @carrier.find_tracking_info('abc')
298
298
  end
299
299
  end
300
+
301
+ def test_cant_obtain_multiple_shipping_labels
302
+ assert_raises(ActiveShipping::Error,"Multiple packages are not supported yet.") do
303
+ @carrier.create_shipment(
304
+ location_fixtures[:beverly_hills_with_name],
305
+ location_fixtures[:new_york_with_name],
306
+ [package_fixtures[:wii],package_fixtures[:wii]],
307
+ :test => true,
308
+ :reference_number => {
309
+ :value => "FOO-123",
310
+ :code => "PO"
311
+ }
312
+ )
313
+ end
314
+ end
315
+
316
+ def test_obtain_shipping_label_with_single_element_array
317
+ response = @carrier.create_shipment(
318
+ location_fixtures[:beverly_hills_with_name],
319
+ location_fixtures[:new_york_with_name],
320
+ [package_fixtures[:wii]],
321
+ :test => true,
322
+ :reference_number => {
323
+ :value => "FOO-123",
324
+ :code => "PO"
325
+ }
326
+ )
327
+
328
+ assert response.success?
329
+ refute_empty response.labels
330
+ refute_empty response.labels.first.img_data
331
+ end
332
+
333
+ def test_obtain_shipping_label
334
+ response = @carrier.create_shipment(
335
+ location_fixtures[:beverly_hills_with_name],
336
+ location_fixtures[:new_york_with_name],
337
+ package_fixtures[:wii],
338
+ :test => true,
339
+ :reference_number => {
340
+ :value => "FOO-123",
341
+ :code => "PO"
342
+ }
343
+ )
344
+
345
+ assert response.success?
346
+ refute_empty response.labels
347
+ refute_empty response.labels.first.img_data
348
+ end
349
+
350
+ def test_obtain_shipping_label_with_signature_option
351
+ packages = package_fixtures.values_at(:wii)
352
+ packages.each {|p| p.options[:signature_option] = :adult }
353
+
354
+ response = @carrier.create_shipment(
355
+ location_fixtures[:beverly_hills_with_name],
356
+ location_fixtures[:new_york_with_name],
357
+ packages,
358
+ {:test => true}
359
+ )
360
+
361
+ signature_option = response.params["ProcessShipmentReply"]["CompletedShipmentDetail"]["CompletedPackageDetails"]["SignatureOption"]
362
+ assert_equal FedEx::SIGNATURE_OPTION_CODES[:adult], signature_option
363
+ end
300
364
  end
data/test/test_helper.rb CHANGED
@@ -112,6 +112,16 @@ module ActiveShipping::Test
112
112
  :postal_code => 'K1P 1J1',
113
113
  :phone => '1-613-580-2400',
114
114
  :fax => '1-613-580-2495'),
115
+ :beverly_hills_with_name => Location.new(
116
+ :name => 'JP Silvashy',
117
+ :country => 'US',
118
+ :state => 'CA',
119
+ :city => 'Beverly Hills',
120
+ :address1 => '455 N. Rexford Dr.',
121
+ :address2 => '3rd Floor',
122
+ :zip => '90210',
123
+ :phone => '1-310-285-1013',
124
+ :fax => '1-310-275-8159'),
115
125
  :beverly_hills => Location.new(
116
126
  :country => 'US',
117
127
  :state => 'CA',
@@ -425,4 +425,45 @@ class FedExTest < Minitest::Test
425
425
  @carrier.find_tracking_info('abc')
426
426
  end
427
427
  end
428
+
429
+ def test_create_shipment
430
+ confirm_response = xml_fixture('fedex/create_shipment_response')
431
+ @carrier.stubs(:commit).returns(confirm_response)
432
+
433
+ response = @carrier.create_shipment(
434
+ location_fixtures[:beverly_hills],
435
+ location_fixtures[:new_york],
436
+ package_fixtures.values_at(:chocolate_stuff),
437
+ :test => true
438
+ )
439
+
440
+ # These assertions are to check that the xml fixture is extracted properly.
441
+ assert_equal 1, response.labels.count
442
+ assert_equal response.labels.first.tracking_number, "794637052920"
443
+ assert_equal response.labels.first.img_data.size, 8286
444
+ end
445
+
446
+ def test_create_shipment_signature_option
447
+ packages = package_fixtures.values_at(:chocolate_stuff)
448
+ packages.each {|p| p.options[:signature_option] = :indirect }
449
+ result = Nokogiri::XML(@carrier.send(:build_shipment_request,
450
+ location_fixtures[:beverly_hills],
451
+ location_fixtures[:annapolis],
452
+ packages,
453
+ :test => true))
454
+ assert_equal result.search('SpecialServicesRequested/SpecialServiceTypes').text, "SIGNATURE_OPTION"
455
+ assert_equal result.search('SpecialServicesRequested/SignatureOptionDetail').text.strip, "INDIRECT"
456
+ end
457
+
458
+ def test_create_shipment_reference
459
+ packages = package_fixtures.values_at(:wii)
460
+ packages.each {|p| p.options[:reference_numbers] = [{:value => "FOO-123"}] }
461
+
462
+ result = Nokogiri::XML(@carrier.send(:build_shipment_request,
463
+ location_fixtures[:beverly_hills],
464
+ location_fixtures[:annapolis],
465
+ packages,
466
+ :test => true))
467
+ assert_equal result.search('RequestedPackageLineItems/CustomerReferences/Value').text, "FOO-123"
468
+ end
428
469
  end
@@ -266,12 +266,12 @@ class UPSTest < Minitest::Test
266
266
 
267
267
  # These tracking numbers are part of the fixture data. What we're trying
268
268
  # to verify is that the data in the XML is extracted properly.
269
- tracking = response.labels.map { |label| label[:tracking_number] }
269
+ tracking = response.labels.map { |label| label.tracking_number }
270
270
  assert_includes tracking, "1ZA03R691594829862"
271
271
  assert_includes tracking, "1ZA03R691592132475"
272
272
  assert_includes tracking, "1ZA03R691590470881"
273
273
 
274
- pictures = response.labels.map { |label| label[:image] }
274
+ pictures = response.labels.map { |label| label.img_data }
275
275
  refute_includes pictures, nil
276
276
  end
277
277
 
@@ -299,10 +299,10 @@ class UPSTest < Minitest::Test
299
299
 
300
300
  # These tracking numbers are part of the fixture data. What we're trying
301
301
  # to verify is that the data in the XML is extracted properly.
302
- tracking = response.labels.map { |label| label[:tracking_number] }
302
+ tracking = response.labels.map { |label| label.tracking_number }
303
303
  assert_includes tracking, "1ZA03R691591538440"
304
304
 
305
- pictures = response.labels.map { |label| label[:image] }
305
+ pictures = response.labels.map { |label| label.img_data }
306
306
  refute_includes pictures, nil
307
307
  end
308
308
 
@@ -6,7 +6,13 @@ class USPSTest < Minitest::Test
6
6
  def setup
7
7
  @carrier = USPS.new(:login => 'login')
8
8
  @tracking_response = xml_fixture('usps/tracking_response')
9
+ @batch_tracking_response = xml_fixture('usps/tracking_response_batch')
9
10
  @tracking_response_failure = xml_fixture('usps/tracking_response_failure')
11
+ @tracking_infos_array = [
12
+ {number: '9102901000462189604217', :destination_zip => '12345', :mailing_date => Date.new(2010,1,30)},
13
+ {number: '5555555555555555555555'},
14
+ {number: '9405510ee200828613653750'}
15
+ ]
10
16
  end
11
17
 
12
18
  def test_tracking_request_should_create_correct_xml
@@ -159,6 +165,45 @@ class USPSTest < Minitest::Test
159
165
  assert_equal true, response.delivered?
160
166
  end
161
167
 
168
+ def test_tracking_batch_request_should_create_correct_xml
169
+ @carrier.expects(:commit).with(:track, xml_fixture('usps/tracking_request_batch'),false).returns(@batch_tracking_response)
170
+ @carrier.batch_find_tracking_info(@tracking_infos_array)
171
+ end
172
+
173
+ def test_batch_find_tracking_info_should_return_a_tracking_response_array
174
+ @carrier.expects(:commit).returns(@batch_tracking_response)
175
+ responses = @carrier.batch_find_tracking_info(@tracking_infos_array, :test => true)
176
+ assert_equal 3, responses.length
177
+ assert responses.all? { |x| x.instance_of? ActiveShipping::TrackingResponse}
178
+ end
179
+
180
+ def test_batch_find_tracking_info_should_have_correct_data
181
+ @carrier.expects(:commit).returns(@batch_tracking_response)
182
+ responses = @carrier.batch_find_tracking_info(@tracking_infos_array, :test => true)
183
+ response = responses[0]
184
+ assert_equal ["GX", "OA", "10", "EF", "10", "EF", "07", "PC", "OF", "01"], response.shipment_events.map(&:type_code)
185
+ assert_equal Time.parse('April 28, 2015'), response.scheduled_delivery_date
186
+ assert_equal Time.parse('2015-04-28 09:01:00 UTC'), response.actual_delivery_date
187
+ assert_equal '9102901000462189604217', response.tracking_number
188
+ end
189
+
190
+ def test_batch_find_tracking_info_should_tolerate_failure
191
+ @carrier.expects(:commit).returns(@batch_tracking_response)
192
+ responses = @carrier.batch_find_tracking_info(@tracking_infos_array, :test => true)
193
+ response = responses[2]
194
+ refute response.success?
195
+ assert_equal "The Postal Service could not locate the tracking information for your request. Please verify your tracking number and try again later.",
196
+ response.message
197
+ end
198
+
199
+ def test_batch_find_tracking_info_should_handle_not_found_error
200
+ @carrier.expects(:commit).returns(xml_fixture('usps/tracking_response_test_error'))
201
+ e = assert_raises ResponseError do
202
+ @carrier.find_tracking_info(@batch_tracking_response, :test => true)
203
+ end
204
+ assert_equal "This Information has not been included in this Test Server.", e.message
205
+ end
206
+
162
207
  def test_size_codes
163
208
  assert_equal 'REGULAR', USPS.size_code_for(Package.new(2, [1, 12, 1], :units => :imperial))
164
209
  assert_equal 'LARGE', USPS.size_code_for(Package.new(2, [12.1, 1, 1], :units => :imperial))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_shipping
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James MacAulay
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2015-05-27 00:00:00.000000000 Z
14
+ date: 2015-05-29 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: quantified
@@ -185,6 +185,7 @@ files:
185
185
  - lib/active_shipping/delivery_date_estimate.rb
186
186
  - lib/active_shipping/delivery_date_estimates_response.rb
187
187
  - lib/active_shipping/errors.rb
188
+ - lib/active_shipping/label.rb
188
189
  - lib/active_shipping/label_response.rb
189
190
  - lib/active_shipping/location.rb
190
191
  - lib/active_shipping/package.rb
@@ -246,6 +247,7 @@ files:
246
247
  - test/fixtures/xml/correios/clothes_response.xml
247
248
  - test/fixtures/xml/correios/poster_response.xml
248
249
  - test/fixtures/xml/correios/shoes_response.xml
250
+ - test/fixtures/xml/fedex/create_shipment_response.xml
249
251
  - test/fixtures/xml/fedex/freight_rate_request.xml
250
252
  - test/fixtures/xml/fedex/freight_rate_response.xml
251
253
  - test/fixtures/xml/fedex/invalid_fedex_reply.xml
@@ -321,8 +323,10 @@ files:
321
323
  - test/fixtures/xml/usps/first_class_packages_without_mail_type_response.xml
322
324
  - test/fixtures/xml/usps/invalid_xml_tracking_response_error.xml
323
325
  - test/fixtures/xml/usps/tracking_request.xml
326
+ - test/fixtures/xml/usps/tracking_request_batch.xml
324
327
  - test/fixtures/xml/usps/tracking_response.xml
325
328
  - test/fixtures/xml/usps/tracking_response_alt.xml
329
+ - test/fixtures/xml/usps/tracking_response_batch.xml
326
330
  - test/fixtures/xml/usps/tracking_response_failure.xml
327
331
  - test/fixtures/xml/usps/tracking_response_not_available.xml
328
332
  - test/fixtures/xml/usps/tracking_response_test_error.xml
@@ -356,7 +360,6 @@ files:
356
360
  - test/unit/carriers/ups_test.rb
357
361
  - test/unit/carriers/usps_test.rb
358
362
  - test/unit/carriers_test.rb
359
- - test/unit/label_response_test.rb
360
363
  - test/unit/location_test.rb
361
364
  - test/unit/package_test.rb
362
365
  - test/unit/rate_estimate_test.rb
@@ -434,6 +437,7 @@ test_files:
434
437
  - test/fixtures/xml/correios/clothes_response.xml
435
438
  - test/fixtures/xml/correios/poster_response.xml
436
439
  - test/fixtures/xml/correios/shoes_response.xml
440
+ - test/fixtures/xml/fedex/create_shipment_response.xml
437
441
  - test/fixtures/xml/fedex/freight_rate_request.xml
438
442
  - test/fixtures/xml/fedex/freight_rate_response.xml
439
443
  - test/fixtures/xml/fedex/invalid_fedex_reply.xml
@@ -509,8 +513,10 @@ test_files:
509
513
  - test/fixtures/xml/usps/first_class_packages_without_mail_type_response.xml
510
514
  - test/fixtures/xml/usps/invalid_xml_tracking_response_error.xml
511
515
  - test/fixtures/xml/usps/tracking_request.xml
516
+ - test/fixtures/xml/usps/tracking_request_batch.xml
512
517
  - test/fixtures/xml/usps/tracking_response.xml
513
518
  - test/fixtures/xml/usps/tracking_response_alt.xml
519
+ - test/fixtures/xml/usps/tracking_response_batch.xml
514
520
  - test/fixtures/xml/usps/tracking_response_failure.xml
515
521
  - test/fixtures/xml/usps/tracking_response_not_available.xml
516
522
  - test/fixtures/xml/usps/tracking_response_test_error.xml
@@ -544,7 +550,6 @@ test_files:
544
550
  - test/unit/carriers/ups_test.rb
545
551
  - test/unit/carriers/usps_test.rb
546
552
  - test/unit/carriers_test.rb
547
- - test/unit/label_response_test.rb
548
553
  - test/unit/location_test.rb
549
554
  - test/unit/package_test.rb
550
555
  - test/unit/rate_estimate_test.rb
@@ -1,15 +0,0 @@
1
- require 'test_helper'
2
-
3
- class LabelResponseTest < Minitest::Test
4
- include ActiveShipping::Test::Fixtures
5
-
6
- def test_build_label_from_xml_response
7
- str = xml_fixture('ups/shipment_accept_response')
8
- mapping = Hash.from_xml(str).values.first
9
- response = LabelResponse.new(true, nil, mapping)
10
-
11
- assert_equal 1, response.labels.count
12
- assert_equal '1ZA03R691591538440', response.labels.first[:tracking_number]
13
- assert response.labels.first[:image]
14
- end
15
- end