@fleetbase/fleetops-engine 0.6.61 → 0.6.63
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 +5 -14
- package/addon/controllers/management/drivers/index.js +4 -1
- package/addon/routes/management/fleets/index/details.js +1 -1
- package/addon/routes/management/fleets/index/edit.js +1 -1
- package/addon/routes/management/fleets/index.js +3 -1
- package/addon/services/map-settings.js +4 -1
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +1 -1
- package/server/migrations/2026_09_05_000001_add_unique_indexes_to_fleet_membership_pivots.php +129 -0
- package/server/src/Exceptions/PublicRelationNotFoundException.php +47 -0
- package/server/src/Http/Controllers/Api/v1/Concerns/ResolvesFleetOpsApiResources.php +42 -7
- package/server/src/Http/Controllers/Api/v1/Concerns/ResolvesPublicExpansions.php +187 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +93 -57
- package/server/src/Http/Controllers/Api/v1/EntityController.php +1 -1
- package/server/src/Http/Controllers/Api/v1/FleetController.php +370 -33
- package/server/src/Http/Controllers/Api/v1/FuelReportController.php +1 -1
- package/server/src/Http/Controllers/Api/v1/IssueController.php +1 -1
- package/server/src/Http/Controllers/Api/v1/OrderController.php +1 -1
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +1 -1
- package/server/src/Http/Controllers/Api/v1/TrackingStatusController.php +1 -1
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +134 -35
- package/server/src/Http/Controllers/Api/v1/ZoneController.php +1 -1
- package/server/src/Http/Filter/Concerns/ResolvesPublicRelationUuids.php +54 -0
- package/server/src/Http/Filter/DriverFilter.php +71 -15
- package/server/src/Http/Filter/FleetFilter.php +32 -33
- package/server/src/Http/Filter/VehicleFilter.php +57 -19
- package/server/src/Http/Requests/Concerns/ScopesPublicRelationRules.php +48 -0
- package/server/src/Http/Requests/CreateDriverRequest.php +100 -9
- package/server/src/Http/Requests/CreateFleetRequest.php +30 -2
- package/server/src/Http/Requests/CreateServiceRateRequest.php +7 -2
- package/server/src/Http/Requests/CreateVehicleRequest.php +150 -31
- package/server/src/Http/Resources/v1/Concerns/ResolvesPublicRelationFields.php +110 -0
- package/server/src/Http/Resources/v1/Driver.php +22 -2
- package/server/src/Http/Resources/v1/Fleet.php +38 -25
- package/server/src/Http/Resources/v1/Vehicle.php +46 -4
- package/server/src/Models/Driver.php +2 -2
- package/server/src/routes.php +8 -0
- package/server/tests/ApiDriverControllerContractsTest.php +284 -13
- package/server/tests/ApiEntityControllerContractsTest.php +1 -1
- package/server/tests/ApiEquipmentControllerContractsTest.php +2 -2
- package/server/tests/ApiFleetControllerContractsTest.php +354 -21
- package/server/tests/ApiFuelReportControllerContractsTest.php +1 -1
- package/server/tests/ApiIssueControllerContractsTest.php +1 -1
- package/server/tests/ApiOrderControllerContractsTest.php +1 -1
- package/server/tests/ApiPartControllerContractsTest.php +2 -2
- package/server/tests/ApiServiceAreaZoneControllerContractsTest.php +2 -2
- package/server/tests/ApiTrackingStatusControllerContractsTest.php +1 -1
- package/server/tests/ApiVehicleControllerContractsTest.php +175 -8
- package/server/tests/ControllerFilterContractsTest.php +132 -13
- package/server/tests/ControllerHelperContractsTest.php +6 -3
- package/server/tests/DriverFilterExecutionTest.php +130 -6
- package/server/tests/ExceptionContractsTest.php +15 -0
- package/server/tests/Feature/Http/Api/DeviceControllerContractsTest.php +1 -1
- package/server/tests/Feature/Http/Api/FleetMembershipMigrationTest.php +140 -0
- package/server/tests/Feature/Http/Api/FleetMembershipTest.php +333 -0
- package/server/tests/Feature/Http/Api/FleetPublicContractTest.php +439 -0
- package/server/tests/Feature/Http/Api/FuelTransactionControllerContractsTest.php +2 -2
- package/server/tests/Feature/Http/Api/NullRelationshipInputTest.php +191 -0
- package/server/tests/Feature/Http/Api/PublicIdentifierFilterTest.php +293 -0
- package/server/tests/Feature/Http/Api/SmallApiControllerHelpersTest.php +7 -1
- package/server/tests/Feature/Http/Api/VehicleControllerTrackingTest.php +3 -1
- package/server/tests/Feature/Http/Api/VehiclePublicContractTest.php +454 -0
- package/server/tests/Feature/Http/Api/WorkOrderControllerContractsTest.php +1 -1
- package/server/tests/FleetHierarchyResourceTest.php +56 -8
- package/server/tests/RequestContractsTest.php +100 -7
- package/server/tests/Unit/Http/Resources/FleetResourceTest.php +50 -6
- package/server/tests/VehicleControllerHelperContractsTest.php +1 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Resources\v1\Concerns;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\Support\Http;
|
|
6
|
+
use Illuminate\Http\Resources\MissingValue;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Renders a relationship twice: as an additive `<name>_id` public identifier, and
|
|
10
|
+
* as the nested object under the name the endpoint has always used.
|
|
11
|
+
*
|
|
12
|
+
* The two are deliberately separate keys. The SDK stores whatever the API returns
|
|
13
|
+
* verbatim — `Resource::$attributes = $attributes`, no normalisation — so a
|
|
14
|
+
* property that is an object on one call and a string on another silently breaks
|
|
15
|
+
* every consumer that dereferences it. Navigator interpolates `driver.user`
|
|
16
|
+
* straight into a socket channel name; an object there would subscribe it to
|
|
17
|
+
* `user.[object Object]` and simply stop delivering messages. So an existing
|
|
18
|
+
* object key stays an object key, always, and the identifier arrives beside it.
|
|
19
|
+
*/
|
|
20
|
+
trait ResolvesPublicRelationFields
|
|
21
|
+
{
|
|
22
|
+
/**
|
|
23
|
+
* The relations the caller asked for, as real Eloquent relation names.
|
|
24
|
+
*
|
|
25
|
+
* The controller has already normalised, mapped and allowlisted `with` /
|
|
26
|
+
* `expand` by the time a resource runs, so this is a plain read.
|
|
27
|
+
*
|
|
28
|
+
* @return array<int, string>
|
|
29
|
+
*/
|
|
30
|
+
protected function requestedRelations($request): array
|
|
31
|
+
{
|
|
32
|
+
$with = $request->input('with');
|
|
33
|
+
|
|
34
|
+
if (is_string($with)) {
|
|
35
|
+
$with = explode(',', $with);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!is_array($with)) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return array_values(array_filter(array_map('strval', $with), 'strlen'));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The nested object for a relationship.
|
|
47
|
+
*
|
|
48
|
+
* Internal console requests keep the exact `whenLoaded` behaviour they have
|
|
49
|
+
* always had. Public requests get the object only when they asked for it,
|
|
50
|
+
* which is also what they got before: these relations were never eager
|
|
51
|
+
* loaded on the public endpoints, so the key was absent unless `with` named
|
|
52
|
+
* it. Absent stays absent; it never becomes a string.
|
|
53
|
+
*
|
|
54
|
+
* @param array<int, string> $with camelCased relations the caller asked for
|
|
55
|
+
*/
|
|
56
|
+
protected function publicRelationObject(string $relation, array $with, \Closure $resource): mixed
|
|
57
|
+
{
|
|
58
|
+
if (Http::isInternalRequest()) {
|
|
59
|
+
return $this->whenLoaded($relation, $resource);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!in_array($relation, $with, true)) {
|
|
63
|
+
return new MissingValue();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
$this->loadRelationIfPossible($relation);
|
|
67
|
+
|
|
68
|
+
return $this->{$relation} ? $resource() : null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The public id behind a relationship, or null when nothing is assigned.
|
|
73
|
+
*
|
|
74
|
+
* Additive and unconditional: it is present whether or not the object is,
|
|
75
|
+
* and it does not change when the object is expanded.
|
|
76
|
+
*
|
|
77
|
+
* @param string|null $foreignKey the column holding the relation for a belongsTo;
|
|
78
|
+
* null for the inverse side, which has no local column
|
|
79
|
+
*/
|
|
80
|
+
protected function publicIdForRelation(string $relation, ?string $foreignKey): ?string
|
|
81
|
+
{
|
|
82
|
+
if ($foreignKey !== null && empty($this->{$foreignKey})) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
$resource = $this->resource;
|
|
87
|
+
|
|
88
|
+
if (is_object($resource) && method_exists($resource, 'relationLoaded') && $resource->relationLoaded($relation)) {
|
|
89
|
+
return data_get($this, $relation . '.public_id');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Not every resource wraps an Eloquent model — webhook payload fixtures and
|
|
93
|
+
// compact serializers pass plain objects — so read the already-materialised
|
|
94
|
+
// value rather than calling a relation method that is not there.
|
|
95
|
+
if (!is_object($resource) || !method_exists($resource, $relation)) {
|
|
96
|
+
return data_get($this, $relation . '.public_id');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return $this->{$relation}()->value('public_id');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private function loadRelationIfPossible(string $relation): void
|
|
103
|
+
{
|
|
104
|
+
$resource = $this->resource;
|
|
105
|
+
|
|
106
|
+
if (is_object($resource) && method_exists($resource, 'loadMissing') && method_exists($resource, $relation)) {
|
|
107
|
+
$this->loadMissing($relation);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -37,6 +37,7 @@ class Driver extends FleetbaseResource
|
|
|
37
37
|
'name' => $this->name,
|
|
38
38
|
'email' => $this->email,
|
|
39
39
|
'phone' => $this->phone,
|
|
40
|
+
'timezone' => data_get($this, 'user.timezone'),
|
|
40
41
|
'drivers_license_number' => $this->drivers_license_number,
|
|
41
42
|
'license_expiry' => $this->formatDateOnly($this->license_expiry),
|
|
42
43
|
'photo_url' => $this->photo_url,
|
|
@@ -45,25 +46,38 @@ class Driver extends FleetbaseResource
|
|
|
45
46
|
'vehicle_name' => $this->when(Http::isInternalRequest(), $this->vehicle_name),
|
|
46
47
|
'vehicle_avatar' => $this->vehicle_avatar,
|
|
47
48
|
'vendor_name' => $this->when(Http::isInternalRequest(), $this->vendor_name),
|
|
48
|
-
'assigned_orders_count' => $this->when(Http::isInternalRequest(), $this->assignedOrdersCount()),
|
|
49
|
-
'current_order_reference' => $this->when(Http::isInternalRequest(), $this->currentOrderReference()),
|
|
49
|
+
'assigned_orders_count' => $this->when(Http::isInternalRequest(), fn () => $this->assignedOrdersCount()),
|
|
50
|
+
'current_order_reference' => $this->when(Http::isInternalRequest(), fn () => $this->currentOrderReference()),
|
|
50
51
|
'vehicle' => $this->whenLoaded('vehicle', fn () => new VehicleWithoutDriver($this->vehicle)),
|
|
51
52
|
'current_job' => $this->whenLoaded('currentJob', fn () => new Order($this->currentJob)),
|
|
52
53
|
'current_job_id' => $this->when(Http::isInternalRequest(), data_get($this, 'currentJob.tracking')),
|
|
53
54
|
'jobs' => $this->whenLoaded('jobs', fn () => $this->getJobs()),
|
|
54
55
|
'vendor' => $this->whenLoaded('vendor', fn () => new Vendor($this->vendor)),
|
|
56
|
+
// Public callers receive each assignment as a public id so a write
|
|
57
|
+
// can be read back; the nested objects above are unchanged.
|
|
58
|
+
'vehicle_id' => $this->when(Http::isPublicRequest(), fn () => $this->vehicle_id),
|
|
59
|
+
'vendor_id' => $this->when(Http::isPublicRequest(), fn () => $this->vendor_id),
|
|
60
|
+
'job_id' => $this->when(Http::isPublicRequest(), fn () => data_get($this, 'currentJob.public_id')),
|
|
55
61
|
'fleets' => $this->whenLoaded('fleets', fn () => Fleet::collection($this->fleets()->without('drivers')->get())),
|
|
56
62
|
'current_shift' => $this->whenLoaded('currentShift', fn () => $this->currentShift),
|
|
57
63
|
'location' => $this->wasRecentlyCreated ? new Point(0, 0) : Utils::castPoint($this->location),
|
|
58
64
|
'heading' => (int) data_get($this, 'heading', 0),
|
|
65
|
+
'bearing' => data_get($this, 'bearing'),
|
|
59
66
|
'altitude' => (int) data_get($this, 'altitude', 0),
|
|
60
67
|
'speed' => (int) data_get($this, 'speed', 0),
|
|
61
68
|
'country' => data_get($this, 'country'),
|
|
62
69
|
'currency' => data_get($this, 'currency', Utils::getCurrenyFromCountryCode($this->country)),
|
|
63
70
|
'city' => data_get($this, 'city', Utils::getCapitalCityFromCountryCode($this->country)),
|
|
64
71
|
'online' => data_get($this, 'online', false),
|
|
72
|
+
'current_status' => data_get($this, 'current_status'),
|
|
65
73
|
'status' => $this->status,
|
|
66
74
|
'token' => $this->token,
|
|
75
|
+
// Orchestrator constraints
|
|
76
|
+
'skills' => data_get($this, 'skills'),
|
|
77
|
+
'max_travel_time' => $this->max_travel_time,
|
|
78
|
+
'max_distance' => $this->max_distance,
|
|
79
|
+
'time_window_start' => $this->time_window_start,
|
|
80
|
+
'time_window_end' => $this->time_window_end,
|
|
67
81
|
'meta' => data_get($this, 'meta', Utils::createObject()),
|
|
68
82
|
'updated_at' => $this->updated_at,
|
|
69
83
|
'created_at' => $this->created_at,
|
|
@@ -131,7 +145,13 @@ class Driver extends FleetbaseResource
|
|
|
131
145
|
'currency' => data_get($this, 'currency', Utils::getCurrenyFromCountryCode($this->country)),
|
|
132
146
|
'city' => data_get($this, 'city', Utils::getCapitalCityFromCountryCode($this->country)),
|
|
133
147
|
'online' => data_get($this, 'online', false),
|
|
148
|
+
'current_status' => data_get($this, 'current_status'),
|
|
134
149
|
'status' => $this->status,
|
|
150
|
+
'skills' => data_get($this, 'skills'),
|
|
151
|
+
'max_travel_time' => $this->max_travel_time,
|
|
152
|
+
'max_distance' => $this->max_distance,
|
|
153
|
+
'time_window_start' => $this->time_window_start,
|
|
154
|
+
'time_window_end' => $this->time_window_end,
|
|
135
155
|
'meta' => data_get($this, 'meta', Utils::createObject()),
|
|
136
156
|
'updated_at' => $this->updated_at,
|
|
137
157
|
'created_at' => $this->created_at,
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Resources\v1;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Http\Resources\v1\Concerns\ResolvesPublicRelationFields;
|
|
5
6
|
use Fleetbase\Http\Resources\FleetbaseResource;
|
|
6
7
|
use Fleetbase\Support\Http;
|
|
7
|
-
use
|
|
8
|
+
use Fleetbase\Support\Resolve;
|
|
8
9
|
|
|
9
10
|
class Fleet extends FleetbaseResource
|
|
10
11
|
{
|
|
12
|
+
use ResolvesPublicRelationFields;
|
|
13
|
+
|
|
11
14
|
/**
|
|
12
15
|
* Transform the resource into an array.
|
|
13
16
|
*
|
|
@@ -17,22 +20,15 @@ class Fleet extends FleetbaseResource
|
|
|
17
20
|
*/
|
|
18
21
|
public function toArray($request)
|
|
19
22
|
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (in_array('subfleets', $with, true)) {
|
|
28
|
-
if (in_array('drivers', $with, true)) {
|
|
29
|
-
$this->loadMissing('subFleets.drivers');
|
|
30
|
-
}
|
|
23
|
+
// The controller has already mapped these onto real relation names and
|
|
24
|
+
// dropped anything outside the public allowlist, so they are safe to
|
|
25
|
+
// hand to the loader. `subfleets` used to arrive here spelled exactly
|
|
26
|
+
// that and reach `load('subfleets')`, which is not the relation —
|
|
27
|
+
// the documented expansion raised instead of resolving.
|
|
28
|
+
$with = $this->requestedRelations($request);
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
}
|
|
30
|
+
if ($with !== []) {
|
|
31
|
+
$this->loadMissing($with);
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
return $this->withCustomFields([
|
|
@@ -40,16 +36,31 @@ class Fleet extends FleetbaseResource
|
|
|
40
36
|
'uuid' => $this->when(Http::isInternalRequest(), $this->uuid),
|
|
41
37
|
'public_id' => $this->when(Http::isInternalRequest(), $this->public_id),
|
|
42
38
|
'name' => $this->name,
|
|
39
|
+
'color' => $this->color ?? null,
|
|
43
40
|
'task' => $this->task ?? null,
|
|
44
41
|
'status' => $this->status ?? null,
|
|
45
|
-
'
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
42
|
+
'photo_url' => $this->photo_url,
|
|
43
|
+
// Closures, not values: `when()` evaluates a plain second argument
|
|
44
|
+
// eagerly, so each of these counts ran a query on every public
|
|
45
|
+
// request that then discarded the result.
|
|
46
|
+
'drivers_count' => $this->when(Http::isInternalRequest(), fn () => $this->drivers_count),
|
|
47
|
+
'drivers_online_count' => $this->when(Http::isInternalRequest(), fn () => $this->drivers_online_count),
|
|
48
|
+
'vehicles_count' => $this->when(Http::isInternalRequest(), fn () => $this->vehicles_count),
|
|
49
|
+
'vehicles_online_count' => $this->when(Http::isInternalRequest(), fn () => $this->vehicles_online_count),
|
|
50
|
+
// Additive public identifiers. Always present, unchanged when the
|
|
51
|
+
// object beside them is expanded, and never a substitute for it.
|
|
52
|
+
'service_area_id' => $this->publicIdForRelation('serviceArea', 'service_area_uuid'),
|
|
53
|
+
'zone_id' => $this->publicIdForRelation('zone', 'zone_uuid'),
|
|
54
|
+
'vendor_id' => $this->publicIdForRelation('vendor', 'vendor_uuid'),
|
|
55
|
+
'parent_fleet_id' => $this->publicIdForRelation('parentFleet', 'parent_fleet_uuid'),
|
|
56
|
+
'photo_id' => $this->publicIdForRelation('photo', 'image_uuid'),
|
|
57
|
+
// The objects keep the shape they have always had: absent unless the
|
|
58
|
+
// relation was loaded, an object when it was, never a string.
|
|
59
|
+
'service_area' => $this->publicRelationObject('serviceArea', $with, fn () => new ServiceArea($this->serviceArea)),
|
|
60
|
+
'zone' => $this->publicRelationObject('zone', $with, fn () => new Zone($this->zone)),
|
|
61
|
+
'vendor' => $this->publicRelationObject('vendor', $with, fn () => new Vendor($this->vendor)),
|
|
62
|
+
'parent_fleet' => $this->publicRelationObject('parentFleet', $with, fn () => new ParentFleet($this->parentFleet)),
|
|
63
|
+
'photo' => $this->publicRelationObject('photo', $with, fn () => Resolve::httpResourceForModel($this->photo)),
|
|
53
64
|
'subfleets' => $this->whenLoaded('subFleets', fn () => SubFleet::collection($this->subFleets)),
|
|
54
65
|
'drivers' => $this->whenLoaded('drivers', fn () => Driver::collection($this->drivers()->with(Http::isInternalRequest() || $request->has('with.jobs') ? ['jobs'] : [])->get())),
|
|
55
66
|
'vehicles' => $this->whenLoaded('vehicles', fn () => Vehicle::collection($this->vehicles)),
|
|
@@ -68,11 +79,13 @@ class Fleet extends FleetbaseResource
|
|
|
68
79
|
return [
|
|
69
80
|
'id' => $this->public_id,
|
|
70
81
|
'name' => $this->name,
|
|
82
|
+
'color' => $this->color ?? null,
|
|
71
83
|
'task' => $this->task ?? null,
|
|
72
84
|
'status' => $this->status ?? null,
|
|
73
|
-
'parent_fleet' => $this->when($this->
|
|
85
|
+
'parent_fleet' => $this->when($this->parentFleet, data_get($this, 'parentFleet.public_id')),
|
|
74
86
|
'service_area' => $this->when($this->serviceArea, data_get($this, 'serviceArea.public_id')),
|
|
75
87
|
'zone' => $this->when($this->zone, data_get($this, 'zone.public_id')),
|
|
88
|
+
'vendor' => $this->when($this->vendor, data_get($this, 'vendor.public_id')),
|
|
76
89
|
'updated_at' => $this->updated_at,
|
|
77
90
|
'created_at' => $this->created_at,
|
|
78
91
|
];
|
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Resources\v1;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Http\Resources\v1\Concerns\ResolvesPublicRelationFields;
|
|
5
6
|
use Fleetbase\FleetOps\Support\Utils;
|
|
6
7
|
use Fleetbase\Http\Resources\FleetbaseResource;
|
|
7
8
|
use Fleetbase\Support\Http;
|
|
9
|
+
use Fleetbase\Support\Resolve;
|
|
8
10
|
|
|
9
11
|
class Vehicle extends FleetbaseResource
|
|
10
12
|
{
|
|
13
|
+
use ResolvesPublicRelationFields;
|
|
14
|
+
|
|
11
15
|
/**
|
|
12
16
|
* Transform the resource into an array.
|
|
13
17
|
*
|
|
@@ -17,6 +21,12 @@ class Vehicle extends FleetbaseResource
|
|
|
17
21
|
*/
|
|
18
22
|
public function toArray($request)
|
|
19
23
|
{
|
|
24
|
+
$with = $this->requestedRelations($request);
|
|
25
|
+
|
|
26
|
+
if ($with !== []) {
|
|
27
|
+
$this->loadMissing($with);
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
return $this->withCustomFields([
|
|
21
31
|
// Identity
|
|
22
32
|
'id' => $this->when(Http::isInternalRequest(), $this->id, $this->public_id),
|
|
@@ -39,10 +49,24 @@ class Vehicle extends FleetbaseResource
|
|
|
39
49
|
'description' => $this->description,
|
|
40
50
|
'driver_name' => $this->when(Http::isInternalRequest(), $this->driver_name),
|
|
41
51
|
'vendor_name' => $this->when(Http::isInternalRequest(), $this->vendor_name),
|
|
42
|
-
'assigned_orders_count' => $this->when(Http::isInternalRequest(), $this->assignedOrdersCount()),
|
|
43
|
-
'current_order_reference'=> $this->when(Http::isInternalRequest(), $this->currentOrderReference()),
|
|
44
|
-
// Relationships
|
|
45
|
-
|
|
52
|
+
'assigned_orders_count' => $this->when(Http::isInternalRequest(), fn () => $this->assignedOrdersCount()),
|
|
53
|
+
'current_order_reference'=> $this->when(Http::isInternalRequest(), fn () => $this->currentOrderReference()),
|
|
54
|
+
// Relationships. The identifier is additive and always present so a
|
|
55
|
+
// write can be read back; the object beside it keeps the shape it
|
|
56
|
+
// has always had — absent unless loaded, an object when it is.
|
|
57
|
+
'driver_id' => $this->publicIdForRelation('driver', null),
|
|
58
|
+
'vendor_id' => $this->publicIdForRelation('vendor', 'vendor_uuid'),
|
|
59
|
+
'category_id' => $this->publicIdForRelation('category', 'category_uuid'),
|
|
60
|
+
'warranty_id' => $this->publicIdForRelation('warranty', 'warranty_uuid'),
|
|
61
|
+
'photo_id' => $this->publicIdForRelation('photo', 'photo_uuid'),
|
|
62
|
+
'driver' => $this->publicRelationObject('driver', $with, fn () => new Driver($this->driver)),
|
|
63
|
+
'vendor' => $this->publicRelationObject('vendor', $with, fn () => new Vendor($this->vendor)),
|
|
64
|
+
// Resolved through the repository's own resource resolver rather than
|
|
65
|
+
// a hardcoded class: Category and File live in core, and Warranty has
|
|
66
|
+
// no v1 resource of its own.
|
|
67
|
+
'category' => $this->publicRelationObject('category', $with, fn () => Resolve::httpResourceForModel($this->category)),
|
|
68
|
+
'warranty' => $this->publicRelationObject('warranty', $with, fn () => Resolve::httpResourceForModel($this->warranty)),
|
|
69
|
+
'photo' => $this->publicRelationObject('photo', $with, fn () => Resolve::httpResourceForModel($this->photo)),
|
|
46
70
|
'devices' => $this->whenLoaded('devices', fn () => $this->devices),
|
|
47
71
|
// Vehicle identification
|
|
48
72
|
'make' => $this->make,
|
|
@@ -139,6 +163,15 @@ class Vehicle extends FleetbaseResource
|
|
|
139
163
|
'altitude' => (int) data_get($this, 'altitude', 0),
|
|
140
164
|
'speed' => (int) data_get($this, 'speed', 0),
|
|
141
165
|
'telematics' => data_get($this, 'telematics'),
|
|
166
|
+
// Orchestrator constraints
|
|
167
|
+
'skills' => data_get($this, 'skills'),
|
|
168
|
+
'payload_capacity_volume' => $this->payload_capacity_volume,
|
|
169
|
+
'payload_capacity_pallets' => $this->payload_capacity_pallets,
|
|
170
|
+
'payload_capacity_parcels' => $this->payload_capacity_parcels,
|
|
171
|
+
'max_tasks' => $this->max_tasks,
|
|
172
|
+
'time_window_start' => $this->time_window_start,
|
|
173
|
+
'time_window_end' => $this->time_window_end,
|
|
174
|
+
'return_to_depot' => $this->return_to_depot,
|
|
142
175
|
// Notes & meta
|
|
143
176
|
'notes' => $this->notes,
|
|
144
177
|
'meta' => data_get($this, 'meta', Utils::createObject()),
|
|
@@ -273,6 +306,15 @@ class Vehicle extends FleetbaseResource
|
|
|
273
306
|
'vin_data' => data_get($this, 'vin_data', Utils::createObject()),
|
|
274
307
|
'specs' => data_get($this, 'specs', Utils::createObject()),
|
|
275
308
|
'details' => data_get($this, 'details', Utils::createObject()),
|
|
309
|
+
// Orchestrator constraints
|
|
310
|
+
'skills' => data_get($this, 'skills'),
|
|
311
|
+
'payload_capacity_volume' => $this->payload_capacity_volume,
|
|
312
|
+
'payload_capacity_pallets' => $this->payload_capacity_pallets,
|
|
313
|
+
'payload_capacity_parcels' => $this->payload_capacity_parcels,
|
|
314
|
+
'max_tasks' => $this->max_tasks,
|
|
315
|
+
'time_window_start' => $this->time_window_start,
|
|
316
|
+
'time_window_end' => $this->time_window_end,
|
|
317
|
+
'return_to_depot' => $this->return_to_depot,
|
|
276
318
|
// Notes & meta
|
|
277
319
|
'notes' => $this->notes,
|
|
278
320
|
'meta' => data_get($this, 'meta', Utils::createObject()),
|
|
@@ -110,7 +110,7 @@ class Driver extends Model
|
|
|
110
110
|
'current_status',
|
|
111
111
|
'slug',
|
|
112
112
|
'status',
|
|
113
|
-
'meta
|
|
113
|
+
'meta',
|
|
114
114
|
// Orchestrator
|
|
115
115
|
'skills',
|
|
116
116
|
'max_travel_time',
|
|
@@ -255,7 +255,7 @@ class Driver extends Model
|
|
|
255
255
|
*/
|
|
256
256
|
public function user()
|
|
257
257
|
{
|
|
258
|
-
return $this->belongsTo(User::class)->select(['uuid', 'company_uuid', 'public_id', 'avatar_uuid', 'name', 'phone', 'email', 'type', 'status', 'last_login'])->without(['driver'])->withTrashed();
|
|
258
|
+
return $this->belongsTo(User::class)->select(['uuid', 'company_uuid', 'public_id', 'avatar_uuid', 'name', 'phone', 'email', 'timezone', 'type', 'status', 'last_login'])->without(['driver'])->withTrashed();
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
/**
|
package/server/src/routes.php
CHANGED
|
@@ -302,6 +302,14 @@ Route::prefix(config('fleetops.api.routing.prefix'))->namespace('Fleetbase\Fleet
|
|
|
302
302
|
$router->group(['prefix' => 'fleets'], function () use ($router) {
|
|
303
303
|
$router->post('/', 'FleetController@create');
|
|
304
304
|
$router->get('/', 'FleetController@query');
|
|
305
|
+
// Membership routes are declared ahead of the `{id}` routes so a
|
|
306
|
+
// literal `vehicles`/`drivers` segment can never be swallowed by
|
|
307
|
+
// the single-parameter patterns below. Both parameters are public
|
|
308
|
+
// ids; no internal uuid is ever accepted or returned here.
|
|
309
|
+
$router->post('{id}/vehicles/{vehicle}', 'FleetController@assignVehicle');
|
|
310
|
+
$router->delete('{id}/vehicles/{vehicle}', 'FleetController@removeVehicle');
|
|
311
|
+
$router->post('{id}/drivers/{driver}', 'FleetController@assignDriver');
|
|
312
|
+
$router->delete('{id}/drivers/{driver}', 'FleetController@removeDriver');
|
|
305
313
|
$router->get('{id}', 'FleetController@find');
|
|
306
314
|
$router->put('{id}', 'FleetController@update');
|
|
307
315
|
$router->delete('{id}', 'FleetController@delete');
|