@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.
Files changed (64) hide show
  1. package/RELEASE.md +23 -8
  2. package/addon/services/map-settings.js +4 -1
  3. package/composer.json +1 -1
  4. package/extension.json +1 -1
  5. package/package.json +1 -1
  6. package/server/migrations/2026_09_05_000001_add_unique_indexes_to_fleet_membership_pivots.php +129 -0
  7. package/server/src/Exceptions/PublicRelationNotFoundException.php +47 -0
  8. package/server/src/Http/Controllers/Api/v1/Concerns/ResolvesFleetOpsApiResources.php +42 -7
  9. package/server/src/Http/Controllers/Api/v1/Concerns/ResolvesPublicExpansions.php +187 -0
  10. package/server/src/Http/Controllers/Api/v1/DriverController.php +93 -57
  11. package/server/src/Http/Controllers/Api/v1/EntityController.php +1 -1
  12. package/server/src/Http/Controllers/Api/v1/FleetController.php +370 -33
  13. package/server/src/Http/Controllers/Api/v1/FuelReportController.php +1 -1
  14. package/server/src/Http/Controllers/Api/v1/IssueController.php +1 -1
  15. package/server/src/Http/Controllers/Api/v1/OrderController.php +1 -1
  16. package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +1 -1
  17. package/server/src/Http/Controllers/Api/v1/TrackingStatusController.php +1 -1
  18. package/server/src/Http/Controllers/Api/v1/VehicleController.php +134 -35
  19. package/server/src/Http/Controllers/Api/v1/ZoneController.php +1 -1
  20. package/server/src/Http/Filter/Concerns/ResolvesPublicRelationUuids.php +54 -0
  21. package/server/src/Http/Filter/DriverFilter.php +71 -15
  22. package/server/src/Http/Filter/FleetFilter.php +32 -33
  23. package/server/src/Http/Filter/VehicleFilter.php +57 -19
  24. package/server/src/Http/Requests/Concerns/ScopesPublicRelationRules.php +48 -0
  25. package/server/src/Http/Requests/CreateDriverRequest.php +100 -9
  26. package/server/src/Http/Requests/CreateFleetRequest.php +30 -2
  27. package/server/src/Http/Requests/CreateServiceRateRequest.php +7 -2
  28. package/server/src/Http/Requests/CreateVehicleRequest.php +150 -31
  29. package/server/src/Http/Resources/v1/Concerns/ResolvesPublicRelationFields.php +110 -0
  30. package/server/src/Http/Resources/v1/Driver.php +22 -2
  31. package/server/src/Http/Resources/v1/Fleet.php +38 -25
  32. package/server/src/Http/Resources/v1/Vehicle.php +46 -4
  33. package/server/src/Models/Driver.php +2 -2
  34. package/server/src/routes.php +8 -0
  35. package/server/tests/ApiDriverControllerContractsTest.php +284 -13
  36. package/server/tests/ApiEntityControllerContractsTest.php +1 -1
  37. package/server/tests/ApiEquipmentControllerContractsTest.php +2 -2
  38. package/server/tests/ApiFleetControllerContractsTest.php +354 -21
  39. package/server/tests/ApiFuelReportControllerContractsTest.php +1 -1
  40. package/server/tests/ApiIssueControllerContractsTest.php +1 -1
  41. package/server/tests/ApiOrderControllerContractsTest.php +1 -1
  42. package/server/tests/ApiPartControllerContractsTest.php +2 -2
  43. package/server/tests/ApiServiceAreaZoneControllerContractsTest.php +2 -2
  44. package/server/tests/ApiTrackingStatusControllerContractsTest.php +1 -1
  45. package/server/tests/ApiVehicleControllerContractsTest.php +175 -8
  46. package/server/tests/ControllerFilterContractsTest.php +132 -13
  47. package/server/tests/ControllerHelperContractsTest.php +6 -3
  48. package/server/tests/DriverFilterExecutionTest.php +130 -6
  49. package/server/tests/ExceptionContractsTest.php +15 -0
  50. package/server/tests/Feature/Http/Api/DeviceControllerContractsTest.php +1 -1
  51. package/server/tests/Feature/Http/Api/FleetMembershipMigrationTest.php +140 -0
  52. package/server/tests/Feature/Http/Api/FleetMembershipTest.php +333 -0
  53. package/server/tests/Feature/Http/Api/FleetPublicContractTest.php +439 -0
  54. package/server/tests/Feature/Http/Api/FuelTransactionControllerContractsTest.php +2 -2
  55. package/server/tests/Feature/Http/Api/NullRelationshipInputTest.php +191 -0
  56. package/server/tests/Feature/Http/Api/PublicIdentifierFilterTest.php +293 -0
  57. package/server/tests/Feature/Http/Api/SmallApiControllerHelpersTest.php +7 -1
  58. package/server/tests/Feature/Http/Api/VehicleControllerTrackingTest.php +3 -1
  59. package/server/tests/Feature/Http/Api/VehiclePublicContractTest.php +454 -0
  60. package/server/tests/Feature/Http/Api/WorkOrderControllerContractsTest.php +1 -1
  61. package/server/tests/FleetHierarchyResourceTest.php +56 -8
  62. package/server/tests/RequestContractsTest.php +100 -7
  63. package/server/tests/Unit/Http/Resources/FleetResourceTest.php +50 -6
  64. package/server/tests/VehicleControllerHelperContractsTest.php +1 -1
@@ -2,61 +2,104 @@
2
2
 
3
3
  namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
4
4
 
5
+ use Fleetbase\FleetOps\Exceptions\PublicRelationNotFoundException;
6
+ use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesFleetOpsApiResources;
7
+ use Fleetbase\FleetOps\Http\Controllers\Api\v1\Concerns\ResolvesPublicExpansions;
5
8
  use Fleetbase\FleetOps\Http\Requests\CreateFleetRequest;
6
9
  use Fleetbase\FleetOps\Http\Requests\UpdateFleetRequest;
7
10
  use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
8
11
  use Fleetbase\FleetOps\Http\Resources\v1\Fleet as FleetResource;
12
+ use Fleetbase\FleetOps\Models\Driver;
9
13
  use Fleetbase\FleetOps\Models\Fleet;
10
- use Fleetbase\FleetOps\Support\Utils;
14
+ use Fleetbase\FleetOps\Models\FleetDriver;
15
+ use Fleetbase\FleetOps\Models\FleetVehicle;
16
+ use Fleetbase\FleetOps\Models\ServiceArea;
17
+ use Fleetbase\FleetOps\Models\Vehicle;
18
+ use Fleetbase\FleetOps\Models\Vendor;
19
+ use Fleetbase\FleetOps\Models\Zone;
11
20
  use Fleetbase\Http\Controllers\Controller;
21
+ use Fleetbase\Models\File;
22
+ use Illuminate\Database\Eloquent\ModelNotFoundException;
23
+ use Illuminate\Database\UniqueConstraintViolationException;
12
24
  use Illuminate\Http\Request;
13
25
 
14
26
  class FleetController extends Controller
15
27
  {
28
+ use ResolvesFleetOpsApiResources;
29
+ // Aliased so the fleet-specific override can still reach the shared
30
+ // resolution before adding the implicit subfleet nesting on top.
31
+ use ResolvesPublicExpansions {
32
+ resolvePublicExpansions as protected resolveAllowedExpansions;
33
+ }
34
+
16
35
  /**
17
- * Creates a new Fleetbase Fleet resource.
36
+ * Relationships eager loaded so the public resource can report each
37
+ * assignment as a public id without issuing a query per fleet.
18
38
  *
19
- * @param \Fleetbase\Http\Requests\CreateFleetRequest $request
39
+ * Loading them does not expand them: the resource returns the nested object
40
+ * only for a relation the caller named in `with`, which is the shape the
41
+ * endpoint has always had.
42
+ */
43
+ protected const PUBLIC_RELATIONS = ['serviceArea', 'zone', 'vendor', 'parentFleet', 'photo'];
44
+
45
+ /**
46
+ * Public expansion name => Eloquent relation name.
47
+ *
48
+ * `subfleets` is the reason this map exists rather than a bare list: the
49
+ * public name and the relation differ only in case, and the automatic
50
+ * camelCase normalisation upstream turns `subfleets` into `subfleets`.
51
+ */
52
+ public const EXPANDABLE = [
53
+ 'service_area' => 'serviceArea',
54
+ 'zone' => 'zone',
55
+ 'vendor' => 'vendor',
56
+ 'parent_fleet' => 'parentFleet',
57
+ 'photo' => 'photo',
58
+ 'subfleets' => 'subFleets',
59
+ 'drivers' => 'drivers',
60
+ 'vehicles' => 'vehicles',
61
+ ];
62
+
63
+ /**
64
+ * Creates a new Fleetbase Fleet resource.
20
65
  *
21
66
  * @return \Fleetbase\Http\Resources\Fleet
22
67
  */
23
68
  public function create(CreateFleetRequest $request)
24
69
  {
25
- // get request input
26
- $input = $request->only(['name']);
70
+ $this->applyPublicExpansions($request, static::EXPANDABLE);
71
+
72
+ try {
73
+ $input = $this->fleetInputFromRequest($request);
74
+ } catch (PublicRelationNotFoundException $exception) {
75
+ return $this->jsonResponse(['error' => $exception->getMessage()], 404);
76
+ }
27
77
 
28
78
  // make sure company is set
29
79
  $input['company_uuid'] = session('company');
30
80
 
31
- // service area assignment
32
- if ($request->has('service_area')) {
33
- $input['service_area_uuid'] = $this->getServiceAreaUuid('service_areas', [
34
- 'public_id' => $request->input('service_area'),
35
- 'company_uuid' => session('company'),
36
- ]);
37
- }
38
-
39
81
  // create the fleet
40
82
  $fleet = $this->createFleet($input);
41
83
 
42
- // response the driver resource
43
- return $this->fleetResource($fleet);
84
+ // response the fleet resource
85
+ return $this->fleetResource($this->withPublicRelations($fleet));
44
86
  }
45
87
 
46
88
  /**
47
89
  * Updates a Fleetbase Fleet resource.
48
90
  *
49
- * @param string $id
50
- * @param \Fleetbase\Http\Requests\UpdateFleetRequest $request
91
+ * @param string $id
51
92
  *
52
93
  * @return \Fleetbase\Http\Resources\Fleet
53
94
  */
54
95
  public function update($id, UpdateFleetRequest $request)
55
96
  {
97
+ $this->applyPublicExpansions($request, static::EXPANDABLE);
98
+
56
99
  // find for the fleet
57
100
  try {
58
101
  $fleet = $this->findFleet($id);
59
- } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
102
+ } catch (ModelNotFoundException $exception) {
60
103
  return $this->jsonResponse(
61
104
  [
62
105
  'error' => 'Fleet resource not found.',
@@ -65,22 +108,23 @@ class FleetController extends Controller
65
108
  );
66
109
  }
67
110
 
68
- // get request input
69
- $input = $request->only(['name']);
111
+ try {
112
+ $input = $this->fleetInputFromRequest($request);
113
+ } catch (PublicRelationNotFoundException $exception) {
114
+ return $this->jsonResponse(['error' => $exception->getMessage()], 404);
115
+ }
70
116
 
71
- // service area assignment
72
- if ($request->has('service_area')) {
73
- $input['service_area_uuid'] = $this->getServiceAreaUuid('service_areas', [
74
- 'public_id' => $request->input('service_area'),
75
- 'company_uuid' => session('company'),
76
- ]);
117
+ // a fleet may not be its own parent, nor sit beneath one of its own descendants
118
+ $hierarchyError = $this->hierarchyViolation($fleet, $input);
119
+ if ($hierarchyError) {
120
+ return $this->jsonResponse(['error' => $hierarchyError], 422);
77
121
  }
78
122
 
79
123
  // update the fleet
80
124
  $fleet->update($input);
81
125
 
82
126
  // response the fleet resource
83
- return $this->fleetResource($fleet);
127
+ return $this->fleetResource($this->withPublicRelations($fleet));
84
128
  }
85
129
 
86
130
  /**
@@ -90,6 +134,8 @@ class FleetController extends Controller
90
134
  */
91
135
  public function query(Request $request)
92
136
  {
137
+ $this->applyPublicExpansions($request, static::EXPANDABLE);
138
+
93
139
  $results = $this->queryFleets($request);
94
140
 
95
141
  return $this->fleetResourceCollection($results);
@@ -102,10 +148,12 @@ class FleetController extends Controller
102
148
  */
103
149
  public function find($id, Request $request)
104
150
  {
151
+ $this->applyPublicExpansions($request, static::EXPANDABLE);
152
+
105
153
  // find for the fleet
106
154
  try {
107
155
  $fleet = $this->findFleet($id);
108
- } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
156
+ } catch (ModelNotFoundException $exception) {
109
157
  return $this->jsonResponse(
110
158
  [
111
159
  'error' => 'Fleet resource not found.',
@@ -115,7 +163,7 @@ class FleetController extends Controller
115
163
  }
116
164
 
117
165
  // response the fleet resource
118
- return $this->fleetResource($fleet);
166
+ return $this->fleetResource($this->withPublicRelations($fleet));
119
167
  }
120
168
 
121
169
  /**
@@ -128,7 +176,7 @@ class FleetController extends Controller
128
176
  // find for the driver
129
177
  try {
130
178
  $fleet = $this->findFleet($id);
131
- } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
179
+ } catch (ModelNotFoundException $exception) {
132
180
  return $this->jsonResponse(
133
181
  [
134
182
  'error' => 'Fleet resource not found.',
@@ -144,9 +192,286 @@ class FleetController extends Controller
144
192
  return $this->deletedFleetResource($fleet);
145
193
  }
146
194
 
147
- protected function getServiceAreaUuid(string $table, array $where): ?string
195
+ /**
196
+ * Adds a vehicle to a fleet.
197
+ *
198
+ * Idempotent: assigning a vehicle that is already a member answers exactly as
199
+ * the first assignment did and creates no second pivot row. A membership that
200
+ * was previously removed is restored rather than duplicated.
201
+ *
202
+ * @return \Illuminate\Http\Response
203
+ */
204
+ public function assignVehicle(string $id, string $vehicleId)
205
+ {
206
+ try {
207
+ $fleet = $this->findFleet($id);
208
+ $vehicle = $this->findVehicle($vehicleId);
209
+ } catch (ModelNotFoundException $exception) {
210
+ return $this->jsonResponse(['error' => 'Fleet or vehicle resource not found.'], 404);
211
+ }
212
+
213
+ $this->assignVehicleToFleet($fleet, $vehicle);
214
+
215
+ return $this->jsonResponse($this->vehicleMembershipPayload($fleet, $vehicle, true), 200);
216
+ }
217
+
218
+ /**
219
+ * Removes a vehicle from a fleet.
220
+ *
221
+ * Removing a membership that is not there is a successful no-op, and removing
222
+ * a membership never deletes the vehicle itself, its driver assignment, or its
223
+ * membership of any other fleet.
224
+ *
225
+ * @return \Illuminate\Http\Response
226
+ */
227
+ public function removeVehicle(string $id, string $vehicleId)
228
+ {
229
+ try {
230
+ $fleet = $this->findFleet($id);
231
+ $vehicle = $this->findVehicle($vehicleId);
232
+ } catch (ModelNotFoundException $exception) {
233
+ return $this->jsonResponse(['error' => 'Fleet or vehicle resource not found.'], 404);
234
+ }
235
+
236
+ $this->removeVehicleFromFleet($fleet, $vehicle);
237
+
238
+ return $this->jsonResponse($this->vehicleMembershipPayload($fleet, $vehicle, false), 200);
239
+ }
240
+
241
+ /**
242
+ * Adds a driver to a fleet.
243
+ *
244
+ * @return \Illuminate\Http\Response
245
+ */
246
+ public function assignDriver(string $id, string $driverId)
247
+ {
248
+ try {
249
+ $fleet = $this->findFleet($id);
250
+ $driver = $this->findDriver($driverId);
251
+ } catch (ModelNotFoundException $exception) {
252
+ return $this->jsonResponse(['error' => 'Fleet or driver resource not found.'], 404);
253
+ }
254
+
255
+ $this->assignDriverToFleet($fleet, $driver);
256
+
257
+ return $this->jsonResponse($this->driverMembershipPayload($fleet, $driver, true), 200);
258
+ }
259
+
260
+ /**
261
+ * Removes a driver from a fleet.
262
+ *
263
+ * The driver keeps its vehicle assignment and every other fleet membership.
264
+ *
265
+ * @return \Illuminate\Http\Response
266
+ */
267
+ public function removeDriver(string $id, string $driverId)
268
+ {
269
+ try {
270
+ $fleet = $this->findFleet($id);
271
+ $driver = $this->findDriver($driverId);
272
+ } catch (ModelNotFoundException $exception) {
273
+ return $this->jsonResponse(['error' => 'Fleet or driver resource not found.'], 404);
274
+ }
275
+
276
+ $this->removeDriverFromFleet($fleet, $driver);
277
+
278
+ return $this->jsonResponse($this->driverMembershipPayload($fleet, $driver, false), 200);
279
+ }
280
+
281
+ /**
282
+ * Preserve the implicit subfleet nesting the released contract had.
283
+ *
284
+ * `?with[]=subfleets&with[]=drivers` used to load the subfleets' drivers as
285
+ * well as the fleet's own, so the nested collections were part of that
286
+ * response. The explicit `?with[]=subfleets.drivers` form is the better
287
+ * spelling, but dropping the implicit one would remove data a caller is
288
+ * already receiving.
289
+ *
290
+ * @param array<string, string> $allowed
291
+ *
292
+ * @return array<int, string>
293
+ */
294
+ protected function resolvePublicExpansions(Request $request, array $allowed): array
295
+ {
296
+ $resolved = $this->resolveAllowedExpansions($request, $allowed);
297
+
298
+ if (!in_array('subFleets', $resolved, true)) {
299
+ return $resolved;
300
+ }
301
+
302
+ foreach (['drivers', 'vehicles'] as $nested) {
303
+ $path = 'subFleets.' . $nested;
304
+
305
+ if (in_array($nested, $resolved, true) && !in_array($path, $resolved, true)) {
306
+ $resolved[] = $path;
307
+ }
308
+ }
309
+
310
+ return $resolved;
311
+ }
312
+
313
+ /**
314
+ * The explicit public input allowlist for a fleet.
315
+ *
316
+ * Everything outside this list is either generated (`public_id`, `slug`,
317
+ * `uuid`, `_key`), tenancy (`company_uuid`) or a raw relation column, and is
318
+ * resolved from a public id rather than accepted directly.
319
+ *
320
+ * @throws PublicRelationNotFoundException
321
+ */
322
+ protected function fleetInputFromRequest(Request $request): array
323
+ {
324
+ $input = $request->only(['name', 'color', 'task', 'status']);
325
+
326
+ $this->applyPublicIdRelations($input, [
327
+ 'service_area' => ['service_area_uuid', ServiceArea::class],
328
+ 'zone' => ['zone_uuid', Zone::class],
329
+ 'vendor' => ['vendor_uuid', Vendor::class],
330
+ 'parent_fleet' => ['parent_fleet_uuid', Fleet::class],
331
+ 'photo' => ['image_uuid', File::class],
332
+ ], $request);
333
+
334
+ return $input;
335
+ }
336
+
337
+ /**
338
+ * Reject a parent assignment that would make the tree cyclic.
339
+ *
340
+ * Returns the error message to answer with, or null when the assignment is
341
+ * sound. Walking upward from the proposed parent is enough: a cycle exists
342
+ * exactly when this fleet is already somewhere on that chain.
343
+ */
344
+ protected function hierarchyViolation(Fleet $fleet, array $input): ?string
345
+ {
346
+ if (!array_key_exists('parent_fleet_uuid', $input) || empty($input['parent_fleet_uuid'])) {
347
+ return null;
348
+ }
349
+
350
+ $parentUuid = $input['parent_fleet_uuid'];
351
+
352
+ if ($parentUuid === $fleet->uuid) {
353
+ return 'A fleet cannot be its own parent fleet.';
354
+ }
355
+
356
+ $seen = [$parentUuid => true];
357
+ $ancestor = $this->parentUuidOf($parentUuid);
358
+
359
+ while ($ancestor && !isset($seen[$ancestor])) {
360
+ if ($ancestor === $fleet->uuid) {
361
+ return 'A fleet cannot be assigned beneath one of its own subfleets.';
362
+ }
363
+
364
+ $seen[$ancestor] = true;
365
+ $ancestor = $this->parentUuidOf($ancestor);
366
+ }
367
+
368
+ return null;
369
+ }
370
+
371
+ protected function parentUuidOf(string $uuid): ?string
372
+ {
373
+ return Fleet::where('uuid', $uuid)->value('parent_fleet_uuid');
374
+ }
375
+
376
+ protected function withPublicRelations(Fleet $fleet): Fleet
377
+ {
378
+ $fleet->loadMissing(static::PUBLIC_RELATIONS);
379
+
380
+ return $fleet;
381
+ }
382
+
383
+ protected function assignVehicleToFleet(Fleet $fleet, Vehicle $vehicle): void
384
+ {
385
+ $this->assignMembership(FleetVehicle::class, [
386
+ 'fleet_uuid' => $fleet->uuid,
387
+ 'vehicle_uuid' => $vehicle->uuid,
388
+ ]);
389
+ }
390
+
391
+ /**
392
+ * Create or restore exactly one membership row for a pivot pair.
393
+ *
394
+ * Read-then-write is idempotent only against itself. Two requests arriving
395
+ * together both see no membership and both insert, which is precisely what
396
+ * an importer retrying a timed-out call produces. The composite unique index
397
+ * is the actual guarantee; this catches the violation the loser gets and
398
+ * finishes the winner's work, so both callers still receive the same
399
+ * successful answer.
400
+ *
401
+ * @param class-string $pivotClass
402
+ * @param array<string, string> $attributes
403
+ */
404
+ protected function assignMembership(string $pivotClass, array $attributes): void
405
+ {
406
+ $membership = $pivotClass::withTrashed()->firstOrNew($attributes);
407
+
408
+ if ($membership->trashed()) {
409
+ $membership->restore();
410
+
411
+ return;
412
+ }
413
+
414
+ if ($membership->exists) {
415
+ return;
416
+ }
417
+
418
+ try {
419
+ $membership->save();
420
+ } catch (UniqueConstraintViolationException $exception) {
421
+ // Only this violation is swallowed. Anything else is a real failure
422
+ // and must not be reported as a successful assignment.
423
+ $winner = $pivotClass::withTrashed()->where($attributes)->first();
424
+
425
+ if (!$winner) {
426
+ throw $exception;
427
+ }
428
+
429
+ if ($winner->trashed()) {
430
+ $winner->restore();
431
+ }
432
+ }
433
+ }
434
+
435
+ protected function removeVehicleFromFleet(Fleet $fleet, Vehicle $vehicle): void
148
436
  {
149
- return Utils::getUuid($table, $where);
437
+ FleetVehicle::where([
438
+ 'fleet_uuid' => $fleet->uuid,
439
+ 'vehicle_uuid' => $vehicle->uuid,
440
+ ])->delete();
441
+ }
442
+
443
+ protected function assignDriverToFleet(Fleet $fleet, Driver $driver): void
444
+ {
445
+ $this->assignMembership(FleetDriver::class, [
446
+ 'fleet_uuid' => $fleet->uuid,
447
+ 'driver_uuid' => $driver->uuid,
448
+ ]);
449
+ }
450
+
451
+ protected function removeDriverFromFleet(Fleet $fleet, Driver $driver): void
452
+ {
453
+ FleetDriver::where([
454
+ 'fleet_uuid' => $fleet->uuid,
455
+ 'driver_uuid' => $driver->uuid,
456
+ ])->delete();
457
+ }
458
+
459
+ protected function vehicleMembershipPayload(Fleet $fleet, Vehicle $vehicle, bool $assigned): array
460
+ {
461
+ return [
462
+ 'fleet' => $fleet->public_id,
463
+ 'vehicle' => $vehicle->public_id,
464
+ 'assigned' => $assigned,
465
+ ];
466
+ }
467
+
468
+ protected function driverMembershipPayload(Fleet $fleet, Driver $driver, bool $assigned): array
469
+ {
470
+ return [
471
+ 'fleet' => $fleet->public_id,
472
+ 'driver' => $driver->public_id,
473
+ 'assigned' => $assigned,
474
+ ];
150
475
  }
151
476
 
152
477
  protected function createFleet(array $input): Fleet
@@ -159,9 +484,21 @@ class FleetController extends Controller
159
484
  return Fleet::findRecordOrFail($id);
160
485
  }
161
486
 
487
+ protected function findVehicle(string $id): Vehicle
488
+ {
489
+ return Vehicle::findRecordOrFail($id);
490
+ }
491
+
492
+ protected function findDriver(string $id): Driver
493
+ {
494
+ return Driver::findRecordOrFail($id);
495
+ }
496
+
162
497
  protected function queryFleets(Request $request)
163
498
  {
164
- return Fleet::queryWithRequest($request);
499
+ return Fleet::queryWithRequest($request, function (&$query) {
500
+ $query->with(static::PUBLIC_RELATIONS);
501
+ });
165
502
  }
166
503
 
167
504
  protected function fleetResource(Fleet $fleet)
@@ -157,7 +157,7 @@ class FuelReportController extends Controller
157
157
  return $this->deletedFuelReportResource($fuelReport);
158
158
  }
159
159
 
160
- protected function findDriverRecord(string $id): Driver
160
+ protected function findDriverRecord(?string $id): Driver
161
161
  {
162
162
  return Driver::findRecordOrFail($id);
163
163
  }
@@ -158,7 +158,7 @@ class IssueController extends Controller
158
158
  return $this->deletedIssueResource($issue);
159
159
  }
160
160
 
161
- protected function findDriverRecord(string $id): Driver
161
+ protected function findDriverRecord(?string $id): Driver
162
162
  {
163
163
  return Driver::findRecordOrFail($id);
164
164
  }
@@ -1873,7 +1873,7 @@ class OrderController extends Controller
1873
1873
  return new Payload();
1874
1874
  }
1875
1875
 
1876
- protected function findDriverByPublicId(string $publicId): ?Driver
1876
+ protected function findDriverByPublicId(?string $publicId): ?Driver
1877
1877
  {
1878
1878
  return Driver::where(['public_id' => $publicId, 'company_uuid' => session('company')])->first();
1879
1879
  }
@@ -218,7 +218,7 @@ class ServiceAreaController extends Controller
218
218
  return ServiceArea::createMultiPolygonFromPoint($point, $radius);
219
219
  }
220
220
 
221
- protected function serviceAreaUuid(string $publicId, array $where): ?string
221
+ protected function serviceAreaUuid(?string $publicId, array $where): ?string
222
222
  {
223
223
  return Utils::getUuid('service_areas', $where);
224
224
  }
@@ -187,7 +187,7 @@ class TrackingStatusController extends Controller
187
187
  return Utils::getUuid($table, $where);
188
188
  }
189
189
 
190
- protected function getOrderTrackingNumberUuid(string $orderId): ?string
190
+ protected function getOrderTrackingNumberUuid(?string $orderId): ?string
191
191
  {
192
192
  return Order::where('public_id', $orderId)->value('tracking_number_uuid');
193
193
  }