Backend: Adding new reservation
GitHub Issue: #189
Last modified with commit e90a924 on
by Aljaž.
Backend task for creating a Vozni park reservation (POST /drivepark/reservations) – check the API spec and DB structure, apply the drive park schema, set up drivepark.access checks, lock the vehicle and check overlaps with other reservations and events, fill the vehicle usage overview, implement, and verify against the spec.
Docs: Adding new reservation
Spec: POST /drivepark/reservations (createDriveParkReservation), DriveParkReservationNewRequest, DriveParkReservation, DriveParkReservationConflict in api-docs/openapi.yaml
DB: DriveParkReservations, VehicleReservations (DriveParkReservationId) in api/database/pending_schema.sql (-- DRIVE PARK --)
This is the first Vozni park endpoint, so it also includes the shared setup for the section: the schema, the permission check, the vehicle lock and the overlap check.
1. Double check API spec and DB structure
- Every docs field is in
DriveParkReservationNewRequest, with the right required/optional setting: Vozilo (vehicleId, required), Od / Do (startDate/endDate, required), Tvrtka (company), Ime i prezime (fullName), Email / Telefon (contact, one free-text field). - Times: the form has dates only.
startTime/endTimeare optional and default to 00:00 / 23:59, so a reservation covers whole days and the next one can start the day after. Check the defaults match the docs (“starts at 00:00 on Od, ends at 23:59 on Do”). - Company or name: at least one of
company/fullName→ 400validation_error, andCK_DriveParkReservations_Namein the DB. Treat an empty or whitespace string as null. - Availability: the docs show one message (“Vozilo nije dostupno u odabranom
razdoblju.”) for three cases. Check that the spec maps them to 409:
vehicle_unavailable(vehicle missing / deleted / archived, or Neispravno =IsActive = 0while the period ends today or later in Europe/Zagreb) andvehicle_conflict(overlap with any other VehicleReservations row of the vehicle, Vozni park or event). - Event side: the event endpoints must also see Vozni park bookings as conflicts. Check
that their conflict queries include VehicleReservations rows with
EventId IS NULL(on main, the conflict query inEventAssociationSync.SyncEventVehiclesAsyncandReconcileVehicleReservationsAsyncusesJOIN Events, which drops those rows) and that the error /removedVehiclesentry has a sensible name for a reservation (conflictingEventName). The pending events/vehicles locking PR may already cover this. - Lock order: the overlap rule can’t be a constraint, so the vehicle’s Vehicles row is
locked (
UPDLOCK, HOLDLOCK) before the check, before any other write. Check that the order of locks matches the events/vehicles locking PR (ascending vehicle Id), so an event sync and a reservation can’t deadlock. - Vehicle dropdown: uses
GET /vehicles(getVehicles, search on name/plate/VIN, archived excluded by default).getVehiclesapplieslimitedBrandCodes, but Vozni park shows all brands. Confirm a Manager of Vozni park has no brand restriction. - Roles and permissions:
drivepark.accessexists inPermissions.csand thePermissionNameenum, granted to Admin and DriveManager. No new permission. - DB:
DriveParkReservationscolumns vs request fields (lengths 255);CreatedBy,CreatedAtset by the server (sameCreatedByvalue as Events);CK_VehicleReservations_Owner(exactly one ofEventId/DriveParkReservationId);UX_VehicleReservations_DriveParkReservationId. - Is
201+DriveParkReservationenough for the next step (back to the calendar with a toast, new bar)?
2. Implementation
- DB: apply the
-- DRIVE PARK --section ofpending_schema.sql(run the duplicate check before the unique index; keep the rollback). Later these statements move tocreate_schema.sql, together with the updated VehicleReservations table comment. - Roles and permissions: require
drivepark.access→ 403. - Shared helpers (used by the update task too):
- Lock:
SELECT … FROM Vehicles WITH (UPDLOCK, HOLDLOCK) WHERE Id = @VehicleIdinside the transaction, before any write; several vehicles → one at a time, ascending Id. - Availability: vehicle exists,
DeletedAt IS NULL,isArchived = 0; and not (IsActive = 0and end ≥ today 00:00 Europe/Zagreb) → otherwise 409vehicle_unavailable. - Overlap:
vr.VehicleId = @VehicleId AND vr.StartTime < @EndTime AND vr.EndTime > @StartTime, excluding the reservation’s own row on update → 409vehicle_conflict, with the other booking (event name or reservation label) inmessage.
- Lock:
- Function + repository: in one transaction, lock, check, insert DriveParkReservations
and its VehicleReservations row (
EventIdNULL,DriveParkReservationId,StartTime=startDate+ (startTime?? 00:00),EndTime=endDate+ (endTime?? 23:59)). - Validation → 400
validation_error: required fields, end after start, company/fullName, lengths. - Response 201 with
DriveParkReservation(vehicle brand/name/plate,label= company and fullName joined with " – “). - Vehicle usage overview: fill
drivepark-reservationentries inVehicleRepository.GetVehicleUsageAsync(GET /vehicles/{vehicleId}/usage): name = label, dates from the VehicleReservations row. Update theVehicleUsageEntryrecord comment. - isOccupied / delete / archive: they read VehicleReservations, so Vozni park reservations count automatically. Check the vehicle update/archive/delete paths lock the Vehicles row the same way, so a reservation can’t be added while the vehicle is being set to Neispravno or archived.
3. Verify implementation against spec
- Route, method, request and response shapes match
createDriveParkReservation. - Every status code and
errorvalue in the spec can be produced (400validation_error, 409vehicle_unavailable, 409vehicle_conflict); no undocumented ones. - DriveManager and Admin → 201; BrandManager and Promoter → 403.
- Overlap cases: same days as another reservation → 409; ending on the day another starts → 409 (both cover that day); ending the day before → 201; overlapping an event’s window → 409.
- Neispravno vehicle: period in the past → 201; period including today → 409. Archived vehicle → 409.
- Two parallel requests for the same vehicle and days → exactly one succeeds.
- Adding the reserved vehicle to an event for the same days → the event endpoint rejects it.
- The vehicle’s usage overview shows “Vozni park - rezervacija” with the label.
Done when
A Manager of Vozni park can add a reservation; it can’t overlap any other booking of the vehicle, and it shows on the calendar and in the vehicle’s usage overview.