@fleetbase/fleetops-engine 0.6.38 → 0.6.40

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 (432) hide show
  1. package/addon/components/activity/form.hbs +2 -12
  2. package/addon/components/activity/logic-builder.hbs +1 -1
  3. package/addon/components/custom-entity/form.hbs +4 -20
  4. package/addon/components/device/form.hbs +3 -15
  5. package/addon/components/device/panel-header.hbs +13 -2
  6. package/addon/components/driver/details.hbs +33 -2
  7. package/addon/components/driver/form.hbs +42 -0
  8. package/addon/components/driver/panel-header.hbs +8 -1
  9. package/addon/components/driver/schedule.hbs +115 -76
  10. package/addon/components/driver/schedule.js +349 -157
  11. package/addon/components/driver-onboard-settings.hbs +2 -8
  12. package/addon/components/entity-field-editing-settings.hbs +2 -5
  13. package/addon/components/equipment/card.hbs +49 -0
  14. package/addon/components/equipment/card.js +6 -0
  15. package/addon/components/equipment/details.hbs +83 -44
  16. package/addon/components/equipment/form.hbs +111 -41
  17. package/addon/components/equipment/form.js +78 -10
  18. package/addon/components/equipment/panel-header.hbs +36 -0
  19. package/addon/components/equipment/panel-header.js +2 -0
  20. package/addon/components/fleet/driver-listing.hbs +3 -1
  21. package/addon/components/fleet/vehicle-listing.hbs +4 -7
  22. package/addon/components/fleet-panel/vehicle-listing.hbs +1 -6
  23. package/addon/components/fuel-report/form.hbs +1 -5
  24. package/addon/components/layout/fleet-ops-sidebar.hbs +43 -38
  25. package/addon/components/layout/fleet-ops-sidebar.js +103 -24
  26. package/addon/components/maintenance/cost-panel.hbs +176 -0
  27. package/addon/components/maintenance/cost-panel.js +241 -0
  28. package/addon/components/maintenance/details.hbs +123 -60
  29. package/addon/components/maintenance/form.hbs +138 -78
  30. package/addon/components/maintenance/form.js +131 -6
  31. package/addon/components/maintenance/panel-header.hbs +31 -0
  32. package/addon/components/maintenance/panel-header.js +2 -0
  33. package/addon/components/maintenance-schedule/details.hbs +260 -0
  34. package/addon/components/maintenance-schedule/details.js +158 -0
  35. package/addon/components/maintenance-schedule/form.hbs +287 -0
  36. package/addon/components/maintenance-schedule/form.js +199 -0
  37. package/addon/components/map/container.hbs +1 -1
  38. package/addon/components/map/drawer/device-event-listing.hbs +1 -1
  39. package/addon/components/map/drawer/driver-listing.hbs +1 -6
  40. package/addon/components/map/drawer/place-listing.hbs +1 -6
  41. package/addon/components/map/drawer/vehicle-listing.hbs +1 -6
  42. package/addon/components/map/drawer.hbs +8 -1
  43. package/addon/components/map/leaflet-live-map.hbs +2 -1
  44. package/addon/components/map/toolbar/visibility-control-panel.hbs +1 -1
  45. package/addon/components/map/toolbar.hbs +4 -29
  46. package/addon/components/modals/add-driver-shift.hbs +155 -0
  47. package/addon/components/modals/add-driver-shift.js +210 -0
  48. package/addon/components/modals/bulk-assign-orders.hbs +67 -0
  49. package/addon/components/modals/driver-shift.hbs +43 -0
  50. package/addon/components/modals/driver-shift.js +56 -0
  51. package/addon/components/modals/entity-form.hbs +5 -28
  52. package/addon/components/modals/orchestrator-import.hbs +351 -0
  53. package/addon/components/modals/orchestrator-import.js +807 -0
  54. package/addon/components/modals/order-config-new-status.hbs +1 -5
  55. package/addon/components/modals/scheduling-conflict.hbs +47 -0
  56. package/addon/components/modals/scheduling-conflict.js +53 -0
  57. package/addon/components/modals/send-work-order.hbs +91 -0
  58. package/addon/components/modals/send-work-order.js +3 -0
  59. package/addon/components/modals/service-area-form.hbs +1 -6
  60. package/addon/components/modals/set-driver-availability.hbs +50 -0
  61. package/addon/components/modals/set-driver-availability.js +57 -0
  62. package/addon/components/modals/update-order-activity.hbs +13 -9
  63. package/addon/components/modals/user-form.hbs +1 -5
  64. package/addon/components/modals/vehicle-form.hbs +17 -102
  65. package/addon/components/modals/vendor-form.hbs +15 -82
  66. package/addon/components/orchestrator/card-fields-settings.hbs +76 -0
  67. package/addon/components/orchestrator/card-fields-settings.js +134 -0
  68. package/addon/components/orchestrator/order-pool.hbs +264 -0
  69. package/addon/components/orchestrator/order-pool.js +394 -0
  70. package/addon/components/orchestrator/phase-builder.hbs +162 -0
  71. package/addon/components/orchestrator/phase-builder.js +162 -0
  72. package/addon/components/orchestrator/plan-viewer.hbs +278 -0
  73. package/addon/components/orchestrator/plan-viewer.js +342 -0
  74. package/addon/components/orchestrator/resource-panel.hbs +301 -0
  75. package/addon/components/orchestrator/resource-panel.js +106 -0
  76. package/addon/components/orchestrator-workbench.hbs +318 -0
  77. package/addon/components/orchestrator-workbench.js +1088 -0
  78. package/addon/components/order/details/custom-fields.hbs +10 -1
  79. package/addon/components/order/details/detail.hbs +37 -2
  80. package/addon/components/order/details/detail.js +0 -1
  81. package/addon/components/order/details/integrated-vendor-details.hbs +4 -4
  82. package/addon/components/order/details/notes.hbs +1 -7
  83. package/addon/components/order/details/route.hbs +1 -5
  84. package/addon/components/order/form/details.hbs +44 -10
  85. package/addon/components/order/form/details.js +57 -0
  86. package/addon/components/order/form/notes.hbs +1 -7
  87. package/addon/components/order/form/payload.hbs +1 -7
  88. package/addon/components/order/form/route.hbs +3 -15
  89. package/addon/components/order/header.hbs +1 -7
  90. package/addon/components/order/route-editor.hbs +4 -25
  91. package/addon/components/order/schedule-card.hbs +102 -95
  92. package/addon/components/order/schedule-card.js +8 -3
  93. package/addon/components/order-config-manager/details.hbs +2 -10
  94. package/addon/components/order-config-manager/entities.hbs +1 -7
  95. package/addon/components/order-config-manager.hbs +1 -6
  96. package/addon/components/part/card.hbs +49 -0
  97. package/addon/components/part/card.js +6 -0
  98. package/addon/components/part/details.hbs +102 -56
  99. package/addon/components/part/form.hbs +131 -56
  100. package/addon/components/part/form.js +78 -11
  101. package/addon/components/part/panel-header.hbs +36 -0
  102. package/addon/components/part/panel-header.js +2 -0
  103. package/addon/components/place/form.hbs +1 -7
  104. package/addon/components/sensor/details.hbs +1 -1
  105. package/addon/components/sensor/form.hbs +5 -3
  106. package/addon/components/sensor/panel-header.hbs +8 -1
  107. package/addon/components/service-area/form.hbs +1 -6
  108. package/addon/components/service-rate/form.hbs +12 -60
  109. package/addon/components/telematic/form.hbs +6 -2
  110. package/addon/components/vehicle/details/maintenance-history.hbs +42 -0
  111. package/addon/components/vehicle/details/maintenance-history.js +32 -0
  112. package/addon/components/vehicle/details/schedules.hbs +40 -0
  113. package/addon/components/vehicle/details/schedules.js +32 -0
  114. package/addon/components/vehicle/details/work-orders.hbs +42 -0
  115. package/addon/components/vehicle/details/work-orders.js +32 -0
  116. package/addon/components/vehicle/details.hbs +52 -0
  117. package/addon/components/vehicle/form.hbs +51 -0
  118. package/addon/components/vehicle/panel-header.hbs +19 -49
  119. package/addon/components/warranty/details.hbs +3 -2
  120. package/addon/components/warranty/form.hbs +3 -17
  121. package/addon/components/work-order/details.hbs +135 -40
  122. package/addon/components/work-order/form.hbs +178 -45
  123. package/addon/components/work-order/form.js +197 -4
  124. package/addon/components/work-order/panel-header.hbs +31 -0
  125. package/addon/components/work-order/panel-header.js +2 -0
  126. package/addon/controllers/connectivity/devices/index/details.js +1 -1
  127. package/addon/controllers/maintenance/equipment/index/details/index.js +0 -1
  128. package/addon/controllers/maintenance/equipment/index/details.js +36 -1
  129. package/addon/controllers/maintenance/equipment/index/edit.js +56 -1
  130. package/addon/controllers/maintenance/equipment/index/new.js +32 -1
  131. package/addon/controllers/maintenance/equipment/index.js +127 -113
  132. package/addon/controllers/maintenance/maintenances/index/details/index.js +3 -0
  133. package/addon/controllers/maintenance/maintenances/index/details.js +54 -0
  134. package/addon/controllers/maintenance/maintenances/index/edit.js +68 -0
  135. package/addon/controllers/maintenance/maintenances/index/new.js +34 -0
  136. package/addon/controllers/maintenance/maintenances/index.js +191 -0
  137. package/addon/controllers/maintenance/parts/index/details/index.js +0 -1
  138. package/addon/controllers/maintenance/parts/index/details.js +36 -1
  139. package/addon/controllers/maintenance/parts/index/edit.js +56 -1
  140. package/addon/controllers/maintenance/parts/index/new.js +32 -1
  141. package/addon/controllers/maintenance/parts/index.js +135 -113
  142. package/addon/controllers/maintenance/schedules/index/details.js +115 -0
  143. package/addon/controllers/maintenance/schedules/index/edit.js +41 -0
  144. package/addon/controllers/maintenance/schedules/index/new.js +33 -0
  145. package/addon/controllers/maintenance/schedules/index.js +280 -0
  146. package/addon/controllers/maintenance/work-orders/index/details.js +41 -1
  147. package/addon/controllers/maintenance/work-orders/index/edit.js +67 -1
  148. package/addon/controllers/maintenance/work-orders/index/new.js +43 -1
  149. package/addon/controllers/maintenance/work-orders/index.js +105 -113
  150. package/addon/controllers/management/drivers/index/details.js +6 -1
  151. package/addon/controllers/management/vehicles/index/details.js +65 -0
  152. package/addon/controllers/management/vehicles/index.js +18 -0
  153. package/addon/controllers/operations/orchestrator.js +10 -0
  154. package/addon/controllers/operations/orders/index.js +6 -0
  155. package/addon/controllers/operations/scheduler/fleet-schedule.js +341 -0
  156. package/addon/controllers/operations/scheduler/index.js +799 -275
  157. package/addon/controllers/operations/scheduler.js +21 -0
  158. package/addon/controllers/settings/orchestrator.js +70 -0
  159. package/addon/controllers/settings/scheduling.js +155 -0
  160. package/addon/extension.js +19 -0
  161. package/addon/instance-initializers/register-vroom-allocation.js +27 -0
  162. package/addon/models/maintenance-schedule.js +61 -0
  163. package/addon/routes/maintenance/equipment/index/details.js +27 -1
  164. package/addon/routes/maintenance/equipment/index/edit.js +27 -1
  165. package/addon/routes/maintenance/maintenances/index/details/index.js +3 -0
  166. package/addon/routes/maintenance/maintenances/index/details.js +29 -0
  167. package/addon/routes/maintenance/maintenances/index/edit.js +29 -0
  168. package/addon/routes/maintenance/maintenances/index/new.js +3 -0
  169. package/addon/routes/maintenance/maintenances/index.js +23 -0
  170. package/addon/routes/maintenance/maintenances.js +3 -0
  171. package/addon/routes/maintenance/parts/index/details.js +27 -1
  172. package/addon/routes/maintenance/parts/index/edit.js +27 -1
  173. package/addon/routes/maintenance/schedules/index/details/index.js +2 -0
  174. package/addon/routes/maintenance/schedules/index/details/work-orders.js +11 -0
  175. package/addon/routes/maintenance/schedules/index/details.js +25 -0
  176. package/addon/routes/maintenance/schedules/index/edit.js +25 -0
  177. package/addon/routes/maintenance/schedules/index/new.js +2 -0
  178. package/addon/routes/maintenance/schedules/index.js +21 -0
  179. package/addon/routes/maintenance/schedules.js +2 -0
  180. package/addon/routes/maintenance/work-orders/index/details.js +27 -1
  181. package/addon/routes/maintenance/work-orders/index/edit.js +27 -1
  182. package/addon/routes/management/vehicles/index/details/maintenance-history.js +3 -0
  183. package/addon/routes/management/vehicles/index/details/schedules.js +3 -0
  184. package/addon/routes/management/vehicles/index/details/work-orders.js +3 -0
  185. package/addon/routes/operations/orchestrator.js +23 -0
  186. package/addon/routes/operations/scheduler/fleet-schedule.js +28 -0
  187. package/addon/routes/operations/scheduler/index.js +48 -26
  188. package/addon/routes/operations/scheduler.js +14 -1
  189. package/addon/routes/settings/orchestrator.js +27 -0
  190. package/addon/routes/settings/scheduling.js +3 -0
  191. package/addon/routes.js +31 -1
  192. package/addon/services/driver-actions.js +40 -7
  193. package/addon/services/driver-scheduling.js +4 -1
  194. package/addon/services/equipment-actions.js +15 -5
  195. package/addon/services/leaflet-map-manager.js +14 -6
  196. package/addon/services/maintenance-actions.js +17 -14
  197. package/addon/services/maintenance-schedule-actions.js +118 -0
  198. package/addon/services/orchestration-engine-interface.js +49 -0
  199. package/addon/services/orchestration-engine.js +74 -0
  200. package/addon/services/order-actions.js +15 -0
  201. package/addon/services/order-allocation.js +116 -0
  202. package/addon/services/part-actions.js +12 -2
  203. package/addon/services/scheduling.js +316 -0
  204. package/addon/services/vehicle-actions.js +70 -7
  205. package/addon/services/vroom-allocation-engine.js +45 -0
  206. package/addon/services/work-order-actions.js +79 -0
  207. package/addon/styles/fleetops-engine.css +1658 -0
  208. package/addon/templates/analytics/reports/index/edit.hbs +1 -1
  209. package/addon/templates/analytics/reports/index/new.hbs +1 -1
  210. package/addon/templates/application.hbs +6 -1
  211. package/addon/templates/connectivity/devices/index/details/events.hbs +0 -1
  212. package/addon/templates/connectivity/devices.hbs +0 -1
  213. package/addon/templates/connectivity/events/index/details.hbs +0 -1
  214. package/addon/templates/connectivity/events.hbs +0 -1
  215. package/addon/templates/connectivity/sensors.hbs +0 -1
  216. package/addon/templates/connectivity/telematics/index/details/devices.hbs +0 -1
  217. package/addon/templates/connectivity/telematics/index/details/events.hbs +0 -1
  218. package/addon/templates/connectivity/telematics/index/details/sensors.hbs +0 -1
  219. package/addon/templates/connectivity/telematics.hbs +0 -1
  220. package/addon/templates/connectivity/tracking.hbs +0 -1
  221. package/addon/templates/connectivity.hbs +0 -1
  222. package/addon/templates/maintenance/equipment/index/details/index.hbs +1 -2
  223. package/addon/templates/maintenance/equipment/index/details.hbs +15 -2
  224. package/addon/templates/maintenance/equipment/index/edit.hbs +12 -2
  225. package/addon/templates/maintenance/equipment/index/new.hbs +1 -2
  226. package/addon/templates/maintenance/equipment/index.hbs +48 -13
  227. package/addon/templates/maintenance/equipment.hbs +0 -1
  228. package/addon/templates/maintenance/maintenances/index/details/index.hbs +1 -0
  229. package/addon/templates/maintenance/maintenances/index/details.hbs +15 -0
  230. package/addon/templates/maintenance/maintenances/index/edit.hbs +12 -0
  231. package/addon/templates/maintenance/maintenances/index/new.hbs +11 -0
  232. package/addon/templates/maintenance/maintenances/index.hbs +14 -0
  233. package/addon/templates/maintenance/maintenances.hbs +1 -0
  234. package/addon/templates/maintenance/parts/index/details/index.hbs +1 -2
  235. package/addon/templates/maintenance/parts/index/details.hbs +15 -2
  236. package/addon/templates/maintenance/parts/index/edit.hbs +12 -2
  237. package/addon/templates/maintenance/parts/index/new.hbs +1 -2
  238. package/addon/templates/maintenance/parts/index.hbs +48 -13
  239. package/addon/templates/maintenance/parts.hbs +0 -1
  240. package/addon/templates/maintenance/schedules/index/details/index.hbs +1 -0
  241. package/addon/templates/maintenance/schedules/index/details/work-orders.hbs +39 -0
  242. package/addon/templates/maintenance/schedules/index/details.hbs +14 -0
  243. package/addon/templates/maintenance/schedules/index/edit.hbs +12 -0
  244. package/addon/templates/maintenance/schedules/index/new.hbs +11 -0
  245. package/addon/templates/maintenance/schedules/index.hbs +40 -0
  246. package/addon/templates/maintenance/schedules.hbs +1 -0
  247. package/addon/templates/maintenance/work-orders/index/details.hbs +2 -1
  248. package/addon/templates/maintenance/work-orders/index/edit.hbs +2 -4
  249. package/addon/templates/maintenance/work-orders/index/new.hbs +1 -2
  250. package/addon/templates/maintenance/work-orders.hbs +0 -1
  251. package/addon/templates/management/contacts/customers/edit.hbs +1 -2
  252. package/addon/templates/management/contacts/customers/new.hbs +1 -2
  253. package/addon/templates/management/contacts/customers.hbs +1 -1
  254. package/addon/templates/management/contacts/index/edit.hbs +1 -2
  255. package/addon/templates/management/contacts/index/new.hbs +1 -2
  256. package/addon/templates/management/drivers/index/details/orders.hbs +0 -1
  257. package/addon/templates/management/drivers/index/edit.hbs +1 -2
  258. package/addon/templates/management/drivers/index/new.hbs +1 -2
  259. package/addon/templates/management/fleets/index/edit.hbs +1 -2
  260. package/addon/templates/management/fleets/index/new.hbs +1 -2
  261. package/addon/templates/management/fleets/index.hbs +1 -2
  262. package/addon/templates/management/fuel-reports/index/edit.hbs +1 -2
  263. package/addon/templates/management/fuel-reports/index/new.hbs +1 -2
  264. package/addon/templates/management/fuel-reports/index.hbs +1 -2
  265. package/addon/templates/management/issues/index/edit.hbs +1 -2
  266. package/addon/templates/management/issues/index/new.hbs +1 -2
  267. package/addon/templates/management/issues/index.hbs +1 -2
  268. package/addon/templates/management/places/index/details/activity.hbs +0 -1
  269. package/addon/templates/management/places/index/details/comments.hbs +0 -1
  270. package/addon/templates/management/places/index/details/documents.hbs +0 -1
  271. package/addon/templates/management/places/index/details/map.hbs +0 -1
  272. package/addon/templates/management/places/index/details/operations.hbs +0 -1
  273. package/addon/templates/management/places/index/details/performance.hbs +0 -1
  274. package/addon/templates/management/places/index/details/rules.hbs +0 -1
  275. package/addon/templates/management/vehicles/index/details/equipment.hbs +0 -1
  276. package/addon/templates/management/vehicles/index/details/maintenance-history.hbs +2 -0
  277. package/addon/templates/management/vehicles/index/details/schedules.hbs +2 -0
  278. package/addon/templates/management/vehicles/index/details/work-orders.hbs +2 -0
  279. package/addon/templates/management/vehicles/index/details.hbs +1 -1
  280. package/addon/templates/management/vehicles/index/edit.hbs +1 -2
  281. package/addon/templates/management/vehicles/index/new.hbs +1 -2
  282. package/addon/templates/management/vendors/index/edit.hbs +1 -2
  283. package/addon/templates/management/vendors/index/new.hbs +1 -2
  284. package/addon/templates/management/vendors/index.hbs +1 -2
  285. package/addon/templates/management/vendors/integrated.hbs +1 -2
  286. package/addon/templates/operations/orchestrator.hbs +1 -0
  287. package/addon/templates/operations/orders/index.hbs +6 -1
  288. package/addon/templates/operations/scheduler/fleet-schedule.hbs +41 -0
  289. package/addon/templates/operations/scheduler/index.hbs +147 -88
  290. package/addon/templates/operations/scheduler.hbs +7 -1
  291. package/addon/templates/settings/avatars.hbs +1 -1
  292. package/addon/templates/settings/orchestrator.hbs +65 -0
  293. package/addon/templates/settings/payments/index.hbs +1 -5
  294. package/addon/templates/settings/scheduling.hbs +82 -0
  295. package/addon/utils/create-full-calendar-event-from-order.js +52 -14
  296. package/addon/utils/create-full-calendar-event-from-schedule-item.js +50 -0
  297. package/addon/utils/fleet-ops-options.js +254 -0
  298. package/addon/utils/route-colors.js +99 -0
  299. package/addon/utils/to-calendar-date.js +70 -0
  300. package/app/components/driver/schedule.js +1 -0
  301. package/app/components/maintenance/cost-panel.js +1 -0
  302. package/app/components/maintenance/panel-header.js +1 -0
  303. package/app/components/maintenance-schedule/details.js +1 -0
  304. package/app/components/maintenance-schedule/form.js +1 -0
  305. package/app/components/modals/add-driver-shift.js +1 -0
  306. package/app/components/modals/bulk-assign-orders.js +1 -0
  307. package/app/components/modals/driver-shift.js +1 -0
  308. package/app/components/modals/orchestrator-import.js +1 -0
  309. package/app/components/modals/scheduling-conflict.js +1 -0
  310. package/app/components/modals/send-work-order.js +1 -0
  311. package/app/components/modals/set-driver-availability.js +1 -0
  312. package/app/components/orchestrator/card-fields-settings.js +1 -0
  313. package/app/components/orchestrator/order-pool.js +1 -0
  314. package/app/components/orchestrator/phase-builder.js +1 -0
  315. package/app/components/orchestrator/plan-viewer.js +1 -0
  316. package/app/components/orchestrator/resource-panel.js +1 -0
  317. package/app/components/orchestrator-workbench.js +1 -0
  318. package/app/components/vehicle/details/maintenance-history.js +1 -0
  319. package/app/components/vehicle/details/schedules.js +1 -0
  320. package/app/components/vehicle/details/work-orders.js +1 -0
  321. package/app/controllers/operations/orchestrator.js +1 -0
  322. package/app/controllers/settings/orchestrator.js +1 -0
  323. package/app/controllers/settings/scheduling.js +1 -0
  324. package/app/routes/operations/orchestrator.js +1 -0
  325. package/app/routes/settings/orchestrator.js +1 -0
  326. package/app/routes/settings/scheduling.js +1 -0
  327. package/app/services/maintenance-schedule-actions.js +1 -0
  328. package/app/services/orchestration-engine-interface.js +1 -0
  329. package/app/services/orchestration-engine.js +1 -0
  330. package/app/services/order-allocation.js +1 -0
  331. package/app/services/scheduling.js +1 -0
  332. package/app/services/vroom-allocation-engine.js +1 -0
  333. package/app/templates/settings/scheduling.js +1 -0
  334. package/app/utils/create-full-calendar-event-from-schedule-item.js +1 -0
  335. package/app/utils/route-colors.js +1 -0
  336. package/composer.json +4 -2
  337. package/extension.json +1 -1
  338. package/package.json +6 -5
  339. package/server/config/fleetops.php +20 -1
  340. package/server/migrations/2025_08_28_054927_create_parts_table.php +2 -2
  341. package/server/migrations/2025_08_28_054932_add_public_id_to_maintenance_tables.php +45 -0
  342. package/server/migrations/2025_09_01_000001_create_maintenance_schedules_table.php +88 -0
  343. package/server/migrations/2026_04_01_000001_fix_monetary_columns_in_parts_table.php +48 -0
  344. package/server/migrations/2026_04_01_000003_add_photo_uuid_to_equipment_and_parts_tables.php +61 -0
  345. package/server/migrations/2026_04_01_000004_add_public_id_to_equipments_table.php +38 -0
  346. package/server/migrations/2026_04_01_000005_add_missing_columns_to_parts_table.php +67 -0
  347. package/server/migrations/2026_04_04_000001_add_reminder_offsets_to_maintenance_schedules.php +44 -0
  348. package/server/migrations/2026_04_08_000001_add_orchestrator_columns_to_vehicles_table.php +53 -0
  349. package/server/migrations/2026_04_08_000002_add_orchestrator_columns_to_drivers_table.php +41 -0
  350. package/server/migrations/2026_04_08_000003_add_orchestrator_columns_to_orders_table.php +38 -0
  351. package/server/migrations/2026_04_08_000004_add_orchestrator_columns_to_payloads_table.php +38 -0
  352. package/server/migrations/2026_04_08_000005_add_orchestrator_columns_to_waypoints_table.php +38 -0
  353. package/server/migrations/2026_04_09_000001_create_manifests_table.php +48 -0
  354. package/server/migrations/2026_04_09_000002_create_manifest_stops_table.php +48 -0
  355. package/server/migrations/2026_04_09_000003_add_manifest_uuid_to_orders_table.php +28 -0
  356. package/server/migrations/2026_04_13_000001_add_pod_notes_columns_to_waypoints_table.php +39 -0
  357. package/server/migrations/2026_04_14_000001_drop_redundant_capacity_columns_from_payloads_table.php +42 -0
  358. package/server/migrations/2026_04_14_000002_rename_capacity_columns_on_vehicles_table.php +48 -0
  359. package/server/resources/views/mail/maintenance-schedule-reminder.blade.php +59 -0
  360. package/server/resources/views/mail/work-order-dispatched.blade.php +67 -0
  361. package/server/src/Auth/Schemas/FleetOps.php +44 -0
  362. package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +150 -0
  363. package/server/src/Console/Commands/SendMaintenanceReminders.php +128 -0
  364. package/server/src/Http/Controllers/Api/v1/OrderController.php +15 -2
  365. package/server/src/Http/Controllers/Api/v1/VehicleController.php +18 -2
  366. package/server/src/Http/Controllers/Internal/v1/DriverController.php +1 -0
  367. package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +27 -0
  368. package/server/src/Http/Controllers/Internal/v1/LiveController.php +9 -2
  369. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +165 -0
  370. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +304 -0
  371. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +138 -0
  372. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +975 -0
  373. package/server/src/Http/Controllers/Internal/v1/OrderController.php +42 -0
  374. package/server/src/Http/Controllers/Internal/v1/PartController.php +27 -0
  375. package/server/src/Http/Controllers/Internal/v1/SettingController.php +118 -0
  376. package/server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php +214 -0
  377. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +68 -0
  378. package/server/src/Http/Resources/v1/Driver.php +1 -0
  379. package/server/src/Http/Resources/v1/Maintenance.php +138 -0
  380. package/server/src/Http/Resources/v1/MaintenanceSchedule.php +137 -0
  381. package/server/src/Http/Resources/v1/Orchestrator/Order.php +116 -0
  382. package/server/src/Http/Resources/v1/Order.php +7 -4
  383. package/server/src/Http/Resources/v1/Waypoint.php +7 -0
  384. package/server/src/Http/Resources/v1/WorkOrder.php +136 -0
  385. package/server/src/Imports/EquipmentImport.php +32 -0
  386. package/server/src/Imports/MaintenanceImport.php +32 -0
  387. package/server/src/Imports/MaintenanceScheduleImport.php +32 -0
  388. package/server/src/Imports/PartImport.php +32 -0
  389. package/server/src/Imports/WorkOrderImport.php +32 -0
  390. package/server/src/Jobs/ProcessAllocationJob.php +119 -0
  391. package/server/src/Listeners/HandleDeliveryCompletion.php +47 -0
  392. package/server/src/Listeners/NotifyDriverOnShiftChange.php +63 -0
  393. package/server/src/Mail/MaintenanceScheduleReminder.php +68 -0
  394. package/server/src/Mail/WorkOrderDispatched.php +58 -0
  395. package/server/src/Models/Asset.php +2 -2
  396. package/server/src/Models/Device.php +1 -1
  397. package/server/src/Models/Driver.php +82 -4
  398. package/server/src/Models/Equipment.php +62 -2
  399. package/server/src/Models/Maintenance.php +127 -9
  400. package/server/src/Models/MaintenanceSchedule.php +353 -0
  401. package/server/src/Models/Manifest.php +214 -0
  402. package/server/src/Models/ManifestStop.php +162 -0
  403. package/server/src/Models/Order.php +70 -0
  404. package/server/src/Models/OrderConfig.php +5 -2
  405. package/server/src/Models/Part.php +69 -3
  406. package/server/src/Models/Place.php +1 -1
  407. package/server/src/Models/Sensor.php +1 -1
  408. package/server/src/Models/ServiceQuote.php +1 -1
  409. package/server/src/Models/Vehicle.php +18 -1
  410. package/server/src/Models/Warranty.php +1 -1
  411. package/server/src/Models/Waypoint.php +7 -1
  412. package/server/src/Models/WorkOrder.php +122 -12
  413. package/server/src/Notifications/DriverShiftChanged.php +110 -0
  414. package/server/src/Observers/WorkOrderObserver.php +107 -0
  415. package/server/src/Orchestration/Contracts/OrchestrationEngineInterface.php +63 -0
  416. package/server/src/Orchestration/Engines/DriverAssignmentEngine.php +265 -0
  417. package/server/src/Orchestration/Engines/GreedyOrchestrationEngine.php +155 -0
  418. package/server/src/Orchestration/Engines/RouteSequencingEngine.php +272 -0
  419. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +192 -0
  420. package/server/src/Orchestration/OrchestrationEngineRegistry.php +83 -0
  421. package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +380 -0
  422. package/server/src/Providers/EventServiceProvider.php +7 -1
  423. package/server/src/Providers/FleetOpsServiceProvider.php +42 -15
  424. package/server/src/routes.php +65 -4
  425. package/translations/ar-ae.yml +44 -12
  426. package/translations/bg-bg.yaml +51 -10
  427. package/translations/en-us.yaml +444 -1
  428. package/translations/fr-fr.yaml +51 -10
  429. package/translations/mn-mn.yaml +51 -10
  430. package/translations/pt-br.yaml +51 -10
  431. package/translations/ru-ru.yaml +51 -10
  432. package/translations/vi-vn.yaml +48 -12
@@ -3,6 +3,12 @@
3
3
  namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
4
4
 
5
5
  use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
6
+ use Fleetbase\FleetOps\Imports\MaintenanceImport;
7
+ use Fleetbase\FleetOps\Models\Maintenance;
8
+ use Fleetbase\Http\Requests\ImportRequest;
9
+ use Illuminate\Http\JsonResponse;
10
+ use Illuminate\Http\Request;
11
+ use Maatwebsite\Excel\Facades\Excel;
6
12
 
7
13
  class MaintenanceController extends FleetOpsController
8
14
  {
@@ -12,4 +18,163 @@ class MaintenanceController extends FleetOpsController
12
18
  * @var string
13
19
  */
14
20
  public $resource = 'maintenance';
21
+
22
+ /**
23
+ * Eager-load polymorphic relationships after create so they appear in the API response.
24
+ * Called automatically by HasApiControllerBehavior::createRecord() via getControllerCallback.
25
+ */
26
+ public function onAfterCreate($request, Maintenance $record, array $input): void
27
+ {
28
+ $record->load(['maintainable', 'performedBy']);
29
+ }
30
+
31
+ /**
32
+ * Eager-load polymorphic relationships after update so they appear in the API response.
33
+ * Called automatically by HasApiControllerBehavior::updateRecord() via getControllerCallback.
34
+ */
35
+ public function onAfterUpdate($request, Maintenance $record, array $input): void
36
+ {
37
+ $record->load(['maintainable', 'performedBy']);
38
+ }
39
+
40
+ /**
41
+ * Eager-load polymorphic relationships when finding record so they appear in the API response.
42
+ * Called automatically by HasApiControllerBehavior::findRecord() via getControllerCallback.
43
+ */
44
+ public function onFindRecord($builder, $request): void
45
+ {
46
+ $builder->with(['maintainable', 'performedBy']);
47
+ }
48
+
49
+ /**
50
+ * Add a cost line item to a maintenance record.
51
+ * POST /maintenances/{id}/line-items.
52
+ */
53
+ public function addLineItem(string $id, Request $request): JsonResponse
54
+ {
55
+ $maintenance = Maintenance::where('uuid', $id)
56
+ ->orWhere('public_id', $id)
57
+ ->firstOrFail();
58
+
59
+ $validated = $request->validate([
60
+ 'description' => 'required|string|max:255',
61
+ 'quantity' => 'required|numeric|min:0',
62
+ 'unit_cost' => 'required|integer|min:0',
63
+ 'currency' => 'nullable|string|size:3',
64
+ ]);
65
+
66
+ $maintenance->addLineItem($validated);
67
+ $maintenance->refresh();
68
+ $this->recalculateCosts($maintenance);
69
+
70
+ return response()->json([
71
+ 'status' => 'ok',
72
+ 'line_items' => $maintenance->line_items,
73
+ 'total_cost' => $maintenance->total_cost,
74
+ ]);
75
+ }
76
+
77
+ /**
78
+ * Update a cost line item on a maintenance record.
79
+ * PUT /maintenances/{id}/line-items/{index}.
80
+ */
81
+ public function updateLineItem(string $id, int $index, Request $request): JsonResponse
82
+ {
83
+ $maintenance = Maintenance::where('uuid', $id)
84
+ ->orWhere('public_id', $id)
85
+ ->firstOrFail();
86
+
87
+ $validated = $request->validate([
88
+ 'description' => 'required|string|max:255',
89
+ 'quantity' => 'required|numeric|min:0',
90
+ 'unit_cost' => 'required|integer|min:0',
91
+ 'currency' => 'nullable|string|size:3',
92
+ ]);
93
+
94
+ $lineItems = $maintenance->line_items ?? [];
95
+
96
+ if (!isset($lineItems[$index])) {
97
+ return response()->json(['error' => 'Line item not found.'], 404);
98
+ }
99
+
100
+ $lineItems[$index] = array_merge($lineItems[$index], $validated, [
101
+ 'updated_at' => now(),
102
+ ]);
103
+
104
+ $maintenance->update(['line_items' => $lineItems]);
105
+ $maintenance->refresh();
106
+ $this->recalculateCosts($maintenance);
107
+
108
+ return response()->json([
109
+ 'status' => 'ok',
110
+ 'line_items' => $maintenance->line_items,
111
+ 'total_cost' => $maintenance->total_cost,
112
+ ]);
113
+ }
114
+
115
+ /**
116
+ * Remove a cost line item from a maintenance record.
117
+ * DELETE /maintenances/{id}/line-items/{index}.
118
+ */
119
+ public function removeLineItem(string $id, int $index): JsonResponse
120
+ {
121
+ $maintenance = Maintenance::where('uuid', $id)
122
+ ->orWhere('public_id', $id)
123
+ ->firstOrFail();
124
+
125
+ if (!$maintenance->removeLineItem($index)) {
126
+ return response()->json(['error' => 'Line item not found.'], 404);
127
+ }
128
+
129
+ $maintenance->refresh();
130
+ $this->recalculateCosts($maintenance);
131
+
132
+ return response()->json([
133
+ 'status' => 'ok',
134
+ 'line_items' => $maintenance->line_items,
135
+ 'total_cost' => $maintenance->total_cost,
136
+ ]);
137
+ }
138
+
139
+ /**
140
+ * Process import files (excel, csv) into Maintenance records.
141
+ *
142
+ * @return \Illuminate\Http\Response
143
+ */
144
+ public function import(ImportRequest $request)
145
+ {
146
+ $disk = $request->input('disk', config('filesystems.default'));
147
+ $files = $request->resolveFilesFromIds();
148
+ $importedCount = 0;
149
+
150
+ foreach ($files as $file) {
151
+ try {
152
+ $import = new MaintenanceImport();
153
+ Excel::import($import, $file->path, $disk);
154
+ $importedCount += $import->imported;
155
+ } catch (\Throwable $e) {
156
+ return response()->error('Invalid file, unable to process.');
157
+ }
158
+ }
159
+
160
+ return response()->json(['status' => 'ok', 'message' => 'Import completed', 'imported' => $importedCount]);
161
+ }
162
+
163
+ /**
164
+ * Recalculate and persist parts_cost and total_cost derived from line_items.
165
+ * All monetary values are stored as integers (smallest currency unit, e.g. cents).
166
+ */
167
+ protected function recalculateCosts(Maintenance $maintenance): void
168
+ {
169
+ $lineItems = $maintenance->line_items ?? [];
170
+ $partsCost = (int) collect($lineItems)->sum(fn ($item) => ($item['quantity'] ?? 0) * ($item['unit_cost'] ?? 0));
171
+ $laborCost = (int) ($maintenance->labor_cost ?? 0);
172
+ $tax = (int) ($maintenance->tax ?? 0);
173
+ $totalCost = $laborCost + $partsCost + $tax;
174
+
175
+ $maintenance->update([
176
+ 'parts_cost' => $partsCost,
177
+ 'total_cost' => $totalCost,
178
+ ]);
179
+ }
15
180
  }
@@ -0,0 +1,304 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
4
+
5
+ use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
6
+ use Fleetbase\FleetOps\Imports\MaintenanceScheduleImport;
7
+ use Fleetbase\FleetOps\Models\MaintenanceSchedule;
8
+ use Fleetbase\FleetOps\Models\WorkOrder;
9
+ use Fleetbase\Http\Requests\ImportRequest;
10
+ use Illuminate\Http\JsonResponse;
11
+ use Illuminate\Http\Request;
12
+ use Illuminate\Http\Response;
13
+ use Illuminate\Support\Carbon;
14
+ use Maatwebsite\Excel\Facades\Excel;
15
+ use Spatie\IcalendarGenerator\Components\Calendar;
16
+ use Spatie\IcalendarGenerator\Components\Event;
17
+ use Spatie\IcalendarGenerator\Enums\RecurrenceFrequency;
18
+ use Spatie\IcalendarGenerator\ValueObjects\RRule;
19
+
20
+ class MaintenanceScheduleController extends FleetOpsController
21
+ {
22
+ /**
23
+ * The resource to query.
24
+ *
25
+ * @var string
26
+ */
27
+ public $resource = 'maintenance-schedule';
28
+
29
+ /**
30
+ * Process import files (excel, csv) into MaintenanceSchedule records.
31
+ *
32
+ * @return Response
33
+ */
34
+ public function import(ImportRequest $request)
35
+ {
36
+ $disk = $request->input('disk', config('filesystems.default'));
37
+ $files = $request->resolveFilesFromIds();
38
+ $importedCount = 0;
39
+
40
+ foreach ($files as $file) {
41
+ try {
42
+ $import = new MaintenanceScheduleImport();
43
+ Excel::import($import, $file->path, $disk);
44
+ $importedCount += $import->imported;
45
+ } catch (\Throwable $e) {
46
+ return response()->error('Invalid file, unable to process.');
47
+ }
48
+ }
49
+
50
+ return response()->json(['status' => 'ok', 'message' => 'Import completed', 'imported' => $importedCount]);
51
+ }
52
+
53
+ /**
54
+ * Pause a maintenance schedule.
55
+ * POST /maintenance-schedules/{id}/pause.
56
+ */
57
+ public function pause(string $id): JsonResponse
58
+ {
59
+ $schedule = MaintenanceSchedule::where('uuid', $id)
60
+ ->orWhere('public_id', $id)
61
+ ->firstOrFail();
62
+
63
+ $schedule->pause();
64
+
65
+ return response()->json([
66
+ 'status' => 'ok',
67
+ 'message' => 'Maintenance schedule paused.',
68
+ 'data' => $schedule->fresh(),
69
+ ]);
70
+ }
71
+
72
+ /**
73
+ * Resume a paused maintenance schedule.
74
+ * POST /maintenance-schedules/{id}/resume.
75
+ */
76
+ public function resume(string $id): JsonResponse
77
+ {
78
+ $schedule = MaintenanceSchedule::where('uuid', $id)
79
+ ->orWhere('public_id', $id)
80
+ ->firstOrFail();
81
+
82
+ $schedule->resume();
83
+
84
+ return response()->json([
85
+ 'status' => 'ok',
86
+ 'message' => 'Maintenance schedule resumed.',
87
+ 'data' => $schedule->fresh(),
88
+ ]);
89
+ }
90
+
91
+ /**
92
+ * Manually trigger a work order from a schedule.
93
+ * POST /maintenance-schedules/{id}/trigger.
94
+ */
95
+ public function trigger(string $id, Request $request): JsonResponse
96
+ {
97
+ $schedule = MaintenanceSchedule::where('uuid', $id)
98
+ ->orWhere('public_id', $id)
99
+ ->firstOrFail();
100
+
101
+ $workOrder = WorkOrder::create([
102
+ 'company_uuid' => $schedule->company_uuid,
103
+ 'schedule_uuid' => $schedule->uuid,
104
+ 'subject' => $schedule->name,
105
+ 'status' => 'open',
106
+ 'priority' => $schedule->default_priority ?? 'normal',
107
+ 'target_type' => $schedule->subject_type,
108
+ 'target_uuid' => $schedule->subject_uuid,
109
+ 'assignee_type' => $schedule->default_assignee_type,
110
+ 'assignee_uuid' => $schedule->default_assignee_uuid,
111
+ 'instructions' => $schedule->instructions,
112
+ 'due_at' => $schedule->next_due_date,
113
+ 'created_by_uuid' => session('user'),
114
+ ]);
115
+
116
+ return response()->json([
117
+ 'status' => 'ok',
118
+ 'message' => 'Work order created from schedule.',
119
+ 'work_order' => $workOrder,
120
+ ]);
121
+ }
122
+
123
+ /**
124
+ * Return a JSON calendar feed of upcoming maintenance schedule events.
125
+ *
126
+ * Accepts optional `start` and `end` query params (ISO 8601 date strings)
127
+ * to limit the visible window. Defaults to the next 90 days.
128
+ *
129
+ * For recurring schedules (those with interval_value + interval_unit set)
130
+ * we expand all occurrences that fall inside the requested window, not just
131
+ * the single stored next_due_date. This is what makes navigating to future
132
+ * calendar months work correctly.
133
+ *
134
+ * GET /maintenance-schedules/calendar-feed
135
+ */
136
+ public function calendarFeed(Request $request): JsonResponse
137
+ {
138
+ $windowStart = $request->input('start')
139
+ ? Carbon::parse($request->input('start'))->startOfDay()
140
+ : Carbon::today();
141
+
142
+ $windowEnd = $request->input('end')
143
+ ? Carbon::parse($request->input('end'))->endOfDay()
144
+ : Carbon::today()->addDays(90)->endOfDay();
145
+
146
+ // Fetch all active schedules whose next_due_date is on or before the
147
+ // window end. Schedules that start after the window end can never
148
+ // produce an occurrence inside the window.
149
+ $schedules = MaintenanceSchedule::withoutGlobalScopes()
150
+ ->where('status', 'active')
151
+ ->whereNotNull('next_due_date')
152
+ ->where('next_due_date', '<=', $windowEnd)
153
+ ->whereNull('deleted_at')
154
+ ->with(['subject', 'defaultAssignee'])
155
+ ->get();
156
+
157
+ $events = [];
158
+
159
+ foreach ($schedules as $schedule) {
160
+ $assetName = $schedule->subject?->name
161
+ ?? $schedule->subject?->display_name
162
+ ?? $schedule->subject?->public_id
163
+ ?? 'Unknown Asset';
164
+
165
+ $assigneeName = $schedule->defaultAssignee?->name ?? null;
166
+ $color = $this->eventColorForPriority($schedule->default_priority);
167
+
168
+ $baseEvent = [
169
+ 'id' => $schedule->public_id,
170
+ 'uuid' => $schedule->uuid,
171
+ 'title' => $schedule->name . ' — ' . $assetName,
172
+ 'allDay' => true,
173
+ 'status' => $schedule->status,
174
+ 'priority' => $schedule->default_priority,
175
+ 'type' => $schedule->type,
176
+ 'subject_name' => $assetName,
177
+ 'assignee_name' => $assigneeName,
178
+ 'color' => $color,
179
+ ];
180
+
181
+ $intervalValue = (int) ($schedule->interval_value ?? 0);
182
+ $intervalUnit = $schedule->interval_unit ?? null; // days | weeks | months | years
183
+ $firstDue = $schedule->next_due_date->copy()->startOfDay();
184
+
185
+ if ($intervalValue > 0 && $intervalUnit) {
186
+ // --- Recurring schedule: expand all occurrences in the window ---
187
+ //
188
+ // Walk forward from next_due_date in steps of (interval_value interval_unit)
189
+ // and emit an event for every occurrence that falls inside [windowStart, windowEnd].
190
+ // We cap at 500 occurrences as a safety guard.
191
+ $occurrence = $firstDue->copy();
192
+ $count = 0;
193
+ $maxOccurrences = 500;
194
+
195
+ while ($occurrence->lte($windowEnd) && $count < $maxOccurrences) {
196
+ if ($occurrence->gte($windowStart)) {
197
+ $dateStr = $occurrence->toDateString();
198
+ $events[] = array_merge($baseEvent, [
199
+ // Keep id as the plain public_id so the click
200
+ // handler can navigate to the schedule details.
201
+ 'id' => $schedule->public_id,
202
+ 'start' => $dateStr,
203
+ 'end' => $dateStr,
204
+ 'occurrence_date' => $dateStr,
205
+ ]);
206
+ }
207
+ $occurrence->add($intervalValue . ' ' . $intervalUnit);
208
+ $count++;
209
+ }
210
+ } else {
211
+ // --- One-off schedule: emit only if it falls in the window ---
212
+ if ($firstDue->between($windowStart, $windowEnd)) {
213
+ $dateStr = $firstDue->toDateString();
214
+ $events[] = array_merge($baseEvent, [
215
+ 'start' => $dateStr,
216
+ 'end' => $dateStr,
217
+ ]);
218
+ }
219
+ }
220
+ }
221
+
222
+ return response()->json(['events' => $events]);
223
+ }
224
+
225
+ /**
226
+ * Download an iCal (.ics) file for a single maintenance schedule.
227
+ *
228
+ * GET /maintenance-schedules/{id}/ical
229
+ */
230
+ public function ical(string $id): Response
231
+ {
232
+ $schedule = MaintenanceSchedule::where('uuid', $id)
233
+ ->orWhere('public_id', $id)
234
+ ->with(['subject', 'defaultAssignee'])
235
+ ->firstOrFail();
236
+
237
+ $assetName = $schedule->subject?->name
238
+ ?? $schedule->subject?->display_name
239
+ ?? $schedule->subject?->public_id
240
+ ?? 'Asset';
241
+
242
+ $eventTitle = $schedule->name . ' — ' . $assetName;
243
+
244
+ $dueDate = $schedule->next_due_date ?? Carbon::today();
245
+
246
+ $description = implode("\n", array_filter([
247
+ 'Schedule: ' . $schedule->name,
248
+ 'Asset: ' . $assetName,
249
+ 'Type: ' . ucfirst(str_replace('_', ' ', $schedule->type ?? '')),
250
+ 'Priority: ' . ucfirst($schedule->default_priority ?? 'normal'),
251
+ $schedule->instructions ? 'Instructions: ' . $schedule->instructions : null,
252
+ ]));
253
+
254
+ // Build the event, adding an RRULE when the schedule has a time-based interval.
255
+ $event = Event::create($eventTitle)
256
+ ->uniqueIdentifier($schedule->uuid . '@fleetbase.io')
257
+ ->description($description)
258
+ ->startsAt($dueDate->copy()->startOfDay())
259
+ ->endsAt($dueDate->copy()->endOfDay())
260
+ ->fullDay();
261
+
262
+ $intervalValue = (int) ($schedule->interval_value ?? 0);
263
+ $intervalUnit = $schedule->interval_unit ?? null;
264
+
265
+ if ($intervalValue > 0 && $intervalUnit) {
266
+ $freqMap = [
267
+ 'days' => RecurrenceFrequency::daily(),
268
+ 'weeks' => RecurrenceFrequency::weekly(),
269
+ 'months' => RecurrenceFrequency::monthly(),
270
+ 'years' => RecurrenceFrequency::yearly(),
271
+ ];
272
+ $freq = $freqMap[$intervalUnit] ?? null;
273
+ if ($freq !== null) {
274
+ $rrule = RRule::frequency($freq)->interval($intervalValue);
275
+ $event->rrule($rrule);
276
+ }
277
+ }
278
+
279
+ $calendar = Calendar::create($eventTitle)
280
+ ->productIdentifier('Fleetbase FleetOps')
281
+ ->event($event);
282
+
283
+ $filename = 'maintenance-' . $schedule->public_id . '.ics';
284
+
285
+ return response($calendar->get(), 200, [
286
+ 'Content-Type' => 'text/calendar; charset=utf-8',
287
+ 'Content-Disposition' => 'attachment; filename="' . $filename . '"',
288
+ ]);
289
+ }
290
+
291
+ /**
292
+ * Map a priority string to a FullCalendar-compatible hex colour.
293
+ */
294
+ private function eventColorForPriority(?string $priority): string
295
+ {
296
+ return match ($priority) {
297
+ 'critical' => '#ef4444',
298
+ 'high' => '#f97316',
299
+ 'normal' => '#3b82f6',
300
+ 'low' => '#22c55e',
301
+ default => '#6b7280',
302
+ };
303
+ }
304
+ }
@@ -0,0 +1,138 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
4
+
5
+ use Fleetbase\FleetOps\Models\Manifest;
6
+ use Fleetbase\FleetOps\Models\ManifestStop;
7
+ use Fleetbase\Http\Controllers\Controller;
8
+ use Illuminate\Http\JsonResponse;
9
+ use Illuminate\Http\Request;
10
+
11
+ /**
12
+ * ManifestController.
13
+ *
14
+ * Provides CRUD and status-transition endpoints for Manifests and their stops.
15
+ *
16
+ * Endpoints:
17
+ * GET /int/v1/fleet-ops/manifests — list manifests for company
18
+ * GET /int/v1/fleet-ops/manifests/{id} — get a single manifest with stops
19
+ * DELETE /int/v1/fleet-ops/manifests/{id} — cancel/delete a manifest
20
+ * POST /int/v1/fleet-ops/manifests/{id}/cancel — cancel a manifest
21
+ * GET /int/v1/fleet-ops/manifest-stops/{id} — get a single stop
22
+ * PATCH /int/v1/fleet-ops/manifest-stops/{id} — update a stop (status, sequence)
23
+ */
24
+ class ManifestController extends Controller
25
+ {
26
+ /**
27
+ * List manifests for the current company.
28
+ * Supports filtering by status, driver_id, vehicle_id, and scheduled_date.
29
+ *
30
+ * GET /int/v1/fleet-ops/manifests
31
+ */
32
+ public function index(Request $request): JsonResponse
33
+ {
34
+ $companyUuid = session('company');
35
+ $query = Manifest::forCompany($companyUuid)
36
+ ->with(['driver', 'vehicle', 'stops.place', 'stops.order.trackingNumber']);
37
+
38
+ if ($request->filled('status')) {
39
+ $query->where('status', $request->input('status'));
40
+ }
41
+ if ($request->filled('driver_id')) {
42
+ $query->whereHas('driver', fn ($q) => $q->where('public_id', $request->input('driver_id')));
43
+ }
44
+ if ($request->filled('vehicle_id')) {
45
+ $query->whereHas('vehicle', fn ($q) => $q->where('public_id', $request->input('vehicle_id')));
46
+ }
47
+ if ($request->filled('scheduled_date')) {
48
+ $query->whereDate('scheduled_date', $request->input('scheduled_date'));
49
+ }
50
+
51
+ $manifests = $query->orderByDesc('created_at')->paginate($request->input('limit', 30));
52
+
53
+ return response()->json($manifests);
54
+ }
55
+
56
+ /**
57
+ * Get a single manifest with its full stop list.
58
+ *
59
+ * GET /int/v1/fleet-ops/manifests/{id}
60
+ */
61
+ public function show(string $id): JsonResponse
62
+ {
63
+ $manifest = Manifest::where('public_id', $id)
64
+ ->with(['driver', 'vehicle', 'stops' => fn ($q) => $q->orderBy('sequence')->with(['place', 'order.trackingNumber', 'order.payload.dropoff'])])
65
+ ->firstOrFail();
66
+
67
+ return response()->json(['manifest' => $manifest]);
68
+ }
69
+
70
+ /**
71
+ * Cancel a manifest (soft delete + status update).
72
+ *
73
+ * POST /int/v1/fleet-ops/manifests/{id}/cancel
74
+ */
75
+ public function cancel(string $id): JsonResponse
76
+ {
77
+ $manifest = Manifest::where('public_id', $id)->firstOrFail();
78
+ $manifest->cancel();
79
+
80
+ return response()->json(['status' => 'cancelled', 'manifest' => $manifest]);
81
+ }
82
+
83
+ /**
84
+ * Delete (soft-delete) a manifest.
85
+ *
86
+ * DELETE /int/v1/fleet-ops/manifests/{id}
87
+ */
88
+ public function destroy(string $id): JsonResponse
89
+ {
90
+ $manifest = Manifest::where('public_id', $id)->firstOrFail();
91
+ $manifest->delete();
92
+
93
+ return response()->json(['deleted' => true]);
94
+ }
95
+
96
+ /**
97
+ * Get a single manifest stop.
98
+ *
99
+ * GET /int/v1/fleet-ops/manifest-stops/{id}
100
+ */
101
+ public function showStop(string $id): JsonResponse
102
+ {
103
+ $stop = ManifestStop::where('public_id', $id)
104
+ ->with(['place', 'order.trackingNumber', 'order.payload.dropoff', 'waypoint'])
105
+ ->firstOrFail();
106
+
107
+ return response()->json(['stop' => $stop]);
108
+ }
109
+
110
+ /**
111
+ * Update a manifest stop (status, sequence, notes).
112
+ * Used by the dispatcher for manual reordering and by the Navigator app
113
+ * for arrived/completed/skipped status transitions.
114
+ *
115
+ * PATCH /int/v1/fleet-ops/manifest-stops/{id}
116
+ */
117
+ public function updateStop(Request $request, string $id): JsonResponse
118
+ {
119
+ $stop = ManifestStop::where('public_id', $id)->firstOrFail();
120
+
121
+ $allowed = ['status', 'sequence', 'actual_arrival', 'meta'];
122
+ $data = $request->only($allowed);
123
+
124
+ // Handle status transitions via dedicated methods for side-effect logic
125
+ if (isset($data['status'])) {
126
+ match ($data['status']) {
127
+ 'arrived' => $stop->markArrived(),
128
+ 'completed' => $stop->markCompleted(),
129
+ 'skipped' => $stop->markSkipped(),
130
+ default => $stop->update($data),
131
+ };
132
+ } else {
133
+ $stop->update($data);
134
+ }
135
+
136
+ return response()->json(['stop' => $stop->fresh(['place', 'order.trackingNumber'])]);
137
+ }
138
+ }