friendly_shipping 0.10.1 → 0.10.2
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/Gemfile +1 -0
- data/README.md +16 -16
- data/lib/friendly_shipping/services/rl/parse_rate_quote_response.rb +3 -3
- data/lib/friendly_shipping/services/ups_json/parse_money_hash.rb +1 -0
- data/lib/friendly_shipping/services/usps_international/parse_rate_response.rb +3 -3
- data/lib/friendly_shipping/services/{usps → usps_international}/parse_xml_response.rb +1 -1
- data/lib/friendly_shipping/services/usps_international/serialize_rate_request.rb +1 -1
- data/lib/friendly_shipping/services/usps_ship/parse_rate_estimates_response.rb +1 -1
- data/lib/friendly_shipping/version.rb +1 -1
- metadata +3 -51
- data/lib/friendly_shipping/services/ups/label.rb +0 -20
- data/lib/friendly_shipping/services/ups/label_billing_options.rb +0 -41
- data/lib/friendly_shipping/services/ups/label_item_options.rb +0 -75
- data/lib/friendly_shipping/services/ups/label_options.rb +0 -174
- data/lib/friendly_shipping/services/ups/label_package_options.rb +0 -49
- data/lib/friendly_shipping/services/ups/parse_address_classification_response.rb +0 -29
- data/lib/friendly_shipping/services/ups/parse_address_validation_response.rb +0 -53
- data/lib/friendly_shipping/services/ups/parse_city_state_lookup_response.rb +0 -33
- data/lib/friendly_shipping/services/ups/parse_modifier_element.rb +0 -29
- data/lib/friendly_shipping/services/ups/parse_money_element.rb +0 -128
- data/lib/friendly_shipping/services/ups/parse_rate_response.rb +0 -101
- data/lib/friendly_shipping/services/ups/parse_shipment_accept_response.rb +0 -77
- data/lib/friendly_shipping/services/ups/parse_shipment_confirm_response.rb +0 -24
- data/lib/friendly_shipping/services/ups/parse_time_in_transit_response.rb +0 -56
- data/lib/friendly_shipping/services/ups/parse_void_shipment_response.rb +0 -24
- data/lib/friendly_shipping/services/ups/parse_xml_response.rb +0 -50
- data/lib/friendly_shipping/services/ups/rate_estimate_options.rb +0 -111
- data/lib/friendly_shipping/services/ups/rate_estimate_package_options.rb +0 -22
- data/lib/friendly_shipping/services/ups/serialize_access_request.rb +0 -20
- data/lib/friendly_shipping/services/ups/serialize_address_snippet.rb +0 -60
- data/lib/friendly_shipping/services/ups/serialize_address_validation_request.rb +0 -40
- data/lib/friendly_shipping/services/ups/serialize_city_state_lookup_request.rb +0 -26
- data/lib/friendly_shipping/services/ups/serialize_package_node.rb +0 -75
- data/lib/friendly_shipping/services/ups/serialize_rating_service_selection_request.rb +0 -98
- data/lib/friendly_shipping/services/ups/serialize_shipment_accept_request.rb +0 -27
- data/lib/friendly_shipping/services/ups/serialize_shipment_address_snippet.rb +0 -21
- data/lib/friendly_shipping/services/ups/serialize_shipment_confirm_request.rb +0 -285
- data/lib/friendly_shipping/services/ups/serialize_time_in_transit_request.rb +0 -56
- data/lib/friendly_shipping/services/ups/serialize_void_shipment_request.rb +0 -21
- data/lib/friendly_shipping/services/ups/shipping_methods.rb +0 -111
- data/lib/friendly_shipping/services/ups/timing_options.rb +0 -33
- data/lib/friendly_shipping/services/ups.rb +0 -218
- data/lib/friendly_shipping/services/usps/choose_package_rate.rb +0 -40
- data/lib/friendly_shipping/services/usps/machinable_package.rb +0 -50
- data/lib/friendly_shipping/services/usps/parse_address_validation_response.rb +0 -43
- data/lib/friendly_shipping/services/usps/parse_city_state_lookup_response.rb +0 -41
- data/lib/friendly_shipping/services/usps/parse_package_rate.rb +0 -159
- data/lib/friendly_shipping/services/usps/parse_rate_response.rb +0 -86
- data/lib/friendly_shipping/services/usps/parse_time_in_transit_response.rb +0 -240
- data/lib/friendly_shipping/services/usps/rate_estimate_options.rb +0 -26
- data/lib/friendly_shipping/services/usps/rate_estimate_package_options.rb +0 -66
- data/lib/friendly_shipping/services/usps/serialize_address_validation_request.rb +0 -25
- data/lib/friendly_shipping/services/usps/serialize_city_state_lookup_request.rb +0 -20
- data/lib/friendly_shipping/services/usps/serialize_rate_request.rb +0 -83
- data/lib/friendly_shipping/services/usps/serialize_time_in_transit_request.rb +0 -22
- data/lib/friendly_shipping/services/usps/shipping_methods.rb +0 -66
- data/lib/friendly_shipping/services/usps/timing_options.rb +0 -19
- data/lib/friendly_shipping/services/usps.rb +0 -115
@@ -1,240 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FriendlyShipping
|
4
|
-
module Services
|
5
|
-
class Usps
|
6
|
-
class ParseTimeInTransitResponse
|
7
|
-
class << self
|
8
|
-
# Parse a response from USPS' time in transit API
|
9
|
-
#
|
10
|
-
# @param [FriendlyShipping::Request] request The request that was used to obtain this Response
|
11
|
-
# @param [FriendlyShipping::Response] response The response that USPS returned
|
12
|
-
# @return [Result<ApiResult<Array<FriendlyShipping::Timing>>>] When successfully parsing, an array of timings in a Success Monad.
|
13
|
-
def call(request:, response:)
|
14
|
-
# Filter out error responses and directly return a failure
|
15
|
-
parsing_result = ParseXMLResponse.call(
|
16
|
-
request: request,
|
17
|
-
response: response,
|
18
|
-
expected_root_tag: 'SDCGetLocationsResponse'
|
19
|
-
)
|
20
|
-
parsing_result.fmap do |xml|
|
21
|
-
expedited_commitments = xml.xpath('//Expedited')
|
22
|
-
expedited_timings = parse_expedited_commitment_nodes(expedited_commitments)
|
23
|
-
|
24
|
-
non_expedited_commitments = xml.xpath('//NonExpedited')
|
25
|
-
non_expedited_timings = parse_non_expedited_commitment_nodes(non_expedited_commitments)
|
26
|
-
|
27
|
-
ApiResult.new(
|
28
|
-
expedited_timings + non_expedited_timings,
|
29
|
-
original_request: request,
|
30
|
-
original_response: response
|
31
|
-
)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def parse_expedited_commitment_nodes(expedited_commitment_nodes)
|
38
|
-
return [] if expedited_commitment_nodes.empty?
|
39
|
-
|
40
|
-
# All Expedited Commitments have the same acceptance date
|
41
|
-
# However, sometimes that date is invalid.
|
42
|
-
effective_acceptance_date = [
|
43
|
-
Time.parse(expedited_commitment_nodes.at('EAD').text),
|
44
|
-
Time.parse(expedited_commitment_nodes.document.at('AcceptDate').text)
|
45
|
-
].max
|
46
|
-
expedited_commitment_nodes.xpath('Commitment').map do |commitment_node|
|
47
|
-
shipping_method = SHIPPING_METHODS.detect do |potential_shipping_method|
|
48
|
-
potential_shipping_method.name == MAIL_CLASSES[commitment_node.at('MailClass').text]
|
49
|
-
end
|
50
|
-
commitment_sequence = commitment_node.at('CommitmentSeq').text
|
51
|
-
data = COMMITMENT_SEQUENCES[commitment_sequence]
|
52
|
-
next unless data # Sometimes USPS returns an invalid CommitmentSeq
|
53
|
-
|
54
|
-
scheduled_delivery_time = data.delete(:commitment_time)
|
55
|
-
scheduled_delivery_date = commitment_node.at('SDD').text
|
56
|
-
parsed_delivery_time = Time.parse("#{scheduled_delivery_date} #{scheduled_delivery_time}")
|
57
|
-
guaranteed = commitment_node.at('IsGuaranteed').text == '1'
|
58
|
-
|
59
|
-
FriendlyShipping::Timing.new(
|
60
|
-
shipping_method: shipping_method,
|
61
|
-
pickup: effective_acceptance_date,
|
62
|
-
delivery: parsed_delivery_time,
|
63
|
-
guaranteed: guaranteed,
|
64
|
-
data: data
|
65
|
-
)
|
66
|
-
end.compact
|
67
|
-
end
|
68
|
-
|
69
|
-
def parse_non_expedited_commitment_nodes(non_expedited_commitment_nodes)
|
70
|
-
non_expedited_commitment_nodes.map do |commitment_node|
|
71
|
-
shipping_method = SHIPPING_METHODS.detect do |potential_shipping_method|
|
72
|
-
potential_shipping_method.name == MAIL_CLASSES[commitment_node.at('MailClass').text]
|
73
|
-
end
|
74
|
-
# We cannot find a shipping method for Mail Classes 4 and 5 because USPS' docs are not clear
|
75
|
-
next unless shipping_method
|
76
|
-
|
77
|
-
warning_text = commitment_node.xpath('HFPU//NonExpeditedTransMsg/Msg')&.text
|
78
|
-
warning = warning_text unless warning_text.empty?
|
79
|
-
|
80
|
-
data = {
|
81
|
-
commitment: commitment_node.at('SvcStdMsg')&.text,
|
82
|
-
destination_type: NON_EXPEDITED_DESTINATION_TYPES[commitment_node.at('NonExpeditedDestType').text],
|
83
|
-
warning: warning
|
84
|
-
}.compact
|
85
|
-
|
86
|
-
scheduled_delivery_date = commitment_node.at('SchedDlvryDate')&.text
|
87
|
-
parsed_delivery_time = Time.parse(scheduled_delivery_date) if scheduled_delivery_date
|
88
|
-
effective_acceptance_date = Time.parse(commitment_node.at('EAD').text)
|
89
|
-
|
90
|
-
FriendlyShipping::Timing.new(
|
91
|
-
shipping_method: shipping_method,
|
92
|
-
pickup: effective_acceptance_date,
|
93
|
-
delivery: parsed_delivery_time,
|
94
|
-
guaranteed: false,
|
95
|
-
data: data
|
96
|
-
)
|
97
|
-
end.compact
|
98
|
-
end
|
99
|
-
|
100
|
-
# The USPS docs say the following:
|
101
|
-
#
|
102
|
-
# Valid Values:
|
103
|
-
# “0” = All Mail Classes
|
104
|
-
# “1” = Priority Mail Express
|
105
|
-
# “2” = Priority Mail
|
106
|
-
# “3” = First-Class - replaced by Ground Advantage (up to 15.999 oz)
|
107
|
-
# “4” = Marketing Mail
|
108
|
-
# “5” = Periodicals
|
109
|
-
# “6” = Package Services
|
110
|
-
# “7” = Parcel Select Ground - replaced by Ground Advantage (1-70 lbs)
|
111
|
-
# “9” = Ground Advantage (1-70 lbs)
|
112
|
-
#
|
113
|
-
# However, no shipping methods really map to "Marketing Mail" or "Periodicals".
|
114
|
-
# This will likely be somewhat more work in the future.
|
115
|
-
MAIL_CLASSES = {
|
116
|
-
'1' => 'Priority Mail Express',
|
117
|
-
'2' => 'Priority Mail',
|
118
|
-
'3' => 'First-Class',
|
119
|
-
'6' => 'Package Services',
|
120
|
-
'7' => 'Parcel Select Ground',
|
121
|
-
'9' => 'Ground Advantage'
|
122
|
-
}.freeze
|
123
|
-
|
124
|
-
# This code carries a few details about the shipment:
|
125
|
-
# - What USPS commits to (1-Day, 2-Day or 3-Day delivery)
|
126
|
-
# - what time the package should arrive
|
127
|
-
# - Whether the package is sent from post office to post office ('Hold For Pickup')
|
128
|
-
# A0110 1-Day at 10:30 AM
|
129
|
-
# B0110 1-Day at 10:30 AM HFPU
|
130
|
-
# A0112 1-Day at 12:00 PM
|
131
|
-
# A0115 1-Day at 3:00 PM
|
132
|
-
# B0115 1-Day at 3:00 PM HFPU
|
133
|
-
# A0210 2-Day at 10:30 AM
|
134
|
-
# A0212 2-Day at 12:00 PM
|
135
|
-
# A0215 2-Day at 3:00 PM
|
136
|
-
# B0210 2-Day at 10:30 AM HFPU
|
137
|
-
# B0215 2-Day at 3:00 PM HFPU
|
138
|
-
# C0100 1-Day Street
|
139
|
-
# C0200 2-Day Street
|
140
|
-
# C0300 3-Day Street
|
141
|
-
# D0100 1-Day PO Box
|
142
|
-
# D0200 2-Day PO Box
|
143
|
-
# D0300 3-Day PO Box
|
144
|
-
# E0100 1-Day HFPU
|
145
|
-
# E0200 2-Day HFPU
|
146
|
-
# E0300 3-Day HFPU
|
147
|
-
COMMITMENT_SEQUENCES = {
|
148
|
-
'A0110' => {
|
149
|
-
commitment: '1-Day',
|
150
|
-
commitment_time: '10:30 AM',
|
151
|
-
},
|
152
|
-
'B0110' => {
|
153
|
-
commitment: '1-Day',
|
154
|
-
commitment_time: '10:30 AM',
|
155
|
-
destination_type: :hold_for_pickup
|
156
|
-
},
|
157
|
-
'A0112' => {
|
158
|
-
commitment: '1-Day',
|
159
|
-
commitment_time: '12:00 PM',
|
160
|
-
},
|
161
|
-
'A0115' => {
|
162
|
-
commitment: '1-Day',
|
163
|
-
commitment_time: '3:00 PM',
|
164
|
-
},
|
165
|
-
'B0115' => {
|
166
|
-
commitment: '1-Day',
|
167
|
-
commitment_time: '3:00 PM',
|
168
|
-
destination_type: :hold_for_pickup
|
169
|
-
},
|
170
|
-
'A0210' => {
|
171
|
-
commitment: '2-Day',
|
172
|
-
commitment_time: '10:30 AM',
|
173
|
-
},
|
174
|
-
'A0212' => {
|
175
|
-
commitment: '2-Day',
|
176
|
-
commitment_time: '12:00 PM',
|
177
|
-
},
|
178
|
-
'A0215' => {
|
179
|
-
commitment: '2-Day',
|
180
|
-
commitment_time: '3:00 PM',
|
181
|
-
},
|
182
|
-
'B0210' => {
|
183
|
-
commitment: '2-Day',
|
184
|
-
commitment_time: '10:30 AM',
|
185
|
-
destination_type: :hold_for_pickup
|
186
|
-
},
|
187
|
-
'B0215' => {
|
188
|
-
commitment: '2-Day',
|
189
|
-
commitment_time: '3:00 PM',
|
190
|
-
destination_type: :hold_for_pickup
|
191
|
-
},
|
192
|
-
'C0100' => {
|
193
|
-
commitment: '1-Day',
|
194
|
-
destination_type: :street
|
195
|
-
},
|
196
|
-
'C0200' => {
|
197
|
-
commitment: '2-Day',
|
198
|
-
destination_type: :street
|
199
|
-
},
|
200
|
-
'C0300' => {
|
201
|
-
commitment: '3-Day',
|
202
|
-
destination_type: :street
|
203
|
-
},
|
204
|
-
'D0100' => {
|
205
|
-
commitment: '1-Day',
|
206
|
-
destination_type: :po_box
|
207
|
-
},
|
208
|
-
'D0200' => {
|
209
|
-
commitment: '2-Day',
|
210
|
-
destination_type: :po_box
|
211
|
-
},
|
212
|
-
'D0300' => {
|
213
|
-
commitment: '3-Day',
|
214
|
-
destination_type: :po_box
|
215
|
-
},
|
216
|
-
'E0100' => {
|
217
|
-
commitment: '1-Day',
|
218
|
-
destination_type: :hold_for_pickup
|
219
|
-
},
|
220
|
-
'E0200' => {
|
221
|
-
commitment: '2-Day',
|
222
|
-
destination_type: :hold_for_pickup
|
223
|
-
},
|
224
|
-
'E0300' => {
|
225
|
-
commitment: '3-Day',
|
226
|
-
destination_type: :hold_for_pickup
|
227
|
-
},
|
228
|
-
}.freeze
|
229
|
-
|
230
|
-
# Things are different for non-expedited shipping methods.
|
231
|
-
NON_EXPEDITED_DESTINATION_TYPES = {
|
232
|
-
'1' => :street,
|
233
|
-
'2' => :po_box,
|
234
|
-
'3' => :hold_for_pickup
|
235
|
-
}.freeze
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FriendlyShipping
|
4
|
-
module Services
|
5
|
-
# Option container for rating a shipment via USPS
|
6
|
-
#
|
7
|
-
# Context: The shipment object we're trying to get results for
|
8
|
-
# USPS returns rates on a package-by-package basis, so the options for obtaining rates are
|
9
|
-
# set on the [FriendlyShipping/RateEstimateObject] hash. The possible options are:
|
10
|
-
|
11
|
-
# @param [Physical::ShippingMethod] shipping_method The shipping method ("service" in USPS parlance) we want
|
12
|
-
# to get rates for.
|
13
|
-
# @param [Boolean] commercial_pricing Whether we prefer commercial pricing results or retail results
|
14
|
-
# @param [Boolean] hold_for_pickup Whether we want a rate with Hold For Pickup Service
|
15
|
-
class Usps
|
16
|
-
class RateEstimateOptions < FriendlyShipping::ShipmentOptions
|
17
|
-
def initialize(
|
18
|
-
package_options_class: FriendlyShipping::Services::Usps::RateEstimatePackageOptions,
|
19
|
-
**kwargs
|
20
|
-
)
|
21
|
-
super(**kwargs.reverse_merge(package_options_class: package_options_class))
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FriendlyShipping
|
4
|
-
module Services
|
5
|
-
# Options for one package when rating
|
6
|
-
#
|
7
|
-
# @param [Symbol] box_name The type of box we want to get rates for. Has to be one of the keys
|
8
|
-
# of FriendlyShipping::Services::Usps::CONTAINERS.
|
9
|
-
# @param [Boolean] transmit_dimensions Indicate whether the reuqest should include the package dimensionals.
|
10
|
-
# @param [Boolean] rectangular Indicate whether the package is rectangular.
|
11
|
-
# @param [Boolean] return_dimensional_weight Indicate whether the response should include dimensional weight.
|
12
|
-
# @param [Boolean] return_fees Indicate whether the response should include fees.
|
13
|
-
class Usps
|
14
|
-
class RateEstimatePackageOptions < FriendlyShipping::PackageOptions
|
15
|
-
attr_reader :box_name,
|
16
|
-
:commercial_pricing,
|
17
|
-
:first_class_mail_type,
|
18
|
-
:hold_for_pickup,
|
19
|
-
:shipping_method,
|
20
|
-
:transmit_dimensions,
|
21
|
-
:rectangular,
|
22
|
-
:return_dimensional_weight,
|
23
|
-
:return_fees
|
24
|
-
|
25
|
-
def initialize(**kwargs)
|
26
|
-
box_name = value_or_default(:box_name, :variable, kwargs)
|
27
|
-
@box_name = CONTAINERS.key?(box_name) ? box_name : :variable
|
28
|
-
@commercial_pricing = value_or_default(:commercial_pricing, false, kwargs)
|
29
|
-
@first_class_mail_type = kwargs.delete(:first_class_mail_type)
|
30
|
-
@hold_for_pickup = value_or_default(:hold_for_pickup, false, kwargs)
|
31
|
-
@shipping_method = kwargs.delete(:shipping_method)
|
32
|
-
@transmit_dimensions = value_or_default(:transmit_dimensions, true, kwargs)
|
33
|
-
@rectangular = value_or_default(:rectangular, true, kwargs)
|
34
|
-
@return_dimensional_weight = value_or_default(:return_dimensional_weight, true, kwargs)
|
35
|
-
@return_fees = value_or_default(:return_fees, false, kwargs)
|
36
|
-
super(**kwargs)
|
37
|
-
end
|
38
|
-
|
39
|
-
def container_code
|
40
|
-
CONTAINERS.fetch(box_name)
|
41
|
-
end
|
42
|
-
|
43
|
-
# @return [String, nil]
|
44
|
-
def first_class_mail_type_code
|
45
|
-
if %i[parcel package_service package_service_retail].include?(first_class_mail_type)
|
46
|
-
warn "[DEPRECATION] First Class `:#{first_class_mail_type}` has been replaced by Ground Advantage."
|
47
|
-
else
|
48
|
-
FIRST_CLASS_MAIL_TYPES[first_class_mail_type]
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def service_code
|
53
|
-
return 'ALL' unless shipping_method
|
54
|
-
|
55
|
-
# Cubic shipping methods don't have HFP or COMMERCIAL modifiers
|
56
|
-
return shipping_method.service_code if shipping_method.service_code =~ /CUBIC/
|
57
|
-
|
58
|
-
service_code = [shipping_method.service_code]
|
59
|
-
service_code << 'HFP' if hold_for_pickup
|
60
|
-
service_code << 'COMMERCIAL' if commercial_pricing
|
61
|
-
service_code.join(' ')
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FriendlyShipping
|
4
|
-
module Services
|
5
|
-
class Usps
|
6
|
-
class SerializeAddressValidationRequest
|
7
|
-
def self.call(location:, login:)
|
8
|
-
xml_builder = Nokogiri::XML::Builder.new do |xml|
|
9
|
-
xml.AddressValidateRequest USERID: login do
|
10
|
-
xml.Address do
|
11
|
-
xml.Address1 location.address2 # USPS swaps Address1 and Address2 in the request
|
12
|
-
xml.Address2 location.address1
|
13
|
-
xml.City location.city
|
14
|
-
xml.State location.region.code
|
15
|
-
xml.Zip5 location.zip
|
16
|
-
xml.Zip4
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
xml_builder.to_xml
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FriendlyShipping
|
4
|
-
module Services
|
5
|
-
class Usps
|
6
|
-
class SerializeCityStateLookupRequest
|
7
|
-
def self.call(location:, login:)
|
8
|
-
xml_builder = Nokogiri::XML::Builder.new do |xml|
|
9
|
-
xml.CityStateLookupRequest(USERID: login) do
|
10
|
-
xml.ZipCode do
|
11
|
-
xml.Zip5 location.zip
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
xml_builder.to_xml
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FriendlyShipping
|
4
|
-
module Services
|
5
|
-
class Usps
|
6
|
-
class SerializeRateRequest
|
7
|
-
MAX_REGULAR_PACKAGE_SIDE = Measured::Length(12, :inches)
|
8
|
-
|
9
|
-
class << self
|
10
|
-
# @param [Physical::Shipment] shipment The shipment we want to get rates for
|
11
|
-
# shipment.packages[0].properties[:box_name] Can be :variable or a
|
12
|
-
# flat rate container defined in CONTAINERS.
|
13
|
-
# @param [String] login The USPS login code
|
14
|
-
# @param [FriendlyShipping::Services::Usps::RateEstimateOptions] options The options
|
15
|
-
# object to use with this request.
|
16
|
-
# @return [Array<FriendlyShipping::Rate>] A set of Rates that this package may be sent with
|
17
|
-
def call(shipment:, login:, options:)
|
18
|
-
xml_builder = Nokogiri::XML::Builder.new do |xml|
|
19
|
-
xml.RateV4Request('USERID' => login) do
|
20
|
-
shipment.packages.each_with_index do |package, index|
|
21
|
-
package_options = options.options_for_package(package)
|
22
|
-
xml.Package('ID' => index) do
|
23
|
-
xml.Service(package_options.service_code)
|
24
|
-
if package_options.first_class_mail_type && package_options.first_class_mail_type_code
|
25
|
-
xml.FirstClassMailType(package_options.first_class_mail_type_code)
|
26
|
-
end
|
27
|
-
xml.ZipOrigination(shipment.origin.zip)
|
28
|
-
xml.ZipDestination(shipment.destination.zip)
|
29
|
-
xml.Pounds(0)
|
30
|
-
xml.Ounces(ounces_for(package))
|
31
|
-
size_code = size_code_for(package)
|
32
|
-
xml.Container(package_options.container_code)
|
33
|
-
xml.Size(size_code)
|
34
|
-
if package_options.transmit_dimensions && package_options.container_code == 'VARIABLE'
|
35
|
-
xml.Width("%<width>0.2f" % { width: package.width.convert_to(:inches).value.to_f })
|
36
|
-
xml.Length("%<length>0.2f" % { length: package.length.convert_to(:inches).value.to_f })
|
37
|
-
xml.Height("%<height>0.2f" % { height: package.height.convert_to(:inches).value.to_f })
|
38
|
-
|
39
|
-
# When girth is present, the package is treated as non-rectangular
|
40
|
-
# when calculating dimensional weight. This results in a smaller
|
41
|
-
# dimensional weight than a rectangular package would have.
|
42
|
-
unless package_options.rectangular
|
43
|
-
xml.Girth("%<girth>0.2f" % { girth: girth(package) })
|
44
|
-
end
|
45
|
-
end
|
46
|
-
xml.Machinable(machinable(package))
|
47
|
-
xml.ReturnDimensionalWeight(true) if package_options.return_dimensional_weight
|
48
|
-
xml.ReturnFees(true) if package_options.return_fees
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
xml_builder.to_xml
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def machinable(package)
|
59
|
-
MachinablePackage.new(package).machinable? ? 'TRUE' : 'FALSE'
|
60
|
-
end
|
61
|
-
|
62
|
-
def size_code_for(package)
|
63
|
-
if package.dimensions.max <= MAX_REGULAR_PACKAGE_SIDE
|
64
|
-
'REGULAR'
|
65
|
-
else
|
66
|
-
'LARGE'
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def ounces_for(package)
|
71
|
-
ounces = package.weight.convert_to(:ounces).value.to_f.round(2).ceil
|
72
|
-
ounces == 16 ? 15.999 : [ounces, 1].max
|
73
|
-
end
|
74
|
-
|
75
|
-
def girth(package)
|
76
|
-
width, length = package.dimensions.sort.first(2)
|
77
|
-
(width.scale(2) + length.scale(2)).convert_to(:inches).value.to_f
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,22 +0,0 @@
|
|
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
|
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FriendlyShipping
|
4
|
-
module Services
|
5
|
-
class Usps
|
6
|
-
CONTAINERS = {
|
7
|
-
variable: 'VARIABLE',
|
8
|
-
large_flat_rate_box: 'LG FLAT RATE BOX',
|
9
|
-
medium_flat_rate_box: 'MD FLAT RATE BOX',
|
10
|
-
small_flat_rate_box: 'SM FLAT RATE BOX',
|
11
|
-
regional_rate_box_a: 'REGIONALRATEBOXA',
|
12
|
-
regional_rate_box_b: 'REGIONALRATEBOXB',
|
13
|
-
flat_rate_envelope: 'FLAT RATE ENVELOPE',
|
14
|
-
legal_flat_rate_envelope: 'LEGAL FLAT RATE ENVELOPE',
|
15
|
-
padded_flat_rate_envelope: 'PADDED FLAT RATE ENVELOPE',
|
16
|
-
gift_card_flat_rate_envelope: 'GIFT CARD FLAT RATE ENVELOPE',
|
17
|
-
window_flat_rate_envelope: 'WINDOW FLAT RATE ENVELOPE',
|
18
|
-
small_flat_rate_envelope: 'SM FLAT RATE ENVELOPE',
|
19
|
-
cubic_soft_pack: 'CUBIC SOFT PACK',
|
20
|
-
cubic_parcels: 'CUBIC PARCELS'
|
21
|
-
}.freeze
|
22
|
-
|
23
|
-
FIRST_CLASS_MAIL_TYPES = {
|
24
|
-
letter: 'LETTER',
|
25
|
-
flat: 'FLAT',
|
26
|
-
parcel: 'PARCEL', # @deprecated
|
27
|
-
post_card: 'POSTCARD',
|
28
|
-
large_post_card: 'LARGE POSTCARD',
|
29
|
-
package_service: 'PACKAGE SERVICE', # @deprecated
|
30
|
-
package_service_retail: 'PACKAGE SERVICE RETAIL' # @deprecated
|
31
|
-
}.freeze
|
32
|
-
|
33
|
-
CLASS_IDS = {
|
34
|
-
priority_mail_express: {
|
35
|
-
standard: '3',
|
36
|
-
hold_for_pickup: '2',
|
37
|
-
sunday_holiday_delivery: '23'
|
38
|
-
},
|
39
|
-
priority_mail_cubic: '999',
|
40
|
-
ground_advantage: '1058'
|
41
|
-
}.freeze
|
42
|
-
|
43
|
-
SHIPPING_METHODS = [
|
44
|
-
['FIRST CLASS', 'First-Class'],
|
45
|
-
['GROUND ADVANTAGE', 'Ground Advantage', CLASS_IDS[:ground_advantage]],
|
46
|
-
['PACKAGE SERVICES', 'Package Services'],
|
47
|
-
['PRIORITY', 'Priority Mail'],
|
48
|
-
['PRIORITY MAIL EXPRESS', 'Priority Mail Express', CLASS_IDS[:priority_mail_express].values],
|
49
|
-
['PRIORITY MAIL CUBIC', 'Priority Mail Cubic', CLASS_IDS[:priority_mail_cubic]],
|
50
|
-
['STANDARD POST', 'Standard Post'],
|
51
|
-
['RETAIL GROUND', 'Retail Ground'],
|
52
|
-
['MEDIA MAIL', 'Media Mail'],
|
53
|
-
['LIBRARY MAIL', 'Library Mail'],
|
54
|
-
].map do |code, name, class_ids|
|
55
|
-
FriendlyShipping::ShippingMethod.new(
|
56
|
-
origin_countries: [Carmen::Country.coded('US')],
|
57
|
-
name: name,
|
58
|
-
service_code: code,
|
59
|
-
domestic: true,
|
60
|
-
international: false,
|
61
|
-
data: { class_ids: Array(class_ids) }
|
62
|
-
)
|
63
|
-
end.freeze
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,19 +0,0 @@
|
|
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
|