@fleetbase/fleetops-engine 0.6.69 → 0.6.71

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 (324) hide show
  1. package/RELEASE.md +21 -14
  2. package/addon/components/admin/telematics-settings.hbs +143 -0
  3. package/addon/components/admin/telematics-settings.js +145 -0
  4. package/addon/components/fuel-integration/hub.hbs +1 -1
  5. package/addon/components/layout/fleet-ops-sidebar.js +37 -17
  6. package/addon/components/map/leaflet-live-map.hbs +4 -242
  7. package/addon/components/map/leaflet-live-map.js +2 -1
  8. package/addon/components/map/marker-card/driver.hbs +57 -0
  9. package/addon/components/map/marker-card/driver.js +3 -0
  10. package/addon/components/map/marker-card/vehicle.hbs +78 -0
  11. package/addon/components/map/marker-card/vehicle.js +3 -0
  12. package/addon/components/order/details/activity.hbs +8 -6
  13. package/addon/components/order/details/activity.js +9 -0
  14. package/addon/components/order/details.hbs +29 -0
  15. package/addon/components/order/details.js +26 -0
  16. package/addon/components/order/form/details.hbs +30 -4
  17. package/addon/components/order/form/details.js +46 -0
  18. package/addon/components/order/form/route.hbs +4 -2
  19. package/addon/components/order/form/route.js +11 -0
  20. package/addon/components/order/form.hbs +12 -0
  21. package/addon/components/order/form.js +21 -1
  22. package/addon/components/order/kanban.js +72 -0
  23. package/addon/components/order-config-manager/activity-flow.js +45 -10
  24. package/addon/components/order-config-manager/lifecycle.hbs +250 -0
  25. package/addon/components/order-config-manager/lifecycle.js +358 -0
  26. package/addon/components/order-config-manager.js +5 -1
  27. package/addon/components/telematic/hub.hbs +1 -1
  28. package/addon/components/widget/live-fleet.hbs +8 -40
  29. package/addon/controllers/analytics/reports/index.js +15 -1
  30. package/addon/controllers/connectivity/devices/index/details.js +1 -0
  31. package/addon/controllers/connectivity/devices/index.js +12 -2
  32. package/addon/controllers/connectivity/events/index.js +31 -1
  33. package/addon/controllers/connectivity/fuel-providers/index.js +6 -4
  34. package/addon/controllers/connectivity/sensors/index/details.js +1 -1
  35. package/addon/controllers/connectivity/sensors/index.js +41 -1
  36. package/addon/controllers/connectivity/telematics/index.js +4 -0
  37. package/addon/controllers/maintenance/equipment/index/details.js +2 -2
  38. package/addon/controllers/maintenance/equipment/index.js +36 -10
  39. package/addon/controllers/maintenance/inspection-forms/index.js +32 -12
  40. package/addon/controllers/maintenance/inspection-submissions/index/details.js +2 -1
  41. package/addon/controllers/maintenance/inspection-submissions/index.js +56 -10
  42. package/addon/controllers/maintenance/maintenances/index/details.js +2 -0
  43. package/addon/controllers/maintenance/maintenances/index.js +24 -1
  44. package/addon/controllers/maintenance/parts/index/details.js +2 -2
  45. package/addon/controllers/maintenance/parts/index.js +36 -9
  46. package/addon/controllers/maintenance/schedules/index.js +48 -6
  47. package/addon/controllers/maintenance/work-orders/index/details.js +3 -3
  48. package/addon/controllers/maintenance/work-orders/index.js +47 -9
  49. package/addon/controllers/management/contacts/customers.js +13 -0
  50. package/addon/controllers/management/contacts/index.js +47 -1
  51. package/addon/controllers/management/drivers/index/details.js +1 -0
  52. package/addon/controllers/management/drivers/index.js +36 -2
  53. package/addon/controllers/management/fleets/index/details.js +1 -0
  54. package/addon/controllers/management/fleets/index.js +43 -1
  55. package/addon/controllers/management/fuel-reports/index.js +26 -2
  56. package/addon/controllers/management/fuel-transactions/index.js +45 -7
  57. package/addon/controllers/management/issues/index/details.js +1 -0
  58. package/addon/controllers/management/issues/index.js +26 -2
  59. package/addon/controllers/management/places/index.js +40 -1
  60. package/addon/controllers/management/trailers/index/details.js +1 -0
  61. package/addon/controllers/management/trailers/index.js +50 -2
  62. package/addon/controllers/management/vehicles/index/details.js +1 -0
  63. package/addon/controllers/management/vehicles/index.js +37 -2
  64. package/addon/controllers/management/vendors/index.js +30 -2
  65. package/addon/controllers/operations/orders/index/details.js +61 -5
  66. package/addon/controllers/operations/orders/index.js +66 -4
  67. package/addon/controllers/operations/service-rates/index.js +18 -1
  68. package/addon/controllers/settings/index.js +13 -0
  69. package/addon/controllers/settings/telematics.js +113 -0
  70. package/addon/extension.js +70 -6
  71. package/addon/routes/analytics/index.js +14 -1
  72. package/addon/routes/analytics/reports/index/details.js +1 -1
  73. package/addon/routes/analytics/reports/index/edit.js +1 -1
  74. package/addon/routes/analytics/reports/index/new.js +14 -1
  75. package/addon/routes/analytics/reports.js +14 -1
  76. package/addon/routes/connectivity/devices/index/new.js +14 -1
  77. package/addon/routes/connectivity/devices.js +14 -1
  78. package/addon/routes/connectivity/events.js +14 -1
  79. package/addon/routes/connectivity/fuel-providers/details/transactions.js +12 -0
  80. package/addon/routes/connectivity/fuel-providers/details.js +9 -0
  81. package/addon/routes/connectivity/fuel-providers/edit.js +9 -0
  82. package/addon/routes/connectivity/fuel-providers/new.js +14 -1
  83. package/addon/routes/connectivity/fuel-providers.js +14 -1
  84. package/addon/routes/connectivity/sensors/index/new.js +14 -1
  85. package/addon/routes/connectivity/sensors.js +14 -1
  86. package/addon/routes/connectivity/telematics/new.js +14 -1
  87. package/addon/routes/connectivity/telematics.js +14 -1
  88. package/addon/routes/connectivity/tracking.js +14 -1
  89. package/addon/routes/maintenance/equipment/index/details.js +2 -2
  90. package/addon/routes/maintenance/equipment/index/edit.js +2 -2
  91. package/addon/routes/maintenance/equipment/index/new.js +14 -1
  92. package/addon/routes/maintenance/equipment.js +14 -1
  93. package/addon/routes/maintenance/index.js +14 -1
  94. package/addon/routes/maintenance/inspection-forms/index/details.js +10 -1
  95. package/addon/routes/maintenance/inspection-forms/index/edit.js +10 -1
  96. package/addon/routes/maintenance/inspection-forms/index/new.js +14 -1
  97. package/addon/routes/maintenance/inspection-forms.js +14 -1
  98. package/addon/routes/maintenance/inspection-submissions/index/details.js +10 -1
  99. package/addon/routes/maintenance/inspection-submissions/index/edit.js +10 -1
  100. package/addon/routes/maintenance/inspection-submissions/index/new.js +14 -1
  101. package/addon/routes/maintenance/inspection-submissions.js +14 -1
  102. package/addon/routes/maintenance/maintenances/index/details.js +2 -2
  103. package/addon/routes/maintenance/maintenances/index/edit.js +2 -2
  104. package/addon/routes/maintenance/maintenances/index/new.js +14 -1
  105. package/addon/routes/maintenance/maintenances.js +14 -1
  106. package/addon/routes/maintenance/parts/index/details.js +2 -2
  107. package/addon/routes/maintenance/parts/index/edit.js +2 -2
  108. package/addon/routes/maintenance/parts/index/new.js +14 -1
  109. package/addon/routes/maintenance/parts.js +14 -1
  110. package/addon/routes/maintenance/schedules/index/details.js +2 -2
  111. package/addon/routes/maintenance/schedules/index/edit.js +2 -2
  112. package/addon/routes/maintenance/schedules/index/new.js +15 -1
  113. package/addon/routes/maintenance/schedules.js +15 -1
  114. package/addon/routes/maintenance/work-orders/index/details.js +2 -2
  115. package/addon/routes/maintenance/work-orders/index/edit.js +2 -2
  116. package/addon/routes/maintenance/work-orders/index/new.js +14 -1
  117. package/addon/routes/maintenance/work-orders.js +14 -1
  118. package/addon/routes/management/fuel-transactions/index/details.js +9 -0
  119. package/addon/routes/management/fuel-transactions.js +14 -1
  120. package/addon/routes/management/index.js +13 -0
  121. package/addon/routes/management/trailers.js +16 -0
  122. package/addon/routes/management/vendors/integrated/details.js +14 -1
  123. package/addon/routes/management/vendors/integrated/edit.js +14 -1
  124. package/addon/routes/management/vendors/integrated/new.js +14 -1
  125. package/addon/routes/management/vendors/integrated.js +14 -1
  126. package/addon/routes/operations/orders/index/details.js +1 -0
  127. package/addon/routes/operations/orders.js +40 -1
  128. package/addon/routes/operations/routes/index/details.js +14 -1
  129. package/addon/routes/operations/routes/index/new.js +13 -0
  130. package/addon/routes/operations/routes.js +14 -1
  131. package/addon/routes/operations/service-rates/index/new.js +11 -0
  132. package/addon/routes/operations/service-rates.js +14 -1
  133. package/addon/routes/settings/avatars.js +14 -1
  134. package/addon/routes/settings/custom-fields.js +14 -1
  135. package/addon/routes/settings/map.js +14 -1
  136. package/addon/routes/settings/notifications.js +11 -0
  137. package/addon/routes/settings/orchestrator.js +1 -1
  138. package/addon/routes/settings/payments/index.js +1 -1
  139. package/addon/routes/settings/routing.js +14 -1
  140. package/addon/routes/settings/scheduling.js +14 -1
  141. package/addon/routes/settings/telematics.js +21 -0
  142. package/addon/routes/virtual.js +11 -0
  143. package/addon/routes.js +1 -0
  144. package/addon/services/contact-actions.js +1 -0
  145. package/addon/services/customer-actions.js +2 -1
  146. package/addon/services/device-actions.js +1 -0
  147. package/addon/services/device-event-actions.js +1 -0
  148. package/addon/services/driver-actions.js +4 -13
  149. package/addon/services/equipment-actions.js +1 -0
  150. package/addon/services/fleet-actions.js +1 -0
  151. package/addon/services/fuel-report-actions.js +1 -0
  152. package/addon/services/inspection-form-actions.js +1 -0
  153. package/addon/services/inspection-submission-actions.js +1 -0
  154. package/addon/services/integrated-vendor-actions.js +1 -0
  155. package/addon/services/issue-actions.js +1 -0
  156. package/addon/services/maintenance-actions.js +1 -0
  157. package/addon/services/maintenance-schedule-actions.js +2 -1
  158. package/addon/services/order-actions.js +7 -0
  159. package/addon/services/order-presentation.js +129 -0
  160. package/addon/services/part-actions.js +1 -0
  161. package/addon/services/place-actions.js +1 -0
  162. package/addon/services/sensor-actions.js +1 -0
  163. package/addon/services/service-area-actions.js +1 -0
  164. package/addon/services/service-rate-actions.js +1 -0
  165. package/addon/services/telematic-actions.js +1 -0
  166. package/addon/services/trailer-actions.js +4 -14
  167. package/addon/services/vehicle-actions.js +4 -13
  168. package/addon/services/vendor-actions.js +1 -0
  169. package/addon/services/work-order-actions.js +1 -0
  170. package/addon/services/zone-actions.js +1 -0
  171. package/addon/templates/analytics/reports/index/details.hbs +1 -0
  172. package/addon/templates/analytics/reports/index.hbs +1 -0
  173. package/addon/templates/connectivity/devices/index/details.hbs +1 -0
  174. package/addon/templates/connectivity/devices/index.hbs +1 -0
  175. package/addon/templates/connectivity/events/index.hbs +1 -0
  176. package/addon/templates/connectivity/sensors/index/details.hbs +1 -0
  177. package/addon/templates/connectivity/sensors/index.hbs +1 -0
  178. package/addon/templates/maintenance/equipment/index/details.hbs +1 -0
  179. package/addon/templates/maintenance/equipment/index.hbs +2 -0
  180. package/addon/templates/maintenance/inspection-forms/index/details.hbs +1 -0
  181. package/addon/templates/maintenance/inspection-forms/index.hbs +1 -0
  182. package/addon/templates/maintenance/inspection-submissions/index/details.hbs +1 -0
  183. package/addon/templates/maintenance/inspection-submissions/index.hbs +1 -0
  184. package/addon/templates/maintenance/maintenances/index/details.hbs +1 -0
  185. package/addon/templates/maintenance/maintenances/index.hbs +1 -0
  186. package/addon/templates/maintenance/parts/index/details.hbs +1 -0
  187. package/addon/templates/maintenance/parts/index.hbs +2 -0
  188. package/addon/templates/maintenance/schedules/index/details.hbs +1 -0
  189. package/addon/templates/maintenance/schedules/index.hbs +2 -0
  190. package/addon/templates/maintenance/work-orders/index/details.hbs +1 -0
  191. package/addon/templates/maintenance/work-orders/index.hbs +1 -0
  192. package/addon/templates/management/contacts/customers/details.hbs +1 -0
  193. package/addon/templates/management/contacts/customers.hbs +1 -0
  194. package/addon/templates/management/contacts/index/details.hbs +1 -0
  195. package/addon/templates/management/contacts/index.hbs +1 -0
  196. package/addon/templates/management/contacts.hbs +1 -0
  197. package/addon/templates/management/drivers/index/details.hbs +1 -0
  198. package/addon/templates/management/drivers/index.hbs +2 -0
  199. package/addon/templates/management/fleets/index/details.hbs +1 -0
  200. package/addon/templates/management/fleets/index.hbs +1 -0
  201. package/addon/templates/management/fuel-reports/index/details.hbs +1 -0
  202. package/addon/templates/management/fuel-reports/index.hbs +1 -0
  203. package/addon/templates/management/fuel-transactions/index.hbs +1 -0
  204. package/addon/templates/management/issues/index/details.hbs +1 -0
  205. package/addon/templates/management/issues/index.hbs +1 -0
  206. package/addon/templates/management/places/index/details.hbs +1 -0
  207. package/addon/templates/management/places/index.hbs +1 -0
  208. package/addon/templates/management/trailers/index/details.hbs +1 -0
  209. package/addon/templates/management/trailers/index.hbs +2 -0
  210. package/addon/templates/management/vehicles/index/details.hbs +1 -0
  211. package/addon/templates/management/vehicles/index.hbs +2 -0
  212. package/addon/templates/management/vendors/index/details.hbs +1 -0
  213. package/addon/templates/management/vendors/index.hbs +2 -0
  214. package/addon/templates/management/vendors/integrated.hbs +1 -0
  215. package/addon/templates/operations/orders/index/details.hbs +1 -0
  216. package/addon/templates/operations/orders/index.hbs +1 -0
  217. package/addon/templates/operations/service-rates/index/details.hbs +1 -0
  218. package/addon/templates/operations/service-rates/index.hbs +1 -0
  219. package/addon/templates/settings/telematics.hbs +70 -0
  220. package/addon/utils/normalize-order-config-flow.js +6 -1
  221. package/app/components/admin/telematics-settings.js +1 -0
  222. package/app/components/map/marker-card/driver.js +1 -0
  223. package/app/components/map/marker-card/vehicle.js +1 -0
  224. package/app/controllers/settings/telematics.js +1 -0
  225. package/app/routes/settings/telematics.js +1 -0
  226. package/app/services/order-presentation.js +1 -0
  227. package/app/templates/settings/telematics.js +1 -0
  228. package/composer.json +2 -2
  229. package/docs/AFAQY.md +1 -1
  230. package/docs/TELEMATICS_QUEUES.md +1 -1
  231. package/docs/TELEMETRY_ARCHITECTURE.md +72 -1
  232. package/extension.json +1 -1
  233. package/package.json +3 -3
  234. package/server/config/telemetry.php +12 -0
  235. package/server/migrations/2026_09_23_000001_add_retention_index_to_device_events.php +40 -0
  236. package/server/migrations/2026_09_23_000002_add_retention_index_to_positions.php +40 -0
  237. package/server/migrations/2026_09_23_000003_add_retention_index_to_telematic_sync_runs.php +40 -0
  238. package/server/src/Auth/Schemas/FleetOps.php +104 -5
  239. package/server/src/Console/Commands/DrainTelematicInbox.php +3 -13
  240. package/server/src/Console/Commands/ProcessOperationalAlerts.php +3 -3
  241. package/server/src/Console/Commands/PruneTelematicsData.php +318 -0
  242. package/server/src/Http/Controllers/Api/v1/OrchestrationController.php +9 -0
  243. package/server/src/Http/Controllers/FleetOpsController.php +21 -0
  244. package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +11 -0
  245. package/server/src/Http/Controllers/Internal/v1/ContactController.php +11 -0
  246. package/server/src/Http/Controllers/Internal/v1/CustomerController.php +14 -0
  247. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +13 -0
  248. package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +11 -0
  249. package/server/src/Http/Controllers/Internal/v1/DriverController.php +22 -0
  250. package/server/src/Http/Controllers/Internal/v1/FuelProviderConnectionController.php +13 -0
  251. package/server/src/Http/Controllers/Internal/v1/FuelProviderTransactionController.php +15 -0
  252. package/server/src/Http/Controllers/Internal/v1/FuelReportController.php +9 -3
  253. package/server/src/Http/Controllers/Internal/v1/InspectionFormController.php +15 -0
  254. package/server/src/Http/Controllers/Internal/v1/LiveController.php +16 -0
  255. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +15 -0
  256. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +15 -0
  257. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +15 -0
  258. package/server/src/Http/Controllers/Internal/v1/MetricsController.php +11 -0
  259. package/server/src/Http/Controllers/Internal/v1/NavigatorController.php +42 -1
  260. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +12 -0
  261. package/server/src/Http/Controllers/Internal/v1/OrderController.php +56 -4
  262. package/server/src/Http/Controllers/Internal/v1/PaymentController.php +13 -0
  263. package/server/src/Http/Controllers/Internal/v1/RadarController.php +19 -0
  264. package/server/src/Http/Controllers/Internal/v1/ServiceAreaController.php +9 -3
  265. package/server/src/Http/Controllers/Internal/v1/SettingController.php +465 -14
  266. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +21 -0
  267. package/server/src/Http/Controllers/Internal/v1/TrailerController.php +21 -0
  268. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +21 -0
  269. package/server/src/Http/Controllers/Internal/v1/VendorController.php +17 -0
  270. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +11 -0
  271. package/server/src/Jobs/DispatchTelematicsRetentionJobs.php +70 -0
  272. package/server/src/Jobs/PruneTelematicsDataJob.php +74 -0
  273. package/server/src/Listeners/NotifyDriverOnShiftChange.php +3 -3
  274. package/server/src/Mail/CustomerCredentialsMail.php +1 -1
  275. package/server/src/Models/Order.php +35 -3
  276. package/server/src/Models/OrderConfig.php +129 -0
  277. package/server/src/Models/TrackingNumber.php +8 -4
  278. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +9 -1
  279. package/server/src/Providers/FleetOpsServiceProvider.php +3 -0
  280. package/server/src/Support/Analytics/LiveFleet.php +18 -2
  281. package/server/src/Support/Authorization.php +103 -0
  282. package/server/src/Support/Database/TableIndexes.php +66 -0
  283. package/server/src/Support/OrderTracker.php +2 -2
  284. package/server/src/Support/Reporting/FleetOpsReportSchema.php +476 -116
  285. package/server/src/Support/Telematics/Retention/RetentionPolicy.php +300 -0
  286. package/server/src/Support/Telematics/Retention/TelemetryActivity.php +40 -0
  287. package/server/src/Support/Telematics/TelematicService.php +12 -2
  288. package/server/src/Support/Telematics/Telemetry/Ingestor.php +4 -2
  289. package/server/src/Tracking/TrackingIntelligenceService.php +1 -1
  290. package/server/src/Tracking/TrackingOptions.php +2 -2
  291. package/server/src/Traits/AuthorizesMethods.php +37 -0
  292. package/server/src/routes.php +9 -1
  293. package/server/tests/AnalyticsRoutesTest.php +29 -2
  294. package/server/tests/Feature/Http/AfaqyRealtimeIngestionTest.php +16 -1
  295. package/server/tests/Feature/Http/Api/TelematicsRetentionIndexMigrationTest.php +115 -0
  296. package/server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php +72 -0
  297. package/server/tests/Feature/Http/Internal/OrderControllerCreateRecordTest.php +26 -0
  298. package/server/tests/Feature/Http/Internal/SettingControllerHelpersTest.php +71 -0
  299. package/server/tests/Feature/Http/Internal/TelematicsStorageDeadlinesTest.php +119 -0
  300. package/server/tests/InternalImportExportControllerContractsTest.php +5 -8
  301. package/server/tests/NavigatorControllerContractsTest.php +36 -4
  302. package/server/tests/NotificationAndMailContractsTest.php +31 -0
  303. package/server/tests/ObserverContractsTest.php +1 -1
  304. package/server/tests/OrchestratorConsumableApiTest.php +42 -0
  305. package/server/tests/OrderConfigLifecycleTest.php +120 -0
  306. package/server/tests/ProviderContractsTest.php +10 -0
  307. package/server/tests/ReportSchemaContractsTest.php +120 -6
  308. package/server/tests/SettingControllerContractsTest.php +429 -18
  309. package/server/tests/SmallControllerContractsTest.php +8 -5
  310. package/server/tests/Support/PruneTelematicsDataProbe.php +55 -0
  311. package/server/tests/Unit/Console/Commands/ProcessOperationalAlertsCommandTest.php +2 -2
  312. package/server/tests/Unit/Console/PruneTelematicsDataCommandTest.php +353 -0
  313. package/server/tests/Unit/Events/OrderDispatchFailedTest.php +2 -1
  314. package/server/tests/Unit/Jobs/DispatchTelematicsRetentionJobsTest.php +114 -0
  315. package/server/tests/Unit/Jobs/PruneTelematicsDataJobTest.php +60 -0
  316. package/server/tests/Unit/Orchestration/VroomEngineTransportAndSettingsTest.php +20 -0
  317. package/server/tests/Unit/Support/Analytics/LiveFleetTest.php +6 -0
  318. package/server/tests/Unit/Support/AuthorizationTest.php +142 -0
  319. package/server/tests/Unit/Support/MaintainableAndOperationalAlertsTest.php +12 -1
  320. package/server/tests/Unit/Support/MoreQuerySeamsTest.php +11 -2
  321. package/server/tests/Unit/Support/Telematics/Retention/RetentionPolicyTest.php +190 -0
  322. package/server/tests/Unit/Support/Telematics/Retention/TelemetryActivityTest.php +68 -0
  323. package/server/tests/Unit/Tracking/TrackingOptionsCompanySettingsTest.php +109 -0
  324. package/translations/en-us.yaml +108 -0
@@ -9,7 +9,7 @@
9
9
  @onChange={{perform this.selectOrderConfig}}
10
10
  @placeholder={{t "order.fields.order-type-placeholder"}}
11
11
  @triggerClass="form-select form-input"
12
- @disabled={{cannot-write @resource}}
12
+ @disabled={{or (cannot-write @resource) this.orderTypeLocked}}
13
13
  as |orderConfig|
14
14
  >
15
15
  <SelectOption::OrderConfig @option={{orderConfig}} />
@@ -17,8 +17,19 @@
17
17
  </div>
18
18
  </InputGroup>
19
19
 
20
+ {{#if this.profileDetailFields}}
21
+ <div class="contents">
22
+ <LazyEngineComponent @component={{this.profileDetailFields}} as |ProfileDetailFields|>
23
+ <ProfileDetailFields @resource={{@resource}} @order={{@resource}} @profile={{this.profile}} />
24
+ </LazyEngineComponent>
25
+ </div>
26
+ {{/if}}
27
+
28
+ {{#if this.show.internalId}}
20
29
  <InputGroup @name={{t "order.fields.internal-id"}} @value={{@resource.internal_id}} @helpText={{t "order.fields.internal-id-help-text"}} @disabled={{cannot-write @resource}} />
30
+ {{/if}}
21
31
 
32
+ {{#if this.show.schedule}}
22
33
  <InputGroup>
23
34
  <InputLabel @labelText={{t "order.fields.schedule"}} @helpText={{t "order.fields.schedule-help-text"}} />
24
35
  <DateTimeInput
@@ -30,7 +41,9 @@
30
41
  @disabled={{or @resource.dispatched (cannot-write @resource)}}
31
42
  />
32
43
  </InputGroup>
44
+ {{/if}}
33
45
 
46
+ {{#if this.show.customer}}
34
47
  <InputGroup @name={{t "order.fields.customer"}} @helpText={{t "order.fields.customer-help-text"}}>
35
48
  <ModelSelect
36
49
  @modelName="customer"
@@ -48,7 +61,9 @@
48
61
  <SelectOption::Customer @option={{model}} />
49
62
  </ModelSelect>
50
63
  </InputGroup>
64
+ {{/if}}
51
65
 
66
+ {{#if this.show.facilitator}}
52
67
  <InputGroup @name={{t "order.fields.facilitator"}} @helpText={{t "order.fields.facilitator-help-text"}}>
53
68
  <ModelSelect
54
69
  @modelName="facilitator"
@@ -66,8 +81,9 @@
66
81
  <SelectOption::Facilitator @option={{model}} />
67
82
  </ModelSelect>
68
83
  </InputGroup>
84
+ {{/if}}
69
85
 
70
- {{#if (and @resource.facilitator.isIntegratedVendor @resource.facilitator.service_types.length)}}
86
+ {{#if (and this.show.serviceType @resource.facilitator.isIntegratedVendor @resource.facilitator.service_types.length)}}
71
87
  <InputGroup @name={{t "order.fields.service-type"}} @helpText={{concat (t "order.fields.service-type-help-text") @resource.facilitator.name "."}}>
72
88
  <Select
73
89
  class="w-full"
@@ -82,6 +98,7 @@
82
98
  </InputGroup>
83
99
  {{/if}}
84
100
 
101
+ {{#if this.show.driver}}
85
102
  <InputGroup @name={{t "order.fields.assign-driver"}} @helpText={{t "order.fields.assign-driver-help-text"}}>
86
103
  <ModelSelect
87
104
  @modelName="driver"
@@ -101,7 +118,9 @@
101
118
  <SelectOption::Driver @option={{model}} />
102
119
  </ModelSelect>
103
120
  </InputGroup>
121
+ {{/if}}
104
122
 
123
+ {{#if this.show.vehicle}}
105
124
  <InputGroup @name={{t "order.fields.assign-vehicle"}} @helpText={{t "order.fields.assign-vehicle-help-text"}}>
106
125
  <ModelSelect
107
126
  @modelName="vehicle"
@@ -120,8 +139,9 @@
120
139
  <SelectOption::Vehicle @option={{model}} />
121
140
  </ModelSelect>
122
141
  </InputGroup>
142
+ {{/if}}
123
143
 
124
- {{#if (and @resource.pod_required (not @resource.facilitator.isIntegratedVendor))}}
144
+ {{#if (and this.show.pod @resource.pod_required (not @resource.facilitator.isIntegratedVendor))}}
125
145
  <InputGroup @name={{t "order.fields.proof-delivery"}} @helpText={{t "order.fields.proof-delivery-help-text"}} @required={{true}}>
126
146
  <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
127
147
  <PowerSelect
@@ -142,7 +162,7 @@
142
162
  </InputGroup>
143
163
  {{/if}}
144
164
 
145
- {{#if @resource.adhoc}}
165
+ {{#if (and this.show.adhoc @resource.adhoc)}}
146
166
  <div>
147
167
  <InputGroup
148
168
  @name={{t "order.fields.adhoc-ping"}}
@@ -158,6 +178,7 @@
158
178
  </div>
159
179
  {{/if}}
160
180
 
181
+ {{#if this.show.adhoc}}
161
182
  <div class="lg:col-span-2">
162
183
  <InputGroup @wrapperClass="pt-2">
163
184
  <Toggle
@@ -170,7 +191,9 @@
170
191
  />
171
192
  </InputGroup>
172
193
  </div>
194
+ {{/if}}
173
195
 
196
+ {{#if this.show.dispatch}}
174
197
  <div class="lg:col-span-2">
175
198
  <InputGroup @wrapperClass="pt-1">
176
199
  <Toggle
@@ -183,7 +206,9 @@
183
206
  />
184
207
  </InputGroup>
185
208
  </div>
209
+ {{/if}}
186
210
 
211
+ {{#if this.show.pod}}
187
212
  <div class="lg:col-span-2">
188
213
  <InputGroup @wrapperClass="pt-1">
189
214
  <Toggle
@@ -196,6 +221,7 @@
196
221
  />
197
222
  </InputGroup>
198
223
  </div>
224
+ {{/if}}
199
225
 
200
226
  <RegistryYield @registry="fleet-ops:component:order:form:details" as |RegistryComponent|>
201
227
  <RegistryComponent @order={{@resource}} @resource={{@resource}} />
@@ -13,6 +13,7 @@ export default class OrderFormDetailsComponent extends Component {
13
13
  @service leafletMapManager;
14
14
  @service leafletLayerVisibilityManager;
15
15
  @service currentUser;
16
+ @service orderPresentation;
16
17
  @tracked customFields;
17
18
 
18
19
  constructor() {
@@ -20,6 +21,40 @@ export default class OrderFormDetailsComponent extends Component {
20
21
  this.orderConfigActions.loadAll.perform();
21
22
  }
22
23
 
24
+ /** The presentation profile applying to the order's current config, if any. */
25
+ get profile() {
26
+ return this.orderPresentation.profileFor(this.args.resource);
27
+ }
28
+
29
+ /** Extension fields rendered inside the details grid when a profile applies. */
30
+ get profileDetailFields() {
31
+ return this.profile?.form?.detailFields ?? null;
32
+ }
33
+
34
+ /** A persisted order keeps its profiled order type; reclassification is not supported. */
35
+ get orderTypeLocked() {
36
+ return Boolean(this.profile) && this.args.resource?.isNew === false;
37
+ }
38
+
39
+ /** Field visibility; everything is visible unless a presentation profile hides it. */
40
+ get show() {
41
+ const hidden = this.args.hiddenFields ?? this.orderPresentation.hiddenFieldsFor(this.args.resource);
42
+ const visible = (key) => !hidden.includes(key);
43
+
44
+ return {
45
+ internalId: visible('internal-id'),
46
+ schedule: visible('schedule'),
47
+ customer: visible('customer'),
48
+ facilitator: visible('facilitator'),
49
+ serviceType: visible('service-type'),
50
+ driver: visible('driver'),
51
+ vehicle: visible('vehicle'),
52
+ pod: visible('pod'),
53
+ adhoc: visible('adhoc'),
54
+ dispatch: visible('dispatch'),
55
+ };
56
+ }
57
+
23
58
  get integratedVendorServiceType() {
24
59
  return this.args.resource?.type;
25
60
  }
@@ -38,12 +73,23 @@ export default class OrderFormDetailsComponent extends Component {
38
73
 
39
74
  @task *selectOrderConfig(orderConfig) {
40
75
  if (!orderConfig) return;
76
+ const previousProfile = this.orderPresentation.profileFor(this.args.resource);
41
77
  this.args.resource.setProperties({
42
78
  order_config_uuid: orderConfig.id,
43
79
  order_config: orderConfig,
44
80
  type: orderConfig.key,
45
81
  });
46
82
  this.args.resource.payload.set('type', orderConfig.key);
83
+
84
+ // Let presentation profiles clean up or prepare the draft when the order type changes.
85
+ const nextProfile = this.orderPresentation.profileFor(this.args.resource);
86
+ if (previousProfile && previousProfile !== nextProfile && typeof previousProfile.release === 'function') {
87
+ previousProfile.release(this.args.resource);
88
+ }
89
+ if (nextProfile && nextProfile !== previousProfile) {
90
+ this.orderPresentation.prepare(this.args.resource);
91
+ }
92
+
47
93
  this.requestServiceQuoteRefresh('details.order_config.changed');
48
94
 
49
95
  try {
@@ -1,7 +1,9 @@
1
- <ContentPanel @title={{t "order.fields.route"}} @open={{true}} @wrapperClass="bordered-top">
1
+ <ContentPanel @title={{or this.profileTitle (t "order.fields.route")}} @open={{true}} @wrapperClass="bordered-top">
2
2
  <div class="flex flex-row items-center justify-between mb-4">
3
3
  <div class="flex">
4
- <Toggle @isToggled={{this.multipleWaypoints}} @onToggle={{this.toggleWaypoints}} @label={{t "order.fields.route-label"}} />
4
+ {{#if this.allowsMultipleWaypoints}}
5
+ <Toggle @isToggled={{this.multipleWaypoints}} @onToggle={{this.toggleWaypoints}} @label={{t "order.fields.route-label"}} />
6
+ {{/if}}
5
7
  </div>
6
8
  <div class="flex flex-1 justify-end space-x-2">
7
9
  {{#if this.multipleWaypoints}}
@@ -22,6 +22,7 @@ export default class OrderFormRouteComponent extends Component {
22
22
  @service notifications;
23
23
  @service placeActions;
24
24
  @service orderCreation;
25
+ @service orderPresentation;
25
26
  @tracked multipleWaypoints = false;
26
27
  @tracked routingControl;
27
28
  @tracked route;
@@ -35,6 +36,16 @@ export default class OrderFormRouteComponent extends Component {
35
36
  }
36
37
  }
37
38
 
39
+ /** A presentation profile may hide multi-drop routing and rename the route panel. */
40
+ get allowsMultipleWaypoints() {
41
+ const hidden = this.args.hiddenFields ?? this.orderPresentation.hiddenFieldsFor(this.args.resource);
42
+ return !hidden.includes('multiple-waypoints');
43
+ }
44
+
45
+ get profileTitle() {
46
+ return this.orderPresentation.profileFor(this.args.resource)?.form?.titles?.route ?? null;
47
+ }
48
+
38
49
  get coordinates() {
39
50
  return this.routePoints.map(({ place }) => [place.latitude, place.longitude]);
40
51
  }
@@ -16,6 +16,18 @@
16
16
  RegistryYieldEnd=(component "registry-yield" registry="fleet-ops:component:order:form:end" order=@resource)
17
17
  )
18
18
  }}
19
+ {{else if this.profile}}
20
+ {{#each this.profileSections key="key" as |section|}}
21
+ {{#if section.native}}
22
+ {{#let (component section.componentName resource=@resource hiddenFields=this.hiddenFields) as |NativeSection|}}
23
+ <NativeSection />
24
+ {{/let}}
25
+ {{else}}
26
+ <LazyEngineComponent @component={{section.component}} as |ProfileSection|>
27
+ <ProfileSection @resource={{@resource}} @order={{@resource}} @controller={{@controller}} @profile={{this.profile}} @hiddenFields={{this.hiddenFields}} />
28
+ </LazyEngineComponent>
29
+ {{/if}}
30
+ {{/each}}
19
31
  {{else}}
20
32
  <RegistryYield @registry="fleet-ops:component:order:form:start" as |RegistryComponent|>
21
33
  <RegistryComponent @order={{@resource}} @resource={{@resource}} />
@@ -11,6 +11,7 @@ export default class OrderFormComponent extends Component {
11
11
  @service customFieldsRegistry;
12
12
  @service mapManager;
13
13
  @service currentUser;
14
+ @service orderPresentation;
14
15
  @tracked customFields;
15
16
 
16
17
  constructor() {
@@ -18,6 +19,19 @@ export default class OrderFormComponent extends Component {
18
19
  this.orderConfigActions.loadAll.perform();
19
20
  }
20
21
 
22
+ /** The extension presentation profile applying to this order's config, if any. */
23
+ get profile() {
24
+ return this.orderPresentation.profileFor(this.args.resource);
25
+ }
26
+
27
+ get profileSections() {
28
+ return this.orderPresentation.renderableSectionsFor(this.profile, 'form');
29
+ }
30
+
31
+ get hiddenFields() {
32
+ return this.orderPresentation.hiddenFieldsFor(this.args.resource);
33
+ }
34
+
21
35
  @action selectFacilitator(model) {
22
36
  this.args.resource.set('facilitator', model);
23
37
  this.args.resource.set('driver', null);
@@ -32,7 +46,13 @@ export default class OrderFormComponent extends Component {
32
46
  });
33
47
  this.args.resource.payload.set('type', orderConfig.key);
34
48
 
35
- this.customFields = yield this.customFieldsRegistry.loadSubjectCustomFields.perform(orderConfig);
49
+ // loadSubjectCustomFields rejects when the custom fields cannot be loaded (ember-core 0.3.25).
50
+ try {
51
+ this.customFields = yield this.customFieldsRegistry.loadSubjectCustomFields.perform(orderConfig);
52
+ } catch (err) {
53
+ this.customFields = undefined;
54
+ debug('Unable to load order custom fields: ' + err.message);
55
+ }
36
56
  }
37
57
 
38
58
  @task *selectDriver(driver) {
@@ -13,7 +13,9 @@ export default class OrderKanbanComponent extends Component {
13
13
  @service fetch;
14
14
  @service notifications;
15
15
  @service intl;
16
+ @service store;
16
17
  @tracked statuses = [];
18
+ @tracked orderConfigRecord = null;
17
19
  @tracked orders = this.args.orders ?? [];
18
20
  @tracked orderConfig = this.args.orderConfig ?? null;
19
21
 
@@ -23,6 +25,11 @@ export default class OrderKanbanComponent extends Component {
23
25
  };
24
26
 
25
27
  get columns() {
28
+ const lifecycleColumns = this.#lifecycleColumns();
29
+ if (lifecycleColumns) {
30
+ return lifecycleColumns;
31
+ }
32
+
26
33
  const start = this.#defaultStatuses.start ?? [];
27
34
  const end = this.#defaultStatuses.end ?? [];
28
35
  const loaded = isArray(this.statuses) ? this.statuses : [];
@@ -112,6 +119,64 @@ export default class OrderKanbanComponent extends Component {
112
119
  this.loadStatuses.perform();
113
120
  }
114
121
 
122
+ /**
123
+ * Columns for an order config with a configured lifecycle (`meta.lifecycle`): the flow's own
124
+ * activities in graph order from the initial activity, titled with their status labels.
125
+ * No default dispatch columns are injected. Returns null for configs without a lifecycle.
126
+ */
127
+ #lifecycleColumns() {
128
+ const config = this.orderConfigRecord;
129
+ const lifecycle = config?.meta?.lifecycle;
130
+ const flow = config?.flow;
131
+ if (!lifecycle || !flow || typeof flow !== 'object') {
132
+ return null;
133
+ }
134
+
135
+ const activities = Object.values(flow).filter((activity) => activity && activity.code);
136
+ const byKeyOrCode = new Map();
137
+ for (const activity of activities) {
138
+ byKeyOrCode.set(activity.key ?? activity.code, activity);
139
+ byKeyOrCode.set(activity.code, activity);
140
+ }
141
+
142
+ // Breadth-first walk from the initial activity, then any activities not reachable from it.
143
+ const ordered = [];
144
+ const seen = new Set();
145
+ const queue = [byKeyOrCode.get(lifecycle.initial)].filter(Boolean);
146
+ while (queue.length) {
147
+ const activity = queue.shift();
148
+ if (seen.has(activity.code)) continue;
149
+ seen.add(activity.code);
150
+ ordered.push(activity);
151
+ for (const child of activity.activities ?? []) {
152
+ const next = byKeyOrCode.get(typeof child === 'string' ? child : (child?.key ?? child?.code));
153
+ if (next && !seen.has(next.code)) queue.push(next);
154
+ }
155
+ }
156
+ for (const activity of activities.sort((a, b) => (a.sequence ?? 0) - (b.sequence ?? 0))) {
157
+ if (!seen.has(activity.code)) {
158
+ seen.add(activity.code);
159
+ ordered.push(activity);
160
+ }
161
+ }
162
+
163
+ // Keep orders whose status is not part of the flow visible rather than silently dropping them.
164
+ const columns = ordered.map((activity) => ({ id: activity.code, title: this.#activityTitle(activity) }));
165
+ for (const status of isArray(this.statuses) ? this.statuses : []) {
166
+ if (status && !seen.has(status)) {
167
+ seen.add(status);
168
+ columns.push({ id: status, title: titleize(smartHumanize(status)) });
169
+ }
170
+ }
171
+
172
+ return columns.map((column, index) => ({ ...column, position: index, cards: this.#getOrdersByStatus(column.id, this.orders) }));
173
+ }
174
+
175
+ #activityTitle(activity) {
176
+ const label = typeof activity.status === 'string' ? activity.status.trim() : '';
177
+ return label && !label.includes('{') ? label : titleize(smartHumanize(activity.code));
178
+ }
179
+
115
180
  #getOrdersByStatus(status, orders = []) {
116
181
  let filteredOrders = orders.filter((order) => order.status === status);
117
182
  if (this.orderConfig) {
@@ -125,6 +190,13 @@ export default class OrderKanbanComponent extends Component {
125
190
  const params = {};
126
191
  if (this.orderConfig) params.order_config_uuid = this.orderConfig;
127
192
 
193
+ try {
194
+ this.orderConfigRecord = this.orderConfig ? yield this.store.findRecord('order-config', this.orderConfig) : null;
195
+ } catch (err) {
196
+ this.orderConfigRecord = null;
197
+ debug('Unable to load order config for board: ' + err.message);
198
+ }
199
+
128
200
  try {
129
201
  const statuses = yield this.fetch.get('orders/statuses', params);
130
202
  this.statuses = isArray(statuses) ? statuses : [];
@@ -34,7 +34,17 @@ export default class OrderConfigManagerActivityFlowComponent extends Component {
34
34
  * An array of activity codes that should not be modified.
35
35
  * @type {Array.<string>}
36
36
  */
37
- @tracked immutableActivities = ['created', 'dispatched', 'started'];
37
+ /**
38
+ * Activities that cannot be edited or removed. Configs with their own lifecycle
39
+ * (`meta.lifecycle`) are not bound to the default dispatch activities.
40
+ */
41
+ get immutableActivities() {
42
+ return this.lifecycle ? [] : ['created', 'dispatched', 'started'];
43
+ }
44
+
45
+ get lifecycle() {
46
+ return this.config?.meta?.lifecycle ?? null;
47
+ }
38
48
 
39
49
  /**
40
50
  * The configuration data for the activity flow.
@@ -180,6 +190,12 @@ export default class OrderConfigManagerActivityFlowComponent extends Component {
180
190
  this.clearGraph({
181
191
  onAfter: () => {
182
192
  this.flow = {};
193
+ // A configured lifecycle resets to its saved flow, never to the default dispatch flow.
194
+ if (this.lifecycle && this.config?.flow && Object.keys(this.config.flow).length) {
195
+ this.deserializeFlow(this.config.flow);
196
+ return;
197
+ }
198
+
183
199
  const defaultActivities = this.getDefaultActivities();
184
200
  this.addActivityToGraph(defaultActivities);
185
201
  },
@@ -361,7 +377,7 @@ export default class OrderConfigManagerActivityFlowComponent extends Component {
361
377
  }
362
378
  });
363
379
 
364
- const rootCode = getOrderConfigFlowRootCode(deserializedFlow);
380
+ const rootCode = getOrderConfigFlowRootCode(deserializedFlow, this.lifecycle?.initial);
365
381
  if (rootCode && deserializedFlow[rootCode]) {
366
382
  this.addDeserializedActivityToGraph(deserializedFlow[rootCode]);
367
383
  }
@@ -439,6 +455,10 @@ export default class OrderConfigManagerActivityFlowComponent extends Component {
439
455
  * and creates links from the parent to each child. It then calls itself to add any children of the current child activities.
440
456
  * Finally, it repositions all child activities to maintain visual consistency.
441
457
  *
458
+ * A flow is a graph, not a tree: an activity may be reachable from several parents, and a
459
+ * configured lifecycle can loop back (e.g. active -> extended -> active). An activity that is
460
+ * already on the graph only gets a link from this parent; it is never placed or walked twice.
461
+ *
442
462
  * @param {Object} parentActivity - The parent activity to which child activities will be added.
443
463
  * @param {Array} [childActivities=[]] - An array of child activity objects to be added. Defaults to an empty array.
444
464
  */
@@ -447,6 +467,11 @@ export default class OrderConfigManagerActivityFlowComponent extends Component {
447
467
  return;
448
468
  }
449
469
  childActivities.forEach((childActivityObject) => {
470
+ if (childActivityObject.get('node')) {
471
+ this.addLinkToGraph(parentActivity, childActivityObject);
472
+ return;
473
+ }
474
+
450
475
  const childPositionals = this.getActivityPositioning(childActivityObject, parentActivity);
451
476
  const childActivity = this.createActivityNode(childActivityObject, childPositionals);
452
477
  childActivity.set('parentId', parentActivity.get('id'));
@@ -823,16 +848,26 @@ export default class OrderConfigManagerActivityFlowComponent extends Component {
823
848
  const addButton = this.createAddActivityButton(activity, { x: width });
824
849
  let tools = [];
825
850
 
826
- if (activity.get('code') === 'created' || activity.get('code') === 'dispatched') {
827
- tools = [];
828
- }
851
+ const code = activity.get('code');
852
+ const canUpdate = this.abilities.can('fleet-ops update order-config');
829
853
 
830
- if (activity.get('code') === 'started' && this.abilities.can('fleet-ops update order-config')) {
831
- tools = [addButton];
832
- }
854
+ if (this.lifecycle) {
855
+ // The configured initial activity is the root: it can gain children but not be removed.
856
+ if (canUpdate) {
857
+ tools = code === this.lifecycle.initial ? [addButton] : [removeButton, addButton];
858
+ }
859
+ } else {
860
+ if (code === 'created' || code === 'dispatched') {
861
+ tools = [];
862
+ }
863
+
864
+ if (code === 'started' && canUpdate) {
865
+ tools = [addButton];
866
+ }
833
867
 
834
- if (!this.immutableActivities.includes(activity.get('code')) && this.abilities.can('fleet-ops update order-config')) {
835
- tools = [removeButton, addButton];
868
+ if (!this.immutableActivities.includes(code) && canUpdate) {
869
+ tools = [removeButton, addButton];
870
+ }
836
871
  }
837
872
 
838
873
  const activityNodeView = activityNode.findView(this.paper);