@fleetbase/fleetops-engine 0.6.61 → 0.6.62
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 +23 -8
- 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
|
@@ -5,6 +5,9 @@ namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
|
|
|
5
5
|
use Fleetbase\FleetOps\Events\GeofenceEntered;
|
|
6
6
|
use Fleetbase\FleetOps\Events\GeofenceExited;
|
|
7
7
|
use Fleetbase\FleetOps\Events\VehicleLocationChanged;
|
|
8
|
+
use Fleetbase\FleetOps\Exceptions\PublicRelationNotFoundException;
|
|
9
|
+
use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesFleetOpsApiResources;
|
|
10
|
+
use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesPublicExpansions;
|
|
8
11
|
use Fleetbase\FleetOps\Http\Requests\CreateVehicleRequest;
|
|
9
12
|
use Fleetbase\FleetOps\Http\Requests\UpdateVehicleRequest;
|
|
10
13
|
use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
|
|
@@ -12,16 +15,45 @@ use Fleetbase\FleetOps\Http\Resources\v1\Vehicle as VehicleResource;
|
|
|
12
15
|
use Fleetbase\FleetOps\Jobs\CheckGeofenceDwell;
|
|
13
16
|
use Fleetbase\FleetOps\Models\Driver;
|
|
14
17
|
use Fleetbase\FleetOps\Models\Vehicle;
|
|
18
|
+
use Fleetbase\FleetOps\Models\Vendor;
|
|
19
|
+
use Fleetbase\FleetOps\Models\Warranty;
|
|
15
20
|
use Fleetbase\FleetOps\Support\GeofenceIntersectionService;
|
|
16
21
|
use Fleetbase\FleetOps\Support\Utils;
|
|
17
22
|
use Fleetbase\Http\Controllers\Controller;
|
|
18
23
|
use Fleetbase\LaravelMysqlSpatial\Types\Point;
|
|
24
|
+
use Fleetbase\Models\Category;
|
|
25
|
+
use Fleetbase\Models\File;
|
|
19
26
|
use Illuminate\Http\Request;
|
|
20
27
|
use Illuminate\Support\Carbon;
|
|
21
28
|
use Illuminate\Support\Facades\DB;
|
|
22
29
|
|
|
23
30
|
class VehicleController extends Controller
|
|
24
31
|
{
|
|
32
|
+
use ResolvesFleetOpsApiResources;
|
|
33
|
+
use ResolvesPublicExpansions;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Relationships eager loaded so the public resource can report each
|
|
37
|
+
* assignment as a public id without a query per vehicle.
|
|
38
|
+
*
|
|
39
|
+
* Loading them does not expand them: the nested object is returned only for
|
|
40
|
+
* a relation the caller named in `with`, which is the shape the endpoint has
|
|
41
|
+
* always had.
|
|
42
|
+
*/
|
|
43
|
+
protected const PUBLIC_RELATIONS = ['vendor', 'category', 'warranty', 'driver', 'photo'];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Public expansion name => Eloquent relation name.
|
|
47
|
+
*/
|
|
48
|
+
public const EXPANDABLE = [
|
|
49
|
+
'driver' => 'driver',
|
|
50
|
+
'vendor' => 'vendor',
|
|
51
|
+
'category' => 'category',
|
|
52
|
+
'warranty' => 'warranty',
|
|
53
|
+
'photo' => 'photo',
|
|
54
|
+
'devices' => 'devices',
|
|
55
|
+
];
|
|
56
|
+
|
|
25
57
|
/**
|
|
26
58
|
* Creates a new Fleetbase Vehicle resource.
|
|
27
59
|
*
|
|
@@ -31,8 +63,14 @@ class VehicleController extends Controller
|
|
|
31
63
|
*/
|
|
32
64
|
public function create(CreateVehicleRequest $request)
|
|
33
65
|
{
|
|
66
|
+
$this->applyPublicExpansions($request, static::EXPANDABLE);
|
|
67
|
+
|
|
34
68
|
// get request input
|
|
35
|
-
|
|
69
|
+
try {
|
|
70
|
+
$input = $this->vehicleInputFromRequest($request);
|
|
71
|
+
} catch (PublicRelationNotFoundException $exception) {
|
|
72
|
+
return $this->jsonResponse(['error' => $exception->getMessage()], 404);
|
|
73
|
+
}
|
|
36
74
|
|
|
37
75
|
// make sure company is set
|
|
38
76
|
$input['company_uuid'] = session('company');
|
|
@@ -40,14 +78,6 @@ class VehicleController extends Controller
|
|
|
40
78
|
// set default online
|
|
41
79
|
$input = $this->withDefaultOnline($input);
|
|
42
80
|
|
|
43
|
-
// vendor assignment
|
|
44
|
-
if ($request->has('vendor')) {
|
|
45
|
-
$input['vendor_uuid'] = $this->getVendorUuid('vendors', [
|
|
46
|
-
'public_id' => $request->input('vendor'),
|
|
47
|
-
'company_uuid' => session('company'),
|
|
48
|
-
]);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
81
|
// latitude / longitude
|
|
52
82
|
$input = $this->withCoordinateLocation($input, $request);
|
|
53
83
|
|
|
@@ -85,6 +115,8 @@ class VehicleController extends Controller
|
|
|
85
115
|
*/
|
|
86
116
|
public function update($id, UpdateVehicleRequest $request)
|
|
87
117
|
{
|
|
118
|
+
$this->applyPublicExpansions($request, static::EXPANDABLE);
|
|
119
|
+
|
|
88
120
|
// find for the vehicle
|
|
89
121
|
try {
|
|
90
122
|
$vehicle = $this->findVehicle($id);
|
|
@@ -98,19 +130,12 @@ class VehicleController extends Controller
|
|
|
98
130
|
}
|
|
99
131
|
|
|
100
132
|
// get request input
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
$input['vendor_uuid'] = $this->getVendorUuid('vendors', [
|
|
106
|
-
'public_id' => $request->input('vendor'),
|
|
107
|
-
'company_uuid' => session('company'),
|
|
108
|
-
]);
|
|
133
|
+
try {
|
|
134
|
+
$input = $this->vehicleInputFromRequest($request);
|
|
135
|
+
} catch (PublicRelationNotFoundException $exception) {
|
|
136
|
+
return $this->jsonResponse(['error' => $exception->getMessage()], 404);
|
|
109
137
|
}
|
|
110
138
|
|
|
111
|
-
// set default online
|
|
112
|
-
$input = $this->withDefaultOnline($input);
|
|
113
|
-
|
|
114
139
|
// latitude / longitude
|
|
115
140
|
$input = $this->withCoordinateLocation($input, $request);
|
|
116
141
|
|
|
@@ -157,6 +182,8 @@ class VehicleController extends Controller
|
|
|
157
182
|
*/
|
|
158
183
|
public function query(Request $request)
|
|
159
184
|
{
|
|
185
|
+
$this->applyPublicExpansions($request, static::EXPANDABLE);
|
|
186
|
+
|
|
160
187
|
$results = $this->queryVehicles($request);
|
|
161
188
|
|
|
162
189
|
return $this->vehicleResourceCollection($results);
|
|
@@ -169,8 +196,17 @@ class VehicleController extends Controller
|
|
|
169
196
|
*
|
|
170
197
|
* @return \Fleetbase\Http\Resources\VehicleCollection
|
|
171
198
|
*/
|
|
172
|
-
public function find($id)
|
|
199
|
+
public function find($id, ?Request $request = null)
|
|
173
200
|
{
|
|
201
|
+
// Falls back to the container's request rather than trusting injection:
|
|
202
|
+
// the parameter carries a default, and Laravel's controller dispatcher
|
|
203
|
+
// skips resolving a type-hinted dependency that has one. It arrives null,
|
|
204
|
+
// the expansions are never mapped, and an unsupported name reaches
|
|
205
|
+
// Eloquent — a 500 for a typo, on the one endpoint most likely to be
|
|
206
|
+
// handed one.
|
|
207
|
+
$request = $request instanceof Request ? $request : request();
|
|
208
|
+
$this->applyPublicExpansions($request, static::EXPANDABLE);
|
|
209
|
+
|
|
174
210
|
// find for the vehicle
|
|
175
211
|
try {
|
|
176
212
|
$vehicle = $this->findVehicle($id);
|
|
@@ -347,23 +383,75 @@ class VehicleController extends Controller
|
|
|
347
383
|
}
|
|
348
384
|
}
|
|
349
385
|
|
|
386
|
+
/**
|
|
387
|
+
* The explicit public input allowlist for a vehicle.
|
|
388
|
+
*
|
|
389
|
+
* Mirrors every safe business field the model and the Fleet-Ops vehicle form
|
|
390
|
+
* expose. Deliberately absent: `company_uuid` and the raw `*_uuid` relation
|
|
391
|
+
* columns (resolved from public ids below), `public_id` / `uuid` / `_key` /
|
|
392
|
+
* `slug` (generated), `avatar_url` when given as a uuid, `vin_data` (written
|
|
393
|
+
* by the VIN decoder) and `telematics` (written by telematics ingestion).
|
|
394
|
+
*
|
|
395
|
+
* @throws PublicRelationNotFoundException
|
|
396
|
+
*/
|
|
350
397
|
protected function vehicleInputFromRequest(Request $request): array
|
|
351
398
|
{
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
'
|
|
399
|
+
$input = $request->only([
|
|
400
|
+
// Identity and description
|
|
401
|
+
'internal_id', 'name', 'description', 'make', 'model', 'model_type', 'year',
|
|
402
|
+
'trim', 'color', 'type', 'class', 'plate_number', 'vin', 'serial_number',
|
|
403
|
+
'call_sign', 'fuel_card_number',
|
|
404
|
+
// Measurement and operation.
|
|
405
|
+
//
|
|
355
406
|
// Odometer is fillable on the model and unrestricted by the request
|
|
356
407
|
// rules, but was absent here — so a caller sending one received a
|
|
357
408
|
// 200 and a response body that looked correct while the reading was
|
|
358
409
|
// discarded. Recording mileage is the single most common write a
|
|
359
410
|
// driver app makes against a vehicle.
|
|
360
|
-
'odometer', 'odometer_unit',
|
|
361
|
-
'
|
|
362
|
-
'
|
|
411
|
+
'odometer', 'odometer_unit', 'odometer_at_purchase', 'measurement_system',
|
|
412
|
+
'fuel_type', 'fuel_volume_unit', 'online', 'status',
|
|
413
|
+
'location', 'altitude', 'heading', 'speed',
|
|
414
|
+
// Body, capacity and dimensions
|
|
415
|
+
'transmission', 'body_type', 'body_sub_type', 'usage_type', 'ownership_type',
|
|
416
|
+
'cargo_volume', 'passenger_volume', 'interior_volume', 'weight', 'width',
|
|
417
|
+
'length', 'height', 'towing_capacity', 'payload_capacity', 'seating_capacity',
|
|
418
|
+
'ground_clearance', 'bed_length', 'fuel_capacity',
|
|
419
|
+
// Lifecycle and financing
|
|
420
|
+
'financing_status', 'loan_number_of_payments', 'loan_first_payment', 'loan_amount',
|
|
421
|
+
'estimated_service_life_distance_unit', 'estimated_service_life_distance',
|
|
422
|
+
'estimated_service_life_months', 'insurance_value', 'depreciation_rate',
|
|
423
|
+
'current_value', 'acquisition_cost', 'currency', 'purchased_at', 'lease_expires_at',
|
|
424
|
+
// Regulatory and engine specifications
|
|
425
|
+
'emission_standard', 'dpf_equipped', 'scr_equipped', 'gvwr', 'gcwr',
|
|
426
|
+
'engine_number', 'engine_model', 'engine_make', 'engine_family',
|
|
427
|
+
'engine_configuration', 'engine_displacement', 'engine_size', 'horsepower',
|
|
428
|
+
'horsepower_rpm', 'torque', 'torque_rpm', 'number_of_cylinders',
|
|
429
|
+
'cylinder_arrangement',
|
|
430
|
+
// Structured and descriptive fields
|
|
431
|
+
'specs', 'details', 'notes', 'meta',
|
|
432
|
+
// Orchestrator
|
|
433
|
+
'payload_capacity_volume', 'payload_capacity_pallets', 'payload_capacity_parcels',
|
|
363
434
|
'skills', 'max_tasks', 'time_window_start', 'time_window_end', 'return_to_depot',
|
|
364
435
|
]);
|
|
436
|
+
|
|
437
|
+
$this->applyPublicIdRelations($input, [
|
|
438
|
+
'vendor' => ['vendor_uuid', Vendor::class],
|
|
439
|
+
'category' => ['category_uuid', Category::class],
|
|
440
|
+
'warranty' => ['warranty_uuid', Warranty::class],
|
|
441
|
+
'photo' => ['photo_uuid', File::class],
|
|
442
|
+
], $request);
|
|
443
|
+
|
|
444
|
+
return $input;
|
|
365
445
|
}
|
|
366
446
|
|
|
447
|
+
/**
|
|
448
|
+
* A vehicle that does not say otherwise starts offline.
|
|
449
|
+
*
|
|
450
|
+
* Applied on create only. Applying it on update too meant any partial write —
|
|
451
|
+
* a plate correction, an odometer reading — silently knocked the vehicle
|
|
452
|
+
* offline, because the absent key was read as "set it to false" rather than
|
|
453
|
+
* "leave it alone".
|
|
454
|
+
*/
|
|
367
455
|
protected function withDefaultOnline(array $input): array
|
|
368
456
|
{
|
|
369
457
|
if (!isset($input['online'])) {
|
|
@@ -394,11 +482,6 @@ class VehicleController extends Controller
|
|
|
394
482
|
];
|
|
395
483
|
}
|
|
396
484
|
|
|
397
|
-
protected function getVendorUuid(string $table, array $where): ?string
|
|
398
|
-
{
|
|
399
|
-
return Utils::getUuid($table, $where);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
485
|
protected function createVehicle(array $input): Vehicle
|
|
403
486
|
{
|
|
404
487
|
return Vehicle::create($input);
|
|
@@ -409,19 +492,35 @@ class VehicleController extends Controller
|
|
|
409
492
|
return Vehicle::findRecordOrFail($id);
|
|
410
493
|
}
|
|
411
494
|
|
|
412
|
-
protected function findDriver(string $id): Driver
|
|
495
|
+
protected function findDriver(?string $id): Driver
|
|
413
496
|
{
|
|
414
497
|
return Driver::findRecordOrFail($id);
|
|
415
498
|
}
|
|
416
499
|
|
|
417
500
|
protected function queryVehicles(Request $request)
|
|
418
501
|
{
|
|
419
|
-
return Vehicle::queryWithRequest($request)
|
|
502
|
+
return Vehicle::queryWithRequest($request, function (&$query) {
|
|
503
|
+
$query->with(static::PUBLIC_RELATIONS);
|
|
504
|
+
});
|
|
420
505
|
}
|
|
421
506
|
|
|
422
507
|
protected function vehicleResource(Vehicle $vehicle)
|
|
423
508
|
{
|
|
424
|
-
return new VehicleResource($vehicle);
|
|
509
|
+
return new VehicleResource($this->withPublicRelations($vehicle));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Load the relations the public resource reports as identifiers.
|
|
514
|
+
*
|
|
515
|
+
* One query per relation instead of one per relation per read, and it makes
|
|
516
|
+
* an assignment made moments earlier in the same request readable back
|
|
517
|
+
* immediately.
|
|
518
|
+
*/
|
|
519
|
+
protected function withPublicRelations(Vehicle $vehicle): Vehicle
|
|
520
|
+
{
|
|
521
|
+
$vehicle->loadMissing(static::PUBLIC_RELATIONS);
|
|
522
|
+
|
|
523
|
+
return $vehicle;
|
|
425
524
|
}
|
|
426
525
|
|
|
427
526
|
protected function vehicleResourceCollection($results)
|
|
@@ -212,7 +212,7 @@ class ZoneController extends Controller
|
|
|
212
212
|
return (int) $request->input('radius', 500);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
protected function serviceAreaUuid(string $publicId, array $where): ?string
|
|
215
|
+
protected function serviceAreaUuid(?string $publicId, array $where): ?string
|
|
216
216
|
{
|
|
217
217
|
return Utils::getUuid('service_areas', $where);
|
|
218
218
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Filter\Concerns;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Support\Utils;
|
|
6
|
+
use Fleetbase\Support\Http;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolves a relationship filter value expressed as a public identifier into the
|
|
10
|
+
* internal uuids the column actually stores.
|
|
11
|
+
*
|
|
12
|
+
* The public API never hands out uuids, so `?vendor=vendor_abc` has to be
|
|
13
|
+
* translated before it can be compared against `vendor_uuid`. Internal console
|
|
14
|
+
* requests deliberately keep passing uuids straight through, so the uuid arm is
|
|
15
|
+
* gated on the request kind rather than on the shape of the value.
|
|
16
|
+
*/
|
|
17
|
+
trait ResolvesPublicRelationUuids
|
|
18
|
+
{
|
|
19
|
+
/**
|
|
20
|
+
* Resolve one or more public identifiers to uuids within the session company.
|
|
21
|
+
*
|
|
22
|
+
* @param class-string $modelClass the model the identifiers belong to
|
|
23
|
+
* @param string|array<int, string> $identifiers public_id / internal_id values (uuid when internal)
|
|
24
|
+
*
|
|
25
|
+
* @return array<int, string>
|
|
26
|
+
*/
|
|
27
|
+
protected function resolvePublicRelationUuids(string $modelClass, string|array $identifiers, ?bool $allowUuid = null): array
|
|
28
|
+
{
|
|
29
|
+
$identifiers = array_values(array_filter(Utils::arrayFrom($identifiers), static fn ($identifier) => filled($identifier)));
|
|
30
|
+
|
|
31
|
+
if ($identifiers === []) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
$allowUuid = $allowUuid ?? Http::isInternalRequest($this->request);
|
|
36
|
+
$instance = new $modelClass();
|
|
37
|
+
|
|
38
|
+
return $modelClass::query()
|
|
39
|
+
->where('company_uuid', $this->session->get('company'))
|
|
40
|
+
->where(function ($query) use ($identifiers, $instance, $allowUuid) {
|
|
41
|
+
$query->whereIn('public_id', $identifiers);
|
|
42
|
+
|
|
43
|
+
if (in_array('internal_id', $instance->getFillable())) {
|
|
44
|
+
$query->orWhereIn('internal_id', $identifiers);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if ($allowUuid) {
|
|
48
|
+
$query->orWhereIn('uuid', $identifiers);
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
->pluck('uuid')
|
|
52
|
+
->all();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -2,14 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Filter;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Http\Filter\Concerns\ResolvesPublicRelationUuids;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Fleet;
|
|
5
7
|
use Fleetbase\FleetOps\Models\Place;
|
|
8
|
+
use Fleetbase\FleetOps\Models\Vehicle;
|
|
9
|
+
use Fleetbase\FleetOps\Models\Vendor;
|
|
6
10
|
use Fleetbase\FleetOps\Support\Utils;
|
|
7
11
|
use Fleetbase\Http\Filter\Filter;
|
|
8
12
|
use Fleetbase\Models\Company;
|
|
13
|
+
use Fleetbase\Support\Http;
|
|
9
14
|
use Illuminate\Support\Str;
|
|
10
15
|
|
|
11
16
|
class DriverFilter extends Filter
|
|
12
17
|
{
|
|
18
|
+
use ResolvesPublicRelationUuids;
|
|
19
|
+
|
|
13
20
|
public function queryForInternal()
|
|
14
21
|
{
|
|
15
22
|
$this->builder->where(
|
|
@@ -43,9 +50,28 @@ class DriverFilter extends Filter
|
|
|
43
50
|
});
|
|
44
51
|
}
|
|
45
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Match a driver by the identifier the operator's own system uses.
|
|
55
|
+
*
|
|
56
|
+
* Two callers, two meanings. The Fleet-Ops console types into a search box
|
|
57
|
+
* and expects `VEH-10` to find `VEH-100`; an importer asks whether `VEH-10`
|
|
58
|
+
* exists and must not be told yes because `VEH-100` does. A partial match
|
|
59
|
+
* there produces a duplicate on every rerun.
|
|
60
|
+
*
|
|
61
|
+
* Core's ordinary fillable-column filtering is already equality — this
|
|
62
|
+
* method exists only because the console needs the looser behaviour, so it
|
|
63
|
+
* is the console that gets the exception. An unknown request context is
|
|
64
|
+
* treated as public: exact is the safe default of the two.
|
|
65
|
+
*/
|
|
46
66
|
public function internalId(?string $internalId)
|
|
47
67
|
{
|
|
48
|
-
$this->
|
|
68
|
+
if (Http::isInternalRequest($this->request)) {
|
|
69
|
+
$this->builder->searchWhere('internal_id', $internalId);
|
|
70
|
+
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
$this->builder->where('internal_id', '=', $internalId);
|
|
49
75
|
}
|
|
50
76
|
|
|
51
77
|
public function name(?string $name)
|
|
@@ -58,14 +84,25 @@ class DriverFilter extends Filter
|
|
|
58
84
|
);
|
|
59
85
|
}
|
|
60
86
|
|
|
87
|
+
/**
|
|
88
|
+
* A public id identifies exactly one record, so a public lookup matches it
|
|
89
|
+
* exactly. The console keeps the partial search its id column filter box
|
|
90
|
+
* has always had.
|
|
91
|
+
*/
|
|
61
92
|
public function publicId(?string $publicId)
|
|
62
93
|
{
|
|
63
|
-
$this->
|
|
94
|
+
if (Http::isInternalRequest($this->request)) {
|
|
95
|
+
$this->builder->searchWhere('public_id', $publicId);
|
|
96
|
+
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
$this->builder->where('public_id', '=', $publicId);
|
|
64
101
|
}
|
|
65
102
|
|
|
66
103
|
public function facilitator(string $facilitator)
|
|
67
104
|
{
|
|
68
|
-
$this->builder->
|
|
105
|
+
$this->builder->whereIn('vendor_uuid', $this->resolvePublicRelationUuids(Vendor::class, $facilitator));
|
|
69
106
|
}
|
|
70
107
|
|
|
71
108
|
public function vehicle(string $vehicle)
|
|
@@ -76,16 +113,28 @@ class DriverFilter extends Filter
|
|
|
76
113
|
return;
|
|
77
114
|
}
|
|
78
115
|
|
|
79
|
-
|
|
116
|
+
// The console passes a uuid; the public API passes a public or internal id.
|
|
117
|
+
if (Str::isUuid($vehicle) && Http::isInternalRequest($this->request)) {
|
|
80
118
|
$this->builder->where('vehicle_uuid', $vehicle);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
119
|
+
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
$vehicleUuids = $this->resolvePublicRelationUuids(Vehicle::class, $vehicle);
|
|
124
|
+
|
|
125
|
+
if ($vehicleUuids !== []) {
|
|
126
|
+
$this->builder->whereIn('vehicle_uuid', $vehicleUuids);
|
|
127
|
+
|
|
128
|
+
return;
|
|
88
129
|
}
|
|
130
|
+
|
|
131
|
+
// Fall back to a search so a partial plate or model still narrows a list.
|
|
132
|
+
$this->builder->whereHas(
|
|
133
|
+
'vehicle',
|
|
134
|
+
function ($query) use ($vehicle) {
|
|
135
|
+
$query->search($vehicle);
|
|
136
|
+
}
|
|
137
|
+
);
|
|
89
138
|
}
|
|
90
139
|
|
|
91
140
|
public function driversLicenseNumber(?string $driversLicenseNumber)
|
|
@@ -93,12 +142,17 @@ class DriverFilter extends Filter
|
|
|
93
142
|
$this->builder->searchWhere('drivers_license_number', $driversLicenseNumber);
|
|
94
143
|
}
|
|
95
144
|
|
|
145
|
+
/**
|
|
146
|
+
* `phone` is an accessor sourced from the linked user, not a relation — the
|
|
147
|
+
* previous `whereHas('phone')` asked Eloquent for a relation that does not
|
|
148
|
+
* exist and raised a 500 for every caller of `?phone=`.
|
|
149
|
+
*/
|
|
96
150
|
public function phone(string $phone)
|
|
97
151
|
{
|
|
98
152
|
$this->builder->whereHas(
|
|
99
|
-
'
|
|
153
|
+
'user',
|
|
100
154
|
function ($query) use ($phone) {
|
|
101
|
-
$query->
|
|
155
|
+
$query->searchWhere('phone', $phone);
|
|
102
156
|
}
|
|
103
157
|
);
|
|
104
158
|
}
|
|
@@ -127,10 +181,12 @@ class DriverFilter extends Filter
|
|
|
127
181
|
|
|
128
182
|
public function fleet(string $fleet)
|
|
129
183
|
{
|
|
184
|
+
$fleetUuids = $this->resolvePublicRelationUuids(Fleet::class, $fleet);
|
|
185
|
+
|
|
130
186
|
$this->builder->whereHas(
|
|
131
187
|
'fleets',
|
|
132
|
-
function ($q) use ($
|
|
133
|
-
$q->
|
|
188
|
+
function ($q) use ($fleetUuids) {
|
|
189
|
+
$q->whereIn('fleet_uuid', $fleetUuids);
|
|
134
190
|
}
|
|
135
191
|
);
|
|
136
192
|
}
|
|
@@ -2,11 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Filter;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Http\Filter\Concerns\ResolvesPublicRelationUuids;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Fleet;
|
|
7
|
+
use Fleetbase\FleetOps\Models\ServiceArea;
|
|
8
|
+
use Fleetbase\FleetOps\Models\Vendor;
|
|
9
|
+
use Fleetbase\FleetOps\Models\Zone;
|
|
5
10
|
use Fleetbase\FleetOps\Support\Utils;
|
|
6
11
|
use Fleetbase\Http\Filter\Filter;
|
|
12
|
+
use Fleetbase\Support\Http;
|
|
7
13
|
|
|
8
14
|
class FleetFilter extends Filter
|
|
9
15
|
{
|
|
16
|
+
use ResolvesPublicRelationUuids;
|
|
17
|
+
|
|
10
18
|
public function queryForInternal()
|
|
11
19
|
{
|
|
12
20
|
$this->builder->where('company_uuid', $this->session->get('company'))->with(['serviceArea', 'zone']);
|
|
@@ -17,15 +25,17 @@ class FleetFilter extends Filter
|
|
|
17
25
|
$this->builder->where('company_uuid', $this->session->get('company'));
|
|
18
26
|
}
|
|
19
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Free-text search across a fleet's own columns.
|
|
30
|
+
*
|
|
31
|
+
* Previously matched against a `user` relation, which Fleet does not have —
|
|
32
|
+
* every `?query=` on the fleets endpoint raised a relation-not-found error
|
|
33
|
+
* rather than returning results.
|
|
34
|
+
*/
|
|
20
35
|
public function query(?string $searchQuery)
|
|
21
36
|
{
|
|
22
37
|
$this->builder->where(function ($query) use ($searchQuery) {
|
|
23
|
-
$query->
|
|
24
|
-
'user',
|
|
25
|
-
function ($query) use ($searchQuery) {
|
|
26
|
-
$query->searchWhere(['name', 'email', 'phone'], $searchQuery);
|
|
27
|
-
}
|
|
28
|
-
);
|
|
38
|
+
$query->searchWhere(['name', 'task', 'public_id'], $searchQuery);
|
|
29
39
|
});
|
|
30
40
|
}
|
|
31
41
|
|
|
@@ -38,49 +48,38 @@ class FleetFilter extends Filter
|
|
|
38
48
|
|
|
39
49
|
public function serviceArea(?string $serviceArea)
|
|
40
50
|
{
|
|
41
|
-
$this->builder->
|
|
42
|
-
'serviceArea',
|
|
43
|
-
function ($query) use ($serviceArea) {
|
|
44
|
-
$query->where('uuid', $serviceArea);
|
|
45
|
-
}
|
|
46
|
-
);
|
|
51
|
+
$this->builder->whereIn('service_area_uuid', $this->resolvePublicRelationUuids(ServiceArea::class, $serviceArea));
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
public function zone(?string $zone)
|
|
50
55
|
{
|
|
51
|
-
$this->builder->
|
|
52
|
-
'zone',
|
|
53
|
-
function ($query) use ($zone) {
|
|
54
|
-
$query->where('zone_uuid', $zone);
|
|
55
|
-
}
|
|
56
|
-
);
|
|
56
|
+
$this->builder->whereIn('zone_uuid', $this->resolvePublicRelationUuids(Zone::class, $zone));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
public function parentFleet(?string $fleet)
|
|
60
60
|
{
|
|
61
|
-
$this->builder->
|
|
62
|
-
'parent_fleet',
|
|
63
|
-
function ($query) use ($fleet) {
|
|
64
|
-
$query->where('uuid', $fleet);
|
|
65
|
-
}
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
$this->builder->searchWhere('parent_fleet_uuid', $fleet);
|
|
61
|
+
$this->builder->whereIn('parent_fleet_uuid', $this->resolvePublicRelationUuids(Fleet::class, $fleet));
|
|
69
62
|
}
|
|
70
63
|
|
|
71
64
|
public function vendor(?string $vendor)
|
|
72
65
|
{
|
|
73
|
-
$this->builder->
|
|
74
|
-
'vendor',
|
|
75
|
-
function ($query) use ($vendor) {
|
|
76
|
-
$query->where('uuid', $vendor);
|
|
77
|
-
}
|
|
78
|
-
);
|
|
66
|
+
$this->builder->whereIn('vendor_uuid', $this->resolvePublicRelationUuids(Vendor::class, $vendor));
|
|
79
67
|
}
|
|
80
68
|
|
|
69
|
+
/**
|
|
70
|
+
* A public id identifies exactly one record, so a public lookup matches it
|
|
71
|
+
* exactly. The console keeps the partial search its id column filter box
|
|
72
|
+
* has always had.
|
|
73
|
+
*/
|
|
81
74
|
public function publicId(?string $publicId)
|
|
82
75
|
{
|
|
83
|
-
$this->
|
|
76
|
+
if (Http::isInternalRequest($this->request)) {
|
|
77
|
+
$this->builder->searchWhere('public_id', $publicId);
|
|
78
|
+
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
$this->builder->where('public_id', '=', $publicId);
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
public function task(?string $task)
|