friendly_shipping 0.4.6 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/friendly_shipping.rb +4 -0
- data/lib/friendly_shipping/services/ship_engine/parse_rate_estimate_response.rb +0 -1
- data/lib/friendly_shipping/services/ups.rb +26 -1
- data/lib/friendly_shipping/services/ups/parse_rate_response.rb +11 -3
- data/lib/friendly_shipping/services/ups/parse_time_in_transit_response.rb +52 -0
- data/lib/friendly_shipping/services/ups/serialize_time_in_transit_request.rb +53 -0
- data/lib/friendly_shipping/services/ups/timing_options.rb +33 -0
- data/lib/friendly_shipping/services/usps.rb +18 -1
- data/lib/friendly_shipping/services/usps/parse_time_in_transit_response.rb +217 -0
- data/lib/friendly_shipping/services/usps/serialize_time_in_transit_request.rb +22 -0
- data/lib/friendly_shipping/services/usps/timing_options.rb +19 -0
- data/lib/friendly_shipping/timing.rb +29 -0
- data/lib/friendly_shipping/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a74d755729d634f9a26b89c9cf7fe9de4f0f9c6f
|
4
|
+
data.tar.gz: d55595a012e01a25af70df3f0c7d6d20e1107b6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43ad0e0432113fcd26fb0f3472bc2ebf8c64a0d9a4b9b25b0618831a6ecb2a66a97d55f9764d11387c0ce68f6923a3c190d087ea2f40ee39004df4c0c336890a
|
7
|
+
data.tar.gz: 6c86ce696207eeb0bc1274485769f53070806a11314df7981d684bd2d0cb9eddb1699c22592f2ff90c4484c3b9a9caf398eb7deb597d4c15136e0148ed231375
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## [0.4.7] - 2019-11-28
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- UPS Service: Add timing support
|
11
|
+
- UPS Service: Add indicator for address type changes when quoting rates
|
12
|
+
- USPS Service: Add timing support
|
13
|
+
- Explicitly set Money rounding mode
|
14
|
+
|
7
15
|
## [0.4.6] - 2019-11-28
|
8
16
|
|
9
17
|
### Changed
|
data/lib/friendly_shipping.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "physical"
|
4
|
+
require "money"
|
5
|
+
|
6
|
+
# Explicitly configure the default rounding mode to avoid deprecation warnings
|
7
|
+
Money.rounding_mode = BigDecimal::ROUND_HALF_EVEN
|
4
8
|
|
5
9
|
require "friendly_shipping/version"
|
6
10
|
require "friendly_shipping/request"
|
@@ -8,6 +8,7 @@ require 'friendly_shipping/services/ups/serialize_address_validation_request'
|
|
8
8
|
require 'friendly_shipping/services/ups/serialize_rating_service_selection_request'
|
9
9
|
require 'friendly_shipping/services/ups/serialize_shipment_accept_request'
|
10
10
|
require 'friendly_shipping/services/ups/serialize_shipment_confirm_request'
|
11
|
+
require 'friendly_shipping/services/ups/serialize_time_in_transit_request'
|
11
12
|
require 'friendly_shipping/services/ups/serialize_void_shipment_request'
|
12
13
|
require 'friendly_shipping/services/ups/parse_address_validation_response'
|
13
14
|
require 'friendly_shipping/services/ups/parse_address_classification_response'
|
@@ -15,9 +16,11 @@ require 'friendly_shipping/services/ups/parse_city_state_lookup_response'
|
|
15
16
|
require 'friendly_shipping/services/ups/parse_rate_response'
|
16
17
|
require 'friendly_shipping/services/ups/parse_shipment_confirm_response'
|
17
18
|
require 'friendly_shipping/services/ups/parse_shipment_accept_response'
|
19
|
+
require 'friendly_shipping/services/ups/parse_time_in_transit_response'
|
18
20
|
require 'friendly_shipping/services/ups/parse_void_shipment_response'
|
19
21
|
require 'friendly_shipping/services/ups/shipping_methods'
|
20
22
|
require 'friendly_shipping/services/ups/label_options'
|
23
|
+
require 'friendly_shipping/services/ups/timing_options'
|
21
24
|
|
22
25
|
module FriendlyShipping
|
23
26
|
module Services
|
@@ -42,6 +45,7 @@ module FriendlyShipping
|
|
42
45
|
rates: '/ups.app/xml/Rate',
|
43
46
|
ship_confirm: '/ups.app/xml/ShipConfirm',
|
44
47
|
ship_accept: '/ups.app/xml/ShipAccept',
|
48
|
+
timings: '/ups.app/xml/TimeInTransit',
|
45
49
|
void: '/ups.app/xml/Void',
|
46
50
|
}.freeze
|
47
51
|
|
@@ -58,7 +62,7 @@ module FriendlyShipping
|
|
58
62
|
end
|
59
63
|
|
60
64
|
# Get rates for a shipment
|
61
|
-
# @param [Physical::Shipment]
|
65
|
+
# @param [Physical::Shipment] shipment The shipment we want to get rates for
|
62
66
|
# @return [Result<ApiResult<Array<Rate>>>] The rates returned from UPS encoded in a
|
63
67
|
# `FriendlyShipping::ApiResult` object.
|
64
68
|
def rate_estimates(shipment, debug: false)
|
@@ -75,6 +79,27 @@ module FriendlyShipping
|
|
75
79
|
end
|
76
80
|
end
|
77
81
|
|
82
|
+
# Get timing information for a shipment
|
83
|
+
# @param [Physical::Shipment] shipment The shipment we want to estimate timings for
|
84
|
+
# @param [FriendlyShipping::Services::Ups::TimingOptions] Options for this call
|
85
|
+
def timings(shipment, options:, debug: false)
|
86
|
+
time_in_transit_request_xml = SerializeTimeInTransitRequest.call(
|
87
|
+
shipment: shipment,
|
88
|
+
options: options
|
89
|
+
)
|
90
|
+
time_in_transit_url = base_url + RESOURCES[:timings]
|
91
|
+
|
92
|
+
request = FriendlyShipping::Request.new(
|
93
|
+
url: time_in_transit_url,
|
94
|
+
body: access_request_xml + time_in_transit_request_xml,
|
95
|
+
debug: debug
|
96
|
+
)
|
97
|
+
|
98
|
+
client.post(request).bind do |response|
|
99
|
+
ParseTimeInTransitResponse.call(response: response, request: request)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
78
103
|
def labels(shipment, options:, debug: false)
|
79
104
|
## Method body starts
|
80
105
|
ship_confirm_request_xml = SerializeShipmentConfirmRequest.call(
|
@@ -32,16 +32,24 @@ module FriendlyShipping
|
|
32
32
|
rated_shipment.at('NegotiatedRates/NetSummaryCharges/GrandTotal')
|
33
33
|
)&.last
|
34
34
|
|
35
|
+
rated_shipment_warnings = rated_shipment.css('RatedShipmentWarning').map { |e| e.text.strip }
|
36
|
+
if rated_shipment_warnings.any? { |e| e.match?(/to Residential/) }
|
37
|
+
new_address_type = 'residential'
|
38
|
+
elsif rated_shipment_warnings.any? { |e| e.match?(/to Commercial/) }
|
39
|
+
new_address_type = 'commercial'
|
40
|
+
end
|
41
|
+
|
35
42
|
FriendlyShipping::Rate.new(
|
36
43
|
shipping_method: shipping_method,
|
37
44
|
amounts: { total: total },
|
38
|
-
warnings:
|
45
|
+
warnings: rated_shipment_warnings,
|
39
46
|
errors: [],
|
40
47
|
data: {
|
41
48
|
insurance_price: insurance_price,
|
42
49
|
negotiated_rate: negotiated_rate,
|
43
|
-
days_to_delivery: days_to_delivery
|
44
|
-
|
50
|
+
days_to_delivery: days_to_delivery,
|
51
|
+
new_address_type: new_address_type
|
52
|
+
}.compact
|
45
53
|
)
|
46
54
|
end
|
47
55
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class Ups
|
6
|
+
class ParseTimeInTransitResponse
|
7
|
+
def self.call(request:, response:)
|
8
|
+
parsing_result = ParseXMLResponse.call(response.body, 'TimeInTransitResponse')
|
9
|
+
parsing_result.fmap do |xml|
|
10
|
+
origin_country_code = xml.at('TransitResponse/TransitFrom/AddressArtifactFormat/CountryCode').text
|
11
|
+
timings = xml.root.xpath('//TransitResponse/ServiceSummary').map do |service_summary|
|
12
|
+
# First find the shipping method
|
13
|
+
service_description = service_summary.at('Service/Description').text.gsub(/\s+/, ' ').strip
|
14
|
+
shipping_method = SHIPPING_METHODS.detect do |potential_shipping_method|
|
15
|
+
potential_shipping_method.name.starts_with?(service_description) &&
|
16
|
+
potential_shipping_method.origin_countries.map(&:code).include?(origin_country_code)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Figure out the dates and times
|
20
|
+
delivery_date = service_summary.at('EstimatedArrival/Date').text
|
21
|
+
delivery_time = service_summary.at('EstimatedArrival/Time').text
|
22
|
+
delivery = Time.parse("#{delivery_date} #{delivery_time}")
|
23
|
+
pickup_date = service_summary.at('EstimatedArrival/PickupDate').text
|
24
|
+
pickup_time = service_summary.at('EstimatedArrival/PickupTime').text
|
25
|
+
pickup = Time.parse("#{pickup_date} #{pickup_time}")
|
26
|
+
|
27
|
+
# Some additional data
|
28
|
+
guaranteed = service_summary.at('Guaranteed/Code').text == 'Y'
|
29
|
+
business_transit_days = service_summary.at('EstimatedArrival/BusinessTransitDays').text
|
30
|
+
|
31
|
+
FriendlyShipping::Timing.new(
|
32
|
+
shipping_method: shipping_method,
|
33
|
+
pickup: pickup,
|
34
|
+
delivery: delivery,
|
35
|
+
guaranteed: guaranteed,
|
36
|
+
properties: {
|
37
|
+
business_transit_days: business_transit_days
|
38
|
+
}
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
FriendlyShipping::ApiResult.new(
|
43
|
+
timings,
|
44
|
+
original_request: request,
|
45
|
+
original_response: response
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class Ups
|
6
|
+
class SerializeTimeInTransitRequest
|
7
|
+
def self.call(shipment:, options:)
|
8
|
+
xml_builder = Nokogiri::XML::Builder.new do |xml|
|
9
|
+
xml.TimeInTransitRequest do
|
10
|
+
xml.Request do
|
11
|
+
if options.customer_context
|
12
|
+
xml.TransactionReference do
|
13
|
+
xml.CustomerContext(options.customer_context)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
xml.RequestAction('TimeInTransit')
|
17
|
+
end
|
18
|
+
xml.TransitFrom do
|
19
|
+
xml.AddressArtifactFormat do
|
20
|
+
xml.PoliticalDivision2(shipment.origin.city)
|
21
|
+
xml.PoliticalDivision1(shipment.origin.region.code)
|
22
|
+
xml.CountryCode(shipment.origin.country.code)
|
23
|
+
xml.PostcodePrimaryLow(shipment.origin.zip)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
xml.TransitTo do
|
27
|
+
xml.AddressArtifactFormat do
|
28
|
+
# We no longer include the destination city since it doesn't seem to change the timing
|
29
|
+
# result and can prevent the time in transit request from succeeding when invalid.
|
30
|
+
xml.CountryCode(shipment.destination.country.code)
|
31
|
+
xml.PostcodePrimaryLow(shipment.destination.zip)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
xml.ShipmentWeight do
|
35
|
+
xml.UnitOfMeasurement do
|
36
|
+
xml.Code('LBS')
|
37
|
+
end
|
38
|
+
shipment_weight = shipment.packages.map(&:weight).inject(Measured::Weight(0, :pounds), :+)
|
39
|
+
xml.Weight(shipment_weight.convert_to(:pounds).value.to_f.round(3))
|
40
|
+
end
|
41
|
+
xml.InvoiceLineTotal do
|
42
|
+
xml.CurrencyCode(options.invoice_total.currency.iso_code)
|
43
|
+
xml.MonetaryValue(options.invoice_total.to_f.round(2))
|
44
|
+
end
|
45
|
+
xml.PickupDate(options.pickup.strftime('%Y%m%d'))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
xml_builder.to_xml
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class Ups
|
6
|
+
# Options for getting timing information from UPS
|
7
|
+
# @attribute [Time] pickup When the shipment will be picked up
|
8
|
+
# @attribute [Money] invoice_total How much the items in the shipment are worth
|
9
|
+
# As this is not super important for getting timing information, we use a default
|
10
|
+
# value of 50 USD here.
|
11
|
+
# @attribute [Boolean] documents_only Does the shipment only contain documents?
|
12
|
+
# @attribute [String] customer_context A string to connect request and response in the calling code
|
13
|
+
class TimingOptions
|
14
|
+
attr_reader :pickup,
|
15
|
+
:invoice_total,
|
16
|
+
:documents_only,
|
17
|
+
:customer_context
|
18
|
+
|
19
|
+
def initialize(
|
20
|
+
pickup: Time.now,
|
21
|
+
invoice_total: Money.new(5000, 'USD'),
|
22
|
+
documents_only: false,
|
23
|
+
customer_context: nil
|
24
|
+
)
|
25
|
+
@pickup = pickup
|
26
|
+
@invoice_total = invoice_total
|
27
|
+
@documents_only = documents_only
|
28
|
+
@customer_context = customer_context
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -5,9 +5,12 @@ require 'friendly_shipping/services/usps/shipping_methods'
|
|
5
5
|
require 'friendly_shipping/services/usps/serialize_address_validation_request'
|
6
6
|
require 'friendly_shipping/services/usps/serialize_city_state_lookup_request'
|
7
7
|
require 'friendly_shipping/services/usps/serialize_rate_request'
|
8
|
+
require 'friendly_shipping/services/usps/serialize_time_in_transit_request'
|
8
9
|
require 'friendly_shipping/services/usps/parse_address_validation_response'
|
9
10
|
require 'friendly_shipping/services/usps/parse_city_state_lookup_response'
|
10
11
|
require 'friendly_shipping/services/usps/parse_rate_response'
|
12
|
+
require 'friendly_shipping/services/usps/parse_time_in_transit_response'
|
13
|
+
require 'friendly_shipping/services/usps/timing_options'
|
11
14
|
|
12
15
|
module FriendlyShipping
|
13
16
|
module Services
|
@@ -29,7 +32,8 @@ module FriendlyShipping
|
|
29
32
|
RESOURCES = {
|
30
33
|
address_validation: 'Verify',
|
31
34
|
city_state_lookup: 'CityStateLookup',
|
32
|
-
rates: 'RateV4'
|
35
|
+
rates: 'RateV4',
|
36
|
+
timings: 'SDCGetLocations'
|
33
37
|
}.freeze
|
34
38
|
|
35
39
|
def initialize(login:, test: true, client: HttpClient.new)
|
@@ -66,6 +70,19 @@ module FriendlyShipping
|
|
66
70
|
end
|
67
71
|
end
|
68
72
|
|
73
|
+
# Get timing estimates from USPS
|
74
|
+
#
|
75
|
+
# @param [Physical::Shipment] shipment The shipment we want to estimate. Only destination zip and origin zip are used.
|
76
|
+
# @param [FriendlyShipping::Services::Usps::TimingOptions] options Options for the timing estimate call
|
77
|
+
def timings(shipment, options:, debug: false)
|
78
|
+
timings_request_xml = SerializeTimeInTransitRequest.call(shipment: shipment, options: options, login: login)
|
79
|
+
request = build_request(api: :timings, xml: timings_request_xml, debug: debug)
|
80
|
+
|
81
|
+
client.post(request).bind do |response|
|
82
|
+
ParseTimeInTransitResponse.call(response: response, request: request)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
69
86
|
# Validate an address.
|
70
87
|
# @param [Physical::Location] location The address we want to verify
|
71
88
|
# @return [Result<ApiResult<Array<Physical::Location>>>] The response data from UPS encoded in a
|
@@ -0,0 +1,217 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'friendly_shipping/services/usps/parse_xml_response'
|
4
|
+
require 'friendly_shipping/timing'
|
5
|
+
|
6
|
+
module FriendlyShipping
|
7
|
+
module Services
|
8
|
+
class Usps
|
9
|
+
class ParseTimeInTransitResponse
|
10
|
+
class << self
|
11
|
+
# Parse a response from USPS' time in transit API
|
12
|
+
#
|
13
|
+
# @param [FriendlyShipping::Request] request The request that was used to obtain this Response
|
14
|
+
# @param [FriendlyShipping::Response] response The response that USPS returned
|
15
|
+
# @return [Result<ApiResult<Array<FriendlyShipping::Timing>>>] When successfully parsing, an array of timings in a Success Monad.
|
16
|
+
def call(request:, response:)
|
17
|
+
# Filter out error responses and directly return a failure
|
18
|
+
parsing_result = ParseXMLResponse.call(response.body, 'SDCGetLocationsResponse')
|
19
|
+
parsing_result.fmap do |xml|
|
20
|
+
expedited_commitments = xml.xpath('//Expedited')
|
21
|
+
expedited_timings = parse_expedited_commitment_nodes(expedited_commitments)
|
22
|
+
|
23
|
+
non_expedited_commitments = xml.xpath('//NonExpedited')
|
24
|
+
non_expedited_timings = parse_non_expedited_commitment_nodes(non_expedited_commitments)
|
25
|
+
|
26
|
+
ApiResult.new(
|
27
|
+
expedited_timings + non_expedited_timings,
|
28
|
+
original_request: request,
|
29
|
+
original_response: response
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def parse_expedited_commitment_nodes(expedited_commitment_nodes)
|
37
|
+
# All Expedited Commitments have the same acceptance date
|
38
|
+
effective_acceptance_date = Time.parse(expedited_commitment_nodes.at('EAD').text)
|
39
|
+
expedited_commitment_nodes.xpath('Commitment').map do |commitment_node|
|
40
|
+
shipping_method = SHIPPING_METHODS.detect do |potential_shipping_method|
|
41
|
+
potential_shipping_method.name == MAIL_CLASSES[commitment_node.at('MailClass').text]
|
42
|
+
end
|
43
|
+
commitment_sequence = commitment_node.at('CommitmentSeq').text
|
44
|
+
properties = COMMITMENT_SEQUENCES[commitment_sequence]
|
45
|
+
scheduled_delivery_time = properties.delete(:commitment_time)
|
46
|
+
scheduled_delivery_date = commitment_node.at('SDD').text
|
47
|
+
parsed_delivery_time = Time.parse("#{scheduled_delivery_date} #{scheduled_delivery_time}")
|
48
|
+
guaranteed = commitment_node.at('IsGuaranteed').text == '1'
|
49
|
+
|
50
|
+
FriendlyShipping::Timing.new(
|
51
|
+
shipping_method: shipping_method,
|
52
|
+
pickup: effective_acceptance_date,
|
53
|
+
delivery: parsed_delivery_time,
|
54
|
+
guaranteed: guaranteed,
|
55
|
+
properties: properties
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_non_expedited_commitment_nodes(non_expedited_commitment_nodes)
|
61
|
+
non_expedited_commitment_nodes.map do |commitment_node|
|
62
|
+
shipping_method = SHIPPING_METHODS.detect do |potential_shipping_method|
|
63
|
+
potential_shipping_method.name == MAIL_CLASSES[commitment_node.at('MailClass').text]
|
64
|
+
end
|
65
|
+
# We cannot find a shipping method for Mail Classes 4 and 5 because USPS' docs are not clear
|
66
|
+
next unless shipping_method
|
67
|
+
|
68
|
+
properties = {
|
69
|
+
commitment: commitment_node.at('SvcStdMsg').text,
|
70
|
+
destination_type: NON_EXPEDITED_DESTINATION_TYPES[commitment_node.at('NonExpeditedDestType').text]
|
71
|
+
}
|
72
|
+
scheduled_delivery_date = commitment_node.at('SchedDlvryDate').text
|
73
|
+
parsed_delivery_time = Time.parse(scheduled_delivery_date)
|
74
|
+
effective_acceptance_date = Time.parse(commitment_node.at('EAD').text)
|
75
|
+
|
76
|
+
FriendlyShipping::Timing.new(
|
77
|
+
shipping_method: shipping_method,
|
78
|
+
pickup: effective_acceptance_date,
|
79
|
+
delivery: parsed_delivery_time,
|
80
|
+
guaranteed: false,
|
81
|
+
properties: properties
|
82
|
+
)
|
83
|
+
end.compact
|
84
|
+
end
|
85
|
+
|
86
|
+
# The USPS docs say the following:
|
87
|
+
#
|
88
|
+
# Valid Values:
|
89
|
+
# “0” = All Mail Classes
|
90
|
+
# “1” = Priority Mail Express
|
91
|
+
# “2” = Priority Mail
|
92
|
+
# “3” = First Class Mail
|
93
|
+
# “4” = Marketing Mail
|
94
|
+
# “5” = Periodicals
|
95
|
+
# “6” = Package Services
|
96
|
+
#
|
97
|
+
# However, no shipping methods really map to "Marketing Mail" or "Periodicals".
|
98
|
+
# This will likely be somewhat more work in the future.
|
99
|
+
MAIL_CLASSES = {
|
100
|
+
'1' => 'Priority Mail Express',
|
101
|
+
'2' => 'Priority',
|
102
|
+
'3' => 'First-Class',
|
103
|
+
'6' => 'First-Class Package Service'
|
104
|
+
}.freeze
|
105
|
+
|
106
|
+
# This code carries a few details about the shipment:
|
107
|
+
# - What USPS commits to (1-Day, 2-Day or 3-Day delivery)
|
108
|
+
# - what time the package should arrive
|
109
|
+
# - Whether the package is sent from post office to post office ('Hold For Pickup')
|
110
|
+
# A0110 1-Day at 10:30 AM
|
111
|
+
# B0110 1-Day at 10:30 AM HFPU
|
112
|
+
# A0112 1-Day at 12:00 PM
|
113
|
+
# A0115 1-Day at 3:00 PM
|
114
|
+
# B0115 1-Day at 3:00 PM HFPU
|
115
|
+
# A0210 2-Day at 10:30 AM
|
116
|
+
# A0212 2-Day at 12:00 PM
|
117
|
+
# A0215 2-Day at 3:00 PM
|
118
|
+
# B0210 2-Day at 10:30 AM HFPU
|
119
|
+
# B0215 2-Day at 3:00 PM HFPU
|
120
|
+
# C0100 1-Day Street
|
121
|
+
# C0200 2-Day Street
|
122
|
+
# C0300 3-Day Street
|
123
|
+
# D0100 1-Day PO Box
|
124
|
+
# D0200 2-Day PO Box
|
125
|
+
# D0300 3-Day PO Box
|
126
|
+
# E0100 1-Day HFPU
|
127
|
+
# E0200 2-Day HFPU
|
128
|
+
# E0300 3-Day HFPU
|
129
|
+
COMMITMENT_SEQUENCES = {
|
130
|
+
'A0110' => {
|
131
|
+
commitment: '1-Day',
|
132
|
+
commitment_time: '10:30 AM',
|
133
|
+
},
|
134
|
+
'B0110' => {
|
135
|
+
commitment: '1-Day',
|
136
|
+
commitment_time: '10:30 AM',
|
137
|
+
destination_type: :hold_for_pickup
|
138
|
+
},
|
139
|
+
'A0112' => {
|
140
|
+
commitment: '1-Day',
|
141
|
+
commitment_time: '12:00 PM',
|
142
|
+
},
|
143
|
+
'A0115' => {
|
144
|
+
commitment: '1-Day',
|
145
|
+
commitment_time: '3:00 PM',
|
146
|
+
},
|
147
|
+
'B0115' => {
|
148
|
+
commitment: '1-Day',
|
149
|
+
commitment_time: '3:00 PM',
|
150
|
+
destination_type: :hold_for_pickup
|
151
|
+
},
|
152
|
+
'A0210' => {
|
153
|
+
commitment: '2-Day',
|
154
|
+
commitment_time: '10:30 AM',
|
155
|
+
},
|
156
|
+
'A0212' => {
|
157
|
+
commitment: '2-Day',
|
158
|
+
commitment_time: '12:00 PM',
|
159
|
+
},
|
160
|
+
'A0215' => {
|
161
|
+
commitment: '2-Day',
|
162
|
+
commitment_time: '3:00 PM',
|
163
|
+
},
|
164
|
+
'B0210' => {
|
165
|
+
commitment: '2-Day',
|
166
|
+
commitment_time: '10:30 AM',
|
167
|
+
destination_type: :hold_for_pickup
|
168
|
+
},
|
169
|
+
'C0100' => {
|
170
|
+
commitment: '1-Day',
|
171
|
+
destination_type: :street
|
172
|
+
},
|
173
|
+
'C0200' => {
|
174
|
+
commitment: '2-Day',
|
175
|
+
destination_type: :street
|
176
|
+
},
|
177
|
+
'C0300' => {
|
178
|
+
commitment: '3-Day',
|
179
|
+
destination_type: :street
|
180
|
+
},
|
181
|
+
'D0100' => {
|
182
|
+
commitment: '1-Day',
|
183
|
+
destination_type: :po_box
|
184
|
+
},
|
185
|
+
'D0200' => {
|
186
|
+
commitment: '2-Day',
|
187
|
+
destination_type: :po_box
|
188
|
+
},
|
189
|
+
'D0300' => {
|
190
|
+
commitment: '3-Day',
|
191
|
+
destination_type: :po_box
|
192
|
+
},
|
193
|
+
'E0100' => {
|
194
|
+
commitment: '1-Day',
|
195
|
+
destination_type: :hold_for_pickup
|
196
|
+
},
|
197
|
+
'E0200' => {
|
198
|
+
commitment: '2-Day',
|
199
|
+
destination_type: :hold_for_pickup
|
200
|
+
},
|
201
|
+
'E0300' => {
|
202
|
+
commitment: '3-Day',
|
203
|
+
destination_type: :hold_for_pickup
|
204
|
+
},
|
205
|
+
}.freeze
|
206
|
+
|
207
|
+
# Things are different for non-expedited shipping methods.
|
208
|
+
NON_EXPEDITED_DESTINATION_TYPES = {
|
209
|
+
'1' => :street,
|
210
|
+
'2' => :po_box,
|
211
|
+
'3' => :hold_for_pickup
|
212
|
+
}.freeze
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class Usps
|
6
|
+
class SerializeTimeInTransitRequest
|
7
|
+
def self.call(shipment:, options:, login:)
|
8
|
+
xml_builder = Nokogiri::XML::Builder.new do |xml|
|
9
|
+
xml.SDCGetLocationsRequest(USERID: login) do
|
10
|
+
xml.MailClass 0 # all mail classes
|
11
|
+
xml.OriginZIP shipment.origin.zip
|
12
|
+
xml.DestinationZIP shipment.destination.zip
|
13
|
+
xml.AcceptDate options.pickup.strftime('%d-%b-%Y')
|
14
|
+
xml.NonEMDetail true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
xml_builder.to_xml
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class Usps
|
6
|
+
class TimingOptions
|
7
|
+
attr_reader :pickup, :shipping_method
|
8
|
+
|
9
|
+
def initialize(
|
10
|
+
pickup: Time.now,
|
11
|
+
shipping_method: nil
|
12
|
+
)
|
13
|
+
@pickup = pickup
|
14
|
+
@shipping_method = shipping_method
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
class Timing
|
5
|
+
attr_reader :shipping_method,
|
6
|
+
:pickup,
|
7
|
+
:delivery,
|
8
|
+
:guaranteed,
|
9
|
+
:properties
|
10
|
+
|
11
|
+
def initialize(
|
12
|
+
shipping_method:,
|
13
|
+
pickup:,
|
14
|
+
delivery:,
|
15
|
+
guaranteed: false,
|
16
|
+
properties: {}
|
17
|
+
)
|
18
|
+
@shipping_method = shipping_method
|
19
|
+
@pickup = pickup
|
20
|
+
@delivery = delivery
|
21
|
+
@guaranteed = guaranteed
|
22
|
+
@properties = properties
|
23
|
+
end
|
24
|
+
|
25
|
+
def time_in_transit
|
26
|
+
delivery - pickup
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: friendly_shipping
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Meyerhoff
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-12-
|
11
|
+
date: 2019-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: data_uri
|
@@ -194,6 +194,7 @@ files:
|
|
194
194
|
- lib/friendly_shipping/services/ups/parse_rate_response.rb
|
195
195
|
- lib/friendly_shipping/services/ups/parse_shipment_accept_response.rb
|
196
196
|
- lib/friendly_shipping/services/ups/parse_shipment_confirm_response.rb
|
197
|
+
- lib/friendly_shipping/services/ups/parse_time_in_transit_response.rb
|
197
198
|
- lib/friendly_shipping/services/ups/parse_void_shipment_response.rb
|
198
199
|
- lib/friendly_shipping/services/ups/parse_xml_response.rb
|
199
200
|
- lib/friendly_shipping/services/ups/serialize_access_request.rb
|
@@ -205,8 +206,10 @@ files:
|
|
205
206
|
- lib/friendly_shipping/services/ups/serialize_shipment_accept_request.rb
|
206
207
|
- lib/friendly_shipping/services/ups/serialize_shipment_address_snippet.rb
|
207
208
|
- lib/friendly_shipping/services/ups/serialize_shipment_confirm_request.rb
|
209
|
+
- lib/friendly_shipping/services/ups/serialize_time_in_transit_request.rb
|
208
210
|
- lib/friendly_shipping/services/ups/serialize_void_shipment_request.rb
|
209
211
|
- lib/friendly_shipping/services/ups/shipping_methods.rb
|
212
|
+
- lib/friendly_shipping/services/ups/timing_options.rb
|
210
213
|
- lib/friendly_shipping/services/ups_freight.rb
|
211
214
|
- lib/friendly_shipping/services/ups_freight/generate_commodity_information.rb
|
212
215
|
- lib/friendly_shipping/services/ups_freight/generate_freight_rate_request_hash.rb
|
@@ -225,13 +228,17 @@ files:
|
|
225
228
|
- lib/friendly_shipping/services/usps/parse_city_state_lookup_response.rb
|
226
229
|
- lib/friendly_shipping/services/usps/parse_package_rate.rb
|
227
230
|
- lib/friendly_shipping/services/usps/parse_rate_response.rb
|
231
|
+
- lib/friendly_shipping/services/usps/parse_time_in_transit_response.rb
|
228
232
|
- lib/friendly_shipping/services/usps/parse_xml_response.rb
|
229
233
|
- lib/friendly_shipping/services/usps/serialize_address_validation_request.rb
|
230
234
|
- lib/friendly_shipping/services/usps/serialize_city_state_lookup_request.rb
|
231
235
|
- lib/friendly_shipping/services/usps/serialize_rate_request.rb
|
236
|
+
- lib/friendly_shipping/services/usps/serialize_time_in_transit_request.rb
|
232
237
|
- lib/friendly_shipping/services/usps/shipping_methods.rb
|
238
|
+
- lib/friendly_shipping/services/usps/timing_options.rb
|
233
239
|
- lib/friendly_shipping/shipment_options.rb
|
234
240
|
- lib/friendly_shipping/shipping_method.rb
|
241
|
+
- lib/friendly_shipping/timing.rb
|
235
242
|
- lib/friendly_shipping/types.rb
|
236
243
|
- lib/friendly_shipping/version.rb
|
237
244
|
homepage: https://github.com/friendly_cart/friendly_shipping
|