@fleetbase/fleetops-engine 0.6.69 → 0.6.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/RELEASE.md +21 -14
  2. package/addon/components/admin/telematics-settings.hbs +143 -0
  3. package/addon/components/admin/telematics-settings.js +145 -0
  4. package/addon/components/fuel-integration/hub.hbs +1 -1
  5. package/addon/components/layout/fleet-ops-sidebar.js +37 -17
  6. package/addon/components/map/leaflet-live-map.hbs +4 -242
  7. package/addon/components/map/leaflet-live-map.js +2 -1
  8. package/addon/components/map/marker-card/driver.hbs +57 -0
  9. package/addon/components/map/marker-card/driver.js +3 -0
  10. package/addon/components/map/marker-card/vehicle.hbs +78 -0
  11. package/addon/components/map/marker-card/vehicle.js +3 -0
  12. package/addon/components/order/details/activity.hbs +8 -6
  13. package/addon/components/order/details/activity.js +9 -0
  14. package/addon/components/order/details.hbs +29 -0
  15. package/addon/components/order/details.js +26 -0
  16. package/addon/components/order/form/details.hbs +30 -4
  17. package/addon/components/order/form/details.js +46 -0
  18. package/addon/components/order/form/route.hbs +4 -2
  19. package/addon/components/order/form/route.js +11 -0
  20. package/addon/components/order/form.hbs +12 -0
  21. package/addon/components/order/form.js +21 -1
  22. package/addon/components/order/kanban.js +72 -0
  23. package/addon/components/order-config-manager/activity-flow.js +45 -10
  24. package/addon/components/order-config-manager/lifecycle.hbs +250 -0
  25. package/addon/components/order-config-manager/lifecycle.js +358 -0
  26. package/addon/components/order-config-manager.js +5 -1
  27. package/addon/components/telematic/hub.hbs +1 -1
  28. package/addon/components/widget/live-fleet.hbs +8 -40
  29. package/addon/controllers/analytics/reports/index.js +15 -1
  30. package/addon/controllers/connectivity/devices/index/details.js +1 -0
  31. package/addon/controllers/connectivity/devices/index.js +12 -2
  32. package/addon/controllers/connectivity/events/index.js +31 -1
  33. package/addon/controllers/connectivity/fuel-providers/index.js +6 -4
  34. package/addon/controllers/connectivity/sensors/index/details.js +1 -1
  35. package/addon/controllers/connectivity/sensors/index.js +41 -1
  36. package/addon/controllers/connectivity/telematics/index.js +4 -0
  37. package/addon/controllers/maintenance/equipment/index/details.js +2 -2
  38. package/addon/controllers/maintenance/equipment/index.js +36 -10
  39. package/addon/controllers/maintenance/inspection-forms/index.js +32 -12
  40. package/addon/controllers/maintenance/inspection-submissions/index/details.js +2 -1
  41. package/addon/controllers/maintenance/inspection-submissions/index.js +56 -10
  42. package/addon/controllers/maintenance/maintenances/index/details.js +2 -0
  43. package/addon/controllers/maintenance/maintenances/index.js +24 -1
  44. package/addon/controllers/maintenance/parts/index/details.js +2 -2
  45. package/addon/controllers/maintenance/parts/index.js +36 -9
  46. package/addon/controllers/maintenance/schedules/index.js +48 -6
  47. package/addon/controllers/maintenance/work-orders/index/details.js +3 -3
  48. package/addon/controllers/maintenance/work-orders/index.js +47 -9
  49. package/addon/controllers/management/contacts/customers.js +13 -0
  50. package/addon/controllers/management/contacts/index.js +47 -1
  51. package/addon/controllers/management/drivers/index/details.js +1 -0
  52. package/addon/controllers/management/drivers/index.js +36 -2
  53. package/addon/controllers/management/fleets/index/details.js +1 -0
  54. package/addon/controllers/management/fleets/index.js +43 -1
  55. package/addon/controllers/management/fuel-reports/index.js +26 -2
  56. package/addon/controllers/management/fuel-transactions/index.js +45 -7
  57. package/addon/controllers/management/issues/index/details.js +1 -0
  58. package/addon/controllers/management/issues/index.js +26 -2
  59. package/addon/controllers/management/places/index.js +40 -1
  60. package/addon/controllers/management/trailers/index/details.js +1 -0
  61. package/addon/controllers/management/trailers/index.js +50 -2
  62. package/addon/controllers/management/vehicles/index/details.js +1 -0
  63. package/addon/controllers/management/vehicles/index.js +37 -2
  64. package/addon/controllers/management/vendors/index.js +30 -2
  65. package/addon/controllers/operations/orders/index/details.js +61 -5
  66. package/addon/controllers/operations/orders/index.js +66 -4
  67. package/addon/controllers/operations/service-rates/index.js +18 -1
  68. package/addon/controllers/settings/index.js +13 -0
  69. package/addon/controllers/settings/telematics.js +113 -0
  70. package/addon/extension.js +70 -6
  71. package/addon/routes/analytics/index.js +14 -1
  72. package/addon/routes/analytics/reports/index/details.js +1 -1
  73. package/addon/routes/analytics/reports/index/edit.js +1 -1
  74. package/addon/routes/analytics/reports/index/new.js +14 -1
  75. package/addon/routes/analytics/reports.js +14 -1
  76. package/addon/routes/connectivity/devices/index/new.js +14 -1
  77. package/addon/routes/connectivity/devices.js +14 -1
  78. package/addon/routes/connectivity/events.js +14 -1
  79. package/addon/routes/connectivity/fuel-providers/details/transactions.js +12 -0
  80. package/addon/routes/connectivity/fuel-providers/details.js +9 -0
  81. package/addon/routes/connectivity/fuel-providers/edit.js +9 -0
  82. package/addon/routes/connectivity/fuel-providers/new.js +14 -1
  83. package/addon/routes/connectivity/fuel-providers.js +14 -1
  84. package/addon/routes/connectivity/sensors/index/new.js +14 -1
  85. package/addon/routes/connectivity/sensors.js +14 -1
  86. package/addon/routes/connectivity/telematics/new.js +14 -1
  87. package/addon/routes/connectivity/telematics.js +14 -1
  88. package/addon/routes/connectivity/tracking.js +14 -1
  89. package/addon/routes/maintenance/equipment/index/details.js +2 -2
  90. package/addon/routes/maintenance/equipment/index/edit.js +2 -2
  91. package/addon/routes/maintenance/equipment/index/new.js +14 -1
  92. package/addon/routes/maintenance/equipment.js +14 -1
  93. package/addon/routes/maintenance/index.js +14 -1
  94. package/addon/routes/maintenance/inspection-forms/index/details.js +10 -1
  95. package/addon/routes/maintenance/inspection-forms/index/edit.js +10 -1
  96. package/addon/routes/maintenance/inspection-forms/index/new.js +14 -1
  97. package/addon/routes/maintenance/inspection-forms.js +14 -1
  98. package/addon/routes/maintenance/inspection-submissions/index/details.js +10 -1
  99. package/addon/routes/maintenance/inspection-submissions/index/edit.js +10 -1
  100. package/addon/routes/maintenance/inspection-submissions/index/new.js +14 -1
  101. package/addon/routes/maintenance/inspection-submissions.js +14 -1
  102. package/addon/routes/maintenance/maintenances/index/details.js +2 -2
  103. package/addon/routes/maintenance/maintenances/index/edit.js +2 -2
  104. package/addon/routes/maintenance/maintenances/index/new.js +14 -1
  105. package/addon/routes/maintenance/maintenances.js +14 -1
  106. package/addon/routes/maintenance/parts/index/details.js +2 -2
  107. package/addon/routes/maintenance/parts/index/edit.js +2 -2
  108. package/addon/routes/maintenance/parts/index/new.js +14 -1
  109. package/addon/routes/maintenance/parts.js +14 -1
  110. package/addon/routes/maintenance/schedules/index/details.js +2 -2
  111. package/addon/routes/maintenance/schedules/index/edit.js +2 -2
  112. package/addon/routes/maintenance/schedules/index/new.js +15 -1
  113. package/addon/routes/maintenance/schedules.js +15 -1
  114. package/addon/routes/maintenance/work-orders/index/details.js +2 -2
  115. package/addon/routes/maintenance/work-orders/index/edit.js +2 -2
  116. package/addon/routes/maintenance/work-orders/index/new.js +14 -1
  117. package/addon/routes/maintenance/work-orders.js +14 -1
  118. package/addon/routes/management/fuel-transactions/index/details.js +9 -0
  119. package/addon/routes/management/fuel-transactions.js +14 -1
  120. package/addon/routes/management/index.js +13 -0
  121. package/addon/routes/management/trailers.js +16 -0
  122. package/addon/routes/management/vendors/integrated/details.js +14 -1
  123. package/addon/routes/management/vendors/integrated/edit.js +14 -1
  124. package/addon/routes/management/vendors/integrated/new.js +14 -1
  125. package/addon/routes/management/vendors/integrated.js +14 -1
  126. package/addon/routes/operations/orders/index/details.js +1 -0
  127. package/addon/routes/operations/orders.js +40 -1
  128. package/addon/routes/operations/routes/index/details.js +14 -1
  129. package/addon/routes/operations/routes/index/new.js +13 -0
  130. package/addon/routes/operations/routes.js +14 -1
  131. package/addon/routes/operations/service-rates/index/new.js +11 -0
  132. package/addon/routes/operations/service-rates.js +14 -1
  133. package/addon/routes/settings/avatars.js +14 -1
  134. package/addon/routes/settings/custom-fields.js +14 -1
  135. package/addon/routes/settings/map.js +14 -1
  136. package/addon/routes/settings/notifications.js +11 -0
  137. package/addon/routes/settings/orchestrator.js +1 -1
  138. package/addon/routes/settings/payments/index.js +1 -1
  139. package/addon/routes/settings/routing.js +14 -1
  140. package/addon/routes/settings/scheduling.js +14 -1
  141. package/addon/routes/settings/telematics.js +21 -0
  142. package/addon/routes/virtual.js +11 -0
  143. package/addon/routes.js +1 -0
  144. package/addon/services/contact-actions.js +1 -0
  145. package/addon/services/customer-actions.js +2 -1
  146. package/addon/services/device-actions.js +1 -0
  147. package/addon/services/device-event-actions.js +1 -0
  148. package/addon/services/driver-actions.js +4 -13
  149. package/addon/services/equipment-actions.js +1 -0
  150. package/addon/services/fleet-actions.js +1 -0
  151. package/addon/services/fuel-report-actions.js +1 -0
  152. package/addon/services/inspection-form-actions.js +1 -0
  153. package/addon/services/inspection-submission-actions.js +1 -0
  154. package/addon/services/integrated-vendor-actions.js +1 -0
  155. package/addon/services/issue-actions.js +1 -0
  156. package/addon/services/maintenance-actions.js +1 -0
  157. package/addon/services/maintenance-schedule-actions.js +2 -1
  158. package/addon/services/order-actions.js +7 -0
  159. package/addon/services/order-presentation.js +129 -0
  160. package/addon/services/part-actions.js +1 -0
  161. package/addon/services/place-actions.js +1 -0
  162. package/addon/services/sensor-actions.js +1 -0
  163. package/addon/services/service-area-actions.js +1 -0
  164. package/addon/services/service-rate-actions.js +1 -0
  165. package/addon/services/telematic-actions.js +1 -0
  166. package/addon/services/trailer-actions.js +4 -14
  167. package/addon/services/vehicle-actions.js +4 -13
  168. package/addon/services/vendor-actions.js +1 -0
  169. package/addon/services/work-order-actions.js +1 -0
  170. package/addon/services/zone-actions.js +1 -0
  171. package/addon/templates/analytics/reports/index/details.hbs +1 -0
  172. package/addon/templates/analytics/reports/index.hbs +1 -0
  173. package/addon/templates/connectivity/devices/index/details.hbs +1 -0
  174. package/addon/templates/connectivity/devices/index.hbs +1 -0
  175. package/addon/templates/connectivity/events/index.hbs +1 -0
  176. package/addon/templates/connectivity/sensors/index/details.hbs +1 -0
  177. package/addon/templates/connectivity/sensors/index.hbs +1 -0
  178. package/addon/templates/maintenance/equipment/index/details.hbs +1 -0
  179. package/addon/templates/maintenance/equipment/index.hbs +2 -0
  180. package/addon/templates/maintenance/inspection-forms/index/details.hbs +1 -0
  181. package/addon/templates/maintenance/inspection-forms/index.hbs +1 -0
  182. package/addon/templates/maintenance/inspection-submissions/index/details.hbs +1 -0
  183. package/addon/templates/maintenance/inspection-submissions/index.hbs +1 -0
  184. package/addon/templates/maintenance/maintenances/index/details.hbs +1 -0
  185. package/addon/templates/maintenance/maintenances/index.hbs +1 -0
  186. package/addon/templates/maintenance/parts/index/details.hbs +1 -0
  187. package/addon/templates/maintenance/parts/index.hbs +2 -0
  188. package/addon/templates/maintenance/schedules/index/details.hbs +1 -0
  189. package/addon/templates/maintenance/schedules/index.hbs +2 -0
  190. package/addon/templates/maintenance/work-orders/index/details.hbs +1 -0
  191. package/addon/templates/maintenance/work-orders/index.hbs +1 -0
  192. package/addon/templates/management/contacts/customers/details.hbs +1 -0
  193. package/addon/templates/management/contacts/customers.hbs +1 -0
  194. package/addon/templates/management/contacts/index/details.hbs +1 -0
  195. package/addon/templates/management/contacts/index.hbs +1 -0
  196. package/addon/templates/management/contacts.hbs +1 -0
  197. package/addon/templates/management/drivers/index/details.hbs +1 -0
  198. package/addon/templates/management/drivers/index.hbs +2 -0
  199. package/addon/templates/management/fleets/index/details.hbs +1 -0
  200. package/addon/templates/management/fleets/index.hbs +1 -0
  201. package/addon/templates/management/fuel-reports/index/details.hbs +1 -0
  202. package/addon/templates/management/fuel-reports/index.hbs +1 -0
  203. package/addon/templates/management/fuel-transactions/index.hbs +1 -0
  204. package/addon/templates/management/issues/index/details.hbs +1 -0
  205. package/addon/templates/management/issues/index.hbs +1 -0
  206. package/addon/templates/management/places/index/details.hbs +1 -0
  207. package/addon/templates/management/places/index.hbs +1 -0
  208. package/addon/templates/management/trailers/index/details.hbs +1 -0
  209. package/addon/templates/management/trailers/index.hbs +2 -0
  210. package/addon/templates/management/vehicles/index/details.hbs +1 -0
  211. package/addon/templates/management/vehicles/index.hbs +2 -0
  212. package/addon/templates/management/vendors/index/details.hbs +1 -0
  213. package/addon/templates/management/vendors/index.hbs +2 -0
  214. package/addon/templates/management/vendors/integrated.hbs +1 -0
  215. package/addon/templates/operations/orders/index/details.hbs +1 -0
  216. package/addon/templates/operations/orders/index.hbs +1 -0
  217. package/addon/templates/operations/service-rates/index/details.hbs +1 -0
  218. package/addon/templates/operations/service-rates/index.hbs +1 -0
  219. package/addon/templates/settings/telematics.hbs +70 -0
  220. package/addon/utils/normalize-order-config-flow.js +6 -1
  221. package/app/components/admin/telematics-settings.js +1 -0
  222. package/app/components/map/marker-card/driver.js +1 -0
  223. package/app/components/map/marker-card/vehicle.js +1 -0
  224. package/app/controllers/settings/telematics.js +1 -0
  225. package/app/routes/settings/telematics.js +1 -0
  226. package/app/services/order-presentation.js +1 -0
  227. package/app/templates/settings/telematics.js +1 -0
  228. package/composer.json +2 -2
  229. package/docs/AFAQY.md +1 -1
  230. package/docs/TELEMATICS_QUEUES.md +1 -1
  231. package/docs/TELEMETRY_ARCHITECTURE.md +72 -1
  232. package/extension.json +1 -1
  233. package/package.json +3 -3
  234. package/server/config/telemetry.php +12 -0
  235. package/server/migrations/2026_09_23_000001_add_retention_index_to_device_events.php +40 -0
  236. package/server/migrations/2026_09_23_000002_add_retention_index_to_positions.php +40 -0
  237. package/server/migrations/2026_09_23_000003_add_retention_index_to_telematic_sync_runs.php +40 -0
  238. package/server/src/Auth/Schemas/FleetOps.php +104 -5
  239. package/server/src/Console/Commands/DrainTelematicInbox.php +3 -13
  240. package/server/src/Console/Commands/ProcessOperationalAlerts.php +3 -3
  241. package/server/src/Console/Commands/PruneTelematicsData.php +318 -0
  242. package/server/src/Http/Controllers/Api/v1/OrchestrationController.php +9 -0
  243. package/server/src/Http/Controllers/FleetOpsController.php +21 -0
  244. package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +11 -0
  245. package/server/src/Http/Controllers/Internal/v1/ContactController.php +11 -0
  246. package/server/src/Http/Controllers/Internal/v1/CustomerController.php +14 -0
  247. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +13 -0
  248. package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +11 -0
  249. package/server/src/Http/Controllers/Internal/v1/DriverController.php +22 -0
  250. package/server/src/Http/Controllers/Internal/v1/FuelProviderConnectionController.php +13 -0
  251. package/server/src/Http/Controllers/Internal/v1/FuelProviderTransactionController.php +15 -0
  252. package/server/src/Http/Controllers/Internal/v1/FuelReportController.php +9 -3
  253. package/server/src/Http/Controllers/Internal/v1/InspectionFormController.php +15 -0
  254. package/server/src/Http/Controllers/Internal/v1/LiveController.php +16 -0
  255. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +15 -0
  256. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +15 -0
  257. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +15 -0
  258. package/server/src/Http/Controllers/Internal/v1/MetricsController.php +11 -0
  259. package/server/src/Http/Controllers/Internal/v1/NavigatorController.php +42 -1
  260. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +12 -0
  261. package/server/src/Http/Controllers/Internal/v1/OrderController.php +56 -4
  262. package/server/src/Http/Controllers/Internal/v1/PaymentController.php +13 -0
  263. package/server/src/Http/Controllers/Internal/v1/RadarController.php +19 -0
  264. package/server/src/Http/Controllers/Internal/v1/ServiceAreaController.php +9 -3
  265. package/server/src/Http/Controllers/Internal/v1/SettingController.php +465 -14
  266. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +21 -0
  267. package/server/src/Http/Controllers/Internal/v1/TrailerController.php +21 -0
  268. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +21 -0
  269. package/server/src/Http/Controllers/Internal/v1/VendorController.php +17 -0
  270. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +11 -0
  271. package/server/src/Jobs/DispatchTelematicsRetentionJobs.php +70 -0
  272. package/server/src/Jobs/PruneTelematicsDataJob.php +74 -0
  273. package/server/src/Listeners/NotifyDriverOnShiftChange.php +3 -3
  274. package/server/src/Mail/CustomerCredentialsMail.php +1 -1
  275. package/server/src/Models/Order.php +35 -3
  276. package/server/src/Models/OrderConfig.php +129 -0
  277. package/server/src/Models/TrackingNumber.php +8 -4
  278. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +9 -1
  279. package/server/src/Providers/FleetOpsServiceProvider.php +3 -0
  280. package/server/src/Support/Analytics/LiveFleet.php +18 -2
  281. package/server/src/Support/Authorization.php +103 -0
  282. package/server/src/Support/Database/TableIndexes.php +66 -0
  283. package/server/src/Support/OrderTracker.php +2 -2
  284. package/server/src/Support/Reporting/FleetOpsReportSchema.php +476 -116
  285. package/server/src/Support/Telematics/Retention/RetentionPolicy.php +300 -0
  286. package/server/src/Support/Telematics/Retention/TelemetryActivity.php +40 -0
  287. package/server/src/Support/Telematics/TelematicService.php +12 -2
  288. package/server/src/Support/Telematics/Telemetry/Ingestor.php +4 -2
  289. package/server/src/Tracking/TrackingIntelligenceService.php +1 -1
  290. package/server/src/Tracking/TrackingOptions.php +2 -2
  291. package/server/src/Traits/AuthorizesMethods.php +37 -0
  292. package/server/src/routes.php +9 -1
  293. package/server/tests/AnalyticsRoutesTest.php +29 -2
  294. package/server/tests/Feature/Http/AfaqyRealtimeIngestionTest.php +16 -1
  295. package/server/tests/Feature/Http/Api/TelematicsRetentionIndexMigrationTest.php +115 -0
  296. package/server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php +72 -0
  297. package/server/tests/Feature/Http/Internal/OrderControllerCreateRecordTest.php +26 -0
  298. package/server/tests/Feature/Http/Internal/SettingControllerHelpersTest.php +71 -0
  299. package/server/tests/Feature/Http/Internal/TelematicsStorageDeadlinesTest.php +119 -0
  300. package/server/tests/InternalImportExportControllerContractsTest.php +5 -8
  301. package/server/tests/NavigatorControllerContractsTest.php +36 -4
  302. package/server/tests/NotificationAndMailContractsTest.php +31 -0
  303. package/server/tests/ObserverContractsTest.php +1 -1
  304. package/server/tests/OrchestratorConsumableApiTest.php +42 -0
  305. package/server/tests/OrderConfigLifecycleTest.php +120 -0
  306. package/server/tests/ProviderContractsTest.php +10 -0
  307. package/server/tests/ReportSchemaContractsTest.php +120 -6
  308. package/server/tests/SettingControllerContractsTest.php +429 -18
  309. package/server/tests/SmallControllerContractsTest.php +8 -5
  310. package/server/tests/Support/PruneTelematicsDataProbe.php +55 -0
  311. package/server/tests/Unit/Console/Commands/ProcessOperationalAlertsCommandTest.php +2 -2
  312. package/server/tests/Unit/Console/PruneTelematicsDataCommandTest.php +353 -0
  313. package/server/tests/Unit/Events/OrderDispatchFailedTest.php +2 -1
  314. package/server/tests/Unit/Jobs/DispatchTelematicsRetentionJobsTest.php +114 -0
  315. package/server/tests/Unit/Jobs/PruneTelematicsDataJobTest.php +60 -0
  316. package/server/tests/Unit/Orchestration/VroomEngineTransportAndSettingsTest.php +20 -0
  317. package/server/tests/Unit/Support/Analytics/LiveFleetTest.php +6 -0
  318. package/server/tests/Unit/Support/AuthorizationTest.php +142 -0
  319. package/server/tests/Unit/Support/MaintainableAndOperationalAlertsTest.php +12 -1
  320. package/server/tests/Unit/Support/MoreQuerySeamsTest.php +11 -2
  321. package/server/tests/Unit/Support/Telematics/Retention/RetentionPolicyTest.php +190 -0
  322. package/server/tests/Unit/Support/Telematics/Retention/TelemetryActivityTest.php +68 -0
  323. package/server/tests/Unit/Tracking/TrackingOptionsCompanySettingsTest.php +109 -0
  324. package/translations/en-us.yaml +108 -0
@@ -2,18 +2,54 @@
2
2
 
3
3
  namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
4
4
 
5
+ use Fleetbase\FleetOps\Jobs\DispatchTelematicsRetentionJobs;
6
+ use Fleetbase\FleetOps\Support\Telematics\Retention\RetentionPolicy;
7
+ use Fleetbase\FleetOps\Support\Telematics\Telemetry\Queue;
5
8
  use Fleetbase\FleetOps\Tracking\TrackingProviderRegistry;
9
+ use Fleetbase\FleetOps\Traits\AuthorizesMethods;
6
10
  use Fleetbase\Http\Controllers\Controller;
7
11
  use Fleetbase\Models\Setting;
8
12
  use Fleetbase\Support\Auth;
9
13
  use Fleetbase\Support\NotificationRegistry;
14
+ use Illuminate\Database\MySqlConnection;
15
+ use Illuminate\Database\QueryException;
10
16
  use Illuminate\Http\Request;
17
+ use Illuminate\Support\Facades\DB;
11
18
 
12
19
  /**
13
20
  * Class SettingController.
14
21
  */
15
22
  class SettingController extends Controller
16
23
  {
24
+ use AuthorizesMethods;
25
+
26
+ public function __construct()
27
+ {
28
+ $this->authorizeMethods([
29
+ 'saveEntityEditingSettings' => 'update navigator-settings',
30
+ 'savedDriverOnboardSettings' => 'update navigator-settings',
31
+ 'saveCustomerEnabledOrderConfigs' => 'update order-config',
32
+ 'saveCustomerPortalPaymentConfig' => 'update payments',
33
+ 'saveNotificationSettings' => 'update notification-settings',
34
+ 'saveRoutingSettings' => 'update routing-settings',
35
+ 'saveTrackingSettings' => 'update tracking-settings',
36
+ 'getAdminTrackingSettings' => 'admin',
37
+ 'saveAdminTrackingSettings' => 'admin',
38
+ 'saveMapSettings' => 'update map-settings',
39
+ 'getAdminMapSettings' => 'admin',
40
+ 'saveAdminMapSettings' => 'admin',
41
+ 'saveSchedulingSettings' => 'update scheduling-settings',
42
+ 'saveOrchestratorSettings' => 'update routing-settings',
43
+ 'saveOrchestratorCardFields' => 'update routing-settings',
44
+ 'getAdminTelematicsSettings' => 'admin',
45
+ 'saveAdminTelematicsSettings' => 'admin',
46
+ 'getTelematicsSettings' => 'view telematics-settings',
47
+ 'saveTelematicsSettings' => 'update telematics-settings',
48
+ 'getTelematicsStorageUsage' => 'admin',
49
+ 'runTelematicsRetention' => 'admin',
50
+ ]);
51
+ }
52
+
17
53
  /**
18
54
  * Save entity editing settings.
19
55
  *
@@ -21,37 +57,54 @@ class SettingController extends Controller
21
57
  */
22
58
  public function saveEntityEditingSettings(Request $request)
23
59
  {
24
- $entityEditingSettings = $request->input('entityEditingSettings', []);
60
+ // The setting is one platform-wide map keyed by order config id. Only this
61
+ // company's order configs may be written, and other companies' entries are kept.
62
+ $ownKeys = $this->companyOrderConfigKeys();
63
+ $incoming = array_intersect_key((array) $request->input('entityEditingSettings', []), array_flip($ownKeys));
64
+ $existing = (array) ($this->settingValue('fleet-ops.entity-editing-settings') ?? []);
65
+ $merged = array_merge(array_diff_key($existing, array_flip($ownKeys)), $incoming);
25
66
 
26
- // Save entity editing settings
27
- $this->configureSetting('fleet-ops.entity-editing-settings', $entityEditingSettings);
67
+ $this->configureSetting('fleet-ops.entity-editing-settings', $merged);
28
68
 
29
- return response()->json(['entityEditingSettings' => $entityEditingSettings]);
69
+ return response()->json(['entityEditingSettings' => $incoming]);
30
70
  }
31
71
 
32
72
  /**
33
- * Retrieve entity editing settings.
73
+ * Retrieve entity editing settings for this company's order configs.
34
74
  *
35
75
  * @return \Illuminate\Http\JsonResponse
36
76
  */
37
77
  public function getEntityEditingSettings()
38
78
  {
39
- $entityEditingSettings = $this->settingValue('fleet-ops.entity-editing-settings');
40
- if (!$entityEditingSettings) {
41
- $entityEditingSettings = [];
42
- }
79
+ $entityEditingSettings = (array) ($this->settingValue('fleet-ops.entity-editing-settings') ?? []);
80
+ $entityEditingSettings = array_intersect_key($entityEditingSettings, array_flip($this->companyOrderConfigKeys()));
43
81
 
44
82
  return response()->json(['entityEditingSettings' => $entityEditingSettings]);
45
83
  }
46
84
 
47
85
  /**
48
- * Retrieve driver onboard settings.
86
+ * Ids (uuid and public id) of the session company's order configs.
87
+ */
88
+ protected function companyOrderConfigKeys(): array
89
+ {
90
+ return \Fleetbase\FleetOps\Models\OrderConfig::where('company_uuid', session('company'))
91
+ ->get(['uuid', 'public_id'])
92
+ ->flatMap(fn ($config) => array_filter([$config->uuid, $config->public_id]))
93
+ ->values()
94
+ ->all();
95
+ }
96
+
97
+ /**
98
+ * Retrieve driver onboard settings for the session company.
99
+ *
100
+ * The route still carries a company id for backwards compatibility, but only
101
+ * the session company's settings are ever returned.
49
102
  *
50
103
  * @return \Illuminate\Http\JsonResponse
51
104
  */
52
- public function getDriverOnboardSettings($companyId)
105
+ public function getDriverOnboardSettings($companyId = null)
53
106
  {
54
- $driverOnboardSettings = $this->settingValue('fleet-ops.driver-onboard-settings.' . $companyId);
107
+ $driverOnboardSettings = $this->settingValue('fleet-ops.driver-onboard-settings.' . session('company'));
55
108
  if (!$driverOnboardSettings) {
56
109
  $driverOnboardSettings = [];
57
110
  }
@@ -66,9 +119,10 @@ class SettingController extends Controller
66
119
  */
67
120
  public function savedDriverOnboardSettings(Request $request)
68
121
  {
69
- $driverOnboardSettings = $request->array('driverOnboardSettings', []);
122
+ $driverOnboardSettings = $request->array('driverOnboardSettings', []);
123
+ $driverOnboardSettings['companyId'] = session('company');
70
124
 
71
- if ($driverOnboardSettings['enableDriverOnboardFromApp'] == false) {
125
+ if (empty($driverOnboardSettings['enableDriverOnboardFromApp'])) {
72
126
  $driverOnboardSettings['driverMustProvideOnboardDoucments'] = false;
73
127
  $driverOnboardSettings['requiredOnboardDocuments'] = [];
74
128
  $driverOnboardSettings['driverOnboardAppMethod'] = '';
@@ -628,6 +682,403 @@ class SettingController extends Controller
628
682
  })->values()->all();
629
683
  }
630
684
 
685
+ /**
686
+ * Retrieve customer history preferences for the current company.
687
+ *
688
+ * Company values fall back to the system defaults (admin setting over
689
+ * package config); the response carries both so the UI can show them.
690
+ *
691
+ * @return \Illuminate\Http\JsonResponse
692
+ */
693
+ public function getTelematicsSettings()
694
+ {
695
+ if (!$this->currentCompany()) {
696
+ return response()->error('No company session.', 401);
697
+ }
698
+
699
+ return response()->json($this->telematicsCompanySettings((array) $this->lookupFromCompanySetting(RetentionPolicy::SETTING_KEY, [])));
700
+ }
701
+
702
+ /**
703
+ * Save customer history preferences within the administrator's policy.
704
+ *
705
+ * @return \Illuminate\Http\JsonResponse
706
+ */
707
+ public function saveTelematicsSettings(Request $request)
708
+ {
709
+ $company = $this->currentCompany();
710
+ if (!$company) {
711
+ return response()->error('No company session.', 401);
712
+ }
713
+
714
+ $request->validate([
715
+ 'event_retention_days' => ['nullable', 'integer', 'min:0', 'max:3650'],
716
+ 'position_retention_days' => ['nullable', 'integer', 'min:0', 'max:3650'],
717
+ ]);
718
+
719
+ $saved = $this->withTelematicsPolicyLock(function () use ($request) {
720
+ $preferences = RetentionPolicy::constrainCompanyPreferences($request->only(RetentionPolicy::HISTORY_KEYS), $this->telematicsDefaults());
721
+
722
+ return $this->configureCompanySetting(RetentionPolicy::SETTING_KEY, $preferences);
723
+ });
724
+ if ($saved === false) {
725
+ return response()->error('Unable to save telematics history preferences.', 500);
726
+ }
727
+ RetentionPolicy::flush($company->uuid);
728
+
729
+ return response()->json(array_merge($this->getTelematicsSettings()->getData(true), [
730
+ 'status' => 'ok',
731
+ 'message' => 'Telematics history preferences successfully saved.',
732
+ ]));
733
+ }
734
+
735
+ /**
736
+ * Retrieve the system-wide telematics retention defaults.
737
+ *
738
+ * @return \Illuminate\Http\JsonResponse
739
+ */
740
+ public function getAdminTelematicsSettings()
741
+ {
742
+ return response()->json(array_merge($this->telematicsDefaults(), [
743
+ 'limits' => RetentionPolicy::LIMITS,
744
+ ]));
745
+ }
746
+
747
+ /**
748
+ * Save the system-wide telematics retention defaults.
749
+ *
750
+ * @return \Illuminate\Http\JsonResponse
751
+ */
752
+ public function saveAdminTelematicsSettings(Request $request)
753
+ {
754
+ $request->validate([
755
+ 'max_event_retention_days' => ['sometimes', 'integer', 'min:0', 'max:3650'],
756
+ 'max_position_retention_days' => ['sometimes', 'integer', 'min:0', 'max:3650'],
757
+ ]);
758
+
759
+ try {
760
+ $settings = $this->withTelematicsPolicyLock(function () use ($request) {
761
+ $settings = RetentionPolicy::normalize($request->only(RetentionPolicy::keys()), $this->telematicsDefaults());
762
+ $this->configureSetting(RetentionPolicy::SETTING_KEY, $settings);
763
+
764
+ return $settings;
765
+ });
766
+ $this->reconcileTelematicsCompanyPreferences($settings);
767
+ } finally {
768
+ RetentionPolicy::flush();
769
+ }
770
+
771
+ return response()->json($this->getAdminTelematicsSettings()->getData(true));
772
+ }
773
+
774
+ /**
775
+ * Report system-wide telematics storage, including data without an owner.
776
+ *
777
+ * Use table statistics for MySQL/MariaDB instead of scanning every tenant's
778
+ * history. Oldest-row lookups and fallback counts have a database deadline.
779
+ *
780
+ * @return \Illuminate\Http\JsonResponse
781
+ */
782
+ public function getTelematicsStorageUsage()
783
+ {
784
+ $ageColumns = [
785
+ 'device_events' => 'created_at',
786
+ 'positions' => 'created_at',
787
+ 'telematic_deliveries' => 'received_at',
788
+ 'telematic_sync_runs' => 'created_at',
789
+ ];
790
+ $statistics = $this->storageTableStatistics(array_keys($ageColumns));
791
+ $allRows = fn ($query) => $query;
792
+ $tables = [];
793
+ foreach ($ageColumns as $table => $ageColumn) {
794
+ $tables[$table] = isset($statistics[$table])
795
+ ? array_merge($statistics[$table], ['oldest' => $this->tableOldest($table, $allRows, $ageColumn)])
796
+ : $this->tableUsage($table, $allRows, $ageColumn);
797
+ }
798
+
799
+ // Payload details require reading JSON-bearing rows, so they are opt-in
800
+ // and omitted if they cannot be counted within the database deadline.
801
+ if (request()->boolean('include_payload_counts', false)) {
802
+ try {
803
+ $rawPayloadRows = $this->tableCount('device_events', fn ($query) => $query->whereNotNull('payload'));
804
+ $compactAfter = (int) $this->telematicsDefaults()['event_compact_after_days'];
805
+ $compactableRows = $compactAfter > 0
806
+ ? $this->tableCount('device_events', fn ($query) => $query->whereNotNull('payload')->where('created_at', '<', now()->subDays($compactAfter)->toDateTimeString()))
807
+ : 0;
808
+
809
+ $tables['device_events']['raw_payload_rows'] = $rawPayloadRows;
810
+ $tables['device_events']['compactable_rows'] = $compactableRows;
811
+ } catch (QueryException $exception) {
812
+ if (!$this->isStorageUsageTimeout($exception)) {
813
+ throw $exception;
814
+ }
815
+ }
816
+ }
817
+
818
+ return response()->json([
819
+ 'tables' => $tables,
820
+ 'scope' => 'system',
821
+ 'generated_at' => now()->toISOString(),
822
+ ]);
823
+ }
824
+
825
+ /**
826
+ * Queue retention for all companies and orphaned data, independent of session company.
827
+ *
828
+ * @return \Illuminate\Http\JsonResponse
829
+ */
830
+ public function runTelematicsRetention()
831
+ {
832
+ $connection = (string) config('queue.default', 'sync');
833
+ $driver = config('queue.connections.' . $connection . '.driver', $connection);
834
+ if (in_array($driver, ['sync', 'null'], true)) {
835
+ return response()->error('System-wide telematics cleanup requires an asynchronous queue connection and a running queue worker.', 503);
836
+ }
837
+
838
+ $this->dispatchTelematicsPrune();
839
+
840
+ return response()->json([
841
+ 'status' => 'queued',
842
+ 'scope' => 'system',
843
+ 'message' => 'System-wide telematics cleanup has been queued.',
844
+ ], 202);
845
+ }
846
+
847
+ /**
848
+ * System-wide retention defaults: package config overridden by the admin setting.
849
+ */
850
+ protected function telematicsDefaults(): array
851
+ {
852
+ $config = array_intersect_key((array) config('telematics.telemetry', []), RetentionPolicy::FALLBACKS);
853
+
854
+ return RetentionPolicy::normalize((array) $this->lookupSetting(RetentionPolicy::SETTING_KEY, []), RetentionPolicy::normalize($config, RetentionPolicy::FALLBACKS));
855
+ }
856
+
857
+ /**
858
+ * Customer responses expose history choices and constraints, never internal
859
+ * ingestion, cleanup, storage or activity logging configuration.
860
+ */
861
+ protected function telematicsCompanySettings(array $stored, ?array $defaults = null): array
862
+ {
863
+ $defaults = $defaults ?? $this->telematicsDefaults();
864
+ $history = array_flip(RetentionPolicy::HISTORY_KEYS);
865
+ $preferences = RetentionPolicy::constrainCompanyPreferences($stored, $defaults);
866
+ $effective = RetentionPolicy::fromCompanyPreferences($preferences, $defaults)->toArray();
867
+ $inherited = RetentionPolicy::fromCompanyPreferences([], $defaults)->toArray();
868
+
869
+ return array_merge(array_intersect_key($effective, $history), [
870
+ 'preferences' => array_merge(array_fill_keys(RetentionPolicy::HISTORY_KEYS, null), $preferences),
871
+ 'defaults' => array_intersect_key($inherited, $history),
872
+ 'policy' => array_intersect_key($defaults, array_flip(array_values(RetentionPolicy::MAXIMUMS))),
873
+ 'limits' => array_intersect_key(RetentionPolicy::LIMITS, $history),
874
+ ]);
875
+ }
876
+
877
+ /**
878
+ * Serialize policy changes with a customer's policy lookup and preference
879
+ * write, so a request cannot save an old unlimited choice after a new cap.
880
+ * The empty row is only a lock anchor and keeps config fallbacks intact.
881
+ */
882
+ protected function withTelematicsPolicyLock(\Closure $callback): mixed
883
+ {
884
+ $setting = new Setting();
885
+ if (!$setting->newQuery()->where('key', RetentionPolicy::SETTING_KEY)->exists()) {
886
+ $setting->newQuery()->insertOrIgnore(['key' => RetentionPolicy::SETTING_KEY, 'value' => '[]']);
887
+ }
888
+
889
+ return $setting->getConnection()->transaction(function () use ($setting, $callback) {
890
+ $setting->newQuery()->where('key', RetentionPolicy::SETTING_KEY)->lockForUpdate()->firstOrFail();
891
+
892
+ return $callback();
893
+ }, 3);
894
+ }
895
+
896
+ /**
897
+ * A newly imposed maximum replaces existing unlimited or longer explicit
898
+ * choices for every organization. Keep inherited preferences absent so they
899
+ * continue following future system defaults. Process settings in bounded
900
+ * batches rather than loading every organization or its telemetry history.
901
+ */
902
+ protected function reconcileTelematicsCompanyPreferences(array $defaults): void
903
+ {
904
+ $maximums = array_intersect_key($defaults, array_flip(array_values(RetentionPolicy::MAXIMUMS)));
905
+ if (!array_filter($maximums, fn ($maximum) => $maximum > 0)) {
906
+ return;
907
+ }
908
+
909
+ Setting::query()
910
+ ->where('key', 'like', 'company.%.' . RetentionPolicy::SETTING_KEY)
911
+ ->select('id')
912
+ ->chunkById(200, function ($settings) {
913
+ foreach ($settings as $setting) {
914
+ // Lock policy then company, as customer saves do. Reread
915
+ // both so a later admin change or customer preference is
916
+ // never overwritten using an earlier batch's snapshot.
917
+ $this->withTelematicsPolicyLock(function () use ($setting) {
918
+ $current = Setting::query()->whereKey($setting->getKey())->lockForUpdate()->first();
919
+ if (!$current) {
920
+ return;
921
+ }
922
+
923
+ $stored = (array) $current->value;
924
+ $preferences = RetentionPolicy::companyPreferences($stored);
925
+ $constrained = RetentionPolicy::constrainCompanyPreferences($preferences, $this->telematicsDefaults());
926
+ if ($preferences === $constrained) {
927
+ return;
928
+ }
929
+
930
+ $current->value = array_replace($stored, $constrained);
931
+ $current->save();
932
+ });
933
+ }
934
+ });
935
+ }
936
+
937
+ protected function tableUsage(string $table, \Closure $scope, string $ageColumn): array
938
+ {
939
+ if (!in_array(DB::connection()->getDriverName(), ['mysql', 'mariadb'], true)) {
940
+ return [
941
+ 'rows' => $this->tableCount($table, $scope),
942
+ 'oldest' => $this->tableOldest($table, $scope, $ageColumn),
943
+ ];
944
+ }
945
+
946
+ $query = $scope(DB::table($table));
947
+ $query->selectRaw('COUNT(*) AS row_count, MIN(' . $query->getGrammar()->wrap($ageColumn) . ') AS oldest');
948
+
949
+ try {
950
+ $usage = $this->storageUsageSelect($query->toSql(), $query->getBindings())[0];
951
+
952
+ return [
953
+ 'rows' => (int) $usage->row_count,
954
+ 'oldest' => $usage->oldest === null ? null : (string) $usage->oldest,
955
+ ];
956
+ } catch (QueryException $exception) {
957
+ if (!$this->isStorageUsageTimeout($exception)) {
958
+ throw $exception;
959
+ }
960
+ }
961
+
962
+ // EXPLAIN does not execute the history scan. The caller's scope is
963
+ // preserved when metadata is unavailable and a count reaches its deadline.
964
+ $estimate = null;
965
+ $query = $scope(DB::table($table))->select($ageColumn);
966
+ try {
967
+ $plan = $this->storageUsageSelect($query->toSql(), $query->getBindings(), true)[0] ?? null;
968
+ if (isset($plan->rows)) {
969
+ $estimate = max(0, (int) round((float) $plan->rows * (float) ($plan->filtered ?? 100) / 100));
970
+ }
971
+ } catch (QueryException $exception) {
972
+ if (!$this->isStorageUsageTimeout($exception)) {
973
+ throw $exception;
974
+ }
975
+ }
976
+
977
+ return ['rows' => $estimate, 'oldest' => null, 'rows_estimated' => true];
978
+ }
979
+
980
+ protected function tableCount(string $table, \Closure $scope): int
981
+ {
982
+ $query = $scope(DB::table($table))->selectRaw('COUNT(*) AS row_count');
983
+ $usage = $this->storageUsageSelect($query->toSql(), $query->getBindings())[0];
984
+
985
+ return (int) $usage->row_count;
986
+ }
987
+
988
+ protected function tableOldest(string $table, \Closure $scope, string $column): ?string
989
+ {
990
+ $query = $scope(DB::table($table));
991
+ $query->selectRaw('MIN(' . $query->getGrammar()->wrap($column) . ') AS oldest');
992
+ try {
993
+ $oldest = $this->storageUsageSelect($query->toSql(), $query->getBindings())[0]->oldest ?? null;
994
+ } catch (QueryException $exception) {
995
+ if (!$this->isStorageUsageTimeout($exception)) {
996
+ throw $exception;
997
+ }
998
+
999
+ return null;
1000
+ }
1001
+
1002
+ return $oldest ? (string) $oldest : null;
1003
+ }
1004
+
1005
+ /**
1006
+ * Bound each MySQL/MariaDB scan to one second without changing persistent
1007
+ * connection settings. A browser abort alone leaves database work running.
1008
+ */
1009
+ protected function storageUsageSelect(string $sql, array $bindings = [], bool $explain = false): array
1010
+ {
1011
+ $connection = DB::connection();
1012
+ $driver = $connection->getDriverName();
1013
+ $isMariaDb = $driver === 'mariadb';
1014
+
1015
+ if ($connection instanceof MySqlConnection) {
1016
+ $isMariaDb = stripos((string) $connection->getReadPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION), 'MariaDB') !== false;
1017
+ }
1018
+
1019
+ if ($isMariaDb) {
1020
+ $sql = 'SET STATEMENT max_statement_time=1 FOR ' . ($explain ? 'EXPLAIN ' : '') . $sql;
1021
+ } else {
1022
+ if ($driver === 'mysql') {
1023
+ $sql = preg_replace('/^select\b/i', 'SELECT /*+ MAX_EXECUTION_TIME(1000) */', $sql, 1);
1024
+ }
1025
+ if ($explain) {
1026
+ $sql = 'EXPLAIN ' . $sql;
1027
+ }
1028
+ }
1029
+
1030
+ return $connection->select($sql, $bindings);
1031
+ }
1032
+
1033
+ protected function isStorageUsageTimeout(QueryException $exception): bool
1034
+ {
1035
+ return in_array((int) ($exception->errorInfo[1] ?? 0), [3024, 1969], true);
1036
+ }
1037
+
1038
+ /**
1039
+ * Whole-table row estimates and allocated data/index bytes. Empty on other drivers.
1040
+ *
1041
+ * @return array<string, array<string, int|bool|null>>
1042
+ */
1043
+ protected function storageTableStatistics(array $tables): array
1044
+ {
1045
+ $connection = DB::connection();
1046
+ if (!in_array($connection->getDriverName(), ['mysql', 'mariadb'], true)) {
1047
+ return [];
1048
+ }
1049
+
1050
+ $prefix = $connection->getTablePrefix();
1051
+ $placeholders = implode(', ', array_fill(0, count($tables), '?'));
1052
+ try {
1053
+ $rows = $this->storageUsageSelect(
1054
+ 'SELECT TABLE_NAME AS name, TABLE_ROWS AS row_count, DATA_LENGTH AS data_bytes, INDEX_LENGTH AS index_bytes FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME IN (' . $placeholders . ')',
1055
+ array_map(fn ($table) => $prefix . $table, $tables)
1056
+ );
1057
+ } catch (QueryException $exception) {
1058
+ if (!$this->isStorageUsageTimeout($exception)) {
1059
+ throw $exception;
1060
+ }
1061
+
1062
+ return [];
1063
+ }
1064
+
1065
+ $statistics = [];
1066
+ foreach ($rows as $row) {
1067
+ $statistics[substr($row->name, strlen($prefix))] = [
1068
+ 'rows' => $row->row_count === null ? null : (int) $row->row_count,
1069
+ 'rows_estimated' => true,
1070
+ 'estimated_bytes' => $row->data_bytes === null || $row->index_bytes === null ? null : (int) $row->data_bytes + (int) $row->index_bytes,
1071
+ ];
1072
+ }
1073
+
1074
+ return $statistics;
1075
+ }
1076
+
1077
+ protected function dispatchTelematicsPrune(): void
1078
+ {
1079
+ Queue::dispatch(new DispatchTelematicsRetentionJobs());
1080
+ }
1081
+
631
1082
  protected function configureSetting(string $key, mixed $value): mixed
632
1083
  {
633
1084
  return Setting::configure($key, $value);
@@ -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\FleetOps\Exports\TelematicExport;
6
7
  use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
7
8
  use Fleetbase\FleetOps\Models\Telematic;
@@ -16,6 +17,20 @@ use Maatwebsite\Excel\Facades\Excel;
16
17
 
17
18
  class TelematicController extends FleetOpsController
18
19
  {
20
+ /**
21
+ * Permissions for methods AuthorizationGuard cannot map to a schema action (see FleetOpsController).
22
+ *
23
+ * @var array<string, string|string[]>
24
+ */
25
+ protected array $methodPermissions = [
26
+ 'testConnection' => 'update telematic',
27
+ 'testCredentials' => 'create telematic',
28
+ 'discover' => 'update telematic',
29
+ 'linkDevice' => 'update telematic',
30
+ 'telemetryWebhook' => 'update telematic',
31
+ 'replayTelemetryDelivery' => 'update telematic',
32
+ ];
33
+
19
34
  /**
20
35
  * The resource to query.
21
36
  *
@@ -71,6 +86,7 @@ class TelematicController extends FleetOpsController
71
86
  /**
72
87
  * Test connection to provider.
73
88
  */
89
+ #[SkipAuthorizationCheck]
74
90
  public function testConnection(Request $request, string $id): JsonResponse
75
91
  {
76
92
  $telematic = $this->findTelematic($id);
@@ -89,6 +105,7 @@ class TelematicController extends FleetOpsController
89
105
  /**
90
106
  * Test connection to provider.
91
107
  */
108
+ #[SkipAuthorizationCheck]
92
109
  public function testCredentials(Request $request, string $key): JsonResponse
93
110
  {
94
111
  $credentials = $request->array('credentials', []);
@@ -120,6 +137,7 @@ class TelematicController extends FleetOpsController
120
137
  /**
121
138
  * Discover devices from provider.
122
139
  */
140
+ #[SkipAuthorizationCheck]
123
141
  public function discover(Request $request, string $id): JsonResponse
124
142
  {
125
143
  $telematic = $this->findTelematic($id);
@@ -181,6 +199,7 @@ class TelematicController extends FleetOpsController
181
199
  /**
182
200
  * Link a device to a telematic.
183
201
  */
202
+ #[SkipAuthorizationCheck]
184
203
  public function linkDevice(Request $request, string $id): JsonResponse
185
204
  {
186
205
  $telematic = $this->findTelematic($id);
@@ -199,6 +218,7 @@ class TelematicController extends FleetOpsController
199
218
  ], 201);
200
219
  }
201
220
 
221
+ #[SkipAuthorizationCheck]
202
222
  public function telemetryWebhook(Request $request, string $id): JsonResponse
203
223
  {
204
224
  $telematic = $this->findTelematic($id);
@@ -248,6 +268,7 @@ class TelematicController extends FleetOpsController
248
268
  ]);
249
269
  }
250
270
 
271
+ #[SkipAuthorizationCheck]
251
272
  public function replayTelemetryDelivery(string $id, string $delivery): JsonResponse
252
273
  {
253
274
  $telematic = $this->findTelematic($id);
@@ -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\FleetOps\Exceptions\DeviceAlreadyAttachedException;
6
7
  use Fleetbase\FleetOps\Exports\TrailerExport;
7
8
  use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
@@ -23,6 +24,20 @@ use Maatwebsite\Excel\Facades\Excel;
23
24
 
24
25
  class TrailerController extends FleetOpsController
25
26
  {
27
+ /**
28
+ * Permissions for methods AuthorizationGuard cannot map to a schema action (see FleetOpsController).
29
+ *
30
+ * @var array<string, string|string[]>
31
+ */
32
+ protected array $methodPermissions = [
33
+ 'attach' => 'attach-vehicle-for trailer',
34
+ 'detach' => 'detach-vehicle-for trailer',
35
+ 'attachDevice' => 'attach-device-for trailer',
36
+ 'detachDevice' => 'detach-device-for trailer',
37
+ 'attachEquipment' => 'attach-equipment-for trailer',
38
+ 'detachEquipment' => 'detach-equipment-for trailer',
39
+ ];
40
+
26
41
  /**
27
42
  * The resource to query.
28
43
  *
@@ -69,6 +84,7 @@ class TrailerController extends FleetOpsController
69
84
  /**
70
85
  * Attach the trailer to a vehicle. Re-attaching to the same vehicle is idempotent.
71
86
  */
87
+ #[SkipAuthorizationCheck]
72
88
  public function attach(Request $request, string $id)
73
89
  {
74
90
  $request->validate(['vehicle' => ['required', 'string'], 'position' => ['nullable', 'integer', 'min:1']]);
@@ -134,6 +150,7 @@ class TrailerController extends FleetOpsController
134
150
  /**
135
151
  * End the active towing connection. Detaching an unattached trailer is a no-op.
136
152
  */
153
+ #[SkipAuthorizationCheck]
137
154
  public function detach(string $id)
138
155
  {
139
156
  $trailer = $this->resolveTrailer($id);
@@ -157,6 +174,7 @@ class TrailerController extends FleetOpsController
157
174
  ]);
158
175
  }
159
176
 
177
+ #[SkipAuthorizationCheck]
160
178
  public function attachDevice(Request $request, string $id)
161
179
  {
162
180
  $request->validate(['device' => ['required', 'string']]);
@@ -186,6 +204,7 @@ class TrailerController extends FleetOpsController
186
204
  ]);
187
205
  }
188
206
 
207
+ #[SkipAuthorizationCheck]
189
208
  public function detachDevice(Request $request, string $id)
190
209
  {
191
210
  $request->validate(['device' => ['required', 'string']]);
@@ -215,6 +234,7 @@ class TrailerController extends FleetOpsController
215
234
  ]);
216
235
  }
217
236
 
237
+ #[SkipAuthorizationCheck]
218
238
  public function attachEquipment(Request $request, string $id)
219
239
  {
220
240
  $request->validate(['equipment' => ['required', 'string']]);
@@ -242,6 +262,7 @@ class TrailerController extends FleetOpsController
242
262
  ]);
243
263
  }
244
264
 
265
+ #[SkipAuthorizationCheck]
245
266
  public function detachEquipment(Request $request, string $id)
246
267
  {
247
268
  $request->validate(['equipment' => ['required', 'string']]);