@fleetbase/fleetops-engine 0.6.68 → 0.6.70

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 (294) hide show
  1. package/RELEASE.md +44 -9
  2. package/addon/components/admin/telematics-settings.hbs +143 -0
  3. package/addon/components/admin/telematics-settings.js +145 -0
  4. package/addon/components/contact/form.hbs +13 -2
  5. package/addon/components/customer/details.hbs +17 -17
  6. package/addon/components/customer/form.hbs +13 -2
  7. package/addon/components/customer/form.js +0 -56
  8. package/addon/components/driver/details.hbs +13 -19
  9. package/addon/components/driver/form.hbs +20 -35
  10. package/addon/components/driver/form.js +0 -58
  11. package/addon/components/fuel-integration/hub.hbs +1 -1
  12. package/addon/components/layout/fleet-ops-sidebar.js +37 -17
  13. package/addon/components/map/leaflet-live-map.hbs +4 -242
  14. package/addon/components/map/leaflet-live-map.js +2 -1
  15. package/addon/components/map/marker-card/driver.hbs +57 -0
  16. package/addon/components/map/marker-card/driver.js +3 -0
  17. package/addon/components/map/marker-card/vehicle.hbs +78 -0
  18. package/addon/components/map/marker-card/vehicle.js +3 -0
  19. package/addon/components/modals/driver-form.hbs +2 -2
  20. package/addon/components/modals/reset-profile-credentials.hbs +23 -0
  21. package/addon/components/modals/reset-profile-credentials.js +70 -0
  22. package/addon/components/profile-identity-hint.hbs +13 -0
  23. package/addon/components/profile-identity-hint.js +49 -0
  24. package/addon/components/telematic/hub.hbs +1 -1
  25. package/addon/components/widget/live-fleet.hbs +8 -40
  26. package/addon/controllers/analytics/reports/index.js +5 -0
  27. package/addon/controllers/connectivity/devices/index.js +4 -0
  28. package/addon/controllers/connectivity/fuel-providers/index.js +6 -4
  29. package/addon/controllers/connectivity/sensors/index.js +4 -0
  30. package/addon/controllers/connectivity/telematics/index.js +4 -0
  31. package/addon/controllers/maintenance/equipment/index.js +11 -4
  32. package/addon/controllers/maintenance/inspection-forms/index.js +2 -2
  33. package/addon/controllers/maintenance/inspection-submissions/index.js +8 -2
  34. package/addon/controllers/maintenance/maintenances/index.js +4 -0
  35. package/addon/controllers/maintenance/parts/index.js +11 -4
  36. package/addon/controllers/maintenance/schedules/index.js +17 -4
  37. package/addon/controllers/maintenance/work-orders/index.js +11 -4
  38. package/addon/controllers/management/contacts/index.js +4 -0
  39. package/addon/controllers/management/drivers/index.js +8 -0
  40. package/addon/controllers/management/fleets/index.js +4 -0
  41. package/addon/controllers/management/fuel-reports/index.js +4 -0
  42. package/addon/controllers/management/fuel-transactions/index.js +9 -7
  43. package/addon/controllers/management/issues/index.js +4 -0
  44. package/addon/controllers/management/places/index.js +4 -0
  45. package/addon/controllers/management/vehicles/index.js +4 -0
  46. package/addon/controllers/management/vendors/index.js +4 -0
  47. package/addon/controllers/operations/orders/index/details.js +30 -1
  48. package/addon/controllers/operations/orders/index.js +7 -0
  49. package/addon/controllers/operations/service-rates/index.js +3 -0
  50. package/addon/controllers/settings/index.js +13 -0
  51. package/addon/controllers/settings/telematics.js +113 -0
  52. package/addon/extension.js +42 -8
  53. package/addon/routes/analytics/index.js +14 -1
  54. package/addon/routes/analytics/reports/index/details.js +1 -1
  55. package/addon/routes/analytics/reports/index/edit.js +1 -1
  56. package/addon/routes/analytics/reports/index/new.js +14 -1
  57. package/addon/routes/analytics/reports.js +14 -1
  58. package/addon/routes/connectivity/devices/index/new.js +14 -1
  59. package/addon/routes/connectivity/devices.js +14 -1
  60. package/addon/routes/connectivity/events.js +14 -1
  61. package/addon/routes/connectivity/fuel-providers/details/transactions.js +12 -0
  62. package/addon/routes/connectivity/fuel-providers/details.js +9 -0
  63. package/addon/routes/connectivity/fuel-providers/edit.js +9 -0
  64. package/addon/routes/connectivity/fuel-providers/new.js +14 -1
  65. package/addon/routes/connectivity/fuel-providers.js +14 -1
  66. package/addon/routes/connectivity/sensors/index/new.js +14 -1
  67. package/addon/routes/connectivity/sensors.js +14 -1
  68. package/addon/routes/connectivity/telematics/new.js +14 -1
  69. package/addon/routes/connectivity/telematics.js +14 -1
  70. package/addon/routes/connectivity/tracking.js +14 -1
  71. package/addon/routes/maintenance/equipment/index/details.js +2 -2
  72. package/addon/routes/maintenance/equipment/index/edit.js +2 -2
  73. package/addon/routes/maintenance/equipment/index/new.js +14 -1
  74. package/addon/routes/maintenance/equipment.js +14 -1
  75. package/addon/routes/maintenance/index.js +14 -1
  76. package/addon/routes/maintenance/inspection-forms/index/details.js +10 -1
  77. package/addon/routes/maintenance/inspection-forms/index/edit.js +10 -1
  78. package/addon/routes/maintenance/inspection-forms/index/new.js +14 -1
  79. package/addon/routes/maintenance/inspection-forms.js +14 -1
  80. package/addon/routes/maintenance/inspection-submissions/index/details.js +10 -1
  81. package/addon/routes/maintenance/inspection-submissions/index/edit.js +10 -1
  82. package/addon/routes/maintenance/inspection-submissions/index/new.js +14 -1
  83. package/addon/routes/maintenance/inspection-submissions.js +14 -1
  84. package/addon/routes/maintenance/maintenances/index/details.js +2 -2
  85. package/addon/routes/maintenance/maintenances/index/edit.js +2 -2
  86. package/addon/routes/maintenance/maintenances/index/new.js +14 -1
  87. package/addon/routes/maintenance/maintenances.js +14 -1
  88. package/addon/routes/maintenance/parts/index/details.js +2 -2
  89. package/addon/routes/maintenance/parts/index/edit.js +2 -2
  90. package/addon/routes/maintenance/parts/index/new.js +14 -1
  91. package/addon/routes/maintenance/parts.js +14 -1
  92. package/addon/routes/maintenance/schedules/index/details.js +2 -2
  93. package/addon/routes/maintenance/schedules/index/edit.js +2 -2
  94. package/addon/routes/maintenance/schedules/index/new.js +15 -1
  95. package/addon/routes/maintenance/schedules.js +15 -1
  96. package/addon/routes/maintenance/work-orders/index/details.js +2 -2
  97. package/addon/routes/maintenance/work-orders/index/edit.js +2 -2
  98. package/addon/routes/maintenance/work-orders/index/new.js +14 -1
  99. package/addon/routes/maintenance/work-orders.js +14 -1
  100. package/addon/routes/management/fuel-transactions/index/details.js +9 -0
  101. package/addon/routes/management/fuel-transactions.js +14 -1
  102. package/addon/routes/management/index.js +13 -0
  103. package/addon/routes/management/trailers.js +16 -0
  104. package/addon/routes/management/vendors/integrated/details.js +14 -1
  105. package/addon/routes/management/vendors/integrated/edit.js +14 -1
  106. package/addon/routes/management/vendors/integrated/new.js +14 -1
  107. package/addon/routes/management/vendors/integrated.js +14 -1
  108. package/addon/routes/operations/orders.js +40 -1
  109. package/addon/routes/operations/routes/index/details.js +14 -1
  110. package/addon/routes/operations/routes/index/new.js +13 -0
  111. package/addon/routes/operations/routes.js +14 -1
  112. package/addon/routes/operations/service-rates/index/new.js +11 -0
  113. package/addon/routes/operations/service-rates.js +14 -1
  114. package/addon/routes/settings/avatars.js +14 -1
  115. package/addon/routes/settings/custom-fields.js +14 -1
  116. package/addon/routes/settings/map.js +14 -1
  117. package/addon/routes/settings/notifications.js +11 -0
  118. package/addon/routes/settings/orchestrator.js +1 -1
  119. package/addon/routes/settings/payments/index.js +1 -1
  120. package/addon/routes/settings/routing.js +14 -1
  121. package/addon/routes/settings/scheduling.js +14 -1
  122. package/addon/routes/settings/telematics.js +21 -0
  123. package/addon/routes/virtual.js +11 -0
  124. package/addon/routes.js +1 -0
  125. package/addon/services/contact-actions.js +50 -89
  126. package/addon/services/driver-actions.js +98 -0
  127. package/addon/templates/settings/telematics.hbs +70 -0
  128. package/addon/utils/profile-login-actions.js +172 -0
  129. package/app/components/admin/telematics-settings.js +1 -0
  130. package/app/components/{modals/reset-customer-credentials.js → map/marker-card/driver.js} +1 -1
  131. package/app/components/map/marker-card/vehicle.js +1 -0
  132. package/app/components/modals/reset-profile-credentials.js +1 -0
  133. package/app/components/profile-identity-hint.js +1 -0
  134. package/app/controllers/settings/telematics.js +1 -0
  135. package/app/routes/settings/telematics.js +1 -0
  136. package/app/templates/settings/telematics.js +1 -0
  137. package/composer.json +2 -2
  138. package/docs/AFAQY.md +1 -1
  139. package/docs/TELEMATICS_QUEUES.md +1 -1
  140. package/docs/TELEMETRY_ARCHITECTURE.md +72 -1
  141. package/extension.json +1 -1
  142. package/package.json +3 -3
  143. package/scripts/pest-bootstrap.php +12 -0
  144. package/server/config/telemetry.php +12 -0
  145. package/server/migrations/2026_09_21_000001_convert_profile_only_users_to_managed_accounts.php +132 -0
  146. package/server/migrations/2026_09_23_000001_add_retention_index_to_device_events.php +40 -0
  147. package/server/migrations/2026_09_23_000002_add_retention_index_to_positions.php +40 -0
  148. package/server/migrations/2026_09_23_000003_add_retention_index_to_telematic_sync_runs.php +40 -0
  149. package/server/resources/views/mail/driver-credentials.blade.php +21 -0
  150. package/server/src/Auth/Schemas/FleetOps.php +104 -5
  151. package/server/src/Console/Commands/DrainTelematicInbox.php +3 -13
  152. package/server/src/Console/Commands/FixCustomerCompanies.php +1 -1
  153. package/server/src/Console/Commands/FixDriverCompanies.php +1 -1
  154. package/server/src/Console/Commands/ProcessOperationalAlerts.php +3 -3
  155. package/server/src/Console/Commands/PruneTelematicsData.php +318 -0
  156. package/server/src/Exceptions/ProfileIdentityConflictException.php +34 -0
  157. package/server/src/Http/Controllers/Api/v1/CustomerController.php +14 -0
  158. package/server/src/Http/Controllers/Api/v1/DriverController.php +56 -39
  159. package/server/src/Http/Controllers/Api/v1/OrchestrationController.php +9 -0
  160. package/server/src/Http/Controllers/FleetOpsController.php +21 -0
  161. package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +11 -0
  162. package/server/src/Http/Controllers/Internal/v1/ContactController.php +16 -17
  163. package/server/src/Http/Controllers/Internal/v1/CustomerController.php +54 -4
  164. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +13 -0
  165. package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +11 -0
  166. package/server/src/Http/Controllers/Internal/v1/DriverController.php +204 -162
  167. package/server/src/Http/Controllers/Internal/v1/FleetOpsLookupController.php +47 -0
  168. package/server/src/Http/Controllers/Internal/v1/FuelProviderConnectionController.php +13 -0
  169. package/server/src/Http/Controllers/Internal/v1/FuelProviderTransactionController.php +15 -0
  170. package/server/src/Http/Controllers/Internal/v1/FuelReportController.php +9 -3
  171. package/server/src/Http/Controllers/Internal/v1/InspectionFormController.php +15 -0
  172. package/server/src/Http/Controllers/Internal/v1/LiveController.php +16 -0
  173. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +15 -0
  174. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +15 -0
  175. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +15 -0
  176. package/server/src/Http/Controllers/Internal/v1/MetricsController.php +11 -0
  177. package/server/src/Http/Controllers/Internal/v1/NavigatorController.php +42 -1
  178. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +12 -0
  179. package/server/src/Http/Controllers/Internal/v1/OrderController.php +178 -27
  180. package/server/src/Http/Controllers/Internal/v1/PaymentController.php +13 -0
  181. package/server/src/Http/Controllers/Internal/v1/RadarController.php +19 -0
  182. package/server/src/Http/Controllers/Internal/v1/ServiceAreaController.php +9 -3
  183. package/server/src/Http/Controllers/Internal/v1/SettingController.php +465 -14
  184. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +21 -0
  185. package/server/src/Http/Controllers/Internal/v1/TrailerController.php +21 -0
  186. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +21 -0
  187. package/server/src/Http/Controllers/Internal/v1/VendorController.php +17 -0
  188. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +11 -0
  189. package/server/src/Http/Requests/CreateDriverRequest.php +6 -2
  190. package/server/src/Http/Requests/Internal/CreateDriverRequest.php +9 -18
  191. package/server/src/Http/Resources/v1/Contact.php +3 -0
  192. package/server/src/Http/Resources/v1/Driver.php +3 -0
  193. package/server/src/Jobs/DispatchTelematicsRetentionJobs.php +70 -0
  194. package/server/src/Jobs/PruneTelematicsDataJob.php +74 -0
  195. package/server/src/Listeners/NotifyDriverOnShiftChange.php +3 -3
  196. package/server/src/Mail/CustomerCredentialsMail.php +1 -1
  197. package/server/src/Mail/DriverCredentialsMail.php +55 -0
  198. package/server/src/Models/Contact.php +55 -90
  199. package/server/src/Models/Sensor.php +3 -5
  200. package/server/src/Observers/ContactObserver.php +2 -22
  201. package/server/src/Observers/DriverObserver.php +5 -6
  202. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +9 -1
  203. package/server/src/Providers/FleetOpsServiceProvider.php +18 -0
  204. package/server/src/Support/Ai/Capabilities/AbstractFleetOpsAICapability.php +62 -5
  205. package/server/src/Support/Ai/Capabilities/AssetStatusCapability.php +24 -5
  206. package/server/src/Support/Ai/Capabilities/CreateOrderPreviewCapability.php +6 -2
  207. package/server/src/Support/Ai/Capabilities/OrderInsightsCapability.php +40 -7
  208. package/server/src/Support/Ai/Capabilities/SearchResourcesCapability.php +86 -24
  209. package/server/src/Support/Ai/FleetOpsAiConsoleCommands.php +122 -0
  210. package/server/src/Support/Ai/Tools/CreateOrderTool.php +86 -0
  211. package/server/src/Support/Ai/Tools/ImportOrdersTool.php +53 -0
  212. package/server/src/Support/Ai/Tools/OptimizeOrderRouteTool.php +70 -0
  213. package/server/src/Support/Ai/Tools/SearchResourcesTool.php +73 -0
  214. package/server/src/Support/Analytics/LiveFleet.php +18 -2
  215. package/server/src/Support/Authorization.php +103 -0
  216. package/server/src/Support/Database/TableIndexes.php +66 -0
  217. package/server/src/Support/OrderTracker.php +2 -2
  218. package/server/src/Support/ProfileAccountManager.php +405 -0
  219. package/server/src/Support/Reporting/FleetOpsReportSchema.php +476 -116
  220. package/server/src/Support/Telematics/Retention/RetentionPolicy.php +300 -0
  221. package/server/src/Support/Telematics/Retention/TelemetryActivity.php +40 -0
  222. package/server/src/Support/Telematics/TelematicService.php +12 -2
  223. package/server/src/Support/Telematics/Telemetry/Ingestor.php +4 -2
  224. package/server/src/Tracking/TrackingIntelligenceService.php +1 -1
  225. package/server/src/Tracking/TrackingOptions.php +2 -2
  226. package/server/src/Traits/AuthorizesMethods.php +37 -0
  227. package/server/src/routes.php +14 -1
  228. package/server/tests/AiOperationalQueryCapabilityTest.php +1 -0
  229. package/server/tests/AnalyticsRoutesTest.php +29 -2
  230. package/server/tests/ApiCustomerControllerContractsTest.php +21 -0
  231. package/server/tests/ApiDriverControllerContractsTest.php +102 -20
  232. package/server/tests/CommandContractsTest.php +4 -4
  233. package/server/tests/ControllerHelperContractsTest.php +3 -11
  234. package/server/tests/Feature/Http/AfaqyRealtimeIngestionTest.php +21 -1
  235. package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +36 -0
  236. package/server/tests/Feature/Http/Api/DriverControllerSessionHelpersTest.php +19 -6
  237. package/server/tests/Feature/Http/Api/ProfileOnlyUserConversionMigrationTest.php +201 -0
  238. package/server/tests/Feature/Http/Api/TelematicsRetentionIndexMigrationTest.php +115 -0
  239. package/server/tests/Feature/Http/Internal/ContactControllerEndpointsTest.php +6 -4
  240. package/server/tests/Feature/Http/Internal/CustomerControllerCredentialsTest.php +6 -2
  241. package/server/tests/Feature/Http/Internal/DriverControllerContractsTest.php +23 -0
  242. package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +42 -17
  243. package/server/tests/Feature/Http/Internal/DriverControllerExistingUserAdoptionTest.php +168 -277
  244. package/server/tests/Feature/Http/Internal/DriverControllerLoginManagementTest.php +716 -0
  245. package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +10 -3
  246. package/server/tests/Feature/Http/Internal/OrderControllerImportFromFilesTest.php +18 -0
  247. package/server/tests/Feature/Http/Internal/OrderControllerTenantScopingTest.php +411 -0
  248. package/server/tests/Feature/Http/Internal/OrderControllerUpstreamNotFoundTest.php +8 -20
  249. package/server/tests/Feature/Http/Internal/SettingControllerHelpersTest.php +71 -0
  250. package/server/tests/Feature/Http/Internal/TelematicsStorageDeadlinesTest.php +119 -0
  251. package/server/tests/InternalCustomerControllerContractsTest.php +32 -1
  252. package/server/tests/InternalImportExportControllerContractsTest.php +5 -8
  253. package/server/tests/ModelAccessorContractsTest.php +82 -20
  254. package/server/tests/NavigatorControllerContractsTest.php +36 -4
  255. package/server/tests/NotificationAndMailContractsTest.php +31 -0
  256. package/server/tests/ObserverContractsTest.php +28 -3
  257. package/server/tests/OrchestratorConsumableApiTest.php +42 -0
  258. package/server/tests/ProviderContractsTest.php +10 -0
  259. package/server/tests/ReportSchemaContractsTest.php +120 -6
  260. package/server/tests/RequestContractsTest.php +19 -11
  261. package/server/tests/SettingControllerContractsTest.php +429 -18
  262. package/server/tests/SmallControllerContractsTest.php +8 -5
  263. package/server/tests/Support/PruneTelematicsDataProbe.php +55 -0
  264. package/server/tests/SupportJobAndAiCoverageTest.php +3 -3
  265. package/server/tests/TelematicControllerContractsTest.php +22 -4
  266. package/server/tests/Unit/Console/Commands/ProcessOperationalAlertsCommandTest.php +2 -2
  267. package/server/tests/Unit/Console/PruneTelematicsDataCommandTest.php +353 -0
  268. package/server/tests/Unit/Jobs/DispatchTelematicsRetentionJobsTest.php +114 -0
  269. package/server/tests/Unit/Jobs/PruneTelematicsDataJobTest.php +60 -0
  270. package/server/tests/Unit/Models/ContactCustomerUserTest.php +90 -14
  271. package/server/tests/Unit/Models/ContactTest.php +48 -5
  272. package/server/tests/Unit/Models/SensorTest.php +2 -2
  273. package/server/tests/Unit/Models/SmallModelSurfacesTest.php +31 -32
  274. package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +7 -2
  275. package/server/tests/Unit/Orchestration/VroomEngineTransportAndSettingsTest.php +20 -0
  276. package/server/tests/Unit/Providers/FleetOpsAiRegistrationTest.php +15 -2
  277. package/server/tests/Unit/Support/Ai/AssetStatusCapabilityQueriesTest.php +42 -0
  278. package/server/tests/Unit/Support/Ai/Capabilities/OrderInsightsCapabilityTest.php +19 -1
  279. package/server/tests/Unit/Support/Ai/Capabilities/SearchResourcesCapabilityTest.php +85 -1
  280. package/server/tests/Unit/Support/Ai/FleetOpsAiConsoleCommandsTest.php +31 -0
  281. package/server/tests/Unit/Support/Ai/Tools/FleetOpsAiToolsTest.php +246 -0
  282. package/server/tests/Unit/Support/AiCapabilityPermissionsTest.php +3 -3
  283. package/server/tests/Unit/Support/Analytics/LiveFleetTest.php +6 -0
  284. package/server/tests/Unit/Support/AuthorizationTest.php +142 -0
  285. package/server/tests/Unit/Support/MaintainableAndOperationalAlertsTest.php +12 -1
  286. package/server/tests/Unit/Support/MoreQuerySeamsTest.php +11 -2
  287. package/server/tests/Unit/Support/ProfileAccountManagerTest.php +643 -0
  288. package/server/tests/Unit/Support/Telematics/Retention/RetentionPolicyTest.php +190 -0
  289. package/server/tests/Unit/Support/Telematics/Retention/TelemetryActivityTest.php +68 -0
  290. package/server/tests/Unit/Tracking/TrackingOptionsCompanySettingsTest.php +109 -0
  291. package/translations/en-us.yaml +85 -2
  292. package/translations/uk-ua.yaml +3475 -0
  293. package/addon/components/modals/reset-customer-credentials.hbs +0 -18
  294. package/addon/components/modals/reset-customer-credentials.js +0 -55
@@ -2,6 +2,7 @@
2
2
 
3
3
  namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
4
4
 
5
+ use Fleetbase\Attributes\SkipAuthorizationCheck;
5
6
  use Fleetbase\Exceptions\FleetbaseRequestValidationException;
6
7
  use Fleetbase\FleetOps\Events\EntityActivityChanged;
7
8
  use Fleetbase\FleetOps\Events\EntityCompleted;
@@ -39,6 +40,7 @@ use Fleetbase\Models\File;
39
40
  use Fleetbase\Models\Type;
40
41
  use Fleetbase\Support\Auth;
41
42
  use Fleetbase\Support\TemplateString;
43
+ use Illuminate\Database\Eloquent\Builder;
42
44
  use Illuminate\Database\Eloquent\ModelNotFoundException;
43
45
  use Illuminate\Database\QueryException;
44
46
  use Illuminate\Http\Request;
@@ -52,6 +54,20 @@ use Maatwebsite\Excel\Facades\Excel;
52
54
  class OrderController extends FleetOpsController
53
55
  {
54
56
  use ResolvesOrderServiceStops;
57
+ /**
58
+ * Permissions for methods AuthorizationGuard cannot map to a schema action (see FleetOpsController).
59
+ *
60
+ * @var array<string, string|string[]>
61
+ */
62
+ protected array $methodPermissions = [
63
+ 'editOrderRoute' => 'update-route-for order',
64
+ 'importFromFiles' => 'import order',
65
+ 'bulkCancel' => 'cancel order',
66
+ 'bulkDispatch' => 'dispatch order',
67
+ 'bulkAssignDriver' => 'assign-driver-for order',
68
+ 'dispatchOrder' => 'dispatch order',
69
+ 'scheduleOrder' => 'schedule order',
70
+ ];
55
71
 
56
72
  /**
57
73
  * The resource to query.
@@ -266,6 +282,7 @@ class OrderController extends FleetOpsController
266
282
  *
267
283
  * @return Response
268
284
  */
285
+ #[SkipAuthorizationCheck]
269
286
  public function editOrderRoute(string $id, Request $request)
270
287
  {
271
288
  $pickup = $request->input('pickup');
@@ -336,12 +353,14 @@ class OrderController extends FleetOpsController
336
353
  *
337
354
  * @return \Illuminate\Http\Response
338
355
  */
356
+ #[SkipAuthorizationCheck]
339
357
  public function importFromFiles(Request $request)
340
358
  {
341
359
  $info = Utils::lookupIp();
342
360
  $disk = $request->input('disk', config('filesystems.default'));
343
361
  $files = $request->input('files');
344
- $files = File::whereIn('uuid', $files)->get();
362
+ /** @var \Illuminate\Database\Eloquent\Collection<int, File> $files */
363
+ $files = $this->scopedToCompany(File::whereIn('uuid', $files))->get();
345
364
  $country = $request->input('country', Utils::or($info, ['country_name', 'region'], 'Singapore'));
346
365
 
347
366
  $validFileTypes = ['csv', 'tsv', 'xls', 'xlsx'];
@@ -407,6 +426,7 @@ class OrderController extends FleetOpsController
407
426
  *
408
427
  * @return \Illuminate\Http\Response
409
428
  */
429
+ #[SkipAuthorizationCheck]
410
430
  public function bulkCancel(Request $request)
411
431
  {
412
432
  $request->validate([
@@ -452,6 +472,7 @@ class OrderController extends FleetOpsController
452
472
  *
453
473
  * @return \Illuminate\Http\Response
454
474
  */
475
+ #[SkipAuthorizationCheck]
455
476
  public function bulkDispatch(BulkDispatchRequest $request)
456
477
  {
457
478
  /** @var Order */
@@ -500,6 +521,7 @@ class OrderController extends FleetOpsController
500
521
  *
501
522
  * @return \Illuminate\Http\Response
502
523
  */
524
+ #[SkipAuthorizationCheck]
503
525
  public function bulkAssignDriver(Request $request)
504
526
  {
505
527
  // Validate Inputs
@@ -513,7 +535,13 @@ class OrderController extends FleetOpsController
513
535
  }
514
536
 
515
537
  // Prepare Order UUID Collection
516
- $orderUuids = collect($data['ids'])->unique()->values();
538
+ //
539
+ // Resolved through the company-scoped lookup so ids naming another
540
+ // organization's orders are dropped here rather than being assigned,
541
+ // counted in the response and queued for notification.
542
+ $orderUuids = $this->ordersByUuid(collect($data['ids'])->unique()->values()->all())
543
+ ->pluck('uuid')
544
+ ->values();
517
545
 
518
546
  // Bulk Update Inside A Transaction
519
547
  $this->runTransaction(function () use ($orderUuids, $driver): void {
@@ -545,8 +573,14 @@ class OrderController extends FleetOpsController
545
573
  */
546
574
  public function cancel(CancelOrderRequest $request)
547
575
  {
548
- /** @var Order */
576
+ /** @var Order|null */
549
577
  $order = $this->findOrderByUuid($request->input('order'));
578
+ if (!$order) {
579
+ // `exists:orders,uuid` on the form request is a global existence
580
+ // check, so a known uuid belonging to another organization reaches
581
+ // here and must be rejected rather than dereferenced.
582
+ return $this->errorResponse('No order found to cancel.');
583
+ }
550
584
 
551
585
  $order->cancel();
552
586
 
@@ -564,6 +598,7 @@ class OrderController extends FleetOpsController
564
598
  *
565
599
  * @return \Illuminate\Http\Response
566
600
  */
601
+ #[SkipAuthorizationCheck]
567
602
  public function dispatchOrder(Request $request)
568
603
  {
569
604
  /**
@@ -599,9 +634,39 @@ class OrderController extends FleetOpsController
599
634
  );
600
635
  }
601
636
 
637
+ /**
638
+ * Constrain a tenant-owned lookup to the company the caller is acting for.
639
+ *
640
+ * The order-lifecycle actions on this controller receive their target as a
641
+ * caller-supplied identifier in the request body or query string rather than
642
+ * as a bound route parameter, so nothing upstream narrows these queries to
643
+ * the caller's tenant: `fleetbase.protected` only checks that the caller
644
+ * holds the named RBAC capability, never which company the record belongs to.
645
+ *
646
+ * A missing company session fails the query closed instead of letting it run
647
+ * unbounded across every tenant.
648
+ */
649
+ protected function scopedToCompany(Builder $query): Builder
650
+ {
651
+ $companyUuid = $this->sessionCompany();
652
+ if (!$companyUuid) {
653
+ $query->whereRaw('1 = 0');
654
+
655
+ return $query;
656
+ }
657
+
658
+ return $query->where($query->getModel()->qualifyColumn('company_uuid'), $companyUuid);
659
+ }
660
+
661
+ /**
662
+ * @return \Illuminate\Database\Eloquent\Collection<int, Order>
663
+ */
602
664
  protected function ordersByUuid(array $ids)
603
665
  {
604
- return Order::whereIn('uuid', $ids)->get();
666
+ /** @var \Illuminate\Database\Eloquent\Collection<int, Order> $orders */
667
+ $orders = $this->scopedToCompany(Order::whereIn('uuid', $ids))->get();
668
+
669
+ return $orders;
605
670
  }
606
671
 
607
672
  protected function trackingStatusExists(?string $trackingNumberUuid, string $code): bool
@@ -611,7 +676,10 @@ class OrderController extends FleetOpsController
611
676
 
612
677
  protected function findDriverByUuid(string $uuid): ?Driver
613
678
  {
614
- return Driver::whereUuid($uuid)->first();
679
+ /** @var Driver|null $driver */
680
+ $driver = $this->scopedToCompany(Driver::whereUuid($uuid))->first();
681
+
682
+ return $driver;
615
683
  }
616
684
 
617
685
  protected function driverDisplayName(Driver $driver): string
@@ -630,17 +698,47 @@ class OrderController extends FleetOpsController
630
698
 
631
699
  protected function findOrderByUuid(string $uuid): ?Order
632
700
  {
633
- return Order::where('uuid', $uuid)->first();
701
+ /** @var Order|null $order */
702
+ $order = $this->scopedToCompany(Order::where('uuid', $uuid))->first();
703
+
704
+ return $order;
634
705
  }
635
706
 
636
- protected function findOrderById(string $id, array $with = []): ?Order
707
+ /**
708
+ * Resolve an order by uuid or public_id, constrained to the caller's company.
709
+ *
710
+ * The identifier match is grouped so the company constraint applies to both
711
+ * arms — ungrouped it would read as `uuid = ? OR (public_id = ? AND
712
+ * company_uuid = ?)` and still resolve another organization's orders.
713
+ *
714
+ * The identifier is whatever the caller put in the request, so anything
715
+ * that is not a non-empty string resolves to no order rather than raising.
716
+ *
717
+ * @param array<int, string> $with
718
+ */
719
+ protected function findOrderById(mixed $id, array $with = []): ?Order
637
720
  {
638
- return Order::findById($id, $with);
721
+ if (!is_string($id) || $id === '') {
722
+ return null;
723
+ }
724
+
725
+ /** @var Order|null $order */
726
+ $order = $this->scopedToCompany(Order::query())
727
+ ->where(function (Builder $query) use ($id) {
728
+ $query->where('uuid', $id)->orWhere('public_id', $id);
729
+ })
730
+ ->with($with)
731
+ ->first();
732
+
733
+ return $order;
639
734
  }
640
735
 
641
736
  protected function findOrderRouteForEdit(string $uuid): ?Order
642
737
  {
643
- return Order::where('uuid', $uuid)->with(['payload'])->first();
738
+ /** @var Order|null $order */
739
+ $order = $this->scopedToCompany(Order::where('uuid', $uuid))->with(['payload'])->first();
740
+
741
+ return $order;
644
742
  }
645
743
 
646
744
  protected function orderResponse(Order $order): array
@@ -650,7 +748,7 @@ class OrderController extends FleetOpsController
650
748
 
651
749
  protected function assignDriverToOrders($orderUuids, Driver $driver): void
652
750
  {
653
- Order::whereIn('uuid', $orderUuids)->update([
751
+ $this->scopedToCompany(Order::whereIn('uuid', $orderUuids))->update([
654
752
  'driver_assigned_uuid' => $driver->uuid,
655
753
  'updated_at' => now(),
656
754
  ]);
@@ -739,17 +837,28 @@ class OrderController extends FleetOpsController
739
837
 
740
838
  protected function findOrderForStart(?string $uuid): ?Order
741
839
  {
742
- return Order::where('uuid', $uuid)->withoutGlobalScopes()->first();
840
+ /** @var Order|null $order */
841
+ $order = $this->scopedToCompany(Order::where('uuid', $uuid)->withoutGlobalScopes())->first();
842
+
843
+ return $order;
743
844
  }
744
845
 
745
846
  protected function findDriverForStart(?string $uuid): ?Driver
746
847
  {
747
- return Driver::where('uuid', $uuid)->withoutGlobalScopes()->first();
848
+ /** @var Driver|null $driver */
849
+ $driver = $this->scopedToCompany(Driver::where('uuid', $uuid)->withoutGlobalScopes())->first();
850
+
851
+ return $driver;
748
852
  }
749
853
 
750
854
  protected function findPayloadForStart(?string $uuid): ?Payload
751
855
  {
752
- return Payload::where('uuid', $uuid)->withoutGlobalScopes()->with(['waypoints', 'waypointMarkers', 'entities'])->first();
856
+ /** @var Payload|null $payload */
857
+ $payload = $this->scopedToCompany(Payload::where('uuid', $uuid)->withoutGlobalScopes())
858
+ ->with(['waypoints', 'waypointMarkers', 'entities'])
859
+ ->first();
860
+
861
+ return $payload;
753
862
  }
754
863
 
755
864
  protected function dispatchDomainEvent(object $event): object
@@ -771,7 +880,7 @@ class OrderController extends FleetOpsController
771
880
  */
772
881
  public function updateActivity(string $id, Request $request)
773
882
  {
774
- $order = Order::findById($id, [
883
+ $order = $this->findOrderById($id, [
775
884
  'driverAssigned',
776
885
  'payload.entities',
777
886
  'payload.pickup',
@@ -882,9 +991,8 @@ class OrderController extends FleetOpsController
882
991
  */
883
992
  public function nextActivity(string $id, Request $request)
884
993
  {
885
- try {
886
- $order = Order::findByIdOrFail($id);
887
- } catch (ModelNotFoundException $e) {
994
+ $order = $this->findOrderById($id);
995
+ if (!$order) {
888
996
  return response()->error('No order found.');
889
997
  }
890
998
 
@@ -935,7 +1043,7 @@ class OrderController extends FleetOpsController
935
1043
  */
936
1044
  public function setDestination(string $id, string $placeId)
937
1045
  {
938
- $order = Order::findById($id, [
1046
+ $order = $this->findOrderById($id, [
939
1047
  'payload.pickup',
940
1048
  'payload.dropoff',
941
1049
  'payload.return',
@@ -1013,7 +1121,7 @@ class OrderController extends FleetOpsController
1013
1121
  return response()->error($errorMessage, 422);
1014
1122
  }
1015
1123
 
1016
- $order = Order::findById($id, ['payload.pickup', 'payload.dropoff', 'payload.return', 'payload.waypoints', 'payload.waypointMarkers.place']);
1124
+ $order = $this->findOrderById($id, ['payload.pickup', 'payload.dropoff', 'payload.return', 'payload.waypoints', 'payload.waypointMarkers.place']);
1017
1125
  if (!$order) {
1018
1126
  return response()->error('No order found.');
1019
1127
  }
@@ -1214,7 +1322,14 @@ class OrderController extends FleetOpsController
1214
1322
  }
1215
1323
 
1216
1324
  if (is_string($proof)) {
1217
- return Proof::where('public_id', $proof)->orWhere('uuid', $proof)->first();
1325
+ /** @var Proof|null $resolved */
1326
+ $resolved = $this->scopedToCompany(Proof::query())
1327
+ ->where(function (Builder $query) use ($proof) {
1328
+ $query->where('public_id', $proof)->orWhere('uuid', $proof);
1329
+ })
1330
+ ->first();
1331
+
1332
+ return $resolved;
1218
1333
  }
1219
1334
 
1220
1335
  return null;
@@ -1419,7 +1534,12 @@ class OrderController extends FleetOpsController
1419
1534
 
1420
1535
  protected function findOrderForDriverPing(string $id): Order
1421
1536
  {
1422
- return Order::findByIdOrFail($id, ['driverAssigned']);
1537
+ $order = $this->findOrderById($id, ['driverAssigned']);
1538
+ if (!$order) {
1539
+ throw new ModelNotFoundException();
1540
+ }
1541
+
1542
+ return $order;
1423
1543
  }
1424
1544
 
1425
1545
  protected function sendDriverPing(Driver $driver, Order $order): void
@@ -1692,7 +1812,10 @@ class OrderController extends FleetOpsController
1692
1812
 
1693
1813
  protected function findOrderForProofs(string $id): ?Order
1694
1814
  {
1695
- return Order::where('uuid', $id)->first();
1815
+ /** @var Order|null $order */
1816
+ $order = $this->scopedToCompany(Order::where('uuid', $id))->first();
1817
+
1818
+ return $order;
1696
1819
  }
1697
1820
 
1698
1821
  protected function findWaypointProofSubject(Order $order, string $subjectId): ?Waypoint
@@ -1708,7 +1831,10 @@ class OrderController extends FleetOpsController
1708
1831
 
1709
1832
  protected function findEntityProofSubject(string $subjectId): ?Entity
1710
1833
  {
1711
- return Entity::where('uuid', $subjectId)->withoutGlobalScopes()->first();
1834
+ /** @var Entity|null $entity */
1835
+ $entity = $this->scopedToCompany(Entity::where('uuid', $subjectId)->withoutGlobalScopes())->first();
1836
+
1837
+ return $entity;
1712
1838
  }
1713
1839
 
1714
1840
  protected function proofsForSubject(Order $order, Order|Waypoint|Entity $subject)
@@ -1772,14 +1898,26 @@ class OrderController extends FleetOpsController
1772
1898
  return new OrderResource($order);
1773
1899
  }
1774
1900
 
1901
+ /**
1902
+ * Find the order a tracking number belongs to, for the public Track Order page.
1903
+ *
1904
+ * Deliberately NOT company-scoped, unlike the lifecycle lookups above. The
1905
+ * `fleet-ops/lookup` route is public: the tracking page is used by recipients who
1906
+ * have no session, and the tracking number itself is what grants access. Scoping
1907
+ * it to the session company made every lookup from that page fail, because there
1908
+ * is no company to scope to.
1909
+ */
1775
1910
  protected function findOrderByTrackingNumber(string $trackingNumber): ?Order
1776
1911
  {
1777
- return Order::whereHas(
1912
+ /** @var Order|null $order */
1913
+ $order = Order::whereHas(
1778
1914
  'trackingNumber',
1779
1915
  function ($query) use ($trackingNumber) {
1780
1916
  $query->where('tracking_number', $trackingNumber);
1781
1917
  }
1782
1918
  )->first();
1919
+
1920
+ return $order;
1783
1921
  }
1784
1922
 
1785
1923
  /**
@@ -1789,6 +1927,7 @@ class OrderController extends FleetOpsController
1789
1927
  *
1790
1928
  * @return \Illuminate\Http\Response
1791
1929
  */
1930
+ #[SkipAuthorizationCheck]
1792
1931
  public function scheduleOrder(Request $request)
1793
1932
  {
1794
1933
  $orderId = $request->input('order');
@@ -1824,13 +1963,25 @@ class OrderController extends FleetOpsController
1824
1963
 
1825
1964
  protected function findOrderForSchedule(?string $id): ?Order
1826
1965
  {
1827
- return Order::findById($id);
1966
+ return $this->findOrderById($id);
1828
1967
  }
1829
1968
 
1969
+ /**
1970
+ * Resolve a driver by uuid or public_id, constrained to the caller's company.
1971
+ *
1972
+ * The identifier match is grouped so the company constraint applies to both
1973
+ * arms — ungrouped it would read as `uuid = ? OR (public_id = ? AND
1974
+ * company_uuid = ?)` and still resolve another organization's drivers.
1975
+ */
1830
1976
  protected function findDriverForSchedule(string $id): ?Driver
1831
1977
  {
1832
- return Driver::where('uuid', $id)
1833
- ->orWhere('public_id', $id)
1978
+ /** @var Driver|null $driver */
1979
+ $driver = $this->scopedToCompany(Driver::query())
1980
+ ->where(function (Builder $query) use ($id) {
1981
+ $query->where('uuid', $id)->orWhere('public_id', $id);
1982
+ })
1834
1983
  ->first();
1984
+
1985
+ return $driver;
1835
1986
  }
1836
1987
  }
@@ -4,6 +4,7 @@ namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
4
4
 
5
5
  use Fleetbase\FleetOps\Models\PurchaseRate;
6
6
  use Fleetbase\FleetOps\Support\Payment;
7
+ use Fleetbase\FleetOps\Traits\AuthorizesMethods;
7
8
  use Fleetbase\Http\Controllers\Controller;
8
9
  use Fleetbase\Http\Resources\FleetbaseResource;
9
10
  use Fleetbase\Support\Auth;
@@ -12,6 +13,18 @@ use Illuminate\Support\Str;
12
13
 
13
14
  class PaymentController extends Controller
14
15
  {
16
+ use AuthorizesMethods;
17
+
18
+ public function __construct()
19
+ {
20
+ $this->authorizeMethods([
21
+ 'hasStripeConnectAccount' => 'view payments',
22
+ 'getStripeAccount' => 'onboard payments',
23
+ 'getStripeAccountSession' => 'onboard payments',
24
+ 'getCompanyReceivedPayments' => 'view payments',
25
+ ]);
26
+ }
27
+
15
28
  /**
16
29
  * Checks if the currently authenticated company has an associated Stripe Connect account.
17
30
  *
@@ -19,6 +19,7 @@ use Fleetbase\FleetOps\Support\Radar\RadarAgenda;
19
19
  use Fleetbase\FleetOps\Support\Radar\RadarBriefing;
20
20
  use Fleetbase\FleetOps\Support\Radar\RadarItemState;
21
21
  use Fleetbase\FleetOps\Support\Radar\RadarRules;
22
+ use Fleetbase\FleetOps\Traits\AuthorizesMethods;
22
23
  use Fleetbase\Http\Controllers\Controller;
23
24
  use Fleetbase\Models\Alert;
24
25
  use Fleetbase\Models\CompanyUser;
@@ -42,6 +43,24 @@ use Illuminate\Support\Carbon;
42
43
  */
43
44
  class RadarController extends Controller
44
45
  {
46
+ use AuthorizesMethods;
47
+
48
+ public function __construct()
49
+ {
50
+ $this->authorizeMethods([
51
+ 'extendShift' => ['update driver', 'update vehicle'],
52
+ 'acknowledge' => ['update driver', 'update vehicle'],
53
+ 'snooze' => ['update driver', 'update vehicle'],
54
+ 'wake' => ['update driver', 'update vehicle'],
55
+ 'assign' => ['update driver', 'update vehicle'],
56
+ 'plan' => ['update driver', 'update vehicle'],
57
+ 'resolve' => ['update driver', 'update vehicle'],
58
+ 'bulk' => ['update driver', 'update vehicle'],
59
+ 'storeNotice' => ['update driver', 'update vehicle'],
60
+ 'destroyNotice' => ['update driver', 'update vehicle'],
61
+ ]);
62
+ }
63
+
45
64
  public const RESOLVED_WINDOW_DAYS = 7;
46
65
  public const SNOOZE_MAX_DAYS = 90;
47
66
 
@@ -7,6 +7,7 @@ use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
7
7
  use Fleetbase\FleetOps\Models\ServiceArea;
8
8
  use Fleetbase\Http\Requests\ExportRequest;
9
9
  use Illuminate\Http\Request;
10
+ use Illuminate\Support\Arr;
10
11
  use Illuminate\Support\Str;
11
12
  use Maatwebsite\Excel\Facades\Excel;
12
13
 
@@ -21,11 +22,16 @@ class ServiceAreaController extends FleetOpsController
21
22
 
22
23
  /**
23
24
  * Handle post save transactions.
25
+ *
26
+ * Reads the custom field values from the payload the base controller already extracted
27
+ * rather than from the raw request: Ember Data sends the resource under a camelCase root
28
+ * (`serviceArea`), so `$request->array('service_area.custom_field_values')` was always empty and no
29
+ * custom field value was ever persisted for this resource.
24
30
  */
25
- public function afterSave(Request $request, ServiceArea $serviceArea)
31
+ public function afterSave(Request $request, ServiceArea $serviceArea, array $input = [])
26
32
  {
27
- $customFieldValues = $request->array('service_area.custom_field_values');
28
- if ($customFieldValues) {
33
+ $customFieldValues = Arr::get($input, 'custom_field_values');
34
+ if (is_array($customFieldValues) && $customFieldValues) {
29
35
  $serviceArea->syncCustomFieldValues($customFieldValues);
30
36
  }
31
37
  }