friendly_shipping 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|