@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,7 +2,9 @@
2
2
 
3
3
  namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
4
4
 
5
+ use Fleetbase\Attributes\SkipAuthorizationCheck;
5
6
  use Fleetbase\Exceptions\FleetbaseRequestValidationException;
7
+ use Fleetbase\FleetOps\Exceptions\ProfileIdentityConflictException;
6
8
  use Fleetbase\FleetOps\Exports\DriverExport;
7
9
  use Fleetbase\FleetOps\Http\Controllers\Api\v1\DriverController as ApiDriverController;
8
10
  use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
@@ -13,11 +15,11 @@ use Fleetbase\FleetOps\Imports\DriverImport;
13
15
  use Fleetbase\FleetOps\Models\Driver;
14
16
  use Fleetbase\FleetOps\Models\Order;
15
17
  use Fleetbase\FleetOps\Models\Vehicle;
18
+ use Fleetbase\FleetOps\Support\ProfileAccountManager;
16
19
  use Fleetbase\FleetOps\Support\Utils;
17
20
  use Fleetbase\Http\Requests\ExportRequest;
18
21
  use Fleetbase\Http\Requests\ImportRequest;
19
22
  use Fleetbase\LaravelMysqlSpatial\Types\Point;
20
- use Fleetbase\Models\Invite;
21
23
  use Fleetbase\Models\User;
22
24
  use Fleetbase\Models\VerificationCode;
23
25
  use Fleetbase\Support\Auth;
@@ -32,6 +34,20 @@ use Maatwebsite\Excel\Facades\Excel;
32
34
  class DriverController extends FleetOpsController
33
35
  {
34
36
  use Traits\DriverSchedulingTrait;
37
+ /**
38
+ * Permissions for methods AuthorizationGuard cannot map to a schema action (see FleetOpsController).
39
+ *
40
+ * @var array<string, string|string[]>
41
+ */
42
+ protected array $methodPermissions = [
43
+ 'sendCredentials' => 'update-user-for driver',
44
+ 'resetCredentials' => 'update-user-for driver',
45
+ 'deactivateLogin' => 'update-user-for driver',
46
+ 'reactivateLogin' => 'update-user-for driver',
47
+ 'unassignOrders' => 'assign-order-for driver',
48
+ 'unassignOrder' => 'assign-order-for driver',
49
+ 'unassignVehicle' => 'assign-vehicle-for driver',
50
+ ];
35
51
  /**
36
52
  * The resource to query.
37
53
  *
@@ -58,72 +74,6 @@ class DriverController extends FleetOpsController
58
74
  $validator = Validator::make($input, $rules);
59
75
 
60
76
  if ($validator->fails()) {
61
- // here if the user exists already
62
- // within organization: offer to create driver record
63
- // outside organization: invite to join organization AS DRIVER
64
- if ($validator->errors()->hasAny(['phone', 'email'])) {
65
- // get existing user
66
- $existingUser = null;
67
-
68
- // if values provided for user lookup
69
- if (!empty($input['phone']) || !empty($input['email'])) {
70
- $existingUserQuery = User::query();
71
-
72
- if (!empty($input['phone']) && is_string($input['phone'])) {
73
- $existingUserQuery->orWhere(function ($q) use ($input) {
74
- $q->where('phone', $input['phone'])->whereNotNull('phone');
75
- });
76
- }
77
-
78
- if (!empty($input['email']) && is_string($input['email'])) {
79
- $existingUserQuery->orWhere(function ($q) use ($input) {
80
- $q->where('email', $input['email'])->whereNotNull('email');
81
- });
82
- }
83
-
84
- $existingUser = $existingUserQuery->first();
85
- }
86
-
87
- if ($existingUser) {
88
- // if exists in organization create driver profile for user
89
- $isOrganizationMember = $existingUser->companies()->where('companies.uuid', session('company'))->exists();
90
-
91
- // Check if driver profile also already exists
92
- $existingDriverProfile = Driver::where(['company_uuid' => session('company'), 'user_uuid' => $existingUser->uuid])->first();
93
- if ($existingDriverProfile) {
94
- return ['driver' => new $this->resource($existingDriverProfile)];
95
- }
96
-
97
- // create driver profile for user
98
- $input = collect($input)
99
- ->except(['name', 'password', 'email', 'phone', 'meta', 'avatar_uuid', 'photo_uuid', 'status'])
100
- ->filter()
101
- ->toArray();
102
-
103
- // Get current session company
104
- $company = Auth::getCompany();
105
- $input['company_uuid'] = session('company', $company->uuid);
106
- $input['user_uuid'] = $existingUser->uuid;
107
- $input['slug'] = $existingUser->slug;
108
-
109
- // If no location provided set
110
- if (empty($input['location'])) {
111
- $input['location'] = new Point(0, 0);
112
- }
113
-
114
- // create the profile
115
- $driverProfile = Driver::create($input);
116
-
117
- // If not already a member of the company assign them to the company and send the user an invite
118
- if (!$isOrganizationMember && $company) {
119
- $existingUser->assignCompany($company);
120
- }
121
-
122
- return ['driver' => new $this->resource($driverProfile)];
123
- }
124
- }
125
-
126
- // check from validator object if phone or email is not unique
127
77
  return $createDriverRequest->responseWithErrors($validator);
128
78
  }
129
79
 
@@ -134,104 +84,36 @@ class DriverController extends FleetOpsController
134
84
  $input = collect($input);
135
85
 
136
86
  // Get current session company
137
- $company = Auth::getCompany();
87
+ $company = Auth::getCompany();
138
88
  if (!$company) {
139
89
  throw new \Exception('Unable to create driver.');
140
90
  }
141
91
 
142
- if ($input->has('user_uuid')) {
143
- $user = User::where('uuid', $input->get('user_uuid'))->first();
144
-
145
- // Check if a driver profile already exists for this user in the current company
146
- if ($user) {
147
- $existingDriver = Driver::where(['user_uuid' => $user->uuid, 'company_uuid' => session('company')])->first();
148
- if ($existingDriver) {
149
- throw new \Exception('This user account already belongs to a driver.');
150
- }
151
- }
152
-
153
- // If user doesn't exist with provided UUID, create new user
154
- if (!$user) {
155
- $userInput = $input
156
- ->only(['name', 'password', 'email', 'phone', 'status', 'avatar_uuid'])
157
- ->filter()
158
- ->toArray();
159
-
160
- // handle `photo_uuid`
161
- if (isset($input['photo_uuid']) && Str::isUuid($input['photo_uuid'])) {
162
- $userInput['avatar_uuid'] = $input['photo_uuid'];
163
- }
164
-
165
- // Make sure password is set
166
- if (empty($userInput['password'])) {
167
- $userInput['password'] = Str::random(14);
168
- }
169
-
170
- // Set user company
171
- $userInput['company_uuid'] = session('company', $company->uuid);
172
-
173
- // Apply user infos
174
- $userInput = User::applyUserInfoFromRequest($request, $userInput);
175
-
176
- // Create user account
177
- $user = User::create($userInput);
178
-
179
- // Set the user type to driver
180
- $user->setType('driver');
181
- } elseif ($input->has('photo_uuid')) {
182
- // Update existing user's avatar if photo provided
183
- $user->update(['avatar_uuid' => $input->get('photo_uuid')]);
184
- }
185
- } else {
186
- $userInput = $input
187
- ->only(['name', 'password', 'email', 'phone', 'status', 'avatar_uuid'])
188
- ->filter()
189
- ->toArray();
190
-
191
- // handle `photo_uuid`
192
- if (isset($input['photo_uuid']) && Str::isUuid($input['photo_uuid'])) {
193
- $userInput['avatar_uuid'] = $input['photo_uuid'];
194
- }
195
-
196
- // Make sure password is set
197
- if (empty($userInput['password'])) {
198
- $userInput['password'] = Str::random(14);
199
- }
200
-
201
- // Set user company
202
- $userInput['company_uuid'] = session('company', $company->uuid);
203
-
204
- // Apply user infos
205
- $userInput = User::applyUserInfoFromRequest($request, $userInput);
206
-
207
- // Create user account
208
- $user = User::create($userInput);
209
-
210
- // Set the user type to driver
211
- $user->setType('driver');
212
- }
213
-
214
- // if exists in organization create driver profile for user
215
- $isOrganizationMember = $user->companies()->where('companies.uuid', session('company'))->exists();
92
+ // The login account is managed by the driver profile: a team member
93
+ // with the email/phone is linked, otherwise a driver account is created.
94
+ $avatarUuid = Str::isUuid($input->get('photo_uuid')) ? $input->get('photo_uuid') : $input->get('avatar_uuid');
95
+ $user = ProfileAccountManager::resolveForProfile(
96
+ $company->uuid,
97
+ 'driver',
98
+ $input->get('name'),
99
+ $input->get('email'),
100
+ $input->get('phone'),
101
+ User::applyUserInfoFromRequest($request, array_filter([
102
+ 'password' => $input->get('password'),
103
+ 'status' => 'active',
104
+ 'avatar_uuid' => $avatarUuid,
105
+ ]))
106
+ );
216
107
 
217
108
  // Prepare input
218
109
  $input = $input
219
- ->except(['name', 'password', 'email', 'phone', 'meta', 'avatar_uuid', 'photo_uuid', 'status'])
110
+ ->except(['name', 'password', 'email', 'phone', 'meta', 'avatar_uuid', 'photo_uuid', 'status', 'user_uuid', 'user'])
220
111
  ->filter()
221
112
  ->toArray();
222
113
 
223
- // Assign user to company and send invite
224
- if (!$isOrganizationMember && $company) {
225
- $user->assignCompany($company);
226
- }
227
-
228
- // Set user type as driver and set role to driver
229
- if ($user->type === 'driver') {
230
- $user->assignSingleRole('Driver');
231
- }
232
-
233
- $input['user_uuid'] = $user->uuid;
234
- $input['slug'] = $user->slug;
114
+ $input['company_uuid'] = $company->uuid;
115
+ $input['user_uuid'] = $user->uuid;
116
+ $input['slug'] = $user->slug;
235
117
 
236
118
  // If no location provided set
237
119
  if (empty($input['location'])) {
@@ -248,6 +130,8 @@ class DriverController extends FleetOpsController
248
130
  );
249
131
 
250
132
  return ['driver' => new $this->resource($record)];
133
+ } catch (ProfileIdentityConflictException $e) {
134
+ return response()->error($e->getMessage(), 422, ['field' => $e->getField()]);
251
135
  } catch (QueryException $e) {
252
136
  return response()->error(env('DEBUG') ? $e->getMessage() : 'Error occurred while trying to create a ' . $this->resourceSingularlName);
253
137
  } catch (FleetbaseRequestValidationException $e) {
@@ -287,18 +171,15 @@ class DriverController extends FleetOpsController
287
171
  function (&$request, &$driver, &$input) {
288
172
  $driver->load(['user'])->guard(['user_uuid']);
289
173
  $input = collect($input);
290
- $userInput = $input->only(['name', 'password', 'email', 'phone', 'avatar_uuid'])->reject(fn ($value) => $value === null)->toArray();
174
+ $userInput = $input->only(['name', 'password', 'email', 'phone', 'avatar_uuid'])->reject(fn ($value, $key) => $value === null && !in_array($key, ['email', 'phone'], true))->toArray();
291
175
  // handle `photo_uuid`
292
176
  if (isset($input['photo_uuid']) && Str::isUuid($input['photo_uuid'])) {
293
177
  $userInput['avatar_uuid'] = $input['photo_uuid'];
294
178
  }
295
- $input = $input->except(['name', 'password', 'email', 'phone', 'meta', 'avatar_uuid', 'photo_uuid'])->toArray();
179
+ $input = $input->except(['name', 'password', 'email', 'phone', 'meta', 'avatar_uuid', 'photo_uuid', 'user_uuid', 'user'])->toArray();
296
180
 
297
- // Update driver user details
298
- $driverUser = $driver->getUser();
299
- if ($driverUser && !empty($userInput)) {
300
- $driverUser->update($userInput);
301
- }
181
+ // Update the driver's login account through its proxy fields
182
+ ProfileAccountManager::syncProxyFields($driver->getUser(), $userInput);
302
183
 
303
184
  // Flush cache
304
185
  $driver->flushAttributesCache();
@@ -318,6 +199,8 @@ class DriverController extends FleetOpsController
318
199
  );
319
200
 
320
201
  return ['driver' => new $this->resource($record)];
202
+ } catch (ProfileIdentityConflictException $e) {
203
+ return response()->error($e->getMessage(), 422, ['field' => $e->getField()]);
321
204
  } catch (QueryException $e) {
322
205
  return response()->error(env('DEBUG') ? $e->getMessage() : 'Error occurred while trying to update a ' . $this->resourceSingularlName);
323
206
  } catch (FleetbaseRequestValidationException $e) {
@@ -327,6 +210,154 @@ class DriverController extends FleetOpsController
327
210
  }
328
211
  }
329
212
 
213
+ /**
214
+ * Generate a new password for the driver's login and send it to them.
215
+ *
216
+ * @return JsonResponse
217
+ */
218
+ #[SkipAuthorizationCheck]
219
+ public function sendCredentials(string $id)
220
+ {
221
+ [$driver, $user, $error] = $this->resolveDriverLogin($id);
222
+ if ($error) {
223
+ return $error;
224
+ }
225
+
226
+ try {
227
+ $sentVia = ProfileAccountManager::sendCredentials($driver, $user);
228
+ } catch (\Exception $e) {
229
+ return response()->error($e->getMessage());
230
+ }
231
+
232
+ return response()->json(['status' => 'ok', 'sent_via' => $sentVia, 'driver' => $this->driverLoginPayload($driver)]);
233
+ }
234
+
235
+ /**
236
+ * Set a new password for the driver's login, optionally sending it to them.
237
+ *
238
+ * @return JsonResponse
239
+ */
240
+ #[SkipAuthorizationCheck]
241
+ public function resetCredentials(Request $request, string $id)
242
+ {
243
+ $password = $request->input('password');
244
+ if (!is_string($password) || strlen($password) < 8) {
245
+ return response()->error('Password must be at least 8 characters.');
246
+ }
247
+
248
+ if ($password !== $request->input('password_confirmation')) {
249
+ return response()->error('Passwords do not match.');
250
+ }
251
+
252
+ [$driver, $user, $error] = $this->resolveDriverLogin($id);
253
+ if ($error) {
254
+ return $error;
255
+ }
256
+
257
+ $user->changePassword($password);
258
+
259
+ try {
260
+ if ($request->boolean('send_credentials')) {
261
+ ProfileAccountManager::deliverCredentials($driver, $user, $password);
262
+ }
263
+ } catch (\Exception $e) {
264
+ return response()->error($e->getMessage());
265
+ }
266
+
267
+ return response()->json(['status' => 'ok', 'driver' => $this->driverLoginPayload($driver)]);
268
+ }
269
+
270
+ /**
271
+ * Stop the driver from signing in to the driver app.
272
+ *
273
+ * @return JsonResponse
274
+ */
275
+ #[SkipAuthorizationCheck]
276
+ public function deactivateLogin(string $id)
277
+ {
278
+ [$driver, $user, $error] = $this->resolveDriverLogin($id);
279
+ if ($error) {
280
+ return $error;
281
+ }
282
+
283
+ $user->deactivate();
284
+
285
+ // Sign the driver out of the driver app
286
+ $user->tokens()->delete();
287
+
288
+ return response()->json(['status' => 'ok', 'driver' => $this->driverLoginPayload($driver)]);
289
+ }
290
+
291
+ /**
292
+ * Allow the driver to sign in to the driver app again.
293
+ *
294
+ * @return JsonResponse
295
+ */
296
+ #[SkipAuthorizationCheck]
297
+ public function reactivateLogin(string $id)
298
+ {
299
+ [$driver, $user, $error] = $this->resolveDriverLogin($id);
300
+ if ($error) {
301
+ return $error;
302
+ }
303
+
304
+ $user->activate();
305
+
306
+ return response()->json(['status' => 'ok', 'driver' => $this->driverLoginPayload($driver)]);
307
+ }
308
+
309
+ /**
310
+ * Find the driver and its managed login account. A driver linked to a team
311
+ * member's account has its login managed in IAM.
312
+ *
313
+ * @return array{0: ?Driver, 1: ?User, 2: ?JsonResponse}
314
+ */
315
+ protected function resolveDriverLogin(string $id): array
316
+ {
317
+ $driver = Driver::where('company_uuid', session('company'))
318
+ ->where(function ($query) use ($id) {
319
+ $query->where('uuid', $id)->orWhere('public_id', $id);
320
+ })
321
+ ->first();
322
+
323
+ if (!$driver) {
324
+ return [null, null, response()->error('Driver not found.', 404)];
325
+ }
326
+
327
+ $user = $driver->user_uuid ? User::where('uuid', $driver->user_uuid)->first() : null;
328
+ if (!$user) {
329
+ return [$driver, null, response()->error('This driver has no login account.')];
330
+ }
331
+
332
+ if (ProfileAccountManager::isStaffAccount($user)) {
333
+ return [$driver, $user, response()->error('This driver signs in with a team member account. Manage this login in IAM.', 422)];
334
+ }
335
+
336
+ return [$driver, $user, null];
337
+ }
338
+
339
+ protected function driverLoginPayload(Driver $driver): array
340
+ {
341
+ $user = User::where('uuid', $driver->user_uuid)->first();
342
+
343
+ return [
344
+ 'id' => $driver->public_id,
345
+ 'uuid' => $driver->uuid,
346
+ 'user_uuid' => $driver->user_uuid,
347
+ 'is_staff_linked' => ProfileAccountManager::isStaffAccount($user),
348
+ 'login_status' => $user?->status,
349
+ 'user' => $user ? [
350
+ 'id' => $user->public_id,
351
+ 'uuid' => $user->uuid,
352
+ 'name' => $user->name,
353
+ 'email' => $user->email,
354
+ 'phone' => $user->phone,
355
+ 'status' => $user->status,
356
+ 'session_status' => $user->session_status,
357
+ ] : null,
358
+ ];
359
+ }
360
+
330
361
  /**
331
362
  * Get all status options for an driver.
332
363
  *
@@ -418,6 +449,7 @@ class DriverController extends FleetOpsController
418
449
  ]);
419
450
  }
420
451
 
452
+ #[SkipAuthorizationCheck]
421
453
  public function unassignOrders(Request $request, string $id): JsonResponse
422
454
  {
423
455
  $request->validate([
@@ -450,6 +482,7 @@ class DriverController extends FleetOpsController
450
482
  ]);
451
483
  }
452
484
 
485
+ #[SkipAuthorizationCheck]
453
486
  public function unassignOrder(string $id): JsonResponse
454
487
  {
455
488
  $driver = $this->findDriver($id);
@@ -486,6 +519,7 @@ class DriverController extends FleetOpsController
486
519
  ]);
487
520
  }
488
521
 
522
+ #[SkipAuthorizationCheck]
489
523
  public function unassignVehicle(string $id): JsonResponse
490
524
  {
491
525
  $driver = $this->findDriver($id);
@@ -635,6 +669,10 @@ class DriverController extends FleetOpsController
635
669
  return response()->error('No driver with this phone # found.');
636
670
  }
637
671
 
672
+ if (ApiDriverController::isLoginDeactivated($user)) {
673
+ return response()->error(ApiDriverController::DEACTIVATED_LOGIN_MESSAGE, 403);
674
+ }
675
+
638
676
  // Generate verification token
639
677
  static::generateDriverLoginVerification($user);
640
678
 
@@ -663,6 +701,10 @@ class DriverController extends FleetOpsController
663
701
  return response()->error('Unable to verify code.');
664
702
  }
665
703
 
704
+ if (ApiDriverController::isLoginDeactivated($user)) {
705
+ return response()->error(ApiDriverController::DEACTIVATED_LOGIN_MESSAGE, 403);
706
+ }
707
+
666
708
  // Find and verify code
667
709
  $verificationCode = static::verificationCodeExists($user, $code, $for);
668
710
  if (!$verificationCode && !ApiDriverController::verificationBypassMatches($identity, $code)) {
@@ -3,16 +3,63 @@
3
3
  namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
4
4
 
5
5
  use Fleetbase\FleetOps\Models\Contact;
6
+ use Fleetbase\FleetOps\Models\Driver;
6
7
  use Fleetbase\FleetOps\Models\IntegratedVendor;
7
8
  use Fleetbase\FleetOps\Models\Vendor;
9
+ use Fleetbase\FleetOps\Support\ProfileAccountManager;
8
10
  use Fleetbase\FleetOps\Support\Utils;
9
11
  use Fleetbase\Http\Controllers\Controller;
12
+ use Fleetbase\Models\User;
10
13
  use Illuminate\Http\Request;
11
14
  use Illuminate\Support\Arr;
12
15
  use Illuminate\Support\Str;
13
16
 
14
17
  class FleetOpsLookupController extends Controller
15
18
  {
19
+ /**
20
+ * Describes what an email/phone entered on a driver, contact or customer
21
+ * form resolves to: the team member the profile would be linked to, or why
22
+ * the email/phone can't be used.
23
+ *
24
+ * @return \Illuminate\Http\JsonResponse
25
+ */
26
+ public function profileIdentity(Request $request)
27
+ {
28
+ $type = $request->input('type', 'driver');
29
+ if (!in_array($type, ProfileAccountManager::MANAGED_TYPES, true)) {
30
+ return response()->error('Invalid profile type.', 422);
31
+ }
32
+
33
+ $currentUser = $this->currentProfileUser($type, $request->input('ignore'));
34
+ $lookup = ProfileAccountManager::lookup(session('company'), $type, $request->input('email'), $request->input('phone'), $currentUser);
35
+ $staff = $lookup['staff'];
36
+
37
+ return response()->json([
38
+ 'staff' => $staff ? ['uuid' => $staff->uuid, 'name' => $staff->name, 'email' => $staff->email, 'phone' => $staff->phone] : null,
39
+ 'conflict' => $lookup['conflict'],
40
+ ]);
41
+ }
42
+
43
+ /**
44
+ * The account of the profile being edited, so its own email/phone isn't
45
+ * reported as taken.
46
+ */
47
+ private function currentProfileUser(string $type, ?string $id): ?User
48
+ {
49
+ if (!$id) {
50
+ return null;
51
+ }
52
+
53
+ $model = $type === 'driver' ? Driver::withoutGlobalScopes() : Contact::query();
54
+ $profile = $model->where('company_uuid', session('company'))
55
+ ->where(function ($query) use ($id) {
56
+ $query->where('uuid', $id)->orWhere('public_id', $id);
57
+ })
58
+ ->first();
59
+
60
+ return $profile?->user_uuid ? User::where('uuid', $profile->user_uuid)->first() : null;
61
+ }
62
+
16
63
  /**
17
64
  * Returns a collection of polymorphic resources as JSON.
18
65
  *
@@ -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\Http\Controllers\FleetOpsController;
6
7
  use Fleetbase\FleetOps\Jobs\SyncFuelProviderTransactionsJob;
7
8
  use Fleetbase\FleetOps\Models\FuelProviderConnection;
@@ -13,6 +14,16 @@ use Illuminate\Validation\ValidationException;
13
14
 
14
15
  class FuelProviderConnectionController extends FleetOpsController
15
16
  {
17
+ /**
18
+ * Permissions for methods AuthorizationGuard cannot map to a schema action (see FleetOpsController).
19
+ *
20
+ * @var array<string, string|string[]>
21
+ */
22
+ protected array $methodPermissions = [
23
+ 'testCredentials' => 'create fuel-provider-connection',
24
+ 'testConnection' => 'update fuel-provider-connection',
25
+ ];
26
+
16
27
  public $resource = 'fuel_provider_connection';
17
28
 
18
29
  public function __construct(protected FuelProviderService $fuelProviderService)
@@ -38,6 +49,7 @@ class FuelProviderConnectionController extends FleetOpsController
38
49
  $this->normalizeConnectionInput($input, $connection);
39
50
  }
40
51
 
52
+ #[SkipAuthorizationCheck]
41
53
  public function testCredentials(Request $request, string $provider): JsonResponse
42
54
  {
43
55
  $request->validate([
@@ -54,6 +66,7 @@ class FuelProviderConnectionController extends FleetOpsController
54
66
  return response()->json($result, data_get($result, 'success') ? 200 : 422);
55
67
  }
56
68
 
69
+ #[SkipAuthorizationCheck]
57
70
  public function testConnection(Request $request, string $id): JsonResponse
58
71
  {
59
72
  $connection = $this->findConnection($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\Http\Controllers\FleetOpsController;
6
7
  use Fleetbase\FleetOps\Models\FuelProviderTransaction;
7
8
  use Fleetbase\FleetOps\Support\FuelProviders\FuelProviderService;
@@ -10,6 +11,17 @@ use Illuminate\Http\Request;
10
11
 
11
12
  class FuelProviderTransactionController extends FleetOpsController
12
13
  {
14
+ /**
15
+ * Permissions for methods AuthorizationGuard cannot map to a schema action (see FleetOpsController).
16
+ *
17
+ * @var array<string, string|string[]>
18
+ */
19
+ protected array $methodPermissions = [
20
+ 'matchVehicle' => 'update fuel-provider-transaction',
21
+ 'matchOrder' => 'update fuel-provider-transaction',
22
+ 'reprocess' => 'update fuel-provider-transaction',
23
+ ];
24
+
13
25
  public $resource = 'fuel_provider_transaction';
14
26
 
15
27
  public function __construct(protected FuelProviderService $fuelProviderService)
@@ -22,6 +34,7 @@ class FuelProviderTransactionController extends FleetOpsController
22
34
  $query->with(['vehicle', 'driver', 'fuelReport']);
23
35
  }
24
36
 
37
+ #[SkipAuthorizationCheck]
25
38
  public function matchVehicle(Request $request, string $id): JsonResponse
26
39
  {
27
40
  $request->validate(['vehicle' => 'required|string']);
@@ -33,6 +46,7 @@ class FuelProviderTransactionController extends FleetOpsController
33
46
  ]);
34
47
  }
35
48
 
49
+ #[SkipAuthorizationCheck]
36
50
  public function matchOrder(Request $request, string $id): JsonResponse
37
51
  {
38
52
  $request->validate(['order' => 'required|string']);
@@ -44,6 +58,7 @@ class FuelProviderTransactionController extends FleetOpsController
44
58
  ]);
45
59
  }
46
60
 
61
+ #[SkipAuthorizationCheck]
47
62
  public function reprocess(Request $request, string $id): JsonResponse
48
63
  {
49
64
  $transaction = $this->findTransaction($id);
@@ -9,6 +9,7 @@ use Fleetbase\FleetOps\Models\FuelReport;
9
9
  use Fleetbase\Http\Requests\ExportRequest;
10
10
  use Fleetbase\Http\Requests\ImportRequest;
11
11
  use Illuminate\Http\Request;
12
+ use Illuminate\Support\Arr;
12
13
  use Illuminate\Support\Str;
13
14
  use Maatwebsite\Excel\Facades\Excel;
14
15
 
@@ -23,11 +24,16 @@ class FuelReportController extends FleetOpsController
23
24
 
24
25
  /**
25
26
  * Handle post save transactions.
27
+ *
28
+ * Reads the custom field values from the payload the base controller already extracted
29
+ * rather than from the raw request: Ember Data sends the resource under a camelCase root
30
+ * (`fuelReport`), so `$request->array('fuel_report.custom_field_values')` was always empty and no
31
+ * custom field value was ever persisted for this resource.
26
32
  */
27
- public function afterSave(Request $request, FuelReport $fuelReport)
33
+ public function afterSave(Request $request, FuelReport $fuelReport, array $input = [])
28
34
  {
29
- $customFieldValues = $request->array('fuel_report.custom_field_values');
30
- if ($customFieldValues) {
35
+ $customFieldValues = Arr::get($input, 'custom_field_values');
36
+ if (is_array($customFieldValues) && $customFieldValues) {
31
37
  $fuelReport->syncCustomFieldValues($customFieldValues);
32
38
  }
33
39
  }