friendly_shipping 0.5.2 → 0.5.3
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/.rubocop-relaxed.yml +3 -4
- data/.rubocop.yml +9 -0
- data/CHANGELOG.md +5 -1
- data/friendly_shipping.gemspec +10 -10
- data/lib/friendly_shipping/api_failure.rb +2 -15
- data/lib/friendly_shipping/services/ups/parse_address_classification_response.rb +5 -2
- data/lib/friendly_shipping/services/ups/parse_address_validation_response.rb +5 -2
- data/lib/friendly_shipping/services/ups/parse_city_state_lookup_response.rb +5 -2
- data/lib/friendly_shipping/services/ups/parse_rate_response.rb +5 -1
- data/lib/friendly_shipping/services/ups/parse_shipment_accept_response.rb +5 -1
- data/lib/friendly_shipping/services/ups/parse_shipment_confirm_response.rb +5 -1
- data/lib/friendly_shipping/services/ups/parse_time_in_transit_response.rb +5 -1
- data/lib/friendly_shipping/services/ups/parse_void_shipment_response.rb +5 -1
- data/lib/friendly_shipping/services/ups/parse_xml_response.rb +16 -7
- data/lib/friendly_shipping/services/ups_freight/generate_delivery_options_hash.rb +21 -0
- data/lib/friendly_shipping/services/ups_freight/generate_document_options_hash.rb +28 -0
- data/lib/friendly_shipping/services/ups_freight/generate_email_options_hash.rb +25 -0
- data/lib/friendly_shipping/services/ups_freight/generate_freight_rate_request_hash.rb +2 -10
- data/lib/friendly_shipping/services/ups_freight/generate_freight_ship_request_hash.rb +81 -0
- data/lib/friendly_shipping/services/ups_freight/generate_location_hash.rb +5 -2
- data/lib/friendly_shipping/services/ups_freight/generate_pickup_options_hash.rb +21 -0
- data/lib/friendly_shipping/services/ups_freight/generate_pickup_request_hash.rb +31 -0
- data/lib/friendly_shipping/services/ups_freight/label_delivery_options.rb +29 -0
- data/lib/friendly_shipping/services/ups_freight/label_document_options.rb +56 -0
- data/lib/friendly_shipping/services/ups_freight/label_email_options.rb +40 -0
- data/lib/friendly_shipping/services/ups_freight/label_item_options.rb +10 -0
- data/lib/friendly_shipping/services/ups_freight/label_options.rb +37 -0
- data/lib/friendly_shipping/services/ups_freight/label_package_options.rb +10 -0
- data/lib/friendly_shipping/services/ups_freight/label_pickup_options.rb +29 -0
- data/lib/friendly_shipping/services/ups_freight/parse_freight_label_response.rb +57 -0
- data/lib/friendly_shipping/services/ups_freight/parse_freight_rate_response.rb +29 -32
- data/lib/friendly_shipping/services/ups_freight/parse_shipment_document.rb +24 -0
- data/lib/friendly_shipping/services/ups_freight/pickup_request_options.rb +29 -0
- data/lib/friendly_shipping/services/ups_freight/rates_options.rb +3 -6
- data/lib/friendly_shipping/services/ups_freight/restful_api_error_handler.rb +24 -0
- data/lib/friendly_shipping/services/ups_freight/shipment_document.rb +21 -0
- data/lib/friendly_shipping/services/ups_freight/shipment_information.rb +35 -0
- data/lib/friendly_shipping/services/ups_freight.rb +42 -12
- data/lib/friendly_shipping/services/usps/parse_address_validation_response.rb +5 -1
- data/lib/friendly_shipping/services/usps/parse_city_state_lookup_response.rb +5 -1
- data/lib/friendly_shipping/services/usps/parse_rate_response.rb +5 -2
- data/lib/friendly_shipping/services/usps/parse_time_in_transit_response.rb +5 -1
- data/lib/friendly_shipping/services/usps/parse_xml_response.rb +15 -5
- data/lib/friendly_shipping/version.rb +1 -1
- metadata +70 -35
- data/lib/friendly_shipping/services/ups_freight/generate_ups_security_hash.rb +0 -23
- data/lib/friendly_shipping/services/ups_freight/parse_json_response.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2b1c8a12304b7f798f0df9210f6e52027bd588b9d612f5f05bf5d102e8db0e8
|
4
|
+
data.tar.gz: 7813b10233d7364d836d5cd50bb281b9ef43c37d936009f8f400a164d4a30fb1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc7eb250872e158e3ba105168bda09f47328bd2077f758a5e7e746416aa5af071686b992609c1b62d50d399085164999970119ba2d801cbe7d5f99803d675132
|
7
|
+
data.tar.gz: 4ccb36047bfc9f612af55014b35fb3d72e629277776a65d4c7b4363c056d28d7de16d77564bf371a17ef01002cf04322047b93526f7aa35cc648b6acbfc33567
|
data/.rubocop-relaxed.yml
CHANGED
@@ -133,6 +133,9 @@ Style/WordArray:
|
|
133
133
|
Enabled: false
|
134
134
|
StyleGuide: https://relaxed.ruby.style/#stylewordarray
|
135
135
|
|
136
|
+
Layout/LineLength:
|
137
|
+
Enabled: false
|
138
|
+
|
136
139
|
Lint/AmbiguousRegexpLiteral:
|
137
140
|
Enabled: false
|
138
141
|
StyleGuide: https://relaxed.ruby.style/#lintambiguousregexpliteral
|
@@ -156,9 +159,6 @@ Metrics/ModuleLength:
|
|
156
159
|
Metrics/CyclomaticComplexity:
|
157
160
|
Enabled: false
|
158
161
|
|
159
|
-
Metrics/LineLength:
|
160
|
-
Enabled: false
|
161
|
-
|
162
162
|
Metrics/MethodLength:
|
163
163
|
Enabled: false
|
164
164
|
|
@@ -167,4 +167,3 @@ Metrics/ParameterLists:
|
|
167
167
|
|
168
168
|
Metrics/PerceivedComplexity:
|
169
169
|
Enabled: false
|
170
|
-
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,10 @@ 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.5.3] - 2020-03-11
|
8
|
+
|
9
|
+
- UPS Service: Add support for shipping labels / bills of lading
|
10
|
+
|
7
11
|
## [0.5.2] - 2020-01-31
|
8
12
|
|
9
13
|
### Added
|
@@ -17,7 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
17
21
|
|
18
22
|
### Changed
|
19
23
|
- USPS Service: Rename "Package Services" shipping method (#85)
|
20
|
-
- Documentation updates (#86)
|
24
|
+
- Documentation updates (#86)
|
21
25
|
|
22
26
|
## [0.5] - 2020-01-24
|
23
27
|
|
data/friendly_shipping.gemspec
CHANGED
@@ -24,21 +24,21 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.add_runtime_dependency "data_uri", "~> 0.0.3"
|
26
26
|
spec.add_runtime_dependency "dry-monads", "~> 1.0"
|
27
|
-
spec.add_runtime_dependency "money", "
|
28
|
-
spec.add_runtime_dependency "nokogiri", "
|
29
|
-
spec.add_runtime_dependency "physical", ">= 0.4.4"
|
27
|
+
spec.add_runtime_dependency "money", "~> 6.0"
|
28
|
+
spec.add_runtime_dependency "nokogiri", "~> 1.6"
|
29
|
+
spec.add_runtime_dependency "physical", "~> 0.4", ">= 0.4.4"
|
30
30
|
spec.add_runtime_dependency "rest-client", "~> 2.0"
|
31
31
|
spec.required_ruby_version = '>= 2.5'
|
32
32
|
|
33
|
-
spec.add_development_dependency "bundler"
|
34
|
-
spec.add_development_dependency "dotenv"
|
33
|
+
spec.add_development_dependency "bundler", ">= 1.17", "< 3"
|
34
|
+
spec.add_development_dependency "dotenv", "~> 2.7"
|
35
35
|
spec.add_development_dependency "factory_bot", "~> 5.0"
|
36
|
-
spec.add_development_dependency "pry"
|
36
|
+
spec.add_development_dependency "pry", "~> 0.12"
|
37
37
|
spec.add_development_dependency "rake", "~> 10.0"
|
38
38
|
spec.add_development_dependency "rspec", "~> 3.0"
|
39
39
|
spec.add_development_dependency "rspec_junit_formatter", "~> 0.4"
|
40
|
-
spec.add_development_dependency "rubocop"
|
41
|
-
spec.add_development_dependency "simplecov"
|
42
|
-
spec.add_development_dependency "vcr"
|
43
|
-
spec.add_development_dependency "webmock"
|
40
|
+
spec.add_development_dependency "rubocop", ">= 0.80", "< 1"
|
41
|
+
spec.add_development_dependency "simplecov", "~> 0.17"
|
42
|
+
spec.add_development_dependency "vcr", "~> 5.0"
|
43
|
+
spec.add_development_dependency "webmock", "~> 3.6"
|
44
44
|
end
|
@@ -1,21 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module FriendlyShipping
|
4
|
-
class ApiFailure
|
5
|
-
|
6
|
-
|
7
|
-
# @param [Object] failure The API failure
|
8
|
-
# @param [FriendlyShipping::Request] original_request The HTTP request (when debugging is enabled)
|
9
|
-
# @param [FriendlyShipping::Response] original_response The HTTP response (when debugging is enabled)
|
10
|
-
def initialize(failure, original_request:, original_response:)
|
11
|
-
@failure = failure
|
12
|
-
|
13
|
-
# We do not want to attach debugging information in every single response to save memory in production
|
14
|
-
return unless original_request&.debug
|
15
|
-
|
16
|
-
@original_request = original_request
|
17
|
-
@original_response = original_response
|
18
|
-
end
|
4
|
+
class ApiFailure < ApiResult
|
5
|
+
alias_method :failure, :data
|
19
6
|
|
20
7
|
def to_s
|
21
8
|
failure.to_s
|
@@ -7,8 +7,11 @@ module FriendlyShipping
|
|
7
7
|
extend Dry::Monads::Result::Mixin
|
8
8
|
|
9
9
|
def self.call(request:, response:)
|
10
|
-
parsing_result = ParseXMLResponse.call(
|
11
|
-
|
10
|
+
parsing_result = ParseXMLResponse.call(
|
11
|
+
request: request,
|
12
|
+
response: response,
|
13
|
+
expected_root_tag: 'AddressValidationResponse'
|
14
|
+
)
|
12
15
|
parsing_result.bind do |xml|
|
13
16
|
address_type = xml.at('AddressClassification/Description')&.text&.downcase
|
14
17
|
Success(
|
@@ -7,8 +7,11 @@ module FriendlyShipping
|
|
7
7
|
extend Dry::Monads::Result::Mixin
|
8
8
|
|
9
9
|
def self.call(request:, response:)
|
10
|
-
parsing_result = ParseXMLResponse.call(
|
11
|
-
|
10
|
+
parsing_result = ParseXMLResponse.call(
|
11
|
+
request: request,
|
12
|
+
response: response,
|
13
|
+
expected_root_tag: 'AddressValidationResponse'
|
14
|
+
)
|
12
15
|
parsing_result.bind do |xml|
|
13
16
|
if xml.at('NoCandidatesIndicator')
|
14
17
|
Failure(
|
@@ -7,8 +7,11 @@ module FriendlyShipping
|
|
7
7
|
extend Dry::Monads::Result::Mixin
|
8
8
|
|
9
9
|
def self.call(request:, response:, location:)
|
10
|
-
parsing_result = ParseXMLResponse.call(
|
11
|
-
|
10
|
+
parsing_result = ParseXMLResponse.call(
|
11
|
+
request: request,
|
12
|
+
response: response,
|
13
|
+
expected_root_tag: 'AddressValidationResponse'
|
14
|
+
)
|
12
15
|
parsing_result.fmap do |xml|
|
13
16
|
FriendlyShipping::ApiResult.new(
|
14
17
|
[
|
@@ -9,7 +9,11 @@ module FriendlyShipping
|
|
9
9
|
class ParseRateResponse
|
10
10
|
class << self
|
11
11
|
def call(request:, response:, shipment:)
|
12
|
-
parsing_result = ParseXMLResponse.call(
|
12
|
+
parsing_result = ParseXMLResponse.call(
|
13
|
+
request: request,
|
14
|
+
response: response,
|
15
|
+
expected_root_tag: 'RatingServiceSelectionResponse'
|
16
|
+
)
|
13
17
|
parsing_result.fmap do |xml|
|
14
18
|
FriendlyShipping::ApiResult.new(
|
15
19
|
build_rates(xml, shipment),
|
@@ -11,7 +11,11 @@ module FriendlyShipping
|
|
11
11
|
|
12
12
|
class << self
|
13
13
|
def call(request:, response:)
|
14
|
-
parsing_result = ParseXMLResponse.call(
|
14
|
+
parsing_result = ParseXMLResponse.call(
|
15
|
+
request: request,
|
16
|
+
response: response,
|
17
|
+
expected_root_tag: 'ShipmentAcceptResponse'
|
18
|
+
)
|
15
19
|
parsing_result.fmap do |xml|
|
16
20
|
FriendlyShipping::ApiResult.new(
|
17
21
|
build_labels(xml),
|
@@ -7,7 +7,11 @@ module FriendlyShipping
|
|
7
7
|
class Ups
|
8
8
|
class ParseShipmentConfirmResponse
|
9
9
|
def self.call(request:, response:)
|
10
|
-
parsing_result = ParseXMLResponse.call(
|
10
|
+
parsing_result = ParseXMLResponse.call(
|
11
|
+
request: request,
|
12
|
+
response: response,
|
13
|
+
expected_root_tag: 'ShipmentConfirmResponse'
|
14
|
+
)
|
11
15
|
parsing_result.fmap do |xml|
|
12
16
|
FriendlyShipping::ApiResult.new(
|
13
17
|
xml.root.at('ShipmentDigest').text,
|
@@ -5,7 +5,11 @@ module FriendlyShipping
|
|
5
5
|
class Ups
|
6
6
|
class ParseTimeInTransitResponse
|
7
7
|
def self.call(request:, response:)
|
8
|
-
parsing_result = ParseXMLResponse.call(
|
8
|
+
parsing_result = ParseXMLResponse.call(
|
9
|
+
request: request,
|
10
|
+
response: response,
|
11
|
+
expected_root_tag: 'TimeInTransitResponse'
|
12
|
+
)
|
9
13
|
parsing_result.fmap do |xml|
|
10
14
|
origin_country_code = xml.at('TransitResponse/TransitFrom/AddressArtifactFormat/CountryCode').text
|
11
15
|
timings = xml.root.xpath('//TransitResponse/ServiceSummary').map do |service_summary|
|
@@ -7,7 +7,11 @@ module FriendlyShipping
|
|
7
7
|
class Ups
|
8
8
|
class ParseVoidShipmentResponse
|
9
9
|
def self.call(request:, response:)
|
10
|
-
parsing_result = ParseXMLResponse.call(
|
10
|
+
parsing_result = ParseXMLResponse.call(
|
11
|
+
request: request,
|
12
|
+
response: response,
|
13
|
+
expected_root_tag: 'VoidShipmentResponse'
|
14
|
+
)
|
11
15
|
parsing_result.fmap do |xml|
|
12
16
|
FriendlyShipping::ApiResult.new(
|
13
17
|
xml.root.at('ResponseStatusDescription').text,
|
@@ -8,19 +8,18 @@ module FriendlyShipping
|
|
8
8
|
SUCCESSFUL_RESPONSE_STATUS_CODE = '1'
|
9
9
|
|
10
10
|
class << self
|
11
|
-
def call(
|
12
|
-
xml = Nokogiri.XML(
|
11
|
+
def call(request:, response:, expected_root_tag:)
|
12
|
+
xml = Nokogiri.XML(response.body, &:strict)
|
13
13
|
|
14
14
|
if xml.root.nil? || xml.root.name != expected_root_tag
|
15
|
-
|
16
|
-
|
17
|
-
if request_successful?(xml)
|
15
|
+
wrap_failure('Invalid document', request, response)
|
16
|
+
elsif request_successful?(xml)
|
18
17
|
Success(xml)
|
19
18
|
else
|
20
|
-
|
19
|
+
wrap_failure(error_message(xml), request, response)
|
21
20
|
end
|
22
21
|
rescue Nokogiri::XML::SyntaxError => e
|
23
|
-
|
22
|
+
wrap_failure(e, request, response)
|
24
23
|
end
|
25
24
|
|
26
25
|
private
|
@@ -34,6 +33,16 @@ module FriendlyShipping
|
|
34
33
|
desc = xml.root.at_xpath('Response/Error/ErrorDescription')&.text
|
35
34
|
[status, desc].compact.join(": ").presence || 'UPS could not process the request.'
|
36
35
|
end
|
36
|
+
|
37
|
+
def wrap_failure(failure, request, response)
|
38
|
+
Failure(
|
39
|
+
FriendlyShipping::ApiFailure.new(
|
40
|
+
failure,
|
41
|
+
original_request: request,
|
42
|
+
original_response: response
|
43
|
+
)
|
44
|
+
)
|
45
|
+
end
|
37
46
|
end
|
38
47
|
end
|
39
48
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class UpsFreight
|
6
|
+
class GenerateDeliveryOptionsHash
|
7
|
+
def self.call(delivery_options:)
|
8
|
+
{
|
9
|
+
DeliveryOptions: {
|
10
|
+
LiftGateRequiredIndicator: delivery_options.lift_gate_required ? "" : nil,
|
11
|
+
WeekendPickupIndicator: delivery_options.weekend_delivery ? "" : nil,
|
12
|
+
InsidePickupIndicator: delivery_options.inside_delivery ? "" : nil,
|
13
|
+
HolidayPickupIndicator: delivery_options.holiday_delivery ? "" : nil,
|
14
|
+
LimitedAccessPickupIndicator: delivery_options.limited_access_delivery ? "" : nil
|
15
|
+
}.compact.presence
|
16
|
+
}.compact.presence
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class UpsFreight
|
6
|
+
class GenerateDocumentOptionsHash
|
7
|
+
def self.call(document_options:)
|
8
|
+
{
|
9
|
+
Type: {
|
10
|
+
Code: document_options.document_type_code
|
11
|
+
},
|
12
|
+
LabelsPerPage: document_options.labels_per_page,
|
13
|
+
Format: {
|
14
|
+
Code: document_options.format_code
|
15
|
+
},
|
16
|
+
PrintFormat: {
|
17
|
+
Code: document_options.thermal_code,
|
18
|
+
},
|
19
|
+
PrintSize: {
|
20
|
+
Length: document_options.length,
|
21
|
+
Width: document_options.width,
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class UpsFreight
|
6
|
+
class GenerateEmailOptionsHash
|
7
|
+
def self.call(email_options:)
|
8
|
+
{
|
9
|
+
EMailInformation: {
|
10
|
+
EMailType: {
|
11
|
+
Code: email_options.email_type_code,
|
12
|
+
},
|
13
|
+
EMail: {
|
14
|
+
EMailAddress: email_options.email,
|
15
|
+
UndeliverableEMailAddress: email_options.undeliverable_email,
|
16
|
+
EMailText: email_options.body,
|
17
|
+
Subject: email_options.subject
|
18
|
+
}.compact
|
19
|
+
}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'friendly_shipping/services/ups_freight/generate_location_hash'
|
4
|
+
require 'friendly_shipping/services/ups_freight/generate_pickup_request_hash'
|
4
5
|
|
5
6
|
module FriendlyShipping
|
6
7
|
module Services
|
@@ -20,22 +21,13 @@ module FriendlyShipping
|
|
20
21
|
},
|
21
22
|
Commodity: options.commodity_information_generator.call(shipment: shipment, options: options),
|
22
23
|
TimeInTransitIndicator: 'true',
|
23
|
-
PickupRequest:
|
24
|
+
PickupRequest: GeneratePickupRequestHash.call(pickup_request_options: options.pickup_request_options),
|
24
25
|
}.compact.merge(handling_units(shipment, options).reduce(&:merge).to_h)
|
25
26
|
}
|
26
27
|
end
|
27
28
|
|
28
29
|
private
|
29
30
|
|
30
|
-
def pickup_request(options)
|
31
|
-
return unless options.pickup_date
|
32
|
-
|
33
|
-
{
|
34
|
-
PickupDate: options.pickup_date.strftime('%Y%m%d'),
|
35
|
-
AdditionalComments: options.pickup_comments
|
36
|
-
}
|
37
|
-
end
|
38
|
-
|
39
31
|
def handling_units(shipment, options)
|
40
32
|
all_package_options = shipment.packages.map { |package| options.options_for_package(package) }
|
41
33
|
all_package_options.group_by(&:handling_unit_code).map do |_handling_unit_code, options_group|
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'friendly_shipping/services/ups_freight/generate_location_hash'
|
4
|
+
require 'friendly_shipping/services/ups_freight/generate_document_options_hash'
|
5
|
+
require 'friendly_shipping/services/ups_freight/generate_email_options_hash'
|
6
|
+
require 'friendly_shipping/services/ups_freight/generate_pickup_options_hash'
|
7
|
+
require 'friendly_shipping/services/ups_freight/generate_delivery_options_hash'
|
8
|
+
|
9
|
+
module FriendlyShipping
|
10
|
+
module Services
|
11
|
+
class UpsFreight
|
12
|
+
class GenerateFreightShipRequestHash
|
13
|
+
class << self
|
14
|
+
def call(shipment:, options:)
|
15
|
+
{
|
16
|
+
FreightShipRequest: {
|
17
|
+
Shipment: {
|
18
|
+
ShipperNumber: options.shipper_number,
|
19
|
+
ShipFrom: GenerateLocationHash.call(location: shipment.origin),
|
20
|
+
ShipTo: GenerateLocationHash.call(location: shipment.destination),
|
21
|
+
PaymentInformation: payment_information(options),
|
22
|
+
Service: {
|
23
|
+
Code: options.shipping_method.service_code
|
24
|
+
},
|
25
|
+
Commodity: options.commodity_information_generator.call(shipment: shipment, options: options),
|
26
|
+
Documents: {
|
27
|
+
Image: options.document_options.map { |doc_opts| GenerateDocumentOptionsHash.call(document_options: doc_opts) }
|
28
|
+
},
|
29
|
+
ShipmentServiceOptions: shipment_service_options(options),
|
30
|
+
HandlingInstructions: options.handling_instructions,
|
31
|
+
PickupInstructions: options.pickup_instructions,
|
32
|
+
DeliveryInstructions: options.delivery_instructions,
|
33
|
+
PickupRequest: GeneratePickupRequestHash.call(pickup_request_options: options.pickup_request_options)
|
34
|
+
}.compact.merge(handling_units(shipment, options).reduce(&:merge).to_h)
|
35
|
+
}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def shipment_service_options(options)
|
42
|
+
email_options = options.email_options.map { |email_opts| GenerateEmailOptionsHash.call(email_options: email_opts) }.presence
|
43
|
+
pickup_options = options.pickup_options ? GeneratePickupOptionsHash.call(pickup_options: options.pickup_options) : nil
|
44
|
+
delivery_options = options.delivery_options ? GenerateDeliveryOptionsHash.call(delivery_options: options.delivery_options) : nil
|
45
|
+
[email_options, pickup_options, delivery_options].compact.presence
|
46
|
+
end
|
47
|
+
|
48
|
+
def handling_units(shipment, options)
|
49
|
+
all_package_options = shipment.packages.map { |package| options.options_for_package(package) }
|
50
|
+
all_package_options.group_by(&:handling_unit_code).map do |_handling_unit_code, options_group|
|
51
|
+
[options_group.first, options_group.length]
|
52
|
+
end.map { |package_options, quantity| handling_unit_hash(package_options, quantity) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def handling_unit_hash(package_options, quantity)
|
56
|
+
{
|
57
|
+
package_options.handling_unit_tag => {
|
58
|
+
Quantity: quantity.to_s,
|
59
|
+
Type: {
|
60
|
+
Code: package_options.handling_unit_code,
|
61
|
+
Description: package_options.handling_unit_description
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def payment_information(options)
|
68
|
+
payer_address = GenerateLocationHash.call(location: options.billing_address).
|
69
|
+
merge(ShipperNumber: options.shipper_number)
|
70
|
+
{
|
71
|
+
Payer: payer_address,
|
72
|
+
ShipmentBillingOption: {
|
73
|
+
Code: options.billing_code
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -17,8 +17,11 @@ module FriendlyShipping
|
|
17
17
|
PostalCode: location.zip,
|
18
18
|
CountryCode: location.country.code
|
19
19
|
},
|
20
|
-
AttentionName: location.name
|
21
|
-
|
20
|
+
AttentionName: location.name,
|
21
|
+
Phone: {
|
22
|
+
Number: location.phone
|
23
|
+
}.compact.presence
|
24
|
+
}.compact
|
22
25
|
end
|
23
26
|
|
24
27
|
private
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class UpsFreight
|
6
|
+
class GeneratePickupOptionsHash
|
7
|
+
def self.call(pickup_options:)
|
8
|
+
{
|
9
|
+
PickupOptions: {
|
10
|
+
LiftGateRequiredIndicator: pickup_options.lift_gate_required ? "" : nil,
|
11
|
+
WeekendPickupIndicator: pickup_options.weekend_pickup ? "" : nil,
|
12
|
+
InsidePickupIndicator: pickup_options.inside_pickup ? "" : nil,
|
13
|
+
HolidayPickupIndicator: pickup_options.holiday_pickup ? "" : nil,
|
14
|
+
LimitedAccessPickupIndicator: pickup_options.limited_access_pickup ? "" : nil
|
15
|
+
}.compact.presence
|
16
|
+
}.compact.presence
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class UpsFreight
|
6
|
+
class GeneratePickupRequestHash
|
7
|
+
class << self
|
8
|
+
def call(pickup_request_options:)
|
9
|
+
return unless pickup_request_options
|
10
|
+
|
11
|
+
{
|
12
|
+
AdditionalComments: pickup_request_options.comments,
|
13
|
+
Requester: {
|
14
|
+
ThirdPartyRequester: pickup_request_options.third_party_requester ? '' : nil,
|
15
|
+
AttentionName: pickup_request_options.requester.name,
|
16
|
+
EMailAddress: pickup_request_options.requester_email,
|
17
|
+
Name: pickup_request_options.requester.company_name,
|
18
|
+
Phone: {
|
19
|
+
Number: pickup_request_options.requester.phone
|
20
|
+
}.compact
|
21
|
+
}.compact,
|
22
|
+
PickupDate: pickup_request_options.pickup_time_window.begin.strftime('%Y%m%d'),
|
23
|
+
EarliestTimeReady: pickup_request_options.pickup_time_window.begin.strftime('%H%M'),
|
24
|
+
LatestTimeReady: pickup_request_options.pickup_time_window.end.strftime('%H%M'),
|
25
|
+
}.compact
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class UpsFreight
|
6
|
+
class LabelDeliveryOptions
|
7
|
+
attr_reader :holiday_delivery,
|
8
|
+
:inside_delivery,
|
9
|
+
:weekend_delivery,
|
10
|
+
:lift_gate_required,
|
11
|
+
:limited_access_delivery
|
12
|
+
|
13
|
+
def initialize(
|
14
|
+
holiday_delivery: nil,
|
15
|
+
inside_delivery: nil,
|
16
|
+
weekend_delivery: nil,
|
17
|
+
lift_gate_required: nil,
|
18
|
+
limited_access_delivery: nil
|
19
|
+
)
|
20
|
+
@holiday_delivery = holiday_delivery
|
21
|
+
@inside_delivery = inside_delivery
|
22
|
+
@weekend_delivery = weekend_delivery
|
23
|
+
@lift_gate_required = lift_gate_required
|
24
|
+
@limited_access_delivery = limited_access_delivery
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FriendlyShipping
|
4
|
+
module Services
|
5
|
+
class UpsFreight
|
6
|
+
class LabelDocumentOptions
|
7
|
+
attr_reader :format, :type, :length, :width, :thermal
|
8
|
+
|
9
|
+
DOCUMENT_TYPES = {
|
10
|
+
label: "30",
|
11
|
+
ups_bol: "20",
|
12
|
+
vics_bol: "21"
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
DOCUMENT_FORMATS = {
|
16
|
+
pdf: "01"
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
THERMAL_CODE = {
|
20
|
+
false => "01",
|
21
|
+
true => "02"
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
def initialize(
|
25
|
+
format: :pdf,
|
26
|
+
type: :label,
|
27
|
+
size: "4x6",
|
28
|
+
thermal: false,
|
29
|
+
labels_per_page: 1
|
30
|
+
)
|
31
|
+
@format = format
|
32
|
+
@type = type
|
33
|
+
@length, @width = size.split('x').sort
|
34
|
+
@thermal = thermal
|
35
|
+
@labels_per_page = labels_per_page
|
36
|
+
end
|
37
|
+
|
38
|
+
def format_code
|
39
|
+
DOCUMENT_FORMATS.fetch(format)
|
40
|
+
end
|
41
|
+
|
42
|
+
def document_type_code
|
43
|
+
DOCUMENT_TYPES.fetch(type)
|
44
|
+
end
|
45
|
+
|
46
|
+
def thermal_code
|
47
|
+
THERMAL_CODE.fetch(thermal)
|
48
|
+
end
|
49
|
+
|
50
|
+
def labels_per_page
|
51
|
+
@labels_per_page.to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|