@fleetbase/fleetops-engine 0.6.52 → 0.6.54
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/addon/components/device/details.hbs +106 -135
- package/addon/components/device/details.js +174 -1
- package/addon/components/device/panel-header.hbs +2 -2
- package/addon/components/layout/fleet-ops-sidebar/operations-monitor.hbs +201 -0
- package/addon/components/layout/fleet-ops-sidebar/operations-monitor.js +654 -0
- package/addon/components/layout/fleet-ops-sidebar.hbs +19 -233
- package/addon/components/layout/fleet-ops-sidebar.js +364 -395
- package/addon/components/maintenance-schedule/form.js +1 -0
- package/addon/components/order/form/details.hbs +1 -43
- package/addon/components/order/form/details.js +18 -53
- package/addon/components/order/form/orchestrator-constraints.hbs +40 -0
- package/addon/components/order/form/orchestrator-constraints.js +61 -0
- package/addon/components/order/form/payload.js +50 -2
- package/addon/components/order/form/route.js +15 -0
- package/addon/components/order/form/service-rate.hbs +62 -11
- package/addon/components/order/form/service-rate.js +124 -2
- package/addon/components/order/form.hbs +2 -0
- package/addon/components/telematic/details.hbs +7 -1
- package/addon/components/telematic/form.js +16 -8
- package/addon/components/work-order/details.hbs +5 -1
- package/addon/components/work-order/form.hbs +26 -4
- package/addon/components/work-order/form.js +5 -1
- package/addon/controllers/connectivity/devices/index/details/events.js +244 -1
- package/addon/controllers/connectivity/devices/index/details/sensors.js +199 -0
- package/addon/controllers/connectivity/devices/index/details/vehicle.js +74 -0
- package/addon/controllers/connectivity/devices/index/details.js +12 -0
- package/addon/controllers/connectivity/devices/index.js +4 -1
- package/addon/controllers/connectivity/telematics/details/attachments.js +220 -54
- package/addon/controllers/connectivity/telematics/details/devices.js +90 -41
- package/addon/controllers/connectivity/telematics/details/events.js +39 -11
- package/addon/controllers/connectivity/telematics/details/sensors.js +39 -19
- package/addon/controllers/maintenance/index.js +100 -0
- package/addon/controllers/maintenance/work-orders/index.js +18 -1
- package/addon/controllers/management/fuel-transactions/index.js +1 -1
- package/addon/controllers/management/index.js +88 -0
- package/addon/controllers/settings/index.js +142 -0
- package/addon/extension.js +21 -0
- package/addon/routes/analytics/index.js +3 -0
- package/addon/routes/connectivity/devices/index/details/events.js +37 -1
- package/addon/routes/connectivity/devices/index/details/sensors.js +35 -0
- package/addon/routes/connectivity/devices/index/details/vehicle.js +7 -0
- package/addon/routes/connectivity/devices/index.js +3 -0
- package/addon/routes/connectivity/telematics/details/attachments.js +72 -9
- package/addon/routes/connectivity/telematics/details/devices.js +4 -0
- package/addon/routes/connectivity/telematics/details/sensors.js +1 -0
- package/addon/routes/connectivity/telematics/details.js +1 -1
- package/addon/routes/connectivity/telematics/edit.js +1 -1
- package/addon/routes/maintenance/index.js +3 -0
- package/addon/routes/maintenance/work-orders/index.js +3 -0
- package/addon/routes/settings/index.js +3 -0
- package/addon/routes.js +2 -0
- package/addon/services/order-creation.js +54 -1
- package/addon/services/order-validation.js +3 -2
- package/addon/services/service-rate-actions.js +1 -0
- package/addon/styles/fleetops-engine.css +1821 -6
- package/addon/templates/analytics/index.hbs +14 -0
- package/addon/templates/analytics/reports/index.hbs +4 -4
- package/addon/templates/connectivity/devices/index/details/events.hbs +85 -1
- package/addon/templates/connectivity/devices/index/details/sensors.hbs +73 -0
- package/addon/templates/connectivity/devices/index/details/vehicle.hbs +84 -0
- package/addon/templates/connectivity/telematics/details/attachments.hbs +198 -121
- package/addon/templates/connectivity/telematics/details/devices.hbs +4 -1
- package/addon/templates/connectivity/telematics/details/events.hbs +4 -1
- package/addon/templates/connectivity/telematics/details/sensors.hbs +3 -0
- package/addon/templates/maintenance/index.hbs +113 -0
- package/addon/templates/management/fuel-transactions/index/details.hbs +1 -1
- package/addon/templates/management/index.hbs +109 -73
- package/addon/templates/management/vehicles/index/edit.hbs +1 -1
- package/addon/templates/operations/orders/index.hbs +1 -1
- package/addon/templates/operations/service-rates/index.hbs +1 -1
- package/addon/templates/settings/index.hbs +83 -0
- package/addon/templates/settings/routing.hbs +2 -2
- package/addon/utils/fleet-ops-options.js +38 -0
- package/app/components/order/form/orchestrator-constraints.js +1 -0
- package/app/controllers/connectivity/devices/index/details/sensors.js +1 -0
- package/app/controllers/connectivity/devices/index/details/vehicle.js +1 -0
- package/app/controllers/maintenance/index.js +1 -0
- package/app/controllers/management/index.js +1 -0
- package/app/controllers/settings/index.js +1 -0
- package/app/routes/analytics/index.js +1 -0
- package/app/routes/connectivity/devices/index/details/sensors.js +1 -0
- package/app/routes/connectivity/devices/index/details/vehicle.js +1 -0
- package/app/routes/maintenance/index.js +1 -0
- package/app/routes/settings/index.js +1 -0
- package/app/templates/analytics/index.js +1 -0
- package/app/templates/connectivity/devices/index/details/sensors.js +1 -0
- package/app/templates/connectivity/devices/index/details/vehicle.js +1 -0
- package/app/templates/maintenance/index.js +1 -0
- package/app/templates/management/index.js +1 -0
- package/app/templates/settings/index.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/server/migrations/2025_08_28_054930_create_work_orders_table.php +1 -0
- package/server/migrations/2026_06_15_000001_normalize_device_attachable_vehicle_morph_types.php +30 -0
- package/server/migrations/2026_06_18_000001_add_category_to_work_orders_table.php +32 -0
- package/server/seeders/Testing/MaintenanceSeeder.php +5 -4
- package/server/src/Console/Commands/FixInvalidPolymorphicRelationTypeNamespaces.php +1 -0
- package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +1 -0
- package/server/src/Console/Commands/SyncTelematics.php +89 -0
- package/server/src/Exceptions/TelematicProviderException.php +13 -2
- package/server/src/Http/Controllers/Api/v1/OrderController.php +6 -21
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +112 -11
- package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +16 -5
- package/server/src/Http/Controllers/Internal/v1/FleetController.php +9 -0
- package/server/src/Http/Controllers/Internal/v1/HubController.php +300 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +177 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +1 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +4 -32
- package/server/src/Http/Controllers/Internal/v1/SearchController.php +506 -0
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +4 -4
- package/server/src/Http/Controllers/Internal/v1/TelematicController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +94 -10
- package/server/src/Http/Filter/DeviceEventFilter.php +44 -0
- package/server/src/Http/Filter/DeviceFilter.php +131 -0
- package/server/src/Http/Filter/DriverFilter.php +6 -0
- package/server/src/Http/Filter/SensorFilter.php +125 -0
- package/server/src/Http/Filter/VehicleFilter.php +19 -0
- package/server/src/Http/Requests/CreateDriverRequest.php +14 -13
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -0
- package/server/src/Http/Resources/v1/Driver.php +11 -2
- package/server/src/Http/Resources/v1/WorkOrder.php +1 -0
- package/server/src/Jobs/FinalizeApiOrderCreation.php +47 -0
- package/server/src/Jobs/FinalizeInternalOrderCreation.php +35 -0
- package/server/src/Jobs/NotifyBulkAssignedDriver.php +47 -0
- package/server/src/Jobs/SyncTelematicDevicesJob.php +181 -66
- package/server/src/Models/Device.php +15 -1
- package/server/src/Models/DeviceEvent.php +1 -1
- package/server/src/Models/Driver.php +17 -1
- package/server/src/Models/Sensor.php +15 -2
- package/server/src/Models/Telematic.php +1 -1
- package/server/src/Models/Vehicle.php +5 -1
- package/server/src/Models/WorkOrder.php +5 -2
- package/server/src/Observers/DriverObserver.php +3 -3
- package/server/src/Observers/FleetObserver.php +23 -0
- package/server/src/Observers/VehicleObserver.php +3 -3
- package/server/src/Providers/FleetOpsServiceProvider.php +12 -0
- package/server/src/Support/LiveCacheService.php +1 -1
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +188 -28
- package/server/src/Support/Telematics/Providers/SafeeProvider.php +44 -3
- package/server/src/Support/Telematics/TelematicService.php +330 -30
- package/server/src/routes.php +10 -0
- package/server/tests/DeviceFilterTest.php +36 -0
- package/server/tests/HubRoutesTest.php +62 -0
- package/server/tests/OperationsMonitorEndpointTest.php +38 -0
- package/server/tests/OrderQueuedClosureRegressionTest.php +36 -0
- package/server/tests/TelematicsHardeningTest.php +455 -3
- package/server/tests/VehicleDriverAssignmentTest.php +27 -0
- package/translations/ar-ae.yml +1 -1
- package/translations/en-us.yaml +1 -0
- package/translations/vi-vn.yaml +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Jobs;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Driver;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
7
|
+
use Illuminate\Bus\Queueable;
|
|
8
|
+
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
9
|
+
use Illuminate\Foundation\Bus\Dispatchable;
|
|
10
|
+
use Illuminate\Queue\InteractsWithQueue;
|
|
11
|
+
use Illuminate\Queue\SerializesModels;
|
|
12
|
+
|
|
13
|
+
class NotifyBulkAssignedDriver implements ShouldQueue
|
|
14
|
+
{
|
|
15
|
+
use Dispatchable;
|
|
16
|
+
use InteractsWithQueue;
|
|
17
|
+
use Queueable;
|
|
18
|
+
use SerializesModels;
|
|
19
|
+
|
|
20
|
+
public function __construct(public array $orderUuids, public string $driverUuid)
|
|
21
|
+
{
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public function handle(): void
|
|
25
|
+
{
|
|
26
|
+
$driver = Driver::where('uuid', $this->driverUuid)->first();
|
|
27
|
+
if (!$driver) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
Order::whereIn('uuid', $this->orderUuids)
|
|
32
|
+
->cursor()
|
|
33
|
+
->each(function (Order $order) use ($driver): void {
|
|
34
|
+
$order->setRelation('driverAssigned', $driver);
|
|
35
|
+
$order->driver_assigned_uuid = $driver->uuid;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
$order->notifyDriverAssigned();
|
|
39
|
+
} catch (\Throwable $e) {
|
|
40
|
+
logger()->warning(
|
|
41
|
+
'Failed notifying driver on order ' . $order->uuid,
|
|
42
|
+
['error' => $e->getMessage()]
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
|
|
10
10
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
11
11
|
use Illuminate\Queue\InteractsWithQueue;
|
|
12
12
|
use Illuminate\Queue\SerializesModels;
|
|
13
|
+
use Illuminate\Support\Facades\Cache;
|
|
13
14
|
use Illuminate\Support\Facades\Log;
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -27,7 +28,7 @@ class SyncTelematicDevicesJob implements ShouldQueue
|
|
|
27
28
|
public Telematic $telematic;
|
|
28
29
|
public array $options;
|
|
29
30
|
public string $jobId;
|
|
30
|
-
public int $tries =
|
|
31
|
+
public int $tries = 1;
|
|
31
32
|
public int $timeout = 300;
|
|
32
33
|
|
|
33
34
|
/**
|
|
@@ -46,83 +47,186 @@ class SyncTelematicDevicesJob implements ShouldQueue
|
|
|
46
47
|
public function handle(TelematicProviderRegistry $registry, TelematicService $service): void
|
|
47
48
|
{
|
|
48
49
|
$correlationId = \Illuminate\Support\Str::uuid()->toString();
|
|
50
|
+
$lockKey = 'fleetops:sync-telematic-devices:' . $this->telematic->uuid;
|
|
51
|
+
$lock = Cache::lock($lockKey, $this->timeout + 60);
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
'
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
if (!$lock->get()) {
|
|
54
|
+
Log::info('Device discovery skipped because another sync is already running', [
|
|
55
|
+
'correlation_id' => $correlationId,
|
|
56
|
+
'telematic_uuid' => $this->telematic->uuid,
|
|
57
|
+
'provider' => $this->telematic->provider,
|
|
58
|
+
'lock_key' => $lockKey,
|
|
59
|
+
]);
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
$this->telematic->meta = array_merge($this->telematic->meta ?? [], [
|
|
62
|
+
'last_sync_job_id' => $this->jobId,
|
|
63
|
+
'last_sync_result' => 'skipped',
|
|
64
|
+
'last_sync_skipped_reason' => 'sync_already_running',
|
|
65
|
+
'last_sync_skipped_at' => now()->toDateTimeString(),
|
|
66
|
+
]);
|
|
67
|
+
$this->telematic->save();
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
62
71
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
try {
|
|
73
|
+
Log::info('Device discovery started', [
|
|
74
|
+
'correlation_id' => $correlationId,
|
|
75
|
+
'telematic_uuid' => $this->telematic->uuid,
|
|
76
|
+
'provider' => $this->telematic->provider,
|
|
77
|
+
]);
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
$cursor = null;
|
|
80
|
+
$totalFetched = 0;
|
|
81
|
+
$totalLinked = 0;
|
|
82
|
+
$totalEvents = 0;
|
|
83
|
+
$totalSensors = 0;
|
|
84
|
+
$totalSkipped = 0;
|
|
85
|
+
$pageCount = 0;
|
|
86
|
+
$lastProviderAllCount = null;
|
|
87
|
+
$lastProviderFiltersCount = null;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
$provider = $registry->resolve($this->telematic->provider);
|
|
91
|
+
$provider->connect($this->telematic);
|
|
92
|
+
|
|
93
|
+
do {
|
|
94
|
+
$response = $provider->fetchDevices([
|
|
95
|
+
'limit' => $this->options['limit'] ?? null,
|
|
96
|
+
'cursor' => $cursor,
|
|
97
|
+
'filters' => $this->options['filters'] ?? [],
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
$devices = $response['devices'] ?? [];
|
|
101
|
+
$pagination = $response['pagination'] ?? [];
|
|
102
|
+
$pageCount++;
|
|
103
|
+
$totalFetched += count($devices);
|
|
104
|
+
|
|
105
|
+
if (isset($pagination['allCount'])) {
|
|
106
|
+
$lastProviderAllCount = $pagination['allCount'];
|
|
80
107
|
}
|
|
81
|
-
}
|
|
82
108
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
'correlation_id' => $correlationId,
|
|
87
|
-
'synced' => $totalSynced,
|
|
88
|
-
'has_more' => $response['has_more'],
|
|
89
|
-
]);
|
|
109
|
+
if (isset($pagination['filtersCount'])) {
|
|
110
|
+
$lastProviderFiltersCount = $pagination['filtersCount'];
|
|
111
|
+
}
|
|
90
112
|
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
Log::info('Device discovery page fetched', [
|
|
114
|
+
'correlation_id' => $correlationId,
|
|
115
|
+
'telematic_uuid' => $this->telematic->uuid,
|
|
116
|
+
'provider' => $this->telematic->provider,
|
|
117
|
+
'page' => $pageCount,
|
|
118
|
+
'cursor' => $cursor,
|
|
119
|
+
'limit' => $pagination['limit'] ?? ($this->options['limit'] ?? null),
|
|
120
|
+
'result_count' => count($devices),
|
|
121
|
+
'all_count' => $lastProviderAllCount,
|
|
122
|
+
'filters_count' => $lastProviderFiltersCount,
|
|
123
|
+
'next_cursor' => $response['next_cursor'] ?? null,
|
|
124
|
+
'has_more' => $response['has_more'] ?? false,
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
foreach ($devices as $devicePayload) {
|
|
128
|
+
$normalizedDevice = $provider->normalizeDevice($devicePayload);
|
|
129
|
+
try {
|
|
130
|
+
$result = $service->ingestDeviceSnapshot($this->telematic, $provider, $devicePayload);
|
|
131
|
+
$totalLinked++;
|
|
132
|
+
if ($result['event']) {
|
|
133
|
+
$totalEvents++;
|
|
134
|
+
}
|
|
135
|
+
$totalSensors += $result['sensors'] ?? 0;
|
|
136
|
+
} catch (\Illuminate\Validation\ValidationException $e) {
|
|
137
|
+
$totalSkipped++;
|
|
138
|
+
Log::warning('Skipping telematics device without provider identity', [
|
|
139
|
+
'correlation_id' => $correlationId,
|
|
140
|
+
'provider' => $this->telematic->provider,
|
|
141
|
+
'provider_unit_id' => $devicePayload['_id'] ?? $devicePayload['id'] ?? null,
|
|
142
|
+
'device_id' => $normalizedDevice['device_id'] ?? null,
|
|
143
|
+
'name' => $normalizedDevice['name'] ?? $devicePayload['name'] ?? null,
|
|
144
|
+
'imei' => $devicePayload['imei'] ?? null,
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
93
148
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
149
|
+
$cursor = $response['next_cursor'] ?? null;
|
|
150
|
+
|
|
151
|
+
Log::info('Device discovery progress', [
|
|
152
|
+
'correlation_id' => $correlationId,
|
|
153
|
+
'fetched' => $totalFetched,
|
|
154
|
+
'linked' => $totalLinked,
|
|
155
|
+
'events' => $totalEvents,
|
|
156
|
+
'sensors' => $totalSensors,
|
|
157
|
+
'skipped' => $totalSkipped,
|
|
158
|
+
'has_more' => $response['has_more'] ?? false,
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
// Broadcast progress (TODO: implement WebSocket broadcasting)
|
|
162
|
+
} while (($response['has_more'] ?? false) && $cursor);
|
|
163
|
+
|
|
164
|
+
Log::info('Device discovery completed', [
|
|
165
|
+
'correlation_id' => $correlationId,
|
|
166
|
+
'total_fetched' => $totalFetched,
|
|
167
|
+
'total_linked' => $totalLinked,
|
|
168
|
+
'total_events' => $totalEvents,
|
|
169
|
+
'total_sensors' => $totalSensors,
|
|
170
|
+
'total_skipped' => $totalSkipped,
|
|
171
|
+
'page_count' => $pageCount,
|
|
172
|
+
'provider_all_count' => $lastProviderAllCount,
|
|
173
|
+
'provider_filter_count' => $lastProviderFiltersCount,
|
|
174
|
+
]);
|
|
98
175
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
176
|
+
$this->telematic->status = 'active';
|
|
177
|
+
$this->telematic->meta = array_merge($this->telematic->meta ?? [], [
|
|
178
|
+
'last_sync_job_id' => $this->jobId,
|
|
179
|
+
'last_sync_completed_at' => now()->toDateTimeString(),
|
|
180
|
+
'last_sync_result' => 'success',
|
|
181
|
+
'last_sync_total' => $totalLinked,
|
|
182
|
+
'last_sync_fetched_total' => $totalFetched,
|
|
183
|
+
'last_sync_linked_total' => $totalLinked,
|
|
184
|
+
'last_sync_events_total' => $totalEvents,
|
|
185
|
+
'last_sync_sensors_total' => $totalSensors,
|
|
186
|
+
'last_sync_skipped_total' => $totalSkipped,
|
|
187
|
+
'last_sync_page_count' => $pageCount,
|
|
188
|
+
'last_sync_provider_total' => $lastProviderFiltersCount ?? $lastProviderAllCount,
|
|
189
|
+
'last_sync_provider_all_count' => $lastProviderAllCount,
|
|
190
|
+
'last_sync_provider_filters_count' => $lastProviderFiltersCount,
|
|
191
|
+
'last_sync_error' => null,
|
|
192
|
+
'last_sync_error_context' => null,
|
|
193
|
+
]);
|
|
194
|
+
$this->telematic->save();
|
|
195
|
+
} catch (\Exception $e) {
|
|
196
|
+
$failureContext = method_exists($e, 'context') ? $e->context() : [];
|
|
197
|
+
$failureMessage = $this->safeSyncErrorMessage($e);
|
|
198
|
+
|
|
199
|
+
Log::error('Device discovery failed', [
|
|
200
|
+
'correlation_id' => $correlationId,
|
|
201
|
+
'error' => $failureMessage,
|
|
202
|
+
'exception' => get_class($e),
|
|
203
|
+
'provider_context' => $failureContext,
|
|
204
|
+
]);
|
|
114
205
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
206
|
+
$this->telematic->status = 'error';
|
|
207
|
+
$this->telematic->meta = array_merge($this->telematic->meta ?? [], [
|
|
208
|
+
'last_sync_job_id' => $this->jobId,
|
|
209
|
+
'last_sync_result' => 'failed',
|
|
210
|
+
'last_sync_fetched_total' => $totalFetched,
|
|
211
|
+
'last_sync_linked_total' => $totalLinked,
|
|
212
|
+
'last_sync_events_total' => $totalEvents,
|
|
213
|
+
'last_sync_sensors_total' => $totalSensors,
|
|
214
|
+
'last_sync_skipped_total' => $totalSkipped,
|
|
215
|
+
'last_sync_page_count' => $pageCount,
|
|
216
|
+
'last_sync_provider_total' => $lastProviderFiltersCount ?? $lastProviderAllCount,
|
|
217
|
+
'last_sync_provider_all_count' => $lastProviderAllCount,
|
|
218
|
+
'last_sync_provider_filters_count' => $lastProviderFiltersCount,
|
|
219
|
+
'last_sync_error' => $failureMessage,
|
|
220
|
+
'last_sync_error_type' => class_basename($e),
|
|
221
|
+
'last_sync_error_context' => $failureContext,
|
|
222
|
+
'last_sync_failed_at' => now()->toDateTimeString(),
|
|
223
|
+
]);
|
|
224
|
+
$this->telematic->save();
|
|
124
225
|
|
|
125
|
-
|
|
226
|
+
throw $e;
|
|
227
|
+
}
|
|
228
|
+
} finally {
|
|
229
|
+
$lock->release();
|
|
126
230
|
}
|
|
127
231
|
}
|
|
128
232
|
|
|
@@ -133,4 +237,15 @@ class SyncTelematicDevicesJob implements ShouldQueue
|
|
|
133
237
|
{
|
|
134
238
|
return $this->jobId;
|
|
135
239
|
}
|
|
240
|
+
|
|
241
|
+
protected function safeSyncErrorMessage(\Throwable $e): string
|
|
242
|
+
{
|
|
243
|
+
$message = $e->getMessage();
|
|
244
|
+
|
|
245
|
+
if (!$message || preg_match('/(token=|password|client_secret|authorization|bearer\s+[a-z0-9._-]+)/i', $message)) {
|
|
246
|
+
return 'Device sync failed. Review the provider connection and safe sync metadata, then try again.';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return $message;
|
|
250
|
+
}
|
|
136
251
|
}
|
|
@@ -74,7 +74,21 @@ class Device extends Model
|
|
|
74
74
|
*
|
|
75
75
|
* @var array
|
|
76
76
|
*/
|
|
77
|
-
protected $filterParams = [
|
|
77
|
+
protected $filterParams = [
|
|
78
|
+
'status',
|
|
79
|
+
'attachment_state',
|
|
80
|
+
'vehicle',
|
|
81
|
+
'connection_status',
|
|
82
|
+
'device_id',
|
|
83
|
+
'last_online_at',
|
|
84
|
+
'updated_at',
|
|
85
|
+
'warranty_uuid',
|
|
86
|
+
'attachable_type',
|
|
87
|
+
'attachable_uuid',
|
|
88
|
+
'telematic',
|
|
89
|
+
'telematic_uuid',
|
|
90
|
+
'provider',
|
|
91
|
+
];
|
|
78
92
|
|
|
79
93
|
/**
|
|
80
94
|
* The attributes that are mass assignable.
|
|
@@ -65,7 +65,7 @@ class DeviceEvent extends Model
|
|
|
65
65
|
*
|
|
66
66
|
* @var array
|
|
67
67
|
*/
|
|
68
|
-
protected $filterParams = ['event_type', 'severity', 'device_uuid', 'provider', 'code'];
|
|
68
|
+
protected $filterParams = ['event_type', 'severity', 'device_uuid', 'provider', 'code', 'processed', 'occurred_at', 'created_at', 'updated_at', 'telematic'];
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* The attributes that are mass assignable.
|
|
@@ -32,6 +32,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
|
|
|
32
32
|
use Illuminate\Database\Eloquent\Relations\MorphOne;
|
|
33
33
|
use Illuminate\Notifications\Notifiable;
|
|
34
34
|
use Illuminate\Support\Arr;
|
|
35
|
+
use Illuminate\Support\Carbon;
|
|
35
36
|
use Illuminate\Support\Collection;
|
|
36
37
|
use Illuminate\Support\Facades\DB;
|
|
37
38
|
use Illuminate\Support\Str;
|
|
@@ -196,7 +197,7 @@ class Driver extends Model
|
|
|
196
197
|
*
|
|
197
198
|
* @var array
|
|
198
199
|
*/
|
|
199
|
-
protected $filterParams = ['vendor', 'facilitator', 'customer', 'fleet', 'photo_uuid', 'avatar_uuid', 'avatar_value'];
|
|
200
|
+
protected $filterParams = ['vendor', 'facilitator', 'customer', 'fleet', 'vehicle', 'photo_uuid', 'avatar_uuid', 'avatar_value'];
|
|
200
201
|
|
|
201
202
|
/**
|
|
202
203
|
* The session-agnostic columns for the model.
|
|
@@ -235,6 +236,21 @@ class Driver extends Model
|
|
|
235
236
|
static::addGlobalScope(new DriverScope());
|
|
236
237
|
}
|
|
237
238
|
|
|
239
|
+
public function setLicenseExpiryAttribute($value): void
|
|
240
|
+
{
|
|
241
|
+
if (empty($value)) {
|
|
242
|
+
if ($this->exists && !empty($this->getOriginal('license_expiry'))) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
$this->attributes['license_expiry'] = null;
|
|
247
|
+
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
$this->attributes['license_expiry'] = Carbon::parse($value)->toDateString();
|
|
252
|
+
}
|
|
253
|
+
|
|
238
254
|
/**
|
|
239
255
|
* @return BelongsTo
|
|
240
256
|
*/
|
|
@@ -66,14 +66,27 @@ class Sensor extends Model
|
|
|
66
66
|
*
|
|
67
67
|
* @var array
|
|
68
68
|
*/
|
|
69
|
-
protected $searchableColumns = ['name', 'type', 'internal_id', 'unit', 'public_id'];
|
|
69
|
+
protected $searchableColumns = ['name', 'type', 'internal_id', 'serial_number', 'imei', 'unit', 'public_id'];
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* The attributes that can be used for filtering.
|
|
73
73
|
*
|
|
74
74
|
* @var array
|
|
75
75
|
*/
|
|
76
|
-
protected $filterParams = [
|
|
76
|
+
protected $filterParams = [
|
|
77
|
+
'type',
|
|
78
|
+
'sensor_type',
|
|
79
|
+
'status',
|
|
80
|
+
'device_uuid',
|
|
81
|
+
'serial_number',
|
|
82
|
+
'imei',
|
|
83
|
+
'warranty_uuid',
|
|
84
|
+
'sensorable_type',
|
|
85
|
+
'telematic_uuid',
|
|
86
|
+
'last_reading_at',
|
|
87
|
+
'created_at',
|
|
88
|
+
'updated_at',
|
|
89
|
+
];
|
|
77
90
|
|
|
78
91
|
/**
|
|
79
92
|
* The attributes that are mass assignable.
|
|
@@ -63,7 +63,7 @@ class Telematic extends Model
|
|
|
63
63
|
*
|
|
64
64
|
* @var array
|
|
65
65
|
*/
|
|
66
|
-
protected $filterParams = ['provider', 'status', 'warranty_uuid'];
|
|
66
|
+
protected $filterParams = ['public_id', 'provider', 'status', 'warranty_uuid'];
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* The attributes that are mass assignable.
|
|
@@ -72,7 +72,7 @@ class Vehicle extends Model
|
|
|
72
72
|
*
|
|
73
73
|
* @var array
|
|
74
74
|
*/
|
|
75
|
-
protected $filterParams = ['vendor', 'driver', 'driver_uuid', 'vehicle_make', 'vehicle_model'];
|
|
75
|
+
protected $filterParams = ['vendor', 'driver', 'driver_uuid', 'vehicle_make', 'vehicle_model', 'telematic_uuid'];
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
78
|
* Relationships to auto load with driver.
|
|
@@ -407,6 +407,10 @@ class Vehicle extends Model
|
|
|
407
407
|
*/
|
|
408
408
|
public function getDisplayNameAttribute()
|
|
409
409
|
{
|
|
410
|
+
if (isset($this->name) && strlen($this->name)) {
|
|
411
|
+
return $this->name;
|
|
412
|
+
}
|
|
413
|
+
|
|
410
414
|
// Initialize an empty array to hold the name segments
|
|
411
415
|
$nameSegments = [];
|
|
412
416
|
|
|
@@ -59,14 +59,14 @@ class WorkOrder extends Model
|
|
|
59
59
|
*
|
|
60
60
|
* @var array
|
|
61
61
|
*/
|
|
62
|
-
protected $searchableColumns = ['code', 'subject', 'instructions', 'public_id'];
|
|
62
|
+
protected $searchableColumns = ['code', 'subject', 'category', 'instructions', 'public_id'];
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* The attributes that can be used for filtering.
|
|
66
66
|
*
|
|
67
67
|
* @var array
|
|
68
68
|
*/
|
|
69
|
-
protected $filterParams = ['status', 'priority', 'target_type', 'assignee_type'];
|
|
69
|
+
protected $filterParams = ['category', 'status', 'priority', 'target_type', 'assignee_type'];
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* The attributes that are mass assignable.
|
|
@@ -77,6 +77,7 @@ class WorkOrder extends Model
|
|
|
77
77
|
'company_uuid',
|
|
78
78
|
'code',
|
|
79
79
|
'subject',
|
|
80
|
+
'category',
|
|
80
81
|
'status',
|
|
81
82
|
'priority',
|
|
82
83
|
'target_type',
|
|
@@ -575,6 +576,7 @@ class WorkOrder extends Model
|
|
|
575
576
|
$row = array_filter($row);
|
|
576
577
|
|
|
577
578
|
$subject = Utils::or($row, ['subject', 'title', 'name']);
|
|
579
|
+
$category = Utils::or($row, ['category', 'work_order_category', 'work_type', 'type']);
|
|
578
580
|
$status = Utils::or($row, ['status'], 'open');
|
|
579
581
|
$priority = Utils::or($row, ['priority'], 'normal');
|
|
580
582
|
$instructions = Utils::or($row, ['instructions', 'description', 'notes']);
|
|
@@ -592,6 +594,7 @@ class WorkOrder extends Model
|
|
|
592
594
|
$workOrder = new static([
|
|
593
595
|
'company_uuid' => session('company'),
|
|
594
596
|
'subject' => $subject,
|
|
597
|
+
'category' => $category,
|
|
595
598
|
'status' => $status,
|
|
596
599
|
'priority' => $priority,
|
|
597
600
|
'instructions' => $instructions,
|
|
@@ -30,7 +30,7 @@ class DriverObserver
|
|
|
30
30
|
*/
|
|
31
31
|
public function created(Driver $driver)
|
|
32
32
|
{
|
|
33
|
-
LiveCacheService::
|
|
33
|
+
LiveCacheService::invalidateMultiple(['drivers', 'operations-monitor']);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/**
|
|
@@ -40,7 +40,7 @@ class DriverObserver
|
|
|
40
40
|
*/
|
|
41
41
|
public function updated(Driver $driver)
|
|
42
42
|
{
|
|
43
|
-
LiveCacheService::
|
|
43
|
+
LiveCacheService::invalidateMultiple(['drivers', 'operations-monitor']);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
@@ -70,6 +70,6 @@ class DriverObserver
|
|
|
70
70
|
$user->delete();
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
LiveCacheService::
|
|
73
|
+
LiveCacheService::invalidateMultiple(['drivers', 'operations-monitor']);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -3,9 +3,30 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Observers;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Models\Fleet;
|
|
6
|
+
use Fleetbase\FleetOps\Support\LiveCacheService;
|
|
6
7
|
|
|
7
8
|
class FleetObserver
|
|
8
9
|
{
|
|
10
|
+
/**
|
|
11
|
+
* Handle the Fleet "created" event.
|
|
12
|
+
*
|
|
13
|
+
* @return void
|
|
14
|
+
*/
|
|
15
|
+
public function created(Fleet $fleet)
|
|
16
|
+
{
|
|
17
|
+
LiveCacheService::invalidate('operations-monitor');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Handle the Fleet "updated" event.
|
|
22
|
+
*
|
|
23
|
+
* @return void
|
|
24
|
+
*/
|
|
25
|
+
public function updated(Fleet $fleet)
|
|
26
|
+
{
|
|
27
|
+
LiveCacheService::invalidate('operations-monitor');
|
|
28
|
+
}
|
|
29
|
+
|
|
9
30
|
/**
|
|
10
31
|
* Handle the Driver "deleted" event.
|
|
11
32
|
*
|
|
@@ -15,5 +36,7 @@ class FleetObserver
|
|
|
15
36
|
{
|
|
16
37
|
// If the fleet being deleted is set as parent fleet, remove it as the parent fleet
|
|
17
38
|
$subFleets = Fleet::where(['parent_fleet_uuid' => $fleet->uuid])->update(['parent_fleet_uuid' => null]);
|
|
39
|
+
|
|
40
|
+
LiveCacheService::invalidate('operations-monitor');
|
|
18
41
|
}
|
|
19
42
|
}
|
|
@@ -30,7 +30,7 @@ class VehicleObserver
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
LiveCacheService::
|
|
33
|
+
LiveCacheService::invalidateMultiple(['vehicles', 'operations-monitor']);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/**
|
|
@@ -55,7 +55,7 @@ class VehicleObserver
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
LiveCacheService::
|
|
58
|
+
LiveCacheService::invalidateMultiple(['vehicles', 'operations-monitor']);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
@@ -68,6 +68,6 @@ class VehicleObserver
|
|
|
68
68
|
// Unassign the deleted vehicle from matching driver/(s)
|
|
69
69
|
Driver::where(['vehicle_uuid' => $vehicle->uuid])->delete();
|
|
70
70
|
|
|
71
|
-
LiveCacheService::
|
|
71
|
+
LiveCacheService::invalidateMultiple(['vehicles', 'operations-monitor']);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -7,6 +7,7 @@ use Brick\Geo\Engine\GEOSEngine;
|
|
|
7
7
|
use Fleetbase\Providers\CoreServiceProvider;
|
|
8
8
|
use Fleetbase\Support\NotificationRegistry;
|
|
9
9
|
use Fleetbase\Support\Utils;
|
|
10
|
+
use Illuminate\Database\Eloquent\Relations\Relation;
|
|
10
11
|
|
|
11
12
|
if (!Utils::classExists(CoreServiceProvider::class)) {
|
|
12
13
|
throw new \Exception('FleetOps cannot be loaded without `fleetbase/core-api` installed!');
|
|
@@ -68,6 +69,7 @@ class FleetOpsServiceProvider extends CoreServiceProvider
|
|
|
68
69
|
\Fleetbase\FleetOps\Console\Commands\ProcessMaintenanceTriggers::class,
|
|
69
70
|
\Fleetbase\FleetOps\Console\Commands\SendMaintenanceReminders::class,
|
|
70
71
|
\Fleetbase\FleetOps\Console\Commands\ProcessOperationalAlerts::class,
|
|
72
|
+
\Fleetbase\FleetOps\Console\Commands\SyncTelematics::class,
|
|
71
73
|
];
|
|
72
74
|
|
|
73
75
|
/**
|
|
@@ -129,6 +131,7 @@ class FleetOpsServiceProvider extends CoreServiceProvider
|
|
|
129
131
|
*/
|
|
130
132
|
public function boot()
|
|
131
133
|
{
|
|
134
|
+
$this->registerMorphMap();
|
|
132
135
|
$this->registerObservers();
|
|
133
136
|
$this->registerCommands();
|
|
134
137
|
$this->scheduleCommands(function ($schedule) {
|
|
@@ -139,6 +142,7 @@ class FleetOpsServiceProvider extends CoreServiceProvider
|
|
|
139
142
|
$schedule->command('fleetops:process-maintenance-triggers')->daily()->withoutOverlapping()->storeOutputInDb();
|
|
140
143
|
$schedule->command('fleetops:send-maintenance-reminders')->daily()->withoutOverlapping()->storeOutputInDb();
|
|
141
144
|
$schedule->command('fleetops:process-operational-alerts')->everyMinute()->withoutOverlapping()->storeOutputInDb();
|
|
145
|
+
$schedule->command('fleetops:sync-telematics')->everyMinute()->withoutOverlapping()->storeOutputInDb();
|
|
142
146
|
});
|
|
143
147
|
$this->registerNotifications();
|
|
144
148
|
$this->registerExpansionsFrom(__DIR__ . '/../Expansions');
|
|
@@ -194,6 +198,14 @@ class FleetOpsServiceProvider extends CoreServiceProvider
|
|
|
194
198
|
}
|
|
195
199
|
}
|
|
196
200
|
|
|
201
|
+
public function registerMorphMap(): void
|
|
202
|
+
{
|
|
203
|
+
Relation::morphMap([
|
|
204
|
+
'Fleetbase\\Models\\Vehicle' => \Fleetbase\FleetOps\Models\Vehicle::class,
|
|
205
|
+
'\\Fleetbase\\Models\\Vehicle' => \Fleetbase\FleetOps\Models\Vehicle::class,
|
|
206
|
+
]);
|
|
207
|
+
}
|
|
208
|
+
|
|
197
209
|
public function registerNotifications()
|
|
198
210
|
{
|
|
199
211
|
// Register Notifications
|
|
@@ -108,7 +108,7 @@ class LiveCacheService
|
|
|
108
108
|
}
|
|
109
109
|
} else {
|
|
110
110
|
// Invalidate all endpoints
|
|
111
|
-
$endpoints = ['orders', 'routes', 'coordinates', 'drivers', 'vehicles', 'places'];
|
|
111
|
+
$endpoints = ['orders', 'routes', 'coordinates', 'drivers', 'vehicles', 'places', 'operations-monitor'];
|
|
112
112
|
foreach ($endpoints as $ep) {
|
|
113
113
|
static::incrementVersion($ep);
|
|
114
114
|
}
|