@fleetbase/fleetops-engine 0.6.53 → 0.6.55
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/admin/routing-settings.hbs +2 -1
- package/addon/components/cell/attached-vehicle.hbs +10 -0
- package/addon/components/cell/attached-vehicle.js +58 -0
- package/addon/components/cell/device-identity.hbs +24 -0
- package/addon/components/cell/device-identity.js +129 -0
- package/addon/components/cell/driver-identity.hbs +33 -0
- package/addon/components/cell/driver-identity.js +143 -0
- package/addon/components/cell/equipment-identity.hbs +5 -0
- package/addon/components/cell/equipment-identity.js +48 -0
- package/addon/components/cell/part-identity.hbs +5 -0
- package/addon/components/cell/part-identity.js +63 -0
- package/addon/components/cell/telematic-device.hbs +38 -0
- package/addon/components/cell/telematic-device.js +44 -0
- package/addon/components/cell/telematic-provider.hbs +33 -16
- package/addon/components/cell/telematic-provider.js +50 -8
- package/addon/components/cell/vehicle-identity.hbs +33 -0
- package/addon/components/cell/vehicle-identity.js +138 -0
- package/addon/components/device/details.hbs +106 -135
- package/addon/components/device/details.js +174 -1
- package/addon/components/device/form.hbs +2 -2
- package/addon/components/device/form.js +7 -0
- package/addon/components/device/panel-header.hbs +46 -21
- package/addon/components/device/panel-header.js +49 -1
- package/addon/components/device/panel-tabs/events.hbs +30 -0
- package/addon/components/device/panel-tabs/events.js +128 -0
- package/addon/components/device/panel-tabs/sensors.hbs +30 -0
- package/addon/components/device/panel-tabs/sensors.js +93 -0
- package/addon/components/device/panel-tabs/vehicle.hbs +68 -0
- package/addon/components/device/panel-tabs/vehicle.js +90 -0
- package/addon/components/equipment/form.js +8 -1
- package/addon/components/layout/fleet-ops-sidebar/operations-monitor.js +76 -11
- package/addon/components/layout/fleet-ops-sidebar.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.js +101 -17
- package/addon/components/map/drawer/device-event-listing.js +2 -0
- package/addon/components/map/drawer/driver-listing.js +2 -0
- package/addon/components/map/drawer/place-listing.js +2 -0
- package/addon/components/map/drawer/position-listing.hbs +11 -6
- package/addon/components/map/drawer/position-listing.js +17 -4
- package/addon/components/map/drawer/vehicle-listing.js +2 -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 +48 -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/sensor/panel-header.hbs +47 -15
- package/addon/components/sensor/panel-header.js +59 -1
- package/addon/components/table/cell/dropdown/action-item.hbs +31 -0
- package/addon/components/table/cell/dropdown/action-item.js +62 -0
- package/addon/components/table/cell/dropdown.hbs +28 -0
- package/addon/components/table/cell/dropdown.js +91 -0
- package/addon/components/telematic/details.hbs +7 -1
- package/addon/components/telematic/form.js +16 -8
- package/addon/components/vendor/panel-header.hbs +82 -0
- package/addon/components/vendor/panel-header.js +48 -0
- 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 +122 -0
- package/addon/controllers/connectivity/devices/index/details.js +12 -0
- package/addon/controllers/connectivity/devices/index.js +144 -129
- package/addon/controllers/connectivity/events/details.js +3 -0
- package/addon/controllers/connectivity/events/index.js +187 -11
- package/addon/controllers/connectivity/sensors/index.js +156 -14
- package/addon/controllers/connectivity/telematics/details/attachments.js +220 -54
- package/addon/controllers/connectivity/telematics/details/devices.js +170 -129
- package/addon/controllers/connectivity/telematics/details/events.js +39 -11
- package/addon/controllers/connectivity/telematics/details/sensors.js +39 -19
- package/addon/controllers/maintenance/equipment/index.js +1 -2
- package/addon/controllers/maintenance/parts/index.js +1 -2
- package/addon/controllers/maintenance/work-orders/index.js +18 -1
- package/addon/controllers/management/drivers/index.js +82 -42
- package/addon/controllers/management/fuel-reports/index.js +63 -4
- package/addon/controllers/management/issues/index.js +63 -4
- package/addon/controllers/management/vehicles/index.js +33 -6
- 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 +29 -0
- package/addon/routes/connectivity/devices/index.js +7 -0
- package/addon/routes/connectivity/events/details.js +3 -0
- package/addon/routes/connectivity/events/index.js +6 -2
- package/addon/routes/connectivity/sensors/index.js +7 -2
- package/addon/routes/connectivity/telematics/details/attachments.js +72 -9
- package/addon/routes/connectivity/telematics/details/devices.js +6 -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/work-orders/index.js +1 -0
- package/addon/routes.js +2 -0
- package/addon/services/device-actions.js +30 -0
- package/addon/services/device-event-actions.js +1 -1
- package/addon/services/equipment-actions.js +1 -0
- package/addon/services/order-creation.js +54 -1
- package/addon/services/order-validation.js +3 -2
- package/addon/services/sensor-actions.js +3 -0
- package/addon/services/service-rate-actions.js +40 -1
- package/addon/services/vehicle-actions.js +24 -12
- package/addon/services/vendor-actions.js +3 -0
- package/addon/styles/fleetops-engine.css +28 -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 +147 -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/management/vehicles/index/edit.hbs +1 -1
- package/addon/templates/management/vendors/index/details.hbs +2 -2
- package/addon/templates/settings/notifications.hbs +2 -1
- package/addon/templates/settings/routing.hbs +1 -0
- package/addon/utils/device-table-columns.js +288 -0
- package/addon/utils/fleet-ops-options.js +38 -0
- package/addon/utils/identity-cell-resource.js +20 -0
- package/addon/utils/map-drawer-dropdown-position.js +47 -0
- package/addon/utils/trackable-option.js +93 -0
- package/app/components/cell/attached-vehicle.js +1 -0
- package/app/components/cell/device-identity.js +1 -0
- package/app/components/cell/driver-identity.js +1 -0
- package/app/components/cell/equipment-identity.js +1 -0
- package/app/components/cell/part-identity.js +1 -0
- package/app/components/cell/telematic-device.js +1 -0
- package/app/components/cell/vehicle-identity.js +1 -0
- package/app/components/device/panel-tabs/events.hbs +30 -0
- package/app/components/device/panel-tabs/events.js +128 -0
- package/app/components/device/panel-tabs/sensors.hbs +30 -0
- package/app/components/device/panel-tabs/sensors.js +93 -0
- package/app/components/device/panel-tabs/vehicle.hbs +68 -0
- package/app/components/device/panel-tabs/vehicle.js +90 -0
- package/app/components/order/form/orchestrator-constraints.js +1 -0
- package/app/components/table/cell/dropdown/action-item.js +1 -0
- package/app/components/table/cell/dropdown.js +1 -0
- package/app/components/vendor/panel-header.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/connectivity/events/{index/details.js → details.js} +1 -1
- 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/connectivity/events/{index/details.js → details.js} +1 -1
- 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/connectivity/events/{index/details.js → details.js} +1 -1
- package/app/utils/map-drawer-dropdown-position.js +1 -0
- package/app/utils/trackable-option.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +4 -4
- package/server/migrations/2025_08_28_054930_create_work_orders_table.php +1 -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/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 +55 -1
- package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +17 -6
- package/server/src/Http/Controllers/Internal/v1/FleetController.php +9 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +178 -1
- 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 +2 -2
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +4 -4
- package/server/src/Http/Controllers/Internal/v1/ServiceRateController.php +82 -13
- package/server/src/Http/Filter/DeviceEventFilter.php +65 -0
- package/server/src/Http/Filter/DeviceFilter.php +80 -0
- package/server/src/Http/Filter/SensorFilter.php +125 -0
- package/server/src/Http/Filter/VehicleFilter.php +13 -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/Index/Vehicle.php +19 -0
- 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 +180 -123
- package/server/src/Models/Device.php +17 -1
- package/server/src/Models/DeviceEvent.php +82 -1
- package/server/src/Models/Driver.php +16 -0
- package/server/src/Models/Equipment.php +55 -0
- 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 +2 -0
- package/server/src/Support/LiveCacheService.php +1 -1
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +143 -20
- package/server/src/Support/Telematics/Providers/FlespiProvider.php +89 -15
- package/server/src/Support/Telematics/Providers/GeotabProvider.php +123 -35
- package/server/src/Support/Telematics/Providers/SafeeProvider.php +70 -5
- package/server/src/Support/Telematics/Providers/SamsaraProvider.php +122 -14
- package/server/src/Support/Telematics/TelematicService.php +330 -30
- package/server/src/routes.php +1 -1
- package/server/tests/DeviceFilterTest.php +45 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +19 -2
- package/server/tests/EquipmentAssignmentMorphTypeTest.php +35 -0
- package/server/tests/OperationsMonitorEndpointTest.php +38 -0
- package/server/tests/OrderQueuedClosureRegressionTest.php +36 -0
- package/server/tests/ServiceRateRouteValidationTest.php +101 -0
- package/server/tests/TelematicsHardeningTest.php +598 -6
- package/server/tests/VehicleDriverAssignmentTest.php +27 -0
- package/translations/en-us.yaml +1 -0
- package/addon/controllers/connectivity/events/index/details.js +0 -3
- package/addon/routes/connectivity/events/index/details.js +0 -3
- /package/addon/templates/connectivity/events/{index/details.hbs → details.hbs} +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Jobs;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Events\OrderReady;
|
|
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 FinalizeInternalOrderCreation implements ShouldQueue
|
|
14
|
+
{
|
|
15
|
+
use Dispatchable;
|
|
16
|
+
use InteractsWithQueue;
|
|
17
|
+
use Queueable;
|
|
18
|
+
use SerializesModels;
|
|
19
|
+
|
|
20
|
+
public function __construct(public string $orderUuid)
|
|
21
|
+
{
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public function handle(): void
|
|
25
|
+
{
|
|
26
|
+
$order = Order::where('uuid', $this->orderUuid)->first();
|
|
27
|
+
if (!$order) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
$order->notifyDriverAssigned();
|
|
32
|
+
|
|
33
|
+
event(new OrderReady($order));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -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,141 +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
|
+
]);
|
|
60
|
+
|
|
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();
|
|
55
68
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
$totalLinked = 0;
|
|
59
|
-
$totalSkipped = 0;
|
|
60
|
-
$pageCount = 0;
|
|
61
|
-
$lastProviderAllCount = null;
|
|
62
|
-
$lastProviderFiltersCount = null;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
63
71
|
|
|
64
72
|
try {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
'limit' => $this->options['limit'] ?? null,
|
|
71
|
-
'cursor' => $cursor,
|
|
72
|
-
'filters' => $this->options['filters'] ?? [],
|
|
73
|
-
]);
|
|
73
|
+
Log::info('Device discovery started', [
|
|
74
|
+
'correlation_id' => $correlationId,
|
|
75
|
+
'telematic_uuid' => $this->telematic->uuid,
|
|
76
|
+
'provider' => $this->telematic->provider,
|
|
77
|
+
]);
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
'
|
|
97
|
-
'
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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'];
|
|
107
|
+
}
|
|
101
108
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
$service->linkDevice($this->telematic, $normalizedDevice);
|
|
106
|
-
$totalLinked++;
|
|
107
|
-
} catch (\Illuminate\Validation\ValidationException $e) {
|
|
108
|
-
$totalSkipped++;
|
|
109
|
-
Log::warning('Skipping telematics device without provider identity', [
|
|
110
|
-
'correlation_id' => $correlationId,
|
|
111
|
-
'provider' => $this->telematic->provider,
|
|
112
|
-
'provider_unit_id' => $devicePayload['_id'] ?? $devicePayload['id'] ?? null,
|
|
113
|
-
'device_id' => $normalizedDevice['device_id'] ?? null,
|
|
114
|
-
'name' => $normalizedDevice['name'] ?? $devicePayload['name'] ?? null,
|
|
115
|
-
'imei' => $devicePayload['imei'] ?? null,
|
|
116
|
-
]);
|
|
109
|
+
if (isset($pagination['filtersCount'])) {
|
|
110
|
+
$lastProviderFiltersCount = $pagination['filtersCount'];
|
|
117
111
|
}
|
|
118
|
-
}
|
|
119
112
|
|
|
120
|
-
|
|
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
|
+
}
|
|
121
148
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
'
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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,
|
|
128
174
|
]);
|
|
129
175
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
'
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
'
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
} catch (\Exception $e) {
|
|
160
|
-
Log::error('Device discovery failed', [
|
|
161
|
-
'correlation_id' => $correlationId,
|
|
162
|
-
'error' => $e->getMessage(),
|
|
163
|
-
'exception' => get_class($e),
|
|
164
|
-
]);
|
|
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
|
+
]);
|
|
165
205
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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();
|
|
182
225
|
|
|
183
|
-
|
|
226
|
+
throw $e;
|
|
227
|
+
}
|
|
228
|
+
} finally {
|
|
229
|
+
$lock->release();
|
|
184
230
|
}
|
|
185
231
|
}
|
|
186
232
|
|
|
@@ -191,4 +237,15 @@ class SyncTelematicDevicesJob implements ShouldQueue
|
|
|
191
237
|
{
|
|
192
238
|
return $this->jobId;
|
|
193
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
|
+
}
|
|
194
251
|
}
|
|
@@ -74,7 +74,23 @@ 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
|
+
'type',
|
|
84
|
+
'serial_number',
|
|
85
|
+
'last_online_at',
|
|
86
|
+
'updated_at',
|
|
87
|
+
'warranty_uuid',
|
|
88
|
+
'attachable_type',
|
|
89
|
+
'attachable_uuid',
|
|
90
|
+
'telematic',
|
|
91
|
+
'telematic_uuid',
|
|
92
|
+
'provider',
|
|
93
|
+
];
|
|
78
94
|
|
|
79
95
|
/**
|
|
80
96
|
* 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.
|
|
@@ -103,6 +103,15 @@ class DeviceEvent extends Model
|
|
|
103
103
|
*/
|
|
104
104
|
protected $appends = [
|
|
105
105
|
'device_name',
|
|
106
|
+
'device_id',
|
|
107
|
+
'device_imei',
|
|
108
|
+
'device_serial_number',
|
|
109
|
+
'device_connection_status',
|
|
110
|
+
'device_status',
|
|
111
|
+
'device_photo_url',
|
|
112
|
+
'telematic_uuid',
|
|
113
|
+
'telematic_name',
|
|
114
|
+
'provider_descriptor',
|
|
106
115
|
'is_processed',
|
|
107
116
|
'age_minutes',
|
|
108
117
|
'processing_delay_minutes',
|
|
@@ -208,6 +217,78 @@ class DeviceEvent extends Model
|
|
|
208
217
|
return $this->device?->name;
|
|
209
218
|
}
|
|
210
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Get the provider device identifier.
|
|
222
|
+
*/
|
|
223
|
+
public function getDeviceIdAttribute(): ?string
|
|
224
|
+
{
|
|
225
|
+
return $this->device?->device_id ?? $this->ident;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get the device IMEI.
|
|
230
|
+
*/
|
|
231
|
+
public function getDeviceImeiAttribute(): ?string
|
|
232
|
+
{
|
|
233
|
+
return $this->device?->imei;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get the device serial number.
|
|
238
|
+
*/
|
|
239
|
+
public function getDeviceSerialNumberAttribute(): ?string
|
|
240
|
+
{
|
|
241
|
+
return $this->device?->serial_number;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get the device connection status.
|
|
246
|
+
*/
|
|
247
|
+
public function getDeviceConnectionStatusAttribute(): ?string
|
|
248
|
+
{
|
|
249
|
+
return $this->device?->connection_status;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get the device operational status.
|
|
254
|
+
*/
|
|
255
|
+
public function getDeviceStatusAttribute(): ?string
|
|
256
|
+
{
|
|
257
|
+
return $this->device?->status;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Get the device photo URL.
|
|
262
|
+
*/
|
|
263
|
+
public function getDevicePhotoUrlAttribute(): ?string
|
|
264
|
+
{
|
|
265
|
+
return $this->device?->photo_url;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get the related telematic UUID.
|
|
270
|
+
*/
|
|
271
|
+
public function getTelematicUuidAttribute(): ?string
|
|
272
|
+
{
|
|
273
|
+
return $this->device?->telematic_uuid;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Get the related telematic name.
|
|
278
|
+
*/
|
|
279
|
+
public function getTelematicNameAttribute(): ?string
|
|
280
|
+
{
|
|
281
|
+
return $this->device?->telematic?->name;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Get the related telematic provider descriptor.
|
|
286
|
+
*/
|
|
287
|
+
public function getProviderDescriptorAttribute(): array
|
|
288
|
+
{
|
|
289
|
+
return $this->device?->telematic?->provider_descriptor ?? [];
|
|
290
|
+
}
|
|
291
|
+
|
|
211
292
|
/**
|
|
212
293
|
* Check if the event has been processed.
|
|
213
294
|
*/
|
|
@@ -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;
|
|
@@ -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
|
*/
|