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
@@ -1,450 +1,431 @@
1
- # frozen_string_literal: true
2
-
3
1
  module FreightKit
4
- class CarrierLogistics < Platform
5
- class << self
6
- def required_credential_types
7
- %i[api]
8
- end
2
+ module Platforms
3
+ class CarrierLogistics < Platform
4
+ class << self
5
+ def required_credential_types = %i[api]
9
6
 
10
- def requirements
11
- return %i[credentials tariff] if overlength_fees_require_tariff?
7
+ def requirements
8
+ return %i[credentials tariff] if overlength_fees_require_tariff?
12
9
 
13
- %i[credentials]
10
+ %i[credentials]
11
+ end
14
12
  end
15
- end
16
-
17
- REACTIVE_FREIGHT_PLATFORM = true
18
13
 
19
- EXPIRED_CREDENTIALS_MESSAGES = [
20
- 'Your password has expired',
21
- ].freeze
22
- INVALID_CREDENTIALS_MESSAGES = [
23
- 'Unable to log in',
24
- 'Your Username or Password is Incorrect',
25
- ].freeze
14
+ include FreightKit::Trackable
15
+ include FreightKit::Rateable
16
+
17
+ API = {
18
+ domain: 'tracking.carrierlogistics.com',
19
+ endpoints: {
20
+ bol: '/scripts/@CARRIER_CODE@.pol/web-login.htm?',
21
+ pod: '/scripts/@CARRIER_CODE@.pol/web-login.htm?',
22
+ rates: '/scripts/@CARRIER_CODE@.pol/ratequote.xml?',
23
+ track: '/scripts/@CARRIER_CODE@.pol/protracexml.htm?'
24
+ },
25
+ use_ssl: { bol: true, pod: true, rates: true, track: true }
26
+ }.freeze
27
+ EVENTS = {
28
+ types: {
29
+ arrived_at_terminal: 'trailer arrived',
30
+ delivered: 'delivered',
31
+ departed: 'trailer departed',
32
+ out_for_delivery: 'out for delivery',
33
+ picked_up: 'picked up',
34
+ pickup_scheduled: 'pickup request received',
35
+ trailer_closed: 'loaded on trailer',
36
+ trailer_unloaded: 'undisp'
37
+ }
38
+ }.freeze
39
+ EXPIRED_CREDENTIALS_MESSAGES = ['Your password has expired'].freeze
40
+ INVALID_CREDENTIALS_MESSAGES = ['Unable to log in', 'Your Username or Password is Incorrect'].freeze
41
+
42
+ # Documents
43
+
44
+ def build_calculated_accessorials(_shipment) = []
45
+
46
+ def build_rate_request(shipment:)
47
+ api_credentials = fetch_credential(:api)
48
+
49
+ query = {
50
+ xmlv: 'yes', # must be first
51
+ quotenumber: 'YES',
52
+ vdzip: shipment.destination.postal_code,
53
+ vozip: shipment.origin.postal_code,
54
+ xmlpass: api_credentials.password,
55
+ xmluser: api_credentials.username
56
+ }
57
+
58
+ i = 0
59
+ shipment.packages.each do |package|
60
+ i += 1 # API starts at 1 (not 0)
61
+
62
+ query["vclass[#{i}]"] = package.freight_class
63
+ query["wheight[#{i}]"] = package.height(:in).ceil
64
+ query["wlength[#{i}]"] = package.length(:in).ceil
65
+ query["wpallets[#{i}]"] = package.quantity
66
+ query["wpieces[#{i}]"] = package.quantity
67
+ query["wweight[#{i}]"] = package.pounds(:total).ceil
68
+ query["wwidth[#{i}]"] = package.width(:in).ceil
69
+ end
26
70
 
27
- include FreightKit::Trackable
28
- include FreightKit::Rateable
71
+ accessorials = []
29
72
 
30
- # Documents
73
+ if shipment.accessorials.present?
74
+ serviceable_accessorials?(shipment.accessorials)
31
75
 
32
- def pod(tracking_number)
33
- query = build_tracking_request(tracking_number)
34
- response = commit(:track, query)
35
- parse_document_response(response, :pod)
36
- end
76
+ shipment
77
+ .accessorials
78
+ .reject { |accessorial| unquotable_accessorials.include?(accessorial) }
79
+ .each do |shipment_accessorial|
80
+ conf_accessorial = mappable_accessorials[shipment_accessorial]
37
81
 
38
- def scanned_bol(tracking_number)
39
- query = build_tracking_request(tracking_number)
40
- response = commit(:track, query)
41
- parse_document_response(response, :bol)
42
- end
82
+ case conf_accessorial
83
+ when Array then accessorials += conf_accessorial
84
+ when String then accessorials << conf_accessorial
85
+ end
86
+ end
87
+ end
43
88
 
44
- # protected
89
+ accessorials += build_calculated_accessorials(shipment)
45
90
 
46
- def build_url(action, query:)
47
- scheme = @conf.dig(:api, :use_ssl, action) ? 'https://' : 'http://'
91
+ accessorials.uniq.compact.each { |accessorial| query[accessorial] = 'Yes' } if accessorials.any?
48
92
 
49
- uri = URI.parse("#{scheme}#{@conf.dig(:api, :domain)}#{@conf.dig(:api, :endpoints, action)}")
50
- uri.query = query.to_query
51
- url = uri.to_s
52
- return url if url.exclude?('@CARRIER_CODE@')
93
+ query
94
+ end
53
95
 
54
- url.sub('@CARRIER_CODE@', @conf.dig(:api, :carrier_code))
55
- end
96
+ def build_tracking_request(tracking_number)
97
+ api_credentials = fetch_credential(:api)
56
98
 
57
- def commit(action, query)
58
- url = build_url(action, query:)
59
- save_request(url)
99
+ { pronum: tracking_number, xmlpass: api_credentials.password, xmluser: api_credentials.username }
100
+ end
60
101
 
61
- HTTPClient.get(url)
62
- end
102
+ def build_url(action, query:)
103
+ scheme = self.class::API.dig(:use_ssl, action) ? 'https://' : 'http://'
63
104
 
64
- def map_response_errors(response, not_found_error: DocumentNotFoundError)
65
- return ResponseError.new('Unknown response') if response.blank?
105
+ uri = URI.parse("#{scheme}#{self.class::API[:domain]}#{self.class::API.dig(:endpoints, action)}")
106
+ uri.query = query.to_query
107
+ url = uri.to_s
108
+ return url if url.exclude?('@CARRIER_CODE@')
66
109
 
67
- webspeed_error = (response.is_a?(String) || response.is_a?(HTTPClient::Response)) &&
68
- response.include?('WebSpeed error')
69
- return ResponseError.new('Temporary error (WebSpeed error)') if webspeed_error
110
+ url.sub('@CARRIER_CODE@', self.class::API[:carrier_code])
111
+ end
70
112
 
71
- return if response.code == 200
113
+ def commit(action, query)
114
+ url = build_url(action, query:)
115
+ save_request(url)
72
116
 
73
- response.code == 400 ? not_found_error.new : ResponseError.new("HTTP #{response.code}")
74
- end
117
+ HTTPClient.get(url, proxy_url:)
118
+ end
75
119
 
76
- # Documents
120
+ def map_response_errors(response, not_found_error: DocumentNotFoundError)
121
+ return ResponseError.new('Unknown response') if response.blank?
77
122
 
78
- def parse_document_response(tracking_response, document_type)
79
- document_response = DocumentResponse.new
80
- document_response.error = map_response_errors(tracking_response)
81
- return document_response if document_response.error.present?
123
+ webspeed_error = (response.is_a?(String) || response.is_a?(HTTPClient::Response)) && response.include?('WebSpeed error')
124
+ return ResponseError.new('Temporary error (WebSpeed error)') if webspeed_error
82
125
 
83
- tracking_response.deep_symbolize_keys!
126
+ return if response.code == 200
84
127
 
85
- image_type_code = case document_type
86
- when :bol then 'B'
87
- when :pod then 'P'
88
- end
128
+ response.code == 400 ? not_found_error.new : ResponseError.new("HTTP #{response.code}")
129
+ end
89
130
 
90
- api_images = tracking_response.dig(:protrace, :images, :image)
91
- api_images = [api_images] if api_images.is_a?(Hash)
131
+ def parse_amount(amount)
132
+ negative = amount.include?('-')
92
133
 
93
- image = api_images&.find { |image| image[:imagetypecode] == image_type_code }
94
- url = image.blank? ? nil : (image[:directurl].presence || image[:imageurl])
134
+ ['$', ',', '-'].each { |char| amount = amount.sub(char, '') }
95
135
 
96
- if url.blank?
97
- document_response.error = DocumentNotFoundError.new
98
- return document_response
99
- end
136
+ return 0 if amount.blank?
100
137
 
101
- begin
102
- response = HTTPClient.get(url)
103
- rescue StandardError => e
104
- document_response.error = e
105
- return document_response
106
- end
138
+ amount = (amount.to_f * 100).to_i
139
+ return amount unless negative
107
140
 
108
- unless response.code == 200
109
- document_response.error = DocumentNotFoundError.new
110
- return document_response
141
+ amount * -1
111
142
  end
112
143
 
113
- document_response.assign_attributes(content_type: response.headers['content-type'], data: response.body)
114
- document_response
115
- end
144
+ def parse_api_city_state(str)
145
+ return if str.blank?
116
146
 
117
- # Tracking
147
+ city = str.split(', ')[0].titleize
148
+ province = str.split(', ')[1].upcase
118
149
 
119
- def build_tracking_request(tracking_number)
120
- api_credentials = fetch_credential(:api)
150
+ if province == '*'
151
+ province = case city
152
+ when 'Albuquerque' then 'NM'
153
+ end
154
+ end
121
155
 
122
- { pronum: tracking_number, xmlpass: api_credentials.password, xmluser: api_credentials.username }
123
- end
156
+ Location.new(city:, province:, country: ActiveUtils::Country.find('USA'))
157
+ end
124
158
 
125
- def parse_api_city_state(str)
126
- return if str.blank?
159
+ def parse_api_city_state_zip(str)
160
+ return if str.blank?
127
161
 
128
- city = str.split(', ')[0].titleize
129
- province = str.split(', ')[1].upcase
162
+ parts = str.split(', ')
130
163
 
131
- if province == '*'
132
- province = case city
133
- when 'Albuquerque' then 'NM'
134
- end
164
+ Location.new(city: parts.first.titleize, province: parts.last.upcase, country: ActiveUtils::Country.find('USA'))
135
165
  end
136
166
 
137
- Location.new(
138
- city:,
139
- province:,
140
- country: ActiveUtils::Country.find('USA'),
141
- )
142
- end
167
+ def parse_api_date(date, location)
168
+ return if date.blank?
143
169
 
144
- def parse_api_city_state_zip(str)
145
- return if str.blank?
170
+ separator = ['?', '-'].find { |separator| date.include?(separator) }
171
+ return if separator.blank?
146
172
 
147
- parts = str.split(', ')
173
+ format = case date
174
+ when /^\d{4}#{separator}/
175
+ ['%Y', '%m', '%d'].join(separator)
176
+ when /^\d{2}#{separator}/
177
+ ['%m', '%d', '%Y'].join(separator)
178
+ end
179
+ return if format.blank?
148
180
 
149
- Location.new(
150
- city: parts.first.titleize,
151
- province: parts.last.upcase,
152
- country: ActiveUtils::Country.find('USA'),
153
- )
154
- end
181
+ local_date = ::Date.strptime(date, format)
182
+ ::FreightKit::DateTime.new(local_date:, location:)
183
+ end
155
184
 
156
- def parse_api_date(date, location)
157
- return if date.blank?
185
+ def parse_api_date_time(date_time, location)
186
+ return if date_time.blank?
158
187
 
159
- separator = ['?', '-'].find { |separator| date.include?(separator) }
160
- return if separator.blank?
188
+ local_date_time = ::Time.strptime(date_time, '%Y-%m-%d %H:%M').to_fs(:db)
189
+ ::FreightKit::DateTime.new(local_date_time:, location:)
190
+ rescue Date::Error
191
+ raise if local_date_time.present?
161
192
 
162
- format = case date
163
- when /^\d{4}#{separator}/
164
- ['%Y', '%m', '%d'].join(separator)
165
- when /^\d{2}#{separator}/
166
- ['%m', '%d', '%Y'].join(separator)
167
- end
168
- return if format.blank?
193
+ parse_api_date(local_date_time, location)
194
+ end
169
195
 
170
- local_date = ::Date.strptime(date, format)
171
- ::FreightKit::DateTime.new(local_date:, location:)
172
- end
196
+ def parse_document_response(tracking_response, document_type)
197
+ document_response = DocumentResponse.new
198
+ document_response.error = map_response_errors(tracking_response)
199
+ return document_response if document_response.error.present?
173
200
 
174
- def parse_api_date_time(date_time, location)
175
- return if date_time.blank?
201
+ tracking_response.deep_symbolize_keys!
176
202
 
177
- local_date_time = ::Time.strptime(date_time, '%Y-%m-%d %H:%M').to_fs(:db)
178
- ::FreightKit::DateTime.new(local_date_time:, location:)
179
- rescue Date::Error
180
- raise if local_date_time.present?
203
+ image_type_code = case document_type
204
+ when :bol then 'B'
205
+ when :pod then 'P'
206
+ end
181
207
 
182
- parse_api_date(local_date_time, location)
183
- end
208
+ api_images = tracking_response.dig(:protrace, :images, :image)
209
+ api_images = [api_images] if api_images.is_a?(Hash)
210
+
211
+ image = api_images&.find { |image| image[:imagetypecode] == image_type_code }
212
+ url = image.blank? ? nil : (image[:directurl].presence || image[:imageurl])
184
213
 
185
- def parse_tracking_response(tracking_number, response:)
186
- tracking_response = TrackingResponse.new(carrier: self, request: last_request, response:)
187
- tracking_response.error = map_response_errors(response, not_found_error: ShipmentNotFoundError)
188
- return tracking_response if tracking_response.error.present?
214
+ if url.blank?
215
+ document_response.error = DocumentNotFoundError.new
216
+ return document_response
217
+ end
189
218
 
190
- response.deep_symbolize_keys!
219
+ begin
220
+ response = HTTPClient.get(url, proxy_url:)
221
+ rescue StandardError => e
222
+ document_response.error = e
223
+ return document_response
224
+ end
191
225
 
192
- api_events = response.dig(:protrace, :shiphists, :shiphist)
193
- if api_events.blank?
194
- tracking_response.error = ResponseError.new('Empty response')
195
- return tracking_response
226
+ unless response.code == 200
227
+ document_response.error = DocumentNotFoundError.new
228
+ return document_response
229
+ end
230
+
231
+ document_response.assign_attributes(content_type: response.headers['content-type'], data: response.body)
232
+ document_response
196
233
  end
197
234
 
198
- origin = Location.new(
199
- address1: response.dig(:protrace, :shipaddr)&.titleize,
200
- address2: response.dig(:protrace, :shipaddr2)&.titleize,
201
- city: response.dig(:protrace, :origcity)&.titleize,
202
- province: response.dig(:protrace, :origstate)&.upcase,
203
- country: ActiveUtils::Country.find('USA'),
204
- )
205
-
206
- destination = Location.new(
207
- address1: response.dig(:protrace, :consaddr)&.titleize,
208
- address2: response.dig(:protrace, :consaddr2)&.titleize,
209
- city: response.dig(:protrace, :destcity)&.titleize,
210
- province: response.dig(:protrace, :deststate)&.upcase,
211
- country: ActiveUtils::Country.find('USA'),
212
- )
213
-
214
- deldateiso = response.dig(:protrace, :deldateiso)
215
- actual_delivery_date = (parse_api_date(deldateiso, destination) if deldateiso.present?)
216
-
217
- estdeliverydateiso = response.dig(:protrace, :estdeliverydateiso)
218
- estdeliverytimestart = response.dig(:protrace, :estdeliverytimestart)
219
- estimated_delivery_date = if estdeliverydateiso.present? && estdeliverytimestart.present?
220
- parse_api_date_time([estdeliverydateiso, estdeliverytimestart].join(' '), destination)
221
- elsif estdeliverydateiso.present?
222
- parse_api_date(estdeliverydateiso, destination)
223
- end
224
-
225
- scheduled_delivery_date = nil
226
- ship_time = nil
227
-
228
- api_events = response.dig(:protrace, :shiphists, :shiphist)
229
- api_events = [api_events] if api_events.is_a?(Hash)
230
-
231
- last_location = origin
232
-
233
- shipment_events = api_events.reverse.map do |api_event|
234
- hist_code = api_event[:histcode]&.downcase
235
- next if hist_code.blank?
236
-
237
- event = conf.dig(:events, :types).key(hist_code)
238
- next if event.blank?
239
-
240
- remarks = api_event[:histremarks]
241
-
242
- location = if remarks.present? && remarks.match?(/, \w{2}/) # ends in state abbreviation
243
- parse_api_city_state(api_event[:histremarks])
244
- end
245
-
246
- location ||= case event
247
- when :delivered then destination
248
- when :departed then last_location
249
- when :picked_up, :pickup_scheduled then origin
250
- end
235
+ def parse_rate_response(shipment:, response:)
236
+ rate_response = RateResponse.new(request: last_request, response:)
251
237
 
252
- date = api_event[:histdate]
253
- time = api_event[:histtime]
254
- # Some api_event[:histtime] returns a string with missing hours and minutes like ' : '
255
- time = nil if (time =~ /\d/).blank?
238
+ rate_response.error = map_response_errors(response)
239
+ return rate_response if rate_response.error.present?
256
240
 
257
- date_time = if [date, time].all?(&:present?)
258
- parse_api_date_time([date, time].compact.join(' '), location)
259
- elsif date.present?
260
- parse_api_date(date, location)
261
- end
241
+ error = response.dig('error', 'errormessage')
262
242
 
263
- last_location = location
243
+ if error.present?
244
+ rate_response.error = InvalidCredentialsError.new if error.downcase.include?('invalid username/password')
264
245
 
265
- ShipmentEvent.new(location:, date_time:, type_code: event)
266
- end
246
+ if error.downcase.include?('is not available') || error.downcase.include?('out of the serviceable area')
247
+ rate_response.error = UnserviceableError.new(error)
248
+ end
267
249
 
268
- shipment_events.compact!
250
+ rate_response.error = ResponseError.new(error) if rate_response.error.blank?
269
251
 
270
- status = shipment_events.last&.type_code
252
+ return rate_response
253
+ end
271
254
 
272
- tracking_response.assign_attributes(
273
- actual_delivery_date:,
274
- destination:,
275
- estimated_delivery_date:,
276
- origin:,
277
- scheduled_delivery_date:,
278
- ship_time:,
279
- shipment_events:,
280
- status:,
281
- tracking_number:,
282
- )
255
+ if response.dig('ratequote', 'quotetotal').blank?
256
+ rate_response.error = ResponseError.new('Cost is blank')
257
+ return rate_response
258
+ end
283
259
 
284
- tracking_response
285
- end
260
+ total_cents = parse_amount(response.dig('ratequote', 'quotetotal'))
286
261
 
287
- # Rates
262
+ transit_days = response.dig('ratequote', 'busdays').to_i
263
+ estimate_reference = response.dig('ratequote', 'quotenumber')
288
264
 
289
- def build_calculated_accessorials(shipment)
290
- []
291
- end
265
+ ratequote_lines = response.dig('ratequote', 'ratequoteline')
266
+ prices = []
292
267
 
293
- def parse_amount(amount)
294
- negative = amount.include?('-')
268
+ ratequote_lines.each do |ratequote_line|
269
+ next if ratequote_line['chrg'].blank?
270
+ next if ratequote_line['chargedesc'] == 'FREIGHT'
295
271
 
296
- ['$', ',', '-'].each do |char|
297
- amount = amount.sub(char, '')
298
- end
272
+ cents = parse_amount(ratequote_line['chrg'])
273
+ next if cents.zero?
299
274
 
300
- return 0 if amount.blank?
275
+ prices << Price.new(blame: :api, cents:, description: ratequote_line_description(ratequote_line))
276
+ end
301
277
 
302
- amount = (amount.to_f * 100).to_i
303
- return amount unless negative
278
+ prices = [Price.new(blame: :api, cents: total_cents - prices.sum(&:cents), description: 'Freight')] + prices
304
279
 
305
- amount * -1
306
- end
280
+ if self.class.overlength_fees_require_tariff?
281
+ cents = 0
307
282
 
308
- def ratequote_line_description(ratequote_line)
309
- description = ratequote_line['chargedesc'] || ''
310
- description = description.capitalize
283
+ shipment.packages.each { |package| cents += overlength_fee(tariff, package) }
311
284
 
312
- code = ratequote_line['chargecode']&.upcase || ''
313
- description = "#{description} (#{code})" if code.present?
285
+ prices << Price.new(blame: :tariff, cents:, description: 'Overlength fees') if cents.nonzero?
286
+ end
314
287
 
315
- description.squish
316
- end
288
+ rate = Rate.new(
289
+ carrier: self,
290
+ carrier_name: name,
291
+ currency: 'USD',
292
+ estimate_reference:,
293
+ scac:,
294
+ service_name: :standard,
295
+ shipment:,
296
+ prices:,
297
+ transit_days:,
298
+ with_excessive_length_fees: rates_with_excessive_length_fees?
299
+ )
317
300
 
318
- def build_rate_request(shipment:)
319
- api_credentials = fetch_credential(:api)
320
-
321
- query = {
322
- xmlv: 'yes', # must be first
323
- quotenumber: 'YES',
324
- vdzip: shipment.destination.postal_code,
325
- vozip: shipment.origin.postal_code,
326
- xmlpass: api_credentials.password,
327
- xmluser: api_credentials.username
328
- }
329
-
330
- i = 0
331
- shipment.packages.each do |package|
332
- i += 1 # API starts at 1 (not 0)
333
-
334
- query["vclass[#{i}]"] = package.freight_class
335
- query["wheight[#{i}]"] = package.height(:in).ceil
336
- query["wlength[#{i}]"] = package.length(:in).ceil
337
- query["wpallets[#{i}]"] = package.quantity
338
- query["wpieces[#{i}]"] = package.quantity
339
- query["wweight[#{i}]"] = package.pounds(:total).ceil
340
- query["wwidth[#{i}]"] = package.width(:in).ceil
301
+ rate_response.rates = [rate]
302
+ rate_response
341
303
  end
342
304
 
343
- accessorials = []
305
+ def parse_tracking_response(tracking_number, response:)
306
+ tracking_response = TrackingResponse.new(carrier: self, request: last_request, response:)
307
+ tracking_response.error = map_response_errors(response, not_found_error: ShipmentNotFoundError)
308
+ return tracking_response if tracking_response.error.present?
344
309
 
345
- if shipment.accessorials.present?
346
- serviceable_accessorials?(shipment.accessorials)
310
+ response.deep_symbolize_keys!
347
311
 
348
- shipment
349
- .accessorials
350
- .reject { |accessorial| conf.dig(:accessorials, :unquotable).include?(accessorial) }
351
- .each do |shipment_accessorial|
352
- conf_accessorial = conf.dig(:accessorials, :mappable, shipment_accessorial)
353
-
354
- case conf_accessorial
355
- when Array then accessorials += conf_accessorial
356
- when String then accessorials << conf_accessorial
357
- end
312
+ api_events = response.dig(:protrace, :shiphists, :shiphist)
313
+ if api_events.blank?
314
+ tracking_response.error = ResponseError.new('Empty response')
315
+ return tracking_response
358
316
  end
359
- end
360
317
 
361
- accessorials += build_calculated_accessorials(shipment)
318
+ origin = Location.new(
319
+ address1: response.dig(:protrace, :shipaddr)&.titleize,
320
+ address2: response.dig(:protrace, :shipaddr2)&.titleize,
321
+ city: response.dig(:protrace, :origcity)&.titleize,
322
+ province: response.dig(:protrace, :origstate)&.upcase,
323
+ country: ActiveUtils::Country.find('USA')
324
+ )
362
325
 
363
- accessorials.uniq.compact.each { |accessorial| query[accessorial] = 'Yes' } if accessorials.any?
326
+ destination = Location.new(
327
+ address1: response.dig(:protrace, :consaddr)&.titleize,
328
+ address2: response.dig(:protrace, :consaddr2)&.titleize,
329
+ city: response.dig(:protrace, :destcity)&.titleize,
330
+ province: response.dig(:protrace, :deststate)&.upcase,
331
+ country: ActiveUtils::Country.find('USA')
332
+ )
364
333
 
365
- query
366
- end
334
+ deldateiso = response.dig(:protrace, :deldateiso)
335
+ actual_delivery_date = (parse_api_date(deldateiso, destination) if deldateiso.present?)
367
336
 
368
- def parse_rate_response(shipment:, response:)
369
- rate_response = RateResponse.new(request: last_request, response:)
337
+ estdeliverydateiso = response.dig(:protrace, :estdeliverydateiso)
338
+ estdeliverytimestart = response.dig(:protrace, :estdeliverytimestart)
339
+ estdelivery = [estdeliverydateiso, estdeliverytimestart].join(' ')
340
+ estimated_delivery_date = if estdeliverydateiso.present? && estdeliverytimestart.present?
341
+ parse_api_date_time(estdelivery, destination)
342
+ elsif estdeliverydateiso.present?
343
+ parse_api_date(estdeliverydateiso, destination)
344
+ end
370
345
 
371
- rate_response.error = map_response_errors(response)
372
- return rate_response if rate_response.error.present?
346
+ scheduled_delivery_date = nil
347
+ ship_time = nil
373
348
 
374
- error = response.dig('error', 'errormessage')
349
+ api_events = response.dig(:protrace, :shiphists, :shiphist)
350
+ api_events = [api_events] if api_events.is_a?(Hash)
375
351
 
376
- if error.present?
377
- rate_response.error = InvalidCredentialsError.new if error.downcase.include?('invalid username/password')
352
+ last_location = origin
378
353
 
379
- if error.downcase.include?('is not available') || error.downcase.include?('out of the serviceable area')
380
- rate_response.error = UnserviceableError.new(error)
381
- end
354
+ shipment_events = api_events.reverse.map do |api_event|
355
+ hist_code = api_event[:histcode]&.downcase
356
+ next if hist_code.blank?
382
357
 
383
- rate_response.error = ResponseError.new(error) if rate_response.error.blank?
358
+ event = self.class::EVENTS[:types].key(hist_code)
359
+ next if event.blank?
384
360
 
385
- return rate_response
386
- end
361
+ remarks = api_event[:histremarks]
387
362
 
388
- if response.dig('ratequote', 'quotetotal').blank?
389
- rate_response.error = ResponseError.new('Cost is blank')
390
- return rate_response
391
- end
363
+ location = if remarks.present? && remarks.match?(/, \w{2}/) # ends in state abbreviation
364
+ parse_api_city_state(api_event[:histremarks])
365
+ end
392
366
 
393
- total_cents = parse_amount(response.dig('ratequote', 'quotetotal'))
367
+ location ||= case event
368
+ when :delivered then destination
369
+ when :departed then last_location
370
+ when :picked_up, :pickup_scheduled then origin
371
+ end
394
372
 
395
- transit_days = response.dig('ratequote', 'busdays').to_i
396
- estimate_reference = response.dig('ratequote', 'quotenumber')
373
+ date = api_event[:histdate]
374
+ time = api_event[:histtime]
375
+ # Some api_event[:histtime] returns a string with missing hours and minutes like ' : '
376
+ time = nil if (time =~ /\d/).blank?
397
377
 
398
- ratequote_lines = response.dig('ratequote', 'ratequoteline')
399
- prices = []
378
+ date_time = if [date, time].all?(&:present?)
379
+ parse_api_date_time([date, time].compact.join(' '), location)
380
+ elsif date.present?
381
+ parse_api_date(date, location)
382
+ end
400
383
 
401
- ratequote_lines.each do |ratequote_line|
402
- next if ratequote_line['chrg'].blank?
403
- next if ratequote_line['chargedesc'] == 'FREIGHT'
384
+ last_location = location
404
385
 
405
- cents = parse_amount(ratequote_line['chrg'])
406
- next if cents.zero?
386
+ ShipmentEvent.new(location:, date_time:, type_code: event)
387
+ end
388
+
389
+ shipment_events.compact!
407
390
 
408
- prices << Price.new(
409
- blame: :api,
410
- cents:,
411
- description: ratequote_line_description(ratequote_line),
391
+ status = shipment_events.last&.type_code
392
+
393
+ tracking_response.assign_attributes(
394
+ actual_delivery_date:,
395
+ destination:,
396
+ estimated_delivery_date:,
397
+ origin:,
398
+ scheduled_delivery_date:,
399
+ ship_time:,
400
+ shipment_events:,
401
+ status:,
402
+ tracking_number:
412
403
  )
404
+
405
+ tracking_response
413
406
  end
414
407
 
415
- prices = [
416
- Price.new(
417
- blame: :api,
418
- cents: total_cents - prices.sum(&:cents),
419
- description: 'Freight',
420
- ),
421
- ] + prices
408
+ def pod(tracking_number)
409
+ query = build_tracking_request(tracking_number)
410
+ response = commit(:track, query)
411
+ parse_document_response(response, :pod)
412
+ end
422
413
 
423
- if self.class.overlength_fees_require_tariff?
424
- cents = 0
414
+ def ratequote_line_description(ratequote_line)
415
+ description = ratequote_line['chargedesc'] || ''
416
+ description = description.capitalize
425
417
 
426
- shipment.packages.each do |package|
427
- cents += overlength_fee(tariff, package)
428
- end
418
+ code = ratequote_line['chargecode']&.upcase || ''
419
+ description = "#{description} (#{code})" if code.present?
429
420
 
430
- prices << Price.new(blame: :tariff, cents:, description: 'Overlength fees') if cents.nonzero?
421
+ description.squish
431
422
  end
432
423
 
433
- rate = Rate.new(
434
- carrier: self,
435
- carrier_name: self.class.name,
436
- currency: 'USD',
437
- estimate_reference:,
438
- scac: self.class.scac.upcase,
439
- service_name: :standard,
440
- shipment:,
441
- prices:,
442
- transit_days:,
443
- with_excessive_length_fees: @conf.dig(:attributes, :rates, :with_excessive_length_fees),
444
- )
445
-
446
- rate_response.rates = [rate]
447
- rate_response
424
+ def scanned_bol(tracking_number)
425
+ query = build_tracking_request(tracking_number)
426
+ response = commit(:track, query)
427
+ parse_document_response(response, :bol)
428
+ end
448
429
  end
449
430
  end
450
431
  end