@fleetbase/fleetops-engine 0.6.70 → 0.6.72

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 (510) hide show
  1. package/RELEASE.md +18 -47
  2. package/addon/components/admin/tracking-page-settings.hbs +56 -0
  3. package/addon/components/admin/tracking-page-settings.js +67 -0
  4. package/addon/components/device/form.hbs +2 -2
  5. package/addon/components/driver/details.hbs +1 -1
  6. package/addon/components/driver/form.hbs +1 -1
  7. package/addon/components/equipment/form.hbs +1 -1
  8. package/addon/components/layout/fleet-ops-sidebar.js +11 -3
  9. package/addon/components/maintenance/form.hbs +3 -3
  10. package/addon/components/map/google-live-map.js +2 -2
  11. package/addon/components/map/leaflet-live-map.js +45 -32
  12. package/addon/components/orchestrator/phase-builder.js +2 -1
  13. package/addon/components/orchestrator-workbench.hbs +7 -1
  14. package/addon/components/orchestrator-workbench.js +16 -2
  15. package/addon/components/order/details/activity.hbs +8 -6
  16. package/addon/components/order/details/activity.js +9 -0
  17. package/addon/components/order/details.hbs +29 -0
  18. package/addon/components/order/details.js +26 -0
  19. package/addon/components/order/form/details.hbs +30 -4
  20. package/addon/components/order/form/details.js +46 -0
  21. package/addon/components/order/form/route.hbs +5 -3
  22. package/addon/components/order/form/route.js +24 -1
  23. package/addon/components/order/form.hbs +12 -0
  24. package/addon/components/order/form.js +21 -1
  25. package/addon/components/order/kanban.js +72 -0
  26. package/addon/components/order-config-manager/activity-flow.js +45 -10
  27. package/addon/components/order-config-manager/lifecycle.hbs +250 -0
  28. package/addon/components/order-config-manager/lifecycle.js +358 -0
  29. package/addon/components/order-config-manager.js +5 -1
  30. package/addon/components/service-rate/details.hbs +1 -1
  31. package/addon/components/service-rate/form.hbs +1 -1
  32. package/addon/components/tracking-page/full-view.hbs +276 -0
  33. package/addon/components/tracking-page/full-view.js +474 -0
  34. package/addon/components/tracking-page/locale-selector.js +30 -0
  35. package/addon/components/tracking-page/map.hbs +44 -0
  36. package/addon/components/tracking-page/map.js +328 -0
  37. package/addon/components/tracking-page/order-list.hbs +35 -0
  38. package/addon/components/tracking-page/public-view.hbs +60 -0
  39. package/addon/components/tracking-page/settings-preview.hbs +21 -0
  40. package/addon/components/tracking-page/settings-preview.js +23 -0
  41. package/addon/components/tracking-page/verify-panel.hbs +109 -0
  42. package/addon/components/tracking-page.hbs +142 -0
  43. package/addon/components/tracking-page.js +564 -0
  44. package/addon/components/trailer/form.hbs +2 -2
  45. package/addon/components/vehicle/form.hbs +3 -3
  46. package/addon/components/warranty/form.hbs +2 -2
  47. package/addon/components/widget/live-fleet.js +54 -6
  48. package/addon/components/widget/proof-of-delivery.hbs +49 -0
  49. package/addon/components/widget/proof-of-delivery.js +62 -0
  50. package/addon/components/work-order/form.hbs +3 -3
  51. package/addon/controllers/analytics/reports/index.js +10 -1
  52. package/addon/controllers/connectivity/devices/index/details.js +1 -0
  53. package/addon/controllers/connectivity/devices/index.js +8 -2
  54. package/addon/controllers/connectivity/events/index.js +31 -1
  55. package/addon/controllers/connectivity/sensors/index/details.js +1 -1
  56. package/addon/controllers/connectivity/sensors/index.js +37 -1
  57. package/addon/controllers/maintenance/equipment/index/details.js +2 -2
  58. package/addon/controllers/maintenance/equipment/index.js +28 -9
  59. package/addon/controllers/maintenance/inspection-forms/index.js +32 -12
  60. package/addon/controllers/maintenance/inspection-submissions/index/details.js +2 -1
  61. package/addon/controllers/maintenance/inspection-submissions/index.js +49 -9
  62. package/addon/controllers/maintenance/maintenances/index/details.js +2 -0
  63. package/addon/controllers/maintenance/maintenances/index.js +20 -1
  64. package/addon/controllers/maintenance/parts/index/details.js +2 -2
  65. package/addon/controllers/maintenance/parts/index.js +28 -8
  66. package/addon/controllers/maintenance/schedules/index.js +33 -4
  67. package/addon/controllers/maintenance/work-orders/index/details.js +3 -3
  68. package/addon/controllers/maintenance/work-orders/index.js +39 -8
  69. package/addon/controllers/management/contacts/customers.js +13 -0
  70. package/addon/controllers/management/contacts/index.js +43 -1
  71. package/addon/controllers/management/drivers/index/details.js +1 -0
  72. package/addon/controllers/management/drivers/index.js +32 -2
  73. package/addon/controllers/management/fleets/index/details.js +1 -0
  74. package/addon/controllers/management/fleets/index.js +39 -1
  75. package/addon/controllers/management/fuel-reports/index.js +22 -2
  76. package/addon/controllers/management/fuel-transactions/index.js +43 -7
  77. package/addon/controllers/management/issues/index/details.js +1 -0
  78. package/addon/controllers/management/issues/index.js +22 -2
  79. package/addon/controllers/management/places/index.js +36 -1
  80. package/addon/controllers/management/trailers/index/details.js +1 -0
  81. package/addon/controllers/management/trailers/index.js +50 -2
  82. package/addon/controllers/management/vehicles/index/details.js +1 -0
  83. package/addon/controllers/management/vehicles/index.js +33 -2
  84. package/addon/controllers/management/vendors/index.js +26 -2
  85. package/addon/controllers/operations/orders/index/details.js +50 -7
  86. package/addon/controllers/operations/orders/index.js +59 -4
  87. package/addon/controllers/operations/scheduler/index.js +45 -14
  88. package/addon/controllers/operations/service-rates/index.js +15 -1
  89. package/addon/controllers/settings/index.js +6 -0
  90. package/addon/controllers/settings/map.js +75 -0
  91. package/addon/controllers/settings/orchestrator.js +8 -6
  92. package/addon/controllers/settings/tracking-page.js +212 -0
  93. package/addon/extension.js +97 -1
  94. package/addon/helpers/parcel-box-image.js +16 -0
  95. package/addon/routes/operations/orders/index/details.js +1 -0
  96. package/addon/routes/operations/orders/index/new.js +5 -4
  97. package/addon/routes/operations/scheduler/index.js +1 -1
  98. package/addon/routes/settings/tracking-page.js +31 -0
  99. package/addon/routes.js +1 -0
  100. package/addon/services/contact-actions.js +1 -0
  101. package/addon/services/customer-actions.js +2 -1
  102. package/addon/services/device-actions.js +1 -0
  103. package/addon/services/device-event-actions.js +1 -0
  104. package/addon/services/driver-actions.js +4 -13
  105. package/addon/services/equipment-actions.js +1 -0
  106. package/addon/services/fleet-actions.js +1 -0
  107. package/addon/services/fuel-report-actions.js +1 -0
  108. package/addon/services/inspection-form-actions.js +1 -0
  109. package/addon/services/inspection-submission-actions.js +1 -0
  110. package/addon/services/integrated-vendor-actions.js +1 -0
  111. package/addon/services/issue-actions.js +1 -0
  112. package/addon/services/location.js +69 -21
  113. package/addon/services/maintenance-actions.js +1 -0
  114. package/addon/services/maintenance-schedule-actions.js +2 -1
  115. package/addon/services/map-settings.js +28 -0
  116. package/addon/services/order-actions.js +7 -0
  117. package/addon/services/order-allocation.js +1 -1
  118. package/addon/services/order-presentation.js +129 -0
  119. package/addon/services/part-actions.js +1 -0
  120. package/addon/services/place-actions.js +1 -0
  121. package/addon/services/sensor-actions.js +1 -0
  122. package/addon/services/service-area-actions.js +1 -0
  123. package/addon/services/service-rate-actions.js +1 -0
  124. package/addon/services/telematic-actions.js +1 -0
  125. package/addon/services/trailer-actions.js +4 -14
  126. package/addon/services/vehicle-actions.js +4 -13
  127. package/addon/services/vendor-actions.js +1 -0
  128. package/addon/services/work-order-actions.js +1 -0
  129. package/addon/services/zone-actions.js +1 -0
  130. package/addon/styles/fleetops-engine.css +1391 -0
  131. package/addon/templates/analytics/reports/index/details.hbs +1 -0
  132. package/addon/templates/analytics/reports/index.hbs +1 -0
  133. package/addon/templates/connectivity/devices/index/details.hbs +1 -0
  134. package/addon/templates/connectivity/devices/index.hbs +1 -0
  135. package/addon/templates/connectivity/events/index.hbs +1 -0
  136. package/addon/templates/connectivity/sensors/index/details.hbs +1 -0
  137. package/addon/templates/connectivity/sensors/index.hbs +1 -0
  138. package/addon/templates/maintenance/equipment/index/details.hbs +1 -0
  139. package/addon/templates/maintenance/equipment/index.hbs +2 -0
  140. package/addon/templates/maintenance/inspection-forms/index/details.hbs +1 -0
  141. package/addon/templates/maintenance/inspection-forms/index.hbs +1 -0
  142. package/addon/templates/maintenance/inspection-submissions/index/details.hbs +1 -0
  143. package/addon/templates/maintenance/inspection-submissions/index.hbs +1 -0
  144. package/addon/templates/maintenance/maintenances/index/details.hbs +1 -0
  145. package/addon/templates/maintenance/maintenances/index.hbs +1 -0
  146. package/addon/templates/maintenance/parts/index/details.hbs +1 -0
  147. package/addon/templates/maintenance/parts/index.hbs +2 -0
  148. package/addon/templates/maintenance/schedules/index/details.hbs +1 -0
  149. package/addon/templates/maintenance/schedules/index.hbs +2 -0
  150. package/addon/templates/maintenance/work-orders/index/details.hbs +1 -0
  151. package/addon/templates/maintenance/work-orders/index.hbs +1 -0
  152. package/addon/templates/management/contacts/customers/details.hbs +1 -0
  153. package/addon/templates/management/contacts/customers.hbs +1 -0
  154. package/addon/templates/management/contacts/index/details.hbs +1 -0
  155. package/addon/templates/management/contacts/index.hbs +1 -0
  156. package/addon/templates/management/contacts.hbs +1 -0
  157. package/addon/templates/management/drivers/index/details.hbs +1 -0
  158. package/addon/templates/management/drivers/index.hbs +2 -0
  159. package/addon/templates/management/fleets/index/details.hbs +1 -0
  160. package/addon/templates/management/fleets/index.hbs +1 -0
  161. package/addon/templates/management/fuel-reports/index/details.hbs +1 -0
  162. package/addon/templates/management/fuel-reports/index.hbs +1 -0
  163. package/addon/templates/management/fuel-transactions/index.hbs +1 -0
  164. package/addon/templates/management/issues/index/details.hbs +1 -0
  165. package/addon/templates/management/issues/index.hbs +1 -0
  166. package/addon/templates/management/places/index/details.hbs +1 -0
  167. package/addon/templates/management/places/index.hbs +1 -0
  168. package/addon/templates/management/trailers/index/details.hbs +1 -0
  169. package/addon/templates/management/trailers/index.hbs +2 -0
  170. package/addon/templates/management/vehicles/index/details.hbs +1 -0
  171. package/addon/templates/management/vehicles/index.hbs +2 -0
  172. package/addon/templates/management/vendors/index/details.hbs +1 -0
  173. package/addon/templates/management/vendors/index.hbs +2 -0
  174. package/addon/templates/management/vendors/integrated.hbs +1 -0
  175. package/addon/templates/operations/orders/index/details.hbs +1 -0
  176. package/addon/templates/operations/orders/index.hbs +1 -0
  177. package/addon/templates/operations/service-rates/index/details.hbs +1 -0
  178. package/addon/templates/operations/service-rates/index.hbs +1 -0
  179. package/addon/templates/settings/map.hbs +46 -0
  180. package/addon/templates/settings/tracking-page.hbs +318 -0
  181. package/addon/utils/identity-cell-resource.js +2 -56
  182. package/addon/utils/map-default-view.js +132 -0
  183. package/addon/utils/normalize-order-config-flow.js +6 -1
  184. package/addon/utils/placeholder-images.js +4 -69
  185. package/addon/utils/resource-descriptors/assets.js +2 -57
  186. package/addon/utils/resource-descriptors/helpers.js +23 -122
  187. package/addon/utils/resource-descriptors/operations.js +2 -61
  188. package/addon/utils/resource-descriptors/people.js +3 -142
  189. package/addon/utils/resource-descriptors/shared.js +18 -0
  190. package/addon/utils/tracking-live-source.js +123 -0
  191. package/addon/utils/tracking-page-theme.js +123 -0
  192. package/app/components/admin/tracking-page-settings.js +1 -0
  193. package/app/components/tracking-page/full-view.js +1 -0
  194. package/app/components/tracking-page/locale-selector.js +1 -0
  195. package/app/components/tracking-page/map.js +1 -0
  196. package/app/components/tracking-page/order-list.js +1 -0
  197. package/app/components/tracking-page/public-view.js +1 -0
  198. package/app/components/tracking-page/settings-preview.js +1 -0
  199. package/app/components/tracking-page/verify-panel.js +1 -0
  200. package/app/components/tracking-page.js +1 -0
  201. package/app/components/widget/proof-of-delivery.js +1 -0
  202. package/app/controllers/settings/tracking-page.js +1 -0
  203. package/app/helpers/parcel-box-image.js +1 -0
  204. package/app/routes/settings/tracking-page.js +1 -0
  205. package/app/services/order-presentation.js +1 -0
  206. package/app/templates/settings/tracking-page.js +1 -0
  207. package/app/utils/map-default-view.js +1 -0
  208. package/app/utils/tracking-live-source.js +1 -0
  209. package/app/utils/tracking-page-theme.js +1 -0
  210. package/composer.json +2 -2
  211. package/extension.json +1 -1
  212. package/package.json +4 -4
  213. package/scripts/pest-bootstrap.php +4 -0
  214. package/server/config/fleetops.php +4 -0
  215. package/server/migrations/2026_10_08_000001_create_tracking_sessions_tables.php +51 -0
  216. package/server/migrations/2026_10_10_000001_add_trashed_index_to_device_events.php +40 -0
  217. package/server/migrations/2026_10_10_000002_add_trashed_index_to_positions.php +40 -0
  218. package/server/migrations/2026_10_12_000001_add_deleted_at_to_tracking_sessions_tables.php +32 -0
  219. package/server/resources/views/labels/default.php +2 -2
  220. package/server/resources/views/labels/entity-label.php +2 -2
  221. package/server/resources/views/labels/waypoint-label.php +2 -2
  222. package/server/src/Auth/Schemas/FleetOps.php +5 -0
  223. package/server/src/Console/Commands/ProcessOperationalAlerts.php +1 -0
  224. package/server/src/Console/Commands/PruneTelematicsData.php +101 -26
  225. package/server/src/Events/OrderChanged.php +257 -0
  226. package/server/src/Events/OrderDriverUnassigned.php +68 -0
  227. package/server/src/Exceptions/OrderEditLockedException.php +32 -0
  228. package/server/src/Expansions/CompanyExpansion.php +3 -2
  229. package/server/src/Expansions/UserExpansion.php +12 -10
  230. package/server/src/Http/Controllers/Api/v1/CustomerController.php +108 -18
  231. package/server/src/Http/Controllers/Api/v1/DriverController.php +95 -10
  232. package/server/src/Http/Controllers/Api/v1/EntityController.php +66 -0
  233. package/server/src/Http/Controllers/Api/v1/FleetController.php +10 -4
  234. package/server/src/Http/Controllers/Api/v1/OrderController.php +139 -36
  235. package/server/src/Http/Controllers/Api/v1/PayloadController.php +76 -8
  236. package/server/src/Http/Controllers/Api/v1/PlaceController.php +3 -3
  237. package/server/src/Http/Controllers/Api/v1/TrackingNumberController.php +6 -32
  238. package/server/src/Http/Controllers/Api/v1/VehicleController.php +5 -0
  239. package/server/src/Http/Controllers/Internal/v1/ContactController.php +3 -0
  240. package/server/src/Http/Controllers/Internal/v1/DriverController.php +10 -0
  241. package/server/src/Http/Controllers/Internal/v1/FleetController.php +16 -2
  242. package/server/src/Http/Controllers/Internal/v1/InspectionSubmissionController.php +13 -8
  243. package/server/src/Http/Controllers/Internal/v1/IssueController.php +2 -0
  244. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +4 -2
  245. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +93 -11
  246. package/server/src/Http/Controllers/Internal/v1/OrderController.php +118 -28
  247. package/server/src/Http/Controllers/Internal/v1/PositionController.php +22 -0
  248. package/server/src/Http/Controllers/Internal/v1/ProofController.php +55 -4
  249. package/server/src/Http/Controllers/Internal/v1/SettingController.php +303 -10
  250. package/server/src/Http/Controllers/Internal/v1/TrailerController.php +1 -0
  251. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +1 -0
  252. package/server/src/Http/Controllers/Public/TrackingController.php +518 -0
  253. package/server/src/Http/Filter/ContactFilter.php +1 -1
  254. package/server/src/Http/Filter/InspectionFormFilter.php +23 -0
  255. package/server/src/Http/Filter/InspectionSubmissionFilter.php +23 -0
  256. package/server/src/Http/Filter/MaintenanceFilter.php +23 -0
  257. package/server/src/Http/Filter/OrderFilter.php +39 -4
  258. package/server/src/Http/Filter/ProofFilter.php +18 -0
  259. package/server/src/Http/Filter/RouteFilter.php +18 -0
  260. package/server/src/Http/Filter/TelematicFilter.php +23 -0
  261. package/server/src/Http/Filter/WarrantyFilter.php +23 -0
  262. package/server/src/Http/Requests/CancelOrderRequest.php +2 -1
  263. package/server/src/Http/Resources/v1/Concerns/ResolvesPublicRelationFields.php +14 -1
  264. package/server/src/Http/Resources/v1/Index/Customer.php +18 -0
  265. package/server/src/Http/Resources/v1/Index/Order.php +11 -2
  266. package/server/src/Http/Resources/v1/PublicOrderTracking.php +153 -0
  267. package/server/src/Http/Resources/v1/TrackingNumber.php +8 -34
  268. package/server/src/Http/Resources/v1/Zone.php +31 -23
  269. package/server/src/Jobs/FinalizeApiOrderCreation.php +3 -1
  270. package/server/src/Jobs/ProcessAllocationJob.php +16 -6
  271. package/server/src/Jobs/PublishTrackingUpdate.php +50 -0
  272. package/server/src/Listeners/HandleGeofenceEntered.php +31 -15
  273. package/server/src/Listeners/HandleOrderCanceled.php +9 -1
  274. package/server/src/Listeners/HandleUserRemovedFromCompany.php +2 -1
  275. package/server/src/Listeners/PublishTrackingUpdates.php +27 -0
  276. package/server/src/Mail/CustomerCredentialsMail.php +4 -2
  277. package/server/src/Models/AssetConnection.php +3 -0
  278. package/server/src/Models/Contact.php +3 -0
  279. package/server/src/Models/Device.php +28 -0
  280. package/server/src/Models/DeviceEvent.php +7 -0
  281. package/server/src/Models/Driver.php +20 -7
  282. package/server/src/Models/Entity.php +12 -4
  283. package/server/src/Models/Fleet.php +11 -32
  284. package/server/src/Models/FleetDriver.php +3 -0
  285. package/server/src/Models/FleetVehicle.php +3 -0
  286. package/server/src/Models/FuelReport.php +5 -13
  287. package/server/src/Models/Issue.php +4 -16
  288. package/server/src/Models/Order.php +234 -11
  289. package/server/src/Models/OrderConfig.php +129 -0
  290. package/server/src/Models/Payload.php +227 -35
  291. package/server/src/Models/Place.php +7 -1
  292. package/server/src/Models/ServiceArea.php +7 -0
  293. package/server/src/Models/ServiceRate.php +7 -17
  294. package/server/src/Models/TrackingNumber.php +109 -6
  295. package/server/src/Models/TrackingSession.php +33 -0
  296. package/server/src/Models/TrackingSessionGrant.php +22 -0
  297. package/server/src/Models/TrackingStatus.php +6 -0
  298. package/server/src/Models/Vehicle.php +21 -2
  299. package/server/src/Models/Vendor.php +7 -0
  300. package/server/src/Models/Waypoint.php +13 -4
  301. package/server/src/Notifications/OrderAssigned.php +2 -1
  302. package/server/src/Notifications/OrderCanceled.php +7 -1
  303. package/server/src/Notifications/OrderChanged.php +87 -0
  304. package/server/src/Notifications/OrderCompleted.php +2 -1
  305. package/server/src/Notifications/OrderDispatchFailed.php +2 -1
  306. package/server/src/Notifications/OrderDispatched.php +2 -1
  307. package/server/src/Notifications/OrderFailed.php +2 -1
  308. package/server/src/Notifications/OrderUnassigned.php +66 -0
  309. package/server/src/Notifications/WaypointCompleted.php +2 -1
  310. package/server/src/Observers/CompanyUserObserver.php +8 -7
  311. package/server/src/Observers/DriverObserver.php +6 -1
  312. package/server/src/Observers/OrderObserver.php +141 -1
  313. package/server/src/Observers/TrackingNumberObserver.php +18 -6
  314. package/server/src/Observers/UserObserver.php +2 -1
  315. package/server/src/Observers/VehicleObserver.php +8 -3
  316. package/server/src/Orchestration/Engines/RouteSequencingEngine.php +21 -3
  317. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +38 -8
  318. package/server/src/Orchestration/OrchestrationEngineRegistry.php +61 -1
  319. package/server/src/Orchestration/Support/OrchestratorSettings.php +61 -0
  320. package/server/src/Orchestration/Support/RouteMetrics.php +165 -0
  321. package/server/src/Providers/EventServiceProvider.php +11 -0
  322. package/server/src/Providers/FleetOpsServiceProvider.php +13 -0
  323. package/server/src/Support/ApiCacheThrottle.php +76 -0
  324. package/server/src/Support/LiveOrderQuery.php +3 -1
  325. package/server/src/Support/OrderEdit.php +365 -0
  326. package/server/src/Support/OrderEditPolicy.php +292 -0
  327. package/server/src/Support/ProfileAccountManager.php +54 -1
  328. package/server/src/Support/ResolvesOrderServiceStops.php +5 -0
  329. package/server/src/Support/SocketChannels.php +346 -0
  330. package/server/src/Support/Telematics/TelematicService.php +7 -2
  331. package/server/src/Support/Telematics/Telemetry/Inbox.php +8 -1
  332. package/server/src/Support/TrackingChannel.php +55 -0
  333. package/server/src/Support/TrackingCode.php +242 -0
  334. package/server/src/Support/TrackingPage/TrackingAccess.php +356 -0
  335. package/server/src/Support/TrackingPage/TrackingPageConfig.php +264 -0
  336. package/server/src/Support/TrackingPage/TrackingRecipient.php +111 -0
  337. package/server/src/Support/TrackingPage/TrackingResolver.php +201 -0
  338. package/server/src/Support/TrackingPage/TrackingTarget.php +40 -0
  339. package/server/src/Support/TrackingPage/TrackingView.php +543 -0
  340. package/server/src/Support/TrackingPublisher.php +261 -0
  341. package/server/src/Support/TrackingScope.php +182 -0
  342. package/server/src/Support/TrackingUpdate.php +402 -0
  343. package/server/src/Support/Utils.php +15 -3
  344. package/server/src/Traits/HasTrackingNumber.php +2 -0
  345. package/server/src/routes.php +39 -7
  346. package/server/tests/ApiCustomerControllerContractsTest.php +54 -7
  347. package/server/tests/ApiDriverPasswordContractsTest.php +25 -21
  348. package/server/tests/ApiEntityControllerContractsTest.php +84 -0
  349. package/server/tests/ApiOrderControllerContractsTest.php +245 -0
  350. package/server/tests/ApiPayloadControllerContractsTest.php +121 -0
  351. package/server/tests/ApiPlaceControllerContractsTest.php +1 -1
  352. package/server/tests/ApiTrackingNumberControllerContractsTest.php +6 -9
  353. package/server/tests/CompactResourceSerializationTest.php +23 -59
  354. package/server/tests/ControllerFilterContractsTest.php +1 -1
  355. package/server/tests/CustomerContactIdentitySafetyTest.php +1 -1
  356. package/server/tests/EventContractsTest.php +26 -60
  357. package/server/tests/ExpansionContractsTest.php +15 -2
  358. package/server/tests/Feature/Http/Api/CustomerControllerHelperSeamsTest.php +29 -3
  359. package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +23 -0
  360. package/server/tests/Feature/Http/Api/OrderControllerNearbyFiltersTest.php +16 -16
  361. package/server/tests/Feature/Http/Api/OrderControllerQueryFiltersTest.php +41 -14
  362. package/server/tests/Feature/Http/Api/TrackingNumberControllerHelpersTest.php +57 -22
  363. package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +23 -9
  364. package/server/tests/Feature/Http/Internal/OrchestrationControllerContractsTest.php +171 -1
  365. package/server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php +72 -0
  366. package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +110 -14
  367. package/server/tests/Feature/Http/Internal/OrderControllerCreateRecordTest.php +26 -0
  368. package/server/tests/Feature/Http/Internal/OrderControllerHelpersTest.php +7 -5
  369. package/server/tests/Feature/Http/Internal/PositionControllerReplayChannelTest.php +135 -0
  370. package/server/tests/Feature/Http/Internal/ProofControllerContractsTest.php +3 -3
  371. package/server/tests/Feature/Http/Internal/ProofControllerRecentTest.php +170 -0
  372. package/server/tests/Feature/Http/Public/TrackingControllerTest.php +495 -0
  373. package/server/tests/IndexCustomerPhotoTest.php +151 -0
  374. package/server/tests/ModelAccessorContractsTest.php +4 -3
  375. package/server/tests/NotificationAndMailContractsTest.php +5 -8
  376. package/server/tests/ObserverContractsTest.php +72 -10
  377. package/server/tests/OrderActivityFlowRegressionTest.php +3 -2
  378. package/server/tests/OrderConfigLifecycleTest.php +120 -0
  379. package/server/tests/OrderFilterExecutionTest.php +167 -2
  380. package/server/tests/ProcessAllocationJobContractsTest.php +20 -0
  381. package/server/tests/PublicOrderTrackingResourceTest.php +221 -0
  382. package/server/tests/QueueJobContractsTest.php +2 -1
  383. package/server/tests/RequestContractsTest.php +1 -1
  384. package/server/tests/RouteRegistrationExecutionTest.php +26 -0
  385. package/server/tests/RouteSequencingEngineTest.php +17 -0
  386. package/server/tests/SettingControllerContractsTest.php +110 -0
  387. package/server/tests/Support/TrackingPageDatabase.php +200 -0
  388. package/server/tests/TenantScopingTest.php +206 -0
  389. package/server/tests/TrackingPageSettingsTest.php +343 -0
  390. package/server/tests/Unit/Console/PruneTelematicsDataCommandTest.php +51 -0
  391. package/server/tests/Unit/Events/OrderDispatchFailedTest.php +2 -1
  392. package/server/tests/Unit/Http/Filter/ContactFilterAddressTest.php +61 -0
  393. package/server/tests/Unit/Http/Resources/IndexOrderResourceTest.php +30 -0
  394. package/server/tests/Unit/Jobs/FinalizeApiOrderCreationDispatchGuardTest.php +266 -0
  395. package/server/tests/Unit/Listeners/HandleGeofenceEnteredTest.php +182 -22
  396. package/server/tests/Unit/Listeners/HandleUserRemovedFromCompanyTest.php +5 -0
  397. package/server/tests/Unit/Models/DriverAssignVehicleTest.php +81 -0
  398. package/server/tests/Unit/Models/FleetTest.php +4 -3
  399. package/server/tests/Unit/Models/OrderCancelReasonTest.php +143 -0
  400. package/server/tests/Unit/Models/OrderPayloadConfigAndDistanceTest.php +60 -0
  401. package/server/tests/Unit/Models/OrderPayloadRouteTest.php +4 -2
  402. package/server/tests/Unit/Models/PayloadTest.php +18 -4
  403. package/server/tests/Unit/Models/ServiceRatePersistenceAndServicabilityTest.php +2 -2
  404. package/server/tests/Unit/Models/TrackingNumberTest.php +67 -6
  405. package/server/tests/Unit/Models/VehicleTest.php +16 -2
  406. package/server/tests/Unit/Models/WaypointTest.php +4 -3
  407. package/server/tests/Unit/Notifications/OrderChangeNotificationsTest.php +59 -0
  408. package/server/tests/Unit/Notifications/TerminalOrderNotificationsTest.php +3 -3
  409. package/server/tests/Unit/Observers/DriverDeletionCleanupTest.php +158 -0
  410. package/server/tests/Unit/Observers/FleetOpsCompanyUserAndServiceAreaObserversTest.php +20 -0
  411. package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +3 -3
  412. package/server/tests/Unit/Observers/OrderAssignmentObserverTest.php +100 -0
  413. package/server/tests/Unit/Observers/OrderDeleteCascadeTest.php +213 -0
  414. package/server/tests/Unit/Observers/VehicleObserverDeleteTest.php +67 -0
  415. package/server/tests/Unit/Orchestration/EngineSelectionAndRouteMetricsTest.php +336 -0
  416. package/server/tests/Unit/Orchestration/Engines/VroomOrchestrationEngineLifecycleTest.php +8 -1
  417. package/server/tests/Unit/Support/ApiCacheDependenciesTest.php +347 -0
  418. package/server/tests/Unit/Support/OrderEditPolicyTest.php +156 -0
  419. package/server/tests/Unit/Support/OrderOperationsTest.php +514 -0
  420. package/server/tests/Unit/Support/OsrmProofAndIssueFilterTest.php +4 -3
  421. package/server/tests/Unit/Support/ProfileAccountManagerTest.php +141 -0
  422. package/server/tests/Unit/Support/QuerySeamsTest.php +5 -1
  423. package/server/tests/Unit/Support/SocketChannelsTest.php +396 -0
  424. package/server/tests/Unit/Support/TrackingChannelTest.php +232 -0
  425. package/server/tests/Unit/Support/TrackingCodeTest.php +186 -0
  426. package/server/tests/Unit/Support/TrackingPage/TrackingAccessTest.php +200 -0
  427. package/server/tests/Unit/Support/TrackingPage/TrackingRecipientTest.php +51 -0
  428. package/server/tests/Unit/Support/TrackingPage/TrackingResolverTest.php +181 -0
  429. package/server/tests/Unit/Support/TrackingPage/TrackingViewTest.php +479 -0
  430. package/server/tests/Unit/Support/TrackingPublisherTest.php +425 -0
  431. package/server/tests/Unit/Support/TrackingUpdateTest.php +363 -0
  432. package/server/tests/Unit/Support/UtilsAdditionalTest.php +9 -0
  433. package/server/tests/Unit/Support/UtilsGeoMatrixAndVendorTest.php +25 -0
  434. package/server/tests/VehicleResourceContractsTest.php +46 -0
  435. package/server/tests/VroomOrchestrationEngineTest.php +34 -1
  436. package/server/tests/ZoneGeofenceSettingsResourceTest.php +121 -0
  437. package/translations/ar-ae.yml +274 -0
  438. package/translations/bg-bg.yaml +274 -0
  439. package/translations/en-us.yaml +347 -0
  440. package/translations/es-pa.yaml +274 -0
  441. package/translations/fr-fr.yaml +274 -0
  442. package/translations/mn-mn.yaml +274 -0
  443. package/translations/pt-br.yaml +274 -0
  444. package/translations/ru-ru.yaml +276 -2
  445. package/translations/uk-ua.yaml +275 -1
  446. package/translations/vi-vn.yaml +274 -0
  447. package/addon/components/cell/contact-identity.hbs +0 -1
  448. package/addon/components/cell/customer-identity.hbs +0 -1
  449. package/addon/components/cell/driver-identity.hbs +0 -1
  450. package/addon/components/cell/fleet-identity.hbs +0 -1
  451. package/addon/components/cell/order-identity.hbs +0 -1
  452. package/addon/components/cell/place-identity.hbs +0 -1
  453. package/addon/components/cell/vehicle-identity.hbs +0 -1
  454. package/addon/components/cell/vendor-identity.hbs +0 -1
  455. package/addon/components/contact/pill.hbs +0 -24
  456. package/addon/components/contact/summary.hbs +0 -1
  457. package/addon/components/customer/pill.hbs +0 -24
  458. package/addon/components/customer/summary.hbs +0 -1
  459. package/addon/components/driver/pill.hbs +0 -24
  460. package/addon/components/driver/summary.hbs +0 -1
  461. package/addon/components/fleet/pill.hbs +0 -24
  462. package/addon/components/fleet/summary.hbs +0 -1
  463. package/addon/components/order/pill.hbs +0 -24
  464. package/addon/components/order/summary.hbs +0 -1
  465. package/addon/components/place/pill.hbs +0 -24
  466. package/addon/components/place/summary.hbs +0 -1
  467. package/addon/components/select-option/contact.hbs +0 -12
  468. package/addon/components/select-option/customer.hbs +0 -12
  469. package/addon/components/select-option/driver.hbs +0 -12
  470. package/addon/components/select-option/fleet.hbs +0 -12
  471. package/addon/components/select-option/order.hbs +0 -12
  472. package/addon/components/select-option/place.hbs +0 -12
  473. package/addon/components/select-option/vehicle.hbs +0 -12
  474. package/addon/components/select-option/vendor.hbs +0 -12
  475. package/addon/components/vehicle/pill.hbs +0 -24
  476. package/addon/components/vehicle/summary.hbs +0 -1
  477. package/addon/components/vendor/pill.hbs +0 -24
  478. package/addon/components/vendor/summary.hbs +0 -1
  479. package/app/components/cell/contact-identity.js +0 -1
  480. package/app/components/cell/customer-identity.js +0 -1
  481. package/app/components/cell/driver-identity.js +0 -1
  482. package/app/components/cell/fleet-identity.js +0 -1
  483. package/app/components/cell/order-identity.js +0 -1
  484. package/app/components/cell/place-identity.js +0 -1
  485. package/app/components/cell/vehicle-identity.js +0 -1
  486. package/app/components/cell/vendor-identity.js +0 -1
  487. package/app/components/contact/pill.js +0 -1
  488. package/app/components/contact/summary.js +0 -1
  489. package/app/components/customer/pill.js +0 -1
  490. package/app/components/customer/summary.js +0 -1
  491. package/app/components/driver/pill.js +0 -1
  492. package/app/components/driver/summary.js +0 -1
  493. package/app/components/fleet/pill.js +0 -1
  494. package/app/components/fleet/summary.js +0 -1
  495. package/app/components/order/pill.js +0 -1
  496. package/app/components/order/summary.js +0 -1
  497. package/app/components/place/pill.js +0 -1
  498. package/app/components/place/summary.js +0 -1
  499. package/app/components/select-option/contact.js +0 -1
  500. package/app/components/select-option/customer.js +0 -1
  501. package/app/components/select-option/driver.js +0 -1
  502. package/app/components/select-option/fleet.js +0 -1
  503. package/app/components/select-option/order.js +0 -1
  504. package/app/components/select-option/place.js +0 -1
  505. package/app/components/select-option/vehicle.js +0 -1
  506. package/app/components/select-option/vendor.js +0 -1
  507. package/app/components/vehicle/pill.js +0 -1
  508. package/app/components/vehicle/summary.js +0 -1
  509. package/app/components/vendor/pill.js +0 -1
  510. package/app/components/vendor/summary.js +0 -1
@@ -0,0 +1,22 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Models;
4
+
5
+ use Fleetbase\Models\Model;
6
+ use Fleetbase\Traits\HasUuid;
7
+
8
+ /**
9
+ * What a verified device may see: one customer within one order, until `expires_at`.
10
+ */
11
+ class TrackingSessionGrant extends Model
12
+ {
13
+ use HasUuid;
14
+
15
+ protected $table = 'tracking_session_grants';
16
+
17
+ protected $fillable = ['tracking_session_uuid', 'company_uuid', 'order_uuid', 'customer_type', 'customer_uuid', 'channel', 'expires_at'];
18
+
19
+ protected $casts = [
20
+ 'expires_at' => 'datetime',
21
+ ];
22
+ }
@@ -7,6 +7,7 @@ use Fleetbase\FleetOps\Casts\Point;
7
7
  use Fleetbase\FleetOps\Support\Utils;
8
8
  use Fleetbase\LaravelMysqlSpatial\Eloquent\SpatialTrait;
9
9
  use Fleetbase\Models\Model;
10
+ use Fleetbase\Support\ApiModelCache;
10
11
  use Fleetbase\Traits\HasApiModelBehavior;
11
12
  use Fleetbase\Traits\HasMetaAttributes;
12
13
  use Fleetbase\Traits\HasPublicId;
@@ -177,6 +178,11 @@ class TrackingStatus extends Model
177
178
 
178
179
  $result = static::insertTrackingStatus($values);
179
180
 
181
+ // Inserted without model events
182
+ if ($result) {
183
+ ApiModelCache::invalidateModelCache($instance, $values['company_uuid']);
184
+ }
185
+
180
186
  return $result ? $uuid : false;
181
187
  }
182
188
 
@@ -81,6 +81,23 @@ class Vehicle extends Model
81
81
  */
82
82
  protected $with = [];
83
83
 
84
+ /**
85
+ * Relation paths the cached vehicle lists embed or filter on (fleetbase/core-api#301).
86
+ *
87
+ * The public list loads vendor (with its place), category, warranty and
88
+ * driver; driver, current trailers (through asset_connections) and fleets
89
+ * (through fleet_vehicles) decide which vehicles the filters return.
90
+ * photo is left out: files is written by every upload, and a new photo is
91
+ * set by writing the vehicle's own photo_uuid. devices is left out too: the
92
+ * device filters only read which devices are attached, but telemetry saves
93
+ * the device row on every ping, which would keep vehicle lists from ever
94
+ * being served from cache. Device::attachTo() and detach() invalidate the
95
+ * vehicle lists instead.
96
+ *
97
+ * @var string[]
98
+ */
99
+ protected $apiCacheDependencies = ['vendor', 'vendor.place', 'category', 'warranty', 'driver', 'currentTrailers', 'fleets'];
100
+
84
101
  /**
85
102
  * Get the activity log options for the model.
86
103
  */
@@ -525,7 +542,8 @@ class Vehicle extends Model
525
542
  return static::getAvatar($value);
526
543
  }
527
544
 
528
- return $value;
545
+ // legacy rows hold an absolute URL; re-sign it if it points into the private media bucket
546
+ return Utils::signStoredFileUrl($value);
529
547
  }
530
548
 
531
549
  /**
@@ -628,6 +646,7 @@ class Vehicle extends Model
628
646
  public function unassignDriver(): self
629
647
  {
630
648
  Driver::where('vehicle_uuid', $this->uuid)->update(['vehicle_uuid' => null]);
649
+ Driver::invalidateApiCacheManually($this->company_uuid);
631
650
  $this->unsetRelation('driver');
632
651
 
633
652
  return $this;
@@ -794,7 +813,7 @@ class Vehicle extends Model
794
813
  return null;
795
814
  }
796
815
 
797
- return static::where(function ($query) use ($vehicleName) {
816
+ return static::where('company_uuid', session('company'))->where(function ($query) use ($vehicleName) {
798
817
  $query->where('public_id', $vehicleName)
799
818
  ->orWhere('internal_id', $vehicleName)
800
819
  ->orWhere('plate_number', $vehicleName)
@@ -117,6 +117,13 @@ class Vendor extends Model
117
117
  */
118
118
  protected $with = ['place'];
119
119
 
120
+ /**
121
+ * Relation paths the cached vendor lists embed through `$with` (fleetbase/core-api#301).
122
+ *
123
+ * @var string[]
124
+ */
125
+ protected $apiCacheDependencies = ['place'];
126
+
120
127
  /**
121
128
  * Get the activity log options for the model.
122
129
  */
@@ -8,6 +8,7 @@ use Fleetbase\FleetOps\Support\Utils;
8
8
  use Fleetbase\FleetOps\Traits\HasTrackingNumber;
9
9
  use Fleetbase\FleetOps\Traits\PayloadAccessors;
10
10
  use Fleetbase\Models\Model;
11
+ use Fleetbase\Traits\HasApiModelCache;
11
12
  use Fleetbase\Traits\HasPublicId;
12
13
  use Fleetbase\Traits\HasUuid;
13
14
  use Fleetbase\Traits\TracksApiCredential;
@@ -20,6 +21,8 @@ use Illuminate\Support\Str;
20
21
  class Waypoint extends Model
21
22
  {
22
23
  use HasUuid;
24
+ // Writes bump the waypoints API cache version, which cached order lists embed.
25
+ use HasApiModelCache;
23
26
  use HasPublicId;
24
27
  use TracksApiCredential;
25
28
  use HasTrackingNumber;
@@ -221,16 +224,22 @@ class Waypoint extends Model
221
224
  if ($result && $payload) {
222
225
  // create tracking number for entity
223
226
  $trackingNumberId = static::createTrackingNumber([
224
- 'owner_uuid' => $uuid,
225
- 'owner_type' => Utils::getModelClassName('waypoint'),
226
- 'region' => $payload->getPickupRegion(),
227
- 'location' => static::pickupLocationWkt($payload),
227
+ 'owner_uuid' => $uuid,
228
+ 'owner_type' => Utils::getModelClassName('waypoint'),
229
+ 'owner_public_id' => $values['public_id'],
230
+ 'region' => $payload->getPickupRegion(),
231
+ 'location' => static::pickupLocationWkt($payload),
228
232
  ]);
229
233
 
230
234
  // set tracking number
231
235
  static::updateTrackingNumberUuid($uuid, $trackingNumberId);
232
236
  }
233
237
 
238
+ // Inserted and updated without model events
239
+ if ($result) {
240
+ static::invalidateApiCacheManually($values['company_uuid']);
241
+ }
242
+
234
243
  return $result ? $uuid : false;
235
244
  }
236
245
 
@@ -4,6 +4,7 @@ namespace Fleetbase\FleetOps\Notifications;
4
4
 
5
5
  use Fleetbase\FleetOps\Http\Resources\v1\Order as OrderResource;
6
6
  use Fleetbase\FleetOps\Models\Order;
7
+ use Fleetbase\FleetOps\Support\TrackingCode;
7
8
  use Fleetbase\FleetOps\Support\Utils;
8
9
  use Fleetbase\Support\PushNotification;
9
10
  use Illuminate\Broadcasting\Channel;
@@ -128,7 +129,7 @@ class OrderAssigned extends Notification implements ShouldQueue
128
129
  $message->line('Dispatch is scheduled for ' . $this->order->scheduled_at);
129
130
  }
130
131
 
131
- $message->action('Track Order', Utils::consoleUrl('track-order', ['order' => $this->order->trackingNumber->tracking_number]));
132
+ $message->action('Track Order', TrackingCode::pageUrl($this->order->trackingNumber->tracking_number, data_get($this, 'order.company_uuid')));
132
133
 
133
134
  return $message;
134
135
  }
@@ -5,6 +5,7 @@ namespace Fleetbase\FleetOps\Notifications;
5
5
  use Fleetbase\FleetOps\Http\Resources\v1\Order as OrderResource;
6
6
  use Fleetbase\FleetOps\Models\Order;
7
7
  use Fleetbase\FleetOps\Models\Waypoint;
8
+ use Fleetbase\FleetOps\Support\TrackingCode;
8
9
  use Fleetbase\FleetOps\Support\Utils;
9
10
  use Fleetbase\Support\PushNotification;
10
11
  use Illuminate\Broadcasting\Channel;
@@ -81,6 +82,11 @@ class OrderCanceled extends Notification implements ShouldQueue
81
82
  $this->title = 'Order ' . $this->getTrackingNumber() . ' was canceled';
82
83
  $this->message = 'Order ' . $this->getTrackingNumber() . ' has been canceled.';
83
84
  $this->data = ['id' => $this->order->public_id, 'type' => 'order_canceled'];
85
+
86
+ // apps show why the order was canceled when a reason was given
87
+ if ($reason !== '') {
88
+ $this->data['reason'] = $reason;
89
+ }
84
90
  }
85
91
 
86
92
  /**
@@ -138,7 +144,7 @@ class OrderCanceled extends Notification implements ShouldQueue
138
144
  ->line($this->message)
139
145
  ->line($this->reason)
140
146
  ->line('No further action is necessary.')
141
- ->action('Track Order', Utils::consoleUrl('track-order', ['order' => $this->getTrackingNumber()]));
147
+ ->action('Track Order', TrackingCode::pageUrl($this->getTrackingNumber(), data_get($this, 'order.company_uuid')));
142
148
  }
143
149
 
144
150
  /**
@@ -0,0 +1,87 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Notifications;
4
+
5
+ use Fleetbase\FleetOps\Models\Order;
6
+ use Fleetbase\Support\PushNotification;
7
+ use Illuminate\Broadcasting\Channel;
8
+ use Illuminate\Bus\Queueable;
9
+ use Illuminate\Contracts\Queue\ShouldQueue;
10
+ use Illuminate\Notifications\Notification;
11
+ use NotificationChannels\Apn\ApnChannel;
12
+ use NotificationChannels\Fcm\FcmChannel;
13
+
14
+ /**
15
+ * Tells the assigned driver that an order they have changed.
16
+ *
17
+ * Push data: { type: "order_changed", id: order public id, changes: [...] }.
18
+ */
19
+ class OrderChanged extends Notification implements ShouldQueue
20
+ {
21
+ use Queueable;
22
+
23
+ public static string $name = 'Order Changed';
24
+ public static string $description = 'Notify the assigned driver when an order they have is changed.';
25
+ public static string $package = 'fleet-ops';
26
+
27
+ public Order $order;
28
+
29
+ public array $changes = [];
30
+
31
+ public string $title;
32
+
33
+ public string $message;
34
+
35
+ public array $data = [];
36
+
37
+ public function __construct(Order $order, array $changes = [], string $summary = '')
38
+ {
39
+ $this->order = $order;
40
+ $this->changes = array_values($changes);
41
+ $this->title = 'Order ' . (data_get($order, 'trackingNumber.tracking_number') ?? $order->public_id) . ' changed';
42
+ $this->message = $summary !== '' ? $summary : 'This order was changed, tap for details.';
43
+ $this->data = ['type' => 'order_changed', 'id' => $order->public_id, 'changes' => $this->changes];
44
+ }
45
+
46
+ public function via($notifiable): array
47
+ {
48
+ return ['broadcast', FcmChannel::class, ApnChannel::class];
49
+ }
50
+
51
+ public function broadcastOn(): array
52
+ {
53
+ return [
54
+ new Channel('driver.' . data_get($this->order, 'driver_assigned_uuid')),
55
+ new Channel('driver.' . data_get($this->order, 'driverAssigned.public_id')),
56
+ ];
57
+ }
58
+
59
+ public function toArray($notifiable = null): array
60
+ {
61
+ return [
62
+ 'event' => 'order.changed_notification',
63
+ 'title' => $this->title,
64
+ 'body' => $this->message,
65
+ 'data' => $this->data,
66
+ ];
67
+ }
68
+
69
+ public function toFcm($notifiable)
70
+ {
71
+ return PushNotification::createFcmMessage($this->title, $this->message, $this->fcmData());
72
+ }
73
+
74
+ public function toApn($notifiable)
75
+ {
76
+ return PushNotification::createApnMessage($this->title, $this->message, $this->data, 'view_order');
77
+ }
78
+
79
+ /**
80
+ * FCM data values must be strings, so on FCM the change list travels comma
81
+ * separated ("dropoff,items"); APNs and the broadcast carry it as a list.
82
+ */
83
+ public function fcmData(): array
84
+ {
85
+ return array_merge($this->data, ['changes' => implode(',', $this->changes)]);
86
+ }
87
+ }
@@ -5,6 +5,7 @@ namespace Fleetbase\FleetOps\Notifications;
5
5
  use Fleetbase\FleetOps\Http\Resources\v1\Order as OrderResource;
6
6
  use Fleetbase\FleetOps\Models\Order;
7
7
  use Fleetbase\FleetOps\Models\Waypoint;
8
+ use Fleetbase\FleetOps\Support\TrackingCode;
8
9
  use Fleetbase\FleetOps\Support\Utils;
9
10
  use Fleetbase\Support\PushNotification;
10
11
  use Illuminate\Broadcasting\Channel;
@@ -131,7 +132,7 @@ class OrderCompleted extends Notification implements ShouldQueue
131
132
  ->subject($this->title)
132
133
  ->line($this->message)
133
134
  ->line('No further action is necessary.')
134
- ->action('Track Order', Utils::consoleUrl('track-order', ['order' => $this->getTrackingNumber()]));
135
+ ->action('Track Order', TrackingCode::pageUrl($this->getTrackingNumber(), data_get($this, 'order.company_uuid')));
135
136
  }
136
137
 
137
138
  /**
@@ -5,6 +5,7 @@ namespace Fleetbase\FleetOps\Notifications;
5
5
  use Fleetbase\FleetOps\Events\OrderDispatchFailed as OrderDispatchFailedEvent;
6
6
  use Fleetbase\FleetOps\Http\Resources\v1\Order as OrderResource;
7
7
  use Fleetbase\FleetOps\Models\Order;
8
+ use Fleetbase\FleetOps\Support\TrackingCode;
8
9
  use Fleetbase\FleetOps\Support\Utils;
9
10
  use Illuminate\Broadcasting\Channel;
10
11
  use Illuminate\Bus\Queueable;
@@ -127,6 +128,6 @@ class OrderDispatchFailed extends Notification implements ShouldQueue
127
128
  return (new MailMessage())
128
129
  ->subject($this->title)
129
130
  ->line($this->message)
130
- ->action('Track Order', Utils::consoleUrl('track-order', ['order' => $this->order->trackingNumber->tracking_number]));
131
+ ->action('Track Order', TrackingCode::pageUrl($this->order->trackingNumber->tracking_number, data_get($this, 'order.company_uuid')));
131
132
  }
132
133
  }
@@ -6,6 +6,7 @@ use Fleetbase\Events\ResourceLifecycleEvent;
6
6
  use Fleetbase\FleetOps\Http\Resources\v1\Order as OrderResource;
7
7
  use Fleetbase\FleetOps\Models\Order;
8
8
  use Fleetbase\FleetOps\Models\Waypoint;
9
+ use Fleetbase\FleetOps\Support\TrackingCode;
9
10
  use Fleetbase\FleetOps\Support\Utils;
10
11
  use Fleetbase\Support\PushNotification;
11
12
  use Illuminate\Broadcasting\Channel;
@@ -170,7 +171,7 @@ class OrderDispatched extends Notification implements ShouldQueue
170
171
  return (new MailMessage())
171
172
  ->subject($this->title)
172
173
  ->line($this->message)
173
- ->action('Track Order', Utils::consoleUrl('track-order', ['order' => $this->getTrackingNumber()]));
174
+ ->action('Track Order', TrackingCode::pageUrl($this->getTrackingNumber(), data_get($this, 'order.company_uuid')));
174
175
  }
175
176
 
176
177
  /**
@@ -5,6 +5,7 @@ namespace Fleetbase\FleetOps\Notifications;
5
5
  use Fleetbase\FleetOps\Http\Resources\v1\Order as OrderResource;
6
6
  use Fleetbase\FleetOps\Models\Order;
7
7
  use Fleetbase\FleetOps\Models\Waypoint;
8
+ use Fleetbase\FleetOps\Support\TrackingCode;
8
9
  use Fleetbase\FleetOps\Support\Utils;
9
10
  use Fleetbase\Support\PushNotification;
10
11
  use Illuminate\Broadcasting\Channel;
@@ -138,7 +139,7 @@ class OrderFailed extends Notification implements ShouldQueue
138
139
  ->line($this->message)
139
140
  ->line($this->reason)
140
141
  ->line('No further action is necessary.')
141
- ->action('Track Order', Utils::consoleUrl('track-order', ['order' => $this->getTrackingNumber()]));
142
+ ->action('Track Order', TrackingCode::pageUrl($this->getTrackingNumber(), data_get($this, 'order.company_uuid')));
142
143
  }
143
144
 
144
145
  /**
@@ -0,0 +1,66 @@
1
+ <?php
2
+
3
+ namespace Fleetbase\FleetOps\Notifications;
4
+
5
+ use Fleetbase\FleetOps\Models\Order;
6
+ use Fleetbase\Support\PushNotification;
7
+ use Illuminate\Bus\Queueable;
8
+ use Illuminate\Contracts\Queue\ShouldQueue;
9
+ use Illuminate\Notifications\Notification;
10
+ use NotificationChannels\Apn\ApnChannel;
11
+ use NotificationChannels\Fcm\FcmChannel;
12
+
13
+ /**
14
+ * Tells a driver that an order was taken off them (unassigned or reassigned).
15
+ *
16
+ * Push data: { type: "order_unassigned", id: order public id }.
17
+ */
18
+ class OrderUnassigned extends Notification implements ShouldQueue
19
+ {
20
+ use Queueable;
21
+
22
+ public static string $name = 'Order Unassigned';
23
+ public static string $description = 'Notify a driver when an order is unassigned from them or reassigned to another driver.';
24
+ public static string $package = 'fleet-ops';
25
+
26
+ public Order $order;
27
+
28
+ public string $title;
29
+
30
+ public string $message;
31
+
32
+ public array $data = [];
33
+
34
+ public function __construct(Order $order)
35
+ {
36
+ $this->order = $order;
37
+ $this->title = 'Order ' . (data_get($order, 'trackingNumber.tracking_number') ?? $order->public_id) . ' unassigned';
38
+ $this->message = 'This order is no longer assigned to you.';
39
+ $this->data = ['type' => 'order_unassigned', 'id' => $order->public_id];
40
+ }
41
+
42
+ public function via($notifiable): array
43
+ {
44
+ return [FcmChannel::class, ApnChannel::class];
45
+ }
46
+
47
+ public function toArray($notifiable = null): array
48
+ {
49
+ return [
50
+ 'event' => 'order.unassigned_notification',
51
+ 'title' => $this->title,
52
+ 'body' => $this->message,
53
+ 'data' => $this->data,
54
+ ];
55
+ }
56
+
57
+ public function toFcm($notifiable)
58
+ {
59
+ return PushNotification::createFcmMessage($this->title, $this->message, $this->data);
60
+ }
61
+
62
+ public function toApn($notifiable)
63
+ {
64
+ return PushNotification::createApnMessage($this->title, $this->message, $this->data, 'view_order');
65
+ }
66
+ }
@@ -5,6 +5,7 @@ namespace Fleetbase\FleetOps\Notifications;
5
5
  use Fleetbase\FleetOps\Flow\Activity;
6
6
  use Fleetbase\FleetOps\Models\Order;
7
7
  use Fleetbase\FleetOps\Models\Waypoint;
8
+ use Fleetbase\FleetOps\Support\TrackingCode;
8
9
  use Fleetbase\FleetOps\Support\Utils;
9
10
  use Fleetbase\Support\PushNotification;
10
11
  use Illuminate\Broadcasting\Channel;
@@ -132,7 +133,7 @@ class WaypointCompleted extends Notification implements ShouldQueue
132
133
  ->subject($this->title)
133
134
  ->line($this->message)
134
135
  ->line('No further action is necessary.')
135
- ->action('Track Order', Utils::consoleUrl('track-order', ['order' => $this->waypoint->trackingNumber->tracking_number]));
136
+ ->action('Track Order', TrackingCode::pageUrl($this->waypoint->trackingNumber->tracking_number, data_get($this, 'waypoint.company_uuid')));
136
137
  }
137
138
 
138
139
  /**
@@ -14,8 +14,8 @@ class CompanyUserObserver
14
14
  */
15
15
  public function deleted(CompanyUser $companyUser)
16
16
  {
17
- // if the company user deleted is a driver, delete their driver record to
18
- $this->deleteDrivers($companyUser->user_uuid);
17
+ // if the company user deleted is a driver, delete their driver record in that company
18
+ $this->deleteDrivers($companyUser->user_uuid, $companyUser->company_uuid);
19
19
  }
20
20
 
21
21
  /**
@@ -26,19 +26,20 @@ class CompanyUserObserver
26
26
  public function updated(CompanyUser $companyUser)
27
27
  {
28
28
  // If the company user has any driver assosciated update status to same as company_users
29
- $driver = $this->findDriver($companyUser->user_uuid);
29
+ $driver = $this->findDriver($companyUser->user_uuid, $companyUser->company_uuid);
30
30
  if ($driver && $companyUser->wasChanged('status')) {
31
31
  $driver->update(['status' => $companyUser->status]);
32
32
  }
33
33
  }
34
34
 
35
- protected function deleteDrivers(string $userUuid): void
35
+ protected function deleteDrivers(string $userUuid, ?string $companyUuid): void
36
36
  {
37
- Driver::where('user_uuid', $userUuid)->delete();
37
+ // Delete each driver through the model so DriverObserver cleans up after it
38
+ Driver::where(['user_uuid' => $userUuid, 'company_uuid' => $companyUuid])->get()->each->delete();
38
39
  }
39
40
 
40
- protected function findDriver(string $userUuid): ?Driver
41
+ protected function findDriver(string $userUuid, ?string $companyUuid): ?Driver
41
42
  {
42
- return Driver::where('user_uuid', $userUuid)->first();
43
+ return Driver::where(['user_uuid' => $userUuid, 'company_uuid' => $companyUuid])->first();
43
44
  }
44
45
  }
@@ -79,7 +79,12 @@ class DriverObserver
79
79
 
80
80
  protected function unassignOrders(Driver $driver): int
81
81
  {
82
- return Order::where(['driver_assigned_uuid' => $driver->uuid])->update(['driver_assigned_uuid' => null]);
82
+ $unassigned = Order::where(['driver_assigned_uuid' => $driver->uuid])->update(['driver_assigned_uuid' => null]);
83
+ if ($unassigned > 0) {
84
+ Order::invalidateApiCacheManually($driver->company_uuid);
85
+ }
86
+
87
+ return $unassigned;
83
88
  }
84
89
 
85
90
  protected function findDriverUser(Driver $driver): ?User
@@ -2,12 +2,28 @@
2
2
 
3
3
  namespace Fleetbase\FleetOps\Observers;
4
4
 
5
+ use Fleetbase\FleetOps\Models\Entity;
5
6
  use Fleetbase\FleetOps\Models\Order;
7
+ use Fleetbase\FleetOps\Models\Payload;
8
+ use Fleetbase\FleetOps\Models\TrackingNumber;
9
+ use Fleetbase\FleetOps\Models\TrackingStatus;
10
+ use Fleetbase\FleetOps\Models\Waypoint;
6
11
  use Fleetbase\FleetOps\Support\LiveCacheService;
12
+ use Fleetbase\FleetOps\Support\TrackingPublisher;
13
+ use Fleetbase\Support\ApiModelCache;
7
14
  use Illuminate\Support\Facades\Cache;
8
15
 
9
16
  class OrderObserver
10
17
  {
18
+ /**
19
+ * Deletion timestamps captured while an order is being restored, keyed by order uuid.
20
+ * Restoring clears the order's deleted_at before "restored" fires, so the timestamp the
21
+ * children were deleted with is read in "restoring" and used once the restore succeeds.
22
+ *
23
+ * @var array<string, string>
24
+ */
25
+ protected static array $restoringFrom = [];
26
+
11
27
  /**
12
28
  * Handle the Order "created" event.
13
29
  *
@@ -42,10 +58,21 @@ class OrderObserver
42
58
  $order->setDriverLocationAsPickup();
43
59
 
44
60
  if ($order->wasChanged('driver_assigned_uuid')) {
45
- $order->notifyDriverAssigned();
61
+ // The driver taken off the order hears about it, whether it was unassigned or reassigned.
62
+ $previousDriverUuid = $order->getOriginal('driver_assigned_uuid');
63
+ if ($previousDriverUuid) {
64
+ $order->notifyDriverUnassigned($previousDriverUuid);
65
+ }
66
+
67
+ if (!$order->silentDriverAssignment) {
68
+ $order->notifyDriverAssigned();
69
+ }
46
70
  }
47
71
 
48
72
  $this->invalidateCache($order);
73
+
74
+ // Public tracking channels follow status, assignment and ETA changes.
75
+ app(TrackingPublisher::class)->orderChanged($order);
49
76
  }
50
77
 
51
78
  /**
@@ -59,9 +86,122 @@ class OrderObserver
59
86
  $order->facilitator->provider()->callback('onDeleted', $order);
60
87
  }
61
88
 
89
+ $this->cascadeDelete($order);
90
+ $this->invalidateCache($order);
91
+ }
92
+
93
+ /**
94
+ * Handle the Order "restoring" event: remember when the order was deleted.
95
+ */
96
+ public function restoring(Order $order): void
97
+ {
98
+ if ($order->uuid && $order->deleted_at) {
99
+ static::$restoringFrom[$order->uuid] = $order->fromDateTime($order->deleted_at);
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Handle the Order "restored" event: bring back the children deleted with the order.
105
+ */
106
+ public function restored(Order $order): void
107
+ {
108
+ $deletedAt = static::$restoringFrom[$order->uuid] ?? null;
109
+ unset(static::$restoringFrom[$order->uuid]);
110
+
111
+ if ($deletedAt) {
112
+ $this->cascadeRestore($order, $deletedAt);
113
+ }
114
+
62
115
  $this->invalidateCache($order);
63
116
  }
64
117
 
118
+ /**
119
+ * Soft-deletes what belongs to the order: its tracking number and, unless another live
120
+ * order shares it, its payload with the payload's entities and waypoints and their
121
+ * tracking numbers, plus every one of those tracking numbers' statuses. Children are
122
+ * stamped with the order's own deleted_at so a restore can find exactly those rows.
123
+ */
124
+ protected function cascadeDelete(Order $order): void
125
+ {
126
+ $deletedAt = $order->fromDateTime($order->deleted_at ?? $order->freshTimestamp());
127
+ $tree = $this->childTree($order, Payload::query(), Entity::query(), Waypoint::query());
128
+
129
+ if ($tree['payload']) {
130
+ Payload::where('uuid', $tree['payload'])->update(['deleted_at' => $deletedAt]);
131
+ Entity::whereIn('uuid', $tree['entities'])->update(['deleted_at' => $deletedAt]);
132
+ Waypoint::whereIn('uuid', $tree['waypoints'])->update(['deleted_at' => $deletedAt]);
133
+ }
134
+
135
+ $trackingNumbers = TrackingNumber::where(fn ($query) => $query->whereIn('uuid', $tree['tracking_numbers'])->orWhereIn('owner_uuid', $tree['owners']))->pluck('uuid')->all();
136
+ TrackingStatus::whereIn('tracking_number_uuid', $trackingNumbers)->update(['deleted_at' => $deletedAt]);
137
+ TrackingNumber::whereIn('uuid', $trackingNumbers)->update(['deleted_at' => $deletedAt]);
138
+
139
+ $this->invalidateChildApiCaches($order);
140
+ }
141
+
142
+ /**
143
+ * Restores the children cascadeDelete() removed: the rows of the same tree whose
144
+ * deleted_at matches the order's. Children deleted on their own beforehand carry
145
+ * a different timestamp and stay deleted.
146
+ */
147
+ protected function cascadeRestore(Order $order, string $deletedAt): void
148
+ {
149
+ $trashed = fn (string $model) => $model::onlyTrashed()->where('deleted_at', $deletedAt);
150
+ $tree = $this->childTree($order, $trashed(Payload::class), $trashed(Entity::class), $trashed(Waypoint::class));
151
+
152
+ $trackingNumbers = $trashed(TrackingNumber::class)->where(fn ($query) => $query->whereIn('uuid', $tree['tracking_numbers'])->orWhereIn('owner_uuid', $tree['owners']))->pluck('uuid')->all();
153
+ $trashed(TrackingNumber::class)->whereIn('uuid', $trackingNumbers)->update(['deleted_at' => null]);
154
+ $trashed(TrackingStatus::class)->whereIn('tracking_number_uuid', $trackingNumbers)->update(['deleted_at' => null]);
155
+
156
+ if ($tree['payload']) {
157
+ $trashed(Payload::class)->where('uuid', $tree['payload'])->update(['deleted_at' => null]);
158
+ $trashed(Entity::class)->whereIn('uuid', $tree['entities'])->update(['deleted_at' => null]);
159
+ $trashed(Waypoint::class)->whereIn('uuid', $tree['waypoints'])->update(['deleted_at' => null]);
160
+ }
161
+
162
+ $this->invalidateChildApiCaches($order);
163
+ }
164
+
165
+ /**
166
+ * The cascades write their tables without model events, so invalidate the API
167
+ * caches of every table they touch.
168
+ */
169
+ protected function invalidateChildApiCaches(Order $order): void
170
+ {
171
+ foreach ([Payload::class, Entity::class, Waypoint::class, TrackingNumber::class, TrackingStatus::class] as $modelClass) {
172
+ ApiModelCache::invalidateModelCache(new $modelClass(), $order->company_uuid);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Collects the uuids under an order from the given payload, entity and waypoint
178
+ * queries. The payload is left out when another live order still references it.
179
+ *
180
+ * @return array{payload: ?string, entities: array, waypoints: array, tracking_numbers: array, owners: array}
181
+ */
182
+ protected function childTree(Order $order, $payloads, $entities, $waypoints): array
183
+ {
184
+ $tree = ['payload' => null, 'entities' => [], 'waypoints' => [], 'tracking_numbers' => [$order->tracking_number_uuid], 'owners' => [$order->uuid]];
185
+
186
+ $shared = $order->payload_uuid && Order::where('payload_uuid', $order->payload_uuid)->where('uuid', '!=', $order->uuid)->exists();
187
+ $payload = $order->payload_uuid && !$shared ? $payloads->where('uuid', $order->payload_uuid)->first() : null;
188
+
189
+ if ($payload) {
190
+ $children = $entities->where('payload_uuid', $payload->uuid)->get(['uuid', 'tracking_number_uuid'])
191
+ ->concat($waypoints->where('payload_uuid', $payload->uuid)->get(['uuid', 'tracking_number_uuid']));
192
+
193
+ $tree['payload'] = $payload->uuid;
194
+ $tree['entities'] = $children->whereInstanceOf(Entity::class)->pluck('uuid')->all();
195
+ $tree['waypoints'] = $children->whereInstanceOf(Waypoint::class)->pluck('uuid')->all();
196
+ $tree['tracking_numbers'] = array_merge($tree['tracking_numbers'], [$payload->pickup_tracking_number_uuid, $payload->dropoff_tracking_number_uuid, $payload->return_tracking_number_uuid], $children->pluck('tracking_number_uuid')->all());
197
+ $tree['owners'] = array_merge($tree['owners'], $children->pluck('uuid')->all());
198
+ }
199
+
200
+ $tree['tracking_numbers'] = array_values(array_filter($tree['tracking_numbers']));
201
+
202
+ return $tree;
203
+ }
204
+
65
205
  /**
66
206
  * Invalidate relevant cache tags for live endpoints.
67
207
  *