@fleetbase/fleetops-engine 0.6.63 → 0.6.65
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/RELEASE.md +7 -6
- package/addon/components/attachment-list.hbs +65 -0
- package/addon/components/attachment-list.js +8 -0
- package/addon/components/cell/attached-vehicle.hbs +1 -1
- package/addon/components/cell/attached-vehicle.js +3 -3
- package/addon/components/cell/driver-identity.js +4 -4
- package/addon/components/cell/driver-name.hbs +1 -1
- package/addon/components/cell/translated-value.hbs +9 -0
- package/addon/components/cell/translated-value.js +57 -0
- package/addon/components/contact/card.hbs +58 -0
- package/addon/components/contact/card.js +19 -0
- package/addon/components/contact/form.hbs +1 -1
- package/addon/components/contact/panel-header.hbs +1 -1
- package/addon/components/customer/form.hbs +2 -2
- package/addon/components/device/manager.hbs +26 -28
- package/addon/components/device/manager.js +28 -2
- package/addon/components/device/panel-tabs/vehicle.hbs +13 -13
- package/addon/components/device/panel-tabs/vehicle.js +8 -0
- package/addon/components/driver/card.hbs +24 -13
- package/addon/components/driver/card.js +20 -6
- package/addon/components/driver/form.hbs +3 -3
- package/addon/components/driver/panel-header.hbs +2 -2
- package/addon/components/driver/pill.hbs +2 -2
- package/addon/components/equipment/form.js +11 -4
- package/addon/components/equipment/pill.hbs +20 -0
- package/addon/components/equipment/pill.js +3 -0
- package/addon/components/filter/multi-model.hbs +19 -0
- package/addon/components/filter/multi-model.js +90 -0
- package/addon/components/fleet/driver-listing.hbs +1 -1
- package/addon/components/fleet/vehicle-listing.hbs +1 -1
- package/addon/components/fleet-panel/driver-listing.hbs +1 -1
- package/addon/components/fleet-panel/vehicle-listing.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.js +9 -7
- package/addon/components/maintenance/form.js +12 -4
- package/addon/components/maintenance-schedule/form.js +13 -7
- package/addon/components/map/leaflet-live-map.hbs +52 -0
- package/addon/components/map/order-list-overlay/order.hbs +4 -4
- package/addon/components/modals/attach-equipment.hbs +22 -0
- package/addon/components/modals/attach-equipment.js +3 -0
- package/addon/components/modals/attach-telematic-device.hbs +13 -8
- package/addon/components/modals/attach-telematic-device.js +11 -1
- package/addon/components/modals/attach-trailer.hbs +30 -0
- package/addon/components/modals/attach-trailer.js +3 -0
- package/addon/components/modals/contact-form.hbs +2 -2
- package/addon/components/modals/convert-customer-to-vendor.hbs +1 -1
- package/addon/components/modals/driver-form.hbs +2 -2
- package/addon/components/modals/select-trailer.hbs +35 -0
- package/addon/components/modals/select-trailer.js +3 -0
- package/addon/components/modals/vehicle-form.hbs +2 -2
- package/addon/components/orchestrator/resource-panel.hbs +2 -2
- package/addon/components/order/customer-avatar-stack.hbs +2 -2
- package/addon/components/order/details/detail.hbs +4 -4
- package/addon/components/order/details/payload.hbs +2 -2
- package/addon/components/order/kanban-card.hbs +4 -4
- package/addon/components/order-list-overlay/order.hbs +4 -4
- package/addon/components/trailer/card.hbs +68 -0
- package/addon/components/trailer/card.js +22 -0
- package/addon/components/trailer/details/connections.hbs +57 -0
- package/addon/components/trailer/details/connections.js +59 -0
- package/addon/components/trailer/details/equipment.hbs +25 -0
- package/addon/components/trailer/details/equipment.js +52 -0
- package/addon/components/trailer/details/maintenance-history.hbs +47 -0
- package/addon/components/trailer/details/maintenance-history.js +40 -0
- package/addon/components/trailer/details/schedules.hbs +45 -0
- package/addon/components/trailer/details/schedules.js +40 -0
- package/addon/components/trailer/details/work-orders.hbs +47 -0
- package/addon/components/trailer/details/work-orders.js +40 -0
- package/addon/components/trailer/details.hbs +399 -0
- package/addon/components/trailer/details.js +118 -0
- package/addon/components/trailer/form.hbs +447 -0
- package/addon/components/trailer/form.js +165 -0
- package/addon/components/trailer/panel-header.hbs +45 -0
- package/addon/components/trailer/panel-header.js +3 -0
- package/addon/components/trailer/pill.hbs +21 -0
- package/addon/components/trailer/pill.js +3 -0
- package/addon/components/vehicle/card.hbs +42 -11
- package/addon/components/vehicle/card.js +31 -0
- package/addon/components/vehicle/details/equipment.hbs +25 -0
- package/addon/components/vehicle/details/equipment.js +55 -0
- package/addon/components/vehicle/details/trailers.hbs +35 -0
- package/addon/components/vehicle/details/trailers.js +55 -0
- package/addon/components/vehicle/form.hbs +2 -2
- package/addon/components/vehicle/panel-header.hbs +2 -2
- package/addon/components/vehicle/pill.hbs +2 -2
- package/addon/components/vendor/card.hbs +58 -0
- package/addon/components/vendor/card.js +6 -0
- package/addon/components/vendor/panel-header.hbs +2 -2
- package/addon/components/vendor/personnel-manager.hbs +2 -2
- package/addon/components/work-order/form.js +12 -4
- package/addon/controllers/connectivity/devices/index/details/vehicle.js +11 -0
- package/addon/controllers/connectivity/devices/index/details.js +3 -3
- package/addon/controllers/connectivity/devices/index.js +1 -0
- package/addon/controllers/connectivity/telematics/details/attachments.js +28 -47
- package/addon/controllers/connectivity/telematics/details/devices.js +1 -0
- package/addon/controllers/management/contacts/customers.js +4 -0
- package/addon/controllers/management/contacts/index.js +32 -0
- package/addon/controllers/management/trailers/index/details.js +119 -0
- package/addon/controllers/management/trailers/index/edit.js +67 -0
- package/addon/controllers/management/trailers/index/new.js +43 -0
- package/addon/controllers/management/trailers/index.js +599 -0
- package/addon/controllers/management/vehicles/index/details.js +16 -0
- package/addon/controllers/management/vehicles/index.js +78 -4
- package/addon/controllers/management/vendors/index.js +32 -0
- package/addon/controllers/operations/orders/index.js +3 -1
- package/addon/controllers/settings/custom-fields.js +8 -0
- package/addon/extension.js +7 -0
- package/addon/helpers/placeholder-image.js +6 -0
- package/addon/helpers/resource-image.js +10 -0
- package/addon/helpers/trailer-placeholder-image.js +6 -0
- package/addon/routes/management/trailers/index/details/connections.js +2 -0
- package/addon/routes/management/trailers/index/details/devices.js +2 -0
- package/addon/routes/management/trailers/index/details/equipment.js +2 -0
- package/addon/routes/management/trailers/index/details/index.js +2 -0
- package/addon/routes/management/trailers/index/details/maintenance-history.js +2 -0
- package/addon/routes/management/trailers/index/details/positions.js +2 -0
- package/addon/routes/management/trailers/index/details/schedules.js +2 -0
- package/addon/routes/management/trailers/index/details/work-orders.js +2 -0
- package/addon/routes/management/trailers/index/details.js +30 -0
- package/addon/routes/management/trailers/index/edit.js +23 -0
- package/addon/routes/management/trailers/index/new.js +14 -0
- package/addon/routes/management/trailers/index.js +42 -0
- package/addon/routes/management/vehicles/index/details/trailers.js +2 -0
- package/addon/routes/management/vehicles/index.js +7 -1
- package/addon/routes.js +17 -0
- package/addon/services/device-actions.js +26 -16
- package/addon/services/trailer-actions.js +394 -0
- package/addon/services/vehicle-actions.js +111 -0
- package/addon/styles/fleetops-engine.css +349 -0
- package/addon/templates/connectivity/devices/index/details/vehicle.hbs +19 -19
- package/addon/templates/connectivity/telematics/details/attachments.hbs +15 -15
- package/addon/templates/management/contacts/customers.hbs +48 -29
- package/addon/templates/management/contacts/index.hbs +52 -33
- package/addon/templates/management/trailers/index/details/connections.hbs +1 -0
- package/addon/templates/management/trailers/index/details/devices.hbs +1 -0
- package/addon/templates/management/trailers/index/details/equipment.hbs +1 -0
- package/addon/templates/management/trailers/index/details/index.hbs +1 -0
- package/addon/templates/management/trailers/index/details/maintenance-history.hbs +1 -0
- package/addon/templates/management/trailers/index/details/positions.hbs +1 -0
- package/addon/templates/management/trailers/index/details/schedules.hbs +1 -0
- package/addon/templates/management/trailers/index/details/work-orders.hbs +1 -0
- package/addon/templates/management/trailers/index/details.hbs +15 -0
- package/addon/templates/management/trailers/index/edit.hbs +12 -0
- package/addon/templates/management/trailers/index/new.hbs +11 -0
- package/addon/templates/management/trailers/index.hbs +67 -0
- package/addon/templates/management/vehicles/index/details/equipment.hbs +1 -1
- package/addon/templates/management/vehicles/index/details/trailers.hbs +1 -0
- package/addon/templates/management/vehicles/index/details.hbs +1 -1
- package/addon/templates/management/vendors/index.hbs +66 -32
- package/addon/utils/device-table-columns.js +10 -6
- package/addon/utils/live-map-card-content.js +56 -0
- package/addon/utils/placeholder-images.js +71 -0
- package/addon/utils/trailer-placeholder-image.js +11 -0
- package/app/components/attachment-list.js +1 -0
- package/app/components/cell/translated-value.js +1 -0
- package/app/components/contact/card.js +1 -0
- package/app/components/equipment/pill.js +1 -0
- package/app/components/filter/multi-model.js +1 -0
- package/app/components/modals/attach-equipment.js +1 -0
- package/app/components/modals/attach-trailer.js +1 -0
- package/app/components/modals/select-trailer.js +1 -0
- package/app/components/trailer/card.js +1 -0
- package/app/components/trailer/details/connections.js +1 -0
- package/app/components/trailer/details/equipment.js +1 -0
- package/app/components/trailer/details/maintenance-history.js +1 -0
- package/app/components/trailer/details/schedules.js +1 -0
- package/app/components/trailer/details/work-orders.js +1 -0
- package/app/components/trailer/details.js +1 -0
- package/app/components/trailer/form.js +1 -0
- package/app/components/trailer/panel-header.js +1 -0
- package/app/components/trailer/pill.js +1 -0
- package/app/components/vehicle/details/equipment.js +1 -0
- package/app/components/vehicle/details/trailers.js +1 -0
- package/app/components/vendor/card.js +1 -0
- package/app/controllers/management/trailers/index/details.js +1 -0
- package/app/controllers/management/trailers/index/edit.js +1 -0
- package/app/controllers/management/trailers/index/new.js +1 -0
- package/app/controllers/management/trailers/index.js +1 -0
- package/app/helpers/placeholder-image.js +1 -0
- package/app/helpers/resource-image.js +1 -0
- package/app/helpers/trailer-placeholder-image.js +1 -0
- package/app/routes/management/trailers/index/details/connections.js +1 -0
- package/app/routes/management/trailers/index/details/devices.js +1 -0
- package/app/routes/management/trailers/index/details/equipment.js +1 -0
- package/app/routes/management/trailers/index/details/index.js +1 -0
- package/app/routes/management/trailers/index/details/maintenance-history.js +1 -0
- package/app/routes/management/trailers/index/details/positions.js +1 -0
- package/app/routes/management/trailers/index/details/schedules.js +1 -0
- package/app/routes/management/trailers/index/details/work-orders.js +1 -0
- package/app/routes/management/trailers/index/details.js +1 -0
- package/app/routes/management/trailers/index/edit.js +1 -0
- package/app/routes/management/trailers/index/new.js +1 -0
- package/app/routes/management/trailers/index.js +1 -0
- package/app/routes/management/vehicles/index/details/trailers.js +1 -0
- package/app/services/trailer-actions.js +1 -0
- package/app/templates/management/trailers/index/details/connections.js +1 -0
- package/app/templates/management/trailers/index/details/devices.js +1 -0
- package/app/templates/management/trailers/index/details/equipment.js +1 -0
- package/app/templates/management/trailers/index/details/index.js +1 -0
- package/app/templates/management/trailers/index/details/maintenance-history.js +1 -0
- package/app/templates/management/trailers/index/details/positions.js +1 -0
- package/app/templates/management/trailers/index/details/schedules.js +1 -0
- package/app/templates/management/trailers/index/details/work-orders.js +1 -0
- package/app/templates/management/trailers/index/details.js +1 -0
- package/app/templates/management/trailers/index/edit.js +1 -0
- package/app/templates/management/trailers/index/new.js +1 -0
- package/app/templates/management/trailers/index.js +1 -0
- package/app/templates/management/vehicles/index/details/trailers.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +1 -1
- package/server/migrations/2026_09_03_000001_add_first_class_trailer_columns_to_assets_table.php +58 -0
- package/server/migrations/2026_09_03_000002_create_asset_connections_table.php +43 -0
- package/server/migrations/2026_09_03_000003_create_device_installations_table.php +33 -0
- package/server/src/Auth/Schemas/FleetOps.php +7 -0
- package/server/src/Events/TrailerLocationChanged.php +70 -0
- package/server/src/Exceptions/DeviceAlreadyAttachedException.php +18 -0
- package/server/src/Exports/TrailerExport.php +31 -0
- package/server/src/Http/Controllers/Api/v1/Concerns/ResolvesFleetOpsApiResources.php +8 -2
- package/server/src/Http/Controllers/Api/v1/DeviceController.php +22 -15
- package/server/src/Http/Controllers/Api/v1/EquipmentController.php +47 -0
- package/server/src/Http/Controllers/Api/v1/TrailerController.php +362 -0
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +1 -0
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +3 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/SearchController.php +16 -0
- package/server/src/Http/Controllers/Internal/v1/TrailerController.php +360 -0
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +74 -0
- package/server/src/Http/Filter/EquipmentFilter.php +51 -0
- package/server/src/Http/Filter/OrderFilter.php +15 -0
- package/server/src/Http/Filter/TrailerFilter.php +269 -0
- package/server/src/Http/Filter/VehicleFilter.php +70 -3
- package/server/src/Http/Requests/CreateDeviceRequest.php +1 -1
- package/server/src/Http/Requests/CreateEquipmentRequest.php +1 -1
- package/server/src/Http/Requests/CreateTrailerRequest.php +54 -0
- package/server/src/Http/Requests/UpdateTrailerRequest.php +7 -0
- package/server/src/Http/Resources/v1/AssetConnection.php +90 -0
- package/server/src/Http/Resources/v1/Concerns/ResolvesPublicRelationFields.php +29 -3
- package/server/src/Http/Resources/v1/Device.php +3 -1
- package/server/src/Http/Resources/v1/Equipment.php +3 -1
- package/server/src/Http/Resources/v1/Fleet.php +3 -1
- package/server/src/Http/Resources/v1/Index/Vehicle.php +10 -0
- package/server/src/Http/Resources/v1/Trailer.php +199 -0
- package/server/src/Http/Resources/v1/Vehicle.php +2 -0
- package/server/src/Http/Resources/v1/VehicleWithoutDriver.php +3 -0
- package/server/src/Imports/TrailerImport.php +33 -0
- package/server/src/Models/Asset.php +39 -10
- package/server/src/Models/AssetConnection.php +43 -0
- package/server/src/Models/Device.php +39 -8
- package/server/src/Models/DeviceInstallation.php +27 -0
- package/server/src/Models/Equipment.php +2 -0
- package/server/src/Models/Trailer.php +181 -0
- package/server/src/Models/Vehicle.php +13 -0
- package/server/src/Providers/FleetOpsServiceProvider.php +4 -2
- package/server/src/Support/Ai/FleetOpsAiQueryResources.php +22 -0
- package/server/src/Support/Reporting/FleetOpsReportSchema.php +38 -0
- package/server/src/Support/Telematics/TelematicService.php +35 -0
- package/server/src/Support/TrailerSummary.php +35 -0
- package/server/src/routes.php +29 -0
- package/server/tests/CompactResourceSerializationTest.php +36 -0
- package/server/tests/ControllerFilterContractsTest.php +29 -0
- package/server/tests/Feature/Http/Api/DeviceControllerContractsTest.php +30 -13
- package/server/tests/Feature/Http/Api/TrailerControllerLifecycleTest.php +640 -0
- package/server/tests/Feature/Http/Api/VehiclePublicContractTest.php +23 -0
- package/server/tests/Feature/Http/Internal/DeviceControllerContractsTest.php +20 -5
- package/server/tests/Feature/Http/Internal/LiveControllerEndpointsTest.php +19 -0
- package/server/tests/ModelAccessorContractsTest.php +10 -0
- package/server/tests/OrderFilterExecutionTest.php +5 -0
- package/server/tests/PublicApiControllerInputContractsTest.php +11 -0
- package/server/tests/PublicFleetOpsRestEndpointsTest.php +15 -3
- package/server/tests/ReportSchemaContractsTest.php +4 -0
- package/server/tests/TelematicsHardeningTest.php +4 -2
- package/server/tests/TrailerSupportCoverageTest.php +524 -0
- package/server/tests/Unit/Http/Resources/FleetResourceTest.php +17 -0
- package/server/tests/Unit/Http/Resources/IndexVehicleResourceTest.php +35 -0
- package/server/tests/Unit/Models/EquipmentTest.php +22 -0
- package/server/tests/Unit/Models/ModelLookupFallbackSeamsTest.php +7 -3
- package/server/tests/Unit/Models/TrailerTest.php +204 -0
- package/server/tests/Unit/Support/Telematics/TelematicServiceTelemetryTest.php +93 -0
- package/server/tests/VehicleControllerHelperContractsTest.php +14 -0
- package/server/tests/VehicleResourceContractsTest.php +39 -0
- package/translations/ar-ae.yml +396 -0
- package/translations/bg-bg.yaml +424 -0
- package/translations/en-us.yaml +423 -0
- package/translations/es-pa.yaml +425 -0
- package/translations/fr-fr.yaml +424 -0
- package/translations/mn-mn.yaml +424 -0
- package/translations/pt-br.yaml +424 -0
- package/translations/ru-ru.yaml +424 -0
- package/translations/vi-vn.yaml +435 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Filter;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Device;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Vehicle;
|
|
7
|
+
use Fleetbase\FleetOps\Models\Vendor;
|
|
8
|
+
use Fleetbase\FleetOps\Support\Utils;
|
|
9
|
+
use Fleetbase\Http\Filter\Filter;
|
|
10
|
+
use Fleetbase\Models\Category;
|
|
11
|
+
use Fleetbase\Support\Http;
|
|
12
|
+
|
|
13
|
+
class TrailerFilter extends Filter
|
|
14
|
+
{
|
|
15
|
+
public function queryForInternal()
|
|
16
|
+
{
|
|
17
|
+
$this->builder->where('company_uuid', $this->session->get('company'));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public function queryForPublic()
|
|
21
|
+
{
|
|
22
|
+
$this->queryForInternal();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public function query(?string $value)
|
|
26
|
+
{
|
|
27
|
+
$this->builder->search($value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public function publicId(?string $value)
|
|
31
|
+
{
|
|
32
|
+
$this->builder->searchWhere('public_id', $value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public function name(?string $value)
|
|
36
|
+
{
|
|
37
|
+
$this->builder->searchWhere('name', $value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public function code(?string $value)
|
|
41
|
+
{
|
|
42
|
+
$this->builder->searchWhere('code', $value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public function trailerType($value)
|
|
46
|
+
{
|
|
47
|
+
$this->whereOneOf('type', $value);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public function status($value)
|
|
51
|
+
{
|
|
52
|
+
$this->whereOneOf('status', $value);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public function trailerMake(?string $value)
|
|
56
|
+
{
|
|
57
|
+
$this->builder->searchWhere('make', $value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public function trailerModel(?string $value)
|
|
61
|
+
{
|
|
62
|
+
$this->builder->searchWhere('model', $value);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public function trailerYear($value)
|
|
66
|
+
{
|
|
67
|
+
$this->whereOneOf('year', $value);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public function plateNumber(?string $value)
|
|
71
|
+
{
|
|
72
|
+
$this->builder->searchWhere('plate_number', $value);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public function vin(?string $value)
|
|
76
|
+
{
|
|
77
|
+
$this->builder->searchWhere('vin', $value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public function serialNumber(?string $value)
|
|
81
|
+
{
|
|
82
|
+
$this->builder->searchWhere('serial_number', $value);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public function bodyType(?string $value)
|
|
86
|
+
{
|
|
87
|
+
$this->builder->searchWhere('body_type', $value);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public function length($value)
|
|
91
|
+
{
|
|
92
|
+
$this->whereOneOf('length', $value);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public function axleCount($value)
|
|
96
|
+
{
|
|
97
|
+
$this->whereOneOf('axle_count', $value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public function gvwr($value)
|
|
101
|
+
{
|
|
102
|
+
$this->whereOneOf('gvwr', $value);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public function payloadCapacity($value)
|
|
106
|
+
{
|
|
107
|
+
$this->whereOneOf('payload_capacity', $value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public function ownershipType($value)
|
|
111
|
+
{
|
|
112
|
+
$this->whereOneOf('ownership_type', $value);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public function refrigerated($value)
|
|
116
|
+
{
|
|
117
|
+
$this->builder->where('refrigerated', filter_var($value, FILTER_VALIDATE_BOOLEAN));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public function devicesCount($value)
|
|
121
|
+
{
|
|
122
|
+
$this->builder->has('devices', '=', (int) $value);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public function equipmentCount($value)
|
|
126
|
+
{
|
|
127
|
+
$this->builder->has('equipments', '=', (int) $value);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Connectivity is derived from `last_online_at`, mirroring
|
|
132
|
+
* Trailer::getConnectivityStatusAttribute(). Multiple states combine with OR.
|
|
133
|
+
*/
|
|
134
|
+
public function connectivityStatus($value)
|
|
135
|
+
{
|
|
136
|
+
$states = array_values(array_filter(array_map('strval', (array) $value), 'strlen'));
|
|
137
|
+
|
|
138
|
+
if (empty($states)) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
$this->builder->where(function ($query) use ($states) {
|
|
143
|
+
foreach ($states as $state) {
|
|
144
|
+
$query->orWhere(function ($query) use ($state) {
|
|
145
|
+
match ($state) {
|
|
146
|
+
'never_connected' => $query->whereNull('last_online_at'),
|
|
147
|
+
'online' => $query->where('last_online_at', '>=', now()->subMinutes(10)),
|
|
148
|
+
'recently_offline' => $query->whereBetween('last_online_at', [now()->subDay(), now()->subMinutes(10)]),
|
|
149
|
+
'offline' => $query->where('last_online_at', '<', now()->subDay()),
|
|
150
|
+
default => $query->whereRaw('0 = 1'),
|
|
151
|
+
};
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public function attachmentState($value)
|
|
158
|
+
{
|
|
159
|
+
$states = array_unique(array_values(array_filter(array_map('strval', (array) $value), 'strlen')));
|
|
160
|
+
|
|
161
|
+
// Asking for both states is the same as not filtering.
|
|
162
|
+
if (empty($states) || count($states) > 1) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
$method = $states[0] === 'attached' ? 'whereHas' : 'whereDoesntHave';
|
|
167
|
+
$this->builder->{$method}('currentConnection');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public function vehicle($value)
|
|
171
|
+
{
|
|
172
|
+
$uuids = $this->resolveRelationUuids(Vehicle::class, $value);
|
|
173
|
+
|
|
174
|
+
$this->builder->whereHas('currentConnection', fn ($query) => $query->whereIn('connector_uuid', $uuids));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public function vendor($value)
|
|
178
|
+
{
|
|
179
|
+
$this->builder->whereIn('vendor_uuid', $this->resolveRelationUuids(Vendor::class, $value));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Trailers with any of the given devices installed.
|
|
184
|
+
*/
|
|
185
|
+
public function device($value)
|
|
186
|
+
{
|
|
187
|
+
$uuids = $this->resolveRelationUuids(Device::class, $value);
|
|
188
|
+
|
|
189
|
+
$this->builder->whereHas('devices', fn ($query) => $query->whereIn('uuid', $uuids));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public function category($value)
|
|
193
|
+
{
|
|
194
|
+
$this->builder->whereIn('category_uuid', $this->resolveRelationUuids(Category::class, $value, false));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public function createdAt($value)
|
|
198
|
+
{
|
|
199
|
+
$this->dateFilter('created_at', $value);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public function updatedAt($value)
|
|
203
|
+
{
|
|
204
|
+
$this->dateFilter('updated_at', $value);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public function lastOnlineAt($value)
|
|
208
|
+
{
|
|
209
|
+
$this->dateFilter('last_online_at', $value);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public function purchasedAt($value)
|
|
213
|
+
{
|
|
214
|
+
$this->dateFilter('purchased_at', $value);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Console multi-option filters submit arrays; the public API submits scalars.
|
|
219
|
+
*/
|
|
220
|
+
private function whereOneOf(string $column, $value): void
|
|
221
|
+
{
|
|
222
|
+
$values = array_values(array_filter(array_map('strval', (array) $value), 'strlen'));
|
|
223
|
+
|
|
224
|
+
if (empty($values)) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
count($values) === 1 ? $this->builder->where($column, $values[0]) : $this->builder->whereIn($column, $values);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Resolve related record identifiers to UUIDs. The console filters by the record's
|
|
233
|
+
* UUID; the public API filters by public id (or a vehicle's internal id).
|
|
234
|
+
*/
|
|
235
|
+
private function resolveRelationUuids(string $modelClass, $identifiers, bool $scopeToCompany = true): array
|
|
236
|
+
{
|
|
237
|
+
$identifiers = array_values(array_filter(array_map('strval', (array) $identifiers), 'strlen'));
|
|
238
|
+
|
|
239
|
+
if (empty($identifiers)) {
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
$instance = new $modelClass();
|
|
244
|
+
$query = $modelClass::query()->where(function ($query) use ($identifiers, $instance) {
|
|
245
|
+
$query->whereIn('public_id', $identifiers);
|
|
246
|
+
|
|
247
|
+
if (in_array('internal_id', $instance->getFillable(), true)) {
|
|
248
|
+
$query->orWhereIn('internal_id', $identifiers);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (Http::isInternalRequest($this->request)) {
|
|
252
|
+
$query->orWhereIn('uuid', $identifiers);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
if ($scopeToCompany) {
|
|
257
|
+
$query->where('company_uuid', $this->session->get('company'));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return $query->pluck('uuid')->all();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private function dateFilter(string $column, $value): void
|
|
264
|
+
{
|
|
265
|
+
$range = Utils::dateRange($value);
|
|
266
|
+
|
|
267
|
+
is_array($range) ? $this->builder->whereBetween($column, $range) : $this->builder->whereDate($column, $range);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Filter;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Http\Filter\Concerns\ResolvesPublicRelationUuids;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Device;
|
|
6
7
|
use Fleetbase\FleetOps\Models\Driver;
|
|
7
8
|
use Fleetbase\FleetOps\Models\Fleet;
|
|
9
|
+
use Fleetbase\FleetOps\Models\Trailer;
|
|
8
10
|
use Fleetbase\FleetOps\Models\Vehicle;
|
|
9
11
|
use Fleetbase\FleetOps\Models\Vendor;
|
|
10
12
|
use Fleetbase\FleetOps\Support\Utils;
|
|
@@ -55,8 +57,18 @@ class VehicleFilter extends Filter
|
|
|
55
57
|
* is the console that gets the exception. An unknown request context is
|
|
56
58
|
* treated as public: exact is the safe default of the two.
|
|
57
59
|
*/
|
|
58
|
-
public function internalId(
|
|
60
|
+
public function internalId($internalId)
|
|
59
61
|
{
|
|
62
|
+
// The console's tag-input filter sends several ids at once (batch scans);
|
|
63
|
+
// any list is an exact match on each id.
|
|
64
|
+
$ids = array_values(array_filter(array_map('trim', explode(',', (string) $internalId)), 'strlen'));
|
|
65
|
+
|
|
66
|
+
if (count($ids) > 1) {
|
|
67
|
+
$this->builder->whereIn('internal_id', $ids);
|
|
68
|
+
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
60
72
|
if (Http::isInternalRequest($this->request)) {
|
|
61
73
|
$this->builder->searchWhere('internal_id', $internalId);
|
|
62
74
|
|
|
@@ -107,7 +119,7 @@ class VehicleFilter extends Filter
|
|
|
107
119
|
$this->builder->searchWhere('year', $vehicleYear);
|
|
108
120
|
}
|
|
109
121
|
|
|
110
|
-
public function driver(
|
|
122
|
+
public function driver($driverId)
|
|
111
123
|
{
|
|
112
124
|
if ($driverId === 'unassigned') {
|
|
113
125
|
$this->builder->whereDoesntHave('driver');
|
|
@@ -125,7 +137,62 @@ class VehicleFilter extends Filter
|
|
|
125
137
|
);
|
|
126
138
|
}
|
|
127
139
|
|
|
128
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Vehicles currently towing any of the given trailers (public id, internal id or,
|
|
142
|
+
* for the console, uuid). Several trailers combine with OR.
|
|
143
|
+
*/
|
|
144
|
+
public function trailer($trailer)
|
|
145
|
+
{
|
|
146
|
+
$uuids = $this->resolvePublicRelationUuids(Trailer::class, $trailer);
|
|
147
|
+
|
|
148
|
+
$this->builder->whereHas('currentTrailers', function ($query) use ($uuids) {
|
|
149
|
+
$query->whereIn('assets.uuid', $uuids);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Vehicles with any of the given devices installed.
|
|
155
|
+
*/
|
|
156
|
+
public function device($device)
|
|
157
|
+
{
|
|
158
|
+
$uuids = $this->resolvePublicRelationUuids(Device::class, $device);
|
|
159
|
+
|
|
160
|
+
$this->builder->whereHas('devices', function ($query) use ($uuids) {
|
|
161
|
+
$query->whereIn('uuid', $uuids);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public function hasTrailer($value)
|
|
166
|
+
{
|
|
167
|
+
$this->whereRelationPresence('currentTrailers', $value);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public function hasDriver($value)
|
|
171
|
+
{
|
|
172
|
+
$this->whereRelationPresence('driver', $value);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public function hasDevice($value)
|
|
176
|
+
{
|
|
177
|
+
$this->whereRelationPresence('devices', $value);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* `true` keeps vehicles that have the relation, `false` those that do not, and
|
|
182
|
+
* anything else (an empty select) leaves the query untouched.
|
|
183
|
+
*/
|
|
184
|
+
protected function whereRelationPresence(string $relation, $value): void
|
|
185
|
+
{
|
|
186
|
+
$presence = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
|
187
|
+
|
|
188
|
+
if ($presence === true) {
|
|
189
|
+
$this->builder->whereHas($relation, fn ($query) => $query);
|
|
190
|
+
} elseif ($presence === false) {
|
|
191
|
+
$this->builder->whereDoesntHave($relation);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
public function vendor($vendor)
|
|
129
196
|
{
|
|
130
197
|
if (!$vendor) {
|
|
131
198
|
return;
|
|
@@ -41,7 +41,7 @@ class CreateDeviceRequest extends FleetbaseRequest
|
|
|
41
41
|
'telematic' => ['nullable', 'string'],
|
|
42
42
|
'warranty' => ['nullable', 'string'],
|
|
43
43
|
'photo' => ['nullable', 'string'],
|
|
44
|
-
'attachable_type' => ['nullable', '
|
|
44
|
+
'attachable_type' => ['nullable', Rule::in(['fleet-ops:vehicle', 'fleet-ops:trailer', 'vehicle', 'trailer'])],
|
|
45
45
|
'attachable' => ['nullable', 'required_with:attachable_type', 'string'],
|
|
46
46
|
'meta' => ['nullable', 'array'],
|
|
47
47
|
'data' => ['nullable', 'array'],
|
|
@@ -24,7 +24,7 @@ class CreateEquipmentRequest extends FleetbaseRequest
|
|
|
24
24
|
'model' => ['nullable', 'string'],
|
|
25
25
|
'warranty' => ['nullable', 'string'],
|
|
26
26
|
'photo' => ['nullable', 'string'],
|
|
27
|
-
'equipable_type' => ['nullable', '
|
|
27
|
+
'equipable_type' => ['nullable', Rule::in(['fleet-ops:vehicle', 'fleet-ops:trailer', 'fleet-ops:driver', 'vehicle', 'trailer', 'driver'])],
|
|
28
28
|
'equipable' => ['nullable', 'required_with:equipable_type', 'string'],
|
|
29
29
|
'purchased_at' => ['nullable', 'date'],
|
|
30
30
|
'purchase_price' => ['nullable'],
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Requests;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Trailer;
|
|
6
|
+
use Fleetbase\FleetOps\Rules\ResolvablePoint;
|
|
7
|
+
use Fleetbase\Http\Requests\FleetbaseRequest;
|
|
8
|
+
use Illuminate\Validation\Rule;
|
|
9
|
+
|
|
10
|
+
class CreateTrailerRequest extends FleetbaseRequest
|
|
11
|
+
{
|
|
12
|
+
public function authorize(): bool
|
|
13
|
+
{
|
|
14
|
+
return request()->session()->has('api_credential') || request()->session()->has('is_sanctum_token');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public function rules(): array
|
|
18
|
+
{
|
|
19
|
+
return [
|
|
20
|
+
'name' => [Rule::requiredIf($this->isMethod('POST')), 'string', 'max:255'],
|
|
21
|
+
'description' => ['nullable', 'string'],
|
|
22
|
+
'code' => ['nullable', 'string', 'max:255'],
|
|
23
|
+
'type' => ['nullable', Rule::in(Trailer::TYPES)],
|
|
24
|
+
'body_type' => ['nullable', 'string', 'max:255'],
|
|
25
|
+
'status' => ['nullable', Rule::in(Trailer::STATUSES)],
|
|
26
|
+
'vin' => ['nullable', 'string', 'max:255'],
|
|
27
|
+
'plate_number' => ['nullable', 'string', 'max:255'],
|
|
28
|
+
'serial_number' => ['nullable', 'string', 'max:255'],
|
|
29
|
+
'make' => ['nullable', 'string', 'max:255'],
|
|
30
|
+
'model' => ['nullable', 'string', 'max:255'],
|
|
31
|
+
'year' => ['nullable', 'integer', 'min:1880', 'max:' . (now()->year + 2)],
|
|
32
|
+
'color' => ['nullable', 'string', 'max:255'],
|
|
33
|
+
'usage_type' => ['nullable', 'string', 'max:255'],
|
|
34
|
+
'category' => ['nullable', 'string'], 'vendor' => ['nullable', 'string'], 'warranty' => ['nullable', 'string'], 'photo' => ['nullable', 'string'],
|
|
35
|
+
'location' => ['nullable', new ResolvablePoint()], 'latitude' => ['nullable', 'numeric', 'between:-90,90', 'required_with:longitude'],
|
|
36
|
+
'longitude' => ['nullable', 'numeric', 'between:-180,180', 'required_with:latitude'],
|
|
37
|
+
'length' => ['nullable', 'numeric', 'min:0'], 'width' => ['nullable', 'numeric', 'min:0'], 'height' => ['nullable', 'numeric', 'min:0'],
|
|
38
|
+
'tare_weight' => ['nullable', 'numeric', 'min:0'], 'gvwr' => ['nullable', 'numeric', 'min:0'], 'payload_capacity' => ['nullable', 'numeric', 'min:0'],
|
|
39
|
+
'cargo_volume' => ['nullable', 'numeric', 'min:0'], 'axle_count' => ['nullable', 'integer', 'min:0'], 'tire_count' => ['nullable', 'integer', 'min:0'],
|
|
40
|
+
'door_count' => ['nullable', 'integer', 'min:0'], 'abs_equipped' => ['nullable', 'boolean'], 'ebs_equipped' => ['nullable', 'boolean'],
|
|
41
|
+
'refrigerated' => ['nullable', 'boolean'], 'temperature_min' => ['nullable', 'numeric'], 'temperature_max' => ['nullable', 'numeric', 'gte:temperature_min'],
|
|
42
|
+
'reefer_engine_hours' => ['nullable', 'numeric', 'min:0'],
|
|
43
|
+
'measurement_system' => ['nullable', Rule::in(['metric', 'imperial'])], 'odometer' => ['nullable', 'numeric', 'min:0'],
|
|
44
|
+
'odometer_unit' => ['nullable', Rule::in(['km', 'mi', 'miles', 'kilometers'])],
|
|
45
|
+
'coupling_type' => ['nullable', 'string', 'max:255'], 'brake_type' => ['nullable', 'string', 'max:255'],
|
|
46
|
+
'ownership_type' => ['nullable', Rule::in(Trailer::OWNERSHIP_TYPES)],
|
|
47
|
+
'purchased_at' => ['nullable', 'date'], 'lease_expires_at' => ['nullable', 'date'],
|
|
48
|
+
'financing_status' => ['nullable', 'string', 'max:255'], 'currency' => ['nullable', 'string', 'size:3'],
|
|
49
|
+
'acquisition_cost' => ['nullable', 'numeric', 'min:0'], 'current_value' => ['nullable', 'numeric', 'min:0'],
|
|
50
|
+
'insurance_value' => ['nullable', 'numeric', 'min:0'], 'depreciation_rate' => ['nullable', 'numeric', 'min:0'],
|
|
51
|
+
'capacity' => ['nullable', 'array'], 'specs' => ['nullable', 'array'], 'attributes' => ['nullable', 'array'], 'notes' => ['nullable', 'string'],
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Resources\v1;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Http\Resources\v1\Concerns\ResolvesPublicRelationFields;
|
|
6
|
+
use Fleetbase\FleetOps\Support\Utils;
|
|
7
|
+
use Fleetbase\Http\Resources\FleetbaseResource;
|
|
8
|
+
use Fleetbase\Support\Http;
|
|
9
|
+
use Fleetbase\Support\Resolve;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* An effective-dated towing connection between a Vehicle (connector) and a
|
|
13
|
+
* Trailer (connected asset).
|
|
14
|
+
*/
|
|
15
|
+
class AssetConnection extends FleetbaseResource
|
|
16
|
+
{
|
|
17
|
+
use ResolvesPublicRelationFields;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Transform the resource into an array.
|
|
21
|
+
*
|
|
22
|
+
* @param \Illuminate\Http\Request $request
|
|
23
|
+
*/
|
|
24
|
+
public function toArray($request): array
|
|
25
|
+
{
|
|
26
|
+
$internal = Http::isInternalRequest();
|
|
27
|
+
|
|
28
|
+
return [
|
|
29
|
+
'id' => $this->when($internal, $this->id, $this->public_id),
|
|
30
|
+
'uuid' => $this->when($internal, $this->uuid),
|
|
31
|
+
'public_id' => $this->when($internal, $this->public_id),
|
|
32
|
+
'company_uuid' => $this->when($internal, $this->company_uuid),
|
|
33
|
+
'connector_type' => $this->when($internal, Utils::toEmberResourceType($this->connector_type)),
|
|
34
|
+
'connector_uuid' => $this->when($internal, $this->connector_uuid),
|
|
35
|
+
'connected_type' => $this->when($internal, Utils::toEmberResourceType($this->connected_type)),
|
|
36
|
+
'connected_uuid' => $this->when($internal, $this->connected_uuid),
|
|
37
|
+
'relationship_type' => $this->relationship_type,
|
|
38
|
+
'position' => $this->position,
|
|
39
|
+
// Identifiers are always present so a connection can be followed back to both
|
|
40
|
+
// sides even when the related records were not eager loaded.
|
|
41
|
+
'vehicle_id' => $this->publicIdForRelation('vehicle', 'connector_uuid'),
|
|
42
|
+
'trailer_id' => $this->publicIdForRelation('trailer', 'connected_uuid'),
|
|
43
|
+
'vehicle' => $this->whenLoaded('vehicle', fn () => $this->vehicleRepresentation($internal)),
|
|
44
|
+
'trailer' => $this->whenLoaded('trailer', fn () => $this->trailerRepresentation($internal)),
|
|
45
|
+
'connected_at' => $this->connected_at,
|
|
46
|
+
'disconnected_at' => $this->disconnected_at,
|
|
47
|
+
'active' => $this->disconnected_at === null,
|
|
48
|
+
'source' => $this->source,
|
|
49
|
+
'confidence' => $this->confidence,
|
|
50
|
+
'notes' => $this->notes,
|
|
51
|
+
'meta' => data_get($this, 'meta', Utils::createObject()),
|
|
52
|
+
'created_at' => $this->created_at,
|
|
53
|
+
'updated_at' => $this->updated_at,
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Only invoked through whenLoaded(), which already yields null for an absent record.
|
|
59
|
+
*/
|
|
60
|
+
protected function vehicleRepresentation(bool $internal): mixed
|
|
61
|
+
{
|
|
62
|
+
$vehicle = $this->vehicle;
|
|
63
|
+
|
|
64
|
+
if ($internal) {
|
|
65
|
+
return Resolve::httpResourceForModel($vehicle);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return [
|
|
69
|
+
'id' => $vehicle->public_id,
|
|
70
|
+
'name' => $vehicle->display_name,
|
|
71
|
+
'plate_number' => $vehicle->plate_number,
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
protected function trailerRepresentation(bool $internal): mixed
|
|
76
|
+
{
|
|
77
|
+
$trailer = $this->trailer;
|
|
78
|
+
|
|
79
|
+
if ($internal) {
|
|
80
|
+
return Resolve::httpResourceForModel($trailer);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return [
|
|
84
|
+
'id' => $trailer->public_id,
|
|
85
|
+
'name' => $trailer->display_name,
|
|
86
|
+
'type' => $trailer->type,
|
|
87
|
+
'plate_number' => $trailer->plate_number,
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -22,8 +22,13 @@ trait ResolvesPublicRelationFields
|
|
|
22
22
|
/**
|
|
23
23
|
* The relations the caller asked for, as real Eloquent relation names.
|
|
24
24
|
*
|
|
25
|
-
* The controller
|
|
26
|
-
*
|
|
25
|
+
* The controller normalises, maps and allowlists `with` / `expand` for the
|
|
26
|
+
* resource it returns. That guarantee does not travel: this resource is also
|
|
27
|
+
* rendered nested inside Order and Driver responses, where the request's
|
|
28
|
+
* `with` names the parent's relations (`payload`, `driverAssigned.vehicle`).
|
|
29
|
+
* A nested resource re-reads the same request, so only relations the wrapped
|
|
30
|
+
* model actually defines are kept — a parent's expansion must never reach
|
|
31
|
+
* this model's loader, where it raises RelationNotFoundException.
|
|
27
32
|
*
|
|
28
33
|
* @return array<int, string>
|
|
29
34
|
*/
|
|
@@ -39,7 +44,28 @@ trait ResolvesPublicRelationFields
|
|
|
39
44
|
return [];
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
$with = array_values(array_filter(array_map('strval', $with), 'strlen'));
|
|
48
|
+
|
|
49
|
+
return array_values(array_filter($with, fn (string $relation): bool => $this->definesRelation($relation)));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether the wrapped model defines the root of a relation path.
|
|
54
|
+
*
|
|
55
|
+
* Only the first segment is checked: nested segments belong to the related
|
|
56
|
+
* model, and Eloquent validates those itself once the root exists.
|
|
57
|
+
*/
|
|
58
|
+
private function definesRelation(string $relation): bool
|
|
59
|
+
{
|
|
60
|
+
$resource = $this->resource;
|
|
61
|
+
|
|
62
|
+
if (!is_object($resource) || !method_exists($resource, 'loadMissing')) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
$root = explode('.', $relation, 2)[0];
|
|
67
|
+
|
|
68
|
+
return $root !== '' && method_exists($resource, $root);
|
|
43
69
|
}
|
|
44
70
|
|
|
45
71
|
/**
|
|
@@ -18,7 +18,9 @@ class Device extends FleetbaseResource
|
|
|
18
18
|
'company_uuid' => $this->when(Http::isInternalRequest(), $this->company_uuid),
|
|
19
19
|
'telematic_uuid' => $this->when(Http::isInternalRequest(), $this->telematic_uuid),
|
|
20
20
|
'attachable_uuid' => $this->when(Http::isInternalRequest(), $this->attachable_uuid),
|
|
21
|
-
'attachable_type' => $this->
|
|
21
|
+
'attachable_type' => $this->attachable_type ? Utils::toEmberResourceType($this->attachable_type) : null,
|
|
22
|
+
'attachable_id' => $this->whenLoaded('attachable', fn () => $this->attachable?->public_id),
|
|
23
|
+
'attachable_name' => $this->whenLoaded('attachable', fn () => $this->attachable?->display_name ?? $this->attachable?->name),
|
|
22
24
|
'warranty_uuid' => $this->when(Http::isInternalRequest(), $this->warranty_uuid),
|
|
23
25
|
'photo_uuid' => $this->when(Http::isInternalRequest(), $this->photo_uuid),
|
|
24
26
|
'telematic' => $this->whenLoaded('telematic', fn () => $this->resolveLoadedRelation($this->telematic)),
|
|
@@ -19,7 +19,9 @@ class Equipment extends FleetbaseResource
|
|
|
19
19
|
'warranty_uuid' => $this->when(Http::isInternalRequest(), $this->warranty_uuid),
|
|
20
20
|
'photo_uuid' => $this->when(Http::isInternalRequest(), $this->photo_uuid),
|
|
21
21
|
'equipable_uuid' => $this->when(Http::isInternalRequest(), $this->equipable_uuid),
|
|
22
|
-
'equipable_type' => $this->
|
|
22
|
+
'equipable_type' => $this->equipable_type ? Utils::toEmberResourceType($this->equipable_type) : null,
|
|
23
|
+
'equipable_id' => $this->whenLoaded('equipable', fn () => $this->equipable?->public_id),
|
|
24
|
+
'equipable_name' => $this->whenLoaded('equipable', fn () => $this->equipable?->display_name ?? $this->equipable?->name),
|
|
23
25
|
'warranty' => $this->whenLoaded('warranty', fn () => $this->resolveLoadedRelation($this->warranty)),
|
|
24
26
|
'photo' => $this->whenLoaded('photo', fn () => $this->resolveLoadedRelation($this->photo)),
|
|
25
27
|
'equipable' => $this->whenLoaded('equipable', fn () => $this->resolveLoadedRelation($this->equipable)),
|
|
@@ -24,7 +24,9 @@ class Fleet extends FleetbaseResource
|
|
|
24
24
|
// dropped anything outside the public allowlist, so they are safe to
|
|
25
25
|
// hand to the loader. `subfleets` used to arrive here spelled exactly
|
|
26
26
|
// that and reach `load('subfleets')`, which is not the relation —
|
|
27
|
-
// the documented expansion raised instead of resolving.
|
|
27
|
+
// the documented expansion raised instead of resolving. Relations the
|
|
28
|
+
// fleet does not define are dropped as well: nested under another
|
|
29
|
+
// response the request's `with` describes the parent, not the fleet.
|
|
28
30
|
$with = $this->requestedRelations($request);
|
|
29
31
|
|
|
30
32
|
if ($with !== []) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Resources\v1\Index;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Models\Order;
|
|
6
|
+
use Fleetbase\FleetOps\Support\TrailerSummary;
|
|
6
7
|
use Fleetbase\FleetOps\Support\Utils;
|
|
7
8
|
use Fleetbase\Http\Resources\FleetbaseResource;
|
|
8
9
|
use Fleetbase\Support\Http;
|
|
@@ -47,6 +48,7 @@ class Vehicle extends FleetbaseResource
|
|
|
47
48
|
'speed' => (int) data_get($this, 'speed', 0),
|
|
48
49
|
'online' => (bool) data_get($this, 'online', false),
|
|
49
50
|
'devices' => $this->whenLoaded('devices', fn () => $this->compactDevices()),
|
|
51
|
+
'trailers' => $this->whenLoaded('currentTrailers', fn () => $this->compactTrailers()),
|
|
50
52
|
'assigned_orders_count' => $this->when($isInternal, $this->assignedOrdersCount()),
|
|
51
53
|
|
|
52
54
|
// Meta flag to indicate this is an index resource
|
|
@@ -84,6 +86,14 @@ class Vehicle extends FleetbaseResource
|
|
|
84
86
|
)->values()->all();
|
|
85
87
|
}
|
|
86
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Trailers currently coupled to the vehicle, compacted for map popovers.
|
|
91
|
+
*/
|
|
92
|
+
protected function compactTrailers(): array
|
|
93
|
+
{
|
|
94
|
+
return TrailerSummary::collection($this->currentTrailers);
|
|
95
|
+
}
|
|
96
|
+
|
|
87
97
|
protected function currentOrderReference(): ?string
|
|
88
98
|
{
|
|
89
99
|
$this->loadMissing('driver.currentOrder');
|