freight_kit 0.1.20 → 0.1.22

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 (466) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -2
  3. data/README.md +6 -5
  4. data/Rakefile +4 -0
  5. data/VERSION +1 -1
  6. data/configuration/locations/amazon_warehouses.yml +3330 -0
  7. data/freight_kit-0.1.21.gem +0 -0
  8. data/freight_kit.gemspec +11 -22
  9. data/lib/freight_kit/address_normalizer.rb +24 -0
  10. data/lib/freight_kit/amazon_warehouse.rb +33 -0
  11. data/lib/freight_kit/api_clients/soap_client.rb +33 -10
  12. data/lib/freight_kit/carrier.rb +248 -302
  13. data/lib/freight_kit/carriers/a1_express.rb +25 -0
  14. data/lib/freight_kit/carriers/a_duie_pyle.rb +30 -0
  15. data/lib/freight_kit/carriers/aaa_cooper.rb +11 -0
  16. data/lib/freight_kit/carriers/aaa_expedited_services.rb +10 -0
  17. data/lib/freight_kit/carriers/ab_brothers_usa_inc.rb +12 -0
  18. data/lib/freight_kit/carriers/aberdeen_express_inc.rb +28 -0
  19. data/lib/freight_kit/carriers/abf_freight_system.rb +475 -0
  20. data/lib/freight_kit/carriers/access_transport.rb +10 -0
  21. data/lib/freight_kit/carriers/action_express.rb +10 -0
  22. data/lib/freight_kit/carriers/adams_and_companies.rb +10 -0
  23. data/lib/freight_kit/carriers/advance_distribution.rb +28 -0
  24. data/lib/freight_kit/carriers/alcon_transport.rb +10 -0
  25. data/lib/freight_kit/carriers/all_freight.rb +10 -0
  26. data/lib/freight_kit/carriers/alliance_transportation_group.rb +10 -0
  27. data/lib/freight_kit/carriers/allstate_transport.rb +10 -0
  28. data/lib/freight_kit/carriers/aloha_freight_forwarders.rb +31 -0
  29. data/lib/freight_kit/carriers/amado_trucking.rb +31 -0
  30. data/lib/freight_kit/carriers/american_fast_freight.rb +28 -0
  31. data/lib/freight_kit/carriers/amigos_service_transportation.rb +10 -0
  32. data/lib/freight_kit/carriers/anderson_towing_and_transport.rb +10 -0
  33. data/lib/freight_kit/carriers/apex_transporting.rb +10 -0
  34. data/lib/freight_kit/carriers/archer_cargo.rb +35 -0
  35. data/lib/freight_kit/carriers/armstrong_transport_group.rb +10 -0
  36. data/lib/freight_kit/carriers/autobahn_logistics.rb +10 -0
  37. data/lib/freight_kit/carriers/averitt_express.rb +30 -0
  38. data/lib/freight_kit/carriers/ba_transportation.rb +10 -0
  39. data/lib/freight_kit/carriers/balkin_express.rb +10 -0
  40. data/lib/freight_kit/carriers/bay_area_express.rb +10 -0
  41. data/lib/freight_kit/carriers/bc_freightways.rb +11 -0
  42. data/lib/freight_kit/carriers/benjamin_best_freight_inc.rb +37 -0
  43. data/lib/freight_kit/carriers/bennett_trucking.rb +10 -0
  44. data/lib/freight_kit/carriers/best_overnite_express.rb +78 -0
  45. data/lib/freight_kit/carriers/best_trucking.rb +10 -0
  46. data/lib/freight_kit/carriers/bestway_transport.rb +10 -0
  47. data/lib/freight_kit/carriers/bryan_systems.rb +10 -0
  48. data/lib/freight_kit/carriers/c_and_w_express.rb +10 -0
  49. data/lib/freight_kit/carriers/campbell_trucking.rb +10 -0
  50. data/lib/freight_kit/carriers/carde_transportation.rb +10 -0
  51. data/lib/freight_kit/carriers/cardinal_logistics.rb +28 -0
  52. data/lib/freight_kit/carriers/cdc_transport.rb +10 -0
  53. data/lib/freight_kit/carriers/cdo_express_diversified.rb +10 -0
  54. data/lib/freight_kit/carriers/cedar_supply_trucking.rb +10 -0
  55. data/lib/freight_kit/carriers/central_freight_lines.rb +12 -0
  56. data/lib/freight_kit/carriers/central_transport.rb +12 -0
  57. data/lib/freight_kit/carriers/chicago_suburban_express.rb +28 -0
  58. data/lib/freight_kit/carriers/city_logistics.rb +11 -0
  59. data/lib/freight_kit/carriers/clear_lane_freight_systems.rb +474 -0
  60. data/lib/freight_kit/carriers/cleopatra_trucking.rb +10 -0
  61. data/lib/freight_kit/carriers/clifford_transportation.rb +10 -0
  62. data/lib/freight_kit/carriers/cms_domestic.rb +25 -0
  63. data/lib/freight_kit/carriers/cnd_logistics.rb +10 -0
  64. data/lib/freight_kit/carriers/coast_to_coast_transport.rb +27 -0
  65. data/lib/freight_kit/carriers/countrywide_carriers.rb +10 -0
  66. data/lib/freight_kit/carriers/cpl_express.rb +31 -0
  67. data/lib/freight_kit/carriers/crosby_trucking_service.rb +29 -0
  68. data/lib/freight_kit/carriers/cross_country_freight_solutions.rb +408 -0
  69. data/lib/freight_kit/carriers/crowley.rb +10 -0
  70. data/lib/freight_kit/carriers/csa_transportation.rb +40 -0
  71. data/lib/freight_kit/carriers/csx_transportation.rb +40 -0
  72. data/lib/freight_kit/carriers/custom_global_logistics.rb +14 -0
  73. data/lib/freight_kit/carriers/d_and_a_transport.rb +10 -0
  74. data/lib/freight_kit/carriers/dak_transport.rb +10 -0
  75. data/lib/freight_kit/carriers/dash_courier_and_logistics_north_carolina.rb +10 -0
  76. data/lib/freight_kit/carriers/daylight_transport.rb +29 -0
  77. data/lib/freight_kit/carriers/dayton_freight_lines.rb +30 -0
  78. data/lib/freight_kit/carriers/dbr_logistics.rb +10 -0
  79. data/lib/freight_kit/carriers/deep_south_freight.rb +27 -0
  80. data/lib/freight_kit/carriers/delta_express.rb +10 -0
  81. data/lib/freight_kit/carriers/delta_transport.rb +10 -0
  82. data/lib/freight_kit/carriers/delta_trucking_logistics.rb +10 -0
  83. data/lib/freight_kit/carriers/demco_express.rb +10 -0
  84. data/lib/freight_kit/carriers/dependable_global_express.rb +10 -0
  85. data/lib/freight_kit/carriers/dependable_hawaiian_express.rb +24 -0
  86. data/lib/freight_kit/carriers/dependable_highway_express.rb +573 -0
  87. data/lib/freight_kit/carriers/desert_coastal_transport.rb +10 -0
  88. data/lib/freight_kit/carriers/dhl.rb +11 -0
  89. data/lib/freight_kit/carriers/diamond_line_delivery.rb +174 -0
  90. data/lib/freight_kit/carriers/ding_tai_trucking.rb +10 -0
  91. data/lib/freight_kit/carriers/diversified_transfer_and_storage.rb +29 -0
  92. data/lib/freight_kit/carriers/dohrn_transfer_company.rb +30 -0
  93. data/lib/freight_kit/carriers/dot_line_transportation.rb +29 -0
  94. data/lib/freight_kit/carriers/drt_transportation.rb +13 -0
  95. data/lib/freight_kit/carriers/dugan_truck_line.rb +29 -0
  96. data/lib/freight_kit/carriers/dupont.rb +10 -0
  97. data/lib/freight_kit/carriers/e_transport_carriers.rb +29 -0
  98. data/lib/freight_kit/carriers/echo_global_logistics.rb +13 -0
  99. data/lib/freight_kit/carriers/ecklund_logistics.rb +10 -0
  100. data/lib/freight_kit/carriers/el_emplumado_transport.rb +10 -0
  101. data/lib/freight_kit/carriers/elite_carriers.rb +28 -0
  102. data/lib/freight_kit/carriers/emerald_express.rb +10 -0
  103. data/lib/freight_kit/carriers/era_transport.rb +10 -0
  104. data/lib/freight_kit/carriers/estes_express_lines.rb +25 -0
  105. data/lib/freight_kit/carriers/euro_kam.rb +28 -0
  106. data/lib/freight_kit/carriers/ew_riverside_trucking.rb +10 -0
  107. data/lib/freight_kit/carriers/express_2000_inc.rb +12 -0
  108. data/lib/freight_kit/carriers/express_services_international.rb +10 -0
  109. data/lib/freight_kit/carriers/f_and_a_express.rb +27 -0
  110. data/lib/freight_kit/carriers/fairmont_logistics.rb +13 -0
  111. data/lib/freight_kit/carriers/fargo_cargo.rb +10 -0
  112. data/lib/freight_kit/carriers/fast_freight_transportation.rb +10 -0
  113. data/lib/freight_kit/carriers/fast_load_transport.rb +10 -0
  114. data/lib/freight_kit/carriers/fed_ex_express.rb +30 -0
  115. data/lib/freight_kit/carriers/fed_ex_ground.rb +24 -0
  116. data/lib/freight_kit/carriers/fed_ex_ltl_economy.rb +11 -0
  117. data/lib/freight_kit/carriers/fed_ex_ltl_priority.rb +11 -0
  118. data/lib/freight_kit/carriers/first_trucking_express.rb +10 -0
  119. data/lib/freight_kit/carriers/flash_trucking.rb +10 -0
  120. data/lib/freight_kit/carriers/fort_transportation_and_service_company_inc.rb +37 -0
  121. data/lib/freight_kit/carriers/forward_air.rb +781 -0
  122. data/lib/freight_kit/carriers/frahtex.rb +10 -0
  123. data/lib/freight_kit/carriers/frozen_food_express.rb +10 -0
  124. data/lib/freight_kit/carriers/gholia_bros_trucking.rb +36 -0
  125. data/lib/freight_kit/carriers/global_cargo.rb +10 -0
  126. data/lib/freight_kit/carriers/global_logistics_and_transportation.rb +10 -0
  127. data/lib/freight_kit/carriers/gls_freight.rb +32 -0
  128. data/lib/freight_kit/carriers/go_trucking_services.rb +10 -0
  129. data/lib/freight_kit/carriers/gold_eagle_transportation.rb +11 -0
  130. data/lib/freight_kit/carriers/golden_drop_trucking.rb +10 -0
  131. data/lib/freight_kit/carriers/gonzalez_transportation.rb +10 -0
  132. data/lib/freight_kit/carriers/gps_transport.rb +10 -0
  133. data/lib/freight_kit/carriers/greene_enterprise.rb +10 -0
  134. data/lib/freight_kit/carriers/gsa_international.rb +10 -0
  135. data/lib/freight_kit/carriers/gsp_express.rb +24 -0
  136. data/lib/freight_kit/carriers/gsv_transportation.rb +10 -0
  137. data/lib/freight_kit/carriers/gta_transportation.rb +10 -0
  138. data/lib/freight_kit/carriers/guam_freight_services.rb +10 -0
  139. data/lib/freight_kit/carriers/harrah_transportation.rb +23 -0
  140. data/lib/freight_kit/carriers/her_trucking.rb +10 -0
  141. data/lib/freight_kit/carriers/hercules_freight.rb +20 -0
  142. data/lib/freight_kit/carriers/highlander_xpress.rb +36 -0
  143. data/lib/freight_kit/carriers/hn_transport.rb +10 -0
  144. data/lib/freight_kit/carriers/honolulu_freight_service.rb +10 -0
  145. data/lib/freight_kit/carriers/hot_line_freight_system_inc.rb +12 -0
  146. data/lib/freight_kit/carriers/hotshot_freight_of_texas.rb +13 -0
  147. data/lib/freight_kit/carriers/houser_transport.rb +27 -0
  148. data/lib/freight_kit/carriers/infiniti_freight_logistics.rb +28 -0
  149. data/lib/freight_kit/carriers/infinity_trucking.rb +10 -0
  150. data/lib/freight_kit/carriers/interstate_tma.rb +10 -0
  151. data/lib/freight_kit/carriers/intrade_industries.rb +10 -0
  152. data/lib/freight_kit/carriers/jasper_trucking.rb +28 -0
  153. data/lib/freight_kit/carriers/jb_hunt.rb +11 -0
  154. data/lib/freight_kit/carriers/jfj_transportation.rb +8 -8
  155. data/lib/freight_kit/carriers/jkb_express.rb +10 -0
  156. data/lib/freight_kit/carriers/jlm_transport.rb +10 -0
  157. data/lib/freight_kit/carriers/johansson_transport.rb +10 -0
  158. data/lib/freight_kit/carriers/jones_express.rb +10 -0
  159. data/lib/freight_kit/carriers/jp_logistics.rb +41 -0
  160. data/lib/freight_kit/carriers/just_in_time_cargo.rb +29 -0
  161. data/lib/freight_kit/carriers/kengkok_times.rb +10 -0
  162. data/lib/freight_kit/carriers/kings_express.rb +10 -0
  163. data/lib/freight_kit/carriers/klc_transportation.rb +10 -0
  164. data/lib/freight_kit/carriers/km_trucking.rb +10 -0
  165. data/lib/freight_kit/carriers/kma_logistics.rb +10 -0
  166. data/lib/freight_kit/carriers/krc_logistics.rb +10 -0
  167. data/lib/freight_kit/carriers/ktx.rb +10 -0
  168. data/lib/freight_kit/carriers/landstar.rb +25 -0
  169. data/lib/freight_kit/carriers/lanter_distributing_llc.rb +30 -0
  170. data/lib/freight_kit/carriers/ld_trucking.rb +10 -0
  171. data/lib/freight_kit/carriers/legacy_logistics_solutions.rb +10 -0
  172. data/lib/freight_kit/carriers/leonards_express.rb +10 -0
  173. data/lib/freight_kit/carriers/liberty_linehaul_west.rb +28 -0
  174. data/lib/freight_kit/carriers/liberty_transport.rb +10 -0
  175. data/lib/freight_kit/carriers/lightning_express.rb +10 -0
  176. data/lib/freight_kit/carriers/lj_express.rb +24 -0
  177. data/lib/freight_kit/carriers/lkp_enterprises.rb +10 -0
  178. data/lib/freight_kit/carriers/ltl_pros.rb +28 -0
  179. data/lib/freight_kit/carriers/m_and_m_and_sons.rb +10 -0
  180. data/lib/freight_kit/carriers/mag_lines_transport.rb +10 -0
  181. data/lib/freight_kit/carriers/magic_messenger.rb +24 -0
  182. data/lib/freight_kit/carriers/majors_transit.rb +10 -0
  183. data/lib/freight_kit/carriers/map_global_logistics.rb +10 -0
  184. data/lib/freight_kit/carriers/matths_trucking.rb +11 -0
  185. data/lib/freight_kit/carriers/mav_canadian_transport.rb +13 -0
  186. data/lib/freight_kit/carriers/mc_systems.rb +35 -0
  187. data/lib/freight_kit/carriers/mega_fleet.rb +10 -0
  188. data/lib/freight_kit/carriers/mega_trans_group.rb +28 -0
  189. data/lib/freight_kit/carriers/meta_express.rb +10 -0
  190. data/lib/freight_kit/carriers/mia_transportation.rb +10 -0
  191. data/lib/freight_kit/carriers/midwest_motor_express.rb +14 -0
  192. data/lib/freight_kit/carriers/midwest_transport_and_trucking.rb +10 -0
  193. data/lib/freight_kit/carriers/mikade_delivery_service.rb +10 -0
  194. data/lib/freight_kit/carriers/mohoff_freight_lines.rb +23 -0
  195. data/lib/freight_kit/carriers/montata_travelers_express.rb +10 -0
  196. data/lib/freight_kit/carriers/morgan_van_lines.rb +29 -0
  197. data/lib/freight_kit/carriers/moto_transportation.rb +24 -0
  198. data/lib/freight_kit/carriers/mountain_valley_express.rb +197 -0
  199. data/lib/freight_kit/carriers/ms_freight.rb +27 -0
  200. data/lib/freight_kit/carriers/mvg_transport.rb +10 -0
  201. data/lib/freight_kit/carriers/n_and_m_transfer_co_inc.rb +29 -0
  202. data/lib/freight_kit/carriers/nabot_transport_solutions.rb +10 -0
  203. data/lib/freight_kit/carriers/national_freight_xpress.rb +10 -0
  204. data/lib/freight_kit/carriers/navigation_group.rb +10 -0
  205. data/lib/freight_kit/carriers/nelson_trucking.rb +10 -0
  206. data/lib/freight_kit/carriers/network_1_transportation.rb +10 -0
  207. data/lib/freight_kit/carriers/new_century_transport.rb +28 -0
  208. data/lib/freight_kit/carriers/new_penn_motor_express.rb +11 -0
  209. data/lib/freight_kit/carriers/nieves_transportation.rb +10 -0
  210. data/lib/freight_kit/carriers/nolan_transportation_group.rb +26 -0
  211. data/lib/freight_kit/carriers/northland_trucking.rb +10 -0
  212. data/lib/freight_kit/carriers/northwest_carrier_logistics.rb +27 -0
  213. data/lib/freight_kit/carriers/nt_usa.rb +10 -0
  214. data/lib/freight_kit/carriers/nu_line_transportation.rb +10 -0
  215. data/lib/freight_kit/carriers/numark_transportation.rb +80 -0
  216. data/lib/freight_kit/carriers/nvk_transport.rb +10 -0
  217. data/lib/freight_kit/carriers/ny_transport.rb +10 -0
  218. data/lib/freight_kit/carriers/oak_harbor.rb +28 -0
  219. data/lib/freight_kit/carriers/oakline_transportation.rb +10 -0
  220. data/lib/freight_kit/carriers/old_dominion_freight_line.rb +30 -0
  221. data/lib/freight_kit/carriers/on_trac.rb +555 -0
  222. data/lib/freight_kit/carriers/one_hundred_one_transport.rb +10 -0
  223. data/lib/freight_kit/carriers/one_way_delivery.rb +10 -0
  224. data/lib/freight_kit/carriers/oracle_transportation_solutions.rb +10 -0
  225. data/lib/freight_kit/carriers/orange_courier.rb +27 -0
  226. data/lib/freight_kit/carriers/ortas_trucking.rb +10 -0
  227. data/lib/freight_kit/carriers/pac_trux_transportation.rb +10 -0
  228. data/lib/freight_kit/carriers/pacer.rb +10 -0
  229. data/lib/freight_kit/carriers/pacific_logistics.rb +10 -0
  230. data/lib/freight_kit/carriers/pacific_states_truck_lines.rb +10 -0
  231. data/lib/freight_kit/carriers/paradise_distributing.rb +10 -0
  232. data/lib/freight_kit/carriers/paramount_freight_systems.rb +10 -0
  233. data/lib/freight_kit/carriers/peninsula_truck_lines.rb +206 -0
  234. data/lib/freight_kit/carriers/pg_transport.rb +10 -0
  235. data/lib/freight_kit/carriers/pgs_360.rb +10 -0
  236. data/lib/freight_kit/carriers/pilot_freight_services.rb +10 -0
  237. data/lib/freight_kit/carriers/pious_trans.rb +27 -0
  238. data/lib/freight_kit/carriers/pitt_ohio_express.rb +30 -0
  239. data/lib/freight_kit/carriers/pki_logistics.rb +12 -0
  240. data/lib/freight_kit/carriers/plana_transportation.rb +28 -0
  241. data/lib/freight_kit/carriers/prestige.rb +10 -0
  242. data/lib/freight_kit/carriers/pro_trans_solutions.rb +28 -0
  243. data/lib/freight_kit/carriers/puma_logistics.rb +10 -0
  244. data/lib/freight_kit/carriers/pwc_international.rb +10 -0
  245. data/lib/freight_kit/carriers/quality_logistics.rb +10 -0
  246. data/lib/freight_kit/carriers/queen_star_trucking.rb +27 -0
  247. data/lib/freight_kit/carriers/r_and_l_carriers.rb +11 -0
  248. data/lib/freight_kit/carriers/rac_transport.rb +10 -0
  249. data/lib/freight_kit/carriers/re_logistics.rb +23 -0
  250. data/lib/freight_kit/carriers/reddaway.rb +11 -0
  251. data/lib/freight_kit/carriers/reefer_van_network.rb +13 -0
  252. data/lib/freight_kit/carriers/refrigerated_trucking_and_ltl_transportation_services.rb +10 -0
  253. data/lib/freight_kit/carriers/ridgeline_express.rb +10 -0
  254. data/lib/freight_kit/carriers/rist_transport.rb +29 -0
  255. data/lib/freight_kit/carriers/river_city_transport.rb +10 -0
  256. data/lib/freight_kit/carriers/roadrunner_transportation_services.rb +669 -0
  257. data/lib/freight_kit/carriers/roadtex.rb +13 -0
  258. data/lib/freight_kit/carriers/ronald_transport_services.rb +10 -0
  259. data/lib/freight_kit/carriers/rose_trucking.rb +24 -0
  260. data/lib/freight_kit/carriers/rp_and_j_transport.rb +24 -0
  261. data/lib/freight_kit/carriers/rpm_transportation.rb +11 -0
  262. data/lib/freight_kit/carriers/rude_transportation.rb +30 -0
  263. data/lib/freight_kit/carriers/s_and_s_logistics.rb +10 -0
  264. data/lib/freight_kit/carriers/saia.rb +456 -362
  265. data/lib/freight_kit/carriers/sb_transportation.rb +27 -0
  266. data/lib/freight_kit/carriers/schneider_national.rb +29 -0
  267. data/lib/freight_kit/carriers/schwarz_trucking.rb +10 -0
  268. data/lib/freight_kit/carriers/sewell_motor_express.rb +29 -0
  269. data/lib/freight_kit/carriers/shawnee_trucking.rb +28 -0
  270. data/lib/freight_kit/carriers/shi_logistics.rb +10 -0
  271. data/lib/freight_kit/carriers/sinbad_transport.rb +10 -0
  272. data/lib/freight_kit/carriers/skyway.rb +10 -0
  273. data/lib/freight_kit/carriers/smith_cargo_transportation_services.rb +23 -0
  274. data/lib/freight_kit/carriers/sna_transportation.rb +10 -0
  275. data/lib/freight_kit/carriers/solid_group.rb +10 -0
  276. data/lib/freight_kit/carriers/solution_carrier_express.rb +10 -0
  277. data/lib/freight_kit/carriers/southeastern_freight_lines.rb +441 -0
  278. data/lib/freight_kit/carriers/southwestern_motor_transport.rb +30 -0
  279. data/lib/freight_kit/carriers/span_alaska.rb +10 -0
  280. data/lib/freight_kit/carriers/specialize_transportation.rb +27 -0
  281. data/lib/freight_kit/carriers/ssi_refrigerated_express.rb +10 -0
  282. data/lib/freight_kit/carriers/standard_forwarding_llc.rb +28 -0
  283. data/lib/freight_kit/carriers/star_american_enterprise.rb +10 -0
  284. data/lib/freight_kit/carriers/sterling_transportation.rb +10 -0
  285. data/lib/freight_kit/carriers/stg.rb +163 -0
  286. data/lib/freight_kit/carriers/supervan_service.rb +10 -0
  287. data/lib/freight_kit/carriers/sutton_transport_inc.rb +12 -0
  288. data/lib/freight_kit/carriers/swift_transportation.rb +28 -0
  289. data/lib/freight_kit/carriers/t_force_worldwide.rb +605 -0
  290. data/lib/freight_kit/carriers/tax_airfreight_inc.rb +35 -0
  291. data/lib/freight_kit/carriers/tforce_freight_inc.rb +30 -0
  292. data/lib/freight_kit/carriers/the_custom_companies.rb +162 -0
  293. data/lib/freight_kit/carriers/thk_trucking.rb +28 -0
  294. data/lib/freight_kit/carriers/tiger_cool_express.rb +11 -0
  295. data/lib/freight_kit/carriers/tili_logistics.rb +10 -0
  296. data/lib/freight_kit/carriers/tmt_industries.rb +10 -0
  297. data/lib/freight_kit/carriers/top_ten_express.rb +28 -0
  298. data/lib/freight_kit/carriers/total_quality_logistics.rb +339 -0
  299. data/lib/freight_kit/carriers/total_transportation.rb +258 -0
  300. data/lib/freight_kit/carriers/trans_international_trucking.rb +29 -0
  301. data/lib/freight_kit/carriers/treasure_transportation.rb +10 -0
  302. data/lib/freight_kit/carriers/tri_state_services.rb +10 -0
  303. data/lib/freight_kit/carriers/tritex_freighters.rb +10 -0
  304. data/lib/freight_kit/carriers/triways.rb +10 -0
  305. data/lib/freight_kit/carriers/tsi_kansas.rb +29 -0
  306. data/lib/freight_kit/carriers/u_and_r_transportation.rb +23 -0
  307. data/lib/freight_kit/carriers/union_pacific.rb +11 -0
  308. data/lib/freight_kit/carriers/united_freight_logistics.rb +10 -0
  309. data/lib/freight_kit/carriers/unlimited_distance_transportation.rb +10 -0
  310. data/lib/freight_kit/carriers/up_streamline.rb +10 -0
  311. data/lib/freight_kit/carriers/ups_freight.rb +12 -0
  312. data/lib/freight_kit/carriers/us_freightways.rb +10 -0
  313. data/lib/freight_kit/carriers/usa_truck.rb +28 -0
  314. data/lib/freight_kit/carriers/usf_holland.rb +11 -0
  315. data/lib/freight_kit/carriers/valley_cartage.rb +11 -0
  316. data/lib/freight_kit/carriers/vc_transport.rb +10 -0
  317. data/lib/freight_kit/carriers/vision_express.rb +11 -0
  318. data/lib/freight_kit/carriers/vision_logistics_group.rb +10 -0
  319. data/lib/freight_kit/carriers/vmg_transportation_group.rb +10 -0
  320. data/lib/freight_kit/carriers/volpe_express.rb +10 -0
  321. data/lib/freight_kit/carriers/ward_trucking.rb +29 -0
  322. data/lib/freight_kit/carriers/wdwb.rb +10 -0
  323. data/lib/freight_kit/carriers/western_flyer_express.rb +28 -0
  324. data/lib/freight_kit/carriers/western_regional_delivery_service.rb +265 -0
  325. data/lib/freight_kit/carriers/western_specialized_trucking.rb +22 -0
  326. data/lib/freight_kit/carriers/win_distribution.rb +28 -0
  327. data/lib/freight_kit/carriers/wlb_transport.rb +10 -0
  328. data/lib/freight_kit/carriers/x_trux.rb +29 -0
  329. data/lib/freight_kit/carriers/xpo_logistics.rb +415 -0
  330. data/lib/freight_kit/carriers/xpress_global_systems.rb +30 -0
  331. data/lib/freight_kit/carriers/xylo_trucking.rb +10 -0
  332. data/lib/freight_kit/carriers/yrc_freight.rb +24 -0
  333. data/lib/freight_kit/carriers/zentner_transportation.rb +28 -0
  334. data/lib/freight_kit/carriers.rb +331 -34
  335. data/lib/freight_kit/countries.rb +22 -0
  336. data/lib/freight_kit/error.rb +0 -2
  337. data/lib/freight_kit/errors/document_not_found_error.rb +0 -2
  338. data/lib/freight_kit/errors/expired_credentials_error.rb +0 -2
  339. data/lib/freight_kit/errors/http_error.rb +0 -2
  340. data/lib/freight_kit/errors/invalid_credentials_error.rb +0 -2
  341. data/lib/freight_kit/errors/rate_limit_error.rb +12 -0
  342. data/lib/freight_kit/errors/response_error.rb +0 -2
  343. data/lib/freight_kit/errors/shipment_not_found_error.rb +0 -2
  344. data/lib/freight_kit/errors/unserviceable_accessorials_error.rb +1 -5
  345. data/lib/freight_kit/errors/unserviceable_error.rb +0 -2
  346. data/lib/freight_kit/helpers/documentable.rb +0 -2
  347. data/lib/freight_kit/helpers/pickupable.rb +3 -14
  348. data/lib/freight_kit/helpers/rateable.rb +0 -2
  349. data/lib/freight_kit/helpers/trackable.rb +0 -2
  350. data/lib/freight_kit/http_client/cache_store.rb +2 -4
  351. data/lib/freight_kit/http_client/httpi_adapter.rb +37 -0
  352. data/lib/freight_kit/http_client/log_device.rb +0 -2
  353. data/lib/freight_kit/http_client/response.rb +0 -2
  354. data/lib/freight_kit/http_client/revalidation.rb +0 -2
  355. data/lib/freight_kit/http_client/safe_retries.rb +17 -3
  356. data/lib/freight_kit/http_client/zstd.rb +2 -4
  357. data/lib/freight_kit/http_client.rb +21 -21
  358. data/lib/freight_kit/model.rb +1 -3
  359. data/lib/freight_kit/models/address.rb +15 -0
  360. data/lib/freight_kit/models/contact.rb +3 -4
  361. data/lib/freight_kit/models/country.rb +20 -0
  362. data/lib/freight_kit/models/credential.rb +3 -18
  363. data/lib/freight_kit/models/date_time.rb +0 -2
  364. data/lib/freight_kit/models/document_response.rb +0 -2
  365. data/lib/freight_kit/models/label.rb +0 -2
  366. data/lib/freight_kit/models/location.rb +10 -25
  367. data/lib/freight_kit/models/pickup_response.rb +0 -2
  368. data/lib/freight_kit/models/price.rb +0 -2
  369. data/lib/freight_kit/models/rate.rb +0 -2
  370. data/lib/freight_kit/models/rate_response.rb +0 -2
  371. data/lib/freight_kit/models/region.rb +19 -0
  372. data/lib/freight_kit/models/response.rb +0 -2
  373. data/lib/freight_kit/models/shipment.rb +5 -14
  374. data/lib/freight_kit/models/shipment_event.rb +0 -2
  375. data/lib/freight_kit/models/terminal.rb +10 -0
  376. data/lib/freight_kit/models/tracking_response.rb +0 -2
  377. data/lib/freight_kit/package.rb +52 -64
  378. data/lib/freight_kit/package_item.rb +14 -14
  379. data/lib/freight_kit/packaging.rb +6 -26
  380. data/lib/freight_kit/platform.rb +0 -32
  381. data/lib/freight_kit/platforms/carrier_logistics.rb +326 -345
  382. data/lib/freight_kit/platforms/dsdc.rb +82 -0
  383. data/lib/freight_kit/platforms/next_tms/graphql_client/mutation/quote_create.rb +32 -0
  384. data/lib/freight_kit/platforms/next_tms/graphql_client/mutation/shipment_create.rb +29 -0
  385. data/lib/freight_kit/platforms/next_tms/graphql_client/query/broker_carrier.rb +37 -0
  386. data/lib/freight_kit/platforms/next_tms/graphql_client/query/broker_carriers.rb +45 -0
  387. data/lib/freight_kit/platforms/next_tms/graphql_client/query/broker_customer.rb +54 -0
  388. data/lib/freight_kit/platforms/next_tms/graphql_client/query/broker_customers.rb +62 -0
  389. data/lib/freight_kit/platforms/next_tms/graphql_client/query/carrier.rb +22 -0
  390. data/lib/freight_kit/platforms/next_tms/graphql_client/query/carriers.rb +30 -0
  391. data/lib/freight_kit/platforms/next_tms/graphql_client/query/customer.rb +20 -0
  392. data/lib/freight_kit/platforms/next_tms/graphql_client/query/customers.rb +28 -0
  393. data/lib/freight_kit/platforms/next_tms/graphql_client/query/invoice.rb +31 -0
  394. data/lib/freight_kit/platforms/next_tms/graphql_client/query/invoices.rb +39 -0
  395. data/lib/freight_kit/platforms/next_tms/graphql_client/query/shipment.rb +102 -0
  396. data/lib/freight_kit/platforms/next_tms/graphql_client/query/shipment_documents.rb +29 -0
  397. data/lib/freight_kit/platforms/next_tms/graphql_client/query/shipments.rb +110 -0
  398. data/lib/freight_kit/platforms/next_tms/graphql_client.rb +22 -0
  399. data/lib/freight_kit/platforms/next_tms/orm/address.rb +22 -0
  400. data/lib/freight_kit/platforms/next_tms/orm/base.rb +120 -0
  401. data/lib/freight_kit/platforms/next_tms/orm/broker_carrier.rb +18 -0
  402. data/lib/freight_kit/platforms/next_tms/orm/broker_customer.rb +22 -0
  403. data/lib/freight_kit/platforms/next_tms/orm/carrier.rb +14 -0
  404. data/lib/freight_kit/platforms/next_tms/orm/collection.rb +335 -0
  405. data/lib/freight_kit/platforms/next_tms/orm/customer.rb +12 -0
  406. data/lib/freight_kit/platforms/next_tms/orm/invoice.rb +21 -0
  407. data/lib/freight_kit/platforms/next_tms/orm/record_not_found_error.rb +9 -0
  408. data/lib/freight_kit/platforms/next_tms/orm/shipment.rb +50 -0
  409. data/lib/freight_kit/platforms/next_tms/orm.rb +38 -0
  410. data/lib/freight_kit/platforms/next_tms.rb +452 -0
  411. data/lib/freight_kit/platforms/the_great_information_factory.rb +492 -425
  412. data/lib/freight_kit/regions.rb +27 -0
  413. data/lib/freight_kit/shipment_packer.rb +36 -38
  414. data/lib/freight_kit/tariff/overlength_rule.rb +1 -5
  415. data/lib/freight_kit/tariff.rb +0 -2
  416. data/lib/freight_kit.rb +103 -11
  417. data/schemas/next_tms.json +110710 -0
  418. metadata +443 -55
  419. data/configuration/carriers/abfs.yml +0 -124
  420. data/configuration/carriers/btvp.yml +0 -84
  421. data/configuration/carriers/ccyq.yml +0 -121
  422. data/configuration/carriers/clni.yml +0 -113
  423. data/configuration/carriers/cnwy.yml +0 -113
  424. data/configuration/carriers/ctbv.yml +0 -117
  425. data/configuration/carriers/dcha.yml +0 -105
  426. data/configuration/carriers/dlds.yml +0 -111
  427. data/configuration/carriers/dphe.yml +0 -130
  428. data/configuration/carriers/drrq.yml +0 -131
  429. data/configuration/carriers/fcsy.yml +0 -102
  430. data/configuration/carriers/fwda.yml +0 -137
  431. data/configuration/carriers/jfj_transportation.yml +0 -2
  432. data/configuration/carriers/mtvl.yml +0 -12
  433. data/configuration/carriers/numk.yml +0 -14
  434. data/configuration/carriers/otcl.yml +0 -124
  435. data/configuration/carriers/pens.yml +0 -22
  436. data/configuration/carriers/rdfs.yml +0 -142
  437. data/configuration/carriers/saia.yml +0 -129
  438. data/configuration/carriers/sefl.yml +0 -115
  439. data/configuration/carriers/totl.yml +0 -111
  440. data/configuration/carriers/tqyl.yml +0 -28
  441. data/configuration/carriers/wrds.yml +0 -20
  442. data/configuration/platforms/carrier_logistics.yml +0 -25
  443. data/configuration/platforms/next.yml +0 -12
  444. data/configuration/platforms/the_great_information_factory.yml +0 -122
  445. data/lib/freight_kit/carriers/abfs.rb +0 -421
  446. data/lib/freight_kit/carriers/btvp.rb +0 -29
  447. data/lib/freight_kit/carriers/ccyq.rb +0 -310
  448. data/lib/freight_kit/carriers/clni.rb +0 -396
  449. data/lib/freight_kit/carriers/cnwy.rb +0 -327
  450. data/lib/freight_kit/carriers/ctbv.rb +0 -53
  451. data/lib/freight_kit/carriers/dcha.rb +0 -76
  452. data/lib/freight_kit/carriers/dlds.rb +0 -49
  453. data/lib/freight_kit/carriers/dphe.rb +0 -472
  454. data/lib/freight_kit/carriers/drrq.rb +0 -580
  455. data/lib/freight_kit/carriers/fcsy.rb +0 -57
  456. data/lib/freight_kit/carriers/fwda.rb +0 -744
  457. data/lib/freight_kit/carriers/mtvl.rb +0 -34
  458. data/lib/freight_kit/carriers/numk.rb +0 -58
  459. data/lib/freight_kit/carriers/otcl.rb +0 -520
  460. data/lib/freight_kit/carriers/pens.rb +0 -204
  461. data/lib/freight_kit/carriers/rdfs.rb +0 -521
  462. data/lib/freight_kit/carriers/sefl.rb +0 -342
  463. data/lib/freight_kit/carriers/totl.rb +0 -172
  464. data/lib/freight_kit/carriers/tqyl.rb +0 -337
  465. data/lib/freight_kit/carriers/wrds.rb +0 -246
  466. data/lib/freight_kit/platforms/next.rb +0 -101
@@ -0,0 +1,781 @@
1
+ module FreightKit
2
+ module Carriers
3
+ class ForwardAir < FreightKit::Carrier
4
+ class << self
5
+ def check_mailing_addresses
6
+ [
7
+ Address.new(
8
+ city: 'Knoxville',
9
+ country: Countries.find_by(iso31661_alpha2: 'US'),
10
+ id: 'a73d400c-4ed9-4bfb-a9d9-6842cb3bde7d',
11
+ postal_code: '37995',
12
+ region_abbreviation: 'TN',
13
+ street: 'Dept 888155'
14
+ )
15
+ ]
16
+ end
17
+
18
+ def find_rates_with_declared_value? = true
19
+ def id = 'c69bfbcd-3a1f-4d2f-9dd6-c357ab9193de'
20
+ def legal_name = 'Forward Air, LLC'
21
+ def mappable_accessorials = DELIVERY_ACCESSORIALS.merge(PICKUP_ACCESSORIALS)
22
+ def maximum_height = Measured::Length.new(105, :inches)
23
+ def maximum_weight = Measured::Weight.new(10_000, :pounds)
24
+ def minimum_length_for_overlength_fees = Measured::Length.new(6, :feet)
25
+ def name = 'Forward Air'
26
+ def overlength_fees_require_tariff? = false
27
+ def pickup_number_is_tracking_number? = true
28
+ def rates_with_excessive_length_fees? = false
29
+ def required_credential_types = %i[api]
30
+ def requirements = %i[credentials]
31
+ def scac = 'FWDA'
32
+ def services = %i[standard_ltl]
33
+
34
+ def unquotable_accessorials
35
+ %i[
36
+ athletic_facility_delivery
37
+ athletic_facility_pickup
38
+ cemetery_delivery
39
+ cemetery_pickup
40
+ church_delivery
41
+ church_pickup
42
+ construction_delivery
43
+ construction_pickup
44
+ dock_dropoff
45
+ dock_pickup
46
+ driver_assist
47
+ farm_delivery
48
+ farm_pickup
49
+ fitness_center_delivery
50
+ fitness_center_pickup
51
+ golf_course_delivery
52
+ golf_course_pickup
53
+ government_delivery
54
+ government_pickup
55
+ grocery_store_delivery
56
+ grocery_store_pickup
57
+ jobsite_delivery
58
+ jobsite_pickup
59
+ marina_delivery
60
+ marina_pickup
61
+ mine_site_delivery
62
+ mine_site_pickup
63
+ nursing_home_delivery
64
+ nursing_home_pickup
65
+ park_delivery
66
+ park_pickup
67
+ prison_delivery
68
+ prison_pickup
69
+ ranch_delivery
70
+ ranch_pickup
71
+ reservation_delivery
72
+ reservation_pickup
73
+ restaurant_delivery
74
+ restaurant_pickup
75
+ school_pickup
76
+ steel_mill_delivery
77
+ steel_mill_pickup
78
+ storage_facility_delivery
79
+ storage_facility_pickup
80
+ university_pickup
81
+ utility_site_delivery
82
+ utility_site_pickup
83
+ ]
84
+ end
85
+
86
+ def unserviceable_accessorials
87
+ %i[
88
+ brewery_delivery
89
+ brewery_pickup
90
+ customs_brokerage
91
+ flatbed_delivery
92
+ flatbed_pickup
93
+ grocery_warehouse_delivery
94
+ grocery_warehouse_pickup
95
+ guaranteed_delivery
96
+ guaranteed_delivery_am
97
+ guaranteed_delivery_pm
98
+ inspection_site_delivery
99
+ inspection_site_pickup
100
+ winery_delivery
101
+ winery_pickup
102
+ ]
103
+ end
104
+
105
+ def us_dot_number = 2_215_557
106
+ def us_mc_number = 249_708
107
+ end
108
+
109
+ include FreightKit::Rateable
110
+ include FreightKit::Trackable
111
+ include FreightKit::Pickupable
112
+
113
+ API = {
114
+ domains: { production: 'api.forwardair.com', test: 'test-api.forwardair.com' },
115
+ endpoints: {
116
+ documents: '/ltlservices/v2/rest/waybills/%TRACKING_NUMBER%/documents',
117
+ document: '/ltlservices/v2/rest/waybills/%TRACKING_NUMBER%/documents/download' \
118
+ '?docId=%DOC_ID%',
119
+ locations: '/ltlservices/v2/rest/ltlinfo/liststations',
120
+ pickup: '/ltlservices/v2/rest/waybills/book/json',
121
+ rates: '/ltlservices/v2/rest/waybills/quote/json',
122
+ track: '/ltlservices/v2/rest/waybills/%TRACKING_NUMBER%/statuses'
123
+ },
124
+ methods: { locations: :get, pickup: :post, rates: :post, track: :get },
125
+ use_ssl: true
126
+ }.freeze
127
+ DELIVERY_ACCESSORIALS = {
128
+ afterhours_delivery: 'XPD',
129
+ airport_delivery: 'ALD',
130
+ amusement_park_delivery: 'CCD',
131
+ appointment_delivery: 'ADE',
132
+ convention_delivery: 'CCD',
133
+ early_morning_delivery: 'XPD',
134
+ fair_delivery: 'CCD',
135
+ hospital_delivery: 'HDC',
136
+ hotel_delivery: 'HOD',
137
+ inside_delivery: 'IDE',
138
+ liftgate_delivery: 'LGD',
139
+ mall_delivery: 'MDC',
140
+ military_site_delivery: 'MBD',
141
+ motel_delivery: 'HOD',
142
+ residential_delivery: 'RDE',
143
+ resort_delivery: 'HOD',
144
+ school_delivery: 'UND',
145
+ university_delivery: 'UND'
146
+ }.freeze
147
+ EVENTS = {
148
+ types: {
149
+ arrived_at_terminal: 'ARR',
150
+ delivered: 'DLV',
151
+ delivery_appointment_scheduled: 'DPL',
152
+ departed: 'DEP',
153
+ located: 'ARRD',
154
+ out_for_delivery: 'OUTDV',
155
+ picked_up: 'PUP',
156
+ pickup_driver_assigned: 'DPUP'
157
+ }
158
+ }.freeze
159
+ JSON_HEADERS = { Accept: 'application/json', charset: 'utf-8', 'Content-Type' => 'application/json' }.freeze
160
+ PICKUP_ACCESSORIALS = {
161
+ afterhours_pickup: 'XPU',
162
+ airport_pickup: 'ALP',
163
+ amusement_park_pickup: 'CCP',
164
+ appointment_pickup: 'APP',
165
+ convention_pickup: 'CCP',
166
+ early_morning_pickup: 'XPU',
167
+ fair_pickup: 'CCP',
168
+ hospital_pickup: 'HPC',
169
+ hotel_pickup: 'HOT',
170
+ inside_pickup: 'IPU',
171
+ liftgate_pickup: 'LGP',
172
+ mall_pickup: 'MPC',
173
+ military_site_pickup: 'MBP',
174
+ motel_pickup: 'HOT',
175
+ residential_pickup: 'RPU',
176
+ resort_pickup: 'HOP'
177
+ }.freeze
178
+
179
+ # Documents
180
+
181
+ def find_locations(country)
182
+ raise ArgumentError, 'country must be a ActiveUtils::Country' unless country.is_a?(ActiveUtils::Country)
183
+
184
+ request = build_locations_request
185
+ parse_locations_response(country:, response: commit(request))
186
+ end
187
+
188
+ def find_rates(shipment:)
189
+ if shipment.packages.map { |package| package.height(:in) }.any?(&:blank?) ||
190
+ shipment.packages.map { |package| package.length(:in) }.any?(&:blank?) ||
191
+ shipment.packages.map { |package| package.width(:in) }.any?(&:blank?)
192
+
193
+ raise UnserviceableError, 'Dimensions required for quoting'
194
+ end
195
+
196
+ packages = shipment.packages.select { |package| package.height(:in) > 89 }
197
+ if packages.any?
198
+ message = <<~MESSAGE.squish
199
+ #{'Height'.pluralize(packages)}
200
+ #{packages.map { |package| "#{package.height(:in)} inches" }.join(', ')}
201
+ greater than maximum allowed of 89 inches.
202
+ MESSAGE
203
+ raise UnserviceableError, message
204
+ end
205
+
206
+ packages = shipment.packages.select { |package| package.length(:in) > 240 }
207
+ if packages.any?
208
+ message = <<~MESSAGE.squish
209
+ #{'Length'.pluralize(packages)}
210
+ #{packages.map { |package| "#{package.length(:in)} inches" }.join(', ')}
211
+ greater than maximum allowed of 240 inches.
212
+ MESSAGE
213
+ raise UnserviceableError, message
214
+ end
215
+
216
+ packages = shipment.packages.select { |package| package.width(:in) > 82 }
217
+ if packages.any?
218
+ message = <<~MESSAGE.squish
219
+ #{'Width'.pluralize(packages)}
220
+ #{packages.map { |package| "#{package.width(:in)} inches" }.join(', ')}
221
+ greater than maximum allowed of 82 inches.
222
+ MESSAGE
223
+ raise UnserviceableError, message
224
+ end
225
+
226
+ super
227
+ end
228
+
229
+ def pod(tracking_number)
230
+ # Retrieve list of available documents first
231
+ begin
232
+ documents = commit(build_documents_request(tracking_number))
233
+ rescue FreightKit::ResponseError => e
234
+ return DocumentResponse.new(error: FreightKit::DocumentNotFoundError) if e.message.downcase.include?('no airbills found')
235
+
236
+ return DocumentResponse.new(error: e)
237
+ end
238
+
239
+ begin
240
+ doc_id = get_doc_id(documents:, tracking_number:, type: :pod)
241
+ rescue StandardError => e
242
+ return DocumentResponse.new(error: e)
243
+ end
244
+
245
+ request = build_document_request(doc_id:, tracking_number:)
246
+ response = commit(request)
247
+
248
+ parse_document_response(:pod, tracking_number, response)
249
+ end
250
+
251
+ def scanned_bol(tracking_number, _options = {})
252
+ # Retrieve list of available documents first
253
+ begin
254
+ documents = commit(build_documents_request(tracking_number))
255
+ rescue FreightKit::ResponseError => e
256
+ return DocumentResponse.new(error: FreightKit::DocumentNotFoundError) if e.message.downcase.include?('no airbills found')
257
+
258
+ return DocumentResponse.new(error: e)
259
+ end
260
+
261
+ begin
262
+ doc_id = get_doc_id(documents:, tracking_number:, type: :bol)
263
+ rescue StandardError => e
264
+ return DocumentResponse.new(e:)
265
+ end
266
+
267
+ request = build_document_request(doc_id:, tracking_number:)
268
+ response = commit(request)
269
+
270
+ parse_document_response(:bol, tracking_number, response)
271
+ end
272
+
273
+ protected
274
+
275
+ def base_url = "https://#{API.dig(:domains, :production)}"
276
+
277
+ def build_accessorials(accessorials)
278
+ delivery_accessorials = []
279
+ pickup_accessorials = []
280
+
281
+ if accessorials.present?
282
+ serviceable_accessorials?(accessorials)
283
+
284
+ accessorials.excluding(unserviceable_accessorials).each do |a|
285
+ delivery_accessorials << DELIVERY_ACCESSORIALS[a] if DELIVERY_ACCESSORIALS.include?(a)
286
+ pickup_accessorials << PICKUP_ACCESSORIALS[a] if PICKUP_ACCESSORIALS.include?(a)
287
+ end
288
+ end
289
+
290
+ if delivery_accessorials.present? && delivery_accessorials.include?('RDE')
291
+ # Remove duplicate delivery appointment accessorial when residential delivery (included with RDE)
292
+ delivery_accessorials -= ['ADE']
293
+ end
294
+
295
+ if pickup_accessorials.present? && pickup_accessorials.include?('RPU')
296
+ # Remove duplicate pickup appointment accessorial when residential pickup (included with RPU)
297
+ pickup_accessorials -= ['APP']
298
+ end
299
+
300
+ # API doesn't like empty arrays
301
+ delivery_accessorials = nil if delivery_accessorials.blank?
302
+ pickup_accessorials = nil if pickup_accessorials.blank?
303
+
304
+ [pickup_accessorials&.uniq, delivery_accessorials&.uniq]
305
+ end
306
+
307
+ def build_dimensions(packages)
308
+ packages.map do |package|
309
+ {
310
+ height: package.height(:in).ceil,
311
+ length: package.length(:in).ceil,
312
+ pieces: package.quantity,
313
+ width: package.width(:in).ceil
314
+ }
315
+ end
316
+ end
317
+
318
+ def build_document_request(doc_id:, tracking_number:)
319
+ request = { url: build_url(:document, doc_id:, tracking_number:), headers: build_headers, method: API.dig(:methods, :documents) }
320
+
321
+ save_request(request)
322
+ request
323
+ end
324
+
325
+ def build_documents_request(tracking_number)
326
+ request = { url: build_url(:documents, tracking_number:), headers: build_headers, method: API.dig(:methods, :documents) }
327
+
328
+ save_request(request)
329
+ request
330
+ end
331
+
332
+ def build_freight_details(packages)
333
+ packages.map do |package|
334
+ {
335
+ description: package.description || 'Freight',
336
+ freightClass: package.freight_class.to_s,
337
+ pieces: package.quantity.to_s,
338
+ weightType: 'L',
339
+ weight: package.pounds(:total).ceil.to_s
340
+ }
341
+ end
342
+ end
343
+
344
+ def build_headers
345
+ api_credentials = fetch_credential(:api)
346
+
347
+ JSON_HEADERS.merge(
348
+ {
349
+ billToAccountNumber: api_credentials.account,
350
+ customerId: api_credentials.username.upcase,
351
+ password: api_credentials.password,
352
+ user: api_credentials.username
353
+ }
354
+ )
355
+ end
356
+
357
+ def build_locations_request
358
+ request = { url: build_url(:locations), headers: build_headers, method: API.dig(:methods, :locations) }
359
+
360
+ save_request(request)
361
+ request
362
+ end
363
+
364
+ def build_pickup_request(
365
+ delivery_from:,
366
+ delivery_to:,
367
+ dispatcher:,
368
+ pickup_from:,
369
+ pickup_to:,
370
+ scac:,
371
+ service:,
372
+ shipment:
373
+ )
374
+ pickup_accessorials, delivery_accessorials = build_accessorials(shipment.accessorials)
375
+
376
+ dispatcher_phone = dispatcher.phone.delete('^0-9')
377
+ shipper_phone = shipment.origin.contact.phone.delete('^0-9')
378
+ receiver_phone = shipment.destination.contact.phone.delete('^0-9')
379
+
380
+ api_credentials = fetch_credential(:api)
381
+
382
+ declared_value = if shipment.declared_value_cents.blank?
383
+ '0'
384
+ else
385
+ format('%.2f', (shipment.declared_value_cents.to_f / 100).ceil)
386
+ end
387
+
388
+ delivery = {
389
+ airportDelivery: delivery_accessorials&.include?('ALD') ? 'Y' : 'N',
390
+ deliveryAccessorials: { deliveryAccessorial: delivery_accessorials }
391
+ }
392
+ description = shipment.packages.map(&:description).reject(&:blank?).uniq.join(', ')
393
+
394
+ request = {
395
+ headers: build_headers,
396
+ method: API.dig(:methods, :pickup),
397
+ url: build_url(:pickup),
398
+ body: {
399
+ testmode: 'N',
400
+ consignee: {
401
+ consigneeAddress1: shipment.destination.address1,
402
+ consigneeAddress2: '',
403
+ consigneeCity: shipment.destination.city,
404
+ consigneeCloseTime: delivery_to.strftime('%H:%M:00'),
405
+ consigneeContactEmail: '',
406
+ consigneeContactName: shipment.destination.contact.name || 'Receiving',
407
+ consigneeContactPhone: receiver_phone || '',
408
+ consigneeCountry: shipment.destination.country.code(:alpha2).value,
409
+ consigneeLocationName: shipment.destination.contact.name,
410
+ consigneeOpenTime: delivery_from.strftime('%H:%M:00'),
411
+ consigneeState: shipment.destination.province,
412
+ consigneeZipCode: shipment.destination.postal_code.to_s
413
+ },
414
+ shipper: {
415
+ shipperAddress1: shipment.origin.address1,
416
+ shipperAddress2: '',
417
+ shipperCity: shipment.origin.city,
418
+ shipperCloseTime: pickup_to.strftime('%H:%M:00'),
419
+ shipperContactEmail: '',
420
+ shipperContactName: shipment.origin.contact.name || 'Shipping',
421
+ shipperContactPhone: shipper_phone || '',
422
+ shipperCountry: shipment.origin.country.code(:alpha2).value,
423
+ shipperLocationName: shipment.origin.contact.name,
424
+ shipperOpenTime: pickup_from.strftime('%H:%M:00'),
425
+ shipperState: shipment.origin.province,
426
+ shipperZipCode: shipment.origin.postal_code.to_s
427
+ },
428
+ orderDetails: {
429
+ airbillNumber: '00000000',
430
+ billToCustomerNumber: api_credentials.account.to_s,
431
+ customerReferenceNumber: shipment.po_number,
432
+ declaredValue: declared_value,
433
+ description:,
434
+ destinationAirportCode: '',
435
+ guaranteedService: 'N',
436
+ hazmat: shipment.hazmat? ? 'Y' : 'N',
437
+ inBondShipment: declared_value.to_f.positive? ? 'Y' : 'N',
438
+ orderAction: 'CREATE',
439
+ originAirportCode: '',
440
+ shippingDate: pickup_from.strftime('%Y-%m-%d'),
441
+ shipperCustomerNumber: api_credentials.account.to_s,
442
+ specialInstructions: '',
443
+ dimensions: {
444
+ dimension: shipment.packages.map do |package|
445
+ {
446
+ height: package.inches(:height).ceil.to_s,
447
+ length: package.inches(:length).ceil.to_s,
448
+ width: package.inches(:width).ceil.to_s,
449
+ pieces: package.quantity.to_s
450
+ }
451
+ end
452
+ },
453
+ freightDetails: { freightDetail: build_freight_details(shipment.packages) },
454
+ delivery:,
455
+ emergencyContact: { email: dispatcher.email, name: dispatcher.name, phone: dispatcher_phone },
456
+ pickup: {
457
+ airportPickup: pickup_accessorials&.include?('ALP') ? 'Y' : 'N',
458
+ pickupAccessorials: { pickupAccessorial: pickup_accessorials },
459
+ pickupReadyTime: pickup_from.strftime('%H:%M:00')
460
+ },
461
+ referenceNumbers: { referenceNumber: [shipment.order_number, shipment.po_number, ''] }
462
+ }
463
+ }.to_json
464
+ }
465
+
466
+ save_request(request)
467
+ request
468
+ end
469
+
470
+ def build_rate_request(shipment:)
471
+ api_credentials = fetch_credential(:api)
472
+
473
+ freight_details = build_freight_details(shipment.packages)
474
+ dimensions = build_dimensions(shipment.packages)
475
+ declared_value = if shipment.declared_value_cents.blank?
476
+ '0'
477
+ else
478
+ format('%.2f', (shipment.declared_value_cents.to_f / 100).ceil)
479
+ end
480
+ pickup_accessorials, delivery_accessorials = build_accessorials(shipment.accessorials)
481
+
482
+ delivery = {
483
+ airportDelivery: delivery_accessorials&.include?('ALD') ? 'Y' : 'N',
484
+ deliveryAccessorials: { deliveryAccessorial: delivery_accessorials }
485
+ }
486
+
487
+ request = {
488
+ url: build_url(:rates),
489
+ headers: build_headers,
490
+ method: API.dig(:methods, :rates),
491
+ body: {
492
+ billToCustomerNumber: api_credentials.account,
493
+ origin: {
494
+ originZipCode: shipment.origin.postal_code.to_s.upcase,
495
+ pickup: {
496
+ airportPickup: pickup_accessorials&.include?('ALP') ? 'Y' : 'N',
497
+ pickupAccessorials: { pickupAccessorial: pickup_accessorials }
498
+ }
499
+ },
500
+ destination: { destinationZipCode: shipment.destination.postal_code.to_s.upcase, delivery: },
501
+ dimensions: { dimension: dimensions },
502
+ freightDetails: { freightDetail: freight_details },
503
+ hazmat: shipment.hazmat? ? 'Y' : 'N',
504
+ inBondShipment: 'N',
505
+ declaredValue: declared_value,
506
+ shippingDate: Date.current.strftime('%Y-%m-%d')
507
+ }.to_json
508
+ }
509
+
510
+ save_request(request)
511
+ request
512
+ end
513
+
514
+ def build_request(action, options = {})
515
+ headers = JSON_HEADERS
516
+ headers = headers.merge(options[:headers]) if options[:headers].present?
517
+ body = options[:body].to_json if options[:body].present?
518
+
519
+ request = { url: build_url(action, options), headers:, method: API.dig(:methods, action), body: }
520
+
521
+ save_request(request)
522
+ request
523
+ end
524
+
525
+ def build_tracking_request(tracking_number)
526
+ request = {
527
+ url: build_url(:track, tracking_number:),
528
+ headers: build_headers,
529
+ method: API.dig(:methods, :track),
530
+ body: { billToCustomerNumber: fetch_credential(:api).account, referenceNumber: tracking_number.to_s }.to_json
531
+ }
532
+
533
+ save_request(request)
534
+ request
535
+ end
536
+
537
+ def build_url(action, options = {})
538
+ url = "#{base_url}#{API.dig(:endpoints, action)}"
539
+ url = url.gsub('%TRACKING_NUMBER%', options[:tracking_number]) if options[:tracking_number]
540
+ url = url.gsub('%DOC_ID%', options[:doc_id]) if options[:doc_id]
541
+
542
+ url
543
+ end
544
+
545
+ def charge_line_item_description(charge_line_item)
546
+ description = charge_line_item['description'] || ''
547
+ description = description.gsub('-', '')
548
+ description = description.capitalize
549
+
550
+ code = charge_line_item['code']&.upcase || ''
551
+ description = "#{description} (#{code})" if code.present?
552
+ description = description.gsub('Fsc', 'FSC') if description.include?('Fsc')
553
+
554
+ description.squish
555
+ end
556
+
557
+ def commit(request)
558
+ url = request[:url]
559
+ headers = request[:headers]
560
+ method = request[:method]
561
+ body = request[:body]
562
+
563
+ response = case method
564
+ when :post
565
+ HTTPClient.post(url, headers:, body:, proxy_url:)
566
+ else
567
+ HTTPClient.get(url, headers:, proxy_url:)
568
+ end
569
+
570
+ if (200..299).exclude?(response.code)
571
+ message = begin
572
+ JSON.parse(response.body)['errorMessage'] || "HTTP #{response.code}"
573
+ rescue JSON::ParserError
574
+ "HTTP #{response.code}"
575
+ end
576
+
577
+ raise FreightKit::ResponseError, message
578
+ end
579
+
580
+ return response if response.content_type != 'application/json'
581
+
582
+ json = JSON.parse(response.body)
583
+ error = json.is_a?(Array) ? nil : json['errorMessage']
584
+
585
+ return json if error.blank?
586
+
587
+ raise FreightKit::InvalidCredentialsError, error if error.downcase.include?('not authorized')
588
+ raise FreightKit::InvalidCredentialsError, error if error.downcase.include?('shipper client does not exist')
589
+ raise FreightKit::ShipmentNotFoundError, error if error.downcase.include?('no history found')
590
+ raise FreightKit::UnserviceableError, error if error.downcase.include?('not serviced')
591
+
592
+ raise FreightKit::ResponseError, error
593
+ end
594
+
595
+ def get_doc_id(documents:, tracking_number:, type:)
596
+ type = type.to_s.upcase
597
+ link = nil
598
+
599
+ documents.each do |document|
600
+ next unless document['documentType'] == type
601
+
602
+ link = document['link']
603
+ end
604
+
605
+ raise FreightKit::DocumentNotFoundError, "API Error: #{name}: Document not found" unless link
606
+
607
+ query = URI.parse(link).query
608
+ doc_id = URI.decode_www_form(query).assoc('docId')&.last
609
+
610
+ raise FreightKit::DocumentNotFoundError, "API Error: #{name}: Document not found" unless doc_id
611
+
612
+ doc_id
613
+ end
614
+
615
+ def parse_api_date_time(date_time, location)
616
+ return if date_time.blank?
617
+
618
+ local_date_time = ::Time.strptime(date_time, '%m/%d/%y %H:%M').to_fs(:db)
619
+ ::FreightKit::DateTime.new(local_date_time:, location:)
620
+ end
621
+
622
+ def parse_document_response(_type, _tracking_number, response)
623
+ DocumentResponse.new(content_type: response.headers['content-type'], data: response.body, request: last_request)
624
+ end
625
+
626
+ def parse_locations_response(country:, response:)
627
+ raise ResponseError, 'API Error: Unknown response' if response.blank?
628
+
629
+ raise ResponseError, 'API Error: Unknown response' unless response.is_a?(Array)
630
+
631
+ locations = response
632
+
633
+ locations = locations.map do |location|
634
+ Location.new(
635
+ address1: location['address1'],
636
+ city: location['city'],
637
+ province: location['state'],
638
+ country: ActiveUtils::Country.find(location['countrycd']),
639
+ contact: Contact.new(fax: location['fax'], phone: location['phone'])
640
+ )
641
+ end
642
+
643
+ locations.select { |location| location.country == country }
644
+ end
645
+
646
+ def parse_pickup_response(response)
647
+ pickup_response = PickupResponse.new(request: last_request, response:)
648
+ pickup_number = response['airbillNumber']
649
+
650
+ if pickup_number.blank?
651
+ pickup_response.error = FreightKit::ResponseError.new('Unknown response')
652
+ return pickup_response
653
+ end
654
+
655
+ pickup_response.pickup_number = pickup_number
656
+ pickup_response
657
+ end
658
+
659
+ def parse_rate_response(shipment:, response:)
660
+ rate_response = RateResponse.new(request: last_request, response:)
661
+
662
+ if response.blank?
663
+ rate_response.error = ResponseError.new('API Error: Unknown response')
664
+ return rate_response
665
+ end
666
+
667
+ error = response.key?('errorMessage')
668
+
669
+ if error.present?
670
+ rate_response.error = ResponseError.new(error)
671
+ return rate_response
672
+ end
673
+
674
+ if response['quoteTotal'].blank?
675
+ rate_response.error = ResponseError.new('Cost is blank')
676
+ return rate_response
677
+ end
678
+
679
+ transit_days = response['transitDaysTotal']
680
+
681
+ charge_line_items = response.dig('chargeLineItems', 'chargeLineItem')
682
+ prices = []
683
+
684
+ charge_line_items.each do |charge_line_item|
685
+ cents = (charge_line_item['amount'] * 100).to_i
686
+ next if cents.zero?
687
+
688
+ description = charge_line_item_description(charge_line_item)
689
+
690
+ prices << Price.new(blame: :api, cents:, description:)
691
+ end
692
+
693
+ rate = Rate.new(
694
+ carrier: self,
695
+ carrier_name: name,
696
+ currency: 'USD',
697
+ scac:,
698
+ service_name: :standard,
699
+ shipment:,
700
+ prices:,
701
+ transit_days:,
702
+ with_excessive_length_fees: rates_with_excessive_length_fees?
703
+ )
704
+
705
+ rate_response.rates = [rate]
706
+ rate_response
707
+ end
708
+
709
+ def parse_tracking_response(response)
710
+ tracking_response = TrackingResponse.new(carrier: self, request: last_request, response:)
711
+
712
+ actual_delivery_date = nil
713
+ estimated_delivery_date = nil
714
+ receiver_location = nil
715
+ scheduled_delivery_date = nil
716
+ ship_time = nil
717
+ shipper_location = nil
718
+
719
+ shipment_events = []
720
+
721
+ api_events = response
722
+ api_events.each do |api_event|
723
+ event = nil
724
+ EVENTS[:types].each do |key, val|
725
+ if api_event['statusCode'].upcase == val
726
+ event = key
727
+ break
728
+ end
729
+ end
730
+ next if event.blank?
731
+
732
+ location = Location.new(
733
+ city: api_event['city'].titleize,
734
+ province: api_event['state'].upcase,
735
+ postal_code: api_event['zip'].upcase,
736
+ country: ActiveUtils::Country.find(api_event['country'])
737
+ )
738
+
739
+ date_time = parse_api_date_time(api_event['recordDate'], location)
740
+
741
+ api_estimated_delivery_date = api_event['estimatedArrivalDate']
742
+ estimated_delivery_date = parse_api_date_time(api_estimated_delivery_date, nil)
743
+
744
+ case event
745
+ when :delivered
746
+ actual_delivery_date = date_time
747
+ receiver_location = location
748
+ when :delivery_appointment_scheduled
749
+ api_date_time = api_event['scheduledDeliveryFromDate']
750
+ scheduled_delivery_date = parse_api_date_time(api_date_time, location)
751
+ when :picked_up
752
+ ship_time = date_time
753
+ shipper_location = location
754
+ end
755
+
756
+ shipment_events << ShipmentEvent.new(date_time:, location:, type_code: event)
757
+ end
758
+
759
+ estimated_delivery_date = scheduled_delivery_date if scheduled_delivery_date.present?
760
+
761
+ status = shipment_events.last&.type_code
762
+
763
+ tracking_number = api_events.last['airbillNumber']
764
+
765
+ tracking_response.assign_attributes(
766
+ actual_delivery_date:,
767
+ destination: receiver_location,
768
+ estimated_delivery_date:,
769
+ origin: shipper_location,
770
+ scheduled_delivery_date:,
771
+ ship_time:,
772
+ shipment_events:,
773
+ status:,
774
+ tracking_number:
775
+ )
776
+
777
+ tracking_response
778
+ end
779
+ end
780
+ end
781
+ end