Skip to content

Latest commit

 

History

History
142 lines (117 loc) · 6.82 KB

README.md

File metadata and controls

142 lines (117 loc) · 6.82 KB

Parking

Bookings for the hotel's car park. Screens under /dashboard/parking. Project overview in ../general/README.md.

What problem it solves

The hotel rents parking spaces to its clients by the day. It is necessary to know which space is free on each date, not book the same one twice, record who comes in and out, and charge the right amount. The module keeps each space's calendar, each booking's lifecycle and the charging, with a real-time garage map and occupancy statistics.

Who uses it

All roles except mantenimiento. Any user with access creates, edits, charges and closes bookings. Only an admin can delete a vehicle. The amount charged, its method and reference stay editable in any booking state, also completed. This is deliberate for now (ADR-032).

What it can do

  • Panel (/dashboard/parking): occupancy, expected entries and exits and free spaces for today, another day or a date range.
  • Status map (/dashboard/parking/status): each space on each floor with its situation (free, reserved, occupied) and direct actions: entry, exit, cancel, no-show and expired bookings.
  • Booking list (/dashboard/parking/bookings): search by text, state, date range and quick filters for the day (entries, exits, inside…), with pagination. The backend also allows filtering by space, licence plate, owner or origin, but the screen does not offer it.
  • New booking: space, vehicle (searched by licence plate or owner, or registered), expected dates, origin (direct, Booking, Expedia, Airbnb, agency), external reference and notes. The price is calculated automatically, but can be set manually. Today the origin and external reference are lost when creating the booking (it stays "direct"); they are only saved if it is edited afterwards.
  • Booking detail (/dashboard/parking/bookings/<code>): edit dates or space, register entry and exit, charge and delete.

What data it handles

Five tables (backend/db-mysql/aiven/04_parking_tables.sql):

  • parking_spots: the spaces — floor (-2 or -3), number, type and whether it is active. Today there are 20: 10 per floor.
  • parking_vehicles: licence plate, owner, model and notes. A vehicle is reused between bookings.
  • parking_bookings: the booking — code, space, vehicle, expected and real dates, state, amount, payment (amount, method, reference, date), origin and who created and modified it.
  • parking_rates: price according to the number of days, from 1 to 30. It does not depend on the space type.
  • parking_availability: a precalculated calendar, with one row per space and day that says whether it is free and which booking occupies it.

Part of the logic lives in the database: the triggers in 05_parking_functions_triggers.sql generate the booking code and keep the calendar up to date.

The database also has 2 functions (check_availability, get_total_availability) and 5 procedures (06_parking_procedures.sql) that nothing calls, neither the code nor the triggers; only the local seeds run generate_availability, which the backend replaced with extendAvailability. daily_parking_maintenance would mark expired bookings as no-shows if someone ran it by hand. They stay in the database (decided on 2026-10-12).

What rules it follows

Booking states

reserved ──► checked_in ──► completed
    │             │
    ├──► canceled ◄┘
    └──► no_show
  • Entry is only registered on a reserved booking, and exit on a checked_in one. Cancel is valid for both; "no-show" only for reserved.
  • Only a reserved booking can be deleted. The deletion is definitive.
  • An expired booking is one that stays checked_in after its expected exit.

Availability

  • A booking occupies its space from the entry day to the day before the exit. When creating it, or changing its space or dates while it is reserved or checked_in, the backend rejects it if another active booking holds any of those days. The check reads the bookings themselves and locks the space row first, so two requests for the same space at the same time cannot both pass. When creating, a database trigger checks the calendar again.
  • When moving to completed, canceled or no_show, the booking's own days are freed automatically; days of other bookings are never touched.
  • The calendar (parking_availability) covers the next 365 days. The backend extends it when it starts and every day at 03:00 (Madrid), adding only the missing days and blocking those inside active bookings.

Price

  • Calendar days between expected entry and exit are counted, with a minimum of 1: entering on the 25th at 23:00 and leaving on the 26th at 10:00 is 1 day.
  • The price comes from parking_rates for that number of days. There are packages: 7 days cost less than 6, and 30 less than 29. From 31 days there is no rate and 15 € per day are charged.
  • When charging, what is paid is saved separately from the calculated amount, with method (cash, card, transfer, agency) and reference.

Others

  • The booking code is generated by the database: PK-AAAAMMDD-NNNN (creation date and number of the day). It is what is seen in URLs; the internal id is not shown.
  • Who creates or modifies a booking is taken from the session, not from what the browser sends.
  • Listings return 50 bookings by default and 500 as maximum.

How information flows

page.tsx (server) ─► getParkingDashboardStats ─► GET /api/parking/stats
ParkingStatusClient ─► useParkingStatus (React Query) ─┐
BookingsListClient / BookingDetailClient ──────────────┼─► parkingApi (queries.ts) ─► apiClient
                                                       │
                   /api/parking/{spots,vehicles,bookings,stats}
                                        │
      parking · bookings · stats controllers ─► repositories ─► MySQL
                                                                  │
                                              triggers ─► parking_availability
  1. The panel is loaded on the server (getParkingDashboardStats action) and arrives with the day's data already.
  2. The status map requests spaces, bookings, expired ones and statistics with React Query. It does not refresh on its own: each action requests the data again, but changes made by another person do not appear until reloading. Left as is (decided on 2026-10-12).
  3. Creating, moving, closing or cancelling a booking writes to parking_bookings within a transaction; the triggers update the calendar in the same operation.
  4. /api/parking/stats works in three modes: without parameters (today), with date (one day) or with startDate and endDate (a range, without pending entries and exits).