Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

International Shipping Rate Functionality #4

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions lib/usps.rb
Expand Up @@ -5,12 +5,12 @@ module USPS
require 'usps/errors'
require 'usps/configuration'

autoload :Client, 'usps/client'
autoload :Address, 'usps/address'
autoload :Request, 'usps/request'
autoload :VERSION, 'usps/version'
autoload :Response, 'usps/response'
autoload :TrackDetail, 'usps/track_detail'
autoload :Client, 'usps/client'
autoload :Address, 'usps/address'
autoload :Request, 'usps/request'
autoload :VERSION, 'usps/version'
autoload :Response, 'usps/response'
autoload :TrackDetail, 'usps/track_detail'

class << self
attr_writer :config
Expand Down
13 changes: 8 additions & 5 deletions lib/usps/request.rb
@@ -1,14 +1,17 @@
module USPS::Request
autoload :Base, 'usps/request/base'
autoload :ZipCodeLookup, 'usps/request/zip_code_lookup'
autoload :Base, 'usps/request/base'
autoload :ZipCodeLookup, 'usps/request/zip_code_lookup'
autoload :CityAndStateLookup, 'usps/request/city_and_state_lookup'
autoload :AddressStandardization, 'usps/request/address_standardization'

# Delivery and Signature confirmation.
# DeliveryConfirmationCertify and SignatureConfirmationCertify should be used for testing
autoload :DeliveryConfirmation, 'usps/request/delivery_confirmation'
autoload :DeliveryConfirmation, 'usps/request/delivery_confirmation'
autoload :DeliveryConfirmationCertify, 'usps/request/delivery_confirmation_certify'

autoload :TrackingLookup, 'usps/request/tracking_lookup'
autoload :TrackingFieldLookup, 'usps/request/tracking_field_lookup'
autoload :TrackingLookup, 'usps/request/tracking_lookup'
autoload :TrackingFieldLookup, 'usps/request/tracking_field_lookup'
autoload :ShippingRatesLookup, 'usps/request/shipping_rates_lookup'
autoload :InternationalShippingRatesLookup, 'usps/request/international_shipping_rates_lookup'
autoload :Package, 'usps/request/package'
end
2 changes: 1 addition & 1 deletion lib/usps/request/base.rb
Expand Up @@ -35,7 +35,7 @@ def response_for(xml)
end

def build(&block)
builder = Builder::XmlMarkup.new(:indent => 0)
builder = Builder::XmlMarkup.new(:indent => 2)
builder.tag!(self.class.tag, :USERID => USPS.config.username, &block)
end
end
Expand Down
42 changes: 42 additions & 0 deletions lib/usps/request/international_shipping_rates_lookup.rb
@@ -0,0 +1,42 @@
module USPS::Request

class InternationalShippingRatesLookup < Base
config(
:api => 'IntlRateV2',
:tag => 'IntlRateV2Request',
:secure => false,
:response => USPS::Response::InternationalShippingRatesLookup
)

def initialize(*packages)
@packages = packages.flatten
if @packages.none?
raise ArgumentError, 'A shipping rate lookup requires at least one package (USPS::Package)'
end
end

def build
super do |xml|
xml.Revision 2
@packages.each do |package|
xml.Package :ID => package.id do
xml.Pounds package.pounds
xml.Ounces package.ounces
xml.Machinable 'true' # for Service=ALL
xml.MailType package.mail_type
xml.ValueOfContents 103
xml.Country package.country
xml.Container package.container
xml.Size package.size
xml.Width package.width
xml.Length package.length
xml.Height package.height
xml.Girth nil
xml.CommercialFlag 'N'
end
end
end
end

end
end
5 changes: 5 additions & 0 deletions lib/usps/request/package.rb
@@ -0,0 +1,5 @@
module USPS::Request::Package
autoload :Base, 'usps/request/package/base'
autoload :DomesticPackage, 'usps/request/package/domestic_package'
autoload :InternationalPackage, 'usps/request/package/international_package'
end
33 changes: 33 additions & 0 deletions lib/usps/request/package/base.rb
@@ -0,0 +1,33 @@
module USPS::Request::Package
class Base
attr_accessor :id
attr_accessor :pounds, :ounces
attr_accessor :container
attr_accessor :size
attr_accessor :width, :length, :height, :girth

@@required = [:id, :pounds, :ounces, :size]

def initialize(fields)
fields.each { |name, value| send("#{name}=", value) }

yield self if block_given?

if fields[:size] == 'LARGE'
[:container, :width, :length, :height].each do |field|
error "#{field} is required when Size=LARGE" unless send(field)
end
end

@@required.each do |field|
error "#{field} is required" unless send(field)
end
end

protected

def error(message)
raise ArgumentError.new message
end
end
end
18 changes: 18 additions & 0 deletions lib/usps/request/package/domestic_package.rb
@@ -0,0 +1,18 @@
module USPS::Request::Package
class DomesticPackage < Base
attr_accessor :service
attr_accessor :first_class_mail_type
attr_accessor :origin_zip, :destination_zip
attr_accessor :value
attr_accessor :amount_to_collect

@@required += [:service, :origin_zip, :destination_zip]

def initialize(fields = {})
if fields[:service] == 'FIRST CLASS' and !fields[:first_class_mail_type]
error "first_class_mail_type is required when Service=FIRST Class"
end
super
end
end
end
11 changes: 11 additions & 0 deletions lib/usps/request/package/international_package.rb
@@ -0,0 +1,11 @@
module USPS::Request::Package
class InternationalPackage < Base
attr_accessor :country, :mail_type

@@required += [:country, :mail_type]

def initialize(fields = {})
super
end
end
end
42 changes: 42 additions & 0 deletions lib/usps/request/shipping_rates_lookup.rb
@@ -0,0 +1,42 @@
module USPS::Request

class ShippingRatesLookup < Base
config(
:api => 'RateV4',
:tag => 'RateV4Request',
:secure => false,
:response => USPS::Response::ShippingRatesLookup
)

def initialize(*packages)
@packages = packages.flatten
if @packages.none?
raise ArgumentError, 'A shipping rate lookup requires at least one package (USPS::Package)'
end
end

def build
super do |xml|
@packages.each do |package|
xml.Package :ID => package.id do
xml.Service package.service
xml.FirstClassMailType(package.first_class_mail_type) if package.first_class_mail_type
xml.ZipOrigination package.origin_zip
xml.ZipDestination package.destination_zip
xml.Pounds package.pounds
xml.Ounces package.ounces
xml.Container package.container
xml.Size package.size
if package.size == 'LARGE'
xml.Width package.width
xml.Length package.length
xml.Height package.height
end
xml.Machinable 'true' # for Service=ALL
end
end
end
end

end
end
15 changes: 9 additions & 6 deletions lib/usps/response.rb
@@ -1,8 +1,11 @@
module USPS::Response
autoload :Base, 'usps/response/base'
autoload :CityAndStateLookup, 'usps/response/city_and_state_lookup'
autoload :DeliveryConfirmation, 'usps/response/delivery_confirmation'
autoload :AddressStandardization, 'usps/response/address_standardization'
autoload :TrackingLookup, 'usps/response/tracking_lookup'
autoload :TrackingFieldLookup, 'usps/response/tracking_field_lookup'
autoload :Base, 'usps/response/base'
autoload :CityAndStateLookup, 'usps/response/city_and_state_lookup'
autoload :DeliveryConfirmation, 'usps/response/delivery_confirmation'
autoload :AddressStandardization, 'usps/response/address_standardization'
autoload :TrackingLookup, 'usps/response/tracking_lookup'
autoload :TrackingFieldLookup, 'usps/response/tracking_field_lookup'
autoload :ShippingRatesLookup, 'usps/response/shipping_rates_lookup'
autoload :InternationalShippingRatesLookup, 'usps/response/international_shipping_rates_lookup'
autoload :Package, 'usps/response/package'
end
27 changes: 27 additions & 0 deletions lib/usps/response/international_shipping_rates_lookup.rb
@@ -0,0 +1,27 @@
module USPS::Response
class InternationalShippingRatesLookup < Base
attr_reader :packages

def initialize(xml)
@packages = []
xml.search('Package').each do |package_node|
@packages << Package::InternationalPackage.new do |package|
package.id = package_node.attr('ID')
package.services = package_node.search('Service').map do |postage|
parse_service(postage)
end
end
end
end

private

def parse_service(node)
Package::InternationalPackage::Service.new.tap do |service|
service.id = node.attr('ID')
service.description = node.search('SvcDescription').text
service.rate = node.search('Postage').text
end
end
end
end
4 changes: 4 additions & 0 deletions lib/usps/response/package.rb
@@ -0,0 +1,4 @@
module USPS::Response::Package
autoload :DomesticPackage, 'usps/response/package/domestic_package'
autoload :InternationalPackage, 'usps/response/package/international_package'
end
15 changes: 15 additions & 0 deletions lib/usps/response/package/domestic_package.rb
@@ -0,0 +1,15 @@
module USPS::Response::Package
class DomesticPackage
class Postage
attr_accessor :rate, :mail_service, :class_id
end

attr_accessor :postages
attr_accessor :id, :origin_zip, :destination_zip, :pounds, :ounces, :container, :size

def initialize(properties = {})
properties.each_pair { |k, v| send("#{k}=", v) }
yield self if block_given?
end
end
end
18 changes: 18 additions & 0 deletions lib/usps/response/package/international_package.rb
@@ -0,0 +1,18 @@
module USPS::Response::Package
class InternationalPackage
class Service
attr_accessor :id, :description, :rate
end

attr_accessor :id
attr_accessor :origin_zip, :destination_zip
attr_accessor :pounds, :ounces, :container, :size

attr_accessor :services

def initialize(properties = {})
properties.each_pair { |k, v| send("#{k}=", v) }
yield self if block_given?
end
end
end
31 changes: 31 additions & 0 deletions lib/usps/response/shipping_rates_lookup.rb
@@ -0,0 +1,31 @@
module USPS::Response
class ShippingRatesLookup < Base
attr_reader :packages

def initialize(xml)
@packages = []
xml.search('Package').each do |package_node|
@packages << Package::DomesticPackage.new do |package_response|
package_response.postages = package_node.search('Postage').map { |postage| parse_postage(postage) }
package_response.id = package_node.attr('ID')
package_response.pounds = package_node.search('Pounds').text
package_response.ounces = package_node.search('Ounces').text
package_response.size = package_node.search('Size').text
package_response.container = package_node.search('Container').text
package_response.origin_zip = package_node.search('ZipOrigination').text
package_response.destination_zip = package_node.search('ZipDestination').text
end
end
end

private

def parse_postage(node)
Package::DomesticPackage::Postage.new.tap do |postage|
postage.class_id = node.attr('CLASSID')
postage.mail_service = node.search('MailService').text
postage.rate = node.search('Rate').text
end
end
end
end
2 changes: 1 addition & 1 deletion lib/usps/response/tracking_field_lookup.rb
Expand Up @@ -14,7 +14,7 @@ def initialize(xml)
@details << parse(detail)
end
end

private
def parse(node)
USPS::TrackDetail.new(
Expand Down
31 changes: 31 additions & 0 deletions spec/data/shipping_rates_lookup.xml
@@ -0,0 +1,31 @@
<RateV4Response>
<Package ID="42">
<ZipOrigination>20171</ZipOrigination>
<ZipDestination>08540</ZipDestination>
<Pounds>2</Pounds>
<Ounces>0</Ounces>
<Size>REGULAR</Size>
<Machinable>TRUE</Machinable>
<Zone>2</Zone>
<Postage CLASSID="3">
<MailService>Express Mail&amp;lt;sup&amp;gt;&amp;amp;reg;&amp;lt;/sup&amp;gt;</MailService>
<Rate>17.40</Rate>
</Postage>
<Postage CLASSID="2">
<MailService>Express Mail&amp;lt;sup&amp;gt;&amp;amp;reg;&amp;lt;/sup&amp;gt; Hold For Pickup</MailService>
<Rate>17.40</Rate>
</Postage>
<Postage CLASSID="23">
<MailService>Express Mail&amp;lt;sup&amp;gt;&amp;amp;reg;&amp;lt;/sup&amp;gt; Sunday/Holiday Delivery</MailService>
<Rate>29.90</Rate>
</Postage>
<Postage CLASSID="55">
<MailService>Express Mail&amp;lt;sup&amp;gt;&amp;amp;reg;&amp;lt;/sup&amp;gt; Flat Rate Boxes</MailService>
<Rate>39.95</Rate>
</Postage>
<Postage CLASSID="56">
<MailService>Express Mail&amp;lt;sup&amp;gt;&amp;amp;reg;&amp;lt;/sup&amp;gt; Flat Rate Boxes Hold For Pickup</MailService>
<Rate>39.95</Rate>
</Postage>
</Package>
</RateV4Response>