@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
|
@@ -16,6 +16,7 @@ use Illuminate\Http\JsonResponse;
|
|
|
16
16
|
use Illuminate\Http\Request;
|
|
17
17
|
use Illuminate\Support\Arr;
|
|
18
18
|
use Illuminate\Support\Facades\DB;
|
|
19
|
+
use Illuminate\Support\Facades\Log;
|
|
19
20
|
use Illuminate\Support\Str;
|
|
20
21
|
use Maatwebsite\Excel\Facades\Excel;
|
|
21
22
|
|
|
@@ -179,12 +180,31 @@ class VehicleController extends FleetOpsController
|
|
|
179
180
|
{
|
|
180
181
|
$request->validate(['device' => 'required|string']);
|
|
181
182
|
|
|
182
|
-
$
|
|
183
|
-
$
|
|
183
|
+
$deviceId = $request->input('device');
|
|
184
|
+
$vehicle = $this->resolveVehicle($id);
|
|
185
|
+
$device = $this->resolveDevice($deviceId);
|
|
184
186
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
187
|
+
if (!$vehicle) {
|
|
188
|
+
$this->logDeviceAttachmentLookupFailure('attach-device', 'vehicle', $id, $deviceId);
|
|
189
|
+
|
|
190
|
+
return response()->error('Vehicle not found or not available for this organization.', 404);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!$device) {
|
|
194
|
+
$this->logDeviceAttachmentLookupFailure('attach-device', 'device', $id, $deviceId);
|
|
195
|
+
|
|
196
|
+
return response()->error('Device not found or not available for this organization.', 404);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
$device->attachTo($vehicle);
|
|
201
|
+
$device->load(['telematic', 'warranty', 'attachable']);
|
|
202
|
+
$vehicle->load(['driver', 'devices']);
|
|
203
|
+
} catch (\Throwable $e) {
|
|
204
|
+
$this->logDeviceAttachmentFailure('attach-device', $vehicle, $device, $e);
|
|
205
|
+
|
|
206
|
+
return response()->error('Unable to attach device to vehicle. Please try again or contact support.', 500);
|
|
207
|
+
}
|
|
188
208
|
|
|
189
209
|
return response()->json([
|
|
190
210
|
'status' => 'ok',
|
|
@@ -198,16 +218,35 @@ class VehicleController extends FleetOpsController
|
|
|
198
218
|
{
|
|
199
219
|
$request->validate(['device' => 'required|string']);
|
|
200
220
|
|
|
201
|
-
$
|
|
202
|
-
$
|
|
221
|
+
$deviceId = $request->input('device');
|
|
222
|
+
$vehicle = $this->resolveVehicle($id);
|
|
223
|
+
$device = $this->resolveDevice($deviceId);
|
|
224
|
+
|
|
225
|
+
if (!$vehicle) {
|
|
226
|
+
$this->logDeviceAttachmentLookupFailure('detach-device', 'vehicle', $id, $deviceId);
|
|
227
|
+
|
|
228
|
+
return response()->error('Vehicle not found or not available for this organization.', 404);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!$device) {
|
|
232
|
+
$this->logDeviceAttachmentLookupFailure('detach-device', 'device', $id, $deviceId);
|
|
233
|
+
|
|
234
|
+
return response()->error('Device not found or not available for this organization.', 404);
|
|
235
|
+
}
|
|
203
236
|
|
|
204
237
|
if ($device->attachable_uuid !== $vehicle->uuid) {
|
|
205
238
|
return response()->error('This device is not attached to the selected vehicle.');
|
|
206
239
|
}
|
|
207
240
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
241
|
+
try {
|
|
242
|
+
$device->detach();
|
|
243
|
+
$device->load(['telematic', 'warranty', 'attachable']);
|
|
244
|
+
$vehicle->load(['driver', 'devices']);
|
|
245
|
+
} catch (\Throwable $e) {
|
|
246
|
+
$this->logDeviceAttachmentFailure('detach-device', $vehicle, $device, $e);
|
|
247
|
+
|
|
248
|
+
return response()->error('Unable to detach device from vehicle. Please try again or contact support.', 500);
|
|
249
|
+
}
|
|
211
250
|
|
|
212
251
|
return response()->json([
|
|
213
252
|
'status' => 'ok',
|
|
@@ -226,6 +265,15 @@ class VehicleController extends FleetOpsController
|
|
|
226
265
|
->firstOrFail();
|
|
227
266
|
}
|
|
228
267
|
|
|
268
|
+
protected function resolveVehicle(string $id): ?Vehicle
|
|
269
|
+
{
|
|
270
|
+
return Vehicle::where(function ($query) use ($id) {
|
|
271
|
+
$query->where('uuid', $id)->orWhere('public_id', $id);
|
|
272
|
+
})
|
|
273
|
+
->where('company_uuid', session('company'))
|
|
274
|
+
->first();
|
|
275
|
+
}
|
|
276
|
+
|
|
229
277
|
protected function findDriver(string $id): Driver
|
|
230
278
|
{
|
|
231
279
|
return Driver::where(function ($query) use ($id) {
|
|
@@ -235,6 +283,42 @@ class VehicleController extends FleetOpsController
|
|
|
235
283
|
->firstOrFail();
|
|
236
284
|
}
|
|
237
285
|
|
|
286
|
+
protected function resolveDevice(string $id): ?Device
|
|
287
|
+
{
|
|
288
|
+
return Device::where(function ($query) use ($id) {
|
|
289
|
+
$query->where('uuid', $id)->orWhere('public_id', $id);
|
|
290
|
+
})
|
|
291
|
+
->where('company_uuid', session('company'))
|
|
292
|
+
->first();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
protected function logDeviceAttachmentLookupFailure(string $action, string $missingResource, string $vehicleId, ?string $deviceId): void
|
|
296
|
+
{
|
|
297
|
+
Log::warning('Vehicle device attachment lookup failed', [
|
|
298
|
+
'action' => $action,
|
|
299
|
+
'missing_resource' => $missingResource,
|
|
300
|
+
'vehicle_id' => $vehicleId,
|
|
301
|
+
'device_id' => $deviceId,
|
|
302
|
+
'company_uuid' => session('company'),
|
|
303
|
+
'request_id' => request()->headers->get('X-Request-ID') ?? request()->headers->get('X-Correlation-ID'),
|
|
304
|
+
]);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
protected function logDeviceAttachmentFailure(string $action, Vehicle $vehicle, Device $device, \Throwable $exception): void
|
|
308
|
+
{
|
|
309
|
+
Log::error('Vehicle device attachment failed', [
|
|
310
|
+
'action' => $action,
|
|
311
|
+
'vehicle_uuid' => $vehicle->uuid,
|
|
312
|
+
'vehicle_id' => $vehicle->public_id,
|
|
313
|
+
'device_uuid' => $device->uuid,
|
|
314
|
+
'device_id' => $device->public_id,
|
|
315
|
+
'company_uuid' => session('company'),
|
|
316
|
+
'request_id' => request()->headers->get('X-Request-ID') ?? request()->headers->get('X-Correlation-ID'),
|
|
317
|
+
'exception_class' => get_class($exception),
|
|
318
|
+
'exception' => $exception->getMessage(),
|
|
319
|
+
]);
|
|
320
|
+
}
|
|
321
|
+
|
|
238
322
|
protected function activeOrderUuid(Vehicle $vehicle): ?string
|
|
239
323
|
{
|
|
240
324
|
$vehicle->loadMissing('driver.currentOrder');
|
|
@@ -44,6 +44,50 @@ class DeviceEventFilter extends Filter
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
public function deviceUuid(?string $device)
|
|
48
|
+
{
|
|
49
|
+
$this->device($device);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public function severity(string|array $severity)
|
|
53
|
+
{
|
|
54
|
+
$severity = Utils::arrayFrom($severity);
|
|
55
|
+
|
|
56
|
+
if ($severity) {
|
|
57
|
+
$this->builder->whereIn('severity', $severity);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public function processed(string|array $processed)
|
|
62
|
+
{
|
|
63
|
+
$states = Utils::arrayFrom($processed);
|
|
64
|
+
|
|
65
|
+
if (!$states) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
$this->builder->where(function ($query) use ($states) {
|
|
70
|
+
foreach ($states as $state) {
|
|
71
|
+
match ($state) {
|
|
72
|
+
'processed' => $query->orWhereNotNull('processed_at'),
|
|
73
|
+
'unprocessed' => $query->orWhereNull('processed_at'),
|
|
74
|
+
default => null,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public function occurredAt($occurredAt)
|
|
81
|
+
{
|
|
82
|
+
$occurredAt = Utils::dateRange($occurredAt);
|
|
83
|
+
|
|
84
|
+
if (is_array($occurredAt)) {
|
|
85
|
+
$this->builder->whereBetween('occurred_at', $occurredAt);
|
|
86
|
+
} else {
|
|
87
|
+
$this->builder->whereDate('occurred_at', $occurredAt);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
47
91
|
public function createdAt($createdAt)
|
|
48
92
|
{
|
|
49
93
|
$createdAt = Utils::dateRange($createdAt);
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Filter;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Support\Utils;
|
|
6
|
+
use Fleetbase\Http\Filter\Filter;
|
|
7
|
+
|
|
8
|
+
class DeviceFilter extends Filter
|
|
9
|
+
{
|
|
10
|
+
public function queryForInternal()
|
|
11
|
+
{
|
|
12
|
+
$this->builder->where('company_uuid', $this->session->get('company'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public function queryForPublic()
|
|
16
|
+
{
|
|
17
|
+
$this->queryForInternal();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public function query(?string $searchQuery)
|
|
21
|
+
{
|
|
22
|
+
$this->builder->search($searchQuery);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public function status(string|array $status)
|
|
26
|
+
{
|
|
27
|
+
$status = Utils::arrayFrom($status);
|
|
28
|
+
|
|
29
|
+
if ($status) {
|
|
30
|
+
$this->builder->whereIn('status', $status);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public function deviceId(?string $deviceId)
|
|
35
|
+
{
|
|
36
|
+
if ($deviceId) {
|
|
37
|
+
$this->builder->where('device_id', 'like', '%' . $deviceId . '%');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public function telematic(?string $telematic)
|
|
42
|
+
{
|
|
43
|
+
$this->builder->where('telematic_uuid', $telematic);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public function telematicUuid(?string $telematic)
|
|
47
|
+
{
|
|
48
|
+
$this->telematic($telematic);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public function provider(?string $provider)
|
|
52
|
+
{
|
|
53
|
+
$this->builder->where('provider', $provider);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public function warrantyUuid(?string $warranty)
|
|
57
|
+
{
|
|
58
|
+
$this->builder->where('warranty_uuid', $warranty);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public function attachableType(?string $attachableType)
|
|
62
|
+
{
|
|
63
|
+
$this->builder->where('attachable_type', $attachableType);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public function attachableUuid(?string $attachable)
|
|
67
|
+
{
|
|
68
|
+
$this->builder->where('attachable_uuid', $attachable);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public function vehicle(?string $vehicle)
|
|
72
|
+
{
|
|
73
|
+
if ($vehicle) {
|
|
74
|
+
$this->builder->where('attachable_uuid', $vehicle);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public function connectionStatus(string|array $connectionStatus)
|
|
79
|
+
{
|
|
80
|
+
$statuses = Utils::arrayFrom($connectionStatus);
|
|
81
|
+
|
|
82
|
+
if (!$statuses) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
$this->builder->where(function ($query) use ($statuses) {
|
|
87
|
+
foreach ($statuses as $status) {
|
|
88
|
+
match ($status) {
|
|
89
|
+
'online' => $query->orWhere('last_online_at', '>=', now()->subMinutes(10)),
|
|
90
|
+
'recently_offline' => $query->orWhereBetween('last_online_at', [now()->subMinutes(60), now()->subMinutes(10)]),
|
|
91
|
+
'offline' => $query->orWhereBetween('last_online_at', [now()->subDay(), now()->subMinutes(60)]),
|
|
92
|
+
'long_offline' => $query->orWhere('last_online_at', '<', now()->subDay()),
|
|
93
|
+
'never_connected' => $query->orWhereNull('last_online_at'),
|
|
94
|
+
default => null,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public function attachmentState(?string $attachmentState)
|
|
101
|
+
{
|
|
102
|
+
if ($attachmentState === 'attached') {
|
|
103
|
+
$this->builder->whereNotNull('attachable_uuid');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if ($attachmentState === 'unattached') {
|
|
107
|
+
$this->builder->whereNull('attachable_uuid');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public function lastOnlineAt(string|array $lastOnlineAt)
|
|
112
|
+
{
|
|
113
|
+
$this->filterDate('last_online_at', $lastOnlineAt);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public function updatedAt(string|array $updatedAt)
|
|
117
|
+
{
|
|
118
|
+
$this->filterDate('updated_at', $updatedAt);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
protected function filterDate(string $column, string|array $value): void
|
|
122
|
+
{
|
|
123
|
+
$dates = Utils::dateRange($value);
|
|
124
|
+
|
|
125
|
+
if (is_array($dates)) {
|
|
126
|
+
$this->builder->whereBetween($column, $dates);
|
|
127
|
+
} else {
|
|
128
|
+
$this->builder->whereDate($column, $dates);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -70,6 +70,12 @@ class DriverFilter extends Filter
|
|
|
70
70
|
|
|
71
71
|
public function vehicle(string $vehicle)
|
|
72
72
|
{
|
|
73
|
+
if ($vehicle === 'unassigned') {
|
|
74
|
+
$this->builder->whereNull('vehicle_uuid');
|
|
75
|
+
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
73
79
|
if (Str::isUuid($vehicle)) {
|
|
74
80
|
$this->builder->where('vehicle_uuid', $vehicle);
|
|
75
81
|
} else {
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Filter;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Support\Utils;
|
|
6
|
+
use Fleetbase\Http\Filter\Filter;
|
|
7
|
+
|
|
8
|
+
class SensorFilter extends Filter
|
|
9
|
+
{
|
|
10
|
+
public function queryForInternal()
|
|
11
|
+
{
|
|
12
|
+
$this->builder->where('company_uuid', $this->session->get('company'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public function queryForPublic()
|
|
16
|
+
{
|
|
17
|
+
$this->queryForInternal();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public function query(?string $searchQuery)
|
|
21
|
+
{
|
|
22
|
+
$this->builder->search($searchQuery);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public function type(string|array $type)
|
|
26
|
+
{
|
|
27
|
+
$types = Utils::arrayFrom($type);
|
|
28
|
+
|
|
29
|
+
if ($types) {
|
|
30
|
+
$this->builder->whereIn('type', $types);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public function sensorType(string|array $type)
|
|
35
|
+
{
|
|
36
|
+
$this->type($type);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public function status(string|array $status)
|
|
40
|
+
{
|
|
41
|
+
$statuses = Utils::arrayFrom($status);
|
|
42
|
+
|
|
43
|
+
if ($statuses) {
|
|
44
|
+
$this->builder->whereIn('status', $statuses);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public function device(?string $device)
|
|
49
|
+
{
|
|
50
|
+
if ($device) {
|
|
51
|
+
$this->builder->where('device_uuid', $device);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public function deviceUuid(?string $device)
|
|
56
|
+
{
|
|
57
|
+
$this->device($device);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public function telematic(?string $telematic)
|
|
61
|
+
{
|
|
62
|
+
if ($telematic) {
|
|
63
|
+
$this->builder->where('telematic_uuid', $telematic);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public function telematicUuid(?string $telematic)
|
|
68
|
+
{
|
|
69
|
+
$this->telematic($telematic);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public function warrantyUuid(?string $warranty)
|
|
73
|
+
{
|
|
74
|
+
if ($warranty) {
|
|
75
|
+
$this->builder->where('warranty_uuid', $warranty);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public function sensorableType(?string $sensorableType)
|
|
80
|
+
{
|
|
81
|
+
if ($sensorableType) {
|
|
82
|
+
$this->builder->where('sensorable_type', $sensorableType);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public function serialNumber(?string $serialNumber)
|
|
87
|
+
{
|
|
88
|
+
if ($serialNumber) {
|
|
89
|
+
$this->builder->where('serial_number', 'like', '%' . $serialNumber . '%');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public function imei(?string $imei)
|
|
94
|
+
{
|
|
95
|
+
if ($imei) {
|
|
96
|
+
$this->builder->where('imei', 'like', '%' . $imei . '%');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public function lastReadingAt(string|array $lastReadingAt)
|
|
101
|
+
{
|
|
102
|
+
$this->filterDate('last_reading_at', $lastReadingAt);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public function createdAt(string|array $createdAt)
|
|
106
|
+
{
|
|
107
|
+
$this->filterDate('created_at', $createdAt);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public function updatedAt(string|array $updatedAt)
|
|
111
|
+
{
|
|
112
|
+
$this->filterDate('updated_at', $updatedAt);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
protected function filterDate(string $column, string|array $value): void
|
|
116
|
+
{
|
|
117
|
+
$dates = Utils::dateRange($value);
|
|
118
|
+
|
|
119
|
+
if (is_array($dates)) {
|
|
120
|
+
$this->builder->whereBetween($column, $dates);
|
|
121
|
+
} else {
|
|
122
|
+
$this->builder->whereDate($column, $dates);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -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
|
|
|
@@ -59,6 +60,12 @@ class VehicleFilter extends Filter
|
|
|
59
60
|
|
|
60
61
|
public function driver(?string $driverId)
|
|
61
62
|
{
|
|
63
|
+
if ($driverId === 'unassigned') {
|
|
64
|
+
$this->builder->whereDoesntHave('driver');
|
|
65
|
+
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
62
69
|
$this->builder->whereHas(
|
|
63
70
|
'driver',
|
|
64
71
|
function ($query) use ($driverId) {
|
|
@@ -125,4 +132,16 @@ class VehicleFilter extends Filter
|
|
|
125
132
|
$this->builder->whereDoesntHave('fleets');
|
|
126
133
|
}
|
|
127
134
|
}
|
|
135
|
+
|
|
136
|
+
public function telematicUuid(?string $telematic)
|
|
137
|
+
{
|
|
138
|
+
if (!$telematic) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
$this->builder->whereHas('devices', function ($query) use ($telematic) {
|
|
143
|
+
$query->where('telematic_uuid', $telematic);
|
|
144
|
+
$query->whereIn('attachable_type', ['fleet-ops:vehicle', Vehicle::class]);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
128
147
|
}
|
|
@@ -28,19 +28,20 @@ class CreateDriverRequest extends FleetbaseRequest
|
|
|
28
28
|
$isCreating = $this->isMethod('POST');
|
|
29
29
|
|
|
30
30
|
return [
|
|
31
|
-
'name'
|
|
32
|
-
'email'
|
|
33
|
-
'phone'
|
|
34
|
-
'password'
|
|
35
|
-
'country'
|
|
36
|
-
'city'
|
|
37
|
-
'vehicle'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'
|
|
31
|
+
'name' => [Rule::requiredIf($isCreating)],
|
|
32
|
+
'email' => [Rule::requiredIf($isCreating), Rule::when($this->filled('email'), ['email']), Rule::when($isCreating, [Rule::unique('users')->whereNull('deleted_at')])],
|
|
33
|
+
'phone' => [Rule::requiredIf($isCreating), Rule::when($isCreating, [Rule::unique('users')->whereNull('deleted_at')])],
|
|
34
|
+
'password' => 'nullable|string',
|
|
35
|
+
'country' => 'nullable|size:2',
|
|
36
|
+
'city' => 'nullable|string',
|
|
37
|
+
'vehicle' => 'nullable|string|starts_with:vehicle_|exists:vehicles,public_id',
|
|
38
|
+
'license_expiry' => 'nullable|date',
|
|
39
|
+
'status' => 'nullable|string|in:active,available,inactive',
|
|
40
|
+
'vendor' => 'nullable|exists:vendors,public_id',
|
|
41
|
+
'job' => 'nullable|exists:orders,public_id',
|
|
42
|
+
'location' => ['nullable', new ResolvablePoint()],
|
|
43
|
+
'latitude' => ['nullable', 'required_with:longitude'],
|
|
44
|
+
'longitude' => ['nullable', 'required_with:latitude'],
|
|
44
45
|
];
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -51,6 +51,7 @@ class CreateDriverRequest extends CreateDriverApiRequest
|
|
|
51
51
|
'country' => 'nullable|string|size:2',
|
|
52
52
|
'city' => 'nullable|string|max:255',
|
|
53
53
|
'vehicle' => ['nullable', new ResolvableVehicle()],
|
|
54
|
+
'license_expiry' => 'nullable|date',
|
|
54
55
|
'status' => 'nullable|string|in:active,available,inactive',
|
|
55
56
|
'vendor' => 'nullable|exists:vendors,public_id',
|
|
56
57
|
'job' => 'nullable|exists:orders,public_id',
|
|
@@ -38,7 +38,7 @@ class Driver extends FleetbaseResource
|
|
|
38
38
|
'email' => $this->email,
|
|
39
39
|
'phone' => $this->phone,
|
|
40
40
|
'drivers_license_number' => $this->drivers_license_number,
|
|
41
|
-
'license_expiry' => $this->license_expiry,
|
|
41
|
+
'license_expiry' => $this->formatDateOnly($this->license_expiry),
|
|
42
42
|
'photo_url' => $this->photo_url,
|
|
43
43
|
'avatar_url' => $this->avatar_url,
|
|
44
44
|
'avatar_value' => $this->when(Http::isInternalRequest(), $this->getOriginal('avatar_url')),
|
|
@@ -96,6 +96,15 @@ class Driver extends FleetbaseResource
|
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
protected function formatDateOnly($date): ?string
|
|
100
|
+
{
|
|
101
|
+
if (!$date) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return method_exists($date, 'toDateString') ? $date->toDateString() : (string) $date;
|
|
106
|
+
}
|
|
107
|
+
|
|
99
108
|
/**
|
|
100
109
|
* Transform the resource into an webhook payload.
|
|
101
110
|
*
|
|
@@ -110,7 +119,7 @@ class Driver extends FleetbaseResource
|
|
|
110
119
|
'email' => $this->email,
|
|
111
120
|
'phone' => $this->phone,
|
|
112
121
|
'photo_url' => $this->photo_url,
|
|
113
|
-
'license_expiry' => $this->license_expiry,
|
|
122
|
+
'license_expiry' => $this->formatDateOnly($this->license_expiry),
|
|
114
123
|
'vehicle' => data_get($this, 'vehicle.public_id'),
|
|
115
124
|
'current_job' => data_get($this, 'currentJob.public_id'),
|
|
116
125
|
'vendor' => data_get($this, 'vendor.public_id'),
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Jobs;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Events\OrderReady;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
7
|
+
use Fleetbase\FleetOps\Models\ServiceQuote;
|
|
8
|
+
use Illuminate\Bus\Queueable;
|
|
9
|
+
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
10
|
+
use Illuminate\Foundation\Bus\Dispatchable;
|
|
11
|
+
use Illuminate\Queue\InteractsWithQueue;
|
|
12
|
+
use Illuminate\Queue\SerializesModels;
|
|
13
|
+
|
|
14
|
+
class FinalizeApiOrderCreation implements ShouldQueue
|
|
15
|
+
{
|
|
16
|
+
use Dispatchable;
|
|
17
|
+
use InteractsWithQueue;
|
|
18
|
+
use Queueable;
|
|
19
|
+
use SerializesModels;
|
|
20
|
+
|
|
21
|
+
public function __construct(
|
|
22
|
+
public string $orderUuid,
|
|
23
|
+
public ?string $serviceQuoteUuid = null,
|
|
24
|
+
public bool $shouldDispatch = false,
|
|
25
|
+
) {
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public function handle(): void
|
|
29
|
+
{
|
|
30
|
+
$order = Order::where('uuid', $this->orderUuid)->first();
|
|
31
|
+
if (!$order) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
$serviceQuote = $this->serviceQuoteUuid ? ServiceQuote::where('uuid', $this->serviceQuoteUuid)->first() : null;
|
|
36
|
+
|
|
37
|
+
$order->notifyDriverAssigned();
|
|
38
|
+
$order->setPreliminaryDistanceAndTime();
|
|
39
|
+
$order->purchaseServiceQuote($serviceQuote);
|
|
40
|
+
|
|
41
|
+
if ($this->shouldDispatch) {
|
|
42
|
+
$order->dispatchWithActivity();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
event(new OrderReady($order));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -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
|
+
}
|