@fleetbase/fleetops-engine 0.6.54 → 0.6.56
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/map-settings.hbs +1 -1
- 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.js +1 -1
- 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/device-event/details.hbs +192 -0
- package/addon/components/device-event/details.js +151 -0
- package/addon/components/driver/schedule.js +1 -1
- 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 +20 -6
- 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/telematic/details.hbs +1 -1
- package/addon/components/telematic/form.hbs +2 -2
- package/addon/components/telematic/settings.hbs +1 -1
- 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/events.js +1 -1
- package/addon/controllers/connectivity/devices/index/details/vehicle.js +50 -2
- package/addon/controllers/connectivity/devices/index.js +144 -129
- package/addon/controllers/connectivity/events/details.js +70 -0
- package/addon/controllers/connectivity/events/index.js +187 -11
- package/addon/controllers/connectivity/sensors/index.js +160 -14
- package/addon/controllers/connectivity/telematics/details/devices.js +126 -134
- package/addon/controllers/connectivity/telematics/details/events.js +1 -1
- package/addon/controllers/connectivity/telematics/details/sensors.js +4 -0
- 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/controllers/settings/map.js +15 -0
- package/addon/instance-initializers/register-vroom-allocation.js +1 -1
- 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 +28 -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/routes/management/vehicles/index/details.js +3 -1
- 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/map-adapter/google.js +25 -0
- package/addon/services/map-adapter/leaflet.js +1 -1
- package/addon/services/map-adapter-interface.js +1 -1
- package/addon/services/map-manager.js +4 -1
- package/addon/services/map-settings.js +15 -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 +9 -1
- package/addon/templates/connectivity/devices/index/details/vehicle.hbs +66 -2
- package/addon/templates/connectivity/events/details.hbs +40 -0
- package/addon/templates/management/vendors/index/details.hbs +2 -2
- package/addon/templates/settings/map.hbs +38 -9
- 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/leaflet-plugin-loader.js +5 -5
- package/addon/utils/map-drawer-dropdown-position.js +47 -0
- package/addon/utils/route-colors.js +1 -1
- 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/device-event/details.hbs +192 -0
- package/app/components/device-event/details.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/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/migrations/2026_06_23_000001_delete_bad_afaqy_telematics_sensors.php +34 -0
- package/server/src/Console/Commands/SyncTelematics.php +4 -4
- package/server/src/Exports/ContactExport.php +12 -5
- package/server/src/Exports/DeviceExport.php +92 -0
- package/server/src/Exports/DriverExport.php +28 -3
- package/server/src/Exports/EquipmentExport.php +94 -0
- package/server/src/Exports/FleetExport.php +22 -7
- package/server/src/Exports/FuelReportExport.php +16 -1
- package/server/src/Exports/IssueExport.php +25 -3
- package/server/src/Exports/MaintenanceExport.php +106 -0
- package/server/src/Exports/MaintenanceScheduleExport.php +99 -0
- package/server/src/Exports/OrderExport.php +24 -5
- package/server/src/Exports/PartExport.php +99 -0
- package/server/src/Exports/PlaceExport.php +33 -7
- package/server/src/Exports/SensorExport.php +94 -0
- package/server/src/Exports/ServiceAreaExport.php +31 -7
- package/server/src/Exports/ServiceRateExport.php +41 -1
- package/server/src/Exports/TelematicExport.php +90 -0
- package/server/src/Exports/VehicleExport.php +274 -8
- package/server/src/Exports/VendorExport.php +15 -2
- package/server/src/Exports/WorkOrderExport.php +92 -0
- package/server/src/Http/Controllers/Api/v1/Concerns/ResolvesFleetOpsApiResources.php +176 -0
- package/server/src/Http/Controllers/Api/v1/DeviceController.php +225 -0
- package/server/src/Http/Controllers/Api/v1/EquipmentController.php +114 -0
- package/server/src/Http/Controllers/Api/v1/FuelTransactionController.php +185 -0
- package/server/src/Http/Controllers/Api/v1/PartController.php +120 -0
- package/server/src/Http/Controllers/Api/v1/SensorController.php +132 -0
- package/server/src/Http/Controllers/Api/v1/WorkOrderController.php +161 -0
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +36 -1
- package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/PartController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/SensorController.php +18 -0
- package/server/src/Http/Controllers/Internal/v1/ServiceRateController.php +82 -13
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +29 -5
- package/server/src/Http/Controllers/Internal/v1/TelematicController.php +18 -0
- package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +17 -0
- package/server/src/Http/Filter/DeviceEventFilter.php +21 -0
- package/server/src/Http/Filter/DeviceFilter.php +50 -4
- package/server/src/Http/Filter/EquipmentFilter.php +23 -0
- package/server/src/Http/Filter/FuelProviderTransactionFilter.php +57 -2
- package/server/src/Http/Filter/PartFilter.php +47 -0
- package/server/src/Http/Filter/SensorFilter.php +34 -6
- package/server/src/Http/Filter/WorkOrderFilter.php +23 -0
- package/server/src/Http/Requests/CreateDeviceRequest.php +51 -0
- package/server/src/Http/Requests/CreateEquipmentRequest.php +35 -0
- package/server/src/Http/Requests/CreateFuelTransactionRequest.php +50 -0
- package/server/src/Http/Requests/CreatePartRequest.php +40 -0
- package/server/src/Http/Requests/CreateSensorRequest.php +47 -0
- package/server/src/Http/Requests/CreateWorkOrderRequest.php +42 -0
- package/server/src/Http/Requests/UpdateDeviceRequest.php +7 -0
- package/server/src/Http/Requests/UpdateEquipmentRequest.php +7 -0
- package/server/src/Http/Requests/UpdateFuelTransactionRequest.php +7 -0
- package/server/src/Http/Requests/UpdatePartRequest.php +7 -0
- package/server/src/Http/Requests/UpdateSensorRequest.php +7 -0
- package/server/src/Http/Requests/UpdateWorkOrderRequest.php +7 -0
- package/server/src/Http/Resources/v1/Device.php +72 -0
- package/server/src/Http/Resources/v1/Equipment.php +57 -0
- package/server/src/Http/Resources/v1/FuelTransaction.php +69 -0
- package/server/src/Http/Resources/v1/Index/Vehicle.php +19 -0
- package/server/src/Http/Resources/v1/Part.php +64 -0
- package/server/src/Http/Resources/v1/Sensor.php +68 -0
- package/server/src/Jobs/SyncTelematicDevicesJob.php +194 -38
- 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/Models/Order.php +2 -1
- package/server/src/Observers/PurchaseRateObserver.php +2 -1
- package/server/src/Support/Analytics/RevenueTrend.php +3 -8
- package/server/src/Support/Metrics/ActiveRevenueQuery.php +121 -0
- package/server/src/Support/Metrics/AvgOrderValueMetric.php +1 -7
- package/server/src/Support/Metrics/EarningsMetric.php +1 -10
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +105 -5
- 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 +567 -47
- package/server/src/Support/Telematics/Providers/SamsaraProvider.php +122 -14
- package/server/src/Support/Telematics/TelematicService.php +28 -9
- package/server/src/routes.php +67 -15
- package/server/tests/DeviceFilterTest.php +9 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +19 -2
- package/server/tests/EquipmentAssignmentMorphTypeTest.php +35 -0
- package/server/tests/ExportCoverageTest.php +79 -0
- package/server/tests/MetricsRegistryTest.php +45 -5
- package/server/tests/PublicFleetOpsRestEndpointsTest.php +191 -0
- package/server/tests/ServiceRateRouteValidationTest.php +101 -0
- package/server/tests/TelematicsHardeningTest.php +873 -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 +0 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Exports;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\WorkOrder;
|
|
6
|
+
use Maatwebsite\Excel\Concerns\FromCollection;
|
|
7
|
+
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
|
|
8
|
+
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
|
|
9
|
+
use Maatwebsite\Excel\Concerns\WithHeadings;
|
|
10
|
+
use Maatwebsite\Excel\Concerns\WithMapping;
|
|
11
|
+
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
|
12
|
+
|
|
13
|
+
class WorkOrderExport implements FromCollection, WithHeadings, WithMapping, WithColumnFormatting, ShouldAutoSize
|
|
14
|
+
{
|
|
15
|
+
protected array $selections = [];
|
|
16
|
+
|
|
17
|
+
public function __construct(array $selections = [])
|
|
18
|
+
{
|
|
19
|
+
$this->selections = $selections;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public function map($workOrder): array
|
|
23
|
+
{
|
|
24
|
+
return [
|
|
25
|
+
$workOrder->public_id,
|
|
26
|
+
$workOrder->code,
|
|
27
|
+
$workOrder->subject,
|
|
28
|
+
$workOrder->category,
|
|
29
|
+
$workOrder->status,
|
|
30
|
+
$workOrder->priority,
|
|
31
|
+
$workOrder->target_name,
|
|
32
|
+
$workOrder->assignee_name,
|
|
33
|
+
$workOrder->opened_at,
|
|
34
|
+
$workOrder->due_at,
|
|
35
|
+
$workOrder->closed_at,
|
|
36
|
+
$workOrder->completion_percentage,
|
|
37
|
+
$this->yesNo($workOrder->is_overdue),
|
|
38
|
+
$workOrder->days_until_due,
|
|
39
|
+
$workOrder->created_at,
|
|
40
|
+
$workOrder->updated_at,
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public function headings(): array
|
|
45
|
+
{
|
|
46
|
+
return [
|
|
47
|
+
'ID',
|
|
48
|
+
'Code',
|
|
49
|
+
'Subject',
|
|
50
|
+
'Category',
|
|
51
|
+
'Status',
|
|
52
|
+
'Priority',
|
|
53
|
+
'Target',
|
|
54
|
+
'Assignee',
|
|
55
|
+
'Opened At',
|
|
56
|
+
'Due At',
|
|
57
|
+
'Closed At',
|
|
58
|
+
'Completion Percentage',
|
|
59
|
+
'Overdue',
|
|
60
|
+
'Days Until Due',
|
|
61
|
+
'Date Created',
|
|
62
|
+
'Date Updated',
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public function columnFormats(): array
|
|
67
|
+
{
|
|
68
|
+
return [
|
|
69
|
+
'I' => NumberFormat::FORMAT_DATE_DDMMYYYY,
|
|
70
|
+
'J' => NumberFormat::FORMAT_DATE_DDMMYYYY,
|
|
71
|
+
'K' => NumberFormat::FORMAT_DATE_DDMMYYYY,
|
|
72
|
+
'O' => NumberFormat::FORMAT_DATE_DDMMYYYY,
|
|
73
|
+
'P' => NumberFormat::FORMAT_DATE_DDMMYYYY,
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public function collection()
|
|
78
|
+
{
|
|
79
|
+
$query = WorkOrder::where('company_uuid', session('company'))->with(['target', 'assignee']);
|
|
80
|
+
|
|
81
|
+
if ($this->selections) {
|
|
82
|
+
$query->whereIn('uuid', $this->selections);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return $query->get();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected function yesNo($value): string
|
|
89
|
+
{
|
|
90
|
+
return $value ? 'Yes' : 'No';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Contact;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Device;
|
|
7
|
+
use Fleetbase\FleetOps\Models\Driver;
|
|
8
|
+
use Fleetbase\FleetOps\Models\Equipment;
|
|
9
|
+
use Fleetbase\FleetOps\Models\FuelProviderConnection;
|
|
10
|
+
use Fleetbase\FleetOps\Models\FuelReport;
|
|
11
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
12
|
+
use Fleetbase\FleetOps\Models\Part;
|
|
13
|
+
use Fleetbase\FleetOps\Models\Telematic;
|
|
14
|
+
use Fleetbase\FleetOps\Models\Vehicle;
|
|
15
|
+
use Fleetbase\FleetOps\Models\Vendor;
|
|
16
|
+
use Fleetbase\FleetOps\Models\Warranty;
|
|
17
|
+
use Fleetbase\FleetOps\Support\Utils;
|
|
18
|
+
use Fleetbase\Models\File;
|
|
19
|
+
use Illuminate\Database\Eloquent\Model;
|
|
20
|
+
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
21
|
+
use Illuminate\Http\Request;
|
|
22
|
+
use Illuminate\Validation\ValidationException;
|
|
23
|
+
|
|
24
|
+
trait ResolvesFleetOpsApiResources
|
|
25
|
+
{
|
|
26
|
+
protected function resolveUuid(string $modelClass, ?string $id): ?string
|
|
27
|
+
{
|
|
28
|
+
if (empty($id)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return $this->resolveModel($modelClass, $id)->uuid;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
protected function resolveModel(string $modelClass, string $id): Model
|
|
36
|
+
{
|
|
37
|
+
$instance = new $modelClass();
|
|
38
|
+
$query = $modelClass::query()->where(function ($query) use ($id, $instance) {
|
|
39
|
+
$query->whereRaw('0 = 1');
|
|
40
|
+
|
|
41
|
+
if (in_array('public_id', $instance->getFillable()) || method_exists($instance, 'getPublicIdType')) {
|
|
42
|
+
$query->orWhere('public_id', $id);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (in_array('internal_id', $instance->getFillable())) {
|
|
46
|
+
$query->orWhere('internal_id', $id);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (session('company') && $this->modelHasColumn($instance, 'company_uuid')) {
|
|
51
|
+
$query->where($instance->qualifyColumn('company_uuid'), session('company'));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
$model = $query->first();
|
|
55
|
+
if ($model) {
|
|
56
|
+
return $model;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
throw (new ModelNotFoundException())->setModel($modelClass, $id);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
protected function resolveMorph(?string $type, ?string $id): array
|
|
63
|
+
{
|
|
64
|
+
if (empty($type) || empty($id)) {
|
|
65
|
+
return [null, null];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
$modelClass = Utils::getMutationType($type);
|
|
69
|
+
$model = $this->resolveModel($modelClass, $id);
|
|
70
|
+
|
|
71
|
+
return [$modelClass, $model->uuid];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
protected function rejectUuidIdentifiers(Request $request): void
|
|
75
|
+
{
|
|
76
|
+
$invalidKeys = array_unique(array_merge(
|
|
77
|
+
$this->collectUuidIdentifierKeys($request->query->all()),
|
|
78
|
+
$this->collectUuidIdentifierKeys($request->request->all())
|
|
79
|
+
));
|
|
80
|
+
|
|
81
|
+
if (empty($invalidKeys)) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
$messages = [];
|
|
86
|
+
foreach ($invalidKeys as $key) {
|
|
87
|
+
$messages[$key] = ['UUID identifiers are not accepted by the public API. Use public_id or internal_id values instead.'];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
throw ValidationException::withMessages($messages);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected function collectUuidIdentifierKeys(array $input, string $prefix = ''): array
|
|
94
|
+
{
|
|
95
|
+
$keys = [];
|
|
96
|
+
|
|
97
|
+
foreach ($input as $key => $value) {
|
|
98
|
+
$path = $prefix ? $prefix . '.' . $key : (string) $key;
|
|
99
|
+
|
|
100
|
+
if ($this->isUuidIdentifierKey((string) $key)) {
|
|
101
|
+
$keys[] = $path;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (is_array($value)) {
|
|
105
|
+
$keys = array_merge($keys, $this->collectUuidIdentifierKeys($value, $path));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return $keys;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
protected function isUuidIdentifierKey(string $key): bool
|
|
113
|
+
{
|
|
114
|
+
return preg_match('/(^uuid$|_uuid$|Uuid$|UUID$)/', $key) === 1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected function applyPublicIdRelation(array &$input, string $requestKey, string $column, string $modelClass, $request): void
|
|
118
|
+
{
|
|
119
|
+
if (!$request->exists($requestKey)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
$input[$column] = filled($request->input($requestKey))
|
|
124
|
+
? $this->resolveUuid($modelClass, $request->input($requestKey))
|
|
125
|
+
: null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
protected function allowedMorphTypes(): array
|
|
129
|
+
{
|
|
130
|
+
return [
|
|
131
|
+
'fleet-ops:vehicle' => Vehicle::class,
|
|
132
|
+
'vehicle' => Vehicle::class,
|
|
133
|
+
Vehicle::class => Vehicle::class,
|
|
134
|
+
'fleet-ops:driver' => Driver::class,
|
|
135
|
+
'driver' => Driver::class,
|
|
136
|
+
Driver::class => Driver::class,
|
|
137
|
+
'fleet-ops:equipment' => Equipment::class,
|
|
138
|
+
'equipment' => Equipment::class,
|
|
139
|
+
Equipment::class => Equipment::class,
|
|
140
|
+
'fleet-ops:part' => Part::class,
|
|
141
|
+
'part' => Part::class,
|
|
142
|
+
Part::class => Part::class,
|
|
143
|
+
'fleet-ops:vendor' => Vendor::class,
|
|
144
|
+
'vendor' => Vendor::class,
|
|
145
|
+
Vendor::class => Vendor::class,
|
|
146
|
+
'fleet-ops:contact' => Contact::class,
|
|
147
|
+
'contact' => Contact::class,
|
|
148
|
+
Contact::class => Contact::class,
|
|
149
|
+
'fleet-ops:device' => Device::class,
|
|
150
|
+
'device' => Device::class,
|
|
151
|
+
Device::class => Device::class,
|
|
152
|
+
'fleet-ops:telematic' => Telematic::class,
|
|
153
|
+
'telematic' => Telematic::class,
|
|
154
|
+
Telematic::class => Telematic::class,
|
|
155
|
+
'fleet-ops:warranty' => Warranty::class,
|
|
156
|
+
'warranty' => Warranty::class,
|
|
157
|
+
Warranty::class => Warranty::class,
|
|
158
|
+
'fleet-ops:fuel-report' => FuelReport::class,
|
|
159
|
+
'fuel-report' => FuelReport::class,
|
|
160
|
+
FuelReport::class => FuelReport::class,
|
|
161
|
+
'fleet-ops:fuel-provider-connection' => FuelProviderConnection::class,
|
|
162
|
+
'fuel-provider-connection' => FuelProviderConnection::class,
|
|
163
|
+
FuelProviderConnection::class => FuelProviderConnection::class,
|
|
164
|
+
'fleet-ops:order' => Order::class,
|
|
165
|
+
'order' => Order::class,
|
|
166
|
+
Order::class => Order::class,
|
|
167
|
+
'file' => File::class,
|
|
168
|
+
File::class => File::class,
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
protected function modelHasColumn(Model $model, string $column): bool
|
|
173
|
+
{
|
|
174
|
+
return in_array($column, $model->getFillable()) || $column === $model->getKeyName();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesFleetOpsApiResources;
|
|
6
|
+
use Fleetbase\FleetOps\Http\Requests\CreateDeviceRequest;
|
|
7
|
+
use Fleetbase\FleetOps\Http\Requests\UpdateDeviceRequest;
|
|
8
|
+
use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
|
|
9
|
+
use Fleetbase\FleetOps\Http\Resources\v1\Device as DeviceResource;
|
|
10
|
+
use Fleetbase\FleetOps\Models\Device;
|
|
11
|
+
use Fleetbase\FleetOps\Models\Telematic;
|
|
12
|
+
use Fleetbase\FleetOps\Models\Vehicle;
|
|
13
|
+
use Fleetbase\FleetOps\Models\Warranty;
|
|
14
|
+
use Fleetbase\FleetOps\Support\Utils;
|
|
15
|
+
use Fleetbase\Http\Controllers\Controller;
|
|
16
|
+
use Fleetbase\LaravelMysqlSpatial\Types\Point;
|
|
17
|
+
use Fleetbase\Models\File;
|
|
18
|
+
use Illuminate\Http\JsonResponse;
|
|
19
|
+
use Illuminate\Http\Request;
|
|
20
|
+
use Illuminate\Support\Facades\Log;
|
|
21
|
+
|
|
22
|
+
class DeviceController extends Controller
|
|
23
|
+
{
|
|
24
|
+
use ResolvesFleetOpsApiResources;
|
|
25
|
+
|
|
26
|
+
public function create(CreateDeviceRequest $request)
|
|
27
|
+
{
|
|
28
|
+
$this->rejectUuidIdentifiers($request);
|
|
29
|
+
|
|
30
|
+
$input = $this->input($request);
|
|
31
|
+
$input['company_uuid'] = session('company');
|
|
32
|
+
|
|
33
|
+
$device = Device::create($input)->load(['telematic', 'warranty', 'attachable', 'photo'])->loadCount('sensors');
|
|
34
|
+
|
|
35
|
+
return new DeviceResource($device);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public function update(string $id, UpdateDeviceRequest $request)
|
|
39
|
+
{
|
|
40
|
+
$this->rejectUuidIdentifiers($request);
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
$device = $this->resolveModel(Device::class, $id);
|
|
44
|
+
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
45
|
+
return response()->json(['error' => 'Device resource not found.'], 404);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
$device->update($this->input($request));
|
|
49
|
+
|
|
50
|
+
return new DeviceResource($device->refresh()->load(['telematic', 'warranty', 'attachable', 'photo'])->loadCount('sensors'));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public function query(Request $request)
|
|
54
|
+
{
|
|
55
|
+
$this->rejectUuidIdentifiers($request);
|
|
56
|
+
|
|
57
|
+
$results = Device::queryWithRequest($request, function (&$query) {
|
|
58
|
+
$query->with(['telematic', 'warranty', 'attachable', 'photo'])->withCount('sensors');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return DeviceResource::collection($results);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public function find(string $id)
|
|
65
|
+
{
|
|
66
|
+
try {
|
|
67
|
+
$device = $this->resolveModel(Device::class, $id)->load(['telematic', 'warranty', 'attachable', 'photo']);
|
|
68
|
+
$device->loadCount('sensors');
|
|
69
|
+
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
70
|
+
return response()->json(['error' => 'Device resource not found.'], 404);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return new DeviceResource($device);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public function delete(string $id)
|
|
77
|
+
{
|
|
78
|
+
try {
|
|
79
|
+
$device = $this->resolveModel(Device::class, $id);
|
|
80
|
+
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
81
|
+
return response()->json(['error' => 'Device resource not found.'], 404);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
$device->delete();
|
|
85
|
+
|
|
86
|
+
return new DeletedResource($device);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public function attach(Request $request, string $id): JsonResponse
|
|
90
|
+
{
|
|
91
|
+
$this->rejectUuidIdentifiers($request);
|
|
92
|
+
|
|
93
|
+
$request->validate([
|
|
94
|
+
'vehicle' => 'required_without:attachable|string|nullable',
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
$deviceId = $id;
|
|
98
|
+
$vehicleId = $request->input('vehicle') ?? $request->input('attachable');
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
$device = $this->resolveModel(Device::class, $deviceId);
|
|
102
|
+
$vehicle = $this->resolveModel(Vehicle::class, $vehicleId);
|
|
103
|
+
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
104
|
+
$this->logDeviceAttachmentLookupFailure('attach', $deviceId, $vehicleId);
|
|
105
|
+
|
|
106
|
+
return response()->json(['error' => 'Device or vehicle resource not found.'], 404);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
$device->attachTo($vehicle);
|
|
111
|
+
$device->load(['telematic', 'warranty', 'attachable', 'photo'])->loadCount('sensors');
|
|
112
|
+
} catch (\Throwable $e) {
|
|
113
|
+
$this->logDeviceAttachmentFailure('attach', $device, $vehicle, $e);
|
|
114
|
+
|
|
115
|
+
return response()->json(['error' => 'Unable to attach device to vehicle.'], 500);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return response()->json([
|
|
119
|
+
'status' => 'ok',
|
|
120
|
+
'device' => new DeviceResource($device),
|
|
121
|
+
]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public function detach(string $id): JsonResponse
|
|
125
|
+
{
|
|
126
|
+
try {
|
|
127
|
+
$device = $this->resolveModel(Device::class, $id);
|
|
128
|
+
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
129
|
+
$this->logDeviceAttachmentLookupFailure('detach', $id, null);
|
|
130
|
+
|
|
131
|
+
return response()->json(['error' => 'Device resource not found.'], 404);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
$device->detach();
|
|
136
|
+
$device->load(['telematic', 'warranty', 'attachable', 'photo'])->loadCount('sensors');
|
|
137
|
+
} catch (\Throwable $e) {
|
|
138
|
+
$this->logDeviceAttachmentFailure('detach', $device, null, $e);
|
|
139
|
+
|
|
140
|
+
return response()->json(['error' => 'Unable to detach device from vehicle.'], 500);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return response()->json([
|
|
144
|
+
'status' => 'ok',
|
|
145
|
+
'device' => new DeviceResource($device),
|
|
146
|
+
]);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
protected function input(Request $request): array
|
|
150
|
+
{
|
|
151
|
+
$input = $request->only([
|
|
152
|
+
'type',
|
|
153
|
+
'device_id',
|
|
154
|
+
'internal_id',
|
|
155
|
+
'imei',
|
|
156
|
+
'imsi',
|
|
157
|
+
'firmware_version',
|
|
158
|
+
'provider',
|
|
159
|
+
'name',
|
|
160
|
+
'model',
|
|
161
|
+
'location',
|
|
162
|
+
'manufacturer',
|
|
163
|
+
'serial_number',
|
|
164
|
+
'installation_date',
|
|
165
|
+
'last_maintenance_date',
|
|
166
|
+
'meta',
|
|
167
|
+
'data',
|
|
168
|
+
'options',
|
|
169
|
+
'online',
|
|
170
|
+
'status',
|
|
171
|
+
'data_frequency',
|
|
172
|
+
'notes',
|
|
173
|
+
'last_online_at',
|
|
174
|
+
]);
|
|
175
|
+
|
|
176
|
+
if ($request->filled(['latitude', 'longitude'])) {
|
|
177
|
+
$input['last_position'] = new Point($request->input('latitude'), $request->input('longitude'));
|
|
178
|
+
} elseif ($request->filled('last_position')) {
|
|
179
|
+
$input['last_position'] = Utils::getPointFromCoordinates($request->input('last_position'));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
$this->applyPublicIdRelation($input, 'telematic', 'telematic_uuid', Telematic::class, $request);
|
|
183
|
+
$this->applyPublicIdRelation($input, 'warranty', 'warranty_uuid', Warranty::class, $request);
|
|
184
|
+
$this->applyPublicIdRelation($input, 'photo', 'photo_uuid', File::class, $request);
|
|
185
|
+
|
|
186
|
+
if ($request->exists('attachable')) {
|
|
187
|
+
if (blank($request->input('attachable'))) {
|
|
188
|
+
$input['attachable_type'] = null;
|
|
189
|
+
$input['attachable_uuid'] = null;
|
|
190
|
+
} else {
|
|
191
|
+
[$type, $uuid] = $this->resolveMorph($request->input('attachable_type'), $request->input('attachable'));
|
|
192
|
+
$input['attachable_type'] = $type;
|
|
193
|
+
$input['attachable_uuid'] = $uuid;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return $input;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
protected function logDeviceAttachmentLookupFailure(string $action, string $deviceId, ?string $vehicleId): void
|
|
201
|
+
{
|
|
202
|
+
Log::warning('Public API device attachment lookup failed', [
|
|
203
|
+
'action' => $action,
|
|
204
|
+
'device_id' => $deviceId,
|
|
205
|
+
'vehicle_id' => $vehicleId,
|
|
206
|
+
'company_uuid' => session('company'),
|
|
207
|
+
'request_id' => request()->headers->get('X-Request-ID') ?? request()->headers->get('X-Correlation-ID'),
|
|
208
|
+
]);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
protected function logDeviceAttachmentFailure(string $action, Device $device, ?Vehicle $vehicle, \Throwable $exception): void
|
|
212
|
+
{
|
|
213
|
+
Log::error('Public API device attachment failed', [
|
|
214
|
+
'action' => $action,
|
|
215
|
+
'device_uuid' => $device->uuid,
|
|
216
|
+
'device_id' => $device->public_id,
|
|
217
|
+
'vehicle_uuid' => $vehicle?->uuid,
|
|
218
|
+
'vehicle_id' => $vehicle?->public_id,
|
|
219
|
+
'company_uuid' => session('company'),
|
|
220
|
+
'request_id' => request()->headers->get('X-Request-ID') ?? request()->headers->get('X-Correlation-ID'),
|
|
221
|
+
'exception_class' => get_class($exception),
|
|
222
|
+
'exception' => $exception->getMessage(),
|
|
223
|
+
]);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesFleetOpsApiResources;
|
|
6
|
+
use Fleetbase\FleetOps\Http\Requests\CreateEquipmentRequest;
|
|
7
|
+
use Fleetbase\FleetOps\Http\Requests\UpdateEquipmentRequest;
|
|
8
|
+
use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
|
|
9
|
+
use Fleetbase\FleetOps\Http\Resources\v1\Equipment as EquipmentResource;
|
|
10
|
+
use Fleetbase\FleetOps\Models\Equipment;
|
|
11
|
+
use Fleetbase\FleetOps\Models\Warranty;
|
|
12
|
+
use Fleetbase\Http\Controllers\Controller;
|
|
13
|
+
use Fleetbase\Models\File;
|
|
14
|
+
use Illuminate\Http\Request;
|
|
15
|
+
|
|
16
|
+
class EquipmentController extends Controller
|
|
17
|
+
{
|
|
18
|
+
use ResolvesFleetOpsApiResources;
|
|
19
|
+
|
|
20
|
+
public function create(CreateEquipmentRequest $request)
|
|
21
|
+
{
|
|
22
|
+
$this->rejectUuidIdentifiers($request);
|
|
23
|
+
|
|
24
|
+
$input = $this->input($request);
|
|
25
|
+
$input['company_uuid'] = session('company');
|
|
26
|
+
|
|
27
|
+
$equipment = Equipment::create($input)->load(['warranty', 'photo', 'equipable']);
|
|
28
|
+
|
|
29
|
+
return new EquipmentResource($equipment);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public function update(string $id, UpdateEquipmentRequest $request)
|
|
33
|
+
{
|
|
34
|
+
$this->rejectUuidIdentifiers($request);
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
$equipment = $this->resolveModel(Equipment::class, $id);
|
|
38
|
+
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
39
|
+
return response()->json(['error' => 'Equipment resource not found.'], 404);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
$equipment->update($this->input($request));
|
|
43
|
+
|
|
44
|
+
return new EquipmentResource($equipment->refresh()->load(['warranty', 'photo', 'equipable']));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public function query(Request $request)
|
|
48
|
+
{
|
|
49
|
+
$this->rejectUuidIdentifiers($request);
|
|
50
|
+
|
|
51
|
+
$results = Equipment::queryWithRequest($request, function (&$query) {
|
|
52
|
+
$query->with(['warranty', 'photo', 'equipable']);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return EquipmentResource::collection($results);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public function find(string $id)
|
|
59
|
+
{
|
|
60
|
+
try {
|
|
61
|
+
$equipment = $this->resolveModel(Equipment::class, $id)->load(['warranty', 'photo', 'equipable']);
|
|
62
|
+
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
63
|
+
return response()->json(['error' => 'Equipment resource not found.'], 404);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return new EquipmentResource($equipment);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public function delete(string $id)
|
|
70
|
+
{
|
|
71
|
+
try {
|
|
72
|
+
$equipment = $this->resolveModel(Equipment::class, $id);
|
|
73
|
+
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
74
|
+
return response()->json(['error' => 'Equipment resource not found.'], 404);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
$equipment->delete();
|
|
78
|
+
|
|
79
|
+
return new DeletedResource($equipment);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
protected function input(Request $request): array
|
|
83
|
+
{
|
|
84
|
+
$input = $request->only([
|
|
85
|
+
'name',
|
|
86
|
+
'code',
|
|
87
|
+
'type',
|
|
88
|
+
'status',
|
|
89
|
+
'serial_number',
|
|
90
|
+
'manufacturer',
|
|
91
|
+
'model',
|
|
92
|
+
'purchased_at',
|
|
93
|
+
'purchase_price',
|
|
94
|
+
'currency',
|
|
95
|
+
'meta',
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
$this->applyPublicIdRelation($input, 'warranty', 'warranty_uuid', Warranty::class, $request);
|
|
99
|
+
$this->applyPublicIdRelation($input, 'photo', 'photo_uuid', File::class, $request);
|
|
100
|
+
|
|
101
|
+
if ($request->exists('equipable')) {
|
|
102
|
+
if (blank($request->input('equipable'))) {
|
|
103
|
+
$input['equipable_type'] = null;
|
|
104
|
+
$input['equipable_uuid'] = null;
|
|
105
|
+
} else {
|
|
106
|
+
[$type, $uuid] = $this->resolveMorph($request->input('equipable_type'), $request->input('equipable'));
|
|
107
|
+
$input['equipable_type'] = $type;
|
|
108
|
+
$input['equipable_uuid'] = $uuid;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return $input;
|
|
113
|
+
}
|
|
114
|
+
}
|