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.
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