@fleetbase/fleetops-engine 0.6.69 → 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.
- package/RELEASE.md +41 -8
- 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/telematic/hub.hbs +1 -1
- package/addon/components/widget/live-fleet.hbs +8 -40
- package/addon/controllers/analytics/reports/index.js +5 -0
- package/addon/controllers/connectivity/devices/index.js +4 -0
- package/addon/controllers/connectivity/fuel-providers/index.js +6 -4
- package/addon/controllers/connectivity/sensors/index.js +4 -0
- package/addon/controllers/connectivity/telematics/index.js +4 -0
- package/addon/controllers/maintenance/equipment/index.js +11 -4
- package/addon/controllers/maintenance/inspection-forms/index.js +2 -2
- package/addon/controllers/maintenance/inspection-submissions/index.js +8 -2
- package/addon/controllers/maintenance/maintenances/index.js +4 -0
- package/addon/controllers/maintenance/parts/index.js +11 -4
- package/addon/controllers/maintenance/schedules/index.js +17 -4
- package/addon/controllers/maintenance/work-orders/index.js +11 -4
- package/addon/controllers/management/contacts/index.js +4 -0
- package/addon/controllers/management/drivers/index.js +4 -0
- package/addon/controllers/management/fleets/index.js +4 -0
- package/addon/controllers/management/fuel-reports/index.js +4 -0
- package/addon/controllers/management/fuel-transactions/index.js +9 -7
- package/addon/controllers/management/issues/index.js +4 -0
- package/addon/controllers/management/places/index.js +4 -0
- package/addon/controllers/management/vehicles/index.js +4 -0
- package/addon/controllers/management/vendors/index.js +4 -0
- package/addon/controllers/operations/orders/index/details.js +30 -1
- package/addon/controllers/operations/orders/index.js +7 -0
- package/addon/controllers/operations/service-rates/index.js +3 -0
- package/addon/controllers/settings/index.js +13 -0
- package/addon/controllers/settings/telematics.js +113 -0
- package/addon/extension.js +38 -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.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/templates/settings/telematics.hbs +70 -0
- 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/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 +1 -1
- 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 +22 -0
- 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/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/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/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/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 +48 -0
|
@@ -5,17 +5,19 @@ use Illuminate\Http\Request;
|
|
|
5
5
|
|
|
6
6
|
class FleetOpsSettingControllerProbe extends SettingController
|
|
7
7
|
{
|
|
8
|
-
public array $configured
|
|
9
|
-
public array $configuredCompany
|
|
10
|
-
public array $settingValues
|
|
11
|
-
public array $
|
|
12
|
-
public array $
|
|
13
|
-
public array $
|
|
14
|
-
public
|
|
15
|
-
public
|
|
16
|
-
public array $
|
|
17
|
-
public array $
|
|
18
|
-
public
|
|
8
|
+
public array $configured = [];
|
|
9
|
+
public array $configuredCompany = [];
|
|
10
|
+
public array $settingValues = [];
|
|
11
|
+
public array $orderConfigKeys = [];
|
|
12
|
+
public array $settings = [];
|
|
13
|
+
public array $companySettings = [];
|
|
14
|
+
public array $lookupCompanySettings = [];
|
|
15
|
+
public mixed $company = null;
|
|
16
|
+
public array $notifiables = [];
|
|
17
|
+
public array $notifications = [];
|
|
18
|
+
public array $providers = [];
|
|
19
|
+
public string $googleMapsApiKey = '';
|
|
20
|
+
public array $reconciledTelematicsDefaults = [];
|
|
19
21
|
|
|
20
22
|
protected function configureSetting(string $key, mixed $value): mixed
|
|
21
23
|
{
|
|
@@ -34,6 +36,11 @@ class FleetOpsSettingControllerProbe extends SettingController
|
|
|
34
36
|
return null;
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
protected function companyOrderConfigKeys(): array
|
|
40
|
+
{
|
|
41
|
+
return $this->orderConfigKeys;
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
protected function settingValue(string $key): mixed
|
|
38
45
|
{
|
|
39
46
|
return $this->settingValues[$key] ?? null;
|
|
@@ -59,6 +66,16 @@ class FleetOpsSettingControllerProbe extends SettingController
|
|
|
59
66
|
return $this->company;
|
|
60
67
|
}
|
|
61
68
|
|
|
69
|
+
protected function reconcileTelematicsCompanyPreferences(array $defaults): void
|
|
70
|
+
{
|
|
71
|
+
$this->reconciledTelematicsDefaults = $defaults;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
protected function withTelematicsPolicyLock(Closure $callback): mixed
|
|
75
|
+
{
|
|
76
|
+
return $callback();
|
|
77
|
+
}
|
|
78
|
+
|
|
62
79
|
protected function notificationNotifiables(): array
|
|
63
80
|
{
|
|
64
81
|
return $this->notifiables;
|
|
@@ -78,6 +95,45 @@ class FleetOpsSettingControllerProbe extends SettingController
|
|
|
78
95
|
{
|
|
79
96
|
return $this->googleMapsApiKey;
|
|
80
97
|
}
|
|
98
|
+
|
|
99
|
+
public array $counts = [];
|
|
100
|
+
public array $oldest = [];
|
|
101
|
+
public array $tableStatistics = [];
|
|
102
|
+
public array $countScopes = [];
|
|
103
|
+
public array $pruned = [];
|
|
104
|
+
|
|
105
|
+
protected function tableUsage(string $table, Closure $scope, string $ageColumn): array
|
|
106
|
+
{
|
|
107
|
+
// System storage must not add company or connection filters.
|
|
108
|
+
$scope(new stdClass());
|
|
109
|
+
|
|
110
|
+
return [
|
|
111
|
+
'rows' => $this->tableCount($table, $scope),
|
|
112
|
+
'oldest' => $this->tableOldest($table, $scope, $ageColumn),
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
protected function tableCount(string $table, Closure $scope): int
|
|
117
|
+
{
|
|
118
|
+
$this->countScopes[] = $table;
|
|
119
|
+
|
|
120
|
+
return $this->counts[$table] ?? 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
protected function tableOldest(string $table, Closure $scope, string $column): ?string
|
|
124
|
+
{
|
|
125
|
+
return $this->oldest[$table] ?? null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
protected function storageTableStatistics(array $tables): array
|
|
129
|
+
{
|
|
130
|
+
return $this->tableStatistics;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
protected function dispatchTelematicsPrune(): void
|
|
134
|
+
{
|
|
135
|
+
$this->pruned[] = 'system';
|
|
136
|
+
}
|
|
81
137
|
}
|
|
82
138
|
|
|
83
139
|
class FleetOpsTrackingProviderOptionFake
|
|
@@ -107,16 +163,22 @@ function fleetopsJsonPayload(mixed $response): array
|
|
|
107
163
|
}
|
|
108
164
|
|
|
109
165
|
test('setting controller persists and returns basic company settings through configured keys', function () {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
$
|
|
113
|
-
|
|
166
|
+
session(['company' => 'company-1']);
|
|
167
|
+
$controller = new FleetOpsSettingControllerProbe();
|
|
168
|
+
$controller->orderConfigKeys = ['orders'];
|
|
169
|
+
|
|
170
|
+
// The setting is one platform-wide map keyed by order config: another company's entry must survive,
|
|
171
|
+
// and this company cannot write keys for order configs it does not own.
|
|
172
|
+
$controller->settingValues['fleet-ops.entity-editing-settings'] = ['foreign-config' => ['editable' => true]];
|
|
173
|
+
$entityPayload = fleetopsJsonPayload($controller->saveEntityEditingSettings(new Request([
|
|
174
|
+
'entityEditingSettings' => ['orders' => ['editable' => true], 'foreign-config' => ['editable' => false]],
|
|
114
175
|
])));
|
|
115
|
-
$controller->settingValues['fleet-ops.entity-editing-settings'] = ['orders' => ['editable' => false]];
|
|
176
|
+
$controller->settingValues['fleet-ops.entity-editing-settings'] = ['orders' => ['editable' => false], 'foreign-config' => ['editable' => true]];
|
|
116
177
|
|
|
178
|
+
// A request-supplied company id is ignored in favour of the session company.
|
|
117
179
|
$disabledDriverPayload = fleetopsJsonPayload($controller->savedDriverOnboardSettings(new Request([
|
|
118
180
|
'driverOnboardSettings' => [
|
|
119
|
-
'companyId' => 'company-
|
|
181
|
+
'companyId' => 'company-2',
|
|
120
182
|
'enableDriverOnboardFromApp' => false,
|
|
121
183
|
'driverMustProvideOnboardDoucments' => true,
|
|
122
184
|
'requiredOnboardDocuments' => ['license'],
|
|
@@ -137,7 +199,7 @@ test('setting controller persists and returns basic company settings through con
|
|
|
137
199
|
$controller->company = (object) ['stripe_connect_id' => 'acct_123'];
|
|
138
200
|
|
|
139
201
|
expect($entityPayload)->toBe(['entityEditingSettings' => ['orders' => ['editable' => true]]])
|
|
140
|
-
->and($controller->configured['fleet-ops.entity-editing-settings'])->toBe(['orders' => ['editable' => true]])
|
|
202
|
+
->and($controller->configured['fleet-ops.entity-editing-settings'])->toBe(['foreign-config' => ['editable' => true], 'orders' => ['editable' => true]])
|
|
141
203
|
->and(fleetopsJsonPayload($controller->getEntityEditingSettings()))->toBe(['entityEditingSettings' => ['orders' => ['editable' => false]]])
|
|
142
204
|
->and(fleetopsJsonPayload((new FleetOpsSettingControllerProbe())->getEntityEditingSettings()))->toBe(['entityEditingSettings' => []])
|
|
143
205
|
->and($disabledDriverPayload['driverOnboardSettings'])->toMatchArray([
|
|
@@ -148,6 +210,7 @@ test('setting controller persists and returns basic company settings through con
|
|
|
148
210
|
'driverOnboardAppMethod' => '',
|
|
149
211
|
])
|
|
150
212
|
->and($controller->configured['fleet-ops.driver-onboard-settings.company-1'])->toBe($disabledDriverPayload['driverOnboardSettings'])
|
|
213
|
+
->and($controller->configured)->not->toHaveKey('fleet-ops.driver-onboard-settings.company-2')
|
|
151
214
|
->and(fleetopsJsonPayload($controller->getDriverOnboardSettings('company-1')))->toBe(['driverOnboardSettings' => ['enableDriverOnboardFromApp' => true]])
|
|
152
215
|
->and(fleetopsJsonPayload((new FleetOpsSettingControllerProbe())->getDriverOnboardSettings('missing')))->toBe(['driverOnboardSettings' => []])
|
|
153
216
|
->and($enabledConfigs)->toBe(['order-express', 'order-freight'])
|
|
@@ -441,3 +504,351 @@ test('setting controller sanitizes leaflet tile provider urls', function () {
|
|
|
441
504
|
expect($defaults['leafletTileUrl'])->toBe('')
|
|
442
505
|
->and($defaults['leafletDarkTileUrl'])->toBe('');
|
|
443
506
|
});
|
|
507
|
+
|
|
508
|
+
test('telematics settings expose only customer history preferences within administrator limits', function () {
|
|
509
|
+
config(['telematics.telemetry' => ['event_retention_days' => 45, 'processed_retention_hours' => 48, 'log_telemetry_activity' => false, 'poll_queue' => 'default']]);
|
|
510
|
+
$controller = new FleetOpsSettingControllerProbe();
|
|
511
|
+
$controller->company = (object) ['uuid' => 'company-1'];
|
|
512
|
+
$controller->settings['fleet-ops.telematics-settings'] = ['position_retention_days' => 120, 'event_retention_days' => 99999, 'max_event_retention_days' => 60];
|
|
513
|
+
$controller->companySettings['fleet-ops.telematics-settings'] = ['event_compact_after_days' => 3, 'log_telemetry_activity' => '1'];
|
|
514
|
+
|
|
515
|
+
$settings = fleetopsJsonPayload($controller->getTelematicsSettings());
|
|
516
|
+
expect($settings['event_retention_days'])->toBe(60)
|
|
517
|
+
->and($settings['position_retention_days'])->toBe(120)
|
|
518
|
+
->and($settings)->not->toHaveKeys(['event_compact_after_days', 'processed_retention_hours', 'log_telemetry_activity'])
|
|
519
|
+
->and($settings['preferences'])->toBe(['event_retention_days' => null, 'position_retention_days' => null])
|
|
520
|
+
->and($settings['defaults'])->toBe(['event_retention_days' => 60, 'position_retention_days' => 120])
|
|
521
|
+
->and($settings['policy'])->toBe(['max_event_retention_days' => 60, 'max_position_retention_days' => 0])
|
|
522
|
+
->and($settings['limits'])->toBe(['event_retention_days' => [1, 3650], 'position_retention_days' => [1, 3650]]);
|
|
523
|
+
|
|
524
|
+
$saved = fleetopsJsonPayload($controller->saveTelematicsSettings(new Request([
|
|
525
|
+
'event_retention_days' => '0',
|
|
526
|
+
'event_compact_after_days' => -3,
|
|
527
|
+
'processed_retention_hours' => 5000,
|
|
528
|
+
'log_telemetry_activity' => 'false',
|
|
529
|
+
'unexpected' => 'ignored',
|
|
530
|
+
])));
|
|
531
|
+
expect($saved['status'])->toBe('ok')
|
|
532
|
+
->and($saved['event_retention_days'])->toBe(60)
|
|
533
|
+
->and($saved['position_retention_days'])->toBe(120)
|
|
534
|
+
->and($saved['preferences'])->toBe(['event_retention_days' => 60, 'position_retention_days' => null])
|
|
535
|
+
->and($saved)->not->toHaveKeys(['event_compact_after_days', 'processed_retention_hours', 'log_telemetry_activity'])
|
|
536
|
+
->and($controller->configuredCompany['fleet-ops.telematics-settings'])->toBe(['event_retention_days' => 60]);
|
|
537
|
+
|
|
538
|
+
$admin = fleetopsJsonPayload($controller->getAdminTelematicsSettings());
|
|
539
|
+
expect($admin['event_retention_days'])->toBe(3650)
|
|
540
|
+
->and($admin['limits'])->toHaveKeys(['sync_run_retention_days', 'max_event_retention_days', 'max_position_retention_days'])
|
|
541
|
+
->and($admin['log_telemetry_activity'])->toBeFalse();
|
|
542
|
+
|
|
543
|
+
$savedAdmin = fleetopsJsonPayload($controller->saveAdminTelematicsSettings(new Request([
|
|
544
|
+
'event_retention_days' => 60,
|
|
545
|
+
'sync_run_retention_days' => 0,
|
|
546
|
+
'max_event_retention_days' => 90,
|
|
547
|
+
'max_position_retention_days' => 45,
|
|
548
|
+
'log_telemetry_activity' => true,
|
|
549
|
+
])));
|
|
550
|
+
expect($savedAdmin['event_retention_days'])->toBe(60)
|
|
551
|
+
->and($savedAdmin['sync_run_retention_days'])->toBe(0)
|
|
552
|
+
->and($savedAdmin['processed_retention_hours'])->toBe(48)
|
|
553
|
+
->and($savedAdmin['max_event_retention_days'])->toBe(90)
|
|
554
|
+
->and($savedAdmin['max_position_retention_days'])->toBe(45)
|
|
555
|
+
->and($savedAdmin['log_telemetry_activity'])->toBeTrue()
|
|
556
|
+
->and($controller->reconciledTelematicsDefaults['max_event_retention_days'])->toBe(90)
|
|
557
|
+
->and($controller->reconciledTelematicsDefaults['max_position_retention_days'])->toBe(45)
|
|
558
|
+
->and($controller->configured['fleet-ops.telematics-settings']['position_retention_days'])->toBe(120);
|
|
559
|
+
|
|
560
|
+
$partialAdmin = fleetopsJsonPayload($controller->saveAdminTelematicsSettings(new Request(['log_telemetry_activity' => false])));
|
|
561
|
+
expect($partialAdmin['max_event_retention_days'])->toBe(90)
|
|
562
|
+
->and($partialAdmin['max_position_retention_days'])->toBe(45);
|
|
563
|
+
|
|
564
|
+
$inherited = fleetopsJsonPayload($controller->saveTelematicsSettings(new Request(['event_retention_days' => null, 'position_retention_days' => null])));
|
|
565
|
+
expect($inherited['event_retention_days'])->toBe(60)
|
|
566
|
+
->and($inherited['position_retention_days'])->toBe(45)
|
|
567
|
+
->and($inherited['preferences'])->toBe(['event_retention_days' => null, 'position_retention_days' => null])
|
|
568
|
+
->and($controller->configuredCompany['fleet-ops.telematics-settings'])->toBe([]);
|
|
569
|
+
config(['telematics.telemetry' => []]);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
test('explicit unlimited customer history stays distinct from inheritance and reads back with current maximums', function () {
|
|
573
|
+
$controller = new FleetOpsSettingControllerProbe();
|
|
574
|
+
$controller->company = (object) ['uuid' => 'company-1'];
|
|
575
|
+
|
|
576
|
+
$saved = fleetopsJsonPayload($controller->saveTelematicsSettings(new Request([
|
|
577
|
+
'event_retention_days' => 0,
|
|
578
|
+
'position_retention_days' => null,
|
|
579
|
+
])));
|
|
580
|
+
expect($saved['event_retention_days'])->toBe(0)
|
|
581
|
+
->and($saved['preferences'])->toBe(['event_retention_days' => 0, 'position_retention_days' => null])
|
|
582
|
+
->and($controller->configuredCompany['fleet-ops.telematics-settings'])->toBe(['event_retention_days' => 0])
|
|
583
|
+
->and(fleetopsJsonPayload($controller->getTelematicsSettings())['preferences'])->toBe($saved['preferences']);
|
|
584
|
+
|
|
585
|
+
$controller->settings['fleet-ops.telematics-settings'] = ['max_event_retention_days' => 90, 'max_position_retention_days' => 360];
|
|
586
|
+
$loaded = fleetopsJsonPayload($controller->getTelematicsSettings());
|
|
587
|
+
expect($loaded['event_retention_days'])->toBe(90)
|
|
588
|
+
->and($loaded['preferences'])->toBe(['event_retention_days' => 90, 'position_retention_days' => null])
|
|
589
|
+
->and($loaded['policy'])->toBe(['max_event_retention_days' => 90, 'max_position_retention_days' => 360]);
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
test('telematics customer settings require a company session', function () {
|
|
593
|
+
$controller = new FleetOpsSettingControllerProbe();
|
|
594
|
+
expect($controller->getTelematicsSettings()->getStatusCode())->toBe(401)
|
|
595
|
+
->and($controller->saveTelematicsSettings(new Request())->getStatusCode())->toBe(401)
|
|
596
|
+
->and($controller->configuredCompany)->toBe([])
|
|
597
|
+
->and($controller->pruned)->toBe([]);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
test('admin telematics storage and cleanup cover the system without a company session', function () {
|
|
601
|
+
$hadRequest = app()->bound('request');
|
|
602
|
+
$originalRequest = $hadRequest ? app('request') : null;
|
|
603
|
+
$originalQueueConfig = config('queue', []);
|
|
604
|
+
config(['queue.default' => 'database', 'queue.connections.database.driver' => 'database']);
|
|
605
|
+
app()->instance('request', new Request(['include_payload_counts' => true]));
|
|
606
|
+
Illuminate\Support\Carbon::setTestNow('2026-09-23 12:00:00 UTC');
|
|
607
|
+
try {
|
|
608
|
+
$controller = new FleetOpsSettingControllerProbe();
|
|
609
|
+
$controller->counts = ['device_events' => 500, 'positions' => 40, 'telematic_deliveries' => 6, 'telematic_sync_runs' => 3];
|
|
610
|
+
$controller->oldest = ['device_events' => '2026-08-01 00:00:00'];
|
|
611
|
+
$controller->tableStatistics = [
|
|
612
|
+
'device_events' => ['rows' => 500, 'rows_estimated' => true, 'estimated_bytes' => 16000000],
|
|
613
|
+
'positions' => ['rows' => 40, 'rows_estimated' => true, 'estimated_bytes' => 28000],
|
|
614
|
+
];
|
|
615
|
+
|
|
616
|
+
$usage = fleetopsJsonPayload($controller->getTelematicsStorageUsage());
|
|
617
|
+
expect($usage['tables']['device_events'])->toBe([
|
|
618
|
+
'rows' => 500,
|
|
619
|
+
'rows_estimated' => true,
|
|
620
|
+
'estimated_bytes' => 16000000,
|
|
621
|
+
'oldest' => '2026-08-01 00:00:00',
|
|
622
|
+
'raw_payload_rows' => 500,
|
|
623
|
+
'compactable_rows' => 500,
|
|
624
|
+
])
|
|
625
|
+
->and($usage['tables']['positions'])->toBe(['rows' => 40, 'rows_estimated' => true, 'estimated_bytes' => 28000, 'oldest' => null])
|
|
626
|
+
->and($usage['tables']['telematic_deliveries'])->toBe(['rows' => 6, 'oldest' => null])
|
|
627
|
+
->and($usage['tables']['telematic_sync_runs']['rows'])->toBe(3)
|
|
628
|
+
->and($usage['scope'])->toBe('system')
|
|
629
|
+
->and($usage)->not->toHaveKey('company')
|
|
630
|
+
->and($usage['generated_at'])->toBe('2026-09-23T12:00:00.000000Z')
|
|
631
|
+
->and($controller->countScopes)->toBe(['telematic_deliveries', 'telematic_sync_runs', 'device_events', 'device_events']);
|
|
632
|
+
|
|
633
|
+
// Missing metadata uses bounded full-table counts, independent of any organization's connections.
|
|
634
|
+
$controller->tableStatistics = [];
|
|
635
|
+
$controller->settings['fleet-ops.telematics-settings'] = ['event_compact_after_days' => 0];
|
|
636
|
+
$controller->countScopes = [];
|
|
637
|
+
$usage = fleetopsJsonPayload($controller->getTelematicsStorageUsage());
|
|
638
|
+
expect($usage['tables']['telematic_deliveries'])->toBe(['rows' => 6, 'oldest' => null])
|
|
639
|
+
->and($usage['tables']['telematic_sync_runs'])->toBe(['rows' => 3, 'oldest' => null])
|
|
640
|
+
->and($usage['tables']['device_events']['compactable_rows'])->toBe(0)
|
|
641
|
+
->and($controller->countScopes)->toBe(['device_events', 'positions', 'telematic_deliveries', 'telematic_sync_runs', 'device_events']);
|
|
642
|
+
|
|
643
|
+
$queued = $controller->runTelematicsRetention();
|
|
644
|
+
expect($queued->getStatusCode())->toBe(202)
|
|
645
|
+
->and(fleetopsJsonPayload($queued)['status'])->toBe('queued')
|
|
646
|
+
->and(fleetopsJsonPayload($queued)['scope'])->toBe('system')
|
|
647
|
+
->and($controller->pruned)->toBe(['system']);
|
|
648
|
+
} finally {
|
|
649
|
+
if ($hadRequest) {
|
|
650
|
+
app()->instance('request', $originalRequest);
|
|
651
|
+
} else {
|
|
652
|
+
app()->forgetInstance('request');
|
|
653
|
+
}
|
|
654
|
+
config(['queue' => $originalQueueConfig]);
|
|
655
|
+
Illuminate\Support\Carbon::setTestNow();
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
test('system cleanup does not run an entire platform inline on a synchronous queue', function () {
|
|
660
|
+
$originalQueueConfig = config('queue', []);
|
|
661
|
+
try {
|
|
662
|
+
config(['queue.default' => 'sync', 'queue.connections.sync.driver' => 'sync']);
|
|
663
|
+
$controller = new FleetOpsSettingControllerProbe();
|
|
664
|
+
expect($controller->runTelematicsRetention()->getStatusCode())->toBe(503)
|
|
665
|
+
->and($controller->pruned)->toBe([]);
|
|
666
|
+
} finally {
|
|
667
|
+
config(['queue' => $originalQueueConfig]);
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
test('telematics storage helpers query all rows and allocated table bytes from MySQL statistics', function () {
|
|
672
|
+
$connection = new class(new PDO('sqlite::memory:')) extends Illuminate\Database\SQLiteConnection {
|
|
673
|
+
public string $driver = 'sqlite';
|
|
674
|
+
public array $selects = [];
|
|
675
|
+
|
|
676
|
+
public function getDriverName()
|
|
677
|
+
{
|
|
678
|
+
return $this->driver;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
public function select($query, $bindings = [], $useReadPdo = true)
|
|
682
|
+
{
|
|
683
|
+
if (str_contains($query, 'information_schema')) {
|
|
684
|
+
$this->selects[] = [$query, $bindings];
|
|
685
|
+
|
|
686
|
+
// information_schema reports prefixed table names.
|
|
687
|
+
return [(object) ['name' => $bindings[0], 'row_count' => '500', 'data_bytes' => '16000000', 'index_bytes' => '5000'], (object) ['name' => $bindings[1], 'row_count' => 40, 'data_bytes' => 28000, 'index_bytes' => 2000]];
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
return parent::select($query, $bindings, $useReadPdo);
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
$connection->setTablePrefix('');
|
|
694
|
+
$originalDb = app()->bound('db') ? app('db') : null;
|
|
695
|
+
app()->instance('db', new class($connection) {
|
|
696
|
+
public function __construct(public $connection)
|
|
697
|
+
{
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
public function connection($name = null)
|
|
701
|
+
{
|
|
702
|
+
return $this->connection;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
public function __call($method, $arguments)
|
|
706
|
+
{
|
|
707
|
+
return $this->connection->{$method}(...$arguments);
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
Illuminate\Support\Facades\DB::clearResolvedInstance('db');
|
|
711
|
+
try {
|
|
712
|
+
$schema = $connection->getSchemaBuilder();
|
|
713
|
+
$schema->create('telematics', function ($table) {
|
|
714
|
+
$table->increments('id');
|
|
715
|
+
$table->string('uuid');
|
|
716
|
+
$table->string('company_uuid');
|
|
717
|
+
});
|
|
718
|
+
$schema->create('device_events', function ($table) {
|
|
719
|
+
$table->increments('id');
|
|
720
|
+
$table->string('company_uuid')->nullable();
|
|
721
|
+
$table->timestamp('created_at')->nullable();
|
|
722
|
+
});
|
|
723
|
+
$connection->table('telematics')->insert([['uuid' => 'tm-1', 'company_uuid' => 'company-1'], ['uuid' => 'tm-2', 'company_uuid' => 'company-2']]);
|
|
724
|
+
$connection->table('device_events')->insert([
|
|
725
|
+
['company_uuid' => 'company-1', 'created_at' => '2026-08-01 00:00:00'],
|
|
726
|
+
['company_uuid' => 'company-1', 'created_at' => '2026-09-01 00:00:00'],
|
|
727
|
+
['company_uuid' => 'company-2', 'created_at' => '2026-07-01 00:00:00'],
|
|
728
|
+
]);
|
|
729
|
+
|
|
730
|
+
$controller = new class extends SettingController {
|
|
731
|
+
public function usage(string $table, Closure $scope, string $column): array
|
|
732
|
+
{
|
|
733
|
+
return $this->tableUsage($table, $scope, $column);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
public function statistics(array $tables): array
|
|
737
|
+
{
|
|
738
|
+
return $this->storageTableStatistics($tables);
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
expect($controller->usage('device_events', fn ($query) => $query, 'created_at'))->toBe(['rows' => 3, 'oldest' => '2026-07-01 00:00:00'])
|
|
742
|
+
->and($controller->usage('device_events', fn ($query) => $query->where('company_uuid', 'none'), 'created_at'))->toBe(['rows' => 0, 'oldest' => null])
|
|
743
|
+
->and($controller->statistics(['device_events', 'positions']))->toBe([]);
|
|
744
|
+
|
|
745
|
+
$connection->driver = 'mysql';
|
|
746
|
+
$connection->setTablePrefix('fb_');
|
|
747
|
+
expect($controller->statistics(['device_events', 'positions']))->toBe([
|
|
748
|
+
'device_events' => ['rows' => 500, 'rows_estimated' => true, 'estimated_bytes' => 16005000],
|
|
749
|
+
'positions' => ['rows' => 40, 'rows_estimated' => true, 'estimated_bytes' => 30000],
|
|
750
|
+
])
|
|
751
|
+
->and($connection->selects[0][1])->toBe(['fb_device_events', 'fb_positions'])
|
|
752
|
+
->and($connection->selects[0][0])->toContain('TABLE_NAME IN (?, ?)');
|
|
753
|
+
} finally {
|
|
754
|
+
if ($originalDb) {
|
|
755
|
+
app()->instance('db', $originalDb);
|
|
756
|
+
} else {
|
|
757
|
+
app()->forgetInstance('db');
|
|
758
|
+
}
|
|
759
|
+
Illuminate\Support\Facades\DB::clearResolvedInstance('db');
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
test('admin telematics cleanup dispatches the system-wide fanout job', function () {
|
|
764
|
+
$controller = new class extends SettingController {
|
|
765
|
+
public function prune(): void
|
|
766
|
+
{
|
|
767
|
+
$this->dispatchTelematicsPrune();
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
$originalCache = Illuminate\Support\Facades\Cache::getFacadeRoot();
|
|
771
|
+
$originalCacheConfig = config('cache', []);
|
|
772
|
+
$dispatcherContract = Illuminate\Contracts\Bus\Dispatcher::class;
|
|
773
|
+
$originalDispatcher = app()->bound($dispatcherContract) ? app($dispatcherContract) : null;
|
|
774
|
+
try {
|
|
775
|
+
config(['cache.default' => 'array', 'cache.stores.array' => ['driver' => 'array']]);
|
|
776
|
+
Illuminate\Support\Facades\Cache::swap(new Illuminate\Cache\CacheManager(app()));
|
|
777
|
+
$dispatcher = new class(app()) extends Illuminate\Bus\Dispatcher {
|
|
778
|
+
public array $jobs = [];
|
|
779
|
+
public bool $fail = true;
|
|
780
|
+
|
|
781
|
+
public function dispatch($command)
|
|
782
|
+
{
|
|
783
|
+
if ($this->fail) {
|
|
784
|
+
throw new RuntimeException('Broker unavailable');
|
|
785
|
+
}
|
|
786
|
+
$this->jobs[] = $command;
|
|
787
|
+
|
|
788
|
+
return $command;
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
app()->instance($dispatcherContract, $dispatcher);
|
|
792
|
+
|
|
793
|
+
expect(fn () => $controller->prune())->toThrow(RuntimeException::class, 'Broker unavailable');
|
|
794
|
+
$dispatcher->fail = false;
|
|
795
|
+
$controller->prune();
|
|
796
|
+
$controller->prune();
|
|
797
|
+
expect($dispatcher->jobs)->toHaveCount(1)
|
|
798
|
+
->and($dispatcher->jobs[0])->toBeInstanceOf(Fleetbase\FleetOps\Jobs\DispatchTelematicsRetentionJobs::class)
|
|
799
|
+
->and($dispatcher->jobs[0]->afterCompanyId)->toBe(0);
|
|
800
|
+
} finally {
|
|
801
|
+
Illuminate\Support\Facades\Cache::swap($originalCache);
|
|
802
|
+
config(['cache' => $originalCacheConfig]);
|
|
803
|
+
if ($originalDispatcher) {
|
|
804
|
+
app()->instance($dispatcherContract, $originalDispatcher);
|
|
805
|
+
} else {
|
|
806
|
+
app()->forgetInstance($dispatcherContract);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
test('customer history save reports a failed persistence operation', function () {
|
|
812
|
+
$controller = new class extends FleetOpsSettingControllerProbe {
|
|
813
|
+
protected function configureCompanySetting(string $key, mixed $value): mixed
|
|
814
|
+
{
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
$controller->company = (object) ['uuid' => 'company-a'];
|
|
819
|
+
$response = $controller->saveTelematicsSettings(Request::create('/', 'POST', ['event_retention_days' => 0]));
|
|
820
|
+
expect($response->getStatusCode())->toBe(500);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
test('optional payload counts omit timeouts but surface other database errors', function () {
|
|
824
|
+
$hadRequest = app()->bound('request');
|
|
825
|
+
$original = $hadRequest ? app('request') : null;
|
|
826
|
+
app()->instance('request', Request::create('/', 'GET', ['include_payload_counts' => true]));
|
|
827
|
+
try {
|
|
828
|
+
$controller = new class extends FleetOpsSettingControllerProbe {
|
|
829
|
+
public int $errorCode = 3024;
|
|
830
|
+
|
|
831
|
+
protected function tableUsage(string $table, Closure $scope, string $ageColumn): array
|
|
832
|
+
{
|
|
833
|
+
return ['rows' => 5, 'oldest' => null];
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
protected function tableCount(string $table, Closure $scope): int
|
|
837
|
+
{
|
|
838
|
+
$error = new PDOException('Query failed');
|
|
839
|
+
$error->errorInfo = ['HY000', $this->errorCode, 'Query failed'];
|
|
840
|
+
throw new Illuminate\Database\QueryException('mysql', 'select payload', [], $error);
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
$data = $controller->getTelematicsStorageUsage()->getData(true);
|
|
844
|
+
expect($data['scope'])->toBe('system')->and($data['tables']['device_events'])->toBe(['rows' => 5, 'oldest' => null]);
|
|
845
|
+
$controller->errorCode = 1146;
|
|
846
|
+
expect(fn () => $controller->getTelematicsStorageUsage())->toThrow(Illuminate\Database\QueryException::class);
|
|
847
|
+
} finally {
|
|
848
|
+
if ($hadRequest) {
|
|
849
|
+
app()->instance('request', $original);
|
|
850
|
+
} else {
|
|
851
|
+
app()->forgetInstance('request');
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
});
|
|
@@ -383,11 +383,14 @@ test('sensor and service area controllers export selections and eager load query
|
|
|
383
383
|
});
|
|
384
384
|
|
|
385
385
|
test('service area zone and getting started controllers expose small lifecycle contracts', function () {
|
|
386
|
-
$serviceArea
|
|
387
|
-
|
|
388
|
-
'
|
|
389
|
-
])
|
|
390
|
-
(new ServiceAreaController())->afterSave(
|
|
386
|
+
$serviceArea = new FleetOpsControllerCustomFieldModelFake();
|
|
387
|
+
$serviceAreaRequest = new Request([
|
|
388
|
+
'serviceArea' => ['custom_field_values' => [['key' => 'area_code', 'value' => 'raw-request-value']]],
|
|
389
|
+
]);
|
|
390
|
+
(new ServiceAreaController())->afterSave($serviceAreaRequest, $serviceArea, [
|
|
391
|
+
'custom_field_values' => [['key' => 'area_code', 'value' => 'A1']],
|
|
392
|
+
]);
|
|
393
|
+
(new ServiceAreaController())->afterSave($serviceAreaRequest, $serviceArea, ['custom_field_values' => []]);
|
|
391
394
|
|
|
392
395
|
$zone = new FleetOpsControllerZoneFake();
|
|
393
396
|
(new ZoneController())->afterSave(new Request([
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Fleetbase\FleetOps\Console\Commands\PruneTelematicsData;
|
|
4
|
+
use Fleetbase\FleetOps\Support\Telematics\Retention\RetentionPolicy;
|
|
5
|
+
use Illuminate\Support\Collection;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Drives fleetops:prune-telematics-data without the console kernel: options,
|
|
9
|
+
* output and the settings-backed policy are all supplied by the test.
|
|
10
|
+
*/
|
|
11
|
+
class PruneTelematicsDataProbe extends PruneTelematicsData
|
|
12
|
+
{
|
|
13
|
+
public array $messages = [];
|
|
14
|
+
public array $options = ['company' => null, 'table' => [], 'batch-size' => 1000, 'max-batches' => 50, 'dry-run' => false, 'no-lock' => true];
|
|
15
|
+
|
|
16
|
+
/** company uuid ('' for the system defaults) => policy values passed to RetentionPolicy::fromArray */
|
|
17
|
+
public array $policies = [];
|
|
18
|
+
|
|
19
|
+
/** When set, replaces the companies table lookup. */
|
|
20
|
+
public ?Collection $companyRows = null;
|
|
21
|
+
|
|
22
|
+
public function option($key = null, $default = null)
|
|
23
|
+
{
|
|
24
|
+
return $this->options[$key] ?? $default;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public function info($string, $verbosity = null)
|
|
28
|
+
{
|
|
29
|
+
$this->messages[] = ['info', $string];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public function warn($string, $verbosity = null)
|
|
33
|
+
{
|
|
34
|
+
$this->messages[] = ['warn', $string];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public function error($string, $verbosity = null)
|
|
38
|
+
{
|
|
39
|
+
$this->messages[] = ['error', $string];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected function policyFor(?string $companyUuid): RetentionPolicy
|
|
43
|
+
{
|
|
44
|
+
return RetentionPolicy::fromArray($this->policies[$companyUuid ?? ''] ?? $this->policies[''] ?? []);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected function companies(?string $only): Collection
|
|
48
|
+
{
|
|
49
|
+
if ($this->companyRows === null) {
|
|
50
|
+
return parent::companies($only);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return $this->companyRows->filter(fn ($company) => !$only || $company->uuid === $only || $company->public_id === $only)->values();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -125,9 +125,9 @@ class FleetOpsProcessOperationalAlertsCommandProbe extends ProcessOperationalAle
|
|
|
125
125
|
return $this->query;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
protected function alertSettings(): array
|
|
128
|
+
protected function alertSettings(Order $order): array
|
|
129
129
|
{
|
|
130
|
-
$this->settingsCalls[] =
|
|
130
|
+
$this->settingsCalls[] = $order->company_uuid;
|
|
131
131
|
|
|
132
132
|
return [
|
|
133
133
|
'late_departures' => ['enabled' => true],
|