@fleetbase/fleetops-engine 0.6.51 → 0.6.53

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 (396) hide show
  1. package/README.md +0 -2
  2. package/addon/components/cell/telematic-provider.hbs +16 -0
  3. package/addon/components/cell/telematic-provider.js +36 -0
  4. package/addon/components/cell/telematic-status.hbs +1 -0
  5. package/addon/components/cell/telematic-status.js +39 -0
  6. package/addon/components/device/manager.hbs +5 -6
  7. package/addon/components/device/manager.js +12 -15
  8. package/addon/components/driver/details.hbs +1 -1
  9. package/addon/components/fleet/vehicle-listing.js +1 -0
  10. package/addon/components/fuel-integration/form.hbs +200 -0
  11. package/addon/components/fuel-integration/form.js +242 -0
  12. package/addon/components/fuel-integration/hub.hbs +154 -0
  13. package/addon/components/fuel-integration/hub.js +165 -0
  14. package/addon/components/fuel-integration/matching-priority.hbs +76 -0
  15. package/addon/components/fuel-integration/matching-priority.js +145 -0
  16. package/addon/components/fuel-report/details.hbs +13 -1
  17. package/addon/components/issue/details.hbs +3 -57
  18. package/addon/components/issue/details.js +0 -96
  19. package/addon/components/layout/fleet-ops-sidebar/operations-monitor.hbs +201 -0
  20. package/addon/components/layout/fleet-ops-sidebar/operations-monitor.js +589 -0
  21. package/addon/components/layout/fleet-ops-sidebar.hbs +19 -233
  22. package/addon/components/layout/fleet-ops-sidebar.js +287 -376
  23. package/addon/components/maintenance-schedule/form.js +1 -0
  24. package/addon/components/map/drawer/device-event-listing.hbs +12 -2
  25. package/addon/components/map/drawer/driver-listing.hbs +14 -1
  26. package/addon/components/map/drawer/place-listing.hbs +14 -1
  27. package/addon/components/map/drawer/position-listing.hbs +12 -2
  28. package/addon/components/map/drawer/vehicle-listing.hbs +14 -1
  29. package/addon/components/modals/assigned-orders.hbs +40 -0
  30. package/addon/components/modals/attach-device.hbs +4 -3
  31. package/addon/components/modals/attach-telematic-device.hbs +25 -0
  32. package/addon/components/modals/attach-telematic-device.js +3 -0
  33. package/addon/components/modals/driver-assign-order.hbs +7 -58
  34. package/addon/components/modals/driver-assign-vehicle.hbs +10 -2
  35. package/addon/components/modals/fleet-assign-driver.hbs +18 -0
  36. package/addon/components/modals/fleet-assign-driver.js +3 -0
  37. package/addon/components/modals/fleet-assign-vehicle.hbs +18 -0
  38. package/addon/components/modals/fleet-assign-vehicle.js +3 -0
  39. package/addon/components/modals/telematic-connection-diagnostics.hbs +75 -0
  40. package/addon/components/modals/telematic-connection-diagnostics.js +287 -0
  41. package/addon/components/modals/vehicle-details.hbs +6 -1
  42. package/addon/components/modals/vehicle-form.hbs +8 -1
  43. package/addon/components/order/card.hbs +106 -0
  44. package/addon/components/order/card.js +257 -0
  45. package/addon/components/order/form/payload.js +3 -1
  46. package/addon/components/order/form/service-rate.hbs +2 -0
  47. package/addon/components/service-rate/form.hbs +3 -2
  48. package/addon/components/telematic/details.hbs +98 -87
  49. package/addon/components/telematic/details.js +230 -1
  50. package/addon/components/telematic/form.hbs +303 -72
  51. package/addon/components/telematic/form.js +519 -3
  52. package/addon/components/telematic/hub.hbs +149 -0
  53. package/addon/components/telematic/hub.js +142 -0
  54. package/addon/components/telematic/settings.hbs +152 -0
  55. package/addon/components/telematic/settings.js +45 -0
  56. package/addon/components/telematic/tab-empty-state.hbs +29 -0
  57. package/addon/components/telematic/tab-empty-state.js +33 -0
  58. package/addon/components/vehicle/details.hbs +6 -1
  59. package/addon/components/vehicle/form.hbs +2 -1
  60. package/addon/components/widget/fuel-providers.hbs +43 -0
  61. package/addon/components/widget/fuel-providers.js +27 -0
  62. package/addon/controllers/connectivity/devices/index/details.js +27 -1
  63. package/addon/controllers/connectivity/devices/index.js +18 -1
  64. package/addon/controllers/connectivity/fuel-providers/details/sync.js +18 -0
  65. package/addon/controllers/connectivity/fuel-providers/details/transactions.js +24 -0
  66. package/addon/controllers/connectivity/fuel-providers/details.js +69 -0
  67. package/addon/controllers/connectivity/fuel-providers/edit.js +24 -0
  68. package/addon/controllers/connectivity/fuel-providers/index.js +177 -0
  69. package/addon/controllers/connectivity/fuel-providers/new.js +37 -0
  70. package/addon/controllers/connectivity/telematics/details/attachments.js +247 -0
  71. package/addon/controllers/connectivity/telematics/details/devices.js +368 -0
  72. package/addon/controllers/connectivity/telematics/details/events.js +244 -0
  73. package/addon/controllers/connectivity/telematics/details/index.js +3 -0
  74. package/addon/controllers/connectivity/telematics/details/logs.js +43 -0
  75. package/addon/controllers/connectivity/telematics/details/sensors.js +270 -0
  76. package/addon/controllers/connectivity/telematics/details.js +149 -0
  77. package/addon/controllers/connectivity/telematics/{index/edit.js → edit.js} +15 -4
  78. package/addon/controllers/connectivity/telematics/index.js +25 -5
  79. package/addon/controllers/connectivity/telematics/{index/new.js → new.js} +8 -2
  80. package/addon/controllers/maintenance/index.js +100 -0
  81. package/addon/controllers/management/drivers/index/details.js +78 -1
  82. package/addon/controllers/management/drivers/index.js +39 -0
  83. package/addon/controllers/management/fleets/index/details.js +23 -1
  84. package/addon/controllers/management/fleets/index.js +6 -1
  85. package/addon/controllers/management/fuel-reports/index.js +37 -1
  86. package/addon/controllers/management/fuel-transactions/index/details.js +70 -0
  87. package/addon/controllers/management/fuel-transactions/index.js +240 -0
  88. package/addon/controllers/management/index.js +88 -0
  89. package/addon/controllers/management/vehicles/index/details.js +32 -0
  90. package/addon/controllers/management/vehicles/index.js +26 -0
  91. package/addon/controllers/operations/orders/index.js +4 -0
  92. package/addon/controllers/settings/index.js +142 -0
  93. package/addon/controllers/settings/payments/index.js +5 -0
  94. package/addon/extension.js +31 -0
  95. package/addon/routes/analytics/index.js +3 -0
  96. package/addon/routes/connectivity/devices/index.js +3 -0
  97. package/addon/routes/connectivity/fuel-providers/details/index.js +7 -0
  98. package/addon/routes/connectivity/fuel-providers/details/matching.js +7 -0
  99. package/addon/routes/connectivity/fuel-providers/details/settings.js +7 -0
  100. package/addon/routes/connectivity/fuel-providers/details/sync.js +18 -0
  101. package/addon/routes/connectivity/fuel-providers/details/transactions.js +14 -0
  102. package/addon/routes/connectivity/fuel-providers/details.js +18 -0
  103. package/addon/routes/connectivity/fuel-providers/edit.js +18 -0
  104. package/addon/routes/connectivity/fuel-providers/index.js +20 -0
  105. package/addon/routes/connectivity/fuel-providers/new.js +3 -0
  106. package/addon/routes/connectivity/fuel-providers.js +3 -0
  107. package/addon/routes/connectivity/telematics/details/attachments.js +28 -0
  108. package/addon/routes/connectivity/telematics/details/devices.js +30 -0
  109. package/addon/routes/connectivity/telematics/details/events.js +33 -0
  110. package/addon/routes/connectivity/telematics/details/index.js +3 -0
  111. package/addon/routes/connectivity/telematics/details/logs.js +17 -0
  112. package/addon/routes/connectivity/telematics/details/sensors.js +29 -0
  113. package/addon/routes/connectivity/telematics/{index/details.js → details.js} +1 -1
  114. package/addon/routes/connectivity/telematics/{index/edit.js → edit.js} +1 -1
  115. package/addon/routes/connectivity/telematics/new.js +3 -0
  116. package/addon/routes/maintenance/index.js +3 -0
  117. package/addon/routes/maintenance/work-orders/index.js +2 -0
  118. package/addon/routes/management/drivers/index/details/activity.js +3 -0
  119. package/addon/routes/management/fuel-reports/index.js +2 -0
  120. package/addon/routes/management/fuel-transactions/index/details.js +18 -0
  121. package/addon/routes/management/fuel-transactions/index.js +21 -0
  122. package/addon/routes/management/fuel-transactions.js +3 -0
  123. package/addon/routes/settings/index.js +3 -0
  124. package/addon/routes.js +28 -9
  125. package/addon/services/device-actions.js +64 -1
  126. package/addon/services/device-event-actions.js +12 -1
  127. package/addon/services/driver-actions.js +147 -11
  128. package/addon/services/fleet-actions.js +66 -1
  129. package/addon/services/fuel-integration-actions.js +31 -0
  130. package/addon/services/issue-actions.js +1 -1
  131. package/addon/services/telematic-actions.js +9 -3
  132. package/addon/services/vehicle-actions.js +125 -0
  133. package/addon/styles/fleetops-engine.css +2237 -162
  134. package/addon/templates/analytics/index.hbs +14 -0
  135. package/addon/templates/analytics/reports/index.hbs +19 -1
  136. package/addon/templates/connectivity/devices/index.hbs +19 -1
  137. package/addon/templates/connectivity/events/index.hbs +16 -1
  138. package/addon/templates/connectivity/fuel-providers/details/index.hbs +38 -0
  139. package/addon/templates/connectivity/fuel-providers/details/matching.hbs +13 -0
  140. package/addon/templates/connectivity/fuel-providers/details/settings.hbs +11 -0
  141. package/addon/templates/connectivity/fuel-providers/details/sync.hbs +48 -0
  142. package/addon/templates/connectivity/fuel-providers/details/transactions.hbs +21 -0
  143. package/addon/templates/connectivity/fuel-providers/details.hbs +51 -0
  144. package/addon/templates/connectivity/fuel-providers/edit.hbs +24 -0
  145. package/addon/templates/connectivity/fuel-providers/index.hbs +12 -0
  146. package/addon/templates/connectivity/fuel-providers/new.hbs +22 -0
  147. package/addon/templates/connectivity/fuel-providers.hbs +1 -0
  148. package/addon/templates/connectivity/sensors/index.hbs +19 -1
  149. package/addon/templates/connectivity/telematics/details/attachments.hbs +163 -0
  150. package/addon/templates/connectivity/telematics/details/devices.hbs +88 -0
  151. package/addon/templates/connectivity/telematics/details/events.hbs +101 -0
  152. package/addon/templates/connectivity/telematics/{index/details → details}/index.hbs +0 -1
  153. package/addon/templates/connectivity/telematics/details/logs.hbs +69 -0
  154. package/addon/templates/connectivity/telematics/details/sensors.hbs +88 -0
  155. package/addon/templates/connectivity/telematics/details.hbs +53 -0
  156. package/addon/templates/connectivity/telematics/edit.hbs +27 -0
  157. package/addon/templates/connectivity/telematics/index.hbs +6 -8
  158. package/addon/templates/connectivity/telematics/new.hbs +24 -0
  159. package/addon/templates/maintenance/equipment/index.hbs +19 -1
  160. package/addon/templates/maintenance/index.hbs +113 -0
  161. package/addon/templates/maintenance/maintenances/index.hbs +15 -1
  162. package/addon/templates/maintenance/parts/index.hbs +19 -1
  163. package/addon/templates/maintenance/schedules/index.hbs +19 -1
  164. package/addon/templates/maintenance/work-orders/index.hbs +19 -1
  165. package/addon/templates/management/contacts/customers.hbs +15 -1
  166. package/addon/templates/management/contacts/index.hbs +19 -1
  167. package/addon/templates/management/drivers/index/details/activity.hbs +1 -0
  168. package/addon/templates/management/drivers/index.hbs +19 -1
  169. package/addon/templates/management/fleets/index.hbs +19 -1
  170. package/addon/templates/management/fuel-reports/index.hbs +19 -1
  171. package/addon/templates/management/fuel-transactions/index/details.hbs +73 -0
  172. package/addon/templates/management/fuel-transactions/index.hbs +58 -0
  173. package/addon/templates/management/fuel-transactions.hbs +1 -0
  174. package/addon/templates/management/index.hbs +109 -73
  175. package/addon/templates/management/issues/index.hbs +19 -1
  176. package/addon/templates/management/places/index.hbs +19 -1
  177. package/addon/templates/management/vehicles/index.hbs +19 -1
  178. package/addon/templates/management/vendors/index.hbs +19 -1
  179. package/addon/templates/management/vendors/integrated.hbs +13 -1
  180. package/addon/templates/operations/orders/index.hbs +19 -1
  181. package/addon/templates/operations/service-rates/index.hbs +19 -1
  182. package/addon/templates/settings/index.hbs +83 -0
  183. package/addon/templates/settings/payments/index.hbs +15 -1
  184. package/addon/templates/settings/routing.hbs +2 -2
  185. package/addon/utils/fleet-ops-options.js +3 -0
  186. package/app/components/cell/telematic-provider.js +1 -0
  187. package/app/components/cell/telematic-status.js +1 -0
  188. package/app/components/fuel-integration/form.js +1 -0
  189. package/app/components/fuel-integration/hub.js +1 -0
  190. package/app/components/fuel-integration/matching-priority.js +1 -0
  191. package/app/components/modals/assigned-orders.js +1 -0
  192. package/app/components/modals/attach-telematic-device.js +1 -0
  193. package/app/components/modals/fleet-assign-driver.js +1 -0
  194. package/app/components/modals/fleet-assign-vehicle.js +1 -0
  195. package/app/components/modals/telematic-connection-diagnostics.js +1 -0
  196. package/app/components/order/card.js +1 -0
  197. package/app/components/telematic/hub.js +1 -0
  198. package/app/components/telematic/settings.js +1 -0
  199. package/app/components/telematic/tab-empty-state.js +1 -0
  200. package/app/components/widget/fuel-providers.js +1 -0
  201. package/app/controllers/connectivity/fuel-providers/details/sync.js +1 -0
  202. package/app/controllers/connectivity/{telematics/index/details/devices.js → fuel-providers/details/transactions.js} +1 -1
  203. package/app/controllers/connectivity/{telematics/index → fuel-providers}/details.js +1 -1
  204. package/app/controllers/connectivity/{telematics/index → fuel-providers}/edit.js +1 -1
  205. package/app/controllers/connectivity/{telematics/index/details → fuel-providers}/index.js +1 -1
  206. package/app/controllers/connectivity/{telematics/index → fuel-providers}/new.js +1 -1
  207. package/app/controllers/connectivity/telematics/details/attachments.js +1 -0
  208. package/app/controllers/connectivity/telematics/details/devices.js +1 -0
  209. package/app/controllers/connectivity/telematics/details/events.js +1 -0
  210. package/app/controllers/connectivity/telematics/details/index.js +1 -0
  211. package/app/controllers/connectivity/telematics/details/logs.js +1 -0
  212. package/app/controllers/connectivity/telematics/details/sensors.js +1 -0
  213. package/app/controllers/connectivity/telematics/details.js +1 -0
  214. package/app/controllers/connectivity/telematics/edit.js +1 -0
  215. package/app/controllers/connectivity/telematics/new.js +1 -0
  216. package/app/controllers/maintenance/index.js +1 -0
  217. package/app/controllers/management/fuel-transactions/index/details.js +1 -0
  218. package/app/controllers/management/fuel-transactions/index.js +1 -0
  219. package/app/controllers/management/index.js +1 -0
  220. package/app/controllers/settings/index.js +1 -0
  221. package/app/routes/analytics/index.js +1 -0
  222. package/app/routes/connectivity/{telematics/index → fuel-providers}/details/index.js +1 -1
  223. package/app/routes/connectivity/{telematics/index/details/events.js → fuel-providers/details/matching.js} +1 -1
  224. package/app/routes/connectivity/fuel-providers/details/settings.js +1 -0
  225. package/app/routes/connectivity/fuel-providers/details/sync.js +1 -0
  226. package/app/routes/connectivity/{telematics/index/details/devices.js → fuel-providers/details/transactions.js} +1 -1
  227. package/app/routes/connectivity/{telematics/index → fuel-providers}/details.js +1 -1
  228. package/app/routes/connectivity/{telematics/index → fuel-providers}/edit.js +1 -1
  229. package/app/routes/connectivity/fuel-providers/index.js +1 -0
  230. package/app/routes/connectivity/{telematics/index → fuel-providers}/new.js +1 -1
  231. package/app/routes/connectivity/fuel-providers.js +1 -0
  232. package/app/routes/connectivity/telematics/details/attachments.js +1 -0
  233. package/app/routes/connectivity/telematics/details/devices.js +1 -0
  234. package/app/routes/connectivity/telematics/details/events.js +1 -0
  235. package/app/routes/connectivity/telematics/details/index.js +1 -0
  236. package/app/routes/connectivity/telematics/details/logs.js +1 -0
  237. package/app/routes/connectivity/telematics/details/sensors.js +1 -0
  238. package/app/routes/connectivity/telematics/details.js +1 -0
  239. package/app/routes/connectivity/telematics/edit.js +1 -0
  240. package/app/routes/connectivity/telematics/new.js +1 -0
  241. package/app/routes/maintenance/index.js +1 -0
  242. package/app/routes/management/drivers/index/details/activity.js +1 -0
  243. package/app/routes/management/fuel-transactions/index/details.js +1 -0
  244. package/app/routes/management/fuel-transactions/index.js +1 -0
  245. package/app/routes/management/fuel-transactions.js +1 -0
  246. package/app/routes/settings/index.js +1 -0
  247. package/app/services/fuel-integration-actions.js +1 -0
  248. package/app/templates/analytics/index.js +1 -0
  249. package/app/templates/connectivity/{telematics/index → fuel-providers}/details/index.js +1 -1
  250. package/app/templates/connectivity/{telematics/index/details/events.js → fuel-providers/details/matching.js} +1 -1
  251. package/app/templates/connectivity/fuel-providers/details/settings.js +1 -0
  252. package/app/templates/connectivity/fuel-providers/details/sync.js +1 -0
  253. package/app/templates/connectivity/{telematics/index/details/devices.js → fuel-providers/details/transactions.js} +1 -1
  254. package/app/templates/connectivity/{telematics/index → fuel-providers}/details.js +1 -1
  255. package/app/templates/connectivity/{telematics/index → fuel-providers}/edit.js +1 -1
  256. package/app/templates/connectivity/fuel-providers/index.js +1 -0
  257. package/app/templates/connectivity/{telematics/index → fuel-providers}/new.js +1 -1
  258. package/app/templates/connectivity/fuel-providers.js +1 -0
  259. package/app/templates/connectivity/telematics/details/attachments.js +1 -0
  260. package/app/templates/connectivity/telematics/details/devices.js +1 -0
  261. package/app/templates/connectivity/telematics/details/events.js +1 -0
  262. package/app/templates/connectivity/telematics/details/index.js +1 -0
  263. package/app/templates/connectivity/telematics/details/logs.js +1 -0
  264. package/app/templates/connectivity/telematics/details/sensors.js +1 -0
  265. package/app/templates/connectivity/telematics/details.js +1 -0
  266. package/app/templates/connectivity/telematics/edit.js +1 -0
  267. package/app/templates/connectivity/telematics/new.js +1 -0
  268. package/app/templates/maintenance/index.js +1 -0
  269. package/app/templates/management/drivers/index/details/activity.js +1 -0
  270. package/app/templates/management/fuel-transactions/index/details.js +1 -0
  271. package/app/templates/management/fuel-transactions/index.js +1 -0
  272. package/app/templates/management/fuel-transactions.js +1 -0
  273. package/app/templates/management/index.js +1 -0
  274. package/app/templates/settings/index.js +1 -0
  275. package/assets/images/telematics/providers/afaqy.webp +0 -0
  276. package/assets/images/telematics/providers/default.webp +0 -0
  277. package/assets/images/telematics/providers/flespi.webp +0 -0
  278. package/assets/images/telematics/providers/geotab.webp +0 -0
  279. package/assets/images/telematics/providers/safee.webp +0 -0
  280. package/assets/images/telematics/providers/samsara.webp +0 -0
  281. package/composer.json +1 -1
  282. package/extension.json +1 -1
  283. package/package.json +4 -4
  284. package/server/config/fuel-providers.php +42 -0
  285. package/server/config/telematics.php +139 -130
  286. package/server/migrations/2026_06_06_000001_harden_device_events_telematics_contract.php +59 -0
  287. package/server/migrations/2026_06_08_000001_create_fuel_provider_connections_table.php +37 -0
  288. package/server/migrations/2026_06_08_000002_create_fuel_provider_transactions_table.php +53 -0
  289. package/server/migrations/2026_06_08_000003_create_fuel_provider_sync_runs_table.php +40 -0
  290. package/server/migrations/2026_06_10_000001_add_fuel_card_number_to_vehicles_table.php +25 -0
  291. package/server/migrations/2026_06_10_000002_add_vehicle_identity_columns_to_fuel_provider_transactions_table.php +39 -0
  292. package/server/migrations/2026_06_15_000001_normalize_device_attachable_vehicle_morph_types.php +30 -0
  293. package/server/src/Console/Commands/FixInvalidPolymorphicRelationTypeNamespaces.php +1 -0
  294. package/server/src/Contracts/FuelProvider.php +30 -0
  295. package/server/src/Contracts/TelematicProviderDescriptor.php +3 -1
  296. package/server/src/Events/FuelProviderTransactionImported.php +17 -0
  297. package/server/src/Events/FuelProviderTransactionMatched.php +17 -0
  298. package/server/src/Events/FuelProviderTransactionUnmatched.php +17 -0
  299. package/server/src/Events/FuelReportCreatedFromProvider.php +18 -0
  300. package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +6 -0
  301. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +140 -2
  302. package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +44 -0
  303. package/server/src/Http/Controllers/Internal/v1/DriverController.php +158 -7
  304. package/server/src/Http/Controllers/Internal/v1/FuelProviderConnectionController.php +124 -0
  305. package/server/src/Http/Controllers/Internal/v1/FuelProviderSyncRunController.php +10 -0
  306. package/server/src/Http/Controllers/Internal/v1/FuelProviderTransactionController.php +76 -0
  307. package/server/src/Http/Controllers/Internal/v1/HubController.php +300 -0
  308. package/server/src/Http/Controllers/Internal/v1/SearchController.php +506 -0
  309. package/server/src/Http/Controllers/Internal/v1/SensorController.php +1 -1
  310. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +179 -16
  311. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +249 -0
  312. package/server/src/Http/Controllers/TelematicWebhookController.php +103 -19
  313. package/server/src/Http/Filter/DeviceFilter.php +74 -0
  314. package/server/src/Http/Filter/DriverFilter.php +6 -0
  315. package/server/src/Http/Filter/FuelProviderConnectionFilter.php +33 -0
  316. package/server/src/Http/Filter/FuelProviderSyncRunFilter.php +28 -0
  317. package/server/src/Http/Filter/FuelProviderTransactionFilter.php +59 -0
  318. package/server/src/Http/Filter/FuelReportFilter.php +10 -0
  319. package/server/src/Http/Filter/VehicleFilter.php +6 -0
  320. package/server/src/Http/Resources/v1/Driver.php +15 -0
  321. package/server/src/Http/Resources/v1/FuelProviderConnection.php +30 -0
  322. package/server/src/Http/Resources/v1/FuelProviderSyncRun.php +36 -0
  323. package/server/src/Http/Resources/v1/FuelProviderTransaction.php +54 -0
  324. package/server/src/Http/Resources/v1/FuelReport.php +28 -22
  325. package/server/src/Http/Resources/v1/Index/Driver.php +26 -20
  326. package/server/src/Http/Resources/v1/Index/Vehicle.php +29 -22
  327. package/server/src/Http/Resources/v1/Vehicle.php +17 -0
  328. package/server/src/Http/Resources/v1/VehicleWithoutDriver.php +2 -0
  329. package/server/src/Jobs/SyncFuelProviderTransactionsJob.php +57 -0
  330. package/server/src/Jobs/SyncTelematicDevicesJob.php +105 -17
  331. package/server/src/Jobs/TestTelematicConnectionJob.php +9 -22
  332. package/server/src/Models/Asset.php +2 -1
  333. package/server/src/Models/Contact.php +2 -1
  334. package/server/src/Models/Device.php +27 -4
  335. package/server/src/Models/DeviceEvent.php +40 -3
  336. package/server/src/Models/Driver.php +3 -2
  337. package/server/src/Models/Equipment.php +2 -1
  338. package/server/src/Models/Fleet.php +2 -1
  339. package/server/src/Models/FuelProviderConnection.php +60 -0
  340. package/server/src/Models/FuelProviderSyncRun.php +58 -0
  341. package/server/src/Models/FuelProviderTransaction.php +119 -0
  342. package/server/src/Models/FuelReport.php +16 -1
  343. package/server/src/Models/Part.php +2 -1
  344. package/server/src/Models/Sensor.php +21 -4
  345. package/server/src/Models/ServiceRate.php +39 -2
  346. package/server/src/Models/Vehicle.php +14 -2
  347. package/server/src/Models/Vendor.php +2 -1
  348. package/server/src/Providers/FleetOpsServiceProvider.php +20 -0
  349. package/server/src/Support/Analytics/FuelProviderSummary.php +42 -0
  350. package/server/src/Support/FuelProviders/FuelProviderDescriptor.php +55 -0
  351. package/server/src/Support/FuelProviders/FuelProviderRegistry.php +75 -0
  352. package/server/src/Support/FuelProviders/FuelProviderService.php +447 -0
  353. package/server/src/Support/FuelProviders/Providers/AbstractFuelProvider.php +98 -0
  354. package/server/src/Support/FuelProviders/Providers/PetroAppFuelProvider.php +167 -0
  355. package/server/src/Support/Telematics/Providers/AbstractProvider.php +27 -6
  356. package/server/src/Support/Telematics/Providers/AfaqyProvider.php +105 -38
  357. package/server/src/Support/Telematics/Providers/FlespiProvider.php +1 -0
  358. package/server/src/Support/Telematics/Providers/GeotabProvider.php +1 -0
  359. package/server/src/Support/Telematics/Providers/SafeeProvider.php +32 -22
  360. package/server/src/Support/Telematics/Providers/SamsaraProvider.php +3 -1
  361. package/server/src/Support/Telematics/TelematicService.php +340 -21
  362. package/server/src/routes.php +50 -4
  363. package/server/tests/FuelProviderRegistryTest.php +416 -0
  364. package/server/tests/HubRoutesTest.php +62 -0
  365. package/server/tests/ServiceRateAlgorithmWeightVariablesTest.php +44 -0
  366. package/server/tests/TelematicsHardeningTest.php +359 -0
  367. package/server/tests/VehicleDriverAssignmentTest.php +51 -0
  368. package/translations/ar-ae.yml +23 -1
  369. package/translations/bg-bg.yaml +72 -0
  370. package/translations/en-us.yaml +83 -0
  371. package/translations/es-pa.yaml +72 -0
  372. package/translations/fr-fr.yaml +72 -0
  373. package/translations/mn-mn.yaml +72 -0
  374. package/translations/pt-br.yaml +72 -0
  375. package/translations/ru-ru.yaml +72 -0
  376. package/translations/vi-vn.yaml +98 -1
  377. package/addon/controllers/connectivity/telematics/index/details/devices.js +0 -3
  378. package/addon/controllers/connectivity/telematics/index/details/events.js +0 -3
  379. package/addon/controllers/connectivity/telematics/index/details/index.js +0 -3
  380. package/addon/controllers/connectivity/telematics/index/details/sensors.js +0 -3
  381. package/addon/controllers/connectivity/telematics/index/details.js +0 -24
  382. package/addon/routes/connectivity/telematics/index/details/devices.js +0 -3
  383. package/addon/routes/connectivity/telematics/index/details/events.js +0 -3
  384. package/addon/routes/connectivity/telematics/index/details/index.js +0 -3
  385. package/addon/routes/connectivity/telematics/index/details/sensors.js +0 -3
  386. package/addon/routes/connectivity/telematics/index/new.js +0 -3
  387. package/addon/templates/connectivity/telematics/index/details/devices.hbs +0 -1
  388. package/addon/templates/connectivity/telematics/index/details/events.hbs +0 -1
  389. package/addon/templates/connectivity/telematics/index/details/sensors.hbs +0 -1
  390. package/addon/templates/connectivity/telematics/index/details.hbs +0 -14
  391. package/addon/templates/connectivity/telematics/index/edit.hbs +0 -12
  392. package/addon/templates/connectivity/telematics/index/new.hbs +0 -12
  393. package/app/controllers/connectivity/telematics/index/details/events.js +0 -1
  394. package/app/controllers/connectivity/telematics/index/details/sensors.js +0 -1
  395. package/app/routes/connectivity/telematics/index/details/sensors.js +0 -1
  396. package/app/templates/connectivity/telematics/index/details/sensors.js +0 -1
@@ -0,0 +1,359 @@
1
+ <?php
2
+
3
+ use Fleetbase\FleetOps\Contracts\TelematicProviderDescriptor;
4
+
5
+ test('device event model and migration expose lifecycle fields used by telematics workflows', function () {
6
+ $model = file_get_contents(__DIR__ . '/../src/Models/DeviceEvent.php');
7
+ $migration = file_get_contents(__DIR__ . '/../migrations/2026_06_06_000001_harden_device_events_telematics_contract.php');
8
+
9
+ expect($migration)
10
+ ->toContain("'message'")
11
+ ->toContain("'occurred_at'")
12
+ ->toContain("'processed_at'")
13
+ ->toContain("'data'");
14
+
15
+ expect($model)
16
+ ->toContain("'message'")
17
+ ->toContain("'occurred_at'")
18
+ ->toContain("'processed_at'")
19
+ ->toContain("'data'")
20
+ ->toContain("'occurred_at' => 'datetime'")
21
+ ->toContain("'processed_at' => 'datetime'");
22
+ });
23
+
24
+ test('webhook controller resolves integrations explicitly and does not select the first provider record', function () {
25
+ $controller = file_get_contents(__DIR__ . '/../src/Http/Controllers/TelematicWebhookController.php');
26
+
27
+ expect($controller)
28
+ ->toContain('$this->service->resolveWebhookTelematic($providerKey')
29
+ ->toContain("'Unable to resolve telematic integration'")
30
+ ->toContain('$result[\'devices\'] ?? []')
31
+ ->toContain('$result[\'events\'] ?? []')
32
+ ->toContain('$result[\'sensors\'] ?? []')
33
+ ->toContain('Ambiguous telematic integration')
34
+ ->toContain('validateWebhookSignature($request->getContent(), $signature')
35
+ ->not->toContain("Telematic::where('provider', \$providerKey)\n ->when")
36
+ ->not->toContain("->first();\n\n if (!\$telematic)");
37
+ });
38
+
39
+ test('telematics service requires provider identity and stores idempotent event keys', function () {
40
+ $service = file_get_contents(__DIR__ . '/../src/Support/Telematics/TelematicService.php');
41
+
42
+ expect($service)
43
+ ->toContain('Provider device identity is required to link a telematics device.')
44
+ ->toContain('DeviceEvent::firstOrNew([\'_key\' => $eventKey])')
45
+ ->toContain('protected function makeEventKey')
46
+ ->toContain('$telematic->public_id ?? $telematic->uuid')
47
+ ->toContain('resolveWebhookTelematic')
48
+ ->toContain('whereHas(\'device\'')
49
+ ->toContain('meta->provider_account_id');
50
+ });
51
+
52
+ test('native providers normalize device payloads to canonical FleetOps keys', function () {
53
+ $afaqy = file_get_contents(__DIR__ . '/../src/Support/Telematics/Providers/AfaqyProvider.php');
54
+ $safee = file_get_contents(__DIR__ . '/../src/Support/Telematics/Providers/SafeeProvider.php');
55
+
56
+ foreach ([$afaqy, $safee] as $provider) {
57
+ expect($provider)
58
+ ->toContain("'device_id'")
59
+ ->toContain("'name'")
60
+ ->toContain("'provider'")
61
+ ->toContain("'model'")
62
+ ->toContain("'online'")
63
+ ->toContain("'last_seen_at'");
64
+ }
65
+ });
66
+
67
+ test('telematics details use public id for consumer webhook URLs and do not read ember uuid', function () {
68
+ $component = file_get_contents(__DIR__ . '/../../addon/components/telematic/details.js');
69
+ $template = file_get_contents(__DIR__ . '/../../addon/components/telematic/details.hbs');
70
+
71
+ expect($component)
72
+ ->toContain('const id = this.args.resource?.public_id;')
73
+ ->toContain('return null;')
74
+ ->not->toContain('this.args.resource?.uuid')
75
+ ->not->toContain('this.args.resource?.public_id ?? this.args.resource?.id');
76
+
77
+ expect($template)
78
+ ->toContain('this.hasWebhookUrl')
79
+ ->toContain('Webhook URL unavailable until this integration has a public ID.')
80
+ ->toContain('last_sync_job_id')
81
+ ->toContain('last_sync_error');
82
+ });
83
+
84
+ test('native telematics providers expose local provider icons with a descriptor fallback', function () {
85
+ $config = include __DIR__ . '/../config/telematics.php';
86
+ $iconPath = '/engines-dist/images/telematics/providers/';
87
+ $providers = array_filter($config['providers'], fn ($provider) => ($provider['type'] ?? 'native') === 'native');
88
+
89
+ foreach ($providers as $provider) {
90
+ $icon = $provider['icon'] ?? null;
91
+
92
+ expect($icon)
93
+ ->not->toBeNull()
94
+ ->not->toContain('http://')
95
+ ->not->toContain('https://');
96
+
97
+ expect(str_starts_with($icon, $iconPath))->toBeTrue();
98
+ expect(str_ends_with($icon, '.webp'))->toBeTrue();
99
+ expect(file_exists(__DIR__ . '/../../assets/images/telematics/providers/' . basename($icon)))->toBeTrue();
100
+ }
101
+
102
+ $safee = collect($providers)->firstWhere('key', 'safee');
103
+
104
+ expect($safee['icon'])->toBe($iconPath . 'safee.webp');
105
+
106
+ $descriptor = new TelematicProviderDescriptor([
107
+ 'key' => 'custom',
108
+ 'label' => 'Custom',
109
+ ]);
110
+
111
+ expect($descriptor->icon)->toBe(TelematicProviderDescriptor::DEFAULT_ICON);
112
+ expect(file_exists(__DIR__ . '/../../assets/images/telematics/providers/default.webp'))->toBeTrue();
113
+ });
114
+
115
+ test('device event mark processed action is routed and company scoped', function () {
116
+ $routes = file_get_contents(__DIR__ . '/../src/routes.php');
117
+ $controller = file_get_contents(__DIR__ . '/../src/Http/Controllers/Internal/v1/DeviceEventController.php');
118
+
119
+ expect($routes)
120
+ ->toContain("\$router->fleetbaseRoutes('device-events', function")
121
+ ->toContain("'{id}/mark-processed'")
122
+ ->toContain("\$controller('markProcessed')");
123
+
124
+ expect($controller)
125
+ ->toContain('public function markProcessed(string $id): JsonResponse')
126
+ ->toContain("where('company_uuid', session('company'))")
127
+ ->toContain("where('uuid', \$id)->orWhere('public_id', \$id)")
128
+ ->toContain('$deviceEvent->markAsProcessed()')
129
+ ->toContain("'Event was already processed.'");
130
+ });
131
+
132
+ test('afaqy sync stores compact device diagnostics and paginates complete units lists', function () {
133
+ $provider = file_get_contents(__DIR__ . '/../src/Support/Telematics/Providers/AfaqyProvider.php');
134
+
135
+ expect($provider)
136
+ ->toContain('compactLastUpdate')
137
+ ->toContain("'provider_unit_id'")
138
+ ->toContain("'plate_number'")
139
+ ->toContain("'capabilities'")
140
+ ->not->toContain("'raw' => \$payload")
141
+ ->not->toContain("'sensors',")
142
+ ->toContain("'Authorization' => 'Bearer ' . \$token")
143
+ ->toContain('?? 500), 500')
144
+ ->toContain('if (is_array($filters) && empty($filters))')
145
+ ->toContain('$filters = new \stdClass();')
146
+ ->toContain("'limit' => \$limit")
147
+ ->toContain("'offset' => \$offset")
148
+ ->not->toContain("\n 'limit' => \$limit,\n 'offset' => \$offset,")
149
+ ->toContain("\$this->afaqyPost('/units/lists', [")
150
+ ->toContain('], true);')
151
+ ->toContain("http_build_query(['token' => \$this->credentials['token']])")
152
+ ->toContain("\$body = \$tokenInQuery ? \$payload : array_merge(['token' => \$this->credentials['token']], \$payload)")
153
+ ->toContain('$pageOffset = (int) ($pagination[\'offset\'] ?? $offset);')
154
+ ->toContain('$pageLimit = (int) ($pagination[\'limit\'] ?? $limit);')
155
+ ->toContain('$advanceBy = $resultCount > 0 ? $resultCount : max($pageLimit, count($devices), 1);')
156
+ ->toContain('$nextCursor = ($pageOffset + $advanceBy) < $total ? $pageOffset + $advanceBy : null;')
157
+ ->toContain("'requested_limit'")
158
+ ->toContain("'provider_limit'")
159
+ ->toContain("'provider_result_count'")
160
+ ->toContain("'pagination' => [")
161
+ ->toContain("'allCount'")
162
+ ->toContain("'filtersCount'")
163
+ ->toContain("'resultCount'");
164
+ });
165
+
166
+ test('telematics device sync records provider pagination and skipped device counts', function () {
167
+ $job = file_get_contents(__DIR__ . '/../src/Jobs/SyncTelematicDevicesJob.php');
168
+ $controller = file_get_contents(__DIR__ . '/../src/Http/Controllers/Internal/v1/TelematicController.php');
169
+
170
+ expect($controller)
171
+ ->toContain("'limit' => \$request->input('limit')")
172
+ ->not->toContain("'limit' => \$request->input('limit', 100)");
173
+
174
+ expect($job)
175
+ ->toContain("'limit' => \$this->options['limit'] ?? null")
176
+ ->toContain('$totalFetched')
177
+ ->toContain('$totalLinked')
178
+ ->toContain('$totalSkipped')
179
+ ->toContain('$pageCount')
180
+ ->toContain('Device discovery page fetched')
181
+ ->toContain("'provider_unit_id'")
182
+ ->toContain("'last_sync_fetched_total'")
183
+ ->toContain("'last_sync_linked_total'")
184
+ ->toContain("'last_sync_skipped_total'")
185
+ ->toContain("'last_sync_page_count'")
186
+ ->toContain("'last_sync_provider_total'")
187
+ ->toContain("'last_sync_provider_all_count'")
188
+ ->toContain("'last_sync_provider_filters_count'");
189
+ });
190
+
191
+ test('native endpoint fields are advanced optional overrides with provider defaults', function () {
192
+ $config = include __DIR__ . '/../config/telematics.php';
193
+
194
+ $afaqy = collect($config['providers'])->firstWhere('key', 'afaqy');
195
+ $safee = collect($config['providers'])->firstWhere('key', 'safee');
196
+
197
+ $afaqyBaseUrl = collect($afaqy['required_fields'])->firstWhere('name', 'base_url');
198
+ $safeeServerUri = collect($safee['required_fields'])->firstWhere('name', 'server_uri');
199
+
200
+ foreach ([$afaqyBaseUrl, $safeeServerUri] as $field) {
201
+ expect($field['required'])->toBeFalse();
202
+ expect($field['advanced'])->toBeTrue();
203
+ expect($field['is_endpoint'])->toBeTrue();
204
+ expect($field['validation'])->toBe('nullable|url');
205
+ expect($field['default_value'])->not->toBeEmpty();
206
+ }
207
+ });
208
+
209
+ test('telematics activity logging excludes large json and spatial payloads', function () {
210
+ $device = telematics_activity_log_method(file_get_contents(__DIR__ . '/../src/Models/Device.php'));
211
+ $sensor = telematics_activity_log_method(file_get_contents(__DIR__ . '/../src/Models/Sensor.php'));
212
+ $deviceEvent = telematics_activity_log_method(file_get_contents(__DIR__ . '/../src/Models/DeviceEvent.php'));
213
+
214
+ foreach ([$device, $sensor, $deviceEvent] as $model) {
215
+ expect($model)
216
+ ->toContain('->logOnly([')
217
+ ->toContain('->logOnlyDirty()')
218
+ ->not->toContain('return LogOptions::defaults()->logAll();');
219
+ }
220
+
221
+ expect($device)
222
+ ->not->toContain("'meta'")
223
+ ->not->toContain("'data'")
224
+ ->not->toContain("'last_position',");
225
+
226
+ expect($sensor)
227
+ ->not->toContain("'meta',")
228
+ ->not->toContain("'last_position',");
229
+
230
+ expect($deviceEvent)
231
+ ->not->toContain("'payload',")
232
+ ->not->toContain("'data',")
233
+ ->not->toContain("'location',");
234
+ });
235
+
236
+ test('telematics setup renders endpoint overrides only in advanced settings', function () {
237
+ $formTemplate = file_get_contents(__DIR__ . '/../../addon/components/telematic/form.hbs');
238
+ $settingsTemplate = file_get_contents(__DIR__ . '/../../addon/components/telematic/settings.hbs');
239
+ $formComponent = file_get_contents(__DIR__ . '/../../addon/components/telematic/form.js');
240
+ $settingsComponent = file_get_contents(__DIR__ . '/../../addon/components/telematic/settings.js');
241
+
242
+ foreach ([$formComponent, $settingsComponent] as $component) {
243
+ expect($component)
244
+ ->toContain('advancedCredentialFields')
245
+ ->toContain('field.advanced || field.is_endpoint')
246
+ ->toContain('!field.advanced && !field.is_endpoint');
247
+ }
248
+
249
+ foreach ([$formTemplate, $settingsTemplate] as $template) {
250
+ expect($template)
251
+ ->toContain('Advanced connection settings')
252
+ ->toContain('this.advancedCredentialFields')
253
+ ->toContain('field.default_value');
254
+ }
255
+ });
256
+
257
+ test('telematics details logs are routed and use persisted safe data only', function () {
258
+ $routes = file_get_contents(__DIR__ . '/../src/routes.php');
259
+ $controller = file_get_contents(__DIR__ . '/../src/Http/Controllers/Internal/v1/TelematicController.php');
260
+
261
+ expect($routes)
262
+ ->toContain("'{id}/logs'")
263
+ ->toContain("\$controller('logs')");
264
+
265
+ expect($controller)
266
+ ->toContain('public function logs(Request $request, string $id): JsonResponse')
267
+ ->toContain('Activity::with([\'causer\'])')
268
+ ->toContain("where('subject_type', Telematic::class)")
269
+ ->toContain("where('subject_id', \$telematic->uuid)")
270
+ ->toContain("where('company_uuid', session('company'))")
271
+ ->toContain('Provider connection details were updated.')
272
+ ->toContain('makeTelematicMetadataLogs')
273
+ ->toContain('userFacingIssueMessage')
274
+ ->toContain('isSensitiveIssueMessage')
275
+ ->not->toContain('changed_fields')
276
+ ->not->toContain('Str::headline')
277
+ ->not->toContain('storage_path')
278
+ ->not->toContain('Log::');
279
+ });
280
+
281
+ test('telematics provider status display maps active and connected to connected', function () {
282
+ $indexController = file_get_contents(__DIR__ . '/../../addon/controllers/connectivity/telematics/index.js');
283
+ $detailsController = file_get_contents(__DIR__ . '/../../addon/controllers/connectivity/telematics/details.js');
284
+ $statusCell = file_get_contents(__DIR__ . '/../../addon/components/cell/telematic-status.js');
285
+
286
+ expect($indexController)
287
+ ->toContain("cellComponent: 'cell/telematic-status'");
288
+
289
+ expect($detailsController)
290
+ ->toContain("case 'active':\n return 'Connected';");
291
+
292
+ expect($statusCell)
293
+ ->toContain("case 'active':")
294
+ ->toContain("case 'connected':")
295
+ ->toContain("return 'Connected';");
296
+ });
297
+
298
+ test('telematics overview attention items render as full width stacked alerts', function () {
299
+ $template = file_get_contents(__DIR__ . '/../../addon/components/telematic/details.hbs');
300
+
301
+ expect($template)
302
+ ->toContain('<section class="flex flex-col gap-3">')
303
+ ->toContain('class="w-full rounded-md border border-yellow-200 bg-yellow-50 p-3')
304
+ ->not->toContain('lg:grid-cols-2');
305
+ });
306
+
307
+ test('device attachment morph types are normalized and legacy aliases are tolerated', function () {
308
+ $migration = file_get_contents(__DIR__ . '/../migrations/2026_06_15_000001_normalize_device_attachable_vehicle_morph_types.php');
309
+ $command = file_get_contents(__DIR__ . '/../src/Console/Commands/FixInvalidPolymorphicRelationTypeNamespaces.php');
310
+ $serviceProvider = file_get_contents(__DIR__ . '/../src/Providers/FleetOpsServiceProvider.php');
311
+
312
+ expect($migration)
313
+ ->toContain("whereNotNull('attachable_uuid')")
314
+ ->toContain("'Fleetbase\\\\Models\\\\Vehicle'")
315
+ ->toContain("'\\\\Fleetbase\\\\Models\\\\Vehicle'")
316
+ ->toContain("'attachable_type' => Vehicle::class")
317
+ ->toContain('Intentionally do not restore invalid legacy morph class names.');
318
+
319
+ expect($command)
320
+ ->toContain('\\Fleetbase\\FleetOps\\Models\\Device::class')
321
+ ->toContain("'columns' => ['attachable_type']");
322
+
323
+ expect($serviceProvider)
324
+ ->toContain('use Illuminate\Database\Eloquent\Relations\Relation;')
325
+ ->toContain('$this->registerMorphMap();')
326
+ ->toContain("'Fleetbase\\\\Models\\\\Vehicle' => \\Fleetbase\\FleetOps\\Models\\Vehicle::class")
327
+ ->toContain("'\\\\Fleetbase\\\\Models\\\\Vehicle' => \\Fleetbase\\FleetOps\\Models\\Vehicle::class");
328
+ });
329
+
330
+ test('internal device attachment endpoints return specific api errors instead of raw model misses', function () {
331
+ $vehicleController = file_get_contents(__DIR__ . '/../src/Http/Controllers/Internal/v1/VehicleController.php');
332
+ $deviceController = file_get_contents(__DIR__ . '/../src/Http/Controllers/Internal/v1/DeviceController.php');
333
+
334
+ expect($vehicleController)
335
+ ->toContain('$vehicle = $this->resolveVehicle($id);')
336
+ ->toContain('$device = $this->resolveDevice($deviceId);')
337
+ ->toContain('Vehicle not found or not available for this organization.')
338
+ ->toContain('Device not found or not available for this organization.')
339
+ ->toContain('Unable to attach device to vehicle. Please try again or contact support.')
340
+ ->toContain('Unable to detach device from vehicle. Please try again or contact support.')
341
+ ->toContain('logDeviceAttachmentLookupFailure')
342
+ ->toContain('logDeviceAttachmentFailure');
343
+
344
+ expect($deviceController)
345
+ ->toContain('$device = $this->resolveDevice($id);')
346
+ ->toContain('$vehicle = $this->resolveVehicle($vehicleId);')
347
+ ->toContain('Vehicle not found or not available for this organization.')
348
+ ->toContain('Device not found or not available for this organization.')
349
+ ->toContain('Unable to attach device to vehicle. Please try again or contact support.')
350
+ ->toContain('Unable to detach device from vehicle. Please try again or contact support.')
351
+ ->not->toContain('firstOrFail();');
352
+ });
353
+
354
+ function telematics_activity_log_method(string $model): string
355
+ {
356
+ preg_match('/public function getActivitylogOptions\(\): LogOptions\s+\{(?P<body>.*?)\n \}/s', $model, $matches);
357
+
358
+ return $matches['body'] ?? '';
359
+ }
@@ -16,3 +16,54 @@ test('vehicle updates preserve driver assignment unless driver input is explicit
16
16
  ->toContain('public function unassignDriver(): self')
17
17
  ->toContain("Driver::where('vehicle_uuid', \$this->uuid)->update(['vehicle_uuid' => null])");
18
18
  });
19
+
20
+ test('driver assigned-order workflows expose list and multi-unassign endpoints', function () {
21
+ $routes = file_get_contents(dirname(__DIR__) . '/src/routes.php');
22
+ $controller = file_get_contents(dirname(__DIR__) . '/src/Http/Controllers/Internal/v1/DriverController.php');
23
+ $resource = file_get_contents(dirname(__DIR__) . '/src/Http/Resources/v1/Driver.php');
24
+ $index = file_get_contents(dirname(__DIR__) . '/src/Http/Resources/v1/Index/Driver.php');
25
+
26
+ expect($routes)
27
+ ->toContain("\$router->get('{id}/assigned-orders', \$controller('assignedOrders'))")
28
+ ->toContain("\$router->post('{id}/unassign-orders', \$controller('unassignOrders'))")
29
+ ->and($controller)
30
+ ->toContain('public function assignedOrders(string $id): JsonResponse')
31
+ ->toContain('public function unassignOrders(Request $request, string $id): JsonResponse')
32
+ ->toContain("->where('driver_assigned_uuid', \$driver->uuid)")
33
+ ->toContain("->orderByRaw('uuid = ? desc', [\$driver->current_job_uuid])")
34
+ ->toContain("\$request->validate(['orders' => ['required', 'array', 'min:1']])")
35
+ ->toContain("->where('driver_assigned_uuid', \$driver->uuid)")
36
+ ->toContain("->whereIn('uuid', \$selectedOrderUuids)")
37
+ ->toContain("->update(['driver_assigned_uuid' => null])")
38
+ ->toContain("if (\$driver->current_job_uuid && \$orders->contains('uuid', \$driver->current_job_uuid))")
39
+ ->toContain("\$driver->update(['current_job_uuid' => null])")
40
+ ->and($resource)
41
+ ->toContain("'assigned_orders_count'")
42
+ ->toContain("'current_order_reference'")
43
+ ->and($index)
44
+ ->toContain("'assigned_orders_count'");
45
+ });
46
+
47
+ test('vehicle assigned-order workflows expose list and multi-unassign endpoints without clearing driver vehicle assignment', function () {
48
+ $routes = file_get_contents(dirname(__DIR__) . '/src/routes.php');
49
+ $controller = file_get_contents(dirname(__DIR__) . '/src/Http/Controllers/Internal/v1/VehicleController.php');
50
+ $resource = file_get_contents(dirname(__DIR__) . '/src/Http/Resources/v1/Vehicle.php');
51
+ $index = file_get_contents(dirname(__DIR__) . '/src/Http/Resources/v1/Index/Vehicle.php');
52
+
53
+ expect($routes)
54
+ ->toContain("\$router->get('{id}/assigned-orders', \$controller('assignedOrders'))")
55
+ ->toContain("\$router->post('{id}/unassign-orders', \$controller('unassignOrders'))")
56
+ ->and($controller)
57
+ ->toContain('public function assignedOrders(string $id): JsonResponse')
58
+ ->toContain('public function unassignOrders(Request $request, string $id): JsonResponse')
59
+ ->toContain("->where('vehicle_assigned_uuid', \$vehicle->uuid)")
60
+ ->toContain("\$request->validate(['orders' => ['required', 'array', 'min:1']])")
61
+ ->toContain("->whereIn('uuid', \$selectedOrderUuids)")
62
+ ->toContain("->update(['vehicle_assigned_uuid' => null])")
63
+ ->not->toContain("->update(['vehicle_uuid' => null])")
64
+ ->and($resource)
65
+ ->toContain("'assigned_orders_count'")
66
+ ->toContain("'current_order_reference'")
67
+ ->and($index)
68
+ ->toContain("'assigned_orders_count'");
69
+ });
@@ -1,3 +1,7 @@
1
+ menu:
2
+ fuel-providers: تكاملات الوقود
3
+ fuel-transactions: معاملات الوقود
4
+
1
5
  fleet-ops:
2
6
  extension-name: Fleet-Ops
3
7
  common:
@@ -309,10 +313,12 @@ fleet-ops:
309
313
  contacts: جهات الاتصال
310
314
  places: الأماكن
311
315
  fuel-reports: تقارير الوقود
316
+ fuel-transactions: معاملات الوقود
312
317
  issues: القضايا
313
318
  title: إنشاء طلب جديد
314
319
  settings: الإعدادات
315
320
  order-config: تكوين الطلب
321
+ fuel-providers: تكاملات الوقود
316
322
  live-map-drawer:
317
323
  driver-listing:
318
324
  location: الموقع
@@ -1045,7 +1051,7 @@ fleet-ops:
1045
1051
  more-waypoints: المزيد من نقاط الطريق
1046
1052
  settings-window:
1047
1053
  success-message: تم حفظ الإعدادات بنجاح.
1048
- title: إعدادات FleetOps
1054
+ title: إعدادات Fleet-Ops
1049
1055
  save: حفظ التغييرات
1050
1056
  name: مسافة التنبيه الافتراضية للطلبات الفورية
1051
1057
  name-text: يحدد هذا الإعداد المسافة الافتراضية بالأمتار التي يجب أن تنبه الطلبات الفورية السائقين ضمن محيط نقطة استلام الطلب.
@@ -1612,3 +1618,19 @@ scheduler:
1612
1618
  reason: Reason
1613
1619
  reason-placeholder: e.g. Personal appointment
1614
1620
  notes-placeholder: Optional additional notes
1621
+
1622
+ order:
1623
+ card:
1624
+ linked-order: Linked Order
1625
+ unknown-order: Unknown order
1626
+ no-type: No type
1627
+ pickup: Pickup
1628
+ dropoff: Dropoff
1629
+ return: Return
1630
+ waypoints: Waypoints
1631
+ additional-stop: additional stop
1632
+ additional-stops: additional stops
1633
+ no-pickup-address: No pickup address
1634
+ no-dropoff-address: No dropoff address
1635
+ no-route-details: No route details available.
1636
+ no-order-linked: No order is linked to this issue.
@@ -46,6 +46,7 @@ common:
46
46
  overview: Overview
47
47
  notes: Notes
48
48
  schedule: Schedule
49
+ current-job: Current Job
49
50
  menu:
50
51
  operations: Операции
51
52
  dashboard: Табло
@@ -61,6 +62,7 @@ menu:
61
62
  contacts: Контакти
62
63
  places: Места
63
64
  fuel-reports: Доклади за гориво
65
+ fuel-transactions: Транзакции за гориво
64
66
  issues: Проблеми
65
67
  maintenance: Поддръжка
66
68
  work-orders: Работни поръчки
@@ -68,6 +70,7 @@ menu:
68
70
  parts: Части
69
71
  connectivity: Свързаност
70
72
  telematics: Телематика
73
+ fuel-providers: Интеграции за гориво
71
74
  devices: Устройства
72
75
  sensors: Сензори
73
76
  events: Събития
@@ -410,9 +413,30 @@ driver:
410
413
  driver-license: Шофьорска книжка
411
414
  no-vehicle-assigned: Няма зададено превозно средство
412
415
  actions:
416
+ unassign-orders: Unassign Orders
417
+ unassign-vehicle: Unassign Vehicle
418
+ create-issue: Create Issue
413
419
  assign-order: Присвояване на поръчка на шофьор
414
420
  assign-vehicle: Присвояване на превозно средство на шофьор
415
421
  locate-driver: Намери шофьора на картата
422
+ prompts:
423
+ assign-order-success: Order assigned to {driverName}.
424
+ assign-order-title: "Assign order to {driverName}"
425
+ select-order-warning: "Select an order to assign."
426
+ assign-vehicle-title: "Assign vehicle to {driverName}"
427
+ assign-vehicle-success: "Vehicle assigned to {driverName}."
428
+ select-vehicle-warning: Select a vehicle to assign.
429
+ no-assigned-orders-warning: No assigned orders found for {driverName}.
430
+ select-orders-warning: Select at least one order to unassign.
431
+ unassign-orders-title: Unassign orders from {driverName}
432
+ confirm-unassign-orders-title: Unassign {count} order(s) from {driverName}?
433
+ confirm-unassign-orders-body: "This will remove {driverName} from these orders: {orders}."
434
+ unassign-orders-success: Unassigned {driverName} from {count} order(s).
435
+ no-vehicle-assigned-warning: No vehicle is assigned to {driverName}.
436
+ unassign-vehicle-title: Unassign {vehicleName} from {driverName}?
437
+ unassign-vehicle-body: This removes {driverName} from {vehicleName}. Order assignments are not changed.
438
+ unassign-vehicle-success: Unassigned {vehicleName} from {driverName}.
439
+ issue-title: Issue reported for {driverName}
416
440
  device:
417
441
  fields:
418
442
  title: Детайли за устройството
@@ -445,6 +469,18 @@ device:
445
469
  warranty: Гаранция
446
470
  warranty-placeholder: Изберете гаранция
447
471
  notes: Бележки
472
+ actions:
473
+ attach-to-vehicle: Attach to Vehicle
474
+ detach-from-vehicle: Detach from Vehicle
475
+ prompts:
476
+ attach-to-vehicle-title: Attach device to vehicle
477
+ attach-device-to-vehicle-title: "Attach {deviceName} to vehicle"
478
+ select-vehicle-warning: Select a vehicle to attach this device to.
479
+ attach-to-vehicle-success: Device attached to vehicle.
480
+ not-attached-warning: "{deviceName} is not attached to a vehicle."
481
+ detach-from-vehicle-title: "Detach {deviceName} from vehicle?"
482
+ detach-from-vehicle-body: "This detaches {deviceName} from {vehicleName} and stops vehicle telemetry updates until it is attached again."
483
+ detach-from-vehicle-success: Device detached from vehicle.
448
484
  entity:
449
485
  fields:
450
486
  declare: Декларирай
@@ -495,7 +531,15 @@ fleet:
495
531
  add-vehicle: Добавяне на превозно средство към флота
496
532
  loading-message: Зареждане на превозни средства във флота...
497
533
  actions:
534
+ assign-vehicle: Assign Vehicle to Fleet
498
535
  assign-driver: Присвояване на шофьор към флота
536
+ prompts:
537
+ assign-driver-title: Assign driver to {fleetName}
538
+ assign-vehicle-title: Assign vehicle to {fleetName}
539
+ select-driver-warning: Select a driver to assign.
540
+ select-vehicle-warning: Select a vehicle to assign.
541
+ assign-driver-success: Driver assigned to {fleetName}.
542
+ assign-vehicle-success: Vehicle assigned to {fleetName}.
499
543
  fuel-report:
500
544
  fields:
501
545
  details: Детайли за доклада за гориво
@@ -524,6 +568,20 @@ issue:
524
568
  tags: Етикети на проблема
525
569
  type: Тип на проблема
526
570
  order:
571
+ card:
572
+ linked-order: Linked Order
573
+ unknown-order: Unknown order
574
+ no-type: No type
575
+ pickup: Pickup
576
+ dropoff: Dropoff
577
+ return: Return
578
+ waypoints: Waypoints
579
+ additional-stop: additional stop
580
+ additional-stops: additional stops
581
+ no-pickup-address: No pickup address
582
+ no-dropoff-address: No dropoff address
583
+ no-route-details: No route details available.
584
+ no-order-linked: No order is linked to this issue.
527
585
  fields:
528
586
  ad-hoc: Спонтанен
529
587
  ad-hoc-help-text: Активирането на Ad-Hoc ще позволи на поръчката интелигентно
@@ -958,7 +1016,21 @@ vehicle:
958
1016
  vin-number: VIN номер
959
1017
  year: Година
960
1018
  actions:
1019
+ attach-device: Attach Device
1020
+ unassign-orders: Unassign Orders
1021
+ create-issue: Create Issue
961
1022
  locate-vehicle: Намери превозното средство на картата
1023
+ prompts:
1024
+ attach-device-title: Attach device to {vehicleName}
1025
+ select-device-warning: Select a device to attach.
1026
+ attach-device-success: Device attached to {vehicleName}.
1027
+ no-assigned-orders-warning: No assigned orders found for {vehicleName}.
1028
+ select-orders-warning: Select at least one order to unassign.
1029
+ unassign-orders-title: Unassign orders from {vehicleName}
1030
+ confirm-unassign-orders-title: Unassign {count} order(s) from {vehicleName}?
1031
+ confirm-unassign-orders-body: "This will remove {vehicleName} from these orders: {orders}."
1032
+ unassign-orders-success: Unassigned {vehicleName} from {count} order(s).
1033
+ issue-title: Issue reported for {vehicleName}
962
1034
  vendor:
963
1035
  fields:
964
1036
  setup-vendor: Настройка на доставчик