Trabajo de Fin de Grado · Ingeniería Informática · UNEATLANTICO · 2025–2026
ERP modular de reservas corporativas y gestión interna para GRUPOSIETE: parking, oficinas, vacaciones, directorio y tablón.
- GRUPOSIETE nace en 2022 de la fusión de ocho empresas de distribución de materiales de construcción, con sedes en seis comunidades autónomas y oficina central en Alcobendas (Madrid, 240 m²). El reto principal: unificar procesos que cada empresa gestionaba de forma distinta.
- El intento previo de unificación fue a3innuva (Wolters Kluwer) como panel del empleado. Solo se usa para nóminas; la baja usabilidad abandonó el resto de secciones.
- El proyecto arrancó como una app de reservas de parking para Alcobendas (Madrid). Durante el requisitado escaló: se añadió reserva de oficinas y finalmente multi-sede, vacaciones, directorio y tablón para dar cohesión a todas las sedes.
- El mercado ofrece herramientas de RRHH por un lado y de gestión de espacios por otro, pero ninguna las unifica integrándose además con servicios de Microsoft. Para ocho empresas en plena fusión, esa fragmentación es especialmente grave.
El diagrama organiza las entidades en cuatro áreas conceptuales: organización, espacios, recursos humanos y comunicación. La jerarquía de roles (Empleado → RRHH → Manager → Administrador) sigue una cadena de herencia; Plaza unifica aparcamiento y oficinas; Reserva, Cesión y ReservaVisitante modelan las operaciones sobre los espacios.
| Cesión | Solicitud de Ausencia |
|---|---|
Código fuente |
Código fuente |
| Actor | Tipo | Descripción |
|---|---|---|
| Empleado | Primario | Reserva plazas, solicita ausencias y consulta información. |
| RRHH | Primario | Gestiona solicitudes de ausencia y publica anuncios. |
| Manager | Primario | Administra su sede: plazas, usuarios, directorio y configuración. |
| Administrador | Primario | Gestiona entidades, módulos y la configuración global del sistema. |
| Visitante | Pasivo | Persona externa sin acceso al portal. Recibe confirmación por correo. |
El sistema colabora con dos servicios externos: Microsoft Entra ID como proveedor de identidad (OAuth 2.0/OIDC) y Microsoft Graph API para estado fuera de oficina y notificaciones por Teams.
| Espacios | Personas | Administración |
|---|---|---|
Código fuente |
Código fuente |
Código fuente |
El sistema se expresa como una máquina de estados: se pueden asociar vistas del portal a los estados del diagrama de contexto y cada transición a un caso de uso. SESION_CERRADA es el estado de entrada; SISTEMA_DISPONIBLE actúa como hub central. El retorno desde cualquier módulo se unifica mediante completarGestion(), materializada como la barra de navegación lateral.
Flujo estándar de reserva: el empleado selecciona fecha, consulta disponibilidad y confirma. La Server Action createReservation verifica autenticación, valida con Zod y comprueba conflictos antes de persistir en PostgreSQL.
| Contexto | Solución |
|---|---|
![]() |
CalendarioParkingView se comunica exclusivamente con ReservaController, que coordina la carga de disponibilidad y la detección de conflictos delegando en PlazaRepository y ReservaRepository.
La página resuelve la consulta de disponibilidad en servidor antes de renderizar. La Server Action ejecuta tres pasos: autenticación → validación Zod → persistencia con control de conflicto. La respuesta es un ActionResult<Reserva> que la página maneja sin recargar.
Cesión intencional de plaza asignada: solo el propietario puede liberarla para que otros empleados la reserven. La arquitectura deja preparada la consulta del estado fuera de oficina vía Microsoft Graph para sugerir cesiones en futuras iteraciones.
| Contexto | Solución |
|---|---|
![]() |
CesionView recibe la acción desde el calendario de parking. El controlador recupera la plaza asignada mediante PlazaRepository y registra la cesión en CesionRepository. Microsoft Graph colabora en segundo plano, pero la decisión final siempre es del empleado.
Flujo de aprobación en un único paso: RRHH, manager de sede o administrador aprueban o rechazan solicitudes de ausencia. La interfaz presenta dos zonas (lista de pendientes y panel de detalle con acción).
| Contexto | Solución |
|---|---|
![]() |
BandejaSolicitudesView presenta las solicitudes pendientes del equipo. AusenciaController coordina la carga consultando SolicitudRepository y EmpleadoRepository, y al resolver dispara una notificación mediante NotificacionService.
La Server Action approveLeaveRequest valida el rol del actor, verifica que la solicitud esté pendiente y transita el estado en un único paso, enviando la notificación correspondiente al empleado.
El portal concentra las tres responsabilidades del servidor en un único contenedor Next.js: renderizado, Server Actions y callbacks de autenticación. PostgreSQL se comunica directamente con el portal mediante Drizzle ORM, eliminando la sobrecarga de serialización REST.
Los módulos del dashboard dependen débilmente de lib/: importan lo necesario pero lib/ no conoce a sus consumidores. Las acciones dependen de consultas y autenticación; las consultas, del cliente de base de datos. La dependencia unidireccional garantiza que los módulos puedan activarse o desactivarse sin modificar el código de los demás.
Next.js y PostgreSQL 16 se ejecutan en servidor propio mediante Docker Compose. La separación entre servidor de aplicaciones y base de datos responde al requisito de disponibilidad (RNF-02). La ausencia de proveedores cloud para la persistencia materializa el requisito de portabilidad (RNF-07).
Cada clase de análisis tiene su contraparte directa en el repositorio, verificable sin ambigüedad:
| Clase de análisis | Clase de diseño | Archivo | CdU asociado |
|---|---|---|---|
CalendarioParkingView |
ParkingPage (Server Component) |
src/app/(dashboard)/parking/page.tsx |
reservarPlaza |
ReservaController |
createReservation (Server Action) |
src/app/(dashboard)/parking/actions.ts |
reservarPlaza |
PlazaRepository |
getAvailableSpotsForDate |
src/app/(dashboard)/parking/actions.ts |
reservarPlaza, cederPlaza, registrarVisitante |
ReservaRepository |
getReservationsByDate (Query) |
src/lib/queries/reservations.ts |
reservarPlaza |
CesionView |
ParkingPage (panel de cesión) |
src/app/(dashboard)/parking/page.tsx |
cederPlaza |
CesionController |
createCession (Server Action) |
src/app/(dashboard)/parking/cession-actions.ts |
cederPlaza |
BandejaSolicitudesView |
VacacionesPage (Server Component) |
src/app/(dashboard)/vacaciones/page.tsx |
gestionarSolicitudAusencia |
AusenciaController |
approveLeaveRequest (Server Action) |
src/app/(dashboard)/vacaciones/actions.ts |
gestionarSolicitudAusencia |
VisitanteView |
VisitantesPage (Server Component) |
src/app/(dashboard)/parking/visitantes/page.tsx |
registrarVisitante |
VisitanteController |
createVisitorReservation (SA) |
src/app/(dashboard)/parking/visitantes/actions.ts |
registrarVisitante |
AuthController |
config.ts + helpers.ts |
src/lib/auth/ |
cerrarSesion |
NotificacionService |
sendVisitorReservationEmail |
src/lib/email/ |
registrarVisitante |
| Categoría | Tecnología | Para qué |
|---|---|---|
| Framework | Next.js 16 (App Router) | Full-stack: renderizado, Server Actions y API |
| Lenguaje | TypeScript | Tipado estático en todo el proyecto |
| Base de datos | PostgreSQL 16 | Autoalojada en servidor propio con Docker Compose |
| ORM | Drizzle ORM | Acceso a datos con tipos inferidos del esquema SQL |
| Autenticación | Auth.js v5 + Microsoft Entra ID | Login con cuenta Microsoft, sin almacenar contraseñas |
| UI | Tailwind CSS v4 + shadcn/ui | Componentes accesibles y responsivos |
| Testing unitario | Vitest (45 archivos, 831 tests) | Cobertura de Server Actions, queries y validaciones Zod |
| Testing E2E | Playwright | Flujos completos desde la perspectiva del usuario |
| CI/CD | GitHub Actions | Tests automáticos en cada push, despliegue al pasar |
| Contenedores | Docker + Docker Compose | App y base de datos en servidor propio |
| Resend + React Email | Correos transaccionales de confirmación |
| Métrica | Puntuación |
|---|---|
| Rendimiento | 100 |
| Accesibilidad | 96 |
| Buenas prácticas | 100 |
| SEO | 100 |
Cobertura global de tests: 85,3 % sentencias · 74,3 % ramas · 88,8 % funciones · 85,6 % líneas.
Pipeline mediante GitHub Actions: pnpm check (typecheck, lint, format, tests) en cada push. El despliegue se activa automáticamente al superar todas las comprobaciones.
- El portal resuelve la fragmentación entre sedes integrando parking, oficinas, vacaciones, directorio y tablón en una única plataforma con SSO Microsoft 365.
- La arquitectura modular (monolito modular con patrón plugin) permite activar o desactivar módulos por entidad sin modificar el código base, validando la decisión de diseño estructural del proyecto.
- La integración con Microsoft Entra ID elimina la fricción de acceso; la arquitectura está preparada para integrarse con Graph API (notificaciones por Teams, sincronización con calendarios de Outlook).
- La migración temprana de Supabase a PostgreSQL autoalojado mantuvo la compatibilidad del modelo de datos y dio al cliente el control total sobre su información, con coste operativo asumible.
- 85,3 % de cobertura de tests, 100/100 Lighthouse en rendimiento y CI/CD automatizado mediante GitHub Actions.
- La trazabilidad completa -escenario → requisitos → análisis y diseño → implementación → evaluación- verifica un proceso de ingeniería riguroso y auditable.
| (este README) | Capítulo I | Capítulo II | Capítulo III | Capítulo IV | Capítulo V | |
|---|---|---|---|---|---|---|
| Fecha | - | 16/03 | 06/04 | 27/04 | 26/05 | 26/05 |
| Documento | Presentación | Marco teórico | Requisitos | Análisis y diseño | Implementación | Conclusiones |
| Cap. 1 → |
|---|
Álvaro Lostal Sanz · lostal.dev · GitHub · LinkedIn



