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,527 +1,602 @@
1
- # frozen_string_literal: true
2
-
3
1
  module FreightKit
4
- class TheGreatInformationFactory < Platform
5
- class << self
6
- def required_credential_types
7
- %i[api]
8
- end
2
+ module Platforms
3
+ class TheGreatInformationFactory < Platform
4
+ class << self
5
+ def mappable_accessorials
6
+ {
7
+ appointment_delivery: 'NOTFEE',
8
+ inside_delivery: 'INSDEL',
9
+ liftgate_delivery: 'LIFDEL',
10
+ liftgate_pickup: 'LIFPIC',
11
+ mall_delivery: 'INSDEL',
12
+ residential_delivery: 'RESDEL',
13
+ residential_pickup: 'RESPIC',
14
+ restaurant_delivery: 'INSDEL'
15
+ }
16
+ end
9
17
 
10
- def requirements
11
- %i[credentials tariff]
12
- end
18
+ def overlength_fees_require_tariff? = true
19
+ def required_credential_types = %i[api]
20
+ def requirements = %i[credentials tariff]
21
+ def services = %i[standard]
22
+
23
+ def unquotable_accessorials
24
+ %i[
25
+ athletic_facility_delivery
26
+ athletic_facility_pickup
27
+ cemetery_delivery
28
+ cemetery_pickup
29
+ church_delivery
30
+ church_pickup
31
+ construction_delivery
32
+ construction_pickup
33
+ dock_dropoff
34
+ dock_pickup
35
+ farm_delivery
36
+ farm_pickup
37
+ fitness_center_delivery
38
+ fitness_center_pickup
39
+ flatbed_delivery
40
+ flatbed_pickup
41
+ golf_course_delivery
42
+ golf_course_pickup
43
+ government_delivery
44
+ government_pickup
45
+ grocery_store_delivery
46
+ grocery_store_pickup
47
+ holiday_delivery
48
+ holiday_pickup
49
+ hospital_delivery
50
+ hospital_pickup
51
+ hotel_delivery
52
+ hotel_pickup
53
+ inspection_site_delivery
54
+ inspection_site_pickup
55
+ jobsite_delivery
56
+ jobsite_pickup
57
+ military_site_delivery
58
+ military_site_pickup
59
+ mine_site_delivery
60
+ mine_site_pickup
61
+ motel_delivery
62
+ motel_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
+ resort_delivery
74
+ resort_pickup
75
+ restaurant_pickup
76
+ school_delivery
77
+ school_pickup
78
+ steel_mill_delivery
79
+ steel_mill_pickup
80
+ storage_facility_delivery
81
+ storage_facility_pickup
82
+ university_delivery
83
+ university_pickup
84
+ utility_site_delivery
85
+ utility_site_pickup
86
+ ]
87
+ end
13
88
 
14
- def overlength_fees_require_tariff?
15
- true
89
+ def unserviceable_accessorials
90
+ %i[
91
+ afterhours_delivery
92
+ airport_delivery
93
+ airport_pickup
94
+ amusement_park_delivery
95
+ amusement_park_pickup
96
+ appointment_pickup
97
+ brewery_delivery
98
+ brewery_pickup
99
+ convention_delivery
100
+ convention_pickup
101
+ customs_brokerage
102
+ driver_assist
103
+ early_morning_delivery
104
+ fair_delivery
105
+ fair_pickup
106
+ grocery_warehouse_delivery
107
+ grocery_warehouse_pickup
108
+ guaranteed_delivery
109
+ guaranteed_delivery_am
110
+ guaranteed_delivery_pm
111
+ inside_pickup
112
+ marina_delivery
113
+ marina_pickup
114
+ saturday_delivery
115
+ saturday_pickup
116
+ sunday_delivery
117
+ sunday_pickup
118
+ winery_delivery
119
+ winery_pickup
120
+ ]
121
+ end
16
122
  end
17
- end
18
-
19
- REACTIVE_FREIGHT_PLATFORM = true
20
123
 
21
- include FreightKit::Rateable
22
- include FreightKit::Trackable
23
- include FreightKit::Pickupable
124
+ include FreightKit::Rateable
125
+ include FreightKit::Trackable
126
+ include FreightKit::Pickupable
127
+
128
+ API = {
129
+ actions: { rates: :getquote, track: :tracktrace, pickup: :requestpickup },
130
+ use_ssl: { rates: false, track: false }
131
+ }.freeze
132
+ EVENTS = {
133
+ types: {
134
+ arrived_at_terminal: 'arrived',
135
+ delivered: 'delivered',
136
+ departed: 'departed',
137
+ out_for_delivery: 'out for delvry',
138
+ pickup_information_sent_to_carrier: 'p/u dispatched'
139
+ }
140
+ }.freeze
24
141
 
25
- protected
142
+ protected
26
143
 
27
- def wrap_request(request)
28
- { 'arg0' => request }
29
- end
144
+ def build_calculated_accessorials(_packages) = []
30
145
 
31
- def build_soap_header
32
- api_credentials = fetch_credential(:api)
146
+ def build_location(city, province)
147
+ Location.new(city: city.titleize, province: province.upcase, country: ActiveUtils::Country.find('USA'))
148
+ end
33
149
 
34
- { username: api_credentials.username, password: api_credentials.password }
35
- end
150
+ def build_pickup_request(
151
+ delivery_from:,
152
+ delivery_to:,
153
+ dispatcher:,
154
+ pickup_from:,
155
+ pickup_to:,
156
+ scac:,
157
+ service:,
158
+ shipment:
159
+ )
160
+ shipper_phone = shipment.origin.contact.phone.gsub(/\s+/, '').gsub(/[()-+.]/, '')
161
+ shipper_phone = shipper_phone[1..] if shipper_phone.length == 11
162
+
163
+ request = {
164
+ securityinfo: build_soap_header,
165
+ shipperinfo: {
166
+ name: shipment.origin.contact.company_name,
167
+ contact: shipment.origin.contact.name,
168
+ phonenumber: shipper_phone,
169
+ address1: shipment.origin.address1,
170
+ address2: shipment.origin.address2,
171
+ city: shipment.origin.city,
172
+ state: shipment.origin.province,
173
+ ReadyDate: pickup_from.strftime('%m/%d/%Y'),
174
+ ReadyTime: pickup_from.strftime('%H%M').to_i,
175
+ CloseTime: pickup_to.strftime('%H%M').to_i,
176
+ zip: shipment.origin.postal_code,
177
+ SpecialInstructions: ''
178
+ },
179
+ ShipmentCount: 1,
180
+ shipments: [
181
+ {
182
+ DestZip: shipment.destination.postal_code,
183
+ Pieces: shipment.packages.sum(&:quantity),
184
+ Pallets: shipment.packages.select { |p| p.packaging.pallet? }.sum(&:quantity),
185
+ Weight: shipment.packages.sum { |p| p.pounds(:total).ceil },
186
+ HAZ: shipment.packages.any?(&:hazmat?) ? 'Y' : 'N',
187
+ dblStack: 'N',
188
+ SortSeg: 'N',
189
+ Pro: shipment.pro,
190
+ Liftgate: shipment.accessorials.include?(:liftgate_pickup) ? 'Y' : 'N'
191
+ }
192
+ ]
193
+ }
36
194
 
37
- def commit(action, request)
38
- client_args = {
39
- wsdl: build_url(action),
40
- convert_request_keys_to: :upcase,
41
- env_namespace: :soapenv
42
- }
43
-
44
- call_args = {
45
- headers: { 'SOAPAction' => '""' },
46
- soap_action: false,
47
- message: request
48
- }
49
-
50
- ::FreightKit::SoapClient.new(
51
- carrier: self,
52
- action:,
53
- client_args:,
54
- call_args:,
55
- soap_operation: @conf.dig(:api, :actions, action),
56
- ).call
57
- end
195
+ request = wrap_request(request)
196
+ save_request(request)
58
197
 
59
- def parse_api_date(date)
60
- return if date.blank?
198
+ request
199
+ end
61
200
 
62
- local_date = ::Date.strptime(date, '%m/%d/%Y')
63
- ::FreightKit::DateTime.new(local_date:)
64
- end
201
+ def build_rate_request(shipment:)
202
+ accessorials = []
65
203
 
66
- def parse_api_date_time(date_time, location)
67
- return if date_time.blank?
204
+ if shipment.accessorials.present?
205
+ serviceable_accessorials?(shipment.accessorials)
68
206
 
69
- format = date_time.include?('-') ? '%Y-%m-%d %H:%M' : '%m/%d/%Y %H:%M'
207
+ accessorials = shipment
208
+ .accessorials
209
+ .select { |accessorial| unserviceable_accessorials.exclude?(accessorial) }
210
+ .map { |accessorial| mappable_accessorials[accessorial] }
211
+ end
70
212
 
71
- local_date_time = ::Time.strptime(date_time, format).to_fs(:db)
72
- ::FreightKit::DateTime.new(local_date_time:, location:)
73
- end
213
+ accessorials += build_calculated_accessorials(shipment.packages)
214
+ accessorials.uniq!
215
+
216
+ item = shipment.packages.map do |package|
217
+ {
218
+ _class: package.freight_class,
219
+ description: (package.description || 'Freight')[..8].upcase,
220
+ haz: (package.hazmat? ? 'Y' : ''),
221
+ pallets: (package.packaging.pallet? ? package.quantity : 0),
222
+ pieces: package.quantity,
223
+ weight: package.pounds(:total).ceil
224
+ }
225
+ end
74
226
 
75
- def build_url(action)
76
- scheme = @conf.dig(:api, :use_ssl, action) ? 'https://' : 'http://'
77
- domain = @conf.dig(:api, :domains, action).presence || @conf.dig(:api, :domain)
78
- port = @conf.dig(:api, :ports, action)
79
- return [scheme, domain, @conf.dig(:api, :endpoints, action)].join unless port
227
+ request = {
228
+ securityinfo: build_soap_header,
229
+ quote: {
230
+ iam: 'D', # S for shipper, C for consignee, D for third party
231
+ shipper: {
232
+ city: shipment.origin.city.upcase,
233
+ state: shipment.origin.province.upcase,
234
+ zip: shipment.origin.postal_code.gsub(/\s+/, '').upcase
235
+ },
236
+ consignee: {
237
+ city: shipment.destination.city.upcase,
238
+ state: shipment.destination.province.upcase,
239
+ zip: shipment.destination.postal_code.gsub(/\s+/, '').upcase
240
+ },
241
+ accessorialcount: accessorials.count,
242
+ accessorial: accessorials.map { |code| { code: } },
243
+ ppdcol: 'P', # Prepaid
244
+ itemcount: shipment.packages.sum(&:quantity),
245
+ item:
246
+ }
247
+ }
80
248
 
81
- "#{scheme}#{domain}:#{port}#{@conf.dig(:api, :endpoints, action)}"
82
- end
249
+ request = wrap_request(request)
250
+ save_request(request)
83
251
 
84
- def strip_date(str)
85
- str ? str.split(/[A|P]M /)[1] : nil
86
- end
252
+ request
253
+ end
87
254
 
88
- # Rates
255
+ def build_soap_header
256
+ api_credentials = fetch_credential(:api)
89
257
 
90
- def build_calculated_accessorials(packages)
91
- []
92
- end
258
+ { username: api_credentials.username, password: api_credentials.password }
259
+ end
93
260
 
94
- def build_rate_request(shipment:)
95
- accessorials = []
261
+ def build_tracking_request(tracking_number)
262
+ request = { pronumber: tracking_number, securityinfo: build_soap_header }
96
263
 
97
- if shipment.accessorials.present?
98
- serviceable_accessorials?(shipment.accessorials)
264
+ request = wrap_request(request)
265
+ save_request(request)
99
266
 
100
- accessorials = shipment
101
- .accessorials
102
- .select { |accessorial| @conf.dig(:accessorials, :unserviceable).exclude?(accessorial) }
103
- .map { |accessorial| @conf.dig(:accessorials, :mappable, accessorial) }
267
+ request
104
268
  end
105
269
 
106
- accessorials += build_calculated_accessorials(shipment.packages)
107
- accessorials.uniq!
108
-
109
- item = shipment.packages.map do |package|
110
- {
111
- _class: package.freight_class,
112
- description: (package.description || 'Freight')[..8].upcase,
113
- haz: (package.hazmat? ? 'Y' : ''),
114
- pallets: (package.packaging.pallet? ? package.quantity : 0),
115
- pieces: package.quantity,
116
- weight: package.pounds(:total).ceil
117
- }
118
- end
270
+ def build_url(action)
271
+ scheme = self.class::API.dig(:use_ssl, action) ? 'https://' : 'http://'
272
+ domain = self.class::API.dig(:domains, action).presence || self.class::API[:domain]
273
+ port = self.class::API.dig(:ports, action)
274
+ return [scheme, domain, self.class::API.dig(:endpoints, action)].join unless port
119
275
 
120
- request = {
121
- securityinfo: build_soap_header,
122
- quote: {
123
- iam: 'D', # S for shipper, C for consignee, D for third party
124
- shipper: {
125
- city: shipment.origin.city.upcase,
126
- state: shipment.origin.province.upcase,
127
- zip: shipment.origin.postal_code.gsub(/\s+/, '').upcase
128
- },
129
- consignee: {
130
- city: shipment.destination.city.upcase,
131
- state: shipment.destination.province.upcase,
132
- zip: shipment.destination.postal_code.gsub(/\s+/, '').upcase
133
- },
134
- accessorialcount: accessorials.count,
135
- accessorial: accessorials.map { |code| { code: } },
136
- ppdcol: 'P', # Prepaid
137
- itemcount: shipment.packages.sum(&:quantity),
138
- item:
139
- }
140
- }
141
-
142
- request = wrap_request(request)
143
- save_request(request)
144
-
145
- request
146
- end
276
+ "#{scheme}#{domain}:#{port}#{self.class::API.dig(:endpoints, action)}"
277
+ end
147
278
 
148
- def rate_item_description(rate_item)
149
- description = rate_item[:description] || ''
150
- description = description.gsub('-', '')
151
- description = description.squish
152
- description = description.sub('disc.on', 'discount on')
153
- description = description.capitalize
154
- description = description.sub('zip code', 'ZIP code')
155
- description.sub('Zip code', 'ZIP code')
156
- end
279
+ def commit(action, request)
280
+ client_args = { wsdl: build_url(action), convert_request_keys_to: :upcase, env_namespace: :soapenv }
157
281
 
158
- def parse_rate_response(shipment:, response:)
159
- rate_response = RateResponse.new(request: last_request, response:)
282
+ call_args = { headers: { 'SOAPAction' => '""' }, soap_action: false, message: request }
160
283
 
161
- if response.blank?
162
- rate_response.error = ResponseError.new('Unknown response')
163
- return rate_response
284
+ ::FreightKit::SoapClient.new(
285
+ carrier: self,
286
+ action:,
287
+ client_args:,
288
+ call_args:,
289
+ soap_operation: self.class::API.dig(:actions, action)
290
+ ).call
164
291
  end
165
292
 
166
- error_code = response.dig(:getquote_response, :return, :rating, :errorcode)
167
- if error_code
168
- rate_response.error = parse_error_response(error_code)
169
- return rate_response
293
+ def parse_api_date(date)
294
+ return if date.blank?
295
+
296
+ local_date = ::Date.strptime(date, '%m/%d/%Y')
297
+ ::FreightKit::DateTime.new(local_date:)
170
298
  end
171
299
 
172
- total_cents = response.dig(:getquote_response, :return, :rating, :amount)
300
+ def parse_api_date_time(date_time, location)
301
+ return if date_time.blank?
173
302
 
174
- raise FreightKit::ResponseError, 'API Error: Cost is empty' if total_cents.blank?
303
+ format = date_time.include?('-') ? '%Y-%m-%d %H:%M' : '%m/%d/%Y %H:%M'
175
304
 
176
- rate_items = response.dig(:getquote_response, :return, :rateitem)
177
- total_cents = (total_cents.to_f * 100).to_i
305
+ local_date_time = ::Time.strptime(date_time, format).to_fs(:db)
306
+ ::FreightKit::DateTime.new(local_date_time:, location:)
307
+ end
178
308
 
179
- prices = []
309
+ def parse_document_response(type, tracking_number)
310
+ base_url = build_url(type)
311
+ website_credentials = fetch_credential(:api)
312
+ query_parameter = "&username=#{website_credentials.username}&" \
313
+ "password=#{website_credentials.password}&" \
314
+ "pronumber=#{tracking_number}&" \
315
+ 'format=PDF'
180
316
 
181
- # Confusing API sometimes returns lines of freight with high costs and then later includes includes lines that
182
- # override the high cost without adding a discount, etc
183
- rate_items.each do |rate_item|
184
- next if ['Sub Total', 'GrandTotal'].include?(rate_item[:acccode])
317
+ url = [base_url, query_parameter].join
185
318
 
186
- # Exclude lines that are just the packages repeated back to us
187
- next unless rate_item[:pallets] == '0' && rate_item[:pieces] == '0'
319
+ response = HTTPClient.get(url)
188
320
 
189
- cents = (rate_item[:amount].to_f * 100).to_i
190
- description = rate_item_description(rate_item)
321
+ image = response.deep_symbolize_keys.dig(:ImageRequest, :Image)
322
+ image = image.last if image.is_a?(Array)
323
+ # ImageRequest[:Image] sometimes return an Array and both images are identical
191
324
 
192
- prices << Price.new(blame: :api, cents:, description:)
193
- end
325
+ document_response = DocumentResponse.new(request: url)
194
326
 
195
- # Since we expected the low-cost overriding lines earlier, we need to handle situations where those lines do not
196
- # appear
197
- if prices.sum(&:cents) < total_cents
198
- prices = [
199
- Price.new(
200
- blame: :api,
201
- cents: total_cents - prices.sum(&:cents),
202
- description: 'Freight',
203
- ),
204
- ] + prices
205
- end
327
+ unless image
328
+ document_response.error = DocumentNotFoundError.new
329
+ return document_response
330
+ end
206
331
 
207
- if self.class.overlength_fees_require_tariff?
208
- shipment.packages.each do |package|
209
- cents = overlength_fee(tariff, package)
210
- next unless cents.positive?
332
+ base64_document_data = image.dig(:ImageData, :__content__)
211
333
 
212
- prices << Price.new(
213
- blame: :tariff,
214
- cents:,
215
- description: 'Overlength fee',
216
- )
334
+ unless base64_document_data
335
+ document_response.error = DocumentNotFoundError.new
336
+ return document_response
217
337
  end
218
- end
219
338
 
220
- transit_days = response.dig(
221
- :getquote_response,
222
- :return,
223
- :service,
224
- :days,
225
- ).to_i
339
+ decoded_pdf_data = Base64.decode64(base64_document_data)
340
+ document_response.assign_attributes(content_type: 'application/pdf', data: decoded_pdf_data)
226
341
 
227
- # Calculate real transit time based on information we have about the destination service days
228
- %i[mon tue wed thu fri].each do |weekday|
229
- transit_days += 1 if response.dig(:getquote_response, :return, :service, :destination, weekday) == 'N'
342
+ document_response
230
343
  end
231
344
 
232
- estimate_reference = response.dig(
233
- :getquote_response,
234
- :return,
235
- :rating,
236
- :quotenumber,
237
- )
238
-
239
- rate = Rate.new(
240
- carrier_name: self.class.name,
241
- carrier: self,
242
- currency: 'USD',
243
- estimate_reference:,
244
- prices:,
245
- scac: self.class.scac.upcase,
246
- service_name: :standard,
247
- shipment:,
248
- transit_days:,
249
- with_excessive_length_fees: @conf.dig(:attributes, :rates, :with_excessive_length_fees),
250
- )
251
-
252
- rate_response.rates = [rate]
253
- rate_response
254
- end
345
+ def parse_error_response(error_code)
346
+ case error_code
347
+ when 'BADUSRPWD' then InvalidCredentialsError.new
348
+ when 'NOSVC' then UnserviceableError.new('Origin or destination has no service available')
349
+ when 'BADCONZIP' then UnserviceableError.new('Invalid destination ZIP code')
350
+ else
351
+ ResponseError.new("API error code #{error_code}")
352
+ end
353
+ end
255
354
 
256
- # Tracking
355
+ def parse_location(code)
356
+ country = ActiveUtils::Country.find('USA')
357
+ return Location.new(country:) unless code
257
358
 
258
- def build_tracking_request(tracking_number)
259
- request = { pronumber: tracking_number, securityinfo: build_soap_header }
359
+ location = self.class::EVENTS.dig(:locations, code.to_sym)
260
360
 
261
- request = wrap_request(request)
262
- save_request(request)
361
+ if location
362
+ Location.new(city: location[:city], province: location[:state], country:)
363
+ else
364
+ Location.new(city: code, country:)
365
+ end
366
+ end
263
367
 
264
- request
265
- end
368
+ def parse_pickup_response(response)
369
+ pickup_response = PickupResponse.new(request: last_request, response:)
266
370
 
267
- def parse_location(code)
268
- country = ActiveUtils::Country.find('USA')
269
- return Location.new(country:) unless code
371
+ result = response.dig(:requestpickup_response, :return, :results)
372
+ if result[:errorcode]
373
+ pickup_response.error = FreightKit::ResponseError.new("API Error: #{result[:errorcode]}")
374
+ return pickup_response
375
+ end
270
376
 
271
- location = @conf.dig(:events, :locations, code.to_sym)
377
+ pickup_number = result[:pickupnumber]
272
378
 
273
- if location
274
- Location.new(
275
- city: location[:city],
276
- province: location[:state],
277
- country:,
278
- )
279
- else
280
- Location.new(city: code, country:)
281
- end
282
- end
379
+ if pickup_number == '0'
380
+ pickup_response.error = FreightKit::ResponseError.new('Unknown Error')
381
+ return pickup_response
382
+ end
283
383
 
284
- def parse_error_response(error_code)
285
- case error_code
286
- when 'BADUSRPWD' then InvalidCredentialsError.new
287
- when 'NOSVC' then UnserviceableError.new('Origin or destination has no service available')
288
- when 'BADCONZIP' then UnserviceableError.new('Invalid destination ZIP code')
289
- else
290
- ResponseError.new("API error code #{error_code}")
384
+ pickup_response.pickup_number = pickup_number
385
+ pickup_response
291
386
  end
292
- end
293
-
294
- def build_location(city, province)
295
- Location.new(city: city.titleize, province: province.upcase, country: ActiveUtils::Country.find('USA'))
296
- end
297
387
 
298
- def parse_tracking_response(response)
299
- tracking_response = TrackingResponse.new(carrier: self, request: last_request, response:)
300
- mapped_response = response.dig(:tracktrace_response, :return, :currentstatus)
388
+ def parse_rate_response(shipment:, response:)
389
+ rate_response = RateResponse.new(request: last_request, response:)
301
390
 
302
- if mapped_response[:errorcode]
303
- tracking_response.error = parse_error_response(mapped_response[:errorcode])
304
- return tracking_response
305
- end
391
+ if response.blank?
392
+ rate_response.error = ResponseError.new('Unknown response')
393
+ return rate_response
394
+ end
306
395
 
307
- receiver_location = build_location(
308
- mapped_response.dig(:consignee, :city),
309
- mapped_response.dig(:consignee, :state),
310
- )
311
- shipper_location = build_location(mapped_response.dig(:shipper, :city), mapped_response.dig(:shipper, :state))
396
+ error_code = response.dig(:getquote_response, :return, :rating, :errorcode)
397
+ if error_code
398
+ rate_response.error = parse_error_response(error_code)
399
+ return rate_response
400
+ end
312
401
 
313
- actual_delivery_date = mapped_response[:deliverydate]
402
+ total_cents = response.dig(:getquote_response, :return, :rating, :amount)
314
403
 
315
- if actual_delivery_date.present?
316
- comment = mapped_response[:status].downcase
404
+ raise FreightKit::ResponseError, 'API Error: Cost is empty' if total_cents.blank?
317
405
 
318
- if comment.starts_with?('delivered')
319
- api_date = comment.downcase.split('signed')[0].split('on')[1].strip.sub('at ', '')
320
- actual_delivery_date = parse_api_date(api_date)
321
- end
322
- end
406
+ rate_items = response.dig(:getquote_response, :return, :rateitem)
407
+ total_cents = (total_cents.to_f * 100).to_i
323
408
 
324
- shipment_events = []
409
+ prices = []
325
410
 
326
- ship_time = parse_api_date(mapped_response[:shipdate])
327
- # Leave this open for modification later
328
- picked_up_event = ShipmentEvent.new(location: shipper_location, date_time: ship_time, type_code: :picked_up)
411
+ # Confusing API sometimes returns lines of freight with high costs and then later includes includes lines that
412
+ # override the high cost without adding a discount, etc
413
+ rate_items.each do |rate_item|
414
+ next if ['Sub Total', 'GrandTotal'].include?(rate_item[:acccode])
329
415
 
330
- scheduled_delivery_date = parse_api_date(mapped_response[:estdeliverydate])
331
- tracking_number = response.dig(:tracktrace_response, :return, :pronumber)
416
+ # Exclude lines that are just the packages repeated back to us
417
+ next unless rate_item[:pallets] == '0' && rate_item[:pieces] == '0'
332
418
 
333
- api_events = response.dig(:tracktrace_response, :return, :history)
334
- api_events = [api_events] if api_events.is_a?(Hash)
419
+ cents = (rate_item[:amount].to_f * 100).to_i
420
+ description = rate_item_description(rate_item)
335
421
 
336
- last_location = nil
422
+ prices << Price.new(blame: :api, cents:, description:)
423
+ end
337
424
 
338
- api_events.each_with_index do |api_event, index|
339
- next if api_event[:description].blank?
425
+ # Since we expected the low-cost overriding lines earlier, we need to handle situations where those lines do not
426
+ # appear
427
+ if prices.sum(&:cents) < total_cents
428
+ prices = [Price.new(blame: :api, cents: total_cents - prices.sum(&:cents), description: 'Freight')] + prices
429
+ end
340
430
 
341
- event = nil
342
- @conf.dig(:events, :types).each do |key, val|
343
- next if api_event[:description].downcase.exclude?(val)
431
+ if self.class.overlength_fees_require_tariff?
432
+ shipment.packages.each do |package|
433
+ cents = overlength_fee(tariff, package)
434
+ next unless cents.positive?
344
435
 
345
- event = key
346
- break
436
+ prices << Price.new(blame: :tariff, cents:, description: 'Overlength fee')
437
+ end
347
438
  end
348
- next if event.blank?
349
439
 
350
- location = if api_event[:location].blank?
351
- case event
352
- when :departed then last_location
353
- when :picked_up, :pickup_information_sent_to_carrier then shipper_location
354
- when :delivered, :out_for_delivery then receiver_location
355
- end
356
- else
357
- parse_location(api_event[:location])
358
- end
359
-
360
- api_date_time = "#{api_event[:date]} #{api_event[:time]}"
361
- date_time = api_event[:date].present? ? parse_api_date_time(api_date_time, location) : nil
362
-
363
- case event
364
- when :arrived_at_terminal
365
- # Duplicate event occurs without location data from API
366
- break if api_event[:location].blank?
367
- when :delivered
368
- actual_delivery_date = date_time
369
- when :out_for_delivery
370
- # Do not consider out for delivery when out for delivery and interlined dates match
371
- next_api_event = api_events[index + 1]
372
-
373
- break if next_api_event.blank?
374
-
375
- if next_api_event[:description].include?('INTERLINE') && next_api_event[:date] == api_event[:date]
376
- shipment_events << ShipmentEvent.new(date_time:, location:, type_code: :departed)
377
- next
378
- end
379
- when :pickup_information_sent_to_carrier
380
- # Pickup event appears after carrier information sent, let's fix that
381
- picked_up_event.date_time = date_time.dup
440
+ transit_days = response.dig(:getquote_response, :return, :service, :days).to_i
441
+
442
+ # Calculate real transit time based on information we have about the destination service days
443
+ %i[mon tue wed thu fri].each do |weekday|
444
+ transit_days += 1 if response.dig(:getquote_response, :return, :service, :destination, weekday) == 'N'
382
445
  end
383
446
 
384
- last_location = location
447
+ estimate_reference = response.dig(:getquote_response, :return, :rating, :quotenumber)
448
+
449
+ rate = Rate.new(
450
+ carrier_name: name,
451
+ carrier: self,
452
+ currency: 'USD',
453
+ estimate_reference:,
454
+ prices:,
455
+ scac:,
456
+ service_name: :standard,
457
+ shipment:,
458
+ transit_days:,
459
+ with_excessive_length_fees: rates_with_excessive_length_fees?
460
+ )
385
461
 
386
- shipment_events << ShipmentEvent.new(date_time:, location:, type_code: event)
462
+ rate_response.rates = [rate]
463
+ rate_response
387
464
  end
388
465
 
389
- shipment_events << picked_up_event
466
+ def parse_tracking_response(response)
467
+ tracking_response = TrackingResponse.new(carrier: self, request: last_request, response:)
468
+ mapped_response = response.dig(:tracktrace_response, :return, :currentstatus)
390
469
 
391
- if shipment_events.collect(&:date_time).none?(nil)
392
- shipment_events = shipment_events.sort_by do |shipment_event|
393
- d = shipment_event.date_time
394
- d&.local_date_time || d.date_time_with_zone&.to_fs(:db) || d.local_date&.to_fs(:db)
470
+ if mapped_response[:errorcode]
471
+ tracking_response.error = parse_error_response(mapped_response[:errorcode])
472
+ return tracking_response
395
473
  end
396
- end
397
474
 
398
- status = shipment_events.last&.type_code
399
-
400
- # Workarounds for false status on certain events when timestamps are in wrong order
401
- status = :out_for_delivery if shipment_events.find do |shipment_event|
402
- shipment_event.type_code == :out_for_delivery
403
- end
404
- status = :delivered if shipment_events.find { |shipment_event| shipment_event.type_code == :delivered }
405
-
406
- tracking_response.assign_attributes(
407
- actual_delivery_date:,
408
- destination: receiver_location,
409
- origin: shipper_location,
410
- scheduled_delivery_date:,
411
- ship_time:,
412
- shipment_events:,
413
- status:,
414
- tracking_number:,
415
- )
475
+ receiver_location = build_location(
476
+ mapped_response.dig(:consignee, :city),
477
+ mapped_response.dig(:consignee, :state)
478
+ )
479
+ shipper_location = build_location(mapped_response.dig(:shipper, :city), mapped_response.dig(:shipper, :state))
416
480
 
417
- tracking_response
418
- end
481
+ actual_delivery_date = mapped_response[:deliverydate]
419
482
 
420
- def parse_pickup_response(response)
421
- pickup_response = PickupResponse.new(request: last_request, response:)
483
+ if actual_delivery_date.present?
484
+ comment = mapped_response[:status].downcase
422
485
 
423
- result = response.dig(:requestpickup_response, :return, :results)
424
- if result[:errorcode]
425
- pickup_response.error = FreightKit::ResponseError.new("API Error: #{result[:errorcode]}")
426
- return pickup_response
427
- end
486
+ if comment.starts_with?('delivered')
487
+ api_date = comment.downcase.split('signed')[0].split('on')[1].strip.sub('at ', '')
488
+ actual_delivery_date = parse_api_date(api_date)
489
+ end
490
+ end
428
491
 
429
- pickup_number = result[:pickupnumber]
492
+ shipment_events = []
430
493
 
431
- if pickup_number == '0'
432
- pickup_response.error = FreightKit::ResponseError.new('Unknown Error')
433
- return pickup_response
434
- end
494
+ ship_time = parse_api_date(mapped_response[:shipdate])
495
+ # Leave this open for modification later
496
+ picked_up_event = ShipmentEvent.new(location: shipper_location, date_time: ship_time, type_code: :picked_up)
435
497
 
436
- pickup_response.pickup_number = pickup_number
437
- pickup_response
438
- end
498
+ scheduled_delivery_date = parse_api_date(mapped_response[:estdeliverydate])
499
+ tracking_number = response.dig(:tracktrace_response, :return, :pronumber)
439
500
 
440
- def build_pickup_request(
441
- delivery_from:,
442
- delivery_to:,
443
- dispatcher:,
444
- pickup_from:,
445
- pickup_to:,
446
- scac:,
447
- service:,
448
- shipment:
449
- )
450
- shipper_phone = shipment.origin.contact.phone.gsub(/\s+/, '').gsub(/[()-+.]/, '')
451
- shipper_phone = shipper_phone[1..] if shipper_phone.length == 11
452
-
453
- request = {
454
- securityinfo: build_soap_header,
455
- shipperinfo: {
456
- name: shipment.origin.contact.company_name,
457
- contact: shipment.origin.contact.name,
458
- phonenumber: shipper_phone,
459
- address1: shipment.origin.address1,
460
- address2: shipment.origin.address2,
461
- city: shipment.origin.city,
462
- state: shipment.origin.province,
463
- ReadyDate: pickup_from.strftime('%m/%d/%Y'),
464
- ReadyTime: pickup_from.strftime('%H%M').to_i,
465
- CloseTime: pickup_to.strftime('%H%M').to_i,
466
- zip: shipment.origin.postal_code,
467
- SpecialInstructions: ''
468
- },
469
- ShipmentCount: 1,
470
- shipments: [
471
- {
472
- DestZip: shipment.destination.postal_code,
473
- Pieces: shipment.packages.sum(&:quantity),
474
- Pallets: shipment.packages.select { |p| p.packaging.pallet? }.sum(&:quantity),
475
- Weight: shipment.packages.sum { |p| p.pounds(:total).ceil },
476
- HAZ: shipment.packages.any?(&:hazmat?) ? 'Y' : 'N',
477
- dblStack: 'N',
478
- SortSeg: 'N',
479
- Pro: shipment.pro,
480
- Liftgate: shipment.accessorials.include?(:liftgate_pickup) ? 'Y' : 'N'
481
- },
482
- ]
483
- }
484
-
485
- request = wrap_request(request)
486
- save_request(request)
487
-
488
- request
489
- end
501
+ api_events = response.dig(:tracktrace_response, :return, :history)
502
+ api_events = [api_events] if api_events.is_a?(Hash)
503
+
504
+ last_location = nil
490
505
 
491
- def parse_document_response(type, tracking_number)
492
- base_url = build_url(type)
493
- website_credentials = fetch_credential(:api)
494
- query_parameter = "&username=#{website_credentials.username}&" \
495
- "password=#{website_credentials.password}&" \
496
- "pronumber=#{tracking_number}&" \
497
- 'format=PDF'
506
+ api_events.each_with_index do |api_event, index|
507
+ next if api_event[:description].blank?
498
508
 
499
- url = [base_url, query_parameter].join
509
+ event = nil
510
+ self.class::EVENTS[:types].each do |key, val|
511
+ next if api_event[:description].downcase.exclude?(val)
500
512
 
501
- response = HTTPClient.get(url)
513
+ event = key
514
+ break
515
+ end
516
+ next if event.blank?
517
+
518
+ location = if api_event[:location].blank?
519
+ case event
520
+ when :departed then last_location
521
+ when :picked_up, :pickup_information_sent_to_carrier then shipper_location
522
+ when :delivered, :out_for_delivery then receiver_location
523
+ end
524
+ else
525
+ parse_location(api_event[:location])
526
+ end
502
527
 
503
- image = response.deep_symbolize_keys.dig(:ImageRequest, :Image)
504
- image = image.last if image.is_a?(Array)
505
- # ImageRequest[:Image] sometimes return an Array and both images are identical
528
+ api_date_time = "#{api_event[:date]} #{api_event[:time]}"
529
+ date_time = api_event[:date].present? ? parse_api_date_time(api_date_time, location) : nil
530
+
531
+ case event
532
+ when :arrived_at_terminal
533
+ # Duplicate event occurs without location data from API
534
+ break if api_event[:location].blank?
535
+ when :delivered
536
+ actual_delivery_date = date_time
537
+ when :out_for_delivery
538
+ # Do not consider out for delivery when out for delivery and interlined dates match
539
+ next_api_event = api_events[index + 1]
540
+
541
+ break if next_api_event.blank?
542
+
543
+ if next_api_event[:description].include?('INTERLINE') && next_api_event[:date] == api_event[:date]
544
+ shipment_events << ShipmentEvent.new(date_time:, location:, type_code: :departed)
545
+ next
546
+ end
547
+ when :pickup_information_sent_to_carrier
548
+ # Pickup event appears after carrier information sent, let's fix that
549
+ picked_up_event.date_time = date_time.dup
550
+ end
506
551
 
507
- document_response = DocumentResponse.new(request: url)
552
+ last_location = location
508
553
 
509
- unless image
510
- document_response.error = DocumentNotFoundError.new
511
- return document_response
512
- end
554
+ shipment_events << ShipmentEvent.new(date_time:, location:, type_code: event)
555
+ end
513
556
 
514
- base64_document_data = image.dig(:ImageData, :__content__)
557
+ shipment_events << picked_up_event
515
558
 
516
- unless base64_document_data
517
- document_response.error = DocumentNotFoundError.new
518
- return document_response
559
+ if shipment_events.collect(&:date_time).none?(nil)
560
+ shipment_events = shipment_events.sort_by do |shipment_event|
561
+ d = shipment_event.date_time
562
+ d&.local_date_time || d.date_time_with_zone&.to_fs(:db) || d.local_date&.to_fs(:db)
563
+ end
564
+ end
565
+
566
+ status = shipment_events.last&.type_code
567
+
568
+ # Workarounds for false status on certain events when timestamps are in wrong order
569
+ status = :out_for_delivery if shipment_events.find do |shipment_event|
570
+ shipment_event.type_code == :out_for_delivery
571
+ end
572
+ status = :delivered if shipment_events.find { |shipment_event| shipment_event.type_code == :delivered }
573
+
574
+ tracking_response.assign_attributes(
575
+ actual_delivery_date:,
576
+ destination: receiver_location,
577
+ origin: shipper_location,
578
+ scheduled_delivery_date:,
579
+ ship_time:,
580
+ shipment_events:,
581
+ status:,
582
+ tracking_number:
583
+ )
584
+
585
+ tracking_response
519
586
  end
520
587
 
521
- decoded_pdf_data = Base64.decode64(base64_document_data)
522
- document_response.assign_attributes(content_type: 'application/pdf', data: decoded_pdf_data)
588
+ def rate_item_description(rate_item)
589
+ description = rate_item[:description] || ''
590
+ description = description.gsub('-', '')
591
+ description = description.squish
592
+ description = description.sub('disc.on', 'discount on')
593
+ description = description.capitalize
594
+ description = description.sub('zip code', 'ZIP code')
595
+ description.sub('Zip code', 'ZIP code')
596
+ end
523
597
 
524
- document_response
598
+ def strip_date(str) = str ? str.split(/[A|P]M /)[1] : nil
599
+ def wrap_request(request) = { 'arg0' => request }
525
600
  end
526
601
  end
527
602
  end