friendly_shipping 0.5.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-relaxed.yml +3 -4
  3. data/.rubocop.yml +9 -0
  4. data/CHANGELOG.md +28 -1
  5. data/friendly_shipping.gemspec +12 -12
  6. data/lib/friendly_shipping/api_failure.rb +2 -15
  7. data/lib/friendly_shipping/http_client.rb +16 -9
  8. data/lib/friendly_shipping/request.rb +7 -1
  9. data/lib/friendly_shipping/services/ship_engine/bad_request_handler.rb +15 -3
  10. data/lib/friendly_shipping/services/ups.rb +8 -0
  11. data/lib/friendly_shipping/services/ups/parse_address_classification_response.rb +5 -2
  12. data/lib/friendly_shipping/services/ups/parse_address_validation_response.rb +5 -2
  13. data/lib/friendly_shipping/services/ups/parse_city_state_lookup_response.rb +5 -2
  14. data/lib/friendly_shipping/services/ups/parse_rate_response.rb +5 -1
  15. data/lib/friendly_shipping/services/ups/parse_shipment_accept_response.rb +5 -1
  16. data/lib/friendly_shipping/services/ups/parse_shipment_confirm_response.rb +5 -1
  17. data/lib/friendly_shipping/services/ups/parse_time_in_transit_response.rb +6 -2
  18. data/lib/friendly_shipping/services/ups/parse_void_shipment_response.rb +5 -1
  19. data/lib/friendly_shipping/services/ups/parse_xml_response.rb +16 -7
  20. data/lib/friendly_shipping/services/ups_freight.rb +44 -13
  21. data/lib/friendly_shipping/services/ups_freight/generate_delivery_options_hash.rb +21 -0
  22. data/lib/friendly_shipping/services/ups_freight/generate_document_options_hash.rb +28 -0
  23. data/lib/friendly_shipping/services/ups_freight/generate_email_options_hash.rb +25 -0
  24. data/lib/friendly_shipping/services/ups_freight/generate_freight_rate_request_hash.rb +2 -10
  25. data/lib/friendly_shipping/services/ups_freight/generate_freight_ship_request_hash.rb +81 -0
  26. data/lib/friendly_shipping/services/ups_freight/generate_location_hash.rb +5 -2
  27. data/lib/friendly_shipping/services/ups_freight/generate_pickup_options_hash.rb +21 -0
  28. data/lib/friendly_shipping/services/ups_freight/generate_pickup_request_hash.rb +31 -0
  29. data/lib/friendly_shipping/services/ups_freight/label_delivery_options.rb +29 -0
  30. data/lib/friendly_shipping/services/ups_freight/label_document_options.rb +56 -0
  31. data/lib/friendly_shipping/services/ups_freight/label_email_options.rb +40 -0
  32. data/lib/friendly_shipping/services/ups_freight/label_item_options.rb +10 -0
  33. data/lib/friendly_shipping/services/ups_freight/label_options.rb +37 -0
  34. data/lib/friendly_shipping/services/ups_freight/label_package_options.rb +10 -0
  35. data/lib/friendly_shipping/services/ups_freight/label_pickup_options.rb +29 -0
  36. data/lib/friendly_shipping/services/ups_freight/parse_freight_label_response.rb +57 -0
  37. data/lib/friendly_shipping/services/ups_freight/parse_freight_rate_response.rb +29 -32
  38. data/lib/friendly_shipping/services/ups_freight/parse_shipment_document.rb +24 -0
  39. data/lib/friendly_shipping/services/ups_freight/pickup_request_options.rb +29 -0
  40. data/lib/friendly_shipping/services/ups_freight/rates_options.rb +3 -6
  41. data/lib/friendly_shipping/services/ups_freight/restful_api_error_handler.rb +30 -0
  42. data/lib/friendly_shipping/services/ups_freight/shipment_document.rb +21 -0
  43. data/lib/friendly_shipping/services/ups_freight/shipment_information.rb +35 -0
  44. data/lib/friendly_shipping/services/usps.rb +1 -0
  45. data/lib/friendly_shipping/services/usps/parse_address_validation_response.rb +5 -1
  46. data/lib/friendly_shipping/services/usps/parse_city_state_lookup_response.rb +5 -1
  47. data/lib/friendly_shipping/services/usps/parse_rate_response.rb +5 -2
  48. data/lib/friendly_shipping/services/usps/parse_time_in_transit_response.rb +5 -1
  49. data/lib/friendly_shipping/services/usps/parse_xml_response.rb +15 -5
  50. data/lib/friendly_shipping/services/usps/rate_estimate_package_options.rb +4 -1
  51. data/lib/friendly_shipping/services/usps/serialize_rate_request.rb +9 -3
  52. data/lib/friendly_shipping/services/usps/shipping_methods.rb +1 -2
  53. data/lib/friendly_shipping/version.rb +1 -1
  54. metadata +75 -40
  55. data/lib/friendly_shipping/services/ups_freight/generate_ups_security_hash.rb +0 -23
  56. data/lib/friendly_shipping/services/ups_freight/parse_json_response.rb +0 -38
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class LabelEmailOptions
7
+ EMAIL_TYPES = {
8
+ ship_notification: '001',
9
+ delivery_notification: '002',
10
+ exception_notification: '003',
11
+ bol_labels: '004'
12
+ }.freeze
13
+
14
+ attr_reader :email_type,
15
+ :email,
16
+ :undeliverable_email,
17
+ :subject,
18
+ :body
19
+
20
+ def initialize(
21
+ email:,
22
+ email_type:,
23
+ undeliverable_email:,
24
+ subject: nil,
25
+ body: nil
26
+ )
27
+ @email = email
28
+ @email_type = email_type
29
+ @undeliverable_email = undeliverable_email
30
+ @subject = subject
31
+ @body = body
32
+ end
33
+
34
+ def email_type_code
35
+ EMAIL_TYPES.fetch(email_type)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class LabelItemOptions < RatesItemOptions
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class LabelOptions < RatesOptions
7
+ attr_reader :document_options,
8
+ :email_options,
9
+ :pickup_options,
10
+ :delivery_options,
11
+ :pickup_instructions,
12
+ :delivery_instructions,
13
+ :handling_instructions
14
+
15
+ def initialize(
16
+ document_options: [],
17
+ email_options: [],
18
+ pickup_options: nil,
19
+ delivery_options: nil,
20
+ pickup_instructions: nil,
21
+ delivery_instructions: nil,
22
+ handling_instructions: nil,
23
+ **kwargs
24
+ )
25
+ @pickup_options = pickup_options
26
+ @delivery_options = delivery_options
27
+ @document_options = document_options
28
+ @email_options = email_options
29
+ @pickup_instructions = pickup_instructions
30
+ @delivery_instructions = delivery_instructions
31
+ @handling_instructions = handling_instructions
32
+ super kwargs
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class LabelPackageOptions < RatesPackageOptions
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class LabelPickupOptions
7
+ attr_reader :holiday_pickup,
8
+ :inside_pickup,
9
+ :weekend_pickup,
10
+ :lift_gate_required,
11
+ :limited_access_pickup
12
+
13
+ def initialize(
14
+ holiday_pickup: nil,
15
+ inside_pickup: nil,
16
+ weekend_pickup: nil,
17
+ lift_gate_required: nil,
18
+ limited_access_pickup: nil
19
+ )
20
+ @holiday_pickup = holiday_pickup
21
+ @inside_pickup = inside_pickup
22
+ @weekend_pickup = weekend_pickup
23
+ @lift_gate_required = lift_gate_required
24
+ @limited_access_pickup = limited_access_pickup
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'friendly_shipping/services/ups_freight/parse_shipment_document'
4
+ require 'friendly_shipping/services/ups_freight/shipment_information'
5
+
6
+ module FriendlyShipping
7
+ module Services
8
+ class UpsFreight
9
+ class ParseFreightLabelResponse
10
+ class << self
11
+ def call(request:, response:)
12
+ json = JSON.parse(response.body)
13
+
14
+ warnings_json = Array.wrap(json.dig("FreightShipResponse", "Response", "Alert"))
15
+ warnings = warnings_json.map do |detailed_warning|
16
+ status = detailed_warning['Code']
17
+ desc = detailed_warning['Description']
18
+ [status, desc].compact.join(": ")
19
+ end.join("\n")
20
+
21
+ shipment_results = json.dig("FreightShipResponse", "ShipmentResults")
22
+
23
+ service_code = shipment_results.dig("Service", "Code")
24
+ shipping_method = SHIPPING_METHODS.detect { |sm| sm.service_code == service_code }
25
+
26
+ total_shipment_charge = shipment_results.dig("TotalShipmentCharge")
27
+ currency = Money::Currency.new(total_shipment_charge['CurrencyCode'])
28
+ amount = total_shipment_charge['MonetaryValue'].to_f
29
+ total_money = Money.new(amount * currency.subunit_to_unit, currency)
30
+
31
+ images_data = Array.wrap(shipment_results.dig("Documents", "Image"))
32
+
33
+ bol_id = shipment_results.dig("BOLID")
34
+ shipment_number = shipment_results.dig("ShipmentNumber")
35
+ pickup_request_number = shipment_results.dig("PickupRequestConfirmationNumber")
36
+
37
+ documents = images_data.map { |image_data| ParseShipmentDocument.call(image_data: image_data) }
38
+
39
+ FriendlyShipping::ApiResult.new(
40
+ ShipmentInformation.new(
41
+ total: total_money,
42
+ bol_id: bol_id,
43
+ number: shipment_number,
44
+ pickup_request_number: pickup_request_number,
45
+ shipping_method: shipping_method,
46
+ warnings: warnings,
47
+ documents: documents
48
+ ),
49
+ original_request: request,
50
+ original_response: response
51
+ )
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'friendly_shipping/rate'
4
4
  require 'friendly_shipping/api_result'
5
- require 'friendly_shipping/services/ups_freight/parse_json_response'
6
5
  require 'friendly_shipping/services/ups_freight/shipping_methods'
7
6
 
8
7
  module FriendlyShipping
@@ -11,40 +10,38 @@ module FriendlyShipping
11
10
  class ParseFreightRateResponse
12
11
  class << self
13
12
  def call(request:, response:)
14
- parsed_json = ParseJSONResponse.call(response.body, 'FreightRateResponse')
13
+ json = JSON.parse(response.body)
15
14
 
16
- parsed_json.fmap do |json|
17
- service_code = json.dig("FreightRateResponse", "Service", "Code")
18
- shipping_method = SHIPPING_METHODS.detect { |sm| sm.service_code == service_code }
19
- total_shipment_charge = json.dig("FreightRateResponse", "TotalShipmentCharge")
20
- currency = Money::Currency.new(total_shipment_charge['CurrencyCode'])
21
- amount = total_shipment_charge['MonetaryValue'].to_f
22
- total_money = Money.new(amount * currency.subunit_to_unit, currency)
23
- data = {
24
- customer_context: json.dig("FreightRateResponse", "TransactionReference", "TransactionIdentifier"),
25
- commodities: Array.wrap(json.dig("FreightRateResponse", "Commodity")),
26
- response_body: json
27
- }
15
+ service_code = json.dig("FreightRateResponse", "Service", "Code")
16
+ shipping_method = SHIPPING_METHODS.detect { |sm| sm.service_code == service_code }
17
+ total_shipment_charge = json.dig("FreightRateResponse", "TotalShipmentCharge")
18
+ currency = Money::Currency.new(total_shipment_charge['CurrencyCode'])
19
+ amount = total_shipment_charge['MonetaryValue'].to_f
20
+ total_money = Money.new(amount * currency.subunit_to_unit, currency)
21
+ data = {
22
+ customer_context: json.dig("FreightRateResponse", "TransactionReference", "TransactionIdentifier"),
23
+ commodities: Array.wrap(json.dig("FreightRateResponse", "Commodity")),
24
+ response_body: json
25
+ }
28
26
 
29
- days_in_transit = json.dig("FreightRateResponse", "TimeInTransit", "DaysInTransit")
30
- if days_in_transit
31
- data[:days_in_transit] = days_in_transit.to_i
32
- end
33
-
34
- FriendlyShipping::ApiResult.new(
35
- [
36
- FriendlyShipping::Rate.new(
37
- amounts: {
38
- total: total_money
39
- },
40
- shipping_method: shipping_method,
41
- data: data
42
- )
43
- ],
44
- original_request: request,
45
- original_response: response
46
- )
27
+ days_in_transit = json.dig("FreightRateResponse", "TimeInTransit", "DaysInTransit")
28
+ if days_in_transit
29
+ data[:days_in_transit] = days_in_transit.to_i
47
30
  end
31
+
32
+ FriendlyShipping::ApiResult.new(
33
+ [
34
+ FriendlyShipping::Rate.new(
35
+ amounts: {
36
+ total: total_money
37
+ },
38
+ shipping_method: shipping_method,
39
+ data: data
40
+ )
41
+ ],
42
+ original_request: request,
43
+ original_response: response
44
+ )
48
45
  end
49
46
  end
50
47
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'friendly_shipping/services/ups_freight/shipment_document'
4
+
5
+ module FriendlyShipping
6
+ module Services
7
+ class UpsFreight
8
+ class ParseShipmentDocument
9
+ REVERSE_DOCUMENT_TYPES = LabelDocumentOptions::DOCUMENT_TYPES.map(&:reverse_each).map(&:to_a).to_h
10
+
11
+ def self.call(image_data:)
12
+ format_code = image_data.dig("Type", "Code")
13
+ graphic_image_b64 = image_data.dig("GraphicImage")
14
+
15
+ ShipmentDocument.new(
16
+ format: image_data.dig("Format", "Code").downcase.to_sym,
17
+ binary: Base64.decode64(graphic_image_b64),
18
+ document_type: REVERSE_DOCUMENT_TYPES.fetch(format_code)
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class PickupRequestOptions
7
+ attr_reader :pickup_time_window,
8
+ :requester,
9
+ :third_party_requester,
10
+ :requester_email,
11
+ :comments
12
+
13
+ def initialize(
14
+ pickup_time_window:,
15
+ requester:,
16
+ requester_email:,
17
+ comments: nil,
18
+ third_party_requester: false
19
+ )
20
+ @pickup_time_window = pickup_time_window
21
+ @requester = requester
22
+ @third_party_requester = third_party_requester
23
+ @requester_email = requester_email
24
+ @comments = comments
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -31,8 +31,7 @@ module FriendlyShipping
31
31
  :billing_code,
32
32
  :customer_context,
33
33
  :shipping_method,
34
- :pickup_date,
35
- :pickup_comments,
34
+ :pickup_request_options,
36
35
  :commodity_information_generator
37
36
 
38
37
  def initialize(
@@ -41,8 +40,7 @@ module FriendlyShipping
41
40
  shipping_method:,
42
41
  billing: :prepaid,
43
42
  customer_context: nil,
44
- pickup_date: nil,
45
- pickup_comments: nil,
43
+ pickup_request_options: nil,
46
44
  commodity_information_generator: GenerateCommodityInformation,
47
45
  **kwargs
48
46
  )
@@ -51,8 +49,7 @@ module FriendlyShipping
51
49
  @shipping_method = shipping_method
52
50
  @billing_code = BILLING_CODES.fetch(billing)
53
51
  @customer_context = customer_context
54
- @pickup_date = pickup_date
55
- @pickup_comments = pickup_comments
52
+ @pickup_request_options = pickup_request_options
56
53
  @commodity_information_generator = commodity_information_generator
57
54
  super(**kwargs.merge(package_options_class: RatesPackageOptions))
58
55
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class RestfulApiErrorHandler
7
+ extend Dry::Monads::Result::Mixin
8
+
9
+ def self.call(error, original_request: nil, original_response: nil)
10
+ parsed_json = JSON.parse(error.response.body)
11
+ errors = parsed_json.dig('response', 'errors')
12
+
13
+ failure_string = errors.map do |err|
14
+ status = err['code']
15
+ desc = err['message']
16
+ [status, desc].compact.join(": ").presence || 'UPS could not process the request.'
17
+ end.join("\n")
18
+
19
+ Failure(
20
+ ApiFailure.new(
21
+ failure_string,
22
+ original_request: original_request,
23
+ original_response: original_response
24
+ )
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class ShipmentDocument
7
+ attr_reader :format, :document_type, :binary
8
+
9
+ def initialize(
10
+ format:,
11
+ document_type:,
12
+ binary:
13
+ )
14
+ @format = format
15
+ @document_type = document_type
16
+ @binary = binary
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FriendlyShipping
4
+ module Services
5
+ class UpsFreight
6
+ class ShipmentInformation
7
+ attr_reader :documents,
8
+ :number,
9
+ :pickup_request_number,
10
+ :total,
11
+ :bol_id,
12
+ :shipping_method,
13
+ :warnings
14
+
15
+ def initialize(
16
+ total:,
17
+ bol_id:,
18
+ number:,
19
+ pickup_request_number: nil,
20
+ documents: [],
21
+ shipping_method: nil,
22
+ warnings: nil
23
+ )
24
+ @total = total
25
+ @bol_id = bol_id
26
+ @number = number
27
+ @pickup_request_number = pickup_request_number
28
+ @documents = documents
29
+ @shipping_method = shipping_method
30
+ @warnings = warnings
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -112,6 +112,7 @@ module FriendlyShipping
112
112
  FriendlyShipping::Request.new(
113
113
  url: base_url,
114
114
  body: "API=#{RESOURCES[api]}&XML=#{CGI.escape xml}",
115
+ readable_body: xml,
115
116
  debug: debug
116
117
  )
117
118
  end