Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/Http/Controllers/AircraftController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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')) {
Expand Down
37 changes: 15 additions & 22 deletions resources/views/fleet/edit.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,34 +190,27 @@
</div>
</div>

<!-- Card 5: Current Location (Read-Only) -->
<!-- Card 5: Current Location (manager override) -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white py-3 fw-bold border-bottom d-flex align-items-center">
<i class="bi bi-geo-alt-fill text-muted me-2"></i> Current Location
</div>
<div class="card-body p-4">
<div class="p-3 bg-light rounded-3 border d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center">
<i class="bi bi-geo-alt-fill text-danger fs-4 me-3"></i>
<div>
@if(is_null($aircraft->current_loc))
<span class="badge bg-warning text-dark fs-6 px-2 py-1 mb-1">
No Location
</span>
<small class="d-block text-muted">Awaiting first flight log</small>
@else
<span class="badge bg-dark font-monospace fs-6 px-2 py-1 mb-1" title="{{ $aircraft->location->name }}">
{{ $aircraft->location->icao_code }}
</span>
<small class="d-block text-muted">{{ $aircraft->location->name }}</small>
@endif
</div>
</div>
<span class="badge bg-secondary-subtle text-secondary border border-secondary-subtle fs-8 uppercase tracking-wider px-2 py-1">
<i class="bi bi-lock-fill me-1"></i> Admin Only
</span>
<label for="current_loc" class="form-label fw-semibold">Location <span class="text-danger">*</span></label>
<div class="input-group">
<span class="input-group-text bg-light text-muted"><i class="bi bi-geo-alt-fill"></i></span>
<input type="text" class="form-control text-uppercase font-monospace @error('current_loc') is-invalid @enderror"
id="current_loc" name="current_loc"
value="{{ old('current_loc', $aircraft->current_loc) }}"
minlength="4" maxlength="4" placeholder="e.g. EDDL" required>
@error('current_loc')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-text mt-2 fs-7 text-muted">
4-letter ICAO code. Normally set automatically from pilots' flight logs -
edit this to manually relocate a stranded or mis-parked aircraft.
</div>
<div class="form-text mt-2 fs-7 text-muted">Current location updates dynamically via pilots' flight logs.</div>
</div>
</div>

Expand Down
51 changes: 51 additions & 0 deletions tests/Feature/LocationContinuityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}
}
Loading