@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
|
@@ -115,7 +115,7 @@ class ServiceQuoteController extends FleetOpsController
|
|
|
115
115
|
]);
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
-
$quote->setRelation('items', $items);
|
|
118
|
+
$quote->setRelation('items', $quote->items()->get());
|
|
119
119
|
|
|
120
120
|
// if single quotation requested
|
|
121
121
|
if ($single) {
|
|
@@ -161,7 +161,7 @@ class ServiceQuoteController extends FleetOpsController
|
|
|
161
161
|
]);
|
|
162
162
|
});
|
|
163
163
|
|
|
164
|
-
$quote->setRelation('items', $items);
|
|
164
|
+
$quote->setRelation('items', $quote->items()->get());
|
|
165
165
|
$serviceQuotes->push($quote);
|
|
166
166
|
}
|
|
167
167
|
|
|
@@ -298,7 +298,7 @@ class ServiceQuoteController extends FleetOpsController
|
|
|
298
298
|
]);
|
|
299
299
|
});
|
|
300
300
|
|
|
301
|
-
$quote->setRelation('items', $items);
|
|
301
|
+
$quote->setRelation('items', $quote->items()->get());
|
|
302
302
|
$serviceQuotes->push($quote);
|
|
303
303
|
|
|
304
304
|
// if requesting single
|
|
@@ -346,7 +346,7 @@ class ServiceQuoteController extends FleetOpsController
|
|
|
346
346
|
]);
|
|
347
347
|
});
|
|
348
348
|
|
|
349
|
-
$quote->setRelation('items', $items);
|
|
349
|
+
$quote->setRelation('items', $quote->items()->get());
|
|
350
350
|
$serviceQuotes->push($quote);
|
|
351
351
|
}
|
|
352
352
|
|
|
@@ -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) {
|
|
@@ -44,6 +65,50 @@ class DeviceEventFilter extends Filter
|
|
|
44
65
|
});
|
|
45
66
|
}
|
|
46
67
|
|
|
68
|
+
public function deviceUuid(?string $device)
|
|
69
|
+
{
|
|
70
|
+
$this->device($device);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public function severity(string|array $severity)
|
|
74
|
+
{
|
|
75
|
+
$severity = Utils::arrayFrom($severity);
|
|
76
|
+
|
|
77
|
+
if ($severity) {
|
|
78
|
+
$this->builder->whereIn('severity', $severity);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public function processed(string|array $processed)
|
|
83
|
+
{
|
|
84
|
+
$states = Utils::arrayFrom($processed);
|
|
85
|
+
|
|
86
|
+
if (!$states) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
$this->builder->where(function ($query) use ($states) {
|
|
91
|
+
foreach ($states as $state) {
|
|
92
|
+
match ($state) {
|
|
93
|
+
'processed' => $query->orWhereNotNull('processed_at'),
|
|
94
|
+
'unprocessed' => $query->orWhereNull('processed_at'),
|
|
95
|
+
default => null,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public function occurredAt($occurredAt)
|
|
102
|
+
{
|
|
103
|
+
$occurredAt = Utils::dateRange($occurredAt);
|
|
104
|
+
|
|
105
|
+
if (is_array($occurredAt)) {
|
|
106
|
+
$this->builder->whereBetween('occurred_at', $occurredAt);
|
|
107
|
+
} else {
|
|
108
|
+
$this->builder->whereDate('occurred_at', $occurredAt);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
47
112
|
public function createdAt($createdAt)
|
|
48
113
|
{
|
|
49
114
|
$createdAt = Utils::dateRange($createdAt);
|
|
@@ -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
|
|
|
@@ -31,6 +32,29 @@ class DeviceFilter extends Filter
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
public function deviceId(?string $deviceId)
|
|
36
|
+
{
|
|
37
|
+
if ($deviceId) {
|
|
38
|
+
$this->builder->where('device_id', 'like', '%' . $deviceId . '%');
|
|
39
|
+
}
|
|
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
|
+
|
|
34
58
|
public function telematic(?string $telematic)
|
|
35
59
|
{
|
|
36
60
|
$this->builder->where('telematic_uuid', $telematic);
|
|
@@ -61,6 +85,41 @@ class DeviceFilter extends Filter
|
|
|
61
85
|
$this->builder->where('attachable_uuid', $attachable);
|
|
62
86
|
}
|
|
63
87
|
|
|
88
|
+
public function vehicle(?string $vehicle)
|
|
89
|
+
{
|
|
90
|
+
if ($vehicle) {
|
|
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
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public function connectionStatus(string|array $connectionStatus)
|
|
102
|
+
{
|
|
103
|
+
$statuses = Utils::arrayFrom($connectionStatus);
|
|
104
|
+
|
|
105
|
+
if (!$statuses) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
$this->builder->where(function ($query) use ($statuses) {
|
|
110
|
+
foreach ($statuses as $status) {
|
|
111
|
+
match ($status) {
|
|
112
|
+
'online' => $query->orWhere('last_online_at', '>=', now()->subMinutes(10)),
|
|
113
|
+
'recently_offline' => $query->orWhereBetween('last_online_at', [now()->subMinutes(60), now()->subMinutes(10)]),
|
|
114
|
+
'offline' => $query->orWhereBetween('last_online_at', [now()->subDay(), now()->subMinutes(60)]),
|
|
115
|
+
'long_offline' => $query->orWhere('last_online_at', '<', now()->subDay()),
|
|
116
|
+
'never_connected' => $query->orWhereNull('last_online_at'),
|
|
117
|
+
default => null,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
64
123
|
public function attachmentState(?string $attachmentState)
|
|
65
124
|
{
|
|
66
125
|
if ($attachmentState === 'attached') {
|
|
@@ -71,4 +130,25 @@ class DeviceFilter extends Filter
|
|
|
71
130
|
$this->builder->whereNull('attachable_uuid');
|
|
72
131
|
}
|
|
73
132
|
}
|
|
133
|
+
|
|
134
|
+
public function lastOnlineAt(string|array $lastOnlineAt)
|
|
135
|
+
{
|
|
136
|
+
$this->filterDate('last_online_at', $lastOnlineAt);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public function updatedAt(string|array $updatedAt)
|
|
140
|
+
{
|
|
141
|
+
$this->filterDate('updated_at', $updatedAt);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
protected function filterDate(string $column, string|array $value): void
|
|
145
|
+
{
|
|
146
|
+
$dates = Utils::dateRange($value);
|
|
147
|
+
|
|
148
|
+
if (is_array($dates)) {
|
|
149
|
+
$this->builder->whereBetween($column, $dates);
|
|
150
|
+
} else {
|
|
151
|
+
$this->builder->whereDate($column, $dates);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
74
154
|
}
|
|
@@ -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
|
|
|
@@ -131,4 +132,16 @@ class VehicleFilter extends Filter
|
|
|
131
132
|
$this->builder->whereDoesntHave('fleets');
|
|
132
133
|
}
|
|
133
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
|
+
}
|
|
134
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'),
|
|
@@ -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');
|
|
@@ -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
|
+
}
|