Skip to content
This repository has been archived by the owner on Jun 13, 2018. It is now read-only.

Commit

Permalink
Add AustraliaPost (service and calculate)
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Floch committed May 5, 2016
1 parent 73d8b30 commit 81c9024
Show file tree
Hide file tree
Showing 16 changed files with 1,019 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Active Shipping is currently being used and improved in a production environment
* [Shipwire](http://www.shipwire.com)
* [Stamps](http://www.stamps.com)
* [Kunaki](http://www.kunaki.com)
* [Australia Post](http://auspost.com.au/)

## Installation

Expand Down
1 change: 1 addition & 0 deletions lib/active_shipping/carriers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ def find(name)
ActiveShipping::Carriers.register :CanadaPostPWS, 'active_shipping/carriers/canada_post_pws'
ActiveShipping::Carriers.register :Stamps, 'active_shipping/carriers/stamps'
ActiveShipping::Carriers.register :Correios, 'active_shipping/carriers/correios'
ActiveShipping::Carriers.register :AustraliaPost, 'active_shipping/carriers/australia_post'
247 changes: 247 additions & 0 deletions lib/active_shipping/carriers/australia_post.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
require 'active_support/core_ext/object/to_query'

module ActiveShipping
class AustraliaPost < Carrier
cattr_reader :name
@@name = 'Australia Post'

URL = 'https://digitalapi.auspost.com.au/'

PARCEL_ENDPOINTS = {
service: {
domestic: 'postage/parcel/domestic/service',
international: 'postage/parcel/international/service'
},
calculate: {
domestic: 'postage/parcel/domestic/calculate',
international: 'postage/parcel/international/calculate'
}
}.freeze

def requirements
[:api_key]
end

def find_rates(origin, destination, packages, options = {})
packages = Array(packages)

service_requests = packages.map do |package|
service_request = ServiceRequest.new(origin, destination, package, options)

service_request.parse(commit(service_request.url))
service_request
end

combined_request = CombinedRequest.new(origin, destination, packages, service_requests)

RateResponse.new(true, 'success', combined_request.response_params, combined_request.response_options)
end

def calculate_rates(origin, destination, packages, service_code, options = {})
packages = Array(packages)

calculate_requests = packages.map do |package|
calculate_request = CalculateRequest.new(origin, destination, package, service_code, options)

calculate_request.parse(commit(calculate_request.url))
calculate_request
end

combined_request = CombinedRequest.new(origin, destination, packages, calculate_requests)

RateResponse.new(true, 'success', combined_request.response_params, combined_request.response_options)
end

private

def commit(request_url)
ssl_get(request_url, headers)

rescue ActiveUtils::ResponseError, ActiveShipping::ResponseError => e
data = JSON.parse(e.response.body)

RateResponse.new(false, data['error']['errorMessage'], data)
end

def headers
{
'Content-type' => 'application/json',
'auth-key' => @options[:api_key]
}
end

class CombinedRequest

def initialize(origin, destination, packages, requests)
@requests = requests
@origin = origin
@destination = destination
@packages = packages
end

def response_options
{
rates: rates,
raw_responses: @requests.map(&:raw_response),
request: @requests.map(&:url)
}
end

def response_params
{
responses: @requests.map(&:response)
}
end

private

def rate_options(rates)
{
service_name: rates.first[:service_name],
service_code: rates.first[:service_code],
total_price: rates.sum { |rate| rate[:total_price] },
currency: 'AUD',
delivery_time_text: rates.first[:delivery_time_text]
}
end

def rates
rates = @requests.map(&:rates).flatten

rates.group_by { |rate| rate[:service_name] }.map do |service_name, service_rates|
next unless service_rates.size == @packages.size

AustraliaPostRateEstimate.new(@origin, @destination, AustraliaPost.name, service_name, rate_options(service_rates))
end.compact
end

end

class AustraliaPostRequest
attr_reader :raw_response
attr_reader :response
attr_reader :rates

def initialize(origin, destination, package, options)
@origin = Location.from(origin)
@destination = Location.from(destination)
@package = package
@rates = []
@options = options
end

def url
endpoint = domestic_destination? ? @endpoints[:domestic] : @endpoints[:international]
params = domestic_destination? ? domestic_params : international_params

"#{URL}#{endpoint}.json?#{params.to_query}"
end

def parse(data)
@raw_response = data
@response = JSON.parse(data)
end

protected

def domestic_destination?
@destination.country_code == 'AU'
end

def domestic_params
{
length: @package.cm(:length),
width: @package.cm(:width),
height: @package.cm(:height),
weight: @package.weight.in_kg.to_f.round(2),
from_postcode: @origin.postal_code,
to_postcode: @destination.postal_code
}
end

def international_params
{
weight: @package.weight.in_kg.to_f.round(2),
country_code: @destination.country_code
}
end

end

class ServiceRequest < AustraliaPostRequest

def initialize(origin, destination, package, options)
super
@endpoints = PARCEL_ENDPOINTS[:service]
end

def parse(data)
super

@rates = response['services']['service'].map do |service|
{
service_name: service['name'],
service_code: service['code'],
total_price: service['price'].to_f,
currency: 'AUD'
}
end
end

end

class CalculateRequest < AustraliaPostRequest
attr_reader :service_code

def initialize(origin, destination, package, service_code, options)
super(origin, destination, package, options)

@service_code = service_code
@endpoints = PARCEL_ENDPOINTS[:calculate]
end

def parse(data)
super
postage_result = response['postage_result']

@rates = [{
service_name: postage_result['service'],
service_code: service_code,
total_price: postage_result['total_cost'].to_f,
currency: 'AUD',
delivery_time_text: postage_result['delivery_time']
}]
end

private

def calculate_params
{
service_code: @service_code,
option_code: @options[:option_code],
suboption_code: @options[:suboption_code],
extra_cover: @options[:extra_cover]
}.compact
end

def domestic_params
super.merge(calculate_params)
end

def international_params
super.merge(calculate_params)
end

end

class AustraliaPostRateEstimate < RateEstimate
attr_reader :delivery_time_text

def initialize(origin, destination, carrier, service_name, options = {})
super
@delivery_time_text = options[:delivery_time_text]
end

end
end
end
3 changes: 3 additions & 0 deletions test/credentials.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ stamps:
username: <%= ENV['ACTIVESHIPPING_STAMPS_USERNAME'] %>
password: <%= ENV['ACTIVESHIPPING_STAMPS_PASSWORD'] %>

australia_post:
api_key: <%= ENV['ACTIVESHIPPING_AUSTRALIA_POST_API_KEY'] %>

# fedex_freight:
# account: FedExFreightAccountNumber
# shipping_address1: FedExShippingAddress1
Expand Down
13 changes: 13 additions & 0 deletions test/fixtures/json/australia_post/calculate_domestic.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"postage_result": {
"service": "Express Post",
"delivery_time": "Guaranteed Next Business Day within the Express Post network (If posted on any business day Monday to Friday in accordance with the conditions set out on the item).",
"total_cost": "10.20",
"costs": {
"cost": {
"item": "Express Post",
"cost": "10.20"
}
}
}
}
19 changes: 19 additions & 0 deletions test/fixtures/json/australia_post/calculate_domestic_2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"postage_result": {
"service": "Express Post",
"delivery_time": "Guaranteed Next Business Day within the Express Post network (If posted on any business day Monday to Friday in accordance with the conditions set out on the item).",
"total_cost": "14.70",
"costs": {
"cost": [{
"item": "Express Post",
"cost": "10.20"
}, {
"item": "Standard Service",
"cost": "0.00"
}, {
"item": "Extra Cover",
"cost": "4.50"
}]
}
}
}
12 changes: 12 additions & 0 deletions test/fixtures/json/australia_post/calculate_international.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"postage_result": {
"service": "Courier",
"total_cost": "87.36",
"costs": {
"cost": {
"item": "Courier",
"cost": "87.36"
}
}
}
}
15 changes: 15 additions & 0 deletions test/fixtures/json/australia_post/calculate_international_2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"postage_result": {
"service": "Courier",
"total_cost": "87.36",
"costs": {
"cost": [{
"item": "Courier",
"cost": "87.36"
}, {
"item": "SMS track advice",
"cost": "0.00"
}]
}
}
}
5 changes: 5 additions & 0 deletions test/fixtures/json/australia_post/error_message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"error": {
"errorMessage": "Please enter From postcode."
}
}
Loading

0 comments on commit 81c9024

Please sign in to comment.