diff --git a/composer.json b/composer.json index 00e138af..be0effb4 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "fleetbase/fleetops-api", - "version": "0.6.36", + "version": "0.6.37", "description": "Fleet & Transport Management Extension for Fleetbase", "keywords": [ "fleetbase-extension", diff --git a/extension.json b/extension.json index 5136cfa8..6dad2d34 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "Fleet-Ops", - "version": "0.6.36", + "version": "0.6.37", "description": "Fleet & Transport Management Extension for Fleetbase", "repository": "https://github.com/fleetbase/fleetops", "license": "AGPL-3.0-or-later", diff --git a/package.json b/package.json index e0b4b294..c33e2082 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/fleetops-engine", - "version": "0.6.36", + "version": "0.6.37", "description": "Fleet & Transport Management Extension for Fleetbase", "fleetbase": { "route": "fleet-ops" diff --git a/server/src/Http/Controllers/Internal/v1/DriverController.php b/server/src/Http/Controllers/Internal/v1/DriverController.php index 1e966f61..93e6452b 100644 --- a/server/src/Http/Controllers/Internal/v1/DriverController.php +++ b/server/src/Http/Controllers/Internal/v1/DriverController.php @@ -45,6 +45,17 @@ public function createRecord(Request $request) { $input = $request->input('driver'); + // Normalize vehicle field - the frontend may send the full vehicle object, + // a UUID string, or a public_id string. Normalize to a single identifier + // so the ResolvableVehicle rule can validate it correctly. + if (isset($input['vehicle']) && is_array($input['vehicle'])) { + $input['vehicle'] = data_get($input['vehicle'], 'id') + ?? data_get($input['vehicle'], 'public_id') + ?? data_get($input['vehicle'], 'uuid') + ?? null; + $request->merge(['driver' => $input]); + } + // create validation request $createDriverRequest = CreateDriverRequest::createFrom($request); $rules = $createDriverRequest->rules(); @@ -262,6 +273,17 @@ public function updateRecord(Request $request, string $id) // get input data $input = $request->input('driver'); + // Normalize vehicle field - the frontend may send the full vehicle object, + // a UUID string, or a public_id string. Normalize to a single identifier + // so the ResolvableVehicle rule can validate it correctly. + if (isset($input['vehicle']) && is_array($input['vehicle'])) { + $input['vehicle'] = data_get($input['vehicle'], 'id') + ?? data_get($input['vehicle'], 'public_id') + ?? data_get($input['vehicle'], 'uuid') + ?? null; + $request->merge(['driver' => $input]); + } + // create validation request $updateDriverRequest = UpdateDriverRequest::createFrom($request); $rules = $updateDriverRequest->rules(); diff --git a/server/src/Http/Requests/Internal/CreateDriverRequest.php b/server/src/Http/Requests/Internal/CreateDriverRequest.php index 59deb2ad..8dac9771 100644 --- a/server/src/Http/Requests/Internal/CreateDriverRequest.php +++ b/server/src/Http/Requests/Internal/CreateDriverRequest.php @@ -4,6 +4,7 @@ use Fleetbase\FleetOps\Http\Requests\CreateDriverRequest as CreateDriverApiRequest; use Fleetbase\FleetOps\Rules\ResolvablePoint; +use Fleetbase\FleetOps\Rules\ResolvableVehicle; use Fleetbase\Support\Auth; use Illuminate\Validation\Rule; @@ -49,7 +50,7 @@ public function rules() 'internal_id' => 'nullable|string|max:255', 'country' => 'nullable|string|size:2', 'city' => 'nullable|string|max:255', - 'vehicle' => 'nullable|string|starts_with:vehicle_|exists:vehicles,public_id', + 'vehicle' => ['nullable', new ResolvableVehicle()], 'status' => 'nullable|string|in:active,inactive', 'vendor' => 'nullable|exists:vendors,public_id', 'job' => 'nullable|exists:orders,public_id', diff --git a/server/src/Rules/ResolvableVehicle.php b/server/src/Rules/ResolvableVehicle.php new file mode 100644 index 00000000..42aea235 --- /dev/null +++ b/server/src/Rules/ResolvableVehicle.php @@ -0,0 +1,90 @@ +extractIdentifier($value); + + if (empty($identifier)) { + return true; // nullable — let the nullable rule handle empty values + } + + if (Str::isUuid($identifier)) { + $this->resolved = Vehicle::where('uuid', $identifier)->first(); + } else { + $this->resolved = Vehicle::where('public_id', $identifier)->first(); + } + + return $this->resolved !== null; + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return 'The :attribute must be a valid vehicle public ID, UUID, or vehicle object.'; + } + + /** + * Extract a string identifier from the given value. + * + * Handles a plain string, an associative array, or a stdClass object. + */ + protected function extractIdentifier($value): ?string + { + if (is_string($value)) { + return $value; + } + + if (is_array($value)) { + return data_get($value, 'id') + ?? data_get($value, 'public_id') + ?? data_get($value, 'uuid') + ?? null; + } + + if (is_object($value)) { + return data_get($value, 'id') + ?? data_get($value, 'public_id') + ?? data_get($value, 'uuid') + ?? null; + } + + return null; + } + + /** + * Get the resolved Vehicle model instance after validation passes. + */ + public function getResolved(): ?Vehicle + { + return $this->resolved; + } +}