Class: FriendlyShipping::Services::UspsInternational::ParsePackageRate

Inherits:
Object
  • Object
show all
Defined in:
lib/friendly_shipping/services/usps_international/parse_package_rate.rb

Constant Summary collapse

ESCAPING_AND_SYMBOLS =

USPS returns all the info about a rate in a long string with a bit of gibberish.

/<\S*>/.freeze
LEADING_USPS =

At the beginning of the long String, USPS keeps a copy of its own name. We know we’re dealing with them though, so we can filter that out, too.

/^USPS /.freeze
SERVICE_NAME_SUBSTITUTIONS =

This combines all the things we want to filter out.

/#{ESCAPING_AND_SYMBOLS}|#{LEADING_USPS}/.freeze
SERVICE_CODE_TAG =

Often we get a multitude of rates for the same service given some combination of Box type and (see below) and “Hold for Pickup” service. This creates a regular expression with groups named after the keys from the ‘Usps::CONTAINERS` constant. Unfortunately, the keys don’t correspond directly to the codes we use when serializing the request. The tags used in the rate node that we get information from.

'ID'
SERVICE_NAME_TAG =
'SvcDescription'
RATE_TAG =
'Postage'
COMMERCIAL_RATE_TAG =
'CommercialPostage'
COMMERCIAL_PLUS_RATE_TAG =
'CommercialPlusPostage'
CURRENCY =
Money::Currency.new('USD').freeze

Class Method Summary collapse

Class Method Details

.call(rate_node, package, package_options) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/friendly_shipping/services/usps_international/parse_package_rate.rb', line 31

def call(rate_node, package, package_options)
  # "A mail class identifier for the postage returned. Not necessarily unique within a <Package/>."
  # (from the USPS docs). We save this on the data Hash, but do not use it for identifying shipping methods.
  service_code = rate_node.attributes[SERVICE_CODE_TAG].value

  # The long string discussed above.
  service_name = rate_node.at(SERVICE_NAME_TAG).text

  delivery_guarantee = rate_node.at('GuaranteeAvailability')&.text
  delivery_commitment = rate_node.at('SvcCommitments')&.text

  # Clean up the long string
  service_name.gsub!(SERVICE_NAME_SUBSTITUTIONS, '')

  commercial_rate_requested_or_rate_is_zero = package_options.commercial_pricing || rate_node.at(RATE_TAG).text.to_d.zero?
  commercial_rate_available = rate_node.at(COMMERCIAL_RATE_TAG) || rate_node.at(COMMERCIAL_PLUS_RATE_TAG)

  rate_value =
    if commercial_rate_requested_or_rate_is_zero && commercial_rate_available
      commercial_rate = rate_node.at(COMMERCIAL_RATE_TAG)&.text.to_d
      commercial_rate.zero? ? rate_node.at(COMMERCIAL_PLUS_RATE_TAG).text.to_d : commercial_rate
    else
      rate_node.at(RATE_TAG).text.to_d
    end

  # The rate expressed as a RubyMoney objext
  rate = Money.new(rate_value * CURRENCY.subunit_to_unit, CURRENCY)

  # Which shipping method does this rate belong to? We first try to match a rate to a shipping method
  shipping_method = SHIPPING_METHODS.find { |sm| sm.service_code == service_code }

  # Combine all the gathered information in a FriendlyShipping::Rate object.
  # Careful: This rate is only for one package within the shipment, and we get multiple
  # rates per package for the different shipping method/box/hold for pickup combinations.
  FriendlyShipping::Rate.new(
    shipping_method: shipping_method,
    amounts: { package.id => rate },
    data: {
      package: package,
      delivery_commitment: delivery_commitment,
      delivery_guarantee: delivery_guarantee,
      full_mail_service: service_name,
      service_code: service_code,
    }
  )
end