Volver al blog
20 de enero de 2025

Construyendo Four-Points: Un Sistema de Gestión Hotelera desde Cero

La historia de construir un PMS hotelero full-stack desde cero hasta producción - problemas enfrentados, soluciones encontradas, y lecciones aprendidas

Construyendo Four-Points: Un Sistema de Gestión Hotelera desde Cero

🏨 ¿Por Qué Construir un PMS Entero?

Después de 15+ años trabajando en recepción de hotel, me cansé del software lento, torpe y sobreprejado que los hoteles están forzados a usar para luego colocar todo en excel. Así que hice lo que cualquier desarrollador haría: decidí construir el mío. Movimiento clásico de desarrollador, ¿no?

Four-Points empezó como un simple tracker de parking para mi hotel. Avance rápido hasta hoy, y es un sistema de gestión de propiedades completo diseñado para escalar de 1 hotel (10 usuarios) a 12+ hoteles con 100 usuarios concurrentes.

🎯 Lo que Realmente Construí

Módulos Core

  • Gestión de Parking: Tracking de disponibilidad en tiempo real, reservas automatizadas, schedules de mantenimiento
  • Grupos y Reservas: Tracking de pagos, gestión de contactos, workflows de estado de grupo
  • Sistema de Cashier: Operaciones multi-turno (4 turnos/día), tracking de denominaciones, manejo de vouchers, exports PDF
  • Sistema de Scheduling: Generador de horarios para equipo de trabajo, potenciado por IA con algoritmo multi-fase, validación de constraints, exports PDF
  • Logbook: Libro de consignas con permisos basados en roles y hilos de comentarios
  • Reportes de Mantenimiento: Workflows de subida de imágenes con procesamiento Sharp
  • Mensajería: Mensajería interna en tiempo real entre staff
  • Gestión de Usuarios: Auth JWT, control de acceso basado en roles, cookies httpOnly
  • Asistente de IA: Chat integrado para ayuda operacional potenciado por Groq

El Stack

Frontend: Next.js 14 (App Router) + TypeScript + React Query + Zustand
Backend: Node.js + Express + REST API
Base de Datos: MySQL en Aiven Cloud
Auth: JWT con cookies httpOnly (15min access + 7d refresh)
Imágenes: Multer + Sharp + Cloudinary
AI: Claude (Anthropic) + Gemini + Groq + Ollama
Testing: Vitest + Coverage Reports
UI: NextUI + Tailwind CSS
i18n: next-intl (Español/Inglés)
PDF: jsPDF + generadores personalizados

💀 Problemas que me encontré (y las soluciones que ofrezco)

1. "Usa Solo JavaScript" - Últimas Palabras Famosas

Empecé todo el proyecto en JavaScript vanilla. Parecía fine las primeras semanas. Después el codebase creció. Después aparecieron los bugs. Después comenzaron las pesadillas de refactoring.

El Problema: Sin type safety significaba errores de runtime por todos lados. Pasar tipos de datos incorrectos, typos en propiedades de objetos, valores undefined rompiendo producción.

La Solución: Migré todo a TypeScript. Me tomó 2 semanas de dolor, pero valió cada segundo. Ahora mi IDE captura bugs antes de incluso correr el código. Debería haber hecho esto desde el día uno.

2. Triggers de MySQL Volviéndose Locos

Implementé cálculos automáticos usando triggers de base de datos. Suena inteligente, ¿no? Wrong.

El Problema: Triggers actualizando otras tablas que triggerearon más triggers que actualizaron más tablas... loops infinitos. Además debuggear lógica de triggers es absolute hell — no puedes console.log() un trigger.

La Solución: Moví cálculos a la capa de aplicación. Sí, más código que mantener, pero al menos puedo debuggearlo. A veces la lógica "smart" de base de datos no es nada smart.

3. Infierno de Zonas Horarias

Classic backend vs frontend pesadilla horaria según la zona. Servidor en una zona horaria, base de datos en otra, usuarios en una tercera.

El Problema: Turnos de cashier mostrando tiempos wrong, reportes abarcando rangos de fechas incorrectos, timestamps por todos lados.

La Solución:

  • Almacena todo en UTC en la base de datos
  • Convierte a zona horaria del usuario solo en frontend
  • Usa toISOString() religiosamente
  • Nunca confíes en new Date() sin manejo de zona horaria

4. Drift del Esquema de Base de Datos

MySQL desarrollo local, producción en Aiven Cloud. Cambios de schema en dev no igualando producción.

El Problema: Features funcionando perfectamente en dev, crasheando en prod porque una columna no existe o tiene constraints wrong.

La Solución:

  • Scripts de migración para CADA cambio de schema
  • Control de versión para esquema de base de datos
  • Testing contra base de datos cloud antes de deployar
  • Aprendí de la manera difícil: schema es código, trátalo como código

5. Complejidad de Control de Acceso Basado en Roles

Diferentes usuarios necesitan diferentes permisos. Recepcionistas, managers, admins, staff de mantenimiento...

El Problema: Empecé con checks simples de if/else. Se volvió inmantenible rápido. "¿Puede usuario X hacer acción Y en módulo Z?" se complicó.

La Solución:

// Sistema de permisos centralizado
const permissions = {
  parking: {
    view: ['receptionist', 'manager', 'admin'],
    edit: ['manager', 'admin'],
    delete: ['admin']
  },
  cashier: {
    openShift: ['receptionist', 'manager'],
    closeShift: ['manager', 'admin'],
    viewReports: ['manager', 'admin']
  }
}

Middleware checkea permisos antes de cualquier acción. Limpio, testeable, mantenible.

6. Optimización de Subida de Imágenes

Hoteles suben fotos de mantenimiento constantemente. Empecé con uploads directos a servidor.

El Problema: Fotos de teléfono de 10MB matando almacenamiento y ancho de servidor de servidor. Tiempos de carga horrible.

La Solución:

  • Sharp para procesamiento de imágenes
  • Redimensionar a máximo 1920px de ancho
  • Comprimir a 80% calidad
  • Generar thumbnails automáticamente
  • Cloudinary para almacenamiento cloud
  • Pasé de imágenes de 10MB a 200KB sin pérdida de calidad notable

7. Gestión de Estado de Usuarios Concurrentes

Múltiples recepcionistas actualizando los mismos datos simultáneamente.

El Problema: Usuario A reserva habitación 101, Usuario B reserva habitación 101 2 segundos después. Ambos tienen éxito. Desastre.

La Solución:

  • Optimistic locking con números de versión
  • Checks de disponibilidad en tiempo real antes de confirmar
  • Constraints de base de datos como última línea de defensa
  • Feedback a usuario cuando conflictos ocurren
  • React Query para refetching automático e invalidación de cache

8. Migración de MySQL Local a Cloud

Empecé con MySQL local, tuvo que migrar a Aiven Cloud para producción. Trabajo con varios ordenadores, en trabajo y en casa... sincronizar la base de datos en local en cada PC era una odisea. Definitivamente, cuando empiezo un proyecto ahora, creo la base de datos en producción siempre directamente, luego ya migraré si es necesario según donde monte el proyecto.

El Problema: Strings de conexión, certificados SSL, diferencias de rendimiento, migración sin downtime.

La Solución:

  • Probé migración en entorno staging
  • Usé mysqldump para export/import de datos
  • Actualicé configs de conexión con SSL
  • Aprendí: bases de datos cloud tienen características de rendimiento diferentes

9. Pesadilla de Staff Scheduling con IA

Pensé "solo escribiré un algoritmo simple para asignar turnos". Lol. Staff scheduling es un problema NP-hard.

El Problema:

  • Empleados tienen diferentes contratos (part-time, full-time, turnos rotativos)
  • Leyes laborales requieren mínimo 48h descanso entre turnos
  • Turnos de noche necesitan manejo especial
  • Requisitos de coverage cambian por día
  • Balanceo manual toma a managers 8+ horas por mes

La Solución: Construí un constraint solver multi-fase:

  1. Inicializar matriz mensual
  2. Aplicar reglas específicas de empleado (contratos, preferencias)
  3. Asignar días libres semanales (FO)
  4. Distribuir turnos rotativos (M/T)
  5. Manejar bloques de noche (N)
  6. Validar todos los constraints
  7. Optimización con IA usando Claude/Gemini/Groq/Ollama

La optimización con IA fue game-changing. Pasé de schedules "técnicamente válidos pero desbalanceados" a schedules "realmente buenos". La IA entiende contexto como "no le des a alguien 5 noches seguidas aunque sea técnicamente legal".

Realidad check: Primer intento tomó 45 segundos generar un schedule. Optimizado a 3 segundos. Después añadí IA y volvió a 30 segundos. ¿Valió la pena? Absolutamente. A los managers les encanta.

10. Drama de Internationalization (i18n)

Empecé construyendo en Inglés, pero luego me di cuenta que en España o eres programador o eres traductor... nadie sabe inglés en España. Después me di cuenta: escalar a múltiples hoteles significa múltiples idiomas.

El Problema:

  • Strings hardcodeados POR TODAS PARTES
  • Mezcla de Español e Inglés en el codebase
  • Inconsistencias de formato de fecha/número
  • Ningún workflow de gestión de traducciones

La Solución:

  • Migré a next-intl
  • Extraje 500+ strings hardcodeados a archivos JSON de traducción
  • Seté up locale routing adecuado
  • Aprendí: i18n debería ser decisión de día uno, no un afterthought

Historia divertida: Encontré strings como "Haz click aquí" mezclados con "Click here" en el mismo componente. Classic multilingual developer chaos.

11. Infierno de Generación de PDF

"Simplemente exporta a PDF" dijeron. " será fácil" dijeron.

El Problema:

  • Cashier necesita reportes de turno en PDF (con tablas de denominaciones)
  • Scheduling necesita grids mensuales en PDF (30 días × 40 empleados)
  • La función print() del navegador da resultados inconsistentes
  • Necesito headers, footers, branding personalizados

La Solución:

  • jsPDF para generación programática
  • Renderers de tabla personalizados para layouts complejos
  • Manejo correcto de page breaks y overflow
  • Dimensiones pre-calculadas (sin adivinar)

Pro tip: Generación de PDF es tedioso AF. Prueba cada caso edge: nombres largos, filas extra, caracteres especiales. Tu primer PDF parecerá basura. Tu vigésimo se verá profesional.

12. React Query vs Zustand: Cuándo Usar Qué

Tenía Zustand para estado global. Añadí React Query para estado de servidor. Siguió confusión.

El Problema:

  • Estado duplicado entre Zustand y React Query
  • Pesadillas de invalidación de cache
  • Re-renders innecesarios
  • Mezclando patrones inconsistentemente

La Solución: Separación clara de responsabilidades:

  • React Query: TODO estado de servidor (llamadas API, caching, refetching)
  • Zustand: Solo estado de UI (modales abiertos/cerrados, filtros, tema)

Regla general: Si viene de API, es React Query. Si es puramente frontend, es Zustand. Sin solapamiento. La vida es buena.

13. Testing: De "Después" a "Ahora"

Classic dev story: envié features sin tests. "Añadiré tests después." Narrador: no lo hizo.

El Problema:

  • Refactoring rompió cosas silenciosamente
  • Testing manual tomaba para siempre
  • Bugs en producción que podrían haber sido atrapados
  • Miedo de cambiar código

La Solución:

  • Set up Vitest (Jest pero más rápido)
  • Empecé con paths críticos (auth, payments, scheduling)
  • Escribí tests para nuevos features ANTES de enviar
  • Coverage reports para avergonzarme de escribir más tests

Estado actual: 60% coverage. No great, not terrible. Mejor que 0%.

🚀 Features que Realmente Funcionan

Flujo de Autenticación

  • Login con JWT tokens (15min access + 7 day refresh)
  • Cookies httpOnly (sin riesgos de XSS en localStorage)
  • Refresh token automático con sliding sessions
  • Protección de rutas basada en roles

Sistema de Parking

  • Disponibilidad de plazas en tiempo real
  • Manejo automatizado de reservas
  • Modo mantenimiento scheduling
  • Disponibilidad pre-calculada (sin queries dinámicas)

Gestión de Grupos

  • Tracking de pagos con múltiples métodos
  • Organización de contactos
  • Workflows de estados (inquiry → confirmed → checked-in)
  • Códigos de reserva profesionales

Operaciones de Cashier

  • 4 turnos por día (mañana, tarde, noche, interno)
  • Tracking de denominaciones (billetes, monedas, vouchers)
  • Gestión multi-usuario de turnos
  • Export PDF por turno con reconciliación completa
  • Tracking de balance cross-turno

Scheduling Potenciado por IA

  • Constraint solver multi-fase
  • Reglas específicas de empleado (contratos, preferencias)
  • Validación de coverage (mín/máx staff por turno)
  • Optimización de bloque de noche
  • Optimización IA con 4 proveedores (Claude, Gemini, Groq, Ollama)
  • Export PDF con vista grid mensual
  • Servicio de validación con reporte de errores detallado

Sistema de Logbook

  • Notas operacionales con timestamps
  • Hilos de comentarios
  • Visibilidad basada en roles
  • Tracking de leido/no leido
  • Soporte rich text

Sistema de Mensajería

  • Mensajería interna en tiempo real
  • Comunicación staff-a-staff
  • Threading de mensajes
  • Badges de notificación

Reportes de Mantenimiento

  • Subida de imágenes con auto-optimización
  • Integración Cloudinary
  • Workflows de estados (pending → in-progress → completed)
  • Asignación a staff de mantenimiento

AI Assistant Chat

  • Ayuda integrada potenciado por Groq
  • Respuestas conscientes del contexto
  • Respuestas rápidas para preguntas operacionales
  • Sin necesidad de buscar documentación

Gestión de Usuarios

  • Create/edit/deactivate usuarios
  • Asignación de roles (admin, manager, receptionist, maintenance)
  • Subida de avatar con Cloudinary
  • Tracking de actividad
  • Flujos de reset de password

Internationalization

  • Soporte Español e Inglés
  • Formateo de fecha/número consciente de locale
  • Gestión de traducciones con next-intl
  • Routing de locale automático

📚 Lo que Aprendí

TypeScript es Innegociable

Si tu proyecto va a tener más de 500 líneas de código, usa TypeScript desde el día uno. El tiempo que "ahorras" escribiendo JS, lo gastarás 10x debuggeando errores de runtime.

El Diseño de Base de Datos Importa Más de lo que Crees

Pasé el primer mes solo diseñando el esquema de base de datos. Mejor inversión ever. Un buen esquema hace todo más fácil. Un mal esquema te persigue para siempre.

No Optimices Prematuramente (Pero Optimiza Estratégicamente)

Empecé con queries simples, identifiqué bottlenecks con datos de uso real, después optimizé. No adivines qué es lento — mídelo.

AI es una Herramienta, No Magia

Integrar AI para scheduling fue amazing, pero requirió:

  • Prompt engineering adecuado
  • Fallback a no-AI cuando API falla
  • Gestión de costos (Claude Opus vs Haiku vs Ollama local)
  • Entender cuándo AI añade valor vs cuándo algoritmos simples funcionan fine

Costo real: $0.03 por generación de schedule con Claude. $0 con Ollama. Elige sabiamente basado en presupuesto y necesidades de calidad.

Autenticación es Difícil, Usa Librerías Cuando Sea Posible

Hacer mi propio auth me enseñó mucho, pero también me enseñó que librerías battle-tested existen por una razón. Seguridad no es donde quieres ser creativo.

Testing Ahorra Tiempo (Eventualmente)

Sí, escribir tests se siente lento al principio. Pero atrapar bugs antes de producción? Sin precio. Especialmente para flows críticos como payments y reservas.

Hot take: Coverage % es métrica de vanidad. Testea paths críticos primero. 30% coverage de las cosas correctas supera 90% coverage de código trivial.

Documentación es para Tu Yo del Futuro

Comments, archivos README, docs de API — todo parecía tiempo desperdiciado hasta que tuve que debuggear mi propio código 3 meses después. Tu yo del futuro te agradecerá.

i18n Debería Ser Decisión de Día Uno

Añadir internacionalización después de construir features? Dolor. Añadirla desde el inicio? Overhead mínimo. Si hay CUALQUIER posibilidad de que necesites múltiples idiomas, setéalo temprano.

Generación de PDF es Tedioso

Cada librería de PDF te frustrará. Pickea una, apréndela profundamente, construye componentes reutilizables. No cambies de librería a mitad de proyecto.

🎯 Impacto en el Mundo Real

Corriendo en producción en mi hotel con:

  • 15 usuarios diarios
  • 50+ reservas de parking/día
  • 200+ entradas de logbook/mes
  • 4 turnos de cashier/día reconciliados automáticamente
  • Schedules de staff mensuales generados en 30 segundos (vs 8 horas manual)
  • Cero downtime en los últimos 3 meses
  • 60% coverage de tests en paths críticos

Planeando escalar a 12 hoteles (100+ usuarios concurrentes) en 2025.

💭 Opinión Personal

Construir Four-Points me enseñó más que ningún curso jamás pudo. Sí, vi el curso de Node.js de midudev, seguí tutoriales de SuperSimpleDev, leí Stack Overflow hasta las 3am — pero nada supera construir algo que la gente realmente usa.

¿Lo haría de nuevo? Absolutamente. Pero:

  • Empezaría con TypeScript inmediatamente
  • Setearía i18n desde el día uno
  • Pasaría más tiempo en diseño de base de datos
  • Escribiría tests desde el principio
  • Usaría patrones establecidos en vez de reinventar la rueda
  • Deployaría a cloud antes
  • Planificaría integración AI desde el inicio (los costos de API suman)
  • Elegiría React Query desde el principio (estado de servidor es diferente)

¿Es perfecto? Hell no. Hay tech debt, features que quiero refactorizar, y bugs que sé que existen pero no he arreglado. Pero funciona. Gente real lo usa cada día. Staff scheduling que solía tomar 8 horas ahora toma 30 segundos. Y eso es lo que importa.

🔮 Qué Sigue

  • ✅ ~~React Query para mejor gestión de estado~~ (HECHO)
  • ✅ ~~Mensajería en tiempo real~~ (HECHO)
  • ✅ ~~Soporte multi-idioma~~ (HECHO)
  • ✅ ~~Features potenciado por AI~~ (HECHO)
  • WebSockets para notificaciones en tiempo real (actualmente polling)
  • App móvil (¿React Native o PWA?)
  • Dashboard de analytics avanzado
  • Integración con pasarelas de pago
  • Dashboard de gestión multi-hotel
  • Portal de reservas para huéspedes

🙏 Shoutout

Gracias a todos los que inconscientemente ayudaron: héroes de Stack Overflow, midudev por dominio de Node.js, SuperSimpleDev por fundamentos, Claude (sí, AI) por ser un rubber duck que realmente responde y por powering la AI de scheduling, Anthropic por la API, y la comunidad open-source por literalmente todo.


Bottom line: Si tienes conocimiento de dominio en cualquier industria + podés codificar, construí las herramientas que desearías que existieran. ¿El software de hotel sucks? Construí mejor. ¿Workflow ineficiente? Automatízalo. ¿AI lo hace posible? Integralo. No será perfecto, pero será tuyo. Y aprenderás más de lo que ningún tutorial puede enseñar.

Keep shipping. 🚀

Volver al blog
Construyendo Four-Points: Un Sistema de Gestión Hotelera desde Cero | bpstack