freight_kit 0.1.20 → 0.1.21

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 (434) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -2
  3. data/README.md +1 -2
  4. data/Rakefile +6 -0
  5. data/VERSION +1 -1
  6. data/configuration/locations/amazon_warehouses.yml +3330 -0
  7. data/freight_kit.gemspec +13 -13
  8. data/lib/freight_kit/address_normalizer.rb +26 -0
  9. data/lib/freight_kit/amazon_warehouse.rb +33 -0
  10. data/lib/freight_kit/api_clients/soap_client.rb +7 -9
  11. data/lib/freight_kit/carrier.rb +250 -303
  12. data/lib/freight_kit/carriers/a1_express.rb +25 -0
  13. data/lib/freight_kit/carriers/a_duie_pyle.rb +30 -0
  14. data/lib/freight_kit/carriers/aaa_cooper.rb +11 -0
  15. data/lib/freight_kit/carriers/aaa_expedited_services.rb +10 -0
  16. data/lib/freight_kit/carriers/ab_brothers_usa_inc.rb +12 -0
  17. data/lib/freight_kit/carriers/aberdeen_express_inc.rb +28 -0
  18. data/lib/freight_kit/carriers/abf_freight_system.rb +492 -0
  19. data/lib/freight_kit/carriers/access_transport.rb +10 -0
  20. data/lib/freight_kit/carriers/action_express.rb +10 -0
  21. data/lib/freight_kit/carriers/adams_and_companies.rb +10 -0
  22. data/lib/freight_kit/carriers/advance_distribution.rb +28 -0
  23. data/lib/freight_kit/carriers/alcon_transport.rb +10 -0
  24. data/lib/freight_kit/carriers/all_freight.rb +10 -0
  25. data/lib/freight_kit/carriers/alliance_transportation_group.rb +10 -0
  26. data/lib/freight_kit/carriers/allstate_transport.rb +10 -0
  27. data/lib/freight_kit/carriers/aloha_freight_forwarders.rb +35 -0
  28. data/lib/freight_kit/carriers/amado_trucking.rb +35 -0
  29. data/lib/freight_kit/carriers/american_fast_freight.rb +28 -0
  30. data/lib/freight_kit/carriers/amigos_service_transportation.rb +10 -0
  31. data/lib/freight_kit/carriers/anderson_towing_and_transport.rb +10 -0
  32. data/lib/freight_kit/carriers/apex_transporting.rb +10 -0
  33. data/lib/freight_kit/carriers/archer_cargo.rb +35 -0
  34. data/lib/freight_kit/carriers/armstrong_transport_group.rb +10 -0
  35. data/lib/freight_kit/carriers/autobahn_logistics.rb +10 -0
  36. data/lib/freight_kit/carriers/averitt_express.rb +30 -0
  37. data/lib/freight_kit/carriers/ba_transportation.rb +10 -0
  38. data/lib/freight_kit/carriers/balkin_express.rb +10 -0
  39. data/lib/freight_kit/carriers/bay_area_express.rb +10 -0
  40. data/lib/freight_kit/carriers/bc_freightways.rb +11 -0
  41. data/lib/freight_kit/carriers/benjamin_best_freight_inc.rb +37 -0
  42. data/lib/freight_kit/carriers/bennett_trucking.rb +10 -0
  43. data/lib/freight_kit/carriers/best_overnite_express.rb +78 -0
  44. data/lib/freight_kit/carriers/best_trucking.rb +10 -0
  45. data/lib/freight_kit/carriers/bestway_transport.rb +10 -0
  46. data/lib/freight_kit/carriers/bryan_systems.rb +10 -0
  47. data/lib/freight_kit/carriers/c_and_w_express.rb +10 -0
  48. data/lib/freight_kit/carriers/campbell_trucking.rb +10 -0
  49. data/lib/freight_kit/carriers/carde_transportation.rb +10 -0
  50. data/lib/freight_kit/carriers/cardinal_logistics.rb +28 -0
  51. data/lib/freight_kit/carriers/cdc_transport.rb +10 -0
  52. data/lib/freight_kit/carriers/cdo_express_diversified.rb +10 -0
  53. data/lib/freight_kit/carriers/cedar_supply_trucking.rb +10 -0
  54. data/lib/freight_kit/carriers/central_freight_lines.rb +12 -0
  55. data/lib/freight_kit/carriers/central_transport.rb +12 -0
  56. data/lib/freight_kit/carriers/chicago_suburban_express.rb +28 -0
  57. data/lib/freight_kit/carriers/city_logistics.rb +11 -0
  58. data/lib/freight_kit/carriers/clear_lane_freight_systems.rb +483 -0
  59. data/lib/freight_kit/carriers/cleopatra_trucking.rb +10 -0
  60. data/lib/freight_kit/carriers/clifford_transportation.rb +10 -0
  61. data/lib/freight_kit/carriers/cms_domestic.rb +25 -0
  62. data/lib/freight_kit/carriers/cnd_logistics.rb +10 -0
  63. data/lib/freight_kit/carriers/coast_to_coast_transport.rb +27 -0
  64. data/lib/freight_kit/carriers/countrywide_carriers.rb +10 -0
  65. data/lib/freight_kit/carriers/cpl_express.rb +35 -0
  66. data/lib/freight_kit/carriers/crosby_trucking_service.rb +29 -0
  67. data/lib/freight_kit/carriers/cross_country_freight_solutions.rb +419 -0
  68. data/lib/freight_kit/carriers/crowley.rb +10 -0
  69. data/lib/freight_kit/carriers/csa_transportation.rb +40 -0
  70. data/lib/freight_kit/carriers/csx_transportation.rb +40 -0
  71. data/lib/freight_kit/carriers/custom_global_logistics.rb +14 -0
  72. data/lib/freight_kit/carriers/d_and_a_transport.rb +10 -0
  73. data/lib/freight_kit/carriers/dak_transport.rb +10 -0
  74. data/lib/freight_kit/carriers/dash_courier_and_logistics_north_carolina.rb +10 -0
  75. data/lib/freight_kit/carriers/daylight_transport.rb +29 -0
  76. data/lib/freight_kit/carriers/dayton_freight_lines.rb +30 -0
  77. data/lib/freight_kit/carriers/dbr_logistics.rb +10 -0
  78. data/lib/freight_kit/carriers/deep_south_freight.rb +27 -0
  79. data/lib/freight_kit/carriers/delta_express.rb +10 -0
  80. data/lib/freight_kit/carriers/delta_transport.rb +10 -0
  81. data/lib/freight_kit/carriers/delta_trucking_logistics.rb +10 -0
  82. data/lib/freight_kit/carriers/demco_express.rb +10 -0
  83. data/lib/freight_kit/carriers/dependable_global_express.rb +10 -0
  84. data/lib/freight_kit/carriers/dependable_hawaiian_express.rb +24 -0
  85. data/lib/freight_kit/carriers/dependable_highway_express.rb +585 -0
  86. data/lib/freight_kit/carriers/desert_coastal_transport.rb +10 -0
  87. data/lib/freight_kit/carriers/dhl.rb +11 -0
  88. data/lib/freight_kit/carriers/diamond_line_delivery.rb +174 -0
  89. data/lib/freight_kit/carriers/ding_tai_trucking.rb +10 -0
  90. data/lib/freight_kit/carriers/diversified_transfer_and_storage.rb +29 -0
  91. data/lib/freight_kit/carriers/dohrn_transfer_company.rb +30 -0
  92. data/lib/freight_kit/carriers/dot_line_transportation.rb +29 -0
  93. data/lib/freight_kit/carriers/drt_transportation.rb +13 -0
  94. data/lib/freight_kit/carriers/dugan_truck_line.rb +29 -0
  95. data/lib/freight_kit/carriers/dupont.rb +10 -0
  96. data/lib/freight_kit/carriers/e_transport_carriers.rb +29 -0
  97. data/lib/freight_kit/carriers/echo_global_logistics.rb +13 -0
  98. data/lib/freight_kit/carriers/ecklund_logistics.rb +10 -0
  99. data/lib/freight_kit/carriers/el_emplumado_transport.rb +10 -0
  100. data/lib/freight_kit/carriers/elite_carriers.rb +28 -0
  101. data/lib/freight_kit/carriers/emerald_express.rb +10 -0
  102. data/lib/freight_kit/carriers/era_transport.rb +10 -0
  103. data/lib/freight_kit/carriers/estes_express_lines.rb +25 -0
  104. data/lib/freight_kit/carriers/euro_kam.rb +28 -0
  105. data/lib/freight_kit/carriers/ew_riverside_trucking.rb +10 -0
  106. data/lib/freight_kit/carriers/express_2000_inc.rb +12 -0
  107. data/lib/freight_kit/carriers/express_services_international.rb +10 -0
  108. data/lib/freight_kit/carriers/f_and_a_express.rb +27 -0
  109. data/lib/freight_kit/carriers/fairmont_logistics.rb +13 -0
  110. data/lib/freight_kit/carriers/fargo_cargo.rb +10 -0
  111. data/lib/freight_kit/carriers/fast_freight_transportation.rb +10 -0
  112. data/lib/freight_kit/carriers/fast_load_transport.rb +10 -0
  113. data/lib/freight_kit/carriers/fed_ex_express.rb +30 -0
  114. data/lib/freight_kit/carriers/fed_ex_ground.rb +24 -0
  115. data/lib/freight_kit/carriers/fed_ex_ltl_economy.rb +11 -0
  116. data/lib/freight_kit/carriers/fed_ex_ltl_priority.rb +11 -0
  117. data/lib/freight_kit/carriers/first_trucking_express.rb +10 -0
  118. data/lib/freight_kit/carriers/flash_trucking.rb +10 -0
  119. data/lib/freight_kit/carriers/fort_transportation_and_service_company_inc.rb +37 -0
  120. data/lib/freight_kit/carriers/forward_air.rb +789 -0
  121. data/lib/freight_kit/carriers/frahtex.rb +10 -0
  122. data/lib/freight_kit/carriers/frozen_food_express.rb +10 -0
  123. data/lib/freight_kit/carriers/gholia_bros_trucking.rb +36 -0
  124. data/lib/freight_kit/carriers/global_cargo.rb +10 -0
  125. data/lib/freight_kit/carriers/global_logistics_and_transportation.rb +10 -0
  126. data/lib/freight_kit/carriers/gls_freight.rb +32 -0
  127. data/lib/freight_kit/carriers/go_trucking_services.rb +10 -0
  128. data/lib/freight_kit/carriers/gold_eagle_transportation.rb +11 -0
  129. data/lib/freight_kit/carriers/golden_drop_trucking.rb +10 -0
  130. data/lib/freight_kit/carriers/gonzalez_transportation.rb +10 -0
  131. data/lib/freight_kit/carriers/gps_transport.rb +10 -0
  132. data/lib/freight_kit/carriers/greene_enterprise.rb +10 -0
  133. data/lib/freight_kit/carriers/gsa_international.rb +10 -0
  134. data/lib/freight_kit/carriers/gsp_express.rb +24 -0
  135. data/lib/freight_kit/carriers/gsv_transportation.rb +10 -0
  136. data/lib/freight_kit/carriers/gta_transportation.rb +10 -0
  137. data/lib/freight_kit/carriers/guam_freight_services.rb +10 -0
  138. data/lib/freight_kit/carriers/harrah_transportation.rb +23 -0
  139. data/lib/freight_kit/carriers/her_trucking.rb +10 -0
  140. data/lib/freight_kit/carriers/hercules_freight.rb +20 -0
  141. data/lib/freight_kit/carriers/highlander_xpress.rb +36 -0
  142. data/lib/freight_kit/carriers/hn_transport.rb +10 -0
  143. data/lib/freight_kit/carriers/honolulu_freight_service.rb +10 -0
  144. data/lib/freight_kit/carriers/hot_line_freight_system_inc.rb +12 -0
  145. data/lib/freight_kit/carriers/hotshot_freight_of_texas.rb +13 -0
  146. data/lib/freight_kit/carriers/houser_transport.rb +27 -0
  147. data/lib/freight_kit/carriers/infiniti_freight_logistics.rb +28 -0
  148. data/lib/freight_kit/carriers/infinity_trucking.rb +10 -0
  149. data/lib/freight_kit/carriers/interstate_tma.rb +10 -0
  150. data/lib/freight_kit/carriers/intrade_industries.rb +10 -0
  151. data/lib/freight_kit/carriers/jasper_trucking.rb +28 -0
  152. data/lib/freight_kit/carriers/jb_hunt.rb +11 -0
  153. data/lib/freight_kit/carriers/jfj_transportation.rb +8 -8
  154. data/lib/freight_kit/carriers/jkb_express.rb +10 -0
  155. data/lib/freight_kit/carriers/jlm_transport.rb +10 -0
  156. data/lib/freight_kit/carriers/johansson_transport.rb +10 -0
  157. data/lib/freight_kit/carriers/jones_express.rb +10 -0
  158. data/lib/freight_kit/carriers/jp_logistics.rb +41 -0
  159. data/lib/freight_kit/carriers/just_in_time_cargo.rb +29 -0
  160. data/lib/freight_kit/carriers/kengkok_times.rb +10 -0
  161. data/lib/freight_kit/carriers/kings_express.rb +10 -0
  162. data/lib/freight_kit/carriers/klc_transportation.rb +10 -0
  163. data/lib/freight_kit/carriers/km_trucking.rb +10 -0
  164. data/lib/freight_kit/carriers/kma_logistics.rb +10 -0
  165. data/lib/freight_kit/carriers/krc_logistics.rb +10 -0
  166. data/lib/freight_kit/carriers/ktx.rb +10 -0
  167. data/lib/freight_kit/carriers/landstar.rb +25 -0
  168. data/lib/freight_kit/carriers/lanter_distributing_llc.rb +30 -0
  169. data/lib/freight_kit/carriers/ld_trucking.rb +10 -0
  170. data/lib/freight_kit/carriers/legacy_logistics_solutions.rb +10 -0
  171. data/lib/freight_kit/carriers/leonards_express.rb +10 -0
  172. data/lib/freight_kit/carriers/liberty_linehaul_west.rb +28 -0
  173. data/lib/freight_kit/carriers/liberty_transport.rb +10 -0
  174. data/lib/freight_kit/carriers/lightning_express.rb +10 -0
  175. data/lib/freight_kit/carriers/lj_express.rb +24 -0
  176. data/lib/freight_kit/carriers/lkp_enterprises.rb +10 -0
  177. data/lib/freight_kit/carriers/ltl_pros.rb +28 -0
  178. data/lib/freight_kit/carriers/m_and_m_and_sons.rb +10 -0
  179. data/lib/freight_kit/carriers/mag_lines_transport.rb +10 -0
  180. data/lib/freight_kit/carriers/magic_messenger.rb +24 -0
  181. data/lib/freight_kit/carriers/majors_transit.rb +10 -0
  182. data/lib/freight_kit/carriers/map_global_logistics.rb +10 -0
  183. data/lib/freight_kit/carriers/matths_trucking.rb +11 -0
  184. data/lib/freight_kit/carriers/mav_canadian_transport.rb +13 -0
  185. data/lib/freight_kit/carriers/mc_systems.rb +35 -0
  186. data/lib/freight_kit/carriers/mega_fleet.rb +10 -0
  187. data/lib/freight_kit/carriers/mega_trans_group.rb +28 -0
  188. data/lib/freight_kit/carriers/meta_express.rb +10 -0
  189. data/lib/freight_kit/carriers/mia_transportation.rb +10 -0
  190. data/lib/freight_kit/carriers/midwest_motor_express.rb +14 -0
  191. data/lib/freight_kit/carriers/midwest_transport_and_trucking.rb +10 -0
  192. data/lib/freight_kit/carriers/mikade_delivery_service.rb +10 -0
  193. data/lib/freight_kit/carriers/mohoff_freight_lines.rb +23 -0
  194. data/lib/freight_kit/carriers/montata_travelers_express.rb +10 -0
  195. data/lib/freight_kit/carriers/morgan_van_lines.rb +29 -0
  196. data/lib/freight_kit/carriers/moto_transportation.rb +24 -0
  197. data/lib/freight_kit/carriers/mountain_valley_express.rb +197 -0
  198. data/lib/freight_kit/carriers/ms_freight.rb +27 -0
  199. data/lib/freight_kit/carriers/mvg_transport.rb +10 -0
  200. data/lib/freight_kit/carriers/n_and_m_transfer_co_inc.rb +29 -0
  201. data/lib/freight_kit/carriers/nabot_transport_solutions.rb +10 -0
  202. data/lib/freight_kit/carriers/national_freight_xpress.rb +10 -0
  203. data/lib/freight_kit/carriers/navigation_group.rb +10 -0
  204. data/lib/freight_kit/carriers/nelson_trucking.rb +10 -0
  205. data/lib/freight_kit/carriers/network_1_transportation.rb +10 -0
  206. data/lib/freight_kit/carriers/new_century_transport.rb +28 -0
  207. data/lib/freight_kit/carriers/new_penn_motor_express.rb +11 -0
  208. data/lib/freight_kit/carriers/nieves_transportation.rb +10 -0
  209. data/lib/freight_kit/carriers/nolan_transportation_group.rb +26 -0
  210. data/lib/freight_kit/carriers/northland_trucking.rb +10 -0
  211. data/lib/freight_kit/carriers/northwest_carrier_logistics.rb +27 -0
  212. data/lib/freight_kit/carriers/nt_usa.rb +10 -0
  213. data/lib/freight_kit/carriers/nu_line_transportation.rb +10 -0
  214. data/lib/freight_kit/carriers/numark_transportation.rb +80 -0
  215. data/lib/freight_kit/carriers/nvk_transport.rb +10 -0
  216. data/lib/freight_kit/carriers/ny_transport.rb +10 -0
  217. data/lib/freight_kit/carriers/oak_harbor.rb +28 -0
  218. data/lib/freight_kit/carriers/oakline_transportation.rb +10 -0
  219. data/lib/freight_kit/carriers/old_dominion_freight_line.rb +30 -0
  220. data/lib/freight_kit/carriers/on_trac.rb +573 -0
  221. data/lib/freight_kit/carriers/one_hundred_one_transport.rb +10 -0
  222. data/lib/freight_kit/carriers/one_way_delivery.rb +10 -0
  223. data/lib/freight_kit/carriers/oracle_transportation_solutions.rb +10 -0
  224. data/lib/freight_kit/carriers/orange_courier.rb +27 -0
  225. data/lib/freight_kit/carriers/ortas_trucking.rb +10 -0
  226. data/lib/freight_kit/carriers/pac_trux_transportation.rb +10 -0
  227. data/lib/freight_kit/carriers/pacer.rb +10 -0
  228. data/lib/freight_kit/carriers/pacific_logistics.rb +10 -0
  229. data/lib/freight_kit/carriers/pacific_states_truck_lines.rb +10 -0
  230. data/lib/freight_kit/carriers/paradise_distributing.rb +10 -0
  231. data/lib/freight_kit/carriers/paramount_freight_systems.rb +10 -0
  232. data/lib/freight_kit/carriers/peninsula_truck_lines.rb +222 -0
  233. data/lib/freight_kit/carriers/pg_transport.rb +10 -0
  234. data/lib/freight_kit/carriers/pgs_360.rb +10 -0
  235. data/lib/freight_kit/carriers/pilot_freight_services.rb +10 -0
  236. data/lib/freight_kit/carriers/pious_trans.rb +27 -0
  237. data/lib/freight_kit/carriers/pitt_ohio_express.rb +30 -0
  238. data/lib/freight_kit/carriers/pki_logistics.rb +12 -0
  239. data/lib/freight_kit/carriers/plana_transportation.rb +28 -0
  240. data/lib/freight_kit/carriers/prestige.rb +10 -0
  241. data/lib/freight_kit/carriers/pro_trans_solutions.rb +28 -0
  242. data/lib/freight_kit/carriers/puma_logistics.rb +10 -0
  243. data/lib/freight_kit/carriers/pwc_international.rb +10 -0
  244. data/lib/freight_kit/carriers/quality_logistics.rb +10 -0
  245. data/lib/freight_kit/carriers/queen_star_trucking.rb +27 -0
  246. data/lib/freight_kit/carriers/r_and_l_carriers.rb +11 -0
  247. data/lib/freight_kit/carriers/rac_transport.rb +10 -0
  248. data/lib/freight_kit/carriers/re_logistics.rb +23 -0
  249. data/lib/freight_kit/carriers/reddaway.rb +11 -0
  250. data/lib/freight_kit/carriers/reefer_van_network.rb +13 -0
  251. data/lib/freight_kit/carriers/refrigerated_trucking_and_ltl_transportation_services.rb +10 -0
  252. data/lib/freight_kit/carriers/ridgeline_express.rb +10 -0
  253. data/lib/freight_kit/carriers/rist_transport.rb +29 -0
  254. data/lib/freight_kit/carriers/river_city_transport.rb +10 -0
  255. data/lib/freight_kit/carriers/roadrunner_transportation_services.rb +675 -0
  256. data/lib/freight_kit/carriers/roadtex.rb +13 -0
  257. data/lib/freight_kit/carriers/ronald_transport_services.rb +10 -0
  258. data/lib/freight_kit/carriers/rose_trucking.rb +24 -0
  259. data/lib/freight_kit/carriers/rp_and_j_transport.rb +24 -0
  260. data/lib/freight_kit/carriers/rpm_transportation.rb +11 -0
  261. data/lib/freight_kit/carriers/rude_transportation.rb +30 -0
  262. data/lib/freight_kit/carriers/s_and_s_logistics.rb +10 -0
  263. data/lib/freight_kit/carriers/saia.rb +470 -361
  264. data/lib/freight_kit/carriers/sb_transportation.rb +27 -0
  265. data/lib/freight_kit/carriers/schneider_national.rb +29 -0
  266. data/lib/freight_kit/carriers/schwarz_trucking.rb +10 -0
  267. data/lib/freight_kit/carriers/sewell_motor_express.rb +29 -0
  268. data/lib/freight_kit/carriers/shawnee_trucking.rb +28 -0
  269. data/lib/freight_kit/carriers/shi_logistics.rb +10 -0
  270. data/lib/freight_kit/carriers/sinbad_transport.rb +10 -0
  271. data/lib/freight_kit/carriers/skyway.rb +10 -0
  272. data/lib/freight_kit/carriers/smith_cargo_transportation_services.rb +23 -0
  273. data/lib/freight_kit/carriers/sna_transportation.rb +10 -0
  274. data/lib/freight_kit/carriers/solid_group.rb +10 -0
  275. data/lib/freight_kit/carriers/solution_carrier_express.rb +10 -0
  276. data/lib/freight_kit/carriers/southeastern_freight_lines.rb +450 -0
  277. data/lib/freight_kit/carriers/southwestern_motor_transport.rb +30 -0
  278. data/lib/freight_kit/carriers/span_alaska.rb +10 -0
  279. data/lib/freight_kit/carriers/specialize_transportation.rb +27 -0
  280. data/lib/freight_kit/carriers/ssi_refrigerated_express.rb +10 -0
  281. data/lib/freight_kit/carriers/standard_forwarding_llc.rb +28 -0
  282. data/lib/freight_kit/carriers/star_american_enterprise.rb +10 -0
  283. data/lib/freight_kit/carriers/sterling_transportation.rb +10 -0
  284. data/lib/freight_kit/carriers/stg.rb +163 -0
  285. data/lib/freight_kit/carriers/supervan_service.rb +10 -0
  286. data/lib/freight_kit/carriers/sutton_transport_inc.rb +12 -0
  287. data/lib/freight_kit/carriers/swift_transportation.rb +28 -0
  288. data/lib/freight_kit/carriers/t_force_worldwide.rb +617 -0
  289. data/lib/freight_kit/carriers/tax_airfreight_inc.rb +35 -0
  290. data/lib/freight_kit/carriers/tforce_freight_inc.rb +30 -0
  291. data/lib/freight_kit/carriers/the_custom_companies.rb +162 -0
  292. data/lib/freight_kit/carriers/thk_trucking.rb +28 -0
  293. data/lib/freight_kit/carriers/tiger_cool_express.rb +11 -0
  294. data/lib/freight_kit/carriers/tili_logistics.rb +10 -0
  295. data/lib/freight_kit/carriers/tmt_industries.rb +10 -0
  296. data/lib/freight_kit/carriers/top_ten_express.rb +28 -0
  297. data/lib/freight_kit/carriers/total_quality_logistics.rb +338 -0
  298. data/lib/freight_kit/carriers/total_transportation.rb +258 -0
  299. data/lib/freight_kit/carriers/trans_international_trucking.rb +29 -0
  300. data/lib/freight_kit/carriers/treasure_transportation.rb +10 -0
  301. data/lib/freight_kit/carriers/tri_state_services.rb +10 -0
  302. data/lib/freight_kit/carriers/tritex_freighters.rb +10 -0
  303. data/lib/freight_kit/carriers/triways.rb +10 -0
  304. data/lib/freight_kit/carriers/tsi_kansas.rb +29 -0
  305. data/lib/freight_kit/carriers/u_and_r_transportation.rb +23 -0
  306. data/lib/freight_kit/carriers/union_pacific.rb +11 -0
  307. data/lib/freight_kit/carriers/united_freight_logistics.rb +10 -0
  308. data/lib/freight_kit/carriers/unlimited_distance_transportation.rb +10 -0
  309. data/lib/freight_kit/carriers/up_streamline.rb +10 -0
  310. data/lib/freight_kit/carriers/ups_freight.rb +12 -0
  311. data/lib/freight_kit/carriers/us_freightways.rb +10 -0
  312. data/lib/freight_kit/carriers/usa_truck.rb +28 -0
  313. data/lib/freight_kit/carriers/usf_holland.rb +11 -0
  314. data/lib/freight_kit/carriers/valley_cartage.rb +11 -0
  315. data/lib/freight_kit/carriers/vc_transport.rb +10 -0
  316. data/lib/freight_kit/carriers/vision_express.rb +11 -0
  317. data/lib/freight_kit/carriers/vision_logistics_group.rb +10 -0
  318. data/lib/freight_kit/carriers/vmg_transportation_group.rb +10 -0
  319. data/lib/freight_kit/carriers/volpe_express.rb +10 -0
  320. data/lib/freight_kit/carriers/ward_trucking.rb +29 -0
  321. data/lib/freight_kit/carriers/wdwb.rb +10 -0
  322. data/lib/freight_kit/carriers/western_flyer_express.rb +28 -0
  323. data/lib/freight_kit/carriers/western_regional_delivery_service.rb +271 -0
  324. data/lib/freight_kit/carriers/western_specialized_trucking.rb +22 -0
  325. data/lib/freight_kit/carriers/win_distribution.rb +28 -0
  326. data/lib/freight_kit/carriers/wlb_transport.rb +10 -0
  327. data/lib/freight_kit/carriers/x_trux.rb +29 -0
  328. data/lib/freight_kit/carriers/xpo_logistics.rb +427 -0
  329. data/lib/freight_kit/carriers/xpress_global_systems.rb +30 -0
  330. data/lib/freight_kit/carriers/xylo_trucking.rb +10 -0
  331. data/lib/freight_kit/carriers/yrc_freight.rb +24 -0
  332. data/lib/freight_kit/carriers/zentner_transportation.rb +28 -0
  333. data/lib/freight_kit/carriers.rb +331 -34
  334. data/lib/freight_kit/countries.rb +22 -0
  335. data/lib/freight_kit/error.rb +0 -2
  336. data/lib/freight_kit/errors/document_not_found_error.rb +0 -2
  337. data/lib/freight_kit/errors/expired_credentials_error.rb +0 -2
  338. data/lib/freight_kit/errors/http_error.rb +0 -2
  339. data/lib/freight_kit/errors/invalid_credentials_error.rb +0 -2
  340. data/lib/freight_kit/errors/response_error.rb +0 -2
  341. data/lib/freight_kit/errors/shipment_not_found_error.rb +0 -2
  342. data/lib/freight_kit/errors/unserviceable_accessorials_error.rb +2 -4
  343. data/lib/freight_kit/errors/unserviceable_error.rb +0 -2
  344. data/lib/freight_kit/helpers/documentable.rb +0 -2
  345. data/lib/freight_kit/helpers/pickupable.rb +3 -5
  346. data/lib/freight_kit/helpers/rateable.rb +0 -2
  347. data/lib/freight_kit/helpers/trackable.rb +0 -2
  348. data/lib/freight_kit/http_client/cache_store.rb +2 -4
  349. data/lib/freight_kit/http_client/log_device.rb +0 -2
  350. data/lib/freight_kit/http_client/response.rb +0 -2
  351. data/lib/freight_kit/http_client/revalidation.rb +0 -2
  352. data/lib/freight_kit/http_client/safe_retries.rb +0 -2
  353. data/lib/freight_kit/http_client/zstd.rb +2 -4
  354. data/lib/freight_kit/http_client.rb +8 -12
  355. data/lib/freight_kit/model.rb +1 -3
  356. data/lib/freight_kit/models/address.rb +15 -0
  357. data/lib/freight_kit/models/contact.rb +3 -4
  358. data/lib/freight_kit/models/country.rb +20 -0
  359. data/lib/freight_kit/models/credential.rb +14 -16
  360. data/lib/freight_kit/models/date_time.rb +0 -2
  361. data/lib/freight_kit/models/document_response.rb +0 -2
  362. data/lib/freight_kit/models/label.rb +0 -2
  363. data/lib/freight_kit/models/location.rb +10 -25
  364. data/lib/freight_kit/models/pickup_response.rb +0 -2
  365. data/lib/freight_kit/models/price.rb +0 -2
  366. data/lib/freight_kit/models/rate.rb +0 -2
  367. data/lib/freight_kit/models/rate_response.rb +0 -2
  368. data/lib/freight_kit/models/region.rb +19 -0
  369. data/lib/freight_kit/models/response.rb +0 -2
  370. data/lib/freight_kit/models/shipment.rb +4 -6
  371. data/lib/freight_kit/models/shipment_event.rb +0 -2
  372. data/lib/freight_kit/models/terminal.rb +10 -0
  373. data/lib/freight_kit/models/tracking_response.rb +0 -2
  374. data/lib/freight_kit/package.rb +51 -53
  375. data/lib/freight_kit/package_item.rb +14 -14
  376. data/lib/freight_kit/packaging.rb +21 -26
  377. data/lib/freight_kit/platform.rb +0 -32
  378. data/lib/freight_kit/platforms/carrier_logistics.rb +331 -345
  379. data/lib/freight_kit/platforms/next_tms.rb +93 -0
  380. data/lib/freight_kit/platforms/the_great_information_factory.rb +500 -425
  381. data/lib/freight_kit/regions.rb +27 -0
  382. data/lib/freight_kit/shipment_packer.rb +36 -38
  383. data/lib/freight_kit/tariff/overlength_rule.rb +1 -5
  384. data/lib/freight_kit/tariff.rb +0 -2
  385. data/lib/freight_kit.rb +84 -11
  386. metadata +361 -51
  387. data/configuration/carriers/abfs.yml +0 -124
  388. data/configuration/carriers/btvp.yml +0 -84
  389. data/configuration/carriers/ccyq.yml +0 -121
  390. data/configuration/carriers/clni.yml +0 -113
  391. data/configuration/carriers/cnwy.yml +0 -113
  392. data/configuration/carriers/ctbv.yml +0 -117
  393. data/configuration/carriers/dcha.yml +0 -105
  394. data/configuration/carriers/dlds.yml +0 -111
  395. data/configuration/carriers/dphe.yml +0 -130
  396. data/configuration/carriers/drrq.yml +0 -131
  397. data/configuration/carriers/fcsy.yml +0 -102
  398. data/configuration/carriers/fwda.yml +0 -137
  399. data/configuration/carriers/jfj_transportation.yml +0 -2
  400. data/configuration/carriers/mtvl.yml +0 -12
  401. data/configuration/carriers/numk.yml +0 -14
  402. data/configuration/carriers/otcl.yml +0 -124
  403. data/configuration/carriers/pens.yml +0 -22
  404. data/configuration/carriers/rdfs.yml +0 -142
  405. data/configuration/carriers/saia.yml +0 -129
  406. data/configuration/carriers/sefl.yml +0 -115
  407. data/configuration/carriers/totl.yml +0 -111
  408. data/configuration/carriers/tqyl.yml +0 -28
  409. data/configuration/carriers/wrds.yml +0 -20
  410. data/configuration/platforms/carrier_logistics.yml +0 -25
  411. data/configuration/platforms/next.yml +0 -12
  412. data/configuration/platforms/the_great_information_factory.yml +0 -122
  413. data/lib/freight_kit/carriers/abfs.rb +0 -421
  414. data/lib/freight_kit/carriers/btvp.rb +0 -29
  415. data/lib/freight_kit/carriers/ccyq.rb +0 -310
  416. data/lib/freight_kit/carriers/clni.rb +0 -396
  417. data/lib/freight_kit/carriers/cnwy.rb +0 -327
  418. data/lib/freight_kit/carriers/ctbv.rb +0 -53
  419. data/lib/freight_kit/carriers/dcha.rb +0 -76
  420. data/lib/freight_kit/carriers/dlds.rb +0 -49
  421. data/lib/freight_kit/carriers/dphe.rb +0 -472
  422. data/lib/freight_kit/carriers/drrq.rb +0 -580
  423. data/lib/freight_kit/carriers/fcsy.rb +0 -57
  424. data/lib/freight_kit/carriers/fwda.rb +0 -744
  425. data/lib/freight_kit/carriers/mtvl.rb +0 -34
  426. data/lib/freight_kit/carriers/numk.rb +0 -58
  427. data/lib/freight_kit/carriers/otcl.rb +0 -520
  428. data/lib/freight_kit/carriers/pens.rb +0 -204
  429. data/lib/freight_kit/carriers/rdfs.rb +0 -521
  430. data/lib/freight_kit/carriers/sefl.rb +0 -342
  431. data/lib/freight_kit/carriers/totl.rb +0 -172
  432. data/lib/freight_kit/carriers/tqyl.rb +0 -337
  433. data/lib/freight_kit/carriers/wrds.rb +0 -246
  434. data/lib/freight_kit/platforms/next.rb +0 -101
@@ -1,450 +1,436 @@
1
- # frozen_string_literal: true
2
-
3
1
  module FreightKit
4
- class CarrierLogistics < Platform
5
- class << self
6
- def required_credential_types
7
- %i[api]
8
- end
2
+ module Platforms
3
+ class CarrierLogistics < Platform
4
+ class << self
5
+ def required_credential_types = %i[api]
9
6
 
10
- def requirements
11
- return %i[credentials tariff] if overlength_fees_require_tariff?
7
+ def requirements
8
+ return %i[credentials tariff] if overlength_fees_require_tariff?
12
9
 
13
- %i[credentials]
10
+ %i[credentials]
11
+ end
14
12
  end
15
- end
16
-
17
- REACTIVE_FREIGHT_PLATFORM = true
18
13
 
19
- EXPIRED_CREDENTIALS_MESSAGES = [
20
- 'Your password has expired',
21
- ].freeze
22
- INVALID_CREDENTIALS_MESSAGES = [
23
- 'Unable to log in',
24
- 'Your Username or Password is Incorrect',
25
- ].freeze
14
+ include FreightKit::Trackable
15
+ include FreightKit::Rateable
16
+
17
+ API = {
18
+ domain: 'tracking.carrierlogistics.com',
19
+ endpoints: {
20
+ bol: '/scripts/@CARRIER_CODE@.pol/web-login.htm?',
21
+ pod: '/scripts/@CARRIER_CODE@.pol/web-login.htm?',
22
+ rates: '/scripts/@CARRIER_CODE@.pol/ratequote.xml?',
23
+ track: '/scripts/@CARRIER_CODE@.pol/protracexml.htm?'
24
+ },
25
+ use_ssl: { bol: true, pod: true, rates: true, track: true }
26
+ }.freeze
27
+ EVENTS = {
28
+ types: {
29
+ arrived_at_terminal: 'trailer arrived',
30
+ delivered: 'delivered',
31
+ departed: 'trailer departed',
32
+ out_for_delivery: 'out for delivery',
33
+ picked_up: 'picked up',
34
+ pickup_scheduled: 'pickup request received',
35
+ trailer_closed: 'loaded on trailer',
36
+ trailer_unloaded: 'undisp'
37
+ }
38
+ }.freeze
39
+ EXPIRED_CREDENTIALS_MESSAGES = ['Your password has expired'].freeze
40
+ INVALID_CREDENTIALS_MESSAGES = ['Unable to log in', 'Your Username or Password is Incorrect'].freeze
41
+
42
+ # Documents
43
+
44
+ def build_calculated_accessorials(_shipment) = []
45
+
46
+ def build_rate_request(shipment:)
47
+ api_credentials = fetch_credential(:api)
48
+
49
+ query = {
50
+ xmlv: 'yes', # must be first
51
+ quotenumber: 'YES',
52
+ vdzip: shipment.destination.postal_code,
53
+ vozip: shipment.origin.postal_code,
54
+ xmlpass: api_credentials.password,
55
+ xmluser: api_credentials.username
56
+ }
57
+
58
+ i = 0
59
+ shipment.packages.each do |package|
60
+ i += 1 # API starts at 1 (not 0)
61
+
62
+ query["vclass[#{i}]"] = package.freight_class
63
+ query["wheight[#{i}]"] = package.height(:in).ceil
64
+ query["wlength[#{i}]"] = package.length(:in).ceil
65
+ query["wpallets[#{i}]"] = package.quantity
66
+ query["wpieces[#{i}]"] = package.quantity
67
+ query["wweight[#{i}]"] = package.pounds(:total).ceil
68
+ query["wwidth[#{i}]"] = package.width(:in).ceil
69
+ end
26
70
 
27
- include FreightKit::Trackable
28
- include FreightKit::Rateable
71
+ accessorials = []
29
72
 
30
- # Documents
73
+ if shipment.accessorials.present?
74
+ serviceable_accessorials?(shipment.accessorials)
31
75
 
32
- def pod(tracking_number)
33
- query = build_tracking_request(tracking_number)
34
- response = commit(:track, query)
35
- parse_document_response(response, :pod)
36
- end
76
+ shipment
77
+ .accessorials
78
+ .reject { |accessorial| unquotable_accessorials.include?(accessorial) }
79
+ .each do |shipment_accessorial|
80
+ conf_accessorial = mappable_accessorials[shipment_accessorial]
37
81
 
38
- def scanned_bol(tracking_number)
39
- query = build_tracking_request(tracking_number)
40
- response = commit(:track, query)
41
- parse_document_response(response, :bol)
42
- end
82
+ case conf_accessorial
83
+ when Array then accessorials += conf_accessorial
84
+ when String then accessorials << conf_accessorial
85
+ end
86
+ end
87
+ end
43
88
 
44
- # protected
89
+ accessorials += build_calculated_accessorials(shipment)
45
90
 
46
- def build_url(action, query:)
47
- scheme = @conf.dig(:api, :use_ssl, action) ? 'https://' : 'http://'
91
+ accessorials.uniq.compact.each { |accessorial| query[accessorial] = 'Yes' } if accessorials.any?
48
92
 
49
- uri = URI.parse("#{scheme}#{@conf.dig(:api, :domain)}#{@conf.dig(:api, :endpoints, action)}")
50
- uri.query = query.to_query
51
- url = uri.to_s
52
- return url if url.exclude?('@CARRIER_CODE@')
93
+ query
94
+ end
53
95
 
54
- url.sub('@CARRIER_CODE@', @conf.dig(:api, :carrier_code))
55
- end
96
+ def build_tracking_request(tracking_number)
97
+ api_credentials = fetch_credential(:api)
56
98
 
57
- def commit(action, query)
58
- url = build_url(action, query:)
59
- save_request(url)
99
+ { pronum: tracking_number, xmlpass: api_credentials.password, xmluser: api_credentials.username }
100
+ end
60
101
 
61
- HTTPClient.get(url)
62
- end
102
+ def build_url(action, query:)
103
+ scheme = self.class::API.dig(:use_ssl, action) ? 'https://' : 'http://'
63
104
 
64
- def map_response_errors(response, not_found_error: DocumentNotFoundError)
65
- return ResponseError.new('Unknown response') if response.blank?
105
+ uri = URI.parse("#{scheme}#{self.class::API[:domain]}#{self.class::API.dig(:endpoints, action)}")
106
+ uri.query = query.to_query
107
+ url = uri.to_s
108
+ return url if url.exclude?('@CARRIER_CODE@')
66
109
 
67
- webspeed_error = (response.is_a?(String) || response.is_a?(HTTPClient::Response)) &&
68
- response.include?('WebSpeed error')
69
- return ResponseError.new('Temporary error (WebSpeed error)') if webspeed_error
110
+ url.sub('@CARRIER_CODE@', self.class::API[:carrier_code])
111
+ end
70
112
 
71
- return if response.code == 200
113
+ def commit(action, query)
114
+ url = build_url(action, query:)
115
+ save_request(url)
72
116
 
73
- response.code == 400 ? not_found_error.new : ResponseError.new("HTTP #{response.code}")
74
- end
117
+ HTTPClient.get(url)
118
+ end
75
119
 
76
- # Documents
120
+ def map_response_errors(response, not_found_error: DocumentNotFoundError)
121
+ return ResponseError.new('Unknown response') if response.blank?
77
122
 
78
- def parse_document_response(tracking_response, document_type)
79
- document_response = DocumentResponse.new
80
- document_response.error = map_response_errors(tracking_response)
81
- return document_response if document_response.error.present?
123
+ webspeed_error = (response.is_a?(String) || response.is_a?(HTTPClient::Response)) &&
124
+ response.include?('WebSpeed error')
125
+ return ResponseError.new('Temporary error (WebSpeed error)') if webspeed_error
82
126
 
83
- tracking_response.deep_symbolize_keys!
127
+ return if response.code == 200
84
128
 
85
- image_type_code = case document_type
86
- when :bol then 'B'
87
- when :pod then 'P'
88
- end
129
+ response.code == 400 ? not_found_error.new : ResponseError.new("HTTP #{response.code}")
130
+ end
89
131
 
90
- api_images = tracking_response.dig(:protrace, :images, :image)
91
- api_images = [api_images] if api_images.is_a?(Hash)
132
+ def parse_amount(amount)
133
+ negative = amount.include?('-')
92
134
 
93
- image = api_images&.find { |image| image[:imagetypecode] == image_type_code }
94
- url = image.blank? ? nil : (image[:directurl].presence || image[:imageurl])
135
+ ['$', ',', '-'].each do |char|
136
+ amount = amount.sub(char, '')
137
+ end
95
138
 
96
- if url.blank?
97
- document_response.error = DocumentNotFoundError.new
98
- return document_response
99
- end
139
+ return 0 if amount.blank?
100
140
 
101
- begin
102
- response = HTTPClient.get(url)
103
- rescue StandardError => e
104
- document_response.error = e
105
- return document_response
106
- end
141
+ amount = (amount.to_f * 100).to_i
142
+ return amount unless negative
107
143
 
108
- unless response.code == 200
109
- document_response.error = DocumentNotFoundError.new
110
- return document_response
144
+ amount * -1
111
145
  end
112
146
 
113
- document_response.assign_attributes(content_type: response.headers['content-type'], data: response.body)
114
- document_response
115
- end
147
+ def parse_api_city_state(str)
148
+ return if str.blank?
116
149
 
117
- # Tracking
150
+ city = str.split(', ')[0].titleize
151
+ province = str.split(', ')[1].upcase
118
152
 
119
- def build_tracking_request(tracking_number)
120
- api_credentials = fetch_credential(:api)
153
+ if province == '*'
154
+ province = case city
155
+ when 'Albuquerque' then 'NM'
156
+ end
157
+ end
121
158
 
122
- { pronum: tracking_number, xmlpass: api_credentials.password, xmluser: api_credentials.username }
123
- end
159
+ Location.new(city:, province:, country: ActiveUtils::Country.find('USA'))
160
+ end
124
161
 
125
- def parse_api_city_state(str)
126
- return if str.blank?
162
+ def parse_api_city_state_zip(str)
163
+ return if str.blank?
127
164
 
128
- city = str.split(', ')[0].titleize
129
- province = str.split(', ')[1].upcase
165
+ parts = str.split(', ')
130
166
 
131
- if province == '*'
132
- province = case city
133
- when 'Albuquerque' then 'NM'
134
- end
167
+ Location.new(city: parts.first.titleize, province: parts.last.upcase, country: ActiveUtils::Country.find('USA'))
135
168
  end
136
169
 
137
- Location.new(
138
- city:,
139
- province:,
140
- country: ActiveUtils::Country.find('USA'),
141
- )
142
- end
170
+ def parse_api_date(date, location)
171
+ return if date.blank?
143
172
 
144
- def parse_api_city_state_zip(str)
145
- return if str.blank?
173
+ separator = ['?', '-'].find { |separator| date.include?(separator) }
174
+ return if separator.blank?
146
175
 
147
- parts = str.split(', ')
176
+ format = case date
177
+ when /^\d{4}#{separator}/
178
+ ['%Y', '%m', '%d'].join(separator)
179
+ when /^\d{2}#{separator}/
180
+ ['%m', '%d', '%Y'].join(separator)
181
+ end
182
+ return if format.blank?
148
183
 
149
- Location.new(
150
- city: parts.first.titleize,
151
- province: parts.last.upcase,
152
- country: ActiveUtils::Country.find('USA'),
153
- )
154
- end
184
+ local_date = ::Date.strptime(date, format)
185
+ ::FreightKit::DateTime.new(local_date:, location:)
186
+ end
155
187
 
156
- def parse_api_date(date, location)
157
- return if date.blank?
188
+ def parse_api_date_time(date_time, location)
189
+ return if date_time.blank?
158
190
 
159
- separator = ['?', '-'].find { |separator| date.include?(separator) }
160
- return if separator.blank?
191
+ local_date_time = ::Time.strptime(date_time, '%Y-%m-%d %H:%M').to_fs(:db)
192
+ ::FreightKit::DateTime.new(local_date_time:, location:)
193
+ rescue Date::Error
194
+ raise if local_date_time.present?
161
195
 
162
- format = case date
163
- when /^\d{4}#{separator}/
164
- ['%Y', '%m', '%d'].join(separator)
165
- when /^\d{2}#{separator}/
166
- ['%m', '%d', '%Y'].join(separator)
167
- end
168
- return if format.blank?
196
+ parse_api_date(local_date_time, location)
197
+ end
169
198
 
170
- local_date = ::Date.strptime(date, format)
171
- ::FreightKit::DateTime.new(local_date:, location:)
172
- end
199
+ def parse_document_response(tracking_response, document_type)
200
+ document_response = DocumentResponse.new
201
+ document_response.error = map_response_errors(tracking_response)
202
+ return document_response if document_response.error.present?
173
203
 
174
- def parse_api_date_time(date_time, location)
175
- return if date_time.blank?
204
+ tracking_response.deep_symbolize_keys!
176
205
 
177
- local_date_time = ::Time.strptime(date_time, '%Y-%m-%d %H:%M').to_fs(:db)
178
- ::FreightKit::DateTime.new(local_date_time:, location:)
179
- rescue Date::Error
180
- raise if local_date_time.present?
206
+ image_type_code = case document_type
207
+ when :bol then 'B'
208
+ when :pod then 'P'
209
+ end
181
210
 
182
- parse_api_date(local_date_time, location)
183
- end
211
+ api_images = tracking_response.dig(:protrace, :images, :image)
212
+ api_images = [api_images] if api_images.is_a?(Hash)
213
+
214
+ image = api_images&.find { |image| image[:imagetypecode] == image_type_code }
215
+ url = image.blank? ? nil : (image[:directurl].presence || image[:imageurl])
184
216
 
185
- def parse_tracking_response(tracking_number, response:)
186
- tracking_response = TrackingResponse.new(carrier: self, request: last_request, response:)
187
- tracking_response.error = map_response_errors(response, not_found_error: ShipmentNotFoundError)
188
- return tracking_response if tracking_response.error.present?
217
+ if url.blank?
218
+ document_response.error = DocumentNotFoundError.new
219
+ return document_response
220
+ end
189
221
 
190
- response.deep_symbolize_keys!
222
+ begin
223
+ response = HTTPClient.get(url)
224
+ rescue StandardError => e
225
+ document_response.error = e
226
+ return document_response
227
+ end
191
228
 
192
- api_events = response.dig(:protrace, :shiphists, :shiphist)
193
- if api_events.blank?
194
- tracking_response.error = ResponseError.new('Empty response')
195
- return tracking_response
229
+ unless response.code == 200
230
+ document_response.error = DocumentNotFoundError.new
231
+ return document_response
232
+ end
233
+
234
+ document_response.assign_attributes(content_type: response.headers['content-type'], data: response.body)
235
+ document_response
196
236
  end
197
237
 
198
- origin = Location.new(
199
- address1: response.dig(:protrace, :shipaddr)&.titleize,
200
- address2: response.dig(:protrace, :shipaddr2)&.titleize,
201
- city: response.dig(:protrace, :origcity)&.titleize,
202
- province: response.dig(:protrace, :origstate)&.upcase,
203
- country: ActiveUtils::Country.find('USA'),
204
- )
205
-
206
- destination = Location.new(
207
- address1: response.dig(:protrace, :consaddr)&.titleize,
208
- address2: response.dig(:protrace, :consaddr2)&.titleize,
209
- city: response.dig(:protrace, :destcity)&.titleize,
210
- province: response.dig(:protrace, :deststate)&.upcase,
211
- country: ActiveUtils::Country.find('USA'),
212
- )
213
-
214
- deldateiso = response.dig(:protrace, :deldateiso)
215
- actual_delivery_date = (parse_api_date(deldateiso, destination) if deldateiso.present?)
216
-
217
- estdeliverydateiso = response.dig(:protrace, :estdeliverydateiso)
218
- estdeliverytimestart = response.dig(:protrace, :estdeliverytimestart)
219
- estimated_delivery_date = if estdeliverydateiso.present? && estdeliverytimestart.present?
220
- parse_api_date_time([estdeliverydateiso, estdeliverytimestart].join(' '), destination)
221
- elsif estdeliverydateiso.present?
222
- parse_api_date(estdeliverydateiso, destination)
223
- end
224
-
225
- scheduled_delivery_date = nil
226
- ship_time = nil
227
-
228
- api_events = response.dig(:protrace, :shiphists, :shiphist)
229
- api_events = [api_events] if api_events.is_a?(Hash)
230
-
231
- last_location = origin
232
-
233
- shipment_events = api_events.reverse.map do |api_event|
234
- hist_code = api_event[:histcode]&.downcase
235
- next if hist_code.blank?
236
-
237
- event = conf.dig(:events, :types).key(hist_code)
238
- next if event.blank?
239
-
240
- remarks = api_event[:histremarks]
241
-
242
- location = if remarks.present? && remarks.match?(/, \w{2}/) # ends in state abbreviation
243
- parse_api_city_state(api_event[:histremarks])
244
- end
245
-
246
- location ||= case event
247
- when :delivered then destination
248
- when :departed then last_location
249
- when :picked_up, :pickup_scheduled then origin
250
- end
238
+ def parse_rate_response(shipment:, response:)
239
+ rate_response = RateResponse.new(request: last_request, response:)
251
240
 
252
- date = api_event[:histdate]
253
- time = api_event[:histtime]
254
- # Some api_event[:histtime] returns a string with missing hours and minutes like ' : '
255
- time = nil if (time =~ /\d/).blank?
241
+ rate_response.error = map_response_errors(response)
242
+ return rate_response if rate_response.error.present?
256
243
 
257
- date_time = if [date, time].all?(&:present?)
258
- parse_api_date_time([date, time].compact.join(' '), location)
259
- elsif date.present?
260
- parse_api_date(date, location)
261
- end
244
+ error = response.dig('error', 'errormessage')
262
245
 
263
- last_location = location
246
+ if error.present?
247
+ rate_response.error = InvalidCredentialsError.new if error.downcase.include?('invalid username/password')
264
248
 
265
- ShipmentEvent.new(location:, date_time:, type_code: event)
266
- end
249
+ if error.downcase.include?('is not available') || error.downcase.include?('out of the serviceable area')
250
+ rate_response.error = UnserviceableError.new(error)
251
+ end
267
252
 
268
- shipment_events.compact!
253
+ rate_response.error = ResponseError.new(error) if rate_response.error.blank?
269
254
 
270
- status = shipment_events.last&.type_code
255
+ return rate_response
256
+ end
271
257
 
272
- tracking_response.assign_attributes(
273
- actual_delivery_date:,
274
- destination:,
275
- estimated_delivery_date:,
276
- origin:,
277
- scheduled_delivery_date:,
278
- ship_time:,
279
- shipment_events:,
280
- status:,
281
- tracking_number:,
282
- )
258
+ if response.dig('ratequote', 'quotetotal').blank?
259
+ rate_response.error = ResponseError.new('Cost is blank')
260
+ return rate_response
261
+ end
283
262
 
284
- tracking_response
285
- end
263
+ total_cents = parse_amount(response.dig('ratequote', 'quotetotal'))
286
264
 
287
- # Rates
265
+ transit_days = response.dig('ratequote', 'busdays').to_i
266
+ estimate_reference = response.dig('ratequote', 'quotenumber')
288
267
 
289
- def build_calculated_accessorials(shipment)
290
- []
291
- end
268
+ ratequote_lines = response.dig('ratequote', 'ratequoteline')
269
+ prices = []
292
270
 
293
- def parse_amount(amount)
294
- negative = amount.include?('-')
271
+ ratequote_lines.each do |ratequote_line|
272
+ next if ratequote_line['chrg'].blank?
273
+ next if ratequote_line['chargedesc'] == 'FREIGHT'
295
274
 
296
- ['$', ',', '-'].each do |char|
297
- amount = amount.sub(char, '')
298
- end
275
+ cents = parse_amount(ratequote_line['chrg'])
276
+ next if cents.zero?
299
277
 
300
- return 0 if amount.blank?
278
+ prices << Price.new(blame: :api, cents:, description: ratequote_line_description(ratequote_line))
279
+ end
301
280
 
302
- amount = (amount.to_f * 100).to_i
303
- return amount unless negative
281
+ prices = [Price.new(blame: :api, cents: total_cents - prices.sum(&:cents), description: 'Freight')] + prices
304
282
 
305
- amount * -1
306
- end
283
+ if self.class.overlength_fees_require_tariff?
284
+ cents = 0
307
285
 
308
- def ratequote_line_description(ratequote_line)
309
- description = ratequote_line['chargedesc'] || ''
310
- description = description.capitalize
286
+ shipment.packages.each do |package|
287
+ cents += overlength_fee(tariff, package)
288
+ end
311
289
 
312
- code = ratequote_line['chargecode']&.upcase || ''
313
- description = "#{description} (#{code})" if code.present?
290
+ prices << Price.new(blame: :tariff, cents:, description: 'Overlength fees') if cents.nonzero?
291
+ end
314
292
 
315
- description.squish
316
- end
293
+ rate = Rate.new(
294
+ carrier: self,
295
+ carrier_name: name,
296
+ currency: 'USD',
297
+ estimate_reference:,
298
+ scac:,
299
+ service_name: :standard,
300
+ shipment:,
301
+ prices:,
302
+ transit_days:,
303
+ with_excessive_length_fees: rates_with_excessive_length_fees?
304
+ )
317
305
 
318
- def build_rate_request(shipment:)
319
- api_credentials = fetch_credential(:api)
320
-
321
- query = {
322
- xmlv: 'yes', # must be first
323
- quotenumber: 'YES',
324
- vdzip: shipment.destination.postal_code,
325
- vozip: shipment.origin.postal_code,
326
- xmlpass: api_credentials.password,
327
- xmluser: api_credentials.username
328
- }
329
-
330
- i = 0
331
- shipment.packages.each do |package|
332
- i += 1 # API starts at 1 (not 0)
333
-
334
- query["vclass[#{i}]"] = package.freight_class
335
- query["wheight[#{i}]"] = package.height(:in).ceil
336
- query["wlength[#{i}]"] = package.length(:in).ceil
337
- query["wpallets[#{i}]"] = package.quantity
338
- query["wpieces[#{i}]"] = package.quantity
339
- query["wweight[#{i}]"] = package.pounds(:total).ceil
340
- query["wwidth[#{i}]"] = package.width(:in).ceil
306
+ rate_response.rates = [rate]
307
+ rate_response
341
308
  end
342
309
 
343
- accessorials = []
344
-
345
- if shipment.accessorials.present?
346
- serviceable_accessorials?(shipment.accessorials)
310
+ def parse_tracking_response(tracking_number, response:)
311
+ tracking_response = TrackingResponse.new(carrier: self, request: last_request, response:)
312
+ tracking_response.error = map_response_errors(response, not_found_error: ShipmentNotFoundError)
313
+ return tracking_response if tracking_response.error.present?
347
314
 
348
- shipment
349
- .accessorials
350
- .reject { |accessorial| conf.dig(:accessorials, :unquotable).include?(accessorial) }
351
- .each do |shipment_accessorial|
352
- conf_accessorial = conf.dig(:accessorials, :mappable, shipment_accessorial)
315
+ response.deep_symbolize_keys!
353
316
 
354
- case conf_accessorial
355
- when Array then accessorials += conf_accessorial
356
- when String then accessorials << conf_accessorial
357
- end
317
+ api_events = response.dig(:protrace, :shiphists, :shiphist)
318
+ if api_events.blank?
319
+ tracking_response.error = ResponseError.new('Empty response')
320
+ return tracking_response
358
321
  end
359
- end
360
322
 
361
- accessorials += build_calculated_accessorials(shipment)
323
+ origin = Location.new(
324
+ address1: response.dig(:protrace, :shipaddr)&.titleize,
325
+ address2: response.dig(:protrace, :shipaddr2)&.titleize,
326
+ city: response.dig(:protrace, :origcity)&.titleize,
327
+ province: response.dig(:protrace, :origstate)&.upcase,
328
+ country: ActiveUtils::Country.find('USA')
329
+ )
362
330
 
363
- accessorials.uniq.compact.each { |accessorial| query[accessorial] = 'Yes' } if accessorials.any?
331
+ destination = Location.new(
332
+ address1: response.dig(:protrace, :consaddr)&.titleize,
333
+ address2: response.dig(:protrace, :consaddr2)&.titleize,
334
+ city: response.dig(:protrace, :destcity)&.titleize,
335
+ province: response.dig(:protrace, :deststate)&.upcase,
336
+ country: ActiveUtils::Country.find('USA')
337
+ )
364
338
 
365
- query
366
- end
339
+ deldateiso = response.dig(:protrace, :deldateiso)
340
+ actual_delivery_date = (parse_api_date(deldateiso, destination) if deldateiso.present?)
367
341
 
368
- def parse_rate_response(shipment:, response:)
369
- rate_response = RateResponse.new(request: last_request, response:)
342
+ estdeliverydateiso = response.dig(:protrace, :estdeliverydateiso)
343
+ estdeliverytimestart = response.dig(:protrace, :estdeliverytimestart)
344
+ estdelivery = [estdeliverydateiso, estdeliverytimestart].join(' ')
345
+ estimated_delivery_date = if estdeliverydateiso.present? && estdeliverytimestart.present?
346
+ parse_api_date_time(estdelivery, destination)
347
+ elsif estdeliverydateiso.present?
348
+ parse_api_date(estdeliverydateiso, destination)
349
+ end
370
350
 
371
- rate_response.error = map_response_errors(response)
372
- return rate_response if rate_response.error.present?
351
+ scheduled_delivery_date = nil
352
+ ship_time = nil
373
353
 
374
- error = response.dig('error', 'errormessage')
354
+ api_events = response.dig(:protrace, :shiphists, :shiphist)
355
+ api_events = [api_events] if api_events.is_a?(Hash)
375
356
 
376
- if error.present?
377
- rate_response.error = InvalidCredentialsError.new if error.downcase.include?('invalid username/password')
357
+ last_location = origin
378
358
 
379
- if error.downcase.include?('is not available') || error.downcase.include?('out of the serviceable area')
380
- rate_response.error = UnserviceableError.new(error)
381
- end
359
+ shipment_events = api_events.reverse.map do |api_event|
360
+ hist_code = api_event[:histcode]&.downcase
361
+ next if hist_code.blank?
382
362
 
383
- rate_response.error = ResponseError.new(error) if rate_response.error.blank?
363
+ event = self.class::EVENTS[:types].key(hist_code)
364
+ next if event.blank?
384
365
 
385
- return rate_response
386
- end
366
+ remarks = api_event[:histremarks]
387
367
 
388
- if response.dig('ratequote', 'quotetotal').blank?
389
- rate_response.error = ResponseError.new('Cost is blank')
390
- return rate_response
391
- end
368
+ location = if remarks.present? && remarks.match?(/, \w{2}/) # ends in state abbreviation
369
+ parse_api_city_state(api_event[:histremarks])
370
+ end
392
371
 
393
- total_cents = parse_amount(response.dig('ratequote', 'quotetotal'))
372
+ location ||= case event
373
+ when :delivered then destination
374
+ when :departed then last_location
375
+ when :picked_up, :pickup_scheduled then origin
376
+ end
394
377
 
395
- transit_days = response.dig('ratequote', 'busdays').to_i
396
- estimate_reference = response.dig('ratequote', 'quotenumber')
378
+ date = api_event[:histdate]
379
+ time = api_event[:histtime]
380
+ # Some api_event[:histtime] returns a string with missing hours and minutes like ' : '
381
+ time = nil if (time =~ /\d/).blank?
397
382
 
398
- ratequote_lines = response.dig('ratequote', 'ratequoteline')
399
- prices = []
383
+ date_time = if [date, time].all?(&:present?)
384
+ parse_api_date_time([date, time].compact.join(' '), location)
385
+ elsif date.present?
386
+ parse_api_date(date, location)
387
+ end
400
388
 
401
- ratequote_lines.each do |ratequote_line|
402
- next if ratequote_line['chrg'].blank?
403
- next if ratequote_line['chargedesc'] == 'FREIGHT'
389
+ last_location = location
404
390
 
405
- cents = parse_amount(ratequote_line['chrg'])
406
- next if cents.zero?
391
+ ShipmentEvent.new(location:, date_time:, type_code: event)
392
+ end
393
+
394
+ shipment_events.compact!
407
395
 
408
- prices << Price.new(
409
- blame: :api,
410
- cents:,
411
- description: ratequote_line_description(ratequote_line),
396
+ status = shipment_events.last&.type_code
397
+
398
+ tracking_response.assign_attributes(
399
+ actual_delivery_date:,
400
+ destination:,
401
+ estimated_delivery_date:,
402
+ origin:,
403
+ scheduled_delivery_date:,
404
+ ship_time:,
405
+ shipment_events:,
406
+ status:,
407
+ tracking_number:
412
408
  )
409
+
410
+ tracking_response
413
411
  end
414
412
 
415
- prices = [
416
- Price.new(
417
- blame: :api,
418
- cents: total_cents - prices.sum(&:cents),
419
- description: 'Freight',
420
- ),
421
- ] + prices
413
+ def pod(tracking_number)
414
+ query = build_tracking_request(tracking_number)
415
+ response = commit(:track, query)
416
+ parse_document_response(response, :pod)
417
+ end
422
418
 
423
- if self.class.overlength_fees_require_tariff?
424
- cents = 0
419
+ def ratequote_line_description(ratequote_line)
420
+ description = ratequote_line['chargedesc'] || ''
421
+ description = description.capitalize
425
422
 
426
- shipment.packages.each do |package|
427
- cents += overlength_fee(tariff, package)
428
- end
423
+ code = ratequote_line['chargecode']&.upcase || ''
424
+ description = "#{description} (#{code})" if code.present?
429
425
 
430
- prices << Price.new(blame: :tariff, cents:, description: 'Overlength fees') if cents.nonzero?
426
+ description.squish
431
427
  end
432
428
 
433
- rate = Rate.new(
434
- carrier: self,
435
- carrier_name: self.class.name,
436
- currency: 'USD',
437
- estimate_reference:,
438
- scac: self.class.scac.upcase,
439
- service_name: :standard,
440
- shipment:,
441
- prices:,
442
- transit_days:,
443
- with_excessive_length_fees: @conf.dig(:attributes, :rates, :with_excessive_length_fees),
444
- )
445
-
446
- rate_response.rates = [rate]
447
- rate_response
429
+ def scanned_bol(tracking_number)
430
+ query = build_tracking_request(tracking_number)
431
+ response = commit(:track, query)
432
+ parse_document_response(response, :bol)
433
+ end
448
434
  end
449
435
  end
450
436
  end