@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.
- package/RELEASE.md +21 -14
- package/addon/components/admin/telematics-settings.hbs +143 -0
- package/addon/components/admin/telematics-settings.js +145 -0
- package/addon/components/fuel-integration/hub.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.js +37 -17
- package/addon/components/map/leaflet-live-map.hbs +4 -242
- package/addon/components/map/leaflet-live-map.js +2 -1
- package/addon/components/map/marker-card/driver.hbs +57 -0
- package/addon/components/map/marker-card/driver.js +3 -0
- package/addon/components/map/marker-card/vehicle.hbs +78 -0
- package/addon/components/map/marker-card/vehicle.js +3 -0
- package/addon/components/order/details/activity.hbs +8 -6
- package/addon/components/order/details/activity.js +9 -0
- package/addon/components/order/details.hbs +29 -0
- package/addon/components/order/details.js +26 -0
- package/addon/components/order/form/details.hbs +30 -4
- package/addon/components/order/form/details.js +46 -0
- package/addon/components/order/form/route.hbs +4 -2
- package/addon/components/order/form/route.js +11 -0
- package/addon/components/order/form.hbs +12 -0
- package/addon/components/order/form.js +21 -1
- package/addon/components/order/kanban.js +72 -0
- package/addon/components/order-config-manager/activity-flow.js +45 -10
- package/addon/components/order-config-manager/lifecycle.hbs +250 -0
- package/addon/components/order-config-manager/lifecycle.js +358 -0
- package/addon/components/order-config-manager.js +5 -1
- package/addon/components/telematic/hub.hbs +1 -1
- package/addon/components/widget/live-fleet.hbs +8 -40
- package/addon/controllers/analytics/reports/index.js +15 -1
- package/addon/controllers/connectivity/devices/index/details.js +1 -0
- package/addon/controllers/connectivity/devices/index.js +12 -2
- package/addon/controllers/connectivity/events/index.js +31 -1
- package/addon/controllers/connectivity/fuel-providers/index.js +6 -4
- package/addon/controllers/connectivity/sensors/index/details.js +1 -1
- package/addon/controllers/connectivity/sensors/index.js +41 -1
- package/addon/controllers/connectivity/telematics/index.js +4 -0
- package/addon/controllers/maintenance/equipment/index/details.js +2 -2
- package/addon/controllers/maintenance/equipment/index.js +36 -10
- package/addon/controllers/maintenance/inspection-forms/index.js +32 -12
- package/addon/controllers/maintenance/inspection-submissions/index/details.js +2 -1
- package/addon/controllers/maintenance/inspection-submissions/index.js +56 -10
- package/addon/controllers/maintenance/maintenances/index/details.js +2 -0
- package/addon/controllers/maintenance/maintenances/index.js +24 -1
- package/addon/controllers/maintenance/parts/index/details.js +2 -2
- package/addon/controllers/maintenance/parts/index.js +36 -9
- package/addon/controllers/maintenance/schedules/index.js +48 -6
- package/addon/controllers/maintenance/work-orders/index/details.js +3 -3
- package/addon/controllers/maintenance/work-orders/index.js +47 -9
- package/addon/controllers/management/contacts/customers.js +13 -0
- package/addon/controllers/management/contacts/index.js +47 -1
- package/addon/controllers/management/drivers/index/details.js +1 -0
- package/addon/controllers/management/drivers/index.js +36 -2
- package/addon/controllers/management/fleets/index/details.js +1 -0
- package/addon/controllers/management/fleets/index.js +43 -1
- package/addon/controllers/management/fuel-reports/index.js +26 -2
- package/addon/controllers/management/fuel-transactions/index.js +45 -7
- package/addon/controllers/management/issues/index/details.js +1 -0
- package/addon/controllers/management/issues/index.js +26 -2
- package/addon/controllers/management/places/index.js +40 -1
- package/addon/controllers/management/trailers/index/details.js +1 -0
- package/addon/controllers/management/trailers/index.js +50 -2
- package/addon/controllers/management/vehicles/index/details.js +1 -0
- package/addon/controllers/management/vehicles/index.js +37 -2
- package/addon/controllers/management/vendors/index.js +30 -2
- package/addon/controllers/operations/orders/index/details.js +61 -5
- package/addon/controllers/operations/orders/index.js +66 -4
- package/addon/controllers/operations/service-rates/index.js +18 -1
- package/addon/controllers/settings/index.js +13 -0
- package/addon/controllers/settings/telematics.js +113 -0
- package/addon/extension.js +70 -6
- package/addon/routes/analytics/index.js +14 -1
- package/addon/routes/analytics/reports/index/details.js +1 -1
- package/addon/routes/analytics/reports/index/edit.js +1 -1
- package/addon/routes/analytics/reports/index/new.js +14 -1
- package/addon/routes/analytics/reports.js +14 -1
- package/addon/routes/connectivity/devices/index/new.js +14 -1
- package/addon/routes/connectivity/devices.js +14 -1
- package/addon/routes/connectivity/events.js +14 -1
- package/addon/routes/connectivity/fuel-providers/details/transactions.js +12 -0
- package/addon/routes/connectivity/fuel-providers/details.js +9 -0
- package/addon/routes/connectivity/fuel-providers/edit.js +9 -0
- package/addon/routes/connectivity/fuel-providers/new.js +14 -1
- package/addon/routes/connectivity/fuel-providers.js +14 -1
- package/addon/routes/connectivity/sensors/index/new.js +14 -1
- package/addon/routes/connectivity/sensors.js +14 -1
- package/addon/routes/connectivity/telematics/new.js +14 -1
- package/addon/routes/connectivity/telematics.js +14 -1
- package/addon/routes/connectivity/tracking.js +14 -1
- package/addon/routes/maintenance/equipment/index/details.js +2 -2
- package/addon/routes/maintenance/equipment/index/edit.js +2 -2
- package/addon/routes/maintenance/equipment/index/new.js +14 -1
- package/addon/routes/maintenance/equipment.js +14 -1
- package/addon/routes/maintenance/index.js +14 -1
- package/addon/routes/maintenance/inspection-forms/index/details.js +10 -1
- package/addon/routes/maintenance/inspection-forms/index/edit.js +10 -1
- package/addon/routes/maintenance/inspection-forms/index/new.js +14 -1
- package/addon/routes/maintenance/inspection-forms.js +14 -1
- package/addon/routes/maintenance/inspection-submissions/index/details.js +10 -1
- package/addon/routes/maintenance/inspection-submissions/index/edit.js +10 -1
- package/addon/routes/maintenance/inspection-submissions/index/new.js +14 -1
- package/addon/routes/maintenance/inspection-submissions.js +14 -1
- package/addon/routes/maintenance/maintenances/index/details.js +2 -2
- package/addon/routes/maintenance/maintenances/index/edit.js +2 -2
- package/addon/routes/maintenance/maintenances/index/new.js +14 -1
- package/addon/routes/maintenance/maintenances.js +14 -1
- package/addon/routes/maintenance/parts/index/details.js +2 -2
- package/addon/routes/maintenance/parts/index/edit.js +2 -2
- package/addon/routes/maintenance/parts/index/new.js +14 -1
- package/addon/routes/maintenance/parts.js +14 -1
- package/addon/routes/maintenance/schedules/index/details.js +2 -2
- package/addon/routes/maintenance/schedules/index/edit.js +2 -2
- package/addon/routes/maintenance/schedules/index/new.js +15 -1
- package/addon/routes/maintenance/schedules.js +15 -1
- package/addon/routes/maintenance/work-orders/index/details.js +2 -2
- package/addon/routes/maintenance/work-orders/index/edit.js +2 -2
- package/addon/routes/maintenance/work-orders/index/new.js +14 -1
- package/addon/routes/maintenance/work-orders.js +14 -1
- package/addon/routes/management/fuel-transactions/index/details.js +9 -0
- package/addon/routes/management/fuel-transactions.js +14 -1
- package/addon/routes/management/index.js +13 -0
- package/addon/routes/management/trailers.js +16 -0
- package/addon/routes/management/vendors/integrated/details.js +14 -1
- package/addon/routes/management/vendors/integrated/edit.js +14 -1
- package/addon/routes/management/vendors/integrated/new.js +14 -1
- package/addon/routes/management/vendors/integrated.js +14 -1
- package/addon/routes/operations/orders/index/details.js +1 -0
- package/addon/routes/operations/orders.js +40 -1
- package/addon/routes/operations/routes/index/details.js +14 -1
- package/addon/routes/operations/routes/index/new.js +13 -0
- package/addon/routes/operations/routes.js +14 -1
- package/addon/routes/operations/service-rates/index/new.js +11 -0
- package/addon/routes/operations/service-rates.js +14 -1
- package/addon/routes/settings/avatars.js +14 -1
- package/addon/routes/settings/custom-fields.js +14 -1
- package/addon/routes/settings/map.js +14 -1
- package/addon/routes/settings/notifications.js +11 -0
- package/addon/routes/settings/orchestrator.js +1 -1
- package/addon/routes/settings/payments/index.js +1 -1
- package/addon/routes/settings/routing.js +14 -1
- package/addon/routes/settings/scheduling.js +14 -1
- package/addon/routes/settings/telematics.js +21 -0
- package/addon/routes/virtual.js +11 -0
- package/addon/routes.js +1 -0
- package/addon/services/contact-actions.js +1 -0
- package/addon/services/customer-actions.js +2 -1
- package/addon/services/device-actions.js +1 -0
- package/addon/services/device-event-actions.js +1 -0
- package/addon/services/driver-actions.js +4 -13
- package/addon/services/equipment-actions.js +1 -0
- package/addon/services/fleet-actions.js +1 -0
- package/addon/services/fuel-report-actions.js +1 -0
- package/addon/services/inspection-form-actions.js +1 -0
- package/addon/services/inspection-submission-actions.js +1 -0
- package/addon/services/integrated-vendor-actions.js +1 -0
- package/addon/services/issue-actions.js +1 -0
- package/addon/services/maintenance-actions.js +1 -0
- package/addon/services/maintenance-schedule-actions.js +2 -1
- package/addon/services/order-actions.js +7 -0
- package/addon/services/order-presentation.js +129 -0
- package/addon/services/part-actions.js +1 -0
- package/addon/services/place-actions.js +1 -0
- package/addon/services/sensor-actions.js +1 -0
- package/addon/services/service-area-actions.js +1 -0
- package/addon/services/service-rate-actions.js +1 -0
- package/addon/services/telematic-actions.js +1 -0
- package/addon/services/trailer-actions.js +4 -14
- package/addon/services/vehicle-actions.js +4 -13
- package/addon/services/vendor-actions.js +1 -0
- package/addon/services/work-order-actions.js +1 -0
- package/addon/services/zone-actions.js +1 -0
- package/addon/templates/analytics/reports/index/details.hbs +1 -0
- package/addon/templates/analytics/reports/index.hbs +1 -0
- package/addon/templates/connectivity/devices/index/details.hbs +1 -0
- package/addon/templates/connectivity/devices/index.hbs +1 -0
- package/addon/templates/connectivity/events/index.hbs +1 -0
- package/addon/templates/connectivity/sensors/index/details.hbs +1 -0
- package/addon/templates/connectivity/sensors/index.hbs +1 -0
- package/addon/templates/maintenance/equipment/index/details.hbs +1 -0
- package/addon/templates/maintenance/equipment/index.hbs +2 -0
- package/addon/templates/maintenance/inspection-forms/index/details.hbs +1 -0
- package/addon/templates/maintenance/inspection-forms/index.hbs +1 -0
- package/addon/templates/maintenance/inspection-submissions/index/details.hbs +1 -0
- package/addon/templates/maintenance/inspection-submissions/index.hbs +1 -0
- package/addon/templates/maintenance/maintenances/index/details.hbs +1 -0
- package/addon/templates/maintenance/maintenances/index.hbs +1 -0
- package/addon/templates/maintenance/parts/index/details.hbs +1 -0
- package/addon/templates/maintenance/parts/index.hbs +2 -0
- package/addon/templates/maintenance/schedules/index/details.hbs +1 -0
- package/addon/templates/maintenance/schedules/index.hbs +2 -0
- package/addon/templates/maintenance/work-orders/index/details.hbs +1 -0
- package/addon/templates/maintenance/work-orders/index.hbs +1 -0
- package/addon/templates/management/contacts/customers/details.hbs +1 -0
- package/addon/templates/management/contacts/customers.hbs +1 -0
- package/addon/templates/management/contacts/index/details.hbs +1 -0
- package/addon/templates/management/contacts/index.hbs +1 -0
- package/addon/templates/management/contacts.hbs +1 -0
- package/addon/templates/management/drivers/index/details.hbs +1 -0
- package/addon/templates/management/drivers/index.hbs +2 -0
- package/addon/templates/management/fleets/index/details.hbs +1 -0
- package/addon/templates/management/fleets/index.hbs +1 -0
- package/addon/templates/management/fuel-reports/index/details.hbs +1 -0
- package/addon/templates/management/fuel-reports/index.hbs +1 -0
- package/addon/templates/management/fuel-transactions/index.hbs +1 -0
- package/addon/templates/management/issues/index/details.hbs +1 -0
- package/addon/templates/management/issues/index.hbs +1 -0
- package/addon/templates/management/places/index/details.hbs +1 -0
- package/addon/templates/management/places/index.hbs +1 -0
- package/addon/templates/management/trailers/index/details.hbs +1 -0
- package/addon/templates/management/trailers/index.hbs +2 -0
- package/addon/templates/management/vehicles/index/details.hbs +1 -0
- package/addon/templates/management/vehicles/index.hbs +2 -0
- package/addon/templates/management/vendors/index/details.hbs +1 -0
- package/addon/templates/management/vendors/index.hbs +2 -0
- package/addon/templates/management/vendors/integrated.hbs +1 -0
- package/addon/templates/operations/orders/index/details.hbs +1 -0
- package/addon/templates/operations/orders/index.hbs +1 -0
- package/addon/templates/operations/service-rates/index/details.hbs +1 -0
- package/addon/templates/operations/service-rates/index.hbs +1 -0
- package/addon/templates/settings/telematics.hbs +70 -0
- package/addon/utils/normalize-order-config-flow.js +6 -1
- package/app/components/admin/telematics-settings.js +1 -0
- package/app/components/map/marker-card/driver.js +1 -0
- package/app/components/map/marker-card/vehicle.js +1 -0
- package/app/controllers/settings/telematics.js +1 -0
- package/app/routes/settings/telematics.js +1 -0
- package/app/services/order-presentation.js +1 -0
- package/app/templates/settings/telematics.js +1 -0
- package/composer.json +2 -2
- package/docs/AFAQY.md +1 -1
- package/docs/TELEMATICS_QUEUES.md +1 -1
- package/docs/TELEMETRY_ARCHITECTURE.md +72 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/server/config/telemetry.php +12 -0
- package/server/migrations/2026_09_23_000001_add_retention_index_to_device_events.php +40 -0
- package/server/migrations/2026_09_23_000002_add_retention_index_to_positions.php +40 -0
- package/server/migrations/2026_09_23_000003_add_retention_index_to_telematic_sync_runs.php +40 -0
- package/server/src/Auth/Schemas/FleetOps.php +104 -5
- package/server/src/Console/Commands/DrainTelematicInbox.php +3 -13
- package/server/src/Console/Commands/ProcessOperationalAlerts.php +3 -3
- package/server/src/Console/Commands/PruneTelematicsData.php +318 -0
- package/server/src/Http/Controllers/Api/v1/OrchestrationController.php +9 -0
- package/server/src/Http/Controllers/FleetOpsController.php +21 -0
- package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +11 -0
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +11 -0
- package/server/src/Http/Controllers/Internal/v1/CustomerController.php +14 -0
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +13 -0
- package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +11 -0
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +22 -0
- package/server/src/Http/Controllers/Internal/v1/FuelProviderConnectionController.php +13 -0
- package/server/src/Http/Controllers/Internal/v1/FuelProviderTransactionController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/FuelReportController.php +9 -3
- package/server/src/Http/Controllers/Internal/v1/InspectionFormController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +16 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/ManifestController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/MetricsController.php +11 -0
- package/server/src/Http/Controllers/Internal/v1/NavigatorController.php +42 -1
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +12 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +56 -4
- package/server/src/Http/Controllers/Internal/v1/PaymentController.php +13 -0
- package/server/src/Http/Controllers/Internal/v1/RadarController.php +19 -0
- package/server/src/Http/Controllers/Internal/v1/ServiceAreaController.php +9 -3
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +465 -14
- package/server/src/Http/Controllers/Internal/v1/TelematicController.php +21 -0
- package/server/src/Http/Controllers/Internal/v1/TrailerController.php +21 -0
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +21 -0
- package/server/src/Http/Controllers/Internal/v1/VendorController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +11 -0
- package/server/src/Jobs/DispatchTelematicsRetentionJobs.php +70 -0
- package/server/src/Jobs/PruneTelematicsDataJob.php +74 -0
- package/server/src/Listeners/NotifyDriverOnShiftChange.php +3 -3
- package/server/src/Mail/CustomerCredentialsMail.php +1 -1
- package/server/src/Models/Order.php +35 -3
- package/server/src/Models/OrderConfig.php +129 -0
- package/server/src/Models/TrackingNumber.php +8 -4
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +9 -1
- package/server/src/Providers/FleetOpsServiceProvider.php +3 -0
- package/server/src/Support/Analytics/LiveFleet.php +18 -2
- package/server/src/Support/Authorization.php +103 -0
- package/server/src/Support/Database/TableIndexes.php +66 -0
- package/server/src/Support/OrderTracker.php +2 -2
- package/server/src/Support/Reporting/FleetOpsReportSchema.php +476 -116
- package/server/src/Support/Telematics/Retention/RetentionPolicy.php +300 -0
- package/server/src/Support/Telematics/Retention/TelemetryActivity.php +40 -0
- package/server/src/Support/Telematics/TelematicService.php +12 -2
- package/server/src/Support/Telematics/Telemetry/Ingestor.php +4 -2
- package/server/src/Tracking/TrackingIntelligenceService.php +1 -1
- package/server/src/Tracking/TrackingOptions.php +2 -2
- package/server/src/Traits/AuthorizesMethods.php +37 -0
- package/server/src/routes.php +9 -1
- package/server/tests/AnalyticsRoutesTest.php +29 -2
- package/server/tests/Feature/Http/AfaqyRealtimeIngestionTest.php +16 -1
- package/server/tests/Feature/Http/Api/TelematicsRetentionIndexMigrationTest.php +115 -0
- package/server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php +72 -0
- package/server/tests/Feature/Http/Internal/OrderControllerCreateRecordTest.php +26 -0
- package/server/tests/Feature/Http/Internal/SettingControllerHelpersTest.php +71 -0
- package/server/tests/Feature/Http/Internal/TelematicsStorageDeadlinesTest.php +119 -0
- package/server/tests/InternalImportExportControllerContractsTest.php +5 -8
- package/server/tests/NavigatorControllerContractsTest.php +36 -4
- package/server/tests/NotificationAndMailContractsTest.php +31 -0
- package/server/tests/ObserverContractsTest.php +1 -1
- package/server/tests/OrchestratorConsumableApiTest.php +42 -0
- package/server/tests/OrderConfigLifecycleTest.php +120 -0
- package/server/tests/ProviderContractsTest.php +10 -0
- package/server/tests/ReportSchemaContractsTest.php +120 -6
- package/server/tests/SettingControllerContractsTest.php +429 -18
- package/server/tests/SmallControllerContractsTest.php +8 -5
- package/server/tests/Support/PruneTelematicsDataProbe.php +55 -0
- package/server/tests/Unit/Console/Commands/ProcessOperationalAlertsCommandTest.php +2 -2
- package/server/tests/Unit/Console/PruneTelematicsDataCommandTest.php +353 -0
- package/server/tests/Unit/Events/OrderDispatchFailedTest.php +2 -1
- package/server/tests/Unit/Jobs/DispatchTelematicsRetentionJobsTest.php +114 -0
- package/server/tests/Unit/Jobs/PruneTelematicsDataJobTest.php +60 -0
- package/server/tests/Unit/Orchestration/VroomEngineTransportAndSettingsTest.php +20 -0
- package/server/tests/Unit/Support/Analytics/LiveFleetTest.php +6 -0
- package/server/tests/Unit/Support/AuthorizationTest.php +142 -0
- package/server/tests/Unit/Support/MaintainableAndOperationalAlertsTest.php +12 -1
- package/server/tests/Unit/Support/MoreQuerySeamsTest.php +11 -2
- package/server/tests/Unit/Support/Telematics/Retention/RetentionPolicyTest.php +190 -0
- package/server/tests/Unit/Support/Telematics/Retention/TelemetryActivityTest.php +68 -0
- package/server/tests/Unit/Tracking/TrackingOptionsCompanySettingsTest.php +109 -0
- package/translations/en-us.yaml +108 -0
|
@@ -43,7 +43,7 @@ class FleetOps
|
|
|
43
43
|
],
|
|
44
44
|
[
|
|
45
45
|
'name' => 'service-rate',
|
|
46
|
-
'actions' => ['import'],
|
|
46
|
+
'actions' => ['import', 'export'],
|
|
47
47
|
],
|
|
48
48
|
[
|
|
49
49
|
'name' => 'zone',
|
|
@@ -141,6 +141,51 @@ class FleetOps
|
|
|
141
141
|
'name' => 'part',
|
|
142
142
|
'actions' => ['export', 'import'],
|
|
143
143
|
],
|
|
144
|
+
[
|
|
145
|
+
'name' => 'maintenance-schedule',
|
|
146
|
+
'actions' => ['export', 'import'],
|
|
147
|
+
],
|
|
148
|
+
[
|
|
149
|
+
'name' => 'warranty',
|
|
150
|
+
'actions' => [],
|
|
151
|
+
],
|
|
152
|
+
[
|
|
153
|
+
'name' => 'device',
|
|
154
|
+
'actions' => ['export', 'import'],
|
|
155
|
+
],
|
|
156
|
+
[
|
|
157
|
+
'name' => 'sensor',
|
|
158
|
+
'actions' => ['export', 'import'],
|
|
159
|
+
],
|
|
160
|
+
[
|
|
161
|
+
'name' => 'device-event',
|
|
162
|
+
'actions' => ['export'],
|
|
163
|
+
],
|
|
164
|
+
[
|
|
165
|
+
'name' => 'telematic',
|
|
166
|
+
'actions' => ['export', 'import'],
|
|
167
|
+
],
|
|
168
|
+
[
|
|
169
|
+
'name' => 'fuel-provider-connection',
|
|
170
|
+
'actions' => ['sync'],
|
|
171
|
+
],
|
|
172
|
+
[
|
|
173
|
+
'name' => 'fuel-provider-transaction',
|
|
174
|
+
'actions' => ['review'],
|
|
175
|
+
],
|
|
176
|
+
[
|
|
177
|
+
'name' => 'fuel-provider-sync-run',
|
|
178
|
+
'actions' => [],
|
|
179
|
+
],
|
|
180
|
+
[
|
|
181
|
+
'name' => 'purchase-rate',
|
|
182
|
+
'actions' => [],
|
|
183
|
+
],
|
|
184
|
+
[
|
|
185
|
+
'name' => 'analytics', // dashboards, KPI metrics and the live fleet summary
|
|
186
|
+
'actions' => [],
|
|
187
|
+
'remove_actions' => ['create', 'update', 'delete', 'export'],
|
|
188
|
+
],
|
|
144
189
|
[
|
|
145
190
|
'name' => 'custom-field',
|
|
146
191
|
'actions' => [],
|
|
@@ -151,26 +196,41 @@ class FleetOps
|
|
|
151
196
|
],
|
|
152
197
|
[
|
|
153
198
|
'name' => 'navigator-settings', // the navigator mobile app used by drivers
|
|
154
|
-
'
|
|
199
|
+
'actions' => [],
|
|
155
200
|
'remove_actions' => ['delete', 'export', 'list', 'create'],
|
|
156
201
|
],
|
|
157
202
|
[
|
|
158
203
|
'name' => 'payments',
|
|
159
|
-
'
|
|
204
|
+
'actions' => ['onboard'],
|
|
160
205
|
'remove_actions' => ['delete', 'export', 'list', 'create'],
|
|
161
206
|
],
|
|
162
207
|
[
|
|
163
208
|
'name' => 'notification-settings',
|
|
164
|
-
'
|
|
209
|
+
'actions' => [],
|
|
165
210
|
'remove_actions' => ['export', 'create'],
|
|
166
211
|
],
|
|
167
212
|
[
|
|
168
213
|
'name' => 'routing-settings',
|
|
169
|
-
'
|
|
214
|
+
'actions' => [],
|
|
170
215
|
'remove_actions' => ['export', 'create'],
|
|
171
216
|
],
|
|
172
217
|
[
|
|
173
218
|
'name' => 'map-settings',
|
|
219
|
+
'actions' => [],
|
|
220
|
+
'remove_actions' => ['export', 'create'],
|
|
221
|
+
],
|
|
222
|
+
[
|
|
223
|
+
'name' => 'scheduling-settings',
|
|
224
|
+
'actions' => [],
|
|
225
|
+
'remove_actions' => ['delete', 'export', 'create'],
|
|
226
|
+
],
|
|
227
|
+
[
|
|
228
|
+
'name' => 'tracking-settings',
|
|
229
|
+
'actions' => [],
|
|
230
|
+
'remove_actions' => ['delete', 'export', 'create'],
|
|
231
|
+
],
|
|
232
|
+
[
|
|
233
|
+
'name' => 'telematics-settings', // organization device event and position history preferences
|
|
174
234
|
'action' => [],
|
|
175
235
|
'remove_actions' => ['export', 'create'],
|
|
176
236
|
],
|
|
@@ -187,6 +247,9 @@ class FleetOps
|
|
|
187
247
|
'see extension',
|
|
188
248
|
'* order',
|
|
189
249
|
'* route',
|
|
250
|
+
'see analytics',
|
|
251
|
+
'list analytics',
|
|
252
|
+
'view analytics',
|
|
190
253
|
'see order-config',
|
|
191
254
|
'list order-config',
|
|
192
255
|
'view order-config',
|
|
@@ -230,6 +293,12 @@ class FleetOps
|
|
|
230
293
|
'* driver',
|
|
231
294
|
'* vehicle',
|
|
232
295
|
'* trailer',
|
|
296
|
+
'* device',
|
|
297
|
+
'* sensor',
|
|
298
|
+
'* telematic',
|
|
299
|
+
'see device-event',
|
|
300
|
+
'list device-event',
|
|
301
|
+
'view device-event',
|
|
233
302
|
],
|
|
234
303
|
],
|
|
235
304
|
[
|
|
@@ -277,6 +346,7 @@ class FleetOps
|
|
|
277
346
|
'permissions' => [
|
|
278
347
|
'see extension',
|
|
279
348
|
'* service-rate',
|
|
349
|
+
'* purchase-rate',
|
|
280
350
|
'see order',
|
|
281
351
|
'list order',
|
|
282
352
|
'see service-area',
|
|
@@ -309,6 +379,8 @@ class FleetOps
|
|
|
309
379
|
'* inspection-submission',
|
|
310
380
|
'* equipment',
|
|
311
381
|
'* part',
|
|
382
|
+
'* maintenance-schedule',
|
|
383
|
+
'* warranty',
|
|
312
384
|
'* trailer',
|
|
313
385
|
'see vehicle',
|
|
314
386
|
'list vehicle',
|
|
@@ -336,6 +408,18 @@ class FleetOps
|
|
|
336
408
|
'* inspection-submission',
|
|
337
409
|
'* equipment',
|
|
338
410
|
'* part',
|
|
411
|
+
'* maintenance-schedule',
|
|
412
|
+
'* warranty',
|
|
413
|
+
'* device',
|
|
414
|
+
'* sensor',
|
|
415
|
+
'* device-event',
|
|
416
|
+
'* telematic',
|
|
417
|
+
'* fuel-provider-connection',
|
|
418
|
+
'* fuel-provider-transaction',
|
|
419
|
+
'* fuel-provider-sync-run',
|
|
420
|
+
'see analytics',
|
|
421
|
+
'list analytics',
|
|
422
|
+
'view analytics',
|
|
339
423
|
'* trailer',
|
|
340
424
|
],
|
|
341
425
|
],
|
|
@@ -353,6 +437,14 @@ class FleetOps
|
|
|
353
437
|
'list vehicle',
|
|
354
438
|
],
|
|
355
439
|
],
|
|
440
|
+
[
|
|
441
|
+
'name' => 'TelematicsSettingsManager',
|
|
442
|
+
'description' => 'Policy for managing organization telematics history preferences.',
|
|
443
|
+
'permissions' => [
|
|
444
|
+
'see extension',
|
|
445
|
+
'* telematics-settings',
|
|
446
|
+
],
|
|
447
|
+
],
|
|
356
448
|
[
|
|
357
449
|
'name' => 'DriverOperations',
|
|
358
450
|
'description' => 'Policy for drivers to manage their assigned tasks and access necessary information.',
|
|
@@ -453,6 +545,13 @@ class FleetOps
|
|
|
453
545
|
'NavigatorSettingsManager',
|
|
454
546
|
],
|
|
455
547
|
],
|
|
548
|
+
[
|
|
549
|
+
'name' => 'Telematics Settings Manager',
|
|
550
|
+
'description' => 'Role responsible for managing organization telematics history preferences.',
|
|
551
|
+
'policies' => [
|
|
552
|
+
'TelematicsSettingsManager',
|
|
553
|
+
],
|
|
554
|
+
],
|
|
456
555
|
[
|
|
457
556
|
'name' => 'Driver',
|
|
458
557
|
'description' => 'Role for drivers with the necessary access to manage their daily tasks, including order management, and vehicle assignment.',
|
|
@@ -16,7 +16,7 @@ use Illuminate\Support\Facades\Log;
|
|
|
16
16
|
class DrainTelematicInbox extends Command
|
|
17
17
|
{
|
|
18
18
|
protected $signature = 'fleetops:drain-telematic-inbox';
|
|
19
|
-
protected $description = 'Recover pending telematic deliveries and
|
|
19
|
+
protected $description = 'Recover pending telematic deliveries and finish interrupted sync runs.';
|
|
20
20
|
|
|
21
21
|
public function handle(): int
|
|
22
22
|
{
|
|
@@ -58,24 +58,14 @@ class DrainTelematicInbox extends Command
|
|
|
58
58
|
try {
|
|
59
59
|
Queue::dispatch((new ProcessTelematicDelivery($row->uuid))->onQueue($optionsByConnection[$row->telematic_uuid]['ingestion_queue'] ?? 'default'));
|
|
60
60
|
} catch (\Throwable) {
|
|
61
|
-
// A broker outage must not prevent
|
|
61
|
+
// A broker outage must not prevent run recovery; retry the next minute.
|
|
62
62
|
break;
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
//
|
|
66
|
-
foreach (['processed' => now()->subHours(config('telematics.telemetry.processed_retention_hours', 24)), 'quarantined' => now()->subDays(config('telematics.telemetry.quarantine_retention_days', 7))] as $status => $cutoff) {
|
|
67
|
-
for ($batch = 0; $batch < 20; $batch++) {
|
|
68
|
-
$ids = DB::table('telematic_deliveries')->where('status', $status)->where('updated_at', '<', $cutoff)->limit(1000)->pluck('uuid');
|
|
69
|
-
if ($ids->isEmpty()) {
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
DB::table('telematic_deliveries')->whereIn('uuid', $ids)->delete();
|
|
73
|
-
}
|
|
74
|
-
}
|
|
65
|
+
// Retention for deliveries and sync runs is applied by fleetops:prune-telematics-data.
|
|
75
66
|
DB::table('telematic_sync_runs')->where('status', 'fetching')->where('updated_at', '<', now()->subMinutes(5))
|
|
76
67
|
->update(['status' => 'incomplete', 'error' => 'Worker interrupted; next scheduled sweep will recover.']);
|
|
77
68
|
DB::table('telematic_sync_runs')->where('status', 'ingesting')->get(['uuid'])->each(fn ($run) => Inbox::finishRun($run->uuid));
|
|
78
|
-
DB::table('telematic_sync_runs')->where('updated_at', '<', now()->subDays(7))->delete();
|
|
79
69
|
|
|
80
70
|
return self::SUCCESS;
|
|
81
71
|
}
|
|
@@ -60,7 +60,7 @@ class ProcessOperationalAlerts extends Command
|
|
|
60
60
|
|
|
61
61
|
foreach ($orders as $order) {
|
|
62
62
|
session(['company' => $order->company_uuid]);
|
|
63
|
-
$settings = $this->alertSettings();
|
|
63
|
+
$settings = $this->alertSettings($order);
|
|
64
64
|
|
|
65
65
|
if ($this->processLateDeparture($order, $settings, $dryRun)) {
|
|
66
66
|
$triggered++;
|
|
@@ -256,9 +256,9 @@ class ProcessOperationalAlerts extends Command
|
|
|
256
256
|
->min() ?? 0;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
protected function alertSettings(): array
|
|
259
|
+
protected function alertSettings(Order $order): array
|
|
260
260
|
{
|
|
261
|
-
$settings = Setting::
|
|
261
|
+
$settings = Setting::lookupForCompany($order->company_uuid, 'tracking', []);
|
|
262
262
|
$alerts = data_get($settings, 'alerts', []);
|
|
263
263
|
|
|
264
264
|
return [
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Console\Commands;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Support\Telematics\Retention\RetentionPolicy;
|
|
6
|
+
use Illuminate\Console\Command;
|
|
7
|
+
use Illuminate\Support\Collection;
|
|
8
|
+
use Illuminate\Support\Facades\Cache;
|
|
9
|
+
use Illuminate\Support\Facades\DB;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Applies each company's telematics retention policy.
|
|
13
|
+
*
|
|
14
|
+
* Deletes are hard deletes issued in bounded batches so a large backlog never
|
|
15
|
+
* monopolizes the tables ingestion is writing to; a capped run picks up where
|
|
16
|
+
* it left off on the next schedule tick.
|
|
17
|
+
*/
|
|
18
|
+
class PruneTelematicsData extends Command
|
|
19
|
+
{
|
|
20
|
+
public const TABLES = ['device_events', 'positions', 'telematic_deliveries', 'telematic_sync_runs'];
|
|
21
|
+
|
|
22
|
+
public const LOCK = 'fleetops:prune-telematics-data';
|
|
23
|
+
|
|
24
|
+
protected $signature = 'fleetops:prune-telematics-data
|
|
25
|
+
{--company= : Only prune data for this company (uuid or public id)}
|
|
26
|
+
{--orphans-only : Only prune data without an existing company or connection}
|
|
27
|
+
{--table=* : Only prune these tables (device_events, positions, telematic_deliveries, telematic_sync_runs)}
|
|
28
|
+
{--batch-size=1000 : Rows per delete or update statement}
|
|
29
|
+
{--max-batches=50 : Maximum batches per table per company per run}
|
|
30
|
+
{--dry-run : Report matching rows without deleting or compacting}
|
|
31
|
+
{--no-lock : Skip process locking}';
|
|
32
|
+
|
|
33
|
+
protected $description = 'Prune telematics device events, positions, deliveries and sync runs according to each company\'s retention policy.';
|
|
34
|
+
|
|
35
|
+
protected int $batchSize = 1000;
|
|
36
|
+
protected int $maxBatches = 50;
|
|
37
|
+
protected bool $dryRun = false;
|
|
38
|
+
|
|
39
|
+
public function handle(): int
|
|
40
|
+
{
|
|
41
|
+
$tables = $this->requestedTables();
|
|
42
|
+
if ($tables === null) {
|
|
43
|
+
$this->error('Unknown table. Valid tables: ' . implode(', ', self::TABLES) . '.');
|
|
44
|
+
|
|
45
|
+
return self::FAILURE;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
$lock = null;
|
|
49
|
+
if (!$this->option('no-lock')) {
|
|
50
|
+
$lock = Cache::lock(self::LOCK, 840);
|
|
51
|
+
if (!$lock->get()) {
|
|
52
|
+
$this->warn('Another telematics prune run appears to be in progress.');
|
|
53
|
+
|
|
54
|
+
return self::SUCCESS;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
// Queue workers may have cached settings before an administrator saved a new policy.
|
|
60
|
+
RetentionPolicy::flush();
|
|
61
|
+
$this->batchSize = max(100, min(5000, (int) $this->option('batch-size') ?: 1000));
|
|
62
|
+
$this->maxBatches = max(1, (int) $this->option('max-batches') ?: 50);
|
|
63
|
+
$this->dryRun = (bool) $this->option('dry-run');
|
|
64
|
+
|
|
65
|
+
$only = ((string) $this->option('company')) ?: null;
|
|
66
|
+
$orphansOnly = (bool) $this->option('orphans-only');
|
|
67
|
+
if ($only && $orphansOnly) {
|
|
68
|
+
$this->error('Choose either --company or --orphans-only.');
|
|
69
|
+
|
|
70
|
+
return self::FAILURE;
|
|
71
|
+
}
|
|
72
|
+
$companies = $orphansOnly ? new Collection() : $this->companies($only);
|
|
73
|
+
if ($only && $companies->isEmpty()) {
|
|
74
|
+
$this->error(sprintf('Company [%s] was not found.', $only));
|
|
75
|
+
|
|
76
|
+
return self::FAILURE;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
$totals = $this->emptyStats();
|
|
80
|
+
foreach ($companies as $company) {
|
|
81
|
+
$stats = $this->pruneCompany($company->uuid, $this->policyFor($company->uuid), $tables);
|
|
82
|
+
$totals = $this->mergeStats($totals, $stats);
|
|
83
|
+
$this->report($company->public_id ?: $company->uuid, $stats);
|
|
84
|
+
}
|
|
85
|
+
if (!$only) {
|
|
86
|
+
$stats = $this->pruneOrphans($this->policyFor(null), $tables);
|
|
87
|
+
$totals = $this->mergeStats($totals, $stats);
|
|
88
|
+
$this->report('orphaned', $stats);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
$this->info(sprintf(
|
|
92
|
+
'%s: deleted=%d compacted=%d batches=%d%s',
|
|
93
|
+
$this->dryRun ? 'Telematics prune dry run' : 'Telematics prune complete',
|
|
94
|
+
$totals['deleted'],
|
|
95
|
+
$totals['compacted'],
|
|
96
|
+
$totals['batches'],
|
|
97
|
+
$totals['capped'] ? ' (capped; more rows remain for the next run)' : ''
|
|
98
|
+
));
|
|
99
|
+
|
|
100
|
+
return self::SUCCESS;
|
|
101
|
+
} finally {
|
|
102
|
+
$lock?->release();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @return string[]|null null when an unknown table was requested
|
|
108
|
+
*/
|
|
109
|
+
protected function requestedTables(): ?array
|
|
110
|
+
{
|
|
111
|
+
$requested = array_values(array_filter((array) $this->option('table')));
|
|
112
|
+
if ($requested === []) {
|
|
113
|
+
return self::TABLES;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return array_diff($requested, self::TABLES) === [] ? array_values(array_unique($requested)) : null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected function companies(?string $only): Collection
|
|
120
|
+
{
|
|
121
|
+
$query = DB::table('companies')->select(['id', 'uuid', 'public_id'])->orderBy('id');
|
|
122
|
+
if ($only) {
|
|
123
|
+
$query->where(fn ($where) => $where->where('uuid', $only)->orWhere('public_id', $only));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return $query->get();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
protected function policyFor(?string $companyUuid): RetentionPolicy
|
|
130
|
+
{
|
|
131
|
+
return RetentionPolicy::forCompany($companyUuid);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
protected function pruneCompany(string $companyUuid, RetentionPolicy $policy, array $tables): array
|
|
135
|
+
{
|
|
136
|
+
$rows = fn ($query) => $query->where('company_uuid', $companyUuid);
|
|
137
|
+
|
|
138
|
+
$telematics = null;
|
|
139
|
+
if (array_intersect(['telematic_deliveries', 'telematic_sync_runs'], $tables) !== []) {
|
|
140
|
+
// Inbox tables carry no company; trashed connections still prune under their company's policy.
|
|
141
|
+
$telematics = DB::table('telematics')->where('company_uuid', $companyUuid)->pluck('uuid')->all();
|
|
142
|
+
}
|
|
143
|
+
$inbox = $telematics ? fn ($query) => $query->whereIn('telematic_uuid', $telematics) : null;
|
|
144
|
+
|
|
145
|
+
return $this->prune($rows, $inbox, $policy, $tables);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Rows whose company or connection no longer exists fall back to the system defaults.
|
|
150
|
+
*/
|
|
151
|
+
protected function pruneOrphans(RetentionPolicy $policy, array $tables): array
|
|
152
|
+
{
|
|
153
|
+
$rows = fn ($query) => $query->where(function ($where) {
|
|
154
|
+
$where->whereNull('company_uuid')->orWhereNotIn('company_uuid', DB::table('companies')->select('uuid'));
|
|
155
|
+
});
|
|
156
|
+
$inbox = fn ($query) => $query->whereNotExists(function ($connections) use ($query) {
|
|
157
|
+
$connections->selectRaw('1')
|
|
158
|
+
->from('telematics')
|
|
159
|
+
->join('companies', 'companies.uuid', '=', 'telematics.company_uuid')
|
|
160
|
+
->whereColumn('telematics.uuid', $query->from . '.telematic_uuid');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return $this->prune($rows, $inbox, $policy, $tables);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
protected function prune(\Closure $rows, ?\Closure $inbox, RetentionPolicy $policy, array $tables): array
|
|
167
|
+
{
|
|
168
|
+
$stats = $this->emptyStats();
|
|
169
|
+
if (in_array('device_events', $tables, true)) {
|
|
170
|
+
$stats['tables']['device_events'] = $this->pruneDeviceEvents($rows, $policy);
|
|
171
|
+
}
|
|
172
|
+
if (in_array('positions', $tables, true)) {
|
|
173
|
+
$stats['tables']['positions'] = $this->prunePositions($rows, $policy);
|
|
174
|
+
}
|
|
175
|
+
if ($inbox && in_array('telematic_deliveries', $tables, true)) {
|
|
176
|
+
$stats['tables']['telematic_deliveries'] = $this->pruneDeliveries($inbox, $policy);
|
|
177
|
+
}
|
|
178
|
+
if ($inbox && in_array('telematic_sync_runs', $tables, true)) {
|
|
179
|
+
$stats['tables']['telematic_sync_runs'] = $this->pruneSyncRuns($inbox, $policy);
|
|
180
|
+
}
|
|
181
|
+
foreach ($stats['tables'] as $table) {
|
|
182
|
+
$stats['deleted'] += $table['deleted'];
|
|
183
|
+
$stats['compacted'] += $table['compacted'];
|
|
184
|
+
$stats['batches'] += $table['batches'];
|
|
185
|
+
$stats['capped'] = $stats['capped'] || $table['capped'];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return $stats;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
protected function pruneDeviceEvents(\Closure $rows, RetentionPolicy $policy): array
|
|
192
|
+
{
|
|
193
|
+
$stats = $this->emptyTableStats();
|
|
194
|
+
$cutoff = $policy->cutoff('event_retention_days');
|
|
195
|
+
// Soft-deleted events only grow the table; purge them regardless of age.
|
|
196
|
+
$this->deleteInBatches('device_events', 'id', fn ($query) => $rows($query)->whereNotNull('deleted_at'), $stats);
|
|
197
|
+
if ($cutoff) {
|
|
198
|
+
$this->deleteInBatches('device_events', 'id', fn ($query) => $rows($query)->where('created_at', '<', $cutoff->toDateTimeString()), $stats);
|
|
199
|
+
}
|
|
200
|
+
if ($policy->compactsEvents() && ($compactAt = $policy->cutoff('event_compact_after_days'))) {
|
|
201
|
+
// Keep the event (type, severity, location, normalized data) but drop the raw provider blobs.
|
|
202
|
+
// Rows past the delete cutoff are left to the delete sweep rather than rewritten first.
|
|
203
|
+
$this->updateInBatches(
|
|
204
|
+
'device_events',
|
|
205
|
+
'id',
|
|
206
|
+
function ($query) use ($rows, $cutoff, $compactAt) {
|
|
207
|
+
$query = $rows($query)->where('created_at', '<', $compactAt->toDateTimeString())->where(fn ($where) => $where->whereNotNull('payload')->orWhereNotNull('meta'));
|
|
208
|
+
|
|
209
|
+
return $cutoff ? $query->where('created_at', '>=', $cutoff->toDateTimeString()) : $query;
|
|
210
|
+
},
|
|
211
|
+
['payload' => null, 'meta' => null],
|
|
212
|
+
$stats
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return $stats;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
protected function prunePositions(\Closure $rows, RetentionPolicy $policy): array
|
|
220
|
+
{
|
|
221
|
+
$stats = $this->emptyTableStats();
|
|
222
|
+
$this->deleteInBatches('positions', 'id', fn ($query) => $rows($query)->whereNotNull('deleted_at'), $stats);
|
|
223
|
+
if ($cutoff = $policy->cutoff('position_retention_days')) {
|
|
224
|
+
$this->deleteInBatches('positions', 'id', fn ($query) => $rows($query)->where('created_at', '<', $cutoff->toDateTimeString()), $stats);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return $stats;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
protected function pruneDeliveries(\Closure $inbox, RetentionPolicy $policy): array
|
|
231
|
+
{
|
|
232
|
+
$stats = $this->emptyTableStats();
|
|
233
|
+
foreach (['processed' => 'processed_retention_hours', 'quarantined' => 'quarantine_retention_days'] as $status => $key) {
|
|
234
|
+
if ($cutoff = $policy->cutoff($key)) {
|
|
235
|
+
$this->deleteInBatches('telematic_deliveries', 'uuid', fn ($query) => $inbox($query)->where('status', $status)->where('updated_at', '<', $cutoff->toDateTimeString()), $stats);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return $stats;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
protected function pruneSyncRuns(\Closure $inbox, RetentionPolicy $policy): array
|
|
243
|
+
{
|
|
244
|
+
$stats = $this->emptyTableStats();
|
|
245
|
+
if ($cutoff = $policy->cutoff('sync_run_retention_days')) {
|
|
246
|
+
// In-flight runs are recovered by the drain command, never expired here.
|
|
247
|
+
$this->deleteInBatches('telematic_sync_runs', 'uuid', fn ($query) => $inbox($query)->whereNotIn('status', ['fetching', 'ingesting'])->where('updated_at', '<', $cutoff->toDateTimeString()), $stats);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return $stats;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
protected function deleteInBatches(string $table, string $key, \Closure $scope, array &$stats): void
|
|
254
|
+
{
|
|
255
|
+
if ($this->dryRun) {
|
|
256
|
+
$stats['deleted'] += $scope(DB::table($table))->count();
|
|
257
|
+
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
for ($batch = 0; $batch < $this->maxBatches; $batch++) {
|
|
261
|
+
$ids = $scope(DB::table($table))->orderBy($key)->limit($this->batchSize)->pluck($key);
|
|
262
|
+
if ($ids->isEmpty()) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
$stats['batches']++;
|
|
266
|
+
$stats['deleted'] += DB::table($table)->whereIn($key, $ids->all())->delete();
|
|
267
|
+
}
|
|
268
|
+
$stats['capped'] = true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
protected function updateInBatches(string $table, string $key, \Closure $scope, array $values, array &$stats): void
|
|
272
|
+
{
|
|
273
|
+
if ($this->dryRun) {
|
|
274
|
+
$stats['compacted'] += $scope(DB::table($table))->count();
|
|
275
|
+
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
for ($batch = 0; $batch < $this->maxBatches; $batch++) {
|
|
279
|
+
$ids = $scope(DB::table($table))->orderBy($key)->limit($this->batchSize)->pluck($key);
|
|
280
|
+
if ($ids->isEmpty()) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
$stats['batches']++;
|
|
284
|
+
$stats['compacted'] += DB::table($table)->whereIn($key, $ids->all())->update($values);
|
|
285
|
+
}
|
|
286
|
+
$stats['capped'] = true;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
protected function report(string $label, array $stats): void
|
|
290
|
+
{
|
|
291
|
+
foreach ($stats['tables'] as $table => $counts) {
|
|
292
|
+
if (!$counts['deleted'] && !$counts['compacted'] && !$counts['capped']) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
$this->info(sprintf('%s %s: deleted=%d compacted=%d batches=%d%s', $label, $table, $counts['deleted'], $counts['compacted'], $counts['batches'], $counts['capped'] ? ' capped' : ''));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
protected function emptyTableStats(): array
|
|
300
|
+
{
|
|
301
|
+
return ['deleted' => 0, 'compacted' => 0, 'batches' => 0, 'capped' => false];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
protected function emptyStats(): array
|
|
305
|
+
{
|
|
306
|
+
return ['deleted' => 0, 'compacted' => 0, 'batches' => 0, 'capped' => false, 'tables' => []];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
protected function mergeStats(array $totals, array $stats): array
|
|
310
|
+
{
|
|
311
|
+
$totals['deleted'] += $stats['deleted'];
|
|
312
|
+
$totals['compacted'] += $stats['compacted'];
|
|
313
|
+
$totals['batches'] += $stats['batches'];
|
|
314
|
+
$totals['capped'] = $totals['capped'] || $stats['capped'];
|
|
315
|
+
|
|
316
|
+
return $totals;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Http\Controllers\Internal\v1\OrchestrationController as InternalOrchestrationController;
|
|
6
|
+
use Fleetbase\FleetOps\Orchestration\OrchestrationEngineRegistry;
|
|
6
7
|
use Illuminate\Http\JsonResponse;
|
|
7
8
|
use Illuminate\Http\Request;
|
|
8
9
|
|
|
@@ -14,6 +15,14 @@ use Illuminate\Http\Request;
|
|
|
14
15
|
*/
|
|
15
16
|
class OrchestrationController extends InternalOrchestrationController
|
|
16
17
|
{
|
|
18
|
+
public function __construct(OrchestrationEngineRegistry $registry)
|
|
19
|
+
{
|
|
20
|
+
// The fleetbase.api route group authenticates API credentials and establishes
|
|
21
|
+
// their company scope. Console IAM middleware belongs to the internal
|
|
22
|
+
// workbench, not to these organization-credential endpoints.
|
|
23
|
+
$this->registry = $registry;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
public function run(Request $request): JsonResponse
|
|
18
27
|
{
|
|
19
28
|
return $this->publicResponse(parent::run($request));
|
|
@@ -2,12 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Controllers;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Traits\AuthorizesMethods;
|
|
5
6
|
use Fleetbase\Http\Controllers\FleetbaseController;
|
|
7
|
+
use Illuminate\Database\Eloquent\Model;
|
|
6
8
|
|
|
7
9
|
class FleetOpsController extends FleetbaseController
|
|
8
10
|
{
|
|
11
|
+
use AuthorizesMethods;
|
|
12
|
+
|
|
9
13
|
/**
|
|
10
14
|
* The package namespace used to resolve from.
|
|
11
15
|
*/
|
|
12
16
|
public string $namespace = '\\Fleetbase\\FleetOps';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Explicit permissions for custom actions, keyed by controller method, e.g.
|
|
20
|
+
* `['bulkDispatch' => 'dispatch order']`. Those methods also carry
|
|
21
|
+
* `#[SkipAuthorizationCheck]` so AuthorizationGuard does not demand the
|
|
22
|
+
* permission it would otherwise infer from the HTTP verb.
|
|
23
|
+
*
|
|
24
|
+
* @var array<string, string|string[]>
|
|
25
|
+
*/
|
|
26
|
+
protected array $methodPermissions = [];
|
|
27
|
+
|
|
28
|
+
public function __construct(?Model $model = null, ?string $resource = null)
|
|
29
|
+
{
|
|
30
|
+
parent::__construct($model, $resource);
|
|
31
|
+
|
|
32
|
+
$this->authorizeMethods($this->methodPermissions);
|
|
33
|
+
}
|
|
13
34
|
}
|
|
@@ -14,6 +14,7 @@ use Fleetbase\FleetOps\Support\Analytics\OperationsPulse;
|
|
|
14
14
|
use Fleetbase\FleetOps\Support\Analytics\OrdersByStatus;
|
|
15
15
|
use Fleetbase\FleetOps\Support\Analytics\RevenueTrend;
|
|
16
16
|
use Fleetbase\FleetOps\Support\Analytics\TopDrivers;
|
|
17
|
+
use Fleetbase\FleetOps\Support\Authorization;
|
|
17
18
|
use Fleetbase\Http\Controllers\Controller;
|
|
18
19
|
use Illuminate\Http\Request;
|
|
19
20
|
|
|
@@ -28,6 +29,16 @@ use Illuminate\Http\Request;
|
|
|
28
29
|
*/
|
|
29
30
|
class AnalyticsController extends Controller
|
|
30
31
|
{
|
|
32
|
+
public function __construct()
|
|
33
|
+
{
|
|
34
|
+
// Not a resource controller, so AuthorizationGuard cannot resolve a permission for it.
|
|
35
|
+
$this->middleware(function ($request, $next) {
|
|
36
|
+
Authorization::authorize('view analytics');
|
|
37
|
+
|
|
38
|
+
return $next($request);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
public function operationsPulse(Request $request)
|
|
32
43
|
{
|
|
33
44
|
return $this->run($request, OperationsPulse::class);
|