class Fedex::Request::Base

Constants

CARRIER_CODES

List of available Carrier Codes

CLEARANCE_BROKERAGE_TYPE

Clearance Brokerage Type

DROP_OFF_TYPES

List of available DropOffTypes

PACKAGING_TYPES

List of available Packaging Type

PAYMENT_TYPE

List of available Payment Types

PRODUCTION_URL

Fedex Production URL

RECIPIENT_CUSTOM_ID_TYPE

Recipient Custom ID Type

SERVICE_TYPES

List of available Service Types

TEST_URL

Fedex Text URL

Attributes

debug[RW]

If true the rate method will return the complete response from the Fedex Web Service

Public Class Methods

new(credentials, options={}) click to toggle source

In order to use Fedex rates API you must first apply for a developer(and later production keys), Visit {www.fedex.com/us/developer/ Fedex Developer Center} for more information about how to obtain your keys. @param [String] key - Fedex web service key @param [String] password - Fedex password @param [String] account_number - Fedex account_number @param [String] meter - Fedex meter number @param [String] mode - [development/production]

return a Fedex::Request::Base object

# File lib/fedex/request/base.rb, line 50
def initialize(credentials, options={})
  requires!(options, :shipper, :recipient, :packages)
  @credentials = credentials
  @shipper, @recipient, @packages, @service_type, @customs_clearance_detail, @debug = options[:shipper], options[:recipient], options[:packages], options[:service_type], options[:customs_clearance_detail], options[:debug]
  @origin = options[:origin]
  @debug = ENV['DEBUG'] == 'true'
  @shipping_options =  options[:shipping_options] ||={}
  @payment_options = options[:payment_options] ||={}
  requires!(@payment_options, :type, :account_number, :name, :company, :phone_number, :country_code) if @payment_options.length > 0
  if options.has_key?(:mps)
    @mps = options[:mps]
    requires!(@mps, :package_count, :total_weight, :sequence_number)
    requires!(@mps, :master_tracking_id) if @mps.has_key?(:sequence_number) && @mps[:sequence_number].to_i >= 2
  else
    @mps = {}
  end
  # Expects hash with addr and port
  if options[:http_proxy]
    self.class.http_proxy options[:http_proxy][:host], options[:http_proxy][:port]
  end
end

Public Instance Methods

process_request() click to toggle source

Sends post request to Fedex web service and parse the response. Implemented by each subclass

# File lib/fedex/request/base.rb, line 74
def process_request
  raise NotImplementedError, "Override process_request in subclass"
end

Private Instance Methods

add_client_detail(xml) click to toggle source

Add Client Detail information(account_number and meter_number) to xml request

# File lib/fedex/request/base.rb, line 90
def add_client_detail(xml)
  xml.ClientDetail{
    xml.AccountNumber @credentials.account_number
    xml.MeterNumber @credentials.meter
    xml.Localization{
      xml.LanguageCode 'en' # English
      xml.LocaleCode   'us' # United States
    }
  }
end
add_customer_references(xml, package) click to toggle source
# File lib/fedex/request/base.rb, line 305
def add_customer_references(xml, package)
  # customer_refrences is a legacy misspelling
  if refs = package[:customer_references] || package[:customer_refrences]
    refs.each do |ref|
      xml.CustomerReferences{
        if ref.is_a?(Hash)
          # :type can specify custom type:
          #
          # BILL_OF_LADING, CUSTOMER_REFERENCE, DEPARTMENT_NUMBER,
          # ELECTRONIC_PRODUCT_CODE, INTRACOUNTRY_REGULATORY_REFERENCE,
          # INVOICE_NUMBER, P_O_NUMBER, RMA_ASSOCIATION,
          # SHIPMENT_INTEGRITY, STORE_NUMBER
          xml.CustomerReferenceType ref[:type]
          xml.Value                 ref[:value]
        else
          xml.CustomerReferenceType 'CUSTOMER_REFERENCE'
          xml.Value                 ref
        end
      }
    end
  end
end
add_customs_clearance(xml) click to toggle source

Add customs clearance(for international shipments)

# File lib/fedex/request/base.rb, line 329
def add_customs_clearance(xml)
  xml.CustomsClearanceDetail{
    hash_to_xml(xml, @customs_clearance_detail)
  }
end
add_master_tracking_id(xml) click to toggle source

Add Master Tracking Id (for MPS Shipping Labels, this is required when requesting labels 2 through n)

# File lib/fedex/request/base.rb, line 210
def add_master_tracking_id(xml)
  if @mps.has_key? :master_tracking_id
    xml.MasterTrackingId{
      xml.TrackingIdType @mps[:master_tracking_id][:tracking_id_type]
      xml.TrackingNumber @mps[:master_tracking_id][:tracking_number]
    }
  end
end
add_origin(xml) click to toggle source

Add shipper to xml request

# File lib/fedex/request/base.rb, line 147
def add_origin(xml)
  xml.Origin{
    xml.Contact{
      xml.PersonName @origin[:name]
      xml.CompanyName @origin[:company]
      xml.PhoneNumber @origin[:phone_number]
    }
    xml.Address {
      Array(@origin[:address]).take(2).each do |address_line|
        xml.StreetLines address_line
      end
      xml.City @origin[:city]
      xml.StateOrProvinceCode @origin[:state]
      xml.PostalCode @origin[:postal_code]
      xml.CountryCode @origin[:country_code]
    }
  }
end
add_packages(xml) click to toggle source

Add packages to xml request

# File lib/fedex/request/base.rb, line 220
def add_packages(xml)
  add_master_tracking_id(xml) if @mps.has_key? :master_tracking_id
  package_count = @packages.size
  if @mps.has_key? :package_count
    xml.PackageCount @mps[:package_count]
  else
    xml.PackageCount package_count
  end
  @packages.each do |package|
    xml.RequestedPackageLineItems{
      if @mps.has_key? :sequence_number
        xml.SequenceNumber @mps[:sequence_number]
      else
        xml.GroupPackageCount 1
      end
      if package[:insured_value]
        xml.InsuredValue{
          xml.Currency package[:insured_value][:currency]
          xml.Amount package[:insured_value][:amount]
        }
      end
      xml.Weight{
        xml.Units package[:weight][:units]
        xml.Value package[:weight][:value]
      }
      if package[:dimensions]
        xml.Dimensions{
          xml.Length package[:dimensions][:length]
          xml.Width package[:dimensions][:width]
          xml.Height package[:dimensions][:height]
          xml.Units package[:dimensions][:units]
        }
      end
      add_customer_references(xml, package)
      if package[:special_services_requested]
        xml.SpecialServicesRequested{
          if package[:special_services_requested][:special_service_types]
            if package[:special_services_requested][:special_service_types].is_a? Array
              package[:special_services_requested][:special_service_types].each do |type|
                xml.SpecialServiceTypes type
              end
            else
              xml.SpecialServiceTypes package[:special_services_requested][:special_service_types]
            end
          end
          # Handle COD Options
          if package[:special_services_requested][:cod_detail]
            xml.CodDetail{
              xml.CodCollectionAmount{
                xml.Currency package[:special_services_requested][:cod_detail][:cod_collection_amount][:currency]
                xml.Amount package[:special_services_requested][:cod_detail][:cod_collection_amount][:amount]
              }
              if package[:special_services_requested][:cod_detail][:add_transportation_charges]
                xml.AddTransportationCharges package[:special_services_requested][:cod_detail][:add_transportation_charges]
              end
              xml.CollectionType package[:special_services_requested][:cod_detail][:collection_type]
              xml.CodRecipient {
                add_shipper(xml)
              }
              if package[:special_services_requested][:cod_detail][:reference_indicator]
                xml.ReferenceIndicator package[:special_services_requested][:cod_detail][:reference_indicator]
              end
            }
          end
          # DangerousGoodsDetail goes here
          if package[:special_services_requested][:dry_ice_weight]
            xml.DryIceWeight{
              xml.Units package[:special_services_requested][:dry_ice_weight][:units]
              xml.Value package[:special_services_requested][:dry_ice_weight][:value]
            }
          end
          if package[:special_services_requested][:signature_option_detail]
            xml.SignatureOptionDetail{
              xml.OptionType package[:special_services_requested][:signature_option_detail][:signature_option_type]
            }
          end
          if package[:special_services_requested][:priority_alert_detail]
            xml.PriorityAlertDetail package[:special_services_requested][:priority_alert_detail]
          end
        }
      end
    }
  end
end
add_recipient(xml) click to toggle source

Add recipient to xml request

# File lib/fedex/request/base.rb, line 167
def add_recipient(xml)
  xml.Recipient{
    xml.Contact{
      xml.PersonName @recipient[:name]
      xml.CompanyName @recipient[:company]
      xml.PhoneNumber @recipient[:phone_number]
    }
    xml.Address {
      Array(@recipient[:address]).take(2).each do |address_line|
        xml.StreetLines address_line
      end
      xml.City @recipient[:city]
      xml.StateOrProvinceCode @recipient[:state]
      xml.PostalCode @recipient[:postal_code]
      xml.CountryCode @recipient[:country_code]
      xml.Residential @recipient[:residential]
    }
  }
end
add_requested_shipment(xml) click to toggle source

Add information for shipments

# File lib/fedex/request/base.rb, line 112
def add_requested_shipment(xml)
  xml.RequestedShipment{
    xml.DropoffType @shipping_options[:drop_off_type] ||= "REGULAR_PICKUP"
    xml.ServiceType service_type
    xml.PackagingType @shipping_options[:packaging_type] ||= "YOUR_PACKAGING"
    add_shipper(xml)
    add_recipient(xml)
    add_shipping_charges_payment(xml)
    add_customs_clearance(xml) if @customs_clearance_detail
    xml.RateRequestTypes "ACCOUNT"
    add_packages(xml)
  }
end
add_shipper(xml) click to toggle source

Add shipper to xml request

# File lib/fedex/request/base.rb, line 127
def add_shipper(xml)
  xml.Shipper{
    xml.Contact{
      xml.PersonName @shipper[:name]
      xml.CompanyName @shipper[:company]
      xml.PhoneNumber @shipper[:phone_number]
    }
    xml.Address {
      Array(@shipper[:address]).take(2).each do |address_line|
        xml.StreetLines address_line
      end
      xml.City @shipper[:city]
      xml.StateOrProvinceCode @shipper[:state]
      xml.PostalCode @shipper[:postal_code]
      xml.CountryCode @shipper[:country_code]
    }
  }
end
add_shipping_charges_payment(xml) click to toggle source

Add shipping charges to xml request

# File lib/fedex/request/base.rb, line 188
def add_shipping_charges_payment(xml)
  xml.ShippingChargesPayment{
    xml.PaymentType @payment_options[:type] || "SENDER"
    xml.Payor{
      if service[:version].to_i >= Fedex::API_VERSION.to_i
        xml.ResponsibleParty {
          xml.AccountNumber @payment_options[:account_number] || @credentials.account_number
          xml.Contact {
            xml.PersonName @payment_options[:name] || @shipper[:name]
            xml.CompanyName @payment_options[:company] || @shipper[:company]
            xml.PhoneNumber @payment_options[:phone_number] || @shipper[:phone_number]
          }
        }
      else
        xml.AccountNumber @payment_options[:account_number] || @credentials.account_number
        xml.CountryCode @payment_options[:country_code] || @shipper[:country_code]
      end
    }
  }
end
add_version(xml) click to toggle source

Add Version to xml request, using the version identified in the subclass

# File lib/fedex/request/base.rb, line 102
def add_version(xml)
  xml.Version{
    xml.ServiceId service[:id]
    xml.Major     service[:version]
    xml.Intermediate 0
    xml.Minor 0
  }
end
add_web_authentication_detail(xml) click to toggle source

Add web authentication detail information(key and password) to xml request

# File lib/fedex/request/base.rb, line 80
def add_web_authentication_detail(xml)
  xml.WebAuthenticationDetail{
    xml.UserCredential{
      xml.Key @credentials.key
      xml.Password @credentials.password
    }
  }
end
api_url() click to toggle source

Fedex Web Service Api

# File lib/fedex/request/base.rb, line 336
def api_url
  @credentials.mode == "production" ? PRODUCTION_URL : TEST_URL
end
build_xml() click to toggle source

Build xml Fedex Web Service request Implemented by each subclass

# File lib/fedex/request/base.rb, line 342
def build_xml
  raise NotImplementedError, "Override build_xml in subclass"
end
hash_to_xml(xml, hash) click to toggle source

Build xml nodes dynamically from the hash keys and values

# File lib/fedex/request/base.rb, line 347
def hash_to_xml(xml, hash)
  hash.each do |key, value|
    key_s_down = key.to_s.downcase
    if key_s_down.match(/^commodities_\d{1,}$/)
      element = 'Commodities'
    elsif key_s_down.match(/^masked_data_\d{1,}$/)
      element = 'MaskedData'
    else
      element = camelize(key)
    end
    if value.is_a?(Hash)
      xml.send element do |x|
        hash_to_xml(x, value)
      end
    elsif value.is_a?(Array)
      value.each do |v|
        xml.send element do |x|
          hash_to_xml(x, v)
        end
      end
    else
      xml.send element, value
    end
  end
end
parse_response(response) click to toggle source

Parse response, convert keys to underscore symbols

# File lib/fedex/request/base.rb, line 374
def parse_response(response)
  response = sanitize_response_keys(response.parsed_response)
end
sanitize_response_keys(response) click to toggle source

Recursively sanitizes the response object by cleaning up any hash keys.

# File lib/fedex/request/base.rb, line 379
def sanitize_response_keys(response)
  if response.is_a?(Hash)
    response.inject({}) { |result, (key, value)| result[underscorize(key).to_sym] = sanitize_response_keys(value); result }
  elsif response.is_a?(Array)
    response.collect { |result| sanitize_response_keys(result) }
  else
    response
  end
end
service() click to toggle source
# File lib/fedex/request/base.rb, line 389
def service
  raise NotImplementedError,
    "Override service in subclass: {:id => 'service', :version => 1}"
end
service_type() click to toggle source

Use GROUND_HOME_DELIVERY for shipments going to a residential address within the US.

# File lib/fedex/request/base.rb, line 395
def service_type
  if @recipient[:residential].to_s =~ /true/i and @service_type =~ /GROUND/i and @recipient[:country_code] =~ /US/i
    "GROUND_HOME_DELIVERY"
  else
    @service_type
  end
end
success?(response) click to toggle source

Successful request

# File lib/fedex/request/base.rb, line 404
def success?(response)
  (!response[:rate_reply].nil? and %w{SUCCESS WARNING NOTE}.include? response[:rate_reply][:highest_severity])
end