Skip to content

FEAT: VAA Event Stand Planning#1810

Open
MrAdder wants to merge 22 commits intoVATSIM-UK:mainfrom
MrAdder:issue-1763
Open

FEAT: VAA Event Stand Planning#1810
MrAdder wants to merge 22 commits intoVATSIM-UK:mainfrom
MrAdder:issue-1763

Conversation

@MrAdder
Copy link
Contributor

@MrAdder MrAdder commented Feb 15, 2026

Fixes #1763

🛠️ Summary of Changes in FEAT: VAA Event Stand Planning (#1810)

This PR introduces a complete stand reservation plan submission and approval workflow with backend logic, API endpoints, and admin UI support.

✨ New Features

  • Stand Reservation Plan Workflow

    • Added StandReservationPlan model to store JSON payloads, approval metadata, and reservation counts.
    • Includes new scopes (pending, pendingWithinApprovalWindow) and a new migration for the stand_reservation_plans table.
  • New VAA Role

    • Added new enum RoleKeys::VAA.
    • Migration creates the vaa role if not present.
    • Only users with this role can submit stand reservation plans.
  • JSON Support for Importing Stand Reservations

    • stand-reservations:import console command now supports .json files and root-level arrays.
    • Extraction supports default or explicit date fields.
  • Refactored Import Logic

    • StandReservationsImport now:
      • Supports indexed JSON rows.
      • Extracts and validates fields including cid, origin, destination.
      • Outputs optional progress and returns the count of created reservations.
      • Skips invalid rows with warnings.

📡 API Endpoints Added

  • POST /stand/reservations/plan: Submit a new plan (VAA role required).
  • GET /stand/reservations/plan/pending: List pending plans.
  • PATCH /stand/reservations/plan/{id}/approve: Approve a pending plan within its approval window, import reservations, and update approval metadata.

🧑‍💼 Admin UI (Filament)

  • Added StandReservationPlanSubmission page for authorised roles (VAA, Web Team, Ops Team, Division Staff):
    • Validates JSON format
    • Allows plan submission
    • Shows success notifications

🧪 Tests Updated

  • Console command test updated to rename assertion and ensure success return code.
  • Added missing Collection import for tests.

Overall, this PR implements a robust stand reservation planning feature with role-based submissions, import support for JSON, administrative UI, and approval workflows.

@kristiankunc kristiankunc self-requested a review February 15, 2026 16:51
@kristiankunc
Copy link
Contributor

  1. I very much dislike this UI, can we use the filament builtins?
image
  1. We probably gotta document the accepted format a bit more somewhere

  2. After I approve a stand plan - it seems to disappear, we probably need some better flow

Instead of multiple pages, I suggest creating a single one for uploading, reviewing and history tracking. Take a look at the recently added SMR WIP area management - those are decent and you just gotta add the approval flow.

@MrAdder
Copy link
Contributor Author

MrAdder commented Feb 20, 2026

Just a couple of updates, uses the filament builtins, approved stand are now included,

image

Started some documentation aswell (zoomed out screenshot due to not having my snapshot tool for entire page)

Removed useless pages
Made into single page format with filament builtins (Ref Screenshot in PR)
started some documentation
shows Review history
@kristiankunc
Copy link
Contributor

kristiankunc commented Feb 20, 2026

Looks much better - maybe we could publish a JSON schema? Probably would make it clear and we could have the users validate their data beforehand / know the exact spect we're looking for.

@MrAdder
Copy link
Contributor Author

MrAdder commented Feb 20, 2026

Requested the issue creator to look at the Images and the doc's that I have modified to check it ticks the boxes for what was proposed, from a backend side is to make sure it actually reserves the stand for the user and if we need to make modifications to the plugin itself.

@kristiankunc
Copy link
Contributor

Plugin changes should not be required, stand assignments are handled on the server + given some of the stand reservation logic is already present we can assume that it works.

@MrAdder
Copy link
Contributor Author

MrAdder commented Feb 25, 2026

Plugin changes should not be required, stand assignments are handled on the server + given some of the stand reservation logic is already present we can assume that it works.

Good to here no response from Issue poster so at your discretion if your happy with the changes feel free with the merge

@cpawley
Copy link

cpawley commented Feb 25, 2026

I dont think I have strong opinions about how it might work

main concerns are

is it documented well - looks like it is
can we add a 3rd party the (temporary) authority to do this - looks like it isnt

if we give VAA's stand assignment authority we have to be able to protect ourselves from them abusing this

thanks for moving this forward :)

@MrAdder
Copy link
Contributor Author

MrAdder commented Feb 25, 2026

I dont think I have strong opinions about how it might work

main concerns are

is it documented well - looks like it is can we add a 3rd party the (temporary) authority to do this - looks like it isnt

if we give VAA's stand assignment authority we have to be able to protect ourselves from them abusing this

thanks for moving this forward :)

@cpawley VAA role has only Upload permissions, authority of Approval is done by the other roles from Web/Admin team of UKCP potentially may add another role for Events team to approve but that is a different discussion

I can look at adding Temporary role placement for the VAA role as well as the static role for verified VAA's (eg: BAV)

@cpawley
Copy link

cpawley commented Feb 25, 2026

then perfect, concept wise im happy :) this adds a huge amount of flexibility to our system and offers VAA's the chance to be actively involved in events where they take the driving seat :)

thanks!

@MrAdder
Copy link
Contributor Author

MrAdder commented Feb 25, 2026

I just realised a small flaw in my code in regards to the slot planning, It is currently set PER slot for one callsign for the entire event period but in regards to as an Example Speedbird24 its multiple different callsigns at a "Slot" so need to tweak the JSON formatting to accommodate this and the System on the backend.

So currently this is how it works

BAW1234 is expected between 9:00 and 9:30 so Stand 531 is reserved for them
BAW4321 is expected between 9:31 and 10:00 on Stand 531 and the stand according to the system is still reserved for BAW1234

The system currently reserves the slot for the entire Period of the Event for BAW1234

Proposed change to how it will work

BAW1234 is expected between 9:00 and 9:30 on Stand 531 and the stand is reserved for them
BAW4321 is expected between 9:31 and 10:00 on Stand 531 and the stand is reserved for them

System Picks up the change from within the plan that's uploaded by the VAA and so VATUK can limit (if they wish) the amount of Stands an Event can use, and can also assist planning teams of the VAA

@cpawley
Copy link

cpawley commented Feb 25, 2026

since some events can be 8->24 hrs this would need to be adopted - they need to assign stands for multiple time periods in the day

@MrAdder
Copy link
Contributor Author

MrAdder commented Feb 25, 2026

since some events can be 8->24 hrs this would need to be adopted - they need to assign stands for multiple time periods in the day

Downside of thinking about code while watching movies with the family

@MrAdder
Copy link
Contributor Author

MrAdder commented Feb 26, 2026

🧾 Summary of changes (Activation + slot-based stand reservation plans)

🆕 New scheduled command: activate plans + sync assignments

  • Adds stand-reservations:activate-plans (ActivateStandReservationPlans):
    • 🔁 Runs every minute (added to App\Console\Kernel)
    • ✅ Finds approved StandReservationPlans where imported_reservations is null
    • ⏱️ Only imports a plan once its event window has started (event_start or legacy start)
    • 📥 Imports reservations via StandReservationsImport::importReservations() using a shared payload → rows mapper
    • 🧠 Synchronises live stand assignments for active reservations:
      • Matches network aircraft by callsign (or CID) + optional origin/destination
      • Creates/updates a stand assignment with reason “Reservation”
      • Tracks “managed” assignments in cache (stand_reservations:managed_assignments)
      • Automatically lifts previously managed assignments once their reservation is no longer active

🧰 Import tooling: JSON now supports stand_slots

  • Updates stand-reservations:import command JSON parsing to support:
    • ✅ Legacy flat rows (reservations array or raw array of rows)
    • ✅ Slot-based payloads:
      • stand_slots[] containers with slot_reservations[]
      • Allows inheriting airfield/stand from the parent slot when omitted
  • Default datetime resolution updated:
    • Uses event_start/event_finish first, then falls back to start/end

🌐 API: reservation slots take precedence over auto allocation

  • Updates StandController::requestAutomaticStandAssignment():

    • 🥇 If an active reservation exists for the aircraft (callsign/CID + optional route match),
      it forces the reserved stand assignment before distance-based auto allocation.
    • Adds helper activeReservedStandIdForAircraft() to locate the best matching active reservation.
  • Updates reservation plan upload/approval handling to accept slot-based payloads:

    • reservations and stand_slots are now nullable
    • Adds event_start/event_finish support
    • Uses a shared rowsFromPayload() method to flatten both formats for import

📚 Docs + Schema updates

  • Updates Stand Allocation guide to document:
    • ✅ Slot-based plan format (stand_slots + slot_reservations)
    • ✅ Event-day activation task behaviour
  • Updates schema markdown and JSON schema files to:
    • Require at least one of reservations or stand_slots
    • Add event_start/event_finish
    • Define standSlot + slotReservation structures
    • Remove active_from/active_to from the request schema in this version

🧪 Tests added/updated

  • ✅ New ActivateStandReservationPlansTest covers:
    • Importing approved plans once event_start is due
    • Skipping plans with future event_start
    • Creating reservation-managed assignments and lifting expired ones via cache tracking
  • ✅ Updates StandReservationsImportTest to validate slot-based JSON import flattening
  • ✅ Updates API + Filament tests to use slot-based plan examples
  • ✅ Adds controller test to confirm active reservations override automatic assignment

🔁 Backwards compatibility notes

  • Flat reservations format remains supported ✅
  • Top-level defaults still support start/end
  • Slot format (stand_slots) is the preferred structure going forward ✅

@kristiankunc
Copy link
Contributor

This is way too big for me to review atm. Coby might take a look

@kristiankunc kristiankunc requested a review from CLC0609 February 26, 2026 21:49
@CLC0609
Copy link
Contributor

CLC0609 commented Feb 26, 2026

Just to confirm, are we matching aircraft to stands via CID or Callsign?

@MrAdder
Copy link
Contributor Author

MrAdder commented Feb 27, 2026

both, but for arrival stand allocation the system tries CID-reserved first, then (after user-requested) callsign+flightplan reserved.

  • The allocator order in StandServiceProvider puts CidReservedArrivalStandAllocator before CallsignFlightplanReservedArrivalStandAllocator.

  • CID matching is explicitly done on cid (plus destination).

  • Callsign reservation matching is done on callsign plus origin/destination flightplan fields.

  • The docs also describe rule order as CID Reserved, then Callsign Reserved.

For plan-activation matching specifically, it checks by callsign, and if CID is present it also allows matching by CID (callsign OR cid).

If required I can switch it to whichever you would prefer, from an event space we can not reserve callsigns, hence the reason for CID matching.

Copy link
Contributor

@CLC0609 CLC0609 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quite a big review so apologies if I have missed something that makes one of my comments irrelevant.

In terms of CID vs Callsign matching.

I think we should use CID matching only instead of callsigns. I do not see a reason that they would need to provide both or have both allocators as, if we do it by CID irrelevant of the callsign the user is using they would get assigned the correct stand. Could you update the docs to reflect using CID only as well please.

$schedule->command('plugin-events:clean')->everyTenMinutes()->doNotMonitor();
$schedule->command('metars:update')->everyMinute();
$schedule->command('database:check-table-updates')->everyMinute();
$schedule->command('stand-reservations:activate-plans')->everyMinute()->doNotMonitor();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to run every minute, I feel its unlikely that an event would start its stand allocation at 13:03 for example and not just 13:00? Maybe 10 or 15 minutes instead just to make this slightly less resource intensive?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need both this file and app/Support/StandReservationPayloadRows.php, they seem to do very similar things?

private function matchingAircraftForReservation(StandReservation $reservation): ?NetworkAircraft
{
$query = NetworkAircraft::query()->notTimedOut()->where(function ($query) use ($reservation) {
$query->where('callsign', $reservation->callsign);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this match with the CID not the callsign? Otherwise the stand could be assigned to a random aircraft who took the callsign first.

- `stand_slots`: array of one or more stand-slot objects.
- Optional top-level default datetimes:
- `event_start` / `event_finish` (preferred event-window names)
- `start` / `end` (legacy)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is legacy does it need to be included in the documentation?

Comment on lines +38 to +40
- Exactly one of:
- `airfield`: ICAO code, or
- `airport`: ICAO code alias (optional when inherited from `stand_slots[].airfield` / `airport`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we standardise to using airport instead of airfield throughout the documentation and the code. So instead of allowing airport and airfield just have airport inline with the rest of the API.

This will remove the need for lines like this $slotAirfield = $standSlot['airfield'] ?? $standSlot['airport'] ?? null;


### Event-day activation task

Approved plans can be activated automatically on event day with:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be in the public facing documentation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we split this into two jobs. One to activate the reservation plans, and one to do the stand reservation logic. It seems we deviated from the name ActivateStandReservationPlans.

The activate plan job can run less often than the job handling the stand reservation.

@MrAdder
Copy link
Contributor Author

MrAdder commented Feb 27, 2026

Totally fine it is a massive feature will look at your review later and start replying/making changes

MrAdder and others added 5 commits February 27, 2026 13:13
Co-authored-by: Coby Chapman <cobylc.121@gmail.com>
Co-authored-by: Coby Chapman <cobylc.121@gmail.com>
Co-authored-by: Coby Chapman <cobylc.121@gmail.com>
Co-authored-by: Coby Chapman <cobylc.121@gmail.com>
Co-authored-by: Coby Chapman <cobylc.121@gmail.com>
@sonarqubecloud
Copy link

@MrAdder
Copy link
Contributor Author

MrAdder commented Feb 27, 2026

Will do the rest later on

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow upload of (external) stand planning via JSON or some other format

4 participants