freight_kit 0.1.15 → 0.1.16

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +96 -43
  3. data/README.md +28 -6
  4. data/VERSION +1 -1
  5. data/configuration/carriers/abfs.yml +124 -0
  6. data/configuration/carriers/btvp.yml +84 -0
  7. data/configuration/carriers/ccyq.yml +121 -0
  8. data/configuration/carriers/clni.yml +113 -0
  9. data/configuration/carriers/cnwy.yml +113 -0
  10. data/configuration/carriers/ctbv.yml +117 -0
  11. data/configuration/carriers/dcha.yml +105 -0
  12. data/configuration/carriers/dlds.yml +111 -0
  13. data/configuration/carriers/dphe.yml +130 -0
  14. data/configuration/carriers/drrq.yml +131 -0
  15. data/configuration/carriers/fcsy.yml +102 -0
  16. data/configuration/carriers/fwda.yml +137 -0
  17. data/configuration/carriers/jfj_transportation.yml +2 -0
  18. data/configuration/carriers/mtvl.yml +12 -0
  19. data/configuration/carriers/numk.yml +14 -0
  20. data/configuration/carriers/otcl.yml +124 -0
  21. data/configuration/carriers/pens.yml +22 -0
  22. data/configuration/carriers/rdfs.yml +142 -0
  23. data/configuration/carriers/saia.yml +129 -0
  24. data/configuration/carriers/sefl.yml +115 -0
  25. data/configuration/carriers/totl.yml +111 -0
  26. data/configuration/carriers/tqyl.yml +28 -0
  27. data/configuration/carriers/wrds.yml +20 -0
  28. data/configuration/platforms/carrier_logistics.yml +25 -0
  29. data/configuration/platforms/next.yml +12 -0
  30. data/configuration/platforms/the_great_information_factory.yml +122 -0
  31. data/freight_kit.gemspec +9 -7
  32. data/lib/freight_kit/api_clients/soap_client.rb +70 -0
  33. data/lib/freight_kit/api_clients.rb +3 -0
  34. data/lib/freight_kit/carriers/abfs.rb +421 -0
  35. data/lib/freight_kit/carriers/btvp.rb +29 -0
  36. data/lib/freight_kit/carriers/ccyq.rb +317 -0
  37. data/lib/freight_kit/carriers/clni.rb +396 -0
  38. data/lib/freight_kit/carriers/cnwy.rb +327 -0
  39. data/lib/freight_kit/carriers/ctbv.rb +53 -0
  40. data/lib/freight_kit/carriers/dcha.rb +76 -0
  41. data/lib/freight_kit/carriers/dlds.rb +49 -0
  42. data/lib/freight_kit/carriers/dphe.rb +474 -0
  43. data/lib/freight_kit/carriers/drrq.rb +580 -0
  44. data/lib/freight_kit/carriers/fcsy.rb +57 -0
  45. data/lib/freight_kit/carriers/fwda.rb +744 -0
  46. data/lib/freight_kit/carriers/jfj_transportation.rb +13 -0
  47. data/lib/freight_kit/carriers/mtvl.rb +34 -0
  48. data/lib/freight_kit/carriers/numk.rb +58 -0
  49. data/lib/freight_kit/carriers/otcl.rb +528 -0
  50. data/lib/freight_kit/carriers/pens.rb +204 -0
  51. data/lib/freight_kit/carriers/rdfs.rb +521 -0
  52. data/lib/freight_kit/carriers/saia.rb +438 -0
  53. data/lib/freight_kit/carriers/sefl.rb +342 -0
  54. data/lib/freight_kit/carriers/totl.rb +172 -0
  55. data/lib/freight_kit/carriers/tqyl.rb +339 -0
  56. data/lib/freight_kit/carriers/wrds.rb +246 -0
  57. data/lib/freight_kit/carriers.rb +26 -0
  58. data/lib/freight_kit/helpers/documentable.rb +13 -0
  59. data/lib/freight_kit/helpers/pickupable.rb +39 -0
  60. data/lib/freight_kit/helpers/rateable.rb +28 -0
  61. data/lib/freight_kit/helpers/trackable.rb +25 -0
  62. data/lib/freight_kit/helpers.rb +6 -0
  63. data/lib/freight_kit/platforms/carrier_logistics.rb +450 -0
  64. data/lib/freight_kit/platforms/next.rb +101 -0
  65. data/lib/freight_kit/platforms/the_great_information_factory.rb +528 -0
  66. data/lib/freight_kit/platforms.rb +5 -0
  67. data/lib/freight_kit.rb +20 -1
  68. metadata +94 -14
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FreightKit
4
+ class PENS < FreightKit::Carrier
5
+ class << self
6
+ def maximum_height
7
+ Measured::Length.new(105, :inches)
8
+ end
9
+
10
+ def maximum_weight
11
+ Measured::Weight.new(10_000, :pounds)
12
+ end
13
+
14
+ def minimum_length_for_overlength_fees
15
+ Measured::Length.new(6, :feet)
16
+ end
17
+
18
+ def overlength_fees_require_tariff?
19
+ false
20
+ end
21
+
22
+ def required_credential_types
23
+ %i[api]
24
+ end
25
+
26
+ def requirements
27
+ %i[credentials]
28
+ end
29
+ end
30
+
31
+ REACTIVE_FREIGHT_CARRIER = true
32
+
33
+ include FreightKit::Rateable
34
+
35
+ class << self
36
+ attr_reader :name, :scac
37
+ end
38
+ @name = 'Peninsula Truck Lines'
39
+ @scac = 'PENS'
40
+
41
+ # Documents
42
+
43
+ # Tracking
44
+
45
+ protected
46
+
47
+ def commit(action, request)
48
+ client_args = {
49
+ wsdl: request_url(action),
50
+ convert_request_keys_to: :lower_camelcase,
51
+ env_namespace: :soap,
52
+ element_form_default: :qualified
53
+ }
54
+
55
+ call_args = { message: request }
56
+
57
+ ::FreightKit::SoapClient.new(
58
+ carrier: self,
59
+ action:,
60
+ client_args:,
61
+ call_args:,
62
+ soap_operation: @conf.dig(:api, :actions, action),
63
+ ).call
64
+ end
65
+
66
+ def parse_amount(amount)
67
+ negative = amount.start_with?('-$') || amount.start_with?('-')
68
+
69
+ ['$', '-', ','].each do |char|
70
+ amount = amount.sub(char, '')
71
+ end
72
+
73
+ return 0 if amount.blank?
74
+
75
+ amount = (amount.to_f * 100).to_i
76
+ return amount unless negative
77
+
78
+ amount * -1
79
+ end
80
+
81
+ def request_url(action)
82
+ scheme = @conf.dig(:api, :use_ssl, action) ? 'https://' : 'http://'
83
+ "#{scheme}#{@conf.dig(:api, :domains, action)}#{@conf.dig(:api, :endpoints, action)}"
84
+ end
85
+
86
+ # Documents
87
+
88
+ # Rates
89
+ def build_rate_request(shipment:)
90
+ raise UnserviceableError, 'Unable to quote accessorials over API' if shipment.accessorials.present?
91
+
92
+ api_credentials = fetch_credential(:api)
93
+
94
+ request = {
95
+ accessorial_list: '', # TODO: Fix this!
96
+ account: api_credentials.account,
97
+ class_list: shipment.packages.map(&:freight_class).join(','),
98
+ customer_type: 'B',
99
+ destination_zip: shipment.destination.postal_code.to_s,
100
+ none_palletized_mode: shipment.packages.map(&:packaging).map(&:pallet?).any?(false) ? 'Y' : 'N',
101
+ origin_zip: shipment.origin.postal_code.to_s,
102
+ password: api_credentials.password,
103
+ plt_count_list: shipment.packages.map(&:quantity).join(','),
104
+ plt_length_list: shipment.packages.map { |p| p.inches(:length).ceil }.join(','),
105
+ plt_total_weight: shipment.packages.map { |p| p.pounds(:total).ceil }.join(','),
106
+ plt_width_list: shipment.packages.map { |p| p.inches(:width).ceil }.join(','),
107
+ user_id: api_credentials.username,
108
+ weight_list: shipment.packages.map { |p| p.pounds(:total) }.join(',')
109
+ }
110
+
111
+ save_request(request)
112
+ request
113
+ end
114
+
115
+ def parse_rate_response(shipment:, response:)
116
+ rate_response = RateResponse.new(request: last_request, response:)
117
+
118
+ if response.blank?
119
+ rate_response.error = ResponseError.new('Unknown response')
120
+ return rate_response
121
+ end
122
+
123
+ error = response.dig(:create_pens_rate_quote_response, :create_pens_rate_quote_result, :errors, :message)
124
+
125
+ if error.present?
126
+ if error.include?('[RatingService.ValidateZipCodes]')
127
+ rate_response.error = UnserviceableError.new('Origin or destination has no service available')
128
+ end
129
+
130
+ rate_response.error = ResponseError.new(error) if rate_response.error.blank?
131
+ return rate_response
132
+ end
133
+
134
+ result = response.dig(:create_pens_rate_quote_response, :create_pens_rate_quote_result)
135
+
136
+ if result.dig(:quote, :gross_charge).blank?
137
+ rate_response.error = ResponseError.new('Cost is blank')
138
+ return rate_response
139
+ end
140
+
141
+ service_type = :standard
142
+ api_service_type = result.dig(:quote, :transit_type)
143
+
144
+ @conf.dig(:services, :mappable).each do |key, val|
145
+ service_type = key if api_service_type.downcase.include?(val)
146
+ end
147
+
148
+ transit_days = service_type == :next_day_ltl ? 1 : nil # TODO: Detect correctly
149
+
150
+ estimate_reference = result.dig('quote', 'quote_number')
151
+
152
+ prices = []
153
+
154
+ prices << Price.new(
155
+ blame: :api,
156
+ cents: parse_amount(result.dig(:quote, :gross_charge)),
157
+ description: 'Charge based on class and weight',
158
+ )
159
+
160
+ accessorial_details = result.dig(:quote, :accessorial_detail)
161
+ accessorial_details = [accessorial_details] if accessorial_details.is_a?(Hash)
162
+
163
+ accessorial_details.each do |accessorial_detail|
164
+ accessorial_item = accessorial_detail[:accessorial_item]
165
+
166
+ prices << Price.new(
167
+ blame: :api,
168
+ cents: parse_amount(accessorial_item[:charge]),
169
+ description: accessorial_item[:description]&.capitalize,
170
+ )
171
+ end
172
+
173
+ prices << Price.new(
174
+ blame: :api,
175
+ cents: parse_amount(result.dig(:quote, :discount_amount)),
176
+ description: 'Discount',
177
+ )
178
+
179
+ prices << Price.new(
180
+ blame: :api,
181
+ cents: parse_amount(result.dig(:quote, :fsc_amount)),
182
+ description: 'Fuel surcharge',
183
+ )
184
+
185
+ rate = Rate.new(
186
+ carrier: self,
187
+ carrier_name: self.class.name,
188
+ currency: 'USD',
189
+ estimate_reference:,
190
+ scac: self.class.scac.upcase,
191
+ service_name: :standard,
192
+ shipment:,
193
+ prices:,
194
+ transit_days:,
195
+ with_excessive_length_fees: @conf.dig(:attributes, :rates, :with_excessive_length_fees),
196
+ )
197
+
198
+ rate_response.rates = [rate]
199
+ rate_response
200
+ end
201
+
202
+ # Tracking
203
+ end
204
+ end