active_shipping 0.9.15 → 0.10.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.
- data/README.markdown +1 -1
- data/lib/active_shipping.rb +1 -0
- data/lib/active_shipping/shipping/carriers.rb +2 -1
- data/lib/active_shipping/shipping/carriers/canada_post.rb +5 -4
- data/lib/active_shipping/shipping/carriers/canada_post_pws.rb +837 -0
- data/lib/active_shipping/shipping/carriers/fedex.rb +33 -19
- data/lib/active_shipping/shipping/carriers/ups.rb +72 -69
- data/lib/active_shipping/shipping/carriers/ups.rb.orig +456 -0
- data/lib/active_shipping/shipping/carriers/usps.rb +185 -9
- data/lib/active_shipping/shipping/carriers/usps.rb.orig +616 -0
- data/lib/active_shipping/shipping/package.rb +109 -10
- data/lib/active_shipping/shipping/rate_estimate.rb +10 -6
- data/lib/active_shipping/shipping/shipping_response.rb +16 -0
- data/lib/active_shipping/shipping/tracking_response.rb +3 -1
- data/lib/active_shipping/version.rb +1 -1
- metadata +23 -3
| @@ -337,18 +337,22 @@ module ActiveMerchant | |
| 337 337 | 
             
                    message = response_message(xml)
         | 
| 338 338 |  | 
| 339 339 | 
             
                    if success
         | 
| 340 | 
            -
                      tracking_number, origin, destination, status, status_code, status_description = nil
         | 
| 340 | 
            +
                      tracking_number, origin, destination, status, status_code, status_description, delivery_signature = nil
         | 
| 341 341 | 
             
                      shipment_events = []
         | 
| 342 342 |  | 
| 343 343 | 
             
                      tracking_details = root_node.elements['TrackDetails']
         | 
| 344 344 | 
             
                      tracking_number = tracking_details.get_text('TrackingNumber').to_s
         | 
| 345 | 
            -
             | 
| 345 | 
            +
             | 
| 346 346 | 
             
                      status_code = tracking_details.get_text('StatusCode').to_s
         | 
| 347 347 | 
             
                      status_description = tracking_details.get_text('StatusDescription').to_s
         | 
| 348 348 | 
             
                      status = TRACKING_STATUS_CODES[status_code]
         | 
| 349 349 |  | 
| 350 | 
            +
                      if status_code == 'DL' && tracking_details.get_text('SignatureProofOfDeliveryAvailable').to_s == 'true'
         | 
| 351 | 
            +
                        delivery_signature = tracking_details.get_text('DeliverySignatureName').to_s
         | 
| 352 | 
            +
                      end
         | 
| 353 | 
            +
             | 
| 350 354 | 
             
                      origin_node = tracking_details.elements['OriginLocationAddress']
         | 
| 351 | 
            -
             | 
| 355 | 
            +
             | 
| 352 356 | 
             
                      if origin_node
         | 
| 353 357 | 
             
                        origin = Location.new(
         | 
| 354 358 | 
             
                              :country =>     origin_node.get_text('CountryCode').to_s,
         | 
| @@ -357,17 +361,7 @@ module ActiveMerchant | |
| 357 361 | 
             
                        )
         | 
| 358 362 | 
             
                      end
         | 
| 359 363 |  | 
| 360 | 
            -
                       | 
| 361 | 
            -
             | 
| 362 | 
            -
                      if destination_node.nil?
         | 
| 363 | 
            -
                        destination_node = tracking_details.elements['ActualDeliveryAddress']
         | 
| 364 | 
            -
                      end
         | 
| 365 | 
            -
             | 
| 366 | 
            -
                      destination = Location.new(
         | 
| 367 | 
            -
                            :country =>     destination_node.get_text('CountryCode').to_s,
         | 
| 368 | 
            -
                            :province =>    destination_node.get_text('StateOrProvinceCode').to_s,
         | 
| 369 | 
            -
                            :city =>        destination_node.get_text('City').to_s
         | 
| 370 | 
            -
                          )
         | 
| 364 | 
            +
                      destination = extract_destination(tracking_details)
         | 
| 371 365 |  | 
| 372 366 | 
             
                      tracking_details.elements.each('Events') do |event|
         | 
| 373 367 | 
             
                        address  = event.elements['Address']
         | 
| @@ -380,11 +374,10 @@ module ActiveMerchant | |
| 380 374 |  | 
| 381 375 | 
             
                        location = Location.new(:city => city, :state => state, :postal_code => zip_code, :country => country)
         | 
| 382 376 | 
             
                        description = event.get_text('EventDescription').to_s
         | 
| 383 | 
            -
             | 
| 384 | 
            -
                        # | 
| 385 | 
            -
                         | 
| 386 | 
            -
             | 
| 387 | 
            -
                        
         | 
| 377 | 
            +
             | 
| 378 | 
            +
                        time          = Time.parse("#{event.get_text('Timestamp').to_s}")
         | 
| 379 | 
            +
                        zoneless_time = time.utc
         | 
| 380 | 
            +
             | 
| 388 381 | 
             
                        shipment_events << ShipmentEvent.new(description, zoneless_time, location)
         | 
| 389 382 | 
             
                      end
         | 
| 390 383 | 
             
                      shipment_events = shipment_events.sort_by(&:time)
         | 
| @@ -398,6 +391,7 @@ module ActiveMerchant | |
| 398 391 | 
             
                      :status => status,
         | 
| 399 392 | 
             
                      :status_code => status_code,
         | 
| 400 393 | 
             
                      :status_description => status_description,
         | 
| 394 | 
            +
                      :delivery_signature => delivery_signature,
         | 
| 401 395 | 
             
                      :shipment_events => shipment_events,
         | 
| 402 396 | 
             
                      :origin => origin,
         | 
| 403 397 | 
             
                      :destination => destination,
         | 
| @@ -448,6 +442,26 @@ module ActiveMerchant | |
| 448 442 | 
             
                    end
         | 
| 449 443 | 
             
                    results
         | 
| 450 444 | 
             
                  end
         | 
| 445 | 
            +
             | 
| 446 | 
            +
                  def extract_destination(document)
         | 
| 447 | 
            +
                    node = document.elements['DestinationAddress'] || document.elements['ActualDeliveryAddress']
         | 
| 448 | 
            +
             | 
| 449 | 
            +
                    args = if node
         | 
| 450 | 
            +
                      {
         | 
| 451 | 
            +
                        :country => node.get_text('CountryCode').to_s,
         | 
| 452 | 
            +
                        :province => node.get_text('StateOrProvinceCode').to_s,
         | 
| 453 | 
            +
                        :city => node.get_text('City').to_s
         | 
| 454 | 
            +
                      }
         | 
| 455 | 
            +
                    else
         | 
| 456 | 
            +
                      {
         | 
| 457 | 
            +
                        :country => ActiveMerchant::Country.new(:alpha2 => 'ZZ', :name => 'Unknown or Invalid Territory', :alpha3 => 'ZZZ', :numeric => '999'),
         | 
| 458 | 
            +
                        :province => 'unknown',
         | 
| 459 | 
            +
                        :city => 'unknown'
         | 
| 460 | 
            +
                      }
         | 
| 461 | 
            +
                    end
         | 
| 462 | 
            +
             | 
| 463 | 
            +
                    Location.new(args)
         | 
| 464 | 
            +
                  end
         | 
| 451 465 | 
             
                end
         | 
| 452 466 | 
             
              end
         | 
| 453 467 | 
             
            end
         | 
| @@ -4,22 +4,22 @@ module ActiveMerchant | |
| 4 4 | 
             
              module Shipping
         | 
| 5 5 | 
             
                class UPS < Carrier
         | 
| 6 6 | 
             
                  self.retry_safe = true
         | 
| 7 | 
            -
             | 
| 7 | 
            +
             | 
| 8 8 | 
             
                  cattr_accessor :default_options
         | 
| 9 9 | 
             
                  cattr_reader :name
         | 
| 10 10 | 
             
                  @@name = "UPS"
         | 
| 11 | 
            -
             | 
| 11 | 
            +
             | 
| 12 12 | 
             
                  TEST_URL = 'https://wwwcie.ups.com'
         | 
| 13 13 | 
             
                  LIVE_URL = 'https://onlinetools.ups.com'
         | 
| 14 | 
            -
             | 
| 14 | 
            +
             | 
| 15 15 | 
             
                  RESOURCES = {
         | 
| 16 16 | 
             
                    :rates => 'ups.app/xml/Rate',
         | 
| 17 17 | 
             
                    :track => 'ups.app/xml/Track'
         | 
| 18 18 | 
             
                  }
         | 
| 19 | 
            -
             | 
| 19 | 
            +
             | 
| 20 20 | 
             
                  PICKUP_CODES = HashWithIndifferentAccess.new({
         | 
| 21 21 | 
             
                    :daily_pickup => "01",
         | 
| 22 | 
            -
                    :customer_counter => "03", | 
| 22 | 
            +
                    :customer_counter => "03",
         | 
| 23 23 | 
             
                    :one_time_pickup => "06",
         | 
| 24 24 | 
             
                    :on_call_air => "07",
         | 
| 25 25 | 
             
                    :suggested_retail_rates => "11",
         | 
| @@ -29,7 +29,7 @@ module ActiveMerchant | |
| 29 29 |  | 
| 30 30 | 
             
                  CUSTOMER_CLASSIFICATIONS = HashWithIndifferentAccess.new({
         | 
| 31 31 | 
             
                    :wholesale => "01",
         | 
| 32 | 
            -
                    :occasional => "03", | 
| 32 | 
            +
                    :occasional => "03",
         | 
| 33 33 | 
             
                    :retail => "04"
         | 
| 34 34 | 
             
                  })
         | 
| 35 35 |  | 
| @@ -44,7 +44,7 @@ module ActiveMerchant | |
| 44 44 | 
             
                      :occasional
         | 
| 45 45 | 
             
                    end
         | 
| 46 46 | 
             
                  end
         | 
| 47 | 
            -
             | 
| 47 | 
            +
             | 
| 48 48 | 
             
                  DEFAULT_SERVICES = {
         | 
| 49 49 | 
             
                    "01" => "UPS Next Day Air",
         | 
| 50 50 | 
             
                    "02" => "UPS Second Day Air",
         | 
| @@ -64,24 +64,24 @@ module ActiveMerchant | |
| 64 64 | 
             
                    "85" => "UPS Today Express",
         | 
| 65 65 | 
             
                    "86" => "UPS Today Express Saver"
         | 
| 66 66 | 
             
                  }
         | 
| 67 | 
            -
             | 
| 67 | 
            +
             | 
| 68 68 | 
             
                  CANADA_ORIGIN_SERVICES = {
         | 
| 69 69 | 
             
                    "01" => "UPS Express",
         | 
| 70 70 | 
             
                    "02" => "UPS Expedited",
         | 
| 71 71 | 
             
                    "14" => "UPS Express Early A.M."
         | 
| 72 72 | 
             
                  }
         | 
| 73 | 
            -
             | 
| 73 | 
            +
             | 
| 74 74 | 
             
                  MEXICO_ORIGIN_SERVICES = {
         | 
| 75 75 | 
             
                    "07" => "UPS Express",
         | 
| 76 76 | 
             
                    "08" => "UPS Expedited",
         | 
| 77 77 | 
             
                    "54" => "UPS Express Plus"
         | 
| 78 78 | 
             
                  }
         | 
| 79 | 
            -
             | 
| 79 | 
            +
             | 
| 80 80 | 
             
                  EU_ORIGIN_SERVICES = {
         | 
| 81 81 | 
             
                    "07" => "UPS Express",
         | 
| 82 82 | 
             
                    "08" => "UPS Expedited"
         | 
| 83 83 | 
             
                  }
         | 
| 84 | 
            -
             | 
| 84 | 
            +
             | 
| 85 85 | 
             
                  OTHER_NON_US_ORIGIN_SERVICES = {
         | 
| 86 86 | 
             
                    "07" => "UPS Express"
         | 
| 87 87 | 
             
                  }
         | 
| @@ -96,13 +96,13 @@ module ActiveMerchant | |
| 96 96 |  | 
| 97 97 | 
             
                  # From http://en.wikipedia.org/w/index.php?title=European_Union&oldid=174718707 (Current as of November 30, 2007)
         | 
| 98 98 | 
             
                  EU_COUNTRY_CODES = ["GB", "AT", "BE", "BG", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE"]
         | 
| 99 | 
            -
             | 
| 99 | 
            +
             | 
| 100 100 | 
             
                  US_TERRITORIES_TREATED_AS_COUNTRIES = ["AS", "FM", "GU", "MH", "MP", "PW", "PR", "VI"]
         | 
| 101 | 
            -
             | 
| 101 | 
            +
             | 
| 102 102 | 
             
                  def requirements
         | 
| 103 103 | 
             
                    [:key, :login, :password]
         | 
| 104 104 | 
             
                  end
         | 
| 105 | 
            -
             | 
| 105 | 
            +
             | 
| 106 106 | 
             
                  def find_rates(origin, destination, packages, options={})
         | 
| 107 107 | 
             
                    origin, destination = upsified_location(origin), upsified_location(destination)
         | 
| 108 108 | 
             
                    options = @options.merge(options)
         | 
| @@ -112,7 +112,7 @@ module ActiveMerchant | |
| 112 112 | 
             
                    response = commit(:rates, save_request(access_request + rate_request), (options[:test] || false))
         | 
| 113 113 | 
             
                    parse_rate_response(origin, destination, packages, response, options)
         | 
| 114 114 | 
             
                  end
         | 
| 115 | 
            -
             | 
| 115 | 
            +
             | 
| 116 116 | 
             
                  def find_tracking_info(tracking_number, options={})
         | 
| 117 117 | 
             
                    options = @options.update(options)
         | 
| 118 118 | 
             
                    access_request = build_access_request
         | 
| @@ -120,9 +120,9 @@ module ActiveMerchant | |
| 120 120 | 
             
                    response = commit(:track, save_request(access_request + tracking_request), (options[:test] || false))
         | 
| 121 121 | 
             
                    parse_tracking_response(response, options)
         | 
| 122 122 | 
             
                  end
         | 
| 123 | 
            -
             | 
| 123 | 
            +
             | 
| 124 124 | 
             
                  protected
         | 
| 125 | 
            -
             | 
| 125 | 
            +
             | 
| 126 126 | 
             
                  def upsified_location(location)
         | 
| 127 127 | 
             
                    if location.country_code == 'US' && US_TERRITORIES_TREATED_AS_COUNTRIES.include?(location.state)
         | 
| 128 128 | 
             
                      atts = {:country => location.state}
         | 
| @@ -134,7 +134,7 @@ module ActiveMerchant | |
| 134 134 | 
             
                      location
         | 
| 135 135 | 
             
                    end
         | 
| 136 136 | 
             
                  end
         | 
| 137 | 
            -
             | 
| 137 | 
            +
             | 
| 138 138 | 
             
                  def build_access_request
         | 
| 139 139 | 
             
                    xml_request = XmlNode.new('AccessRequest') do |access_request|
         | 
| 140 140 | 
             
                      access_request << XmlNode.new('AccessLicenseNumber', @options[:key])
         | 
| @@ -143,7 +143,7 @@ module ActiveMerchant | |
| 143 143 | 
             
                    end
         | 
| 144 144 | 
             
                    xml_request.to_s
         | 
| 145 145 | 
             
                  end
         | 
| 146 | 
            -
             | 
| 146 | 
            +
             | 
| 147 147 | 
             
                  def build_rate_request(origin, destination, packages, options={})
         | 
| 148 148 | 
             
                    packages = Array(packages)
         | 
| 149 149 | 
             
                    xml_request = XmlNode.new('RatingServiceSelectionRequest') do |root_node|
         | 
| @@ -153,9 +153,9 @@ module ActiveMerchant | |
| 153 153 | 
             
                        # not implemented: 'Rate' RequestOption to specify a single service query
         | 
| 154 154 | 
             
                        # request << XmlNode.new('RequestOption', ((options[:service].nil? or options[:service] == :all) ? 'Shop' : 'Rate'))
         | 
| 155 155 | 
             
                      end
         | 
| 156 | 
            -
             | 
| 156 | 
            +
             | 
| 157 157 | 
             
                      pickup_type = options[:pickup_type] || :daily_pickup
         | 
| 158 | 
            -
             | 
| 158 | 
            +
             | 
| 159 159 | 
             
                      root_node << XmlNode.new('PickupType') do |pickup_type_node|
         | 
| 160 160 | 
             
                        pickup_type_node << XmlNode.new('Code', PICKUP_CODES[pickup_type])
         | 
| 161 161 | 
             
                        # not implemented: PickupType/PickupDetails element
         | 
| @@ -164,7 +164,7 @@ module ActiveMerchant | |
| 164 164 | 
             
                      root_node << XmlNode.new('CustomerClassification') do |cc_node|
         | 
| 165 165 | 
             
                        cc_node << XmlNode.new('Code', CUSTOMER_CLASSIFICATIONS[cc])
         | 
| 166 166 | 
             
                      end
         | 
| 167 | 
            -
             | 
| 167 | 
            +
             | 
| 168 168 | 
             
                      root_node << XmlNode.new('Shipment') do |shipment|
         | 
| 169 169 | 
             
                        # not implemented: Shipment/Description element
         | 
| 170 170 | 
             
                        shipment << build_location_node('Shipper', (options[:shipper] || origin), options)
         | 
| @@ -172,28 +172,28 @@ module ActiveMerchant | |
| 172 172 | 
             
                        if options[:shipper] and options[:shipper] != origin
         | 
| 173 173 | 
             
                          shipment << build_location_node('ShipFrom', origin, options)
         | 
| 174 174 | 
             
                        end
         | 
| 175 | 
            -
             | 
| 175 | 
            +
             | 
| 176 176 | 
             
                        # not implemented:  * Shipment/ShipmentWeight element
         | 
| 177 | 
            -
                        #                   * Shipment/ReferenceNumber element | 
| 178 | 
            -
                        #                   * Shipment/Service element | 
| 179 | 
            -
                        #                   * Shipment/PickupDate element | 
| 180 | 
            -
                        #                   * Shipment/ScheduledDeliveryDate element | 
| 181 | 
            -
                        #                   * Shipment/ScheduledDeliveryTime element | 
| 182 | 
            -
                        #                   * Shipment/AlternateDeliveryTime element | 
| 183 | 
            -
                        #                   * Shipment/DocumentsOnly element | 
| 184 | 
            -
             | 
| 177 | 
            +
                        #                   * Shipment/ReferenceNumber element
         | 
| 178 | 
            +
                        #                   * Shipment/Service element
         | 
| 179 | 
            +
                        #                   * Shipment/PickupDate element
         | 
| 180 | 
            +
                        #                   * Shipment/ScheduledDeliveryDate element
         | 
| 181 | 
            +
                        #                   * Shipment/ScheduledDeliveryTime element
         | 
| 182 | 
            +
                        #                   * Shipment/AlternateDeliveryTime element
         | 
| 183 | 
            +
                        #                   * Shipment/DocumentsOnly element
         | 
| 184 | 
            +
             | 
| 185 185 | 
             
                        packages.each do |package|
         | 
| 186 186 | 
             
                          imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
         | 
| 187 | 
            -
             | 
| 187 | 
            +
             | 
| 188 188 | 
             
                          shipment << XmlNode.new("Package") do |package_node|
         | 
| 189 | 
            -
             | 
| 189 | 
            +
             | 
| 190 190 | 
             
                            # not implemented:  * Shipment/Package/PackagingType element
         | 
| 191 191 | 
             
                            #                   * Shipment/Package/Description element
         | 
| 192 | 
            -
             | 
| 192 | 
            +
             | 
| 193 193 | 
             
                            package_node << XmlNode.new("PackagingType") do |packaging_type|
         | 
| 194 194 | 
             
                              packaging_type << XmlNode.new("Code", '02')
         | 
| 195 195 | 
             
                            end
         | 
| 196 | 
            -
             | 
| 196 | 
            +
             | 
| 197 197 | 
             
                            package_node << XmlNode.new("Dimensions") do |dimensions|
         | 
| 198 198 | 
             
                              dimensions << XmlNode.new("UnitOfMeasurement") do |units|
         | 
| 199 199 | 
             
                                units << XmlNode.new("Code", imperial ? 'IN' : 'CM')
         | 
| @@ -203,33 +203,35 @@ module ActiveMerchant | |
| 203 203 | 
             
                                dimensions << XmlNode.new(axis.to_s.capitalize, [value,0.1].max)
         | 
| 204 204 | 
             
                              end
         | 
| 205 205 | 
             
                            end
         | 
| 206 | 
            -
             | 
| 206 | 
            +
             | 
| 207 207 | 
             
                            package_node << XmlNode.new("PackageWeight") do |package_weight|
         | 
| 208 208 | 
             
                              package_weight << XmlNode.new("UnitOfMeasurement") do |units|
         | 
| 209 209 | 
             
                                units << XmlNode.new("Code", imperial ? 'LBS' : 'KGS')
         | 
| 210 210 | 
             
                              end
         | 
| 211 | 
            -
             | 
| 211 | 
            +
             | 
| 212 212 | 
             
                              value = ((imperial ? package.lbs : package.kgs).to_f*1000).round/1000.0 # 3 decimals
         | 
| 213 213 | 
             
                              package_weight << XmlNode.new("Weight", [value,0.1].max)
         | 
| 214 214 | 
             
                            end
         | 
| 215 | 
            -
             | 
| 215 | 
            +
             | 
| 216 216 | 
             
                            # not implemented:  * Shipment/Package/LargePackageIndicator element
         | 
| 217 217 | 
             
                            #                   * Shipment/Package/ReferenceNumber element
         | 
| 218 218 | 
             
                            #                   * Shipment/Package/PackageServiceOptions element
         | 
| 219 | 
            -
                            #                   * Shipment/Package/AdditionalHandling element | 
| 219 | 
            +
                            #                   * Shipment/Package/AdditionalHandling element
         | 
| 220 220 | 
             
                          end
         | 
| 221 | 
            -
             | 
| 221 | 
            +
             | 
| 222 222 | 
             
                        end
         | 
| 223 | 
            -
             | 
| 223 | 
            +
             | 
| 224 224 | 
             
                        # not implemented:  * Shipment/ShipmentServiceOptions element
         | 
| 225 | 
            -
                         | 
| 226 | 
            -
             | 
| 225 | 
            +
                        if options[:origin_account]
         | 
| 226 | 
            +
                          shipment << XmlNode.new("RateInformation") do |rate_info_node|
         | 
| 227 | 
            +
                            rate_info_node << XmlNode.new("NegotiatedRatesIndicator")
         | 
| 228 | 
            +
                          end
         | 
| 229 | 
            +
                        end
         | 
| 227 230 | 
             
                      end
         | 
| 228 | 
            -
                      
         | 
| 229 231 | 
             
                    end
         | 
| 230 232 | 
             
                    xml_request.to_s
         | 
| 231 233 | 
             
                  end
         | 
| 232 | 
            -
             | 
| 234 | 
            +
             | 
| 233 235 | 
             
                  def build_tracking_request(tracking_number, options={})
         | 
| 234 236 | 
             
                    xml_request = XmlNode.new('TrackRequest') do |root_node|
         | 
| 235 237 | 
             
                      root_node << XmlNode.new('Request') do |request|
         | 
| @@ -240,7 +242,7 @@ module ActiveMerchant | |
| 240 242 | 
             
                    end
         | 
| 241 243 | 
             
                    xml_request.to_s
         | 
| 242 244 | 
             
                  end
         | 
| 243 | 
            -
             | 
| 245 | 
            +
             | 
| 244 246 | 
             
                  def build_location_node(name,location,options={})
         | 
| 245 247 | 
             
                    # not implemented:  * Shipment/Shipper/Name element
         | 
| 246 248 | 
             
                    #                   * Shipment/(ShipTo|ShipFrom)/CompanyName element
         | 
| @@ -249,13 +251,13 @@ module ActiveMerchant | |
| 249 251 | 
             
                    location_node = XmlNode.new(name) do |location_node|
         | 
| 250 252 | 
             
                      location_node << XmlNode.new('PhoneNumber', location.phone.gsub(/[^\d]/,'')) unless location.phone.blank?
         | 
| 251 253 | 
             
                      location_node << XmlNode.new('FaxNumber', location.fax.gsub(/[^\d]/,'')) unless location.fax.blank?
         | 
| 252 | 
            -
             | 
| 254 | 
            +
             | 
| 253 255 | 
             
                      if name == 'Shipper' and (origin_account = @options[:origin_account] || options[:origin_account])
         | 
| 254 256 | 
             
                        location_node << XmlNode.new('ShipperNumber', origin_account)
         | 
| 255 257 | 
             
                      elsif name == 'ShipTo' and (destination_account = @options[:destination_account] || options[:destination_account])
         | 
| 256 258 | 
             
                        location_node << XmlNode.new('ShipperAssignedIdentificationNumber', destination_account)
         | 
| 257 259 | 
             
                      end
         | 
| 258 | 
            -
             | 
| 260 | 
            +
             | 
| 259 261 | 
             
                      location_node << XmlNode.new('Address') do |address|
         | 
| 260 262 | 
             
                        address << XmlNode.new("AddressLine1", location.address1) unless location.address1.blank?
         | 
| 261 263 | 
             
                        address << XmlNode.new("AddressLine2", location.address2) unless location.address2.blank?
         | 
| @@ -270,17 +272,17 @@ module ActiveMerchant | |
| 270 272 | 
             
                      end
         | 
| 271 273 | 
             
                    end
         | 
| 272 274 | 
             
                  end
         | 
| 273 | 
            -
             | 
| 275 | 
            +
             | 
| 274 276 | 
             
                  def parse_rate_response(origin, destination, packages, response, options={})
         | 
| 275 277 | 
             
                    rates = []
         | 
| 276 | 
            -
             | 
| 278 | 
            +
             | 
| 277 279 | 
             
                    xml = REXML::Document.new(response)
         | 
| 278 280 | 
             
                    success = response_success?(xml)
         | 
| 279 281 | 
             
                    message = response_message(xml)
         | 
| 280 | 
            -
             | 
| 282 | 
            +
             | 
| 281 283 | 
             
                    if success
         | 
| 282 284 | 
             
                      rate_estimates = []
         | 
| 283 | 
            -
             | 
| 285 | 
            +
             | 
| 284 286 | 
             
                      xml.elements.each('/*/RatedShipment') do |rated_shipment|
         | 
| 285 287 | 
             
                        service_code = rated_shipment.get_text('Service/Code').to_s
         | 
| 286 288 | 
             
                        days_to_delivery = rated_shipment.get_text('GuaranteedDaysToDelivery').to_s.to_i
         | 
| @@ -292,17 +294,18 @@ module ActiveMerchant | |
| 292 294 | 
             
                                            :currency => rated_shipment.get_text('TotalCharges/CurrencyCode').to_s,
         | 
| 293 295 | 
             
                                            :service_code => service_code,
         | 
| 294 296 | 
             
                                            :packages => packages,
         | 
| 295 | 
            -
                                            :delivery_range => [timestamp_from_business_day(days_to_delivery)] | 
| 297 | 
            +
                                            :delivery_range => [timestamp_from_business_day(days_to_delivery)],
         | 
| 298 | 
            +
                                            :negotiated_rate =>                               rated_shipment.get_text('NegotiatedRates/NetSummaryCharges/GrandTotal/MonetaryValue').to_s.to_f)
         | 
| 296 299 | 
             
                      end
         | 
| 297 300 | 
             
                    end
         | 
| 298 301 | 
             
                    RateResponse.new(success, message, Hash.from_xml(response).values.first, :rates => rate_estimates, :xml => response, :request => last_request)
         | 
| 299 302 | 
             
                  end
         | 
| 300 | 
            -
             | 
| 303 | 
            +
             | 
| 301 304 | 
             
                  def parse_tracking_response(response, options={})
         | 
| 302 305 | 
             
                    xml = REXML::Document.new(response)
         | 
| 303 306 | 
             
                    success = response_success?(xml)
         | 
| 304 307 | 
             
                    message = response_message(xml)
         | 
| 305 | 
            -
             | 
| 308 | 
            +
             | 
| 306 309 | 
             
                    if success
         | 
| 307 310 | 
             
                      tracking_number, origin, destination, status_code, status_description = nil
         | 
| 308 311 | 
             
                      delivered, exception = false
         | 
| @@ -314,7 +317,7 @@ module ActiveMerchant | |
| 314 317 | 
             
                      first_shipment = xml.elements['/*/Shipment']
         | 
| 315 318 | 
             
                      first_package = first_shipment.elements['Package']
         | 
| 316 319 | 
             
                      tracking_number = first_shipment.get_text('ShipmentIdentificationNumber | Package/TrackingNumber').to_s
         | 
| 317 | 
            -
             | 
| 320 | 
            +
             | 
| 318 321 | 
             
                      # Build status hash
         | 
| 319 322 | 
             
                      status_node = first_package.elements['Activity/Status/StatusType']
         | 
| 320 323 | 
             
                      status_code = status_node.get_text('Code').to_s
         | 
| @@ -351,10 +354,10 @@ module ActiveMerchant | |
| 351 354 | 
             
                          location = location_from_address_node(activity.elements['ActivityLocation/Address'])
         | 
| 352 355 | 
             
                          ShipmentEvent.new(description, zoneless_time, location)
         | 
| 353 356 | 
             
                        end
         | 
| 354 | 
            -
             | 
| 357 | 
            +
             | 
| 355 358 | 
             
                        shipment_events = shipment_events.sort_by(&:time)
         | 
| 356 | 
            -
             | 
| 357 | 
            -
                        # UPS will sometimes archive a shipment, stripping all shipment activity except for the delivery | 
| 359 | 
            +
             | 
| 360 | 
            +
                        # UPS will sometimes archive a shipment, stripping all shipment activity except for the delivery
         | 
| 358 361 | 
             
                        # event (see test/fixtures/xml/delivered_shipment_without_events_tracking_response.xml for an example).
         | 
| 359 362 | 
             
                        # This adds an origin event to the shipment activity in such cases.
         | 
| 360 363 | 
             
                        if origin && !(shipment_events.count == 1 && status == :delivered)
         | 
| @@ -377,7 +380,7 @@ module ActiveMerchant | |
| 377 380 | 
             
                          shipment_events[-1] = ShipmentEvent.new(shipment_events.last.name, shipment_events.last.time, destination)
         | 
| 378 381 | 
             
                        end
         | 
| 379 382 | 
             
                      end
         | 
| 380 | 
            -
             | 
| 383 | 
            +
             | 
| 381 384 | 
             
                    end
         | 
| 382 385 | 
             
                    TrackingResponse.new(success, message, Hash.from_xml(response).values.first,
         | 
| 383 386 | 
             
                      :carrier => @@name,
         | 
| @@ -395,7 +398,7 @@ module ActiveMerchant | |
| 395 398 | 
             
                      :destination => destination,
         | 
| 396 399 | 
             
                      :tracking_number => tracking_number)
         | 
| 397 400 | 
             
                  end
         | 
| 398 | 
            -
             | 
| 401 | 
            +
             | 
| 399 402 | 
             
                  def location_from_address_node(address)
         | 
| 400 403 | 
             
                    return nil unless address
         | 
| 401 404 | 
             
                    Location.new(
         | 
| @@ -408,7 +411,7 @@ module ActiveMerchant | |
| 408 411 | 
             
                            :address3 =>    node_text_or_nil(address.elements['AddressLine3'])
         | 
| 409 412 | 
             
                          )
         | 
| 410 413 | 
             
                  end
         | 
| 411 | 
            -
             | 
| 414 | 
            +
             | 
| 412 415 | 
             
                  def parse_ups_datetime(options = {})
         | 
| 413 416 | 
             
                    time, date = options[:time].to_s, options[:date].to_s
         | 
| 414 417 | 
             
                    if time.nil?
         | 
| @@ -424,29 +427,29 @@ module ActiveMerchant | |
| 424 427 | 
             
                  def response_success?(xml)
         | 
| 425 428 | 
             
                    xml.get_text('/*/Response/ResponseStatusCode').to_s == '1'
         | 
| 426 429 | 
             
                  end
         | 
| 427 | 
            -
             | 
| 430 | 
            +
             | 
| 428 431 | 
             
                  def response_message(xml)
         | 
| 429 432 | 
             
                    xml.get_text('/*/Response/Error/ErrorDescription | /*/Response/ResponseStatusDescription').to_s
         | 
| 430 433 | 
             
                  end
         | 
| 431 | 
            -
             | 
| 434 | 
            +
             | 
| 432 435 | 
             
                  def commit(action, request, test = false)
         | 
| 433 436 | 
             
                    ssl_post("#{test ? TEST_URL : LIVE_URL}/#{RESOURCES[action]}", request)
         | 
| 434 437 | 
             
                  end
         | 
| 435 | 
            -
             | 
| 436 | 
            -
             | 
| 438 | 
            +
             | 
| 439 | 
            +
             | 
| 437 440 | 
             
                  def service_name_for(origin, code)
         | 
| 438 441 | 
             
                    origin = origin.country_code(:alpha2)
         | 
| 439 | 
            -
             | 
| 442 | 
            +
             | 
| 440 443 | 
             
                    name = case origin
         | 
| 441 444 | 
             
                    when "CA" then CANADA_ORIGIN_SERVICES[code]
         | 
| 442 445 | 
             
                    when "MX" then MEXICO_ORIGIN_SERVICES[code]
         | 
| 443 446 | 
             
                    when *EU_COUNTRY_CODES then EU_ORIGIN_SERVICES[code]
         | 
| 444 447 | 
             
                    end
         | 
| 445 | 
            -
             | 
| 448 | 
            +
             | 
| 446 449 | 
             
                    name ||= OTHER_NON_US_ORIGIN_SERVICES[code] unless name == 'US'
         | 
| 447 450 | 
             
                    name ||= DEFAULT_SERVICES[code]
         | 
| 448 451 | 
             
                  end
         | 
| 449 | 
            -
             | 
| 452 | 
            +
             | 
| 450 453 | 
             
                end
         | 
| 451 454 | 
             
              end
         | 
| 452 455 | 
             
            end
         |