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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-relaxed.yml +3 -4
  3. data/.rubocop.yml +9 -0
  4. data/CHANGELOG.md +5 -1
  5. data/friendly_shipping.gemspec +10 -10
  6. data/lib/friendly_shipping/api_failure.rb +2 -15
  7. data/lib/friendly_shipping/services/ups/parse_address_classification_response.rb +5 -2
  8. data/lib/friendly_shipping/services/ups/parse_address_validation_response.rb +5 -2
  9. data/lib/friendly_shipping/services/ups/parse_city_state_lookup_response.rb +5 -2
  10. data/lib/friendly_shipping/services/ups/parse_rate_response.rb +5 -1
  11. data/lib/friendly_shipping/services/ups/parse_shipment_accept_response.rb +5 -1
  12. data/lib/friendly_shipping/services/ups/parse_shipment_confirm_response.rb +5 -1
  13. data/lib/friendly_shipping/services/ups/parse_time_in_transit_response.rb +5 -1
  14. data/lib/friendly_shipping/services/ups/parse_void_shipment_response.rb +5 -1
  15. data/lib/friendly_shipping/services/ups/parse_xml_response.rb +16 -7
  16. data/lib/friendly_shipping/services/ups_freight/generate_delivery_options_hash.rb +21 -0
  17. data/lib/friendly_shipping/services/ups_freight/generate_document_options_hash.rb +28 -0
  18. data/lib/friendly_shipping/services/ups_freight/generate_email_options_hash.rb +25 -0
  19. data/lib/friendly_shipping/services/ups_freight/generate_freight_rate_request_hash.rb +2 -10
  20. data/lib/friendly_shipping/services/ups_freight/generate_freight_ship_request_hash.rb +81 -0
  21. data/lib/friendly_shipping/services/ups_freight/generate_location_hash.rb +5 -2
  22. data/lib/friendly_shipping/services/ups_freight/generate_pickup_options_hash.rb +21 -0
  23. data/lib/friendly_shipping/services/ups_freight/generate_pickup_request_hash.rb +31 -0
  24. data/lib/friendly_shipping/services/ups_freight/label_delivery_options.rb +29 -0
  25. data/lib/friendly_shipping/services/ups_freight/label_document_options.rb +56 -0
  26. data/lib/friendly_shipping/services/ups_freight/label_email_options.rb +40 -0
  27. data/lib/friendly_shipping/services/ups_freight/label_item_options.rb +10 -0
  28. data/lib/friendly_shipping/services/ups_freight/label_options.rb +37 -0
  29. data/lib/friendly_shipping/services/ups_freight/label_package_options.rb +10 -0
  30. data/lib/friendly_shipping/services/ups_freight/label_pickup_options.rb +29 -0
  31. data/lib/friendly_shipping/services/ups_freight/parse_freight_label_response.rb +57 -0
  32. data/lib/friendly_shipping/services/ups_freight/parse_freight_rate_response.rb +29 -32
  33. data/lib/friendly_shipping/services/ups_freight/parse_shipment_document.rb +24 -0
  34. data/lib/friendly_shipping/services/ups_freight/pickup_request_options.rb +29 -0
  35. data/lib/friendly_shipping/services/ups_freight/rates_options.rb +3 -6
  36. data/lib/friendly_shipping/services/ups_freight/restful_api_error_handler.rb +24 -0
  37. data/lib/friendly_shipping/services/ups_freight/shipment_document.rb +21 -0
  38. data/lib/friendly_shipping/services/ups_freight/shipment_information.rb +35 -0
  39. data/lib/friendly_shipping/services/ups_freight.rb +42 -12
  40. data/lib/friendly_shipping/services/usps/parse_address_validation_response.rb +5 -1
  41. data/lib/friendly_shipping/services/usps/parse_city_state_lookup_response.rb +5 -1
  42. data/lib/friendly_shipping/services/usps/parse_rate_response.rb +5 -2
  43. data/lib/friendly_shipping/services/usps/parse_time_in_transit_response.rb +5 -1
  44. data/lib/friendly_shipping/services/usps/parse_xml_response.rb +15 -5
  45. data/lib/friendly_shipping/version.rb +1 -1
  46. metadata +70 -35
  47. data/lib/friendly_shipping/services/ups_freight/generate_ups_security_hash.rb +0 -23
  48. 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: 752559509e8e61f905718631a73a9df4944367d3c3c24c292a49d8536eded1cd
4
- data.tar.gz: 0ece2fb1fae5a77c20ddde481bc6995f143bdd02ead0cf1d82f718c649864010
3
+ metadata.gz: b2b1c8a12304b7f798f0df9210f6e52027bd588b9d612f5f05bf5d102e8db0e8
4
+ data.tar.gz: 7813b10233d7364d836d5cd50bb281b9ef43c37d936009f8f400a164d4a30fb1
5
5
  SHA512:
6
- metadata.gz: c64b6cf5cc60a948b64cc30b889c88874f6aa5cd1d448f5e9168e8cf98771e3e25d8c710322c0b6752fa7932fac2eefe720d322eea6cd60ea74fe4b606136f4a
7
- data.tar.gz: 18ee11e1c30eecb0a5e41ad81edda69081a7100bd40d05ce715dc2e8761f9961a12f21d54f63151b90cb6134fc6beb2d703256792caa3647a8396a5713902d02
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
@@ -6,3 +6,12 @@ inherit_from:
6
6
 
7
7
  Metrics/BlockLength:
8
8
  Enabled: false
9
+
10
+ Style/HashEachMethods:
11
+ Enabled: true
12
+
13
+ Style/HashTransformKeys:
14
+ Enabled: true
15
+
16
+ Style/HashTransformValues:
17
+ Enabled: true
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
 
@@ -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", ">= 6.0.0"
28
- spec.add_runtime_dependency "nokogiri", ">= 1.6"
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
- attr_reader :failure, :original_request, :original_response
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(response.body, 'AddressValidationResponse')
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(response.body, 'AddressValidationResponse')
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(response.body, 'AddressValidationResponse')
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(response.body, 'RatingServiceSelectionResponse')
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(response.body, 'ShipmentAcceptResponse')
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(response.body, 'ShipmentConfirmResponse')
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(response.body, 'TimeInTransitResponse')
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(response.body, 'VoidShipmentResponse')
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(response_body, expected_root_tag)
12
- xml = Nokogiri.XML(response_body, &:strict)
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
- Failure('Invalid document')
16
- end
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
- Failure(error_message(xml))
19
+ wrap_failure(error_message(xml), request, response)
21
20
  end
22
21
  rescue Nokogiri::XML::SyntaxError => e
23
- Failure(e)
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: pickup_request(options)
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