Saltar al contenido
← Volver a Proyectos

Académico

El Talón

Sistema de gestión de reservas y sala para un restaurante, con paneles separados para cliente, camarero y administrador.

El problema

Un restaurante necesita gestionar reservas y sala con roles muy distintos a la vez: un cliente reservando mesa, un camarero llevando el servicio del turno en directo (pedidos, cobro, ticket), y un administrador con el control de mesas, turnos, carta y estadísticas. El proyecto (asignatura DWES) plantea las tres experiencias sobre una base de PHP puro, sin framework, con un router y un patrón MVC caseros.

Galería

Portada del restaurante
Flujo de reserva: elegir fecha, turno y mesa
Panel de camarero: cierre y cobro de un servicio
Vista mobile de "Mis reservas"
Bloqueo de cuenta tras varios intentos de login fallidos

Cómo se hizo

  • PHP 8.2 (MVC casero)
  • PDO / MySQL
  • PHPUnit 10
  • PHPMailer
  • mPDF
  • Google OAuth
  • — El aforo de cada mesa se valida también en el servidor (ReservaValidador), no solo en el JavaScript del formulario — una petición manual no puede saltárselo.
  • — Además de la transacción con FOR UPDATE al confirmar una reserva, añadí una restricción UNIQUE a nivel de base de datos (una columna generada, slot_confirmada) que impide dos reservas confirmadas para la misma mesa+turno+fecha aunque un futuro cambio de código se olvide de comprobarlo — la regla de negocio más importante no depende solo de acordarme de aplicarla en el controlador correcto.
  • — Login con bloqueo por fuerza bruta: 5 intentos fallidos bloquean la cuenta 15 minutos, en vez de dejar reintentos ilimitados.
  • — Separé el acceso a datos del propio DTO (p. ej. Reserva.php frente a ReservaBBDD.php) por cada entidad, para que las reglas de negocio puras (los *Validador.php) se puedan testear con PHPUnit sin necesitar una base de datos.

Retos y aprendizajes

Lo más difícil no fue una pantalla en concreto, sino que una mesa se pudiera atender en todo momento sin importar de dónde viniera: puede llegar reservada desde días antes, abrirse como cliente sin reserva, o gestionarla el propio camarero a mano para alguien sin cuenta — y en los tres casos el turno tiene que reflejar en todo momento qué mesas están libres, ocupadas o reservadas, para que un camarero pueda abrir servicio sin sorpresas y una reserva nueva no choque con una mesa que ya está siendo atendida. Diseñar ese estado compartido entre reservas y servicio en vivo, y no solo las pantallas de tomar pedidos o cobrar, fue lo que más tiempo y cuidado me llevó de todo el proyecto — y lo que haría distinto si lo repitiera es tratar el estado de cada mesa (libre, reservada, ocupada) como una única fuente de verdad desde el primer día, en vez de ir derivándolo por separado en reservas y en servicio.

Ver en GitHub