From 3ba9a01e37762e4ba5240354745d3c493fee65b4 Mon Sep 17 00:00:00 2001 From: Marc Sauer Date: Thu, 23 Jul 2026 11:11:10 +0200 Subject: [PATCH] Allow managers to override aircraft location - Make the fleet edit form's location card an editable ICAO input (was read-only "Admin Only") - Validate and persist current_loc in AircraftController::edit(), reusing the create-form airport check - Enables relocating a stranded/mis-parked airframe even under Location Continuity (the escape hatch) - Change is auto-audited via the existing activity-log trait - Add LocationContinuity tests for a successful override and rejection of an unknown airport --- app/Http/Controllers/AircraftController.php | 8 ++++ resources/views/fleet/edit.blade.php | 37 ++++++--------- tests/Feature/LocationContinuityTest.php | 51 +++++++++++++++++++++ 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/app/Http/Controllers/AircraftController.php b/app/Http/Controllers/AircraftController.php index ca20897..d3f5f3a 100644 --- a/app/Http/Controllers/AircraftController.php +++ b/app/Http/Controllers/AircraftController.php @@ -246,8 +246,15 @@ public function edit(Request $request, Aircraft $aircraft) { 'mlw' => 'nullable|integer|min:0|max:1000000', 'service_ceiling' => 'nullable|integer|min:1000|max:60000', 'remarks' => 'nullable|string|max:1000', + 'current_loc' => 'required|max:4', ]); + // Managers can override the aircraft's location at any time (the escape + // hatch for Location Continuity - relocate a stranded/mis-parked airframe). + if (!Airport::find($request->post('current_loc'))) { + throw ValidationException::withMessages(['current_loc' => 'This airport could not be found in the database.']); + } + // Convert to boolean and format strings. The toggle only moves between the two // reversible states - retiring an aircraft is a separate, dedicated action. $finalStatus = $request->boolean('active') ? Aircraft::STATUS_ACTIVE : Aircraft::STATUS_INACTIVE; @@ -274,6 +281,7 @@ public function edit(Request $request, Aircraft $aircraft) { $targetAircraft->service_ceiling = $request->post('service_ceiling'); $targetAircraft->status = $finalStatus; $targetAircraft->remarks = $request->post('remarks'); + $targetAircraft->current_loc = strtoupper($request->post('current_loc')); // If we notice that the registration has changed or active status, we need to check if there is another active aircraft with same tail number if ($targetAircraft->isDirty('registration') || $targetAircraft->isDirty('status')) { diff --git a/resources/views/fleet/edit.blade.php b/resources/views/fleet/edit.blade.php index 79c2401..0e697a1 100644 --- a/resources/views/fleet/edit.blade.php +++ b/resources/views/fleet/edit.blade.php @@ -190,34 +190,27 @@ - +
Current Location
-
-
- -
- @if(is_null($aircraft->current_loc)) - - No Location - - Awaiting first flight log - @else - - {{ $aircraft->location->icao_code }} - - {{ $aircraft->location->name }} - @endif -
-
- - Admin Only - + +
+ + + @error('current_loc') +
{{ $message }}
+ @enderror +
+
+ 4-letter ICAO code. Normally set automatically from pilots' flight logs - + edit this to manually relocate a stranded or mis-parked aircraft.
-
Current location updates dynamically via pilots' flight logs.
diff --git a/tests/Feature/LocationContinuityTest.php b/tests/Feature/LocationContinuityTest.php index 4424e74..bff2128 100644 --- a/tests/Feature/LocationContinuityTest.php +++ b/tests/Feature/LocationContinuityTest.php @@ -7,6 +7,7 @@ use App\Models\Flight; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; +use Spatie\Permission\Models\Permission; use Tests\Concerns\SeedsDomain; use Tests\TestCase; @@ -124,4 +125,54 @@ public function test_rejection_does_not_revert_when_a_later_leg_already_moved_th $this->assertSame('KJFK', $aircraft->fresh()->current_loc); } + + /** + * A manager may edit an aircraft (needs the per-airline Manager role for the + * policy and the Spatie `edit aircraft` permission for the route middleware). + */ + private function managerOf(Airline $airline): User + { + $manager = $this->memberOf($airline, 'Manager'); + $manager->givePermissionTo(Permission::findOrCreate('edit aircraft', 'web')); + + return $manager; + } + + private function editPayload(Aircraft $aircraft, string $currentLoc): array + { + return [ + 'registration' => $aircraft->registration, + 'manufacturer' => 'Airbus', + 'model' => 'A320', + 'engine_type' => 'CFM56', + 'current_loc' => $currentLoc, + 'active' => 1, + ]; + } + + public function test_manager_can_override_aircraft_location_while_continuity_is_active(): void + { + [$airline, $aircraft] = $this->airlineWithAircraftAt('EDDF'); + $manager = $this->managerOf($airline); + + $this->actingAs($manager) + ->withSession(['activeairline' => $airline]) + ->post(route('editaircraft', $aircraft), $this->editPayload($aircraft, 'KLAX')) + ->assertRedirect(route('fleetmanager')); + + $this->assertSame('KLAX', $aircraft->fresh()->current_loc); + } + + public function test_overriding_the_location_with_an_unknown_airport_is_rejected(): void + { + [$airline, $aircraft] = $this->airlineWithAircraftAt('EDDF'); + $manager = $this->managerOf($airline); + + $this->actingAs($manager) + ->withSession(['activeairline' => $airline]) + ->post(route('editaircraft', $aircraft), $this->editPayload($aircraft, 'ZZZZ')) + ->assertSessionHasErrors('current_loc'); + + $this->assertSame('EDDF', $aircraft->fresh()->current_loc); + } }