Get shipping quotes from DHL's XML-PI Service.
Use of the XML-PI Service requires you to have Site ID and Password from DHL. You can sign up here: https://myaccount.dhl.com/MyAccount/jsp/TermsAndConditionsIndex.htm
Many thanks to John Riff of DHL for his help during the development of this gem.
Add this line to your application's Gemfile:
gem 'dhl-get_quote'
And then execute:
$ bundle
Or install it yourself as:
$ gem install dhl-get_quote
require 'dhl-get_quote'
r = Dhl::GetQuote::Request.new(
:site_id => "SiteIdHere",
:password => "p4ssw0rd",
:test_mode => true # changes the url being hit
)
r.metric_measurements!
r.add_special_service("DD")
r.to('CA', "T1H 0A1")
r.from('US', 84010)
r.pieces << Dhl::GetQuote::Piece.new(
:height => 20.0,
:weight => 20.0,
:width => 20.0,
:depth => 19.0
)
response = r.post
if response.error?
raise "There was an error: #{response.raw_xml}"
else
puts "Your cost to ship will be: #{response.total_amount} in #{response.currency_code}."
end
This is where the magic starts. It accepts a hash that, at minimum, requires :site_id and :password. Optionally, :test_mode may be passed in to tell the gem to use the XML-PI test URL. The default is to not use test mode and to hit the production URL.
request = Dhl::GetQuote::Request.new(
:site_id => "SiteIdHere",
:password => "p4ssw0rd",
:test_mode => false
)
NOTE: You can also set default beforehand in, for example, an initializer. For more information on this, please see the section "Initializers with Dhl::GetQuote"
If you are using a special account for shipping payments, you can specify it as
request.payment_account_number('12345678')
To read the current payment account number (if set), use:
request.payment_accout_number
It will return the current number or nil if none has been set.
To set the source and destination, use the #to() and #from() methods:
#to(country_code, postal_code, city_name), #from(country_code, postal_code, city_name)
The country code must be the two-letter capitalized ISO country code. The postal code will be cast in to a string. City name is optional.
Example:
# without city
request.from('US', 84111)
request.to('CA', 'T1H 0A1')
# with city
request.from('US', 84111, "Bountiful")
request.to('MX', "53950", 'Naucalpan de Juárez')
DHL can accept weights and measures in both Metric and US Customary units. Weights are given in either pounds or kilograms, dimensions in either inches or centimeters. This gem defaults to use metric measurements.
To set to US Customary, use:
request.us_measurements! # set dimensions to inches and weight to pounds
To set back to Metric, use
request.metric_measurements! # set dimensions to centimeters and weight to kilograms
To query what measurement system the object is currently using, use the following boolean calls:
request.us_measurements?
request.metric_measurements?
You can also get the value directly:
request.dimensions_unit # will return either "CM" or "IN"
request.weight_unit # will return either "KG" or "LB"
! Note, this a breaking change from 0.4.x
To set the duty on a shipment, use the dutiable() method. It accepts the numeric value and an optional currency code. If not specified, the currency code default to US Dollars (USD).
# set the dutiable value at $100 in US Dollars
request.dutiable(100.00, 'USD')
To remove a previously set duty, use the not_dutiable!() method.
request.not_dutiable!
You can query the current state with #dutiable?:
request.dutiable? # returns true or false
The default is for the request is "not dutiable".
Shipment services (speed, features, etc) can be added, listed and removed.
To add a special service, call #add_special_service as pass in DHL-standard code for the service:
request.add_special_service("D")
To list all services currently added to a request, use #special_services:
request.special_services
To remove a special service, use #remove_special_service and pass the code:
request.remove_special_service("D")
The interface will not allow the same special service code to be added twice, it will be silently ignored if you try.
To add items to the shipping quote request, generate a new Dhl::GetQuote::Piece instance and append it to #pieces:
# minimal
request.pieces << Dhl::GetQuote::Piece.new( :weight => 20.0 )
# more details
request.pieces << Dhl::GetQuote::Piece.new(
:weight => 20.0, :height => 20.0, :width => 20.0, :depth => 19.0
)
Dhl::GetQuote::Piece requires at least :weight to be specified, and it must be a nonzero integer or float. Optionally, you can provide :width, :depth and :height. The measurement options must all be added at once and cannot be added individually. They must be integers or floats.
Once the request is prepared, call #post() to post the request to the DHL XML-PI. This will return a Dhl::GetQuote::Response object.
response = request.post
response.class == Dhl::GetQuote::Response # true
Once a post is sent to DHL, this gem will interpret the XML returned and create a Dhl::GetQuote::Response object.
To check for errors in the response (both local and upstream), query the #error? and #error methods
response.error? # true
response.error
# => < Dhl::GetQuote::Upstream::ValidationFailureError: your site id is not correct blah blah >
The response object exposes the following values:
- currency_code
- currency_role_type_code
- weight_charge
- total_amount
- total_tax_amount
- weight_charge_tax
To find the total change:
puts "Your cost to ship will be: #{response.total_amount} in #{response.currency_code}."
# Your cost to ship will be: 337.360 in USD.
DHL can return the cost in currency unit based on sender location or reciever location. It is unlikely this will be used much, but you can change the currency by calling #load_costs with the CurrencyRoleTypeCode:
response.load_costs('PULCL')
puts "Your cost to ship will be: #{response.total_amount} in #{response.currency_code}."
# Your cost to ship will be: 341.360 in CAD.
CurrencyRoleTypeCodes that can be used are:
- BILLCU – Billing currency
- PULCL – Country of pickup local currency
- INVCU – Invoice currency
- BASEC – Base currency
If you need data from the response that is not exposed by this gem, you can access both the raw xml and parsed xml directly:
response.raw_xml # raw xml string as returned by DHL
response.parsed_xml # xml parsed in to a Hash for easy traversal
#parsed_xml() is not always available in the case of errors. #raw_xml() is, except in cases of network transport errors.
In cases where you have either sent many special service code, or you are evaluating all available services (via the 'OSINFO' special service code), you can obtain a list of all the services with the #offered_services() and #all_services() methods. Both methods return an array of Dhl::GetQuote::MarketService objects.
The #offered_services() method returns only those services intended to be shown to the end user an optional services (XCH) they can apply. These would be services with either 'TransInd' or 'MrkSrvInd' set to 'Y'.
The #all_services() methods returns all associated services, including everything in #offered_services and also including possible fees (FEE) and surcharges (SCH).
If using 'OSINFO' to obtain all offered services, you pass the value of #code() in to Request#add_special_service() to apply this services to another request:
# assume we already did an 'OSINFO' request.
new_request.add_special_service(response.offered_services.first.code)
Instances of this object are returned from Response#offered_services() and Response#all_services().
All XML parameters in the response will be added as methods to this object. They may vary but generally include:
- #local_product_code() - code for user-offered services (signature, tracking, overnight, etc)
- #local_service_type() code for non-offered special services (fees, surcharges, etc)
- #local_service_type_name() - Name for a non-offered service
- #local_product_name() - Name for a user-offered service
- #mrk_srv_ind() - Should this be offered to the user?
- #trans_ind() - Should this be shown to every user regardless of shipping options?
The #code() method will return the code for a given service. It will work on both non-offered and user-offered services, it queries both LocalProductCode and LocalServiceType.
market_service.code # "D"
This code can be passed in to Request#add_special_service()
The #name() method will return the name for a given service. It will work on both non-offered and user-offered services, it queries both LocalProductName and LocalServiceTypeName.
market_service.name # "EXPRESS WORLDWIDE DOC"
By default the gem will only log output of fatal errors that occur when communicating with the DHL servers. If such an error is caught, the gem will log the exception name, the request XML (if generated) and the response xml (if received).
To change the log level:
Dhl::GetQuote::configure do |c|
c.set_log_level :verbose
end
# or
Dhl::GetQuote::set_log_level :verbose
Available log levels are:
:none Logs nothing :critical Logs fatal exceptions (DEFAULT) :verbose Log :critical, also logs internal validation errors :debug Log everything
The default logger is STDERR. You can change this by passing a Proc object to set_logger(). For example, if you wanted to log to the Rails Logger instead:
# with a block
Dhl::GetQuote::set_logger do |message, log_level|
Rails.logger.info(message)
end
# as an argument
logger = Proc.new { |message, log_level| Rails.logger.info(message) }
Dhl::GetQuote::set_logger(logger)
# you can also do this is the configure block:
Dhl::GetQuote::configure do |c|
c.set_logger(
Proc.new { |message, log_level| Rails.logger.info(message) }
)
# or as a block here too
c.set_logger do |message, log_level|
Rails.logger.info(message)
end
end
Log level CAN NOT be set via "Dhl::GetQuote::new()" options.
If you don't want to have to pass email, password, weight setting, etc, every time you build a new request object you can set these defaults beforehand. This works well in cases where you want to put setting in something like a Rails initializer.
To do this, call Dhl::GetQuote::configure and pass a block:
Dhl::GetQuote::configure do |c|
c.side_id "SomeSiteId"
c.password "p4ssw0rd"
c.production_mode! # or test_mode!
c.metric_measurements!
c.us_measurements!
c.dutiable(1.00, 'USD')
end
The above block sets defaults for use thereafter. You would then not have to pass site_id or password in to Dhl::GetQuote::new():
Dhl::GetQuote::configure do |c|
c.side_id "SomeSiteId"
c.password "p4ssw0rd"
end
request = Dhl::GetQuote::new()
Note: options passed in to Dhl::GetQuote::new() will override setting in the Dhl::GetQuote::configure block.
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Add tests, make sure existing tests pass.
- Push to the branch (
git push origin my-new-feature
) - Create new Pull Request