@fleetbase/fleetops-engine 0.6.54 → 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/form.hbs +2 -2
- package/addon/components/device/form.js +7 -0
- package/addon/components/device/panel-header.hbs +45 -20
- 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.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.js +8 -0
- 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/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/vendor/panel-header.hbs +82 -0
- package/addon/components/vendor/panel-header.js +48 -0
- package/addon/controllers/connectivity/devices/index/details/vehicle.js +49 -1
- 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/devices.js +126 -134
- package/addon/controllers/maintenance/equipment/index.js +1 -2
- package/addon/controllers/maintenance/parts/index.js +1 -2
- 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/vehicle.js +24 -2
- 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/devices.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/sensor-actions.js +3 -0
- package/addon/services/service-rate-actions.js +39 -1
- package/addon/services/vehicle-actions.js +24 -12
- package/addon/services/vendor-actions.js +3 -0
- package/addon/styles/fleetops-engine.css +8 -0
- package/addon/templates/connectivity/devices/index/details/vehicle.hbs +64 -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/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/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/events/{index/details.js → details.js} +1 -1
- package/app/routes/connectivity/events/{index/details.js → details.js} +1 -1
- 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/src/Console/Commands/SyncTelematics.php +4 -4
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +18 -1
- package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/ServiceRateController.php +82 -13
- package/server/src/Http/Filter/DeviceEventFilter.php +21 -0
- package/server/src/Http/Filter/DeviceFilter.php +24 -1
- package/server/src/Http/Resources/v1/Index/Vehicle.php +19 -0
- package/server/src/Models/Device.php +2 -0
- package/server/src/Models/DeviceEvent.php +81 -0
- package/server/src/Models/Equipment.php +55 -0
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +1 -0
- 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 +26 -2
- package/server/src/Support/Telematics/Providers/SamsaraProvider.php +122 -14
- package/server/src/routes.php +0 -1
- package/server/tests/DeviceFilterTest.php +9 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +19 -2
- package/server/tests/EquipmentAssignmentMorphTypeTest.php +35 -0
- package/server/tests/ServiceRateRouteValidationTest.php +101 -0
- package/server/tests/TelematicsHardeningTest.php +238 -7
- 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
|
@@ -8,6 +8,7 @@ use Fleetbase\FleetOps\Http\Controllers\FleetOpsController;
|
|
|
8
8
|
use Fleetbase\FleetOps\Models\ServiceRate;
|
|
9
9
|
use Fleetbase\Http\Requests\ExportRequest;
|
|
10
10
|
use Illuminate\Http\Request;
|
|
11
|
+
use Illuminate\Support\Collection;
|
|
11
12
|
use Illuminate\Support\Str;
|
|
12
13
|
use Maatwebsite\Excel\Facades\Excel;
|
|
13
14
|
|
|
@@ -27,24 +28,30 @@ class ServiceRateController extends FleetOpsController
|
|
|
27
28
|
*/
|
|
28
29
|
public function getServicesForRoute(Request $request)
|
|
29
30
|
{
|
|
30
|
-
$coordinates =
|
|
31
|
+
$coordinates = $request->input('coordinates');
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
$coord = explode(',', $coord);
|
|
36
|
-
[$latitude, $longitude] = $coord;
|
|
33
|
+
if (!is_string($coordinates) || trim($coordinates) === '') {
|
|
34
|
+
return response()->error('Route coordinates are required to query service rates.', 422);
|
|
35
|
+
}
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
)
|
|
37
|
+
$waypoints = $this->parseRouteCoordinates($coordinates);
|
|
38
|
+
|
|
39
|
+
if ($waypoints === null) {
|
|
40
|
+
return response()->error('Invalid route coordinates provided.', 422);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if ($waypoints->count() < 2) {
|
|
44
|
+
return response()->error('At least two route coordinates are required to query service rates.', 422);
|
|
45
|
+
}
|
|
41
46
|
|
|
42
|
-
$
|
|
47
|
+
$serviceType = $this->normalizeOptionalQueryValue($request->input('service_type'));
|
|
48
|
+
|
|
49
|
+
$applicableServiceRates = $this->getServicableForWaypoints(
|
|
43
50
|
$waypoints,
|
|
44
|
-
function ($query) use ($request) {
|
|
51
|
+
function ($query) use ($request, $serviceType) {
|
|
45
52
|
$query->where('company_uuid', $request->session()->get('company'));
|
|
46
|
-
if ($
|
|
47
|
-
$query->where('service_type', $
|
|
53
|
+
if ($serviceType) {
|
|
54
|
+
$query->where('service_type', $serviceType);
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
);
|
|
@@ -52,6 +59,68 @@ class ServiceRateController extends FleetOpsController
|
|
|
52
59
|
return response()->json($applicableServiceRates);
|
|
53
60
|
}
|
|
54
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Parse semicolon-delimited latitude,longitude coordinate pairs.
|
|
64
|
+
*/
|
|
65
|
+
protected function parseRouteCoordinates(string $coordinates): ?Collection
|
|
66
|
+
{
|
|
67
|
+
$coordinates = collect(explode(';', $coordinates))
|
|
68
|
+
->map(fn ($coordinate) => trim($coordinate));
|
|
69
|
+
|
|
70
|
+
if ($coordinates->contains('')) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
$waypoints = $coordinates
|
|
75
|
+
->map(function ($coordinate) {
|
|
76
|
+
$parts = array_map('trim', explode(',', $coordinate));
|
|
77
|
+
|
|
78
|
+
if (count($parts) !== 2) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
[$latitude, $longitude] = $parts;
|
|
83
|
+
|
|
84
|
+
if (!is_numeric($latitude) || !is_numeric($longitude)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return Point::fromText("POINT($longitude $latitude)", 4326);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if ($waypoints->contains(null)) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return $waypoints->values();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Treat placeholder query-string values from the UI as absent.
|
|
100
|
+
*/
|
|
101
|
+
protected function normalizeOptionalQueryValue($value): ?string
|
|
102
|
+
{
|
|
103
|
+
if (!is_string($value)) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
$value = trim($value);
|
|
108
|
+
|
|
109
|
+
if ($value === '' || in_array(strtolower($value), ['null', 'undefined'], true)) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return $value;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Resolve applicable service rates for route waypoints.
|
|
118
|
+
*/
|
|
119
|
+
protected function getServicableForWaypoints(Collection $waypoints, \Closure $queryCallback): array
|
|
120
|
+
{
|
|
121
|
+
return ServiceRate::getServicableForWaypoints($waypoints, $queryCallback);
|
|
122
|
+
}
|
|
123
|
+
|
|
55
124
|
/**
|
|
56
125
|
* Export the service rate to excel or csv.
|
|
57
126
|
*
|
|
@@ -26,6 +26,27 @@ class DeviceEventFilter extends Filter
|
|
|
26
26
|
$this->builder->search($searchQuery);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
public function eventType(?string $eventType)
|
|
30
|
+
{
|
|
31
|
+
if ($eventType) {
|
|
32
|
+
$this->builder->where('event_type', 'like', '%' . $eventType . '%');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public function provider(?string $provider)
|
|
37
|
+
{
|
|
38
|
+
if ($provider) {
|
|
39
|
+
$this->builder->where('provider', 'like', '%' . $provider . '%');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public function code(?string $code)
|
|
44
|
+
{
|
|
45
|
+
if ($code) {
|
|
46
|
+
$this->builder->where('code', 'like', '%' . $code . '%');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
29
50
|
public function telematic(?string $telematic)
|
|
30
51
|
{
|
|
31
52
|
$this->builder->whereHas('device', function ($query) use ($telematic) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Filter;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Models\Vehicle;
|
|
5
6
|
use Fleetbase\FleetOps\Support\Utils;
|
|
6
7
|
use Fleetbase\Http\Filter\Filter;
|
|
7
8
|
|
|
@@ -38,6 +39,22 @@ class DeviceFilter extends Filter
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
public function type(string|array|null $type)
|
|
43
|
+
{
|
|
44
|
+
$type = Utils::arrayFrom($type);
|
|
45
|
+
|
|
46
|
+
if ($type) {
|
|
47
|
+
$this->builder->whereIn('type', $type);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public function serialNumber(?string $serialNumber)
|
|
52
|
+
{
|
|
53
|
+
if ($serialNumber) {
|
|
54
|
+
$this->builder->where('serial_number', 'like', '%' . $serialNumber . '%');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
41
58
|
public function telematic(?string $telematic)
|
|
42
59
|
{
|
|
43
60
|
$this->builder->where('telematic_uuid', $telematic);
|
|
@@ -71,7 +88,13 @@ class DeviceFilter extends Filter
|
|
|
71
88
|
public function vehicle(?string $vehicle)
|
|
72
89
|
{
|
|
73
90
|
if ($vehicle) {
|
|
74
|
-
$this->builder->where(
|
|
91
|
+
$this->builder->where(function ($query) use ($vehicle) {
|
|
92
|
+
$query->where('attachable_uuid', $vehicle)
|
|
93
|
+
->orWhereIn('attachable_uuid', Vehicle::query()
|
|
94
|
+
->where('company_uuid', $this->session->get('company'))
|
|
95
|
+
->where('public_id', $vehicle)
|
|
96
|
+
->pluck('uuid'));
|
|
97
|
+
});
|
|
75
98
|
}
|
|
76
99
|
}
|
|
77
100
|
|
|
@@ -46,6 +46,7 @@ class Vehicle extends FleetbaseResource
|
|
|
46
46
|
'altitude' => (int) data_get($this, 'altitude', 0),
|
|
47
47
|
'speed' => (int) data_get($this, 'speed', 0),
|
|
48
48
|
'online' => (bool) data_get($this, 'online', false),
|
|
49
|
+
'devices' => $this->whenLoaded('devices', fn () => $this->compactDevices()),
|
|
49
50
|
'assigned_orders_count' => $this->when($isInternal, $this->assignedOrdersCount()),
|
|
50
51
|
|
|
51
52
|
// Meta flag to indicate this is an index resource
|
|
@@ -65,6 +66,24 @@ class Vehicle extends FleetbaseResource
|
|
|
65
66
|
return Order::where('vehicle_assigned_uuid', $this->uuid)->count();
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
protected function compactDevices(): array
|
|
70
|
+
{
|
|
71
|
+
return $this->devices->map(
|
|
72
|
+
fn ($device) => [
|
|
73
|
+
'id' => $device->uuid,
|
|
74
|
+
'uuid' => $device->uuid,
|
|
75
|
+
'public_id' => $device->public_id,
|
|
76
|
+
'name' => $device->name,
|
|
77
|
+
'display_name' => $device->display_name,
|
|
78
|
+
'device_id' => $device->device_id,
|
|
79
|
+
'serial_number' => $device->serial_number,
|
|
80
|
+
'imei' => $device->imei,
|
|
81
|
+
'provider' => $device->provider,
|
|
82
|
+
'status' => $device->status,
|
|
83
|
+
]
|
|
84
|
+
)->values()->all();
|
|
85
|
+
}
|
|
86
|
+
|
|
68
87
|
protected function currentOrderReference(): ?string
|
|
69
88
|
{
|
|
70
89
|
$this->loadMissing('driver.currentOrder');
|
|
@@ -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
|
*/
|
|
@@ -18,6 +18,7 @@ use Fleetbase\Traits\TracksApiCredential;
|
|
|
18
18
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
19
19
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
20
20
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
|
21
|
+
use Illuminate\Database\Eloquent\Relations\Relation;
|
|
21
22
|
use Spatie\Activitylog\LogOptions;
|
|
22
23
|
use Spatie\Activitylog\Traits\LogsActivity;
|
|
23
24
|
use Spatie\Sluggable\HasSlug;
|
|
@@ -94,6 +95,15 @@ class Equipment extends Model
|
|
|
94
95
|
'slug',
|
|
95
96
|
];
|
|
96
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Set attributes and defaults.
|
|
100
|
+
*
|
|
101
|
+
* @var array
|
|
102
|
+
*/
|
|
103
|
+
protected $attributes = [
|
|
104
|
+
'status' => 'available',
|
|
105
|
+
];
|
|
106
|
+
|
|
97
107
|
/**
|
|
98
108
|
* Dynamic attributes that are appended to object.
|
|
99
109
|
*
|
|
@@ -148,6 +158,19 @@ class Equipment extends Model
|
|
|
148
158
|
*/
|
|
149
159
|
protected static $logName = 'equipment';
|
|
150
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Enforce assignment morph aliases for existing short type values.
|
|
163
|
+
*/
|
|
164
|
+
public static function boot(): void
|
|
165
|
+
{
|
|
166
|
+
parent::boot();
|
|
167
|
+
|
|
168
|
+
Relation::morphMap([
|
|
169
|
+
'fleet-ops:vehicle' => Vehicle::class,
|
|
170
|
+
'fleet-ops:driver' => Driver::class,
|
|
171
|
+
]);
|
|
172
|
+
}
|
|
173
|
+
|
|
151
174
|
/**
|
|
152
175
|
* Get the options for generating the slug.
|
|
153
176
|
*/
|
|
@@ -192,6 +215,38 @@ class Equipment extends Model
|
|
|
192
215
|
return $this->morphTo(__FUNCTION__, 'equipable_type', 'equipable_uuid');
|
|
193
216
|
}
|
|
194
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Set the equipment status attribute.
|
|
220
|
+
*/
|
|
221
|
+
public function setStatusAttribute(?string $status = 'available'): void
|
|
222
|
+
{
|
|
223
|
+
$this->attributes['status'] = $status === null || $status === 'active' ? 'available' : $status;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Normalize equipment assignment type aliases to Eloquent morph classes.
|
|
228
|
+
*/
|
|
229
|
+
public function setEquipableTypeAttribute(?string $type): void
|
|
230
|
+
{
|
|
231
|
+
if ($type === null || $type === '') {
|
|
232
|
+
$this->attributes['equipable_type'] = null;
|
|
233
|
+
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (str_contains($type, '\\')) {
|
|
238
|
+
$this->attributes['equipable_type'] = $type;
|
|
239
|
+
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
$this->attributes['equipable_type'] = match ($type) {
|
|
244
|
+
'fleet-ops:vehicle', 'vehicle' => Utils::getMutationType('fleet-ops:vehicle'),
|
|
245
|
+
'fleet-ops:driver', 'driver' => Utils::getMutationType('fleet-ops:driver'),
|
|
246
|
+
default => $type,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
195
250
|
public function maintenances(): HasMany
|
|
196
251
|
{
|
|
197
252
|
return $this->hasMany(Maintenance::class, 'maintainable_uuid', 'uuid')
|
|
@@ -151,6 +151,7 @@ class AfaqyProvider extends AbstractProvider
|
|
|
151
151
|
|
|
152
152
|
return [
|
|
153
153
|
'device_id' => $payload['_id'] ?? $payload['id'] ?? null,
|
|
154
|
+
'external_id' => $payload['_id'] ?? $payload['id'] ?? null,
|
|
154
155
|
'name' => $payload['name'] ?? data_get($payload, 'profile.plate_number') ?? 'Unknown Unit',
|
|
155
156
|
'provider' => 'afaqy',
|
|
156
157
|
'model' => data_get($payload, 'profile.model') ?? $payload['device'] ?? null,
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Support\Telematics\Providers;
|
|
4
4
|
|
|
5
|
+
use Illuminate\Support\Carbon;
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Class FlespiProvider.
|
|
7
9
|
*
|
|
@@ -77,34 +79,71 @@ class FlespiProvider extends AbstractProvider
|
|
|
77
79
|
|
|
78
80
|
public function normalizeDevice(array $payload): array
|
|
79
81
|
{
|
|
82
|
+
$telemetry = $payload['telemetry'] ?? $payload;
|
|
83
|
+
$externalId = $payload['id'] ?? $payload['device.id'] ?? null;
|
|
84
|
+
$occurredAt = $this->parseTimestamp($telemetry['timestamp'] ?? $payload['last_active'] ?? null);
|
|
85
|
+
|
|
80
86
|
return [
|
|
81
|
-
'
|
|
82
|
-
'
|
|
83
|
-
'
|
|
84
|
-
'
|
|
85
|
-
'
|
|
86
|
-
'
|
|
87
|
-
'
|
|
88
|
-
'
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
'device_id' => $externalId,
|
|
88
|
+
'external_id' => $externalId,
|
|
89
|
+
'name' => $payload['name'] ?? $telemetry['device.name'] ?? 'Unknown Device',
|
|
90
|
+
'provider' => 'flespi',
|
|
91
|
+
'model' => $payload['device_type_id'] ?? $payload['device_type_name'] ?? null,
|
|
92
|
+
'imei' => $payload['configuration']['ident'] ?? $payload['ident'] ?? $telemetry['ident'] ?? null,
|
|
93
|
+
'serial_number' => $payload['configuration']['serial'] ?? $payload['serial'] ?? null,
|
|
94
|
+
'phone' => $payload['configuration']['phone'] ?? null,
|
|
95
|
+
'status' => isset($payload['telemetry']) ? 'active' : 'inactive',
|
|
96
|
+
'online' => $this->resolveOnline($telemetry),
|
|
97
|
+
'last_seen_at' => $occurredAt,
|
|
98
|
+
'location' => [
|
|
99
|
+
'lat' => $telemetry['position.latitude'] ?? null,
|
|
100
|
+
'lng' => $telemetry['position.longitude'] ?? null,
|
|
101
|
+
],
|
|
102
|
+
'speed' => $telemetry['position.speed'] ?? $telemetry['vehicle.speed'] ?? null,
|
|
103
|
+
'heading' => $telemetry['position.direction'] ?? $telemetry['position.heading'] ?? null,
|
|
104
|
+
'altitude' => $telemetry['position.altitude'] ?? null,
|
|
105
|
+
'odometer' => $telemetry['vehicle.mileage'] ?? $telemetry['vehicle.odometer'] ?? null,
|
|
106
|
+
'ignition' => $this->extractIgnition($telemetry),
|
|
107
|
+
'fuel_level' => $telemetry['fuel.level'] ?? $telemetry['can.fuel.level'] ?? null,
|
|
108
|
+
'meta' => [
|
|
109
|
+
'raw' => $payload,
|
|
110
|
+
'provider_status' => array_filter([
|
|
111
|
+
'status' => $payload['status'] ?? $telemetry['status'] ?? null,
|
|
112
|
+
'online' => $telemetry['online'] ?? null,
|
|
113
|
+
'connected' => $telemetry['device.connected'] ?? $telemetry['connected'] ?? null,
|
|
114
|
+
], fn ($value) => $value !== null),
|
|
91
115
|
],
|
|
92
|
-
'meta' => $payload,
|
|
93
116
|
];
|
|
94
117
|
}
|
|
95
118
|
|
|
96
119
|
public function normalizeEvent(array $payload): array
|
|
97
120
|
{
|
|
121
|
+
$deviceId = $payload['device.id'] ?? $payload['id'] ?? null;
|
|
122
|
+
|
|
98
123
|
return [
|
|
99
|
-
'external_id' => $payload['id'] ??
|
|
100
|
-
'device_id' => $
|
|
124
|
+
'external_id' => $payload['id'] ?? $deviceId,
|
|
125
|
+
'device_id' => $deviceId,
|
|
101
126
|
'event_type' => $payload['event.enum'] ?? 'telemetry_update',
|
|
102
|
-
'occurred_at' =>
|
|
127
|
+
'occurred_at' => $this->parseTimestamp($payload['timestamp'] ?? null) ?? now(),
|
|
128
|
+
'online' => $this->resolveOnline($payload),
|
|
103
129
|
'location' => [
|
|
104
130
|
'lat' => $payload['position.latitude'] ?? null,
|
|
105
131
|
'lng' => $payload['position.longitude'] ?? null,
|
|
106
132
|
],
|
|
107
|
-
'
|
|
133
|
+
'speed' => $payload['position.speed'] ?? $payload['vehicle.speed'] ?? null,
|
|
134
|
+
'heading' => $payload['position.direction'] ?? $payload['position.heading'] ?? null,
|
|
135
|
+
'altitude' => $payload['position.altitude'] ?? null,
|
|
136
|
+
'odometer' => $payload['vehicle.mileage'] ?? $payload['vehicle.odometer'] ?? null,
|
|
137
|
+
'ignition' => $this->extractIgnition($payload),
|
|
138
|
+
'fuel_level' => $payload['fuel.level'] ?? $payload['can.fuel.level'] ?? null,
|
|
139
|
+
'meta' => [
|
|
140
|
+
'raw' => $payload,
|
|
141
|
+
'provider_status' => array_filter([
|
|
142
|
+
'status' => $payload['status'] ?? null,
|
|
143
|
+
'online' => $payload['online'] ?? null,
|
|
144
|
+
'connected' => $payload['device.connected'] ?? $payload['connected'] ?? null,
|
|
145
|
+
], fn ($value) => $value !== null),
|
|
146
|
+
],
|
|
108
147
|
];
|
|
109
148
|
}
|
|
110
149
|
|
|
@@ -180,4 +219,39 @@ class FlespiProvider extends AbstractProvider
|
|
|
180
219
|
{
|
|
181
220
|
return true;
|
|
182
221
|
}
|
|
222
|
+
|
|
223
|
+
protected function parseTimestamp($value): ?string
|
|
224
|
+
{
|
|
225
|
+
if (!$value) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (is_numeric($value)) {
|
|
230
|
+
return Carbon::createFromTimestamp((float) $value)->toDateTimeString();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return Carbon::parse($value)->toDateTimeString();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
protected function resolveOnline(array $payload): ?bool
|
|
237
|
+
{
|
|
238
|
+
$value = $payload['online'] ?? $payload['device.connected'] ?? $payload['connected'] ?? null;
|
|
239
|
+
|
|
240
|
+
if ($value === null) {
|
|
241
|
+
return isset($payload['timestamp']);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? (bool) $value;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
protected function extractIgnition(array $payload): ?bool
|
|
248
|
+
{
|
|
249
|
+
$value = $payload['engine.ignition.status'] ?? $payload['ignition.status'] ?? null;
|
|
250
|
+
|
|
251
|
+
if ($value === null) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? (bool) $value;
|
|
256
|
+
}
|
|
183
257
|
}
|