diff --git a/backend/README.md b/backend/README.md index 8f412266..6c6554af 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,443 +1,30 @@ -# Four Points - Backend - -> API del Sistema de Gestión Hotelera (Express + MySQL) - ---- - -## Índice - -1. [Arquitectura](#1-arquitectura) -2. [Estructura del Proyecto](#2-estructura-del-proyecto) -3. [Tech Stack](#3-tech-stack) -4. [API Endpoints](#4-api-endpoints) -5. [Middlewares](#5-middlewares) -6. [Base de Datos](#6-base-de-datos) -7. [Configuración y Deployment](#7-configuración-y-deployment) -8. [Scripts y Comandos](#8-scripts-y-comandos) -9. [Completar en Más Detalle](#9-completar-en-más-detalle) - ---- - -## 1. Arquitectura - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ ARQUITECTURA FOUR POINTS │ -├─────────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ FRONTEND (Next.js 16) │ │ -│ │ │ │ -│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │ │ -│ │ │ Dashboard │ │ Componentes │ │ Lib/ │ │ Stores │ │ │ -│ │ │ (pages) │ │ (React UI) │ │ Hooks │ │ (Zustand)│ │ │ -│ │ └─────────────┘ └─────────────┘ └─────────────┘ └───────────┘ │ │ -│ │ │ │ │ │ │ │ -│ │ └──────────────────┼──────────────────┼──────────────────┘ │ │ -│ │ │ │ │ │ -│ │ ┌────────▼────────┐ ┌────▼────────┐ │ │ -│ │ │ React Query │ │ API Client │ │ │ -│ │ │ (Server State) │ │ (auto-refresh) │ │ -│ │ └─────────────────┘ └─────────────┘ │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ │ HTTPS + Credentials │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ BACKEND (Express 5) │ │ -│ │ │ │ -│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ -│ │ │ Routes │ → │ Controllers │ → │ Repositories│ │ │ -│ │ │ (Express) │ │ (Lógica) │ │ (MySQL) │ │ │ -│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ -│ │ │ │ │ │ │ -│ │ ▼ ▼ ▼ │ │ -│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ -│ │ │ Middlewares │ │ Services │ │ Models │ │ │ -│ │ │ (auth, etc) │ │ (cron, etc) │ │ (Zod/TS) │ │ │ -│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ │ TCP/IP │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────────┐ │ -│ │ BASE DE DATOS │ │ -│ │ │ │ -│ │ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ -│ │ │ MySQL Local │ │ MySQL Aiven (Cloud) │ │ │ -│ │ │ (development) │ │ (production) │ │ │ -│ │ └─────────────────┘ └─────────────────────────────┘ │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────────┘ -``` - -### Flujo de Datos - -``` -1. Usuario abre navegador → Next.js (Vercel) -2. Login → JWT tokens en HttpOnly cookies -3. Petición API → Express (Render) con credentials: 'include' -4. Express valida JWT → Ejecuta controller → Repository → MySQL -5. Respuesta → Cliente (React Query cache) -``` - ---- - -## 2. Estructura del Proyecto - -``` -backend/ -├── config/ -│ ├── config.ts # Variables de entorno -│ ├── db.ts # Conexión MySQL -│ ├── date-utils.ts # Utilidades de fecha -│ ├── error-codes.ts # Códigos de error -│ └── certs/ # Certificados SSL -├── controllers/ # Lógica de negocio -│ ├── auth/ -│ │ ├── auth-controllers.ts -│ │ └── user-controllers.ts -│ ├── activity/ -│ ├── backoffice/ -│ ├── blacklist/ -│ ├── cashier/ -│ ├── conciliation/ -│ ├── departments/ -│ ├── group/ -│ ├── logbook/ -│ ├── maintenance/ -│ ├── messages/ -│ ├── notifications/ -│ ├── parking/ -│ └── search/ -├── middlewares/ -│ ├── authenticateToken.ts # Verificar JWT (cookie o Bearer header) -│ ├── roleCheck.ts # Validar permisos -│ ├── rateLimiter.ts # Rate limiting (100 req/15min) -│ └── demoRestriction.ts # Bloquear writes en demo -├── models/ # Zod schemas + Types -├── repositories/ # Consultas SQL -├── routes/ # Express routers -├── services/ # Servicios externos -│ ├── auth/ -│ ├── blacklist/ -│ ├── conciliation/ -│ ├── cron/ -│ ├── group/ -│ ├── logbook/ -│ ├── notifications/ -│ ├── parking/ -│ └── scheduling/ # Sistema de horarios -├── validations/ # Zod schemas -├── types/ -│ └── express.d.ts -├── index.ts # Entry point -├── package.json -└── tsconfig.json -``` - ---- - -## 3. Tech Stack - -| Tecnología | Propósito | -| ---------------------- | ---------------------------------------- | -| **Node.js 20+** | Runtime environment | -| **Express 5** | Framework web (última versión major) | -| **TypeScript** | Desarrollo con tipos | -| **MySQL 8** | Base de datos relacional | -| **mysql2** | Driver MySQL con promesas | -| **jsonwebtoken** | Autenticación JWT (access 15 min + refresh 7 d) | -| **cookie-parser** | Lectura de HttpOnly cookies | -| **Zod 4** | Validación de requests | -| **Cloudinary** | Almacenamiento cloud de imágenes/PDFs | -| **Multer** | Upload de archivos (multipart/form-data) | -| **Archiver** | Generación de ZIPs | -| **node-cron** | Tareas programadas | -| **nodemailer** | Envío de emails | -| **bcrypt** | Hash de passwords (10 rounds) | -| **express-rate-limit** | Rate limiting | -| **dayjs** | Manipulación de fechas | -| **Axios** | Llamadas a APIs externas | - -### Dependencias - -```json -{ - "archiver": "^7.0.1", - "axios": "^1.12.2", - "bcrypt": "6.0.0", - "cloudinary": "^2.8.0", - "cookie-parser": "^1.4.7", - "cors": "^2.8.5", - "dayjs": "^1.11.18", - "express": "5.1.0", - "express-rate-limit": "^8.2.1", - "express-session": "^1.18.0", - "jsonwebtoken": "^9.0.2", - "multer": "^2.0.2", - "mysql2": "3.14.2", - "node-cron": "^4.2.1", - "nodemailer": "^7.0.10", - "passport": "^0.7.0", - "passport-jwt": "^4.0.1", - "zod": "^4.0.5" -} -``` - ---- - -## 4. API Endpoints - -### Autenticación - -``` -POST /api/auth/login # Login con credenciales -POST /api/auth/logout # Cerrar sesión -GET /api/auth/me # Usuario actual -POST /api/auth/refresh # Refresh token -POST /api/auth/register # Registro (opcional) -``` - -### Módulos Principales - -| Módulo | Base Route | Operaciones | -| ----------------- | -------------------- | ---------------------------------------------------- | -| **Users** | `/api/users` | CRUD, avatar upload, roles | -| **Groups** | `/api/groups` | Reservas, contactos, habitaciones, pagos, historial | -| **Parking** | `/api/parking` | Espacios, reservas, check-in/out, tarifas, analytics | -| **Logbooks** | `/api/logbooks` | Entradas, comentarios, seguimiento de lectura | -| **Cashier** | `/api/cashier` | Turnos, denominaciones, pagos, vouchers, reportes | -| **Maintenance** | `/api/maintenance` | Órdenes de trabajo, imágenes, estados | -| **Blacklist** | `/api/blacklist` | Incidentes, verificación docs | -| **Conciliation** | `/api/conciliations` | Conteo diario de habitaciones | -| **Backoffice** | `/api/backoffice` | Facturas, proveedores, PDFs | -| **Messages** | `/api/messages` | Conversaciones, mensajes directos | -| **Notifications** | `/api/notifications` | Alertas, entrega programada | -| **Activity** | `/api/activity` | Feed del dashboard, KPIs | -| **Departments** | `/api/departments` | Gestión de departamentos | -| **Search** | `/api/search` | Búsqueda global | -| **Scheduling** | `/api/scheduling` | Gestión de horarios de personal | - -### Sistema de Scheduling (Módulo Especial) {#scheduling} - -El módulo de scheduling incluye: - -- **CP-SAT Solver**: Optimizador de horarios con Google OR-Tools -- **Daemon Python**: Proceso persistente para solve requests -- **Validación**: Restricciones de negocio (descansos, días libres, bloques de noche) - -``` -POST /api/scheduling/months/:id/generate # Generar cronograma -POST /api/scheduling/months/:id/reset # Resetear asignaciones -POST /api/scheduling/constraints/:id/approve # Aprobar restricciones -``` - -> Para scripts de debug del scheduling, ver `docs/backend/scheduling/solver-setup.md`. - -### Formato de Respuestas - -```typescript -// Éxito -{ "success": true, "data": { ... } } - -// Error -{ "success": false, "error": "...", "code": "ERROR_CODE" } -``` - ---- - -## 5. Middlewares - -| Middleware | Propósito | -| ---------------------- | ------------------------------------- | -| `authenticateToken` | Verificar JWT (access token) | -| `roleCheck` | Validar permisos por rol | -| `rateLimiter` | Prevenir abuso (100 req/15min por IP) | -| `demoRestriction` | Bloquear writes para usuarios demo | - -### Servicios Cron - -```typescript -// CronService ejecuta: -- Notificaciones: Todos los días a las 7:00 AM -- Batch Payment: Día 10 de cada mes a las 23:59 - (facturas 'validated' → 'paid' del mes anterior) -``` - ---- - -## 6. Base de Datos - -### Esquema de Base de Datos - -El sistema usa **40+ tablas** organizadas por módulo: - -| Categoría | Tablas | -| ----------------- | -------------------------------------------------------------------------------------------------- | -| **Auth** | `users`, `roles` | -| **Groups** | `group_reservations`, `group_contacts`, `group_rooms`, `group_payments`, `group_history` | -| **Parking** | `parking_spaces`, `parking_bookings`, `parking_rates`, `parking_levels`, `parking_invoices` | -| **Logbooks** | `logbook_entries`, `logbook_comments`, `logbook_reads`, `logbook_history` | -| **Cashier** | `cashier_shifts`, `cashier_denominations`, `cashier_payments`, `cashier_vouchers`, `cashier_daily` | -| **Maintenance** | `maintenance_orders`, `maintenance_images`, `maintenance_history` | -| **Blacklist** | `blacklist_entries`, `blacklist_images` | -| **Conciliation** | `conciliations`, `conciliation_monthly` | -| **Backoffice** | `invoices`, `suppliers`, `invoice_images` | -| **Messages** | `conversations`, `messages`, `conversation_participants` | -| **Notifications** | `notifications`, `notification_reads`, `notification_schedule` | -| **Scheduling** | `scheduling_months`, `scheduling_assignments`, `scheduling_constraints`, `scheduling_config` | -| **Activity** | `activity_log`, `activity_types` | -| **System** | `departments`, `settings` | - -### Relaciones Clave - -``` -users (1) ─────────────< (n) logbook_entries -users (1) ─────────────< (n) maintenance_orders -users (1) ─────────────< (n) cashier_shifts -users (1) ─────────────< (n) group_history -users (1) ─────────────< (n) scheduling_assignments - -group_reservations (1) ─< (n) group_contacts -group_reservations (1) ─< (n) group_rooms -group_reservations (1) ─< (n) group_payments -group_reservations (1) ─< (n) group_history - -parking_spaces (1) ────< (n) parking_bookings -parking_levels (1) ────< (n) parking_spaces - -departments (1) ───────< (n) logbook_entries -departments (1) ───────< (n) users -``` - -### Configuración de Base de Datos - -| Entorno | Base de Datos | Host | -| --------- | ------------- | ---------------------- | -| **Local** | `hotel_db` | localhost:3306 | -| **Aiven** | `hotel_db` | Host cloud Aiven (SSL) | - -### Scripts SQL - -``` -db-mysql/ -├── MASTER_INSTALL.sql # Instalación completa -├── MASTER_INSTALL_LOCAL.sql # Para MySQL local -├── MASTER_INSTALL_AIVEN.sql # Para Aiven cloud -├── local/ -│ ├── 01_create_database.sql -│ ├── 02_core_tables.sql -│ ├── 03_logbook_tables.sql -│ ├── 04_parking_tables.sql -│ ├── 05_parking_functions_triggers.sql -│ ├── 06_parking_procedures.sql -│ ├── 07_parking_initial_data.sql -│ ├── 08_parking_sample_data.sql -│ ├── 09_conciliation.sql -│ ├── 10_group-tracking.sql -│ ├── 11_cashier.sql -│ ├── 12_blacklist.sql -│ ├── 13_maintenance.sql -│ ├── 14_messages.sql -│ ├── 15_demo_user.sql -│ ├── 16_backoffice.sql -│ ├── 17_notifications.sql -│ ├── 18_user_avatar.sql -│ ├── 19_scheduling.sql -│ └── 99_verification.sql -└── backup/ - ├── backup_hotel_db-local.sql - └── backup_hotel_db-aiven.sql -``` - ---- - -## 7. Configuración y Deployment - -### Variables de Entorno - -```env -DB_HOST=localhost -DB_USER=root -DB_PASSWORD=password -DB_NAME=hotel_db -PORT=4000 -SECRET_JWT_KEY=your-secret-key -SESSION_SECRET=your-session-secret -SALT_ROUNDS=10 -CLOUDINARY_CLOUD_NAME=... -CLOUDINARY_API_KEY=... -CLOUDINARY_API_SECRET=... -NODE_ENV=development -DB_ENVIRONMENT=local # o 'aiven' para cloud -``` - -### CORS Configuration - -El backend permite los siguientes orígenes: - -``` -- http://localhost:3000 (desarrollo) -- https://four-points.stackbp.es (producción) -- https://four-points.vercel.app (Vercel) -- https://*.vercel.app (todos los previews) -- https://four-points.onrender.com (fallback) -``` - -### Seguridad - -| Medida | Implementación | -| ---------------------- | ---------------------------------- | -| **XSS Protection** | HttpOnly cookies, sin localStorage | -| **CSRF Protection** | SameSite cookies, whitelist CORS | -| **SQL Injection** | Parameterized queries (mysql2) | -| **Rate Limiting** | 100 requests / 15 min por IP | -| **Password Hashing** | bcrypt (10 rounds) | -| **Input Validation** | Zod schemas en todos los endpoints | -| **Error Sanitization** | Errores genéricos en producción | - -### Deployment - -| Servicio | Proveedor | Propósito | -| ------------ | ------------------------ | ---------------------- | -| **Backend** | Render | Auto-deploy desde main | -| **Backend** | four-points.onrender.com | Fallback | -| **Database** | Aiven | MySQL 8 gestionado | -| **Media** | Cloudinary | Imágenes y PDFs | - ---- - -## 8. Scripts y Comandos - -```bash -pnpm dev # Desarrollo con tsx watch (puerto 4000) -pnpm dev:local # Usar MySQL local -pnpm dev:aiven # Usar MySQL Aiven -pnpm start # Start producción -pnpm typecheck # TypeScript check -pnpm test # Tests con Vitest -pnpm test:watch # Tests en modo watch -pnpm test:coverage # Tests con coverage -``` - ---- - -## 9. Documentación Relacionada - -Para documentación detallada consultar: - -- **Sistema de scheduling**: `docs/backend/scheduling/README.md` — arquitectura, endpoints, CP-SAT solver -- **Solver Python**: `docs/backend/scheduling/solver-setup.md` — daemon, OR-Tools, tests -- **Autenticación en producción**: `docs/backend/auth/productionAuthSetup.md` -- **CORS**: `docs/backend/config/cors-configuration.md` -- **Seguridad**: `docs/backend/security/security-implementation.md` -- **Checklist**: `docs/backend/checklist.md` -- **DB**: `backend/db-mysql/INDEX.md`, `MIGRATIONS_POLICY.md` -- **Testing**: `docs/backend/testing/README.md` -- **Restricciones de scheduling**: `docs/scheduling/constraints.md` -- **Variables de entorno**: `docs/backend/enviroments/environment-variables.md` \ No newline at end of file +# Four-Points — Backend + +REST API in Express 5 + TypeScript over MySQL 8, with the scheduling solver +(Python + OR-Tools) as a child process. To install and run the whole project, +see the [root README](../README.md). + +## Layout + +| Folder | What it holds | +| ------------------------------------------------------- | ---------------------------------------------------------------------- | +| `routes/`, `controllers/`, `services/`, `repositories/` | One folder per module | +| `middlewares/` | Auth, roles, demo restrictions, rate limits, uploads | +| `config/` | Database, CORS, logger, environment | +| `db-mysql/` | Schema, migrations and seeds ([policy](db-mysql/MIGRATIONS_POLICY.md)) | +| `scheduling-solver/` | Python CP-SAT solver | +| `scripts/` | `setup:local`, demo user, scheduling seed export | +| `tests/` | Vitest suites ([notes](tests/README.md)) | + +## Commands + +| Command | Does | +| ---------------------------------------------------- | ------------------------------------------------------------------ | +| `pnpm setup:local` | Builds a local `hotel_db` with fictitious data and the local admin | +| `pnpm dev:local` | API on :4000 against the local database | +| `pnpm test` | Unit and integration tests | +| `pnpm test:corpus` | Solver corpus (needs the Python venv) | +| `pnpm typecheck` · `pnpm lint` · `pnpm format:check` | What CI runs | + +Each module has an `AGENTS.md` under `services/` with its rules; the product +documentation lives in [`docs/`](../docs/general/README.md). diff --git a/backend/backend.md b/backend/backend.md deleted file mode 100644 index abb08eaf..00000000 --- a/backend/backend.md +++ /dev/null @@ -1,795 +0,0 @@ -# Four Points - Backend Architecture - -> Private REST API for the Hotel Property Management System - -![Node.js](https://img.shields.io/badge/Node.js-339933?style=flat&logo=node.js&logoColor=white) -![Express](https://img.shields.io/badge/Express_5-000000?style=flat&logo=express&logoColor=white) -![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=flat&logo=typescript&logoColor=white) -![MySQL](https://img.shields.io/badge/MySQL_8-4479A1?style=flat&logo=mysql&logoColor=white) - -This document describes the backend architecture. The backend repository is **private**. - ---- - -## Table of Contents - -1. [Tech Stack](#1-tech-stack) -2. [Architecture](#2-architecture) -3. [Entry Point (index.ts)](#3-entry-point-indexts) -4. [Configuration](#4-configuration) -5. [API Structure](#5-api-structure) -6. [Controllers](#6-controllers) -7. [Repositories](#7-repositories) -8. [Services](#8-services) -9. [Authentication & Security](#9-authentication--security) -10. [Database Schema](#10-database-schema) -11. [Cron Jobs](#11-cron-jobs) -12. [File Handling](#12-file-handling) -13. [Error Handling](#13-error-handling) -14. [Middlewares](#14-middlewares) -15. [Demo Mode](#15-demo-mode) -16. [Deployment](#16-deployment) -17. [Scripts & Commands](#17-scripts--commands) - ---- - -## 1. Tech Stack - -| Technology | Version | Purpose | -| ---------- | ------- | ------- | -| **Node.js** | 20+ | Runtime environment | -| **Express** | 5.1.0 | Web framework (latest major) | -| **TypeScript** | 5.9.3 | Type-safe development | -| **MySQL** | 8 | Relational database | -| **mysql2** | 3.14.2 | MySQL driver with promises | -| **Passport.js** | 0.7.0 | Authentication middleware | -| **passport-jwt** | 4.0.1 | JWT strategy for Passport | -| **jsonwebtoken** | 9.0.2 | JWT token handling | -| **bcrypt** | 6.0.0 | Password hashing (10 rounds) | -| **Zod** | 4.0.5 | Request validation & schemas | -| **Cloudinary** | 2.8.0 | Image & PDF cloud storage | -| **Multer** | 2.0.2 | File upload (multipart/form-data) | -| **Archiver** | 7.0.1 | ZIP file generation | -| **node-cron** | 4.2.1 | Scheduled tasks | -| **nodemailer** | 7.0.10 | Email notifications | -| **express-rate-limit** | 8.2.1 | Rate limiting (100 req/15min) | -| **cors** | 2.8.5 | Cross-origin resource sharing | -| **cookie-parser** | 1.4.7 | Cookie parsing | -| **dayjs** | 1.11.18 | Date manipulation | -| **Axios** | 1.12.2 | External API calls | -| **dotenv** | 17.2.0 | Environment variables | -| **tsx** | 4.20.6 | TypeScript execution (dev) | - -### Package Manager - -```json -{ - "packageManager": "pnpm@10.13.1" -} -``` - -### Dev Dependencies - -| Tool | Purpose | -| ---- | ------- | -| cross-env | Environment variables cross-platform | -| standard | ESLint config | -| @types/* | TypeScript definitions | - ---- - -## 2. Architecture - -``` -┌─────────────────────────────────────────────────────────────┐ -│ EXPRESS SERVER │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ index.ts │ │ -│ │ • CORS configuration (7 origins allowed) │ │ -│ │ • Security middleware (x-powered-by disabled) │ │ -│ │ • Global error handler │ │ -│ │ • 404 handler │ │ -│ └─────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────┐ │ -│ │ Middlewares │ │ -│ │ • express.json() - Body parsing │ │ -│ │ • cookie-parser() - Cookie parsing │ │ -│ │ • cors() - Cross-origin settings │ │ -│ │ • rateLimiter - 100 req/15min per IP │ │ -│ └─────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ -│ │ Routes │ → │ Controllers │ → │Repositories │ │ -│ │ (Express) │ │ (Lógica) │ │ (MySQL) │ │ -│ │ validation │ │ negocio │ │ queries │ │ -│ └─────────────┘ └─────────────┘ └─────────────┘ │ -│ │ │ │ │ -│ ▼ ▼ ▼ │ -│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ -│ │ Zod Schemas │ │ Services │ │ Models │ │ -│ │ (validatio) │ │ (cron,ext) │ │ (Zod/TS) │ │ -│ └─────────────┘ └─────────────┘ └─────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────┐ │ -│ │ MySQL 8 │ │ -│ │ (40+ tables)│ │ -│ └─────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 3. Entry Point (index.ts) - -**File:** `backend/index.ts` (192 líneas) - -```typescript -// Main imports -import express, { Request, Response, NextFunction } from 'express' -import cookieParser from 'cookie-parser' -import cors from 'cors' -import { PORT } from './config/config.js' - -// Services -import { CronService } from './services/cron/cron-service.js' - -// Route imports (18 route files) -import authRoutes from './routes/auth/auth-routes.js' -import userRoutes from './routes/auth/user-routes.js' -import logbookRoutes from './routes/logbook/logbook-routes.js' -import parkingRoutes from './routes/parking/parking.routes.js' -import parkingStatsRoutes from './routes/parking/stats.routes.js' -import parkingBookingsRoutes from './routes/parking/bookings.routes.js' -import departmentsRoutes from './routes/departments/departments-routes.js' -import conciliationRoutes from './routes/conciliation/conciliation.routes.js' -import groupRoutes from './routes/group/group-routes.js' -import notificationRoutes from './routes/notifications/notifications-routes.js' -import cashierRoutes from './routes/cashier/cashier-routes.js' -import blacklistRoutes from './routes/blacklist/blacklist-routes.js' -import maintenanceRoutes from './routes/maintenance/maintenance-routes.js' -import activityRoutes from './routes/activity/activity-routes.js' -import messagesRoutes from './routes/messages/messages-routes.js' -import backofficeRoutes from './routes/backoffice/backoffice-routes.js' -import searchRoutes from './routes/search/search-routes.js' -import demoActivityRoutes from './routes/demo/demo-activity-routes.js' - -// App initialization -const app = express() - -// Security: Disable x-powered-by header -app.disable('x-powered-by') - -// CORS Configuration -const allowedOrigins = [ - 'http://localhost:3000', - 'https://four-points.stackbp.es', - 'https://four-points.vercel.app', - 'https://api.four-points.stackbp.es', - 'https://four-points.onrender.com', - process.env.FRONTEND_URL, -].filter(Boolean) as string[] - -const vercelPreviewPattern = /\.vercel\.app$/ - -app.use(cors({ - origin: (origin, callback) => { - if (!origin) { - callback(null, true) - return - } - if (allowedOrigins.includes(origin) || vercelPreviewPattern.test(origin)) { - callback(null, true) - } else { - console.warn(`[CORS] Blocked origin: ${origin}`) - callback(new Error('Not allowed by CORS')) - } - }, - credentials: true, - methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization', 'Cookie'], - exposedHeaders: ['Set-Cookie'], -})) - -// Global middlewares -app.use(express.json()) -app.use(cookieParser()) - -// Health check endpoint -app.get('/', (_req: Request, res: Response) => { - res.json({ - status: 'online', - message: 'API funcionando correctamente', - timestamp: new Date().toISOString(), - }) -}) - -// Route registration (18 modules) -app.use('/api/auth', authRoutes) -app.use('/api/users', userRoutes) -app.use('/api/logbooks', logbookRoutes) -app.use('/api/parking', parkingRoutes) -app.use('/api/parking/stats', parkingStatsRoutes) -app.use('/api/parking/bookings', parkingBookingsRoutes) -app.use('/api/departments', departmentsRoutes) -app.use('/api/conciliations', conciliationRoutes) -app.use('/api/groups', groupRoutes) -app.use('/api/notifications', notificationRoutes) -app.use('/api/cashier', cashierRoutes) -app.use('/api/blacklist', blacklistRoutes) -app.use('/api/maintenance', maintenanceRoutes) -app.use('/api/activity', activityRoutes) -app.use('/api/messages', messagesRoutes) -app.use('/api/backoffice', backofficeRoutes) -app.use('/api/search', searchRoutes) -app.use('/api/demo-activity', demoActivityRoutes) - -// 404 Handler -app.use((req: Request, res: Response) => { - res.status(404).json({ - error: 'Ruta no encontrada', - path: req.path, - }) -}) - -// Global Error Handler -interface HttpError extends Error { - status?: number -} - -app.use((err: HttpError, _req: Request, res: Response, _next: NextFunction) => { - console.error('Error global:', err) - res.status(err.status || 500).json({ - error: process.env.NODE_ENV === 'production' ? 'Error interno del servidor' : err.message, - }) -}) - -// Start server -app.listen(PORT, () => { - console.log(`🚀 Server running at http://localhost:${PORT}`) - console.log(`📝 Environment: ${process.env.NODE_ENV || 'development'}`) - CronService.start() -}) -``` - ---- - -## 4. Configuration - -**File:** `backend/config/config.ts` (44 líneas) - -```typescript -import dotenv from 'dotenv' -dotenv.config() - -interface EnvConfig { - PORT: number - SECRET_JWT_KEY: string - SALT_ROUNDS: number -} - -// Critical: JWT secret must be defined -if (!process.env.SECRET_JWT_KEY) { - throw new Error('Falta la variable SECRET_JWT_KEY en el entorno') -} - -export const PORT: number = parseInt(process.env.PORT || '3000', 10) -export const SECRET_JWT_KEY: string = process.env.SECRET_JWT_KEY -export const SALT_ROUNDS: number = parseInt(process.env.SALT_ROUNDS || '10', 10) - -export default config -``` - -### Environment Variables (.env.example) - -```env -# Server -PORT=4000 -NODE_ENV=development - -# Database -DB_HOST=localhost -DB_USER=root -DB_PASSWORD=password -DB_NAME=hotel_db -DB_ENVIRONMENT=local - -# Authentication -SECRET_JWT_KEY=your-super-secret-key -SALT_ROUNDS=10 - -# Cloudinary (optional) -CLOUDINARY_CLOUD_NAME=your-cloud-name -CLOUDINARY_API_KEY=your-api-key -CLOUDINARY_API_SECRET=your-api-secret - -# Email (optional) -SMTP_HOST=smtp.example.com -SMTP_PORT=587 -SMTP_USER=your-email -SMTP_PASS=your-password -``` - -### Database Connection - -**File:** `backend/config/db.ts` - -Manages MySQL connection pool using mysql2 with promise support. - -### Error Codes - -**File:** `backend/config/error-codes.ts` - -Centralized error codes for i18n integration. - -### Date Utilities - -**File:** `backend/config/date-utils.ts` - -Helper functions for date manipulation using dayjs. - ---- - -## 5. API Structure - -### Authentication - -| Method | Endpoint | Description | -| ------ | -------- | ----------- | -| POST | `/api/auth/login` | Login with credentials | -| POST | `/api/auth/logout` | Clear session | -| GET | `/api/auth/me` | Get current user | -| POST | `/api/auth/refresh` | Refresh access token | -| POST | `/api/auth/register` | Register new user | - -### Core Modules - -| Module | Base Route | Controllers | -| ------ | ---------- | ----------- | -| **Users** | `/api/users` | user-controllers.ts | -| **Groups** | `/api/groups` | group-controller.ts, contact, payment, room, history | -| **Parking** | `/api/parking` | parking.controller.ts, bookings, stats | -| **Logbooks** | `/api/logbooks` | logbook-controllers.ts, comments, reads | -| **Cashier** | `/api/cashier` | shift, denomination, payment, daily, report, voucher | -| **Maintenance** | `/api/maintenance` | maintenance-controller.ts | -| **Blacklist** | `/api/blacklist` | blacklist-controller.ts | -| **Conciliation** | `/api/conciliations` | conciliation.controller.ts, monthly | -| **Backoffice** | `/api/backoffice` | backoffice-controller.ts | -| **Messages** | `/api/messages` | conversation-controller.ts, message-controller.ts | -| **Notifications** | `/api/notifications` | notification-controller.ts | -| **Activity** | `/api/activity` | activity-controller.ts | -| **Departments** | `/api/departments` | departments-controller.ts | -| **Scheduling** | `/api/scheduling` | scheduling-controller.ts | -| **Chat** | `/api/chat` | chat-controller.ts | -| **Search** | `/api/search` | search-controller.ts | -| **Demo** | `/api/demo-activity` | demo-activity-controller.ts | - ---- - -## 6. Controllers - -Located in `backend/controllers/` (20+ controller files) - -### Auth Controllers - -| File | Functions | -| ---- | --------- | -| `auth-controllers.ts` | login, logout, me, refresh | -| `user-controllers.ts` | CRUD, avatar upload, profile | - -### Module Controllers - -**Groups:** -- `group-controller.ts` - Main CRUD operations -- `group-contact-controller.ts` - Contact management -- `group-payment-controller.ts` - Payment tracking -- `group-room-controller.ts` - Room assignment -- `group-history-controller.ts` - Audit trail -- `group-status-controller.ts` - Status workflow - -**Parking:** -- `parking.controller.ts` - Spaces, rates, levels -- `bookings.controller.ts` - Booking CRUD, check-in/out -- `stats.controller.ts` - Dashboard statistics - -**Cashier:** -- `cashier-shift-controller.ts` - Shift management -- `cashier-denomination-controller.ts` - Currency counting -- `cashier-payment-controller.ts` - Payment processing -- `cashier-daily-controller.ts` - Daily summaries -- `cashier-report-controller.ts` - Report generation -- `cashier-history-controller.ts` - History tracking -- `cashier-voucher-controller.ts` - Voucher management - -**Logbooks:** -- `logbook-controllers.ts` - Entry CRUD -- `logbookComments-controllers.ts` - Comments -- `logbookReads-controllers.ts` - Read tracking - -**Others:** -- `activity-controller.ts` - Dashboard feed -- `backoffice-controller.ts` - Invoices, suppliers -- `blacklist-controller.ts` - Incident records -- `maintenance-controller.ts` - Work orders -- `conciliation.controller.ts` - Daily reconciliation -- `conciliation-monthly.controller.ts` - Monthly summaries -- `departments-controller.ts` - Department management -- `notification-controller.ts` - Alert management -- `conversation-controller.ts` - Chat conversations -- `message-controller.ts` - Individual messages -- `search-controller.ts` - Global search -- `demo-activity-controller.ts` - Demo logging - ---- - -## 7. Repositories - -Located in `backend/repositories/` (25+ repository files) - -### Structure Pattern - -Each module has dedicated repositories for data access: - -``` -repositories/ -├── auth/ -│ └── user-repository.ts -├── activity/ -│ └── activity-repository.ts -├── backoffice/ -│ └── backoffice-repository.ts -├── blacklist/ -│ └── blacklist-repository.ts -├── cashier/ -│ ├── cashier-daily-repository.ts -│ ├── cashier-denomination-repository.ts -│ ├── cashier-history-repository.ts -│ ├── cashier-payment-repository.ts -│ ├── cashier-shift-repository.ts -│ ├── cashier-shift-user-repository.ts -│ └── cashier-voucher-repository.ts -├── conciliation/ -│ ├── conciliation.repository.ts -│ └── conciliation-monthly.repository.ts -├── departments/ -│ └── departments-repository.ts -├── group/ -│ ├── group-contact-repository.ts -│ ├── group-history-repository.ts -│ ├── group-payment-repository.ts -│ ├── group-repository.ts -│ ├── group-room-repository.ts -│ └── group-status-repository.ts -├── logbook/ -│ ├── logbook-repository.ts -│ ├── logbookComments-repository.ts -│ ├── logbookCommentsHistory-repository.ts -│ ├── logbookHistory-repository.ts -│ └── logbookReads-repository.ts -├── maintenance/ -│ └── maintenance-repository.ts -├── messages/ -│ ├── conversation-repository.ts -│ └── message-repository.ts -├── notifications/ -│ └── notification-repository.ts -├── parking/ -│ ├── bookings.repository.ts -│ ├── parking.repository.ts -│ └── stats.repository.ts -└── search/ - └── search-repository.ts -``` - ---- - -## 8. Services - -Located in `backend/services/` (8 service files) - -| Service | File | Purpose | -| ------- | ---- | ------- | -| **Token Service** | `auth/tokenService.ts` | JWT generation and verification | -| **Cloudinary** | `blacklist/cloudinary-service.ts` | Image upload management | -| **Cron Service** | `cron/cron-service.ts` | Scheduled tasks | -| **Group History** | `group/group-history-service.ts` | Audit logging | -| **Payment Calculator** | `group/payment-calculator-service.ts` | Payment calculations | -| **Logbook History** | `logbook/logbookHistory-service.ts` | Read tracking | -| **Notification Generator** | `notifications/notification-generator-service.ts` | Auto notifications | -| **Schedule Validator** | `scheduling/schedule-validator.ts` | Real-time schedule validation | - ---- - -## 9. Authentication & Security - -### JWT Flow - -``` -Login → Validate credentials → Generate tokens → Set HttpOnly cookies - │ -Request with cookie → Verify JWT → Check role → Execute controller ─┘ - │ -401 → Auto-refresh via /api/auth/refresh → Retry request - │ -Refresh expired → Redirect to /login -``` - -### Token Configuration - -| Token | Duration | Storage | -| ----- | -------- | ------- | -| Access | 15 min | HttpOnly cookie | -| Refresh | 7 days | HttpOnly cookie | - -### Security Measures - -| Measure | Implementation | -| ------- | -------------- | -| **XSS Protection** | HttpOnly cookies, no localStorage | -| **CSRF Protection** | SameSite cookies, CORS whitelist | -| **SQL Injection** | Parameterized queries (mysql2) | -| **Rate Limiting** | 100 requests / 15 min per IP | -| **Password Hashing** | bcrypt (10 rounds) | -| **Input Validation** | Zod schemas on all endpoints | -| **Error Sanitization** | Generic errors in production | -| **Header Security** | x-powered-by disabled | - -### Middleware Functions - -```typescript -// authenticateToken.ts -export function authenticateToken(req, res, next) { - const token = req.cookies.access_token - if (!token) return res.status(401).json({ error: 'No token' }) - - jwt.verify(token, SECRET_JWT_KEY, (err, user) => { - if (err) return res.status(403).json({ error: 'Invalid token' }) - req.user = user - next() - }) -} - -// roleCheck.ts -export function roleCheck(...roles) { - return (req, res, next) => { - if (!roles.includes(req.user.role)) { - return res.status(403).json({ error: 'Insufficient permissions' }) - } - next() - } -} -``` - ---- - -## 10. Database Schema - -### Core Tables (40+) - -| Category | Tables | -| --------- | ------ | -| **Auth** | `users`, `refresh_tokens` | -| **Groups** | `group_reservations`, `group_contacts`, `group_rooms`, `group_payments`, `group_history` | -| **Parking** | `parking_spaces`, `parking_bookings`, `parking_rates`, `parking_levels`, `parking_invoices` | -| **Logbooks** | `logbook_entries`, `logbook_comments`, `logbook_reads`, `logbook_history` | -| **Cashier** | `cashier_shifts`, `cashier_denominations`, `cashier_payments`, `cashier_vouchers`, `cashier_daily`, `cashier_shift_users` | -| **Maintenance** | `maintenance_orders`, `maintenance_images`, `maintenance_history` | -| **Blacklist** | `blacklist_entries`, `blacklist_images` | -| **Conciliation** | `conciliations`, `conciliation_monthly` | -| **Backoffice** | `invoices`, `suppliers`, `invoice_images` | -| **Messages** | `conversations`, `messages`, `conversation_participants` | -| **Notifications** | `notifications`, `notification_reads`, `notification_schedule` | -| **Activity** | `activity_log`, `activity_types` | -| **Scheduling** | `scheduling_months`, `scheduling_days`, `scheduling_assignments`, `scheduling_constraints`, `scheduling_shifts`, `scheduling_employee_rules`, `scheduling_employees`, `scheduling_employee_contracts`, `scheduling_config`, `scheduling_history` | -| **System** | `departments`, `settings` | - -### Key Relationships - -``` -users (1) ─────────────< (n) logbook_entries -users (1) ─────────────< (n) maintenance_orders -users (1) ─────────────< (n) cashier_shifts -users (1) ─────────────< (n) group_history - -group_reservations (1) ─< (n) group_contacts -group_reservations (1) ─< (n) group_rooms -group_reservations (1) ─< (n) group_payments -group_reservations (1) ─< (n) group_history - -parking_spaces (1) ────< (n) parking_bookings -parking_levels (1) ────< (n) parking_spaces - -departments (1) ───────< (n) logbook_entries -departments (1) ───────< (n) users -``` - ---- - -## 11. Cron Jobs - -**File:** `backend/services/cron/cron-service.ts` (263 líneas) - -### Scheduled Tasks - -| Job | Schedule | Purpose | -| --- | -------- | ------- | -| **Notifications** | Daily at 7:00 AM (`0 7 * * *`) | Process pending notifications | -| **Batch Payment** | Day 10 at 23:59 (`59 23 10 * *`) | Mark validated invoices as paid | - -### Batch Payment Process - -```typescript -// Marks all 'validated' invoices from previous month as 'paid' -// Triggered automatically on day 10 at 23:59 -// Manual trigger available via CronService.runBatchPaymentNow() -``` - -### Cron Service Methods - -| Method | Purpose | -| ------- | ------- | -| `start()` | Initialize all cron jobs | -| `runNotificationsNow()` | Manual trigger for notifications | -| `runBatchPaymentNow()` | Manual trigger for batch payment | -| `previewBatchPayment()` | Preview invoices to be marked as paid | -| `getStatus()` | Get next run times | - ---- - -## 12. File Handling - -| Feature | Technology | Usage | -| ------- | ---------- | ----- | -| **Image Upload** | Multer + Cloudinary | Avatars, maintenance photos, blacklist evidence | -| **PDF Upload** | Multer + Cloudinary | Invoice documents, contracts | -| **ZIP Generation** | Archiver | Bulk export of images/documents | -| **Email Attachments** | Nodemailer | Reports, notifications with files | -| **PDF Generation** | pdf-lib (service) | Parking invoices, receipts | - ---- - -## 13. Error Handling - -### Response Format - -```typescript -// Success response -{ "success": true, "data": { ... } } - -// Error response -{ "success": false, "error": "Error message", "code": "ERROR_CODE" } - -// Validation error -{ "success": false, "error": "Validation failed", "details": [...] } -``` - -### HTTP Status Codes - -| Code | Meaning | -| ---- | ------- | -| 200 | OK | -| 201 | Created | -| 400 | Bad Request | -| 401 | Unauthorized | -| 403 | Forbidden | -| 404 | Not Found | -| 422 | Validation Error | -| 500 | Internal Server Error | - ---- - -## 14. Middlewares - -| Middleware | File | Purpose | -| --------- | ---- | ------- | -| `authenticateToken` | authenticateToken.ts | Verify JWT, attach user to request | -| `roleCheck` | roleCheck.ts | Validate user role permissions | -| `rateLimiter` | rateLimiter.ts | Prevent abuse (100 req/15min per IP) | -| `demoRestriction` | demoRestriction.ts | Block write operations for demo users | - ---- - -## 15. Demo Mode - -Demo users can read all data but write operations are restricted: - -| Route | Method | Allowed Action | -| ----- | ------ | -------------- | -| `/api/auth/logout` | POST | Session management | -| `/api/parking/bookings` | POST | Test booking flow | -| `/api/logbooks/*/comments` | POST | Test comments | -| `/api/maintenance` | POST | Test work orders | - -All other write operations return `403 Forbidden`. - ---- - -## 16. Deployment - -| Service | Provider | Configuration | -| ------- | -------- | ------------- | -| **Backend** | Render | Auto-deploy from main branch | -| **Backend** | Fallback | four-points.onrender.com:4000 | -| **Database** | Aiven | MySQL 8 with SSL | -| **Media Storage** | Cloudinary | Images and PDFs | - -### Build & Start - -```bash -# Development -pnpm dev # tsx watch -pnpm dev:local # DB_ENVIRONMENT=local -pnpm dev:aiven # DB_ENVIRONMENT=aiven - -# Production -pnpm start # tsx index.ts - -# TypeScript -pnpm typecheck # tsc --noEmit -``` - ---- - -## 17. Scripts & Commands - -### npm Scripts - -```bash -pnpm dev # Development with tsx watch -pnpm dev:local # Use local MySQL database -pnpm dev:aiven # Use Aiven cloud database -pnpm build # No build needed (tsx) -pnpm start # Production start -pnpm typecheck # TypeScript type checking -pnpm test # Run tests -pnpm test:watch # Watch mode -pnpm test:coverage # Coverage report -``` - -### Database Commands - -```bash -# MySQL connection -mysql -u root -p hotel_db - -# Aiven connection -mysql -u usuario -h host -p hotel_db -P 3306 --ssl-ca=certs/ca-certificate.pem - -# Dump local -mysqldump -u root -p hotel_db > backup.sql - -# Import -mysql -u root -p hotel_db < backup.sql -``` - ---- - -## Related Documentation - -| Document | Description | -| --------- | ----------- | -| `backend/README.md` | JWT to Sessions migration guide | -| `backend.md` | This file | -| `docs/backend/database/` | Database configuration guides | -| `docs/backend/security/` | Security implementation details | - ---- - -## Author - -**Salvador Perez** - -- GitHub: [@bpstack](https://github.com/bpstack) -- Email: contact.bpstack@gmail.com - ---- - -

- Built with Express 5, TypeScript, and MySQL -

diff --git a/backend/db-mysql/AGENTS.md b/backend/db-mysql/AGENTS.md index 2eda1fe0..47b3ac1c 100644 --- a/backend/db-mysql/AGENTS.md +++ b/backend/db-mysql/AGENTS.md @@ -24,7 +24,6 @@ backend/db-mysql/ ├── backup/ # DB dumps (local + Aiven) ├── MASTER_INSTALL.sql # Runs all aiven/ files in order. Empty DB only. ├── MIGRATIONS_POLICY.md -├── MIGRATION_GUIDE.md ├── INDEX.md # Full table index + incremental migration log └── mock-data.sql # Clears module data and loads fictitious data (keeps users and scheduling) ``` @@ -172,4 +171,4 @@ password. - `MIGRATIONS_POLICY.md` — full policy rationale and when to change the model. - `INDEX.md` — module-by-module table list + full incremental migration log. -- `MIGRATION_GUIDE.md` — initial install guide (scripts 01-19 walkthrough). +- Fresh install: `pnpm setup:local` (`scripts/setup-local.ts`), never `MASTER_INSTALL.sql` by hand. diff --git a/backend/db-mysql/INDEX.md b/backend/db-mysql/INDEX.md index e0de4188..30eef9bf 100644 --- a/backend/db-mysql/INDEX.md +++ b/backend/db-mysql/INDEX.md @@ -34,7 +34,6 @@ db-mysql/ ├── MASTER_INSTALL.sql # Instalación completa (apunta a aiven/) ├── INDEX.md ├── README.md -├── MIGRATION_GUIDE.md ├── cleanup-scheduling.sql └── mock-data.sql # Vacía los módulos y carga datos ficticios (no toca usuarios ni horarios) ``` diff --git a/backend/db-mysql/MIGRATIONS_POLICY.md b/backend/db-mysql/MIGRATIONS_POLICY.md index a020598f..6a805718 100644 --- a/backend/db-mysql/MIGRATIONS_POLICY.md +++ b/backend/db-mysql/MIGRATIONS_POLICY.md @@ -71,7 +71,7 @@ Esos cambios se han reconstruido a posteriori como scripts marcados `[RETROACTIV ## Archivos relacionados - `INDEX.md` — índice general, tabla "Migraciones incrementales" -- `MIGRATION_GUIDE.md` — guía de instalación inicial (scripts 01-20 congelados) +- `pnpm setup:local` — instalación local desde cero (`scripts/setup-local.ts`) - `README.md` — overview de la carpeta - `scripts/` — scripts incrementales, ordenados por fecha. Única fuente de verdad para cambios desde 2026-05-20. - `aiven/NN_*.sql` — snapshot congelado del install base. No editar. diff --git a/backend/db-mysql/MIGRATION_GUIDE.md b/backend/db-mysql/MIGRATION_GUIDE.md deleted file mode 100644 index 33cf7ae1..00000000 --- a/backend/db-mysql/MIGRATION_GUIDE.md +++ /dev/null @@ -1,216 +0,0 @@ -# 🚀 GUÍA DE MIGRACIÓN - HOTEL DB - -Esta guía te permite reconstruir completamente la base de datos en cualquier momento. - -## 📋 ORDEN DE EJECUCIÓN - -Ejecuta los scripts en orden usando `MASTER_INSTALL.sql` o manualmente desde el directorio `aiven/`: - -```bash -# Opción 1: Un solo comando (recomendado) -mysql -u root -p < MASTER_INSTALL.sql - -# Opción 2: Manual, script por script (desde directorio aiven/) -cd aiven/ -mysql -u root -p < 01_create_database.sql -mysql -u root -p < 02_core_tables.sql -# ... etc -``` - -### Scripts disponibles (01-19) - -| # | Archivo | Descripción | -| --- | ----------------------------------- | ---------------------------------------------------------------------------------- | -| 01 | `01_create_database.sql` | Crea BD con UTF-8 | -| 02 | `02_core_tables.sql` | Roles, departments, users | -| 03 | `03_logbook_tables.sql` | Sistema de bitácora | -| 04 | `04_parking_tables.sql` | Tablas de parking | -| 05 | `05_parking_functions_triggers.sql` | Funciones y triggers | -| 06 | `06_parking_procedures.sql` | Procedimientos almacenados | -| 07 | `07_parking_initial_data.sql` | Datos iniciales (plazas + tarifas) | -| 08 | `08_parking_sample_data.sql` | Datos de ejemplo (opcional) | -| 09 | `09_conciliation.sql` | Conciliación bancaria | -| 10 | `10_group-tracking.sql` | Seguimiento de grupos | -| 11 | `11_cashier.sql` | Sistema de caja | -| 12 | `12_blacklist.sql` | Lista negra | -| 13 | `13_maintenance.sql` | Mantenimiento | -| 14 | `14_messages.sql` | Mensajería | -| 15 | `15_demo_user.sql` | Usuario demo | -| 16 | `16_backoffice.sql` | Backoffice | -| 17 | `17_notifications.sql` | Notificaciones | -| 18 | `18_user_avatar.sql` | Avatares de usuario | -| 19 | `19_scheduling.sql` | Programación de turnos | -| 20 | `20_checklist.sql` | Checklists operativos (runs, step_state, event_log, config, comments, attachments) | -| 99 | `99_verification.sql` | Verificación de instalación | - -> **Política de migraciones (vigente desde 2026-05-20):** Los archivos `aiven/NN_*.sql` están **congelados** como snapshot del schema base. Todo cambio posterior vive únicamente en `scripts/AAAAMMDD_*.sql`. Para reconstruir la BD desde cero: `MASTER_INSTALL.sql` + todos los `scripts/*.sql` en orden cronológico. Ver `MIGRATIONS_POLICY.md`. - ---- - -## ✅ VERIFICACIÓN POST-MIGRACIÓN - -Después de ejecutar todos los scripts, verifica que todo esté correcto: - -```sql -USE hotel_db; - --- Verificar tablas -SHOW TABLES; - --- Verificar plazas -SELECT COUNT(*) AS total_plazas FROM parking_spots; --- Debe devolver: 20 - --- Verificar tarifas -SELECT COUNT(*) AS total_tarifas FROM parking_rates; --- Debe devolver: 30 - --- Verificar disponibilidad -SELECT COUNT(*) AS total_disponibilidad FROM parking_availability; --- Debe devolver: ~7300 (20 plazas × 365 días) - --- Verificar funciones -SELECT ROUTINE_NAME -FROM information_schema.ROUTINES -WHERE ROUTINE_SCHEMA = 'hotel_db' - AND ROUTINE_TYPE = 'FUNCTION'; - --- Verificar procedimientos -SELECT ROUTINE_NAME -FROM information_schema.ROUTINES -WHERE ROUTINE_SCHEMA = 'hotel_db' - AND ROUTINE_TYPE = 'PROCEDURE'; - --- Verificar triggers -SELECT TRIGGER_NAME -FROM information_schema.TRIGGERS -WHERE TRIGGER_SCHEMA = 'hotel_db'; -``` - ---- - -## 🔄 RESET COMPLETO - -Si necesitas empezar desde cero (⚠️ CUIDADO: Borra TODO): - -```bash -# 1. Hacer backup -mysqldump -u root -p hotel_db > backup_$(date +%Y%m%d).sql - -# 2. Ejecutar instalación completa -mysql -u root -p < MASTER_INSTALL.sql -``` - ---- - -## 🛠️ MANTENIMIENTO - -### Sincronización diaria automática (Parking) - -Añade a cron (Linux/Mac) o usa evento MySQL: - -```sql -USE hotel_db; - -SET GLOBAL event_scheduler = ON; - -CREATE EVENT daily_maintenance -ON SCHEDULE EVERY 1 DAY -STARTS (TIMESTAMP(CURRENT_DATE) + INTERVAL 1 DAY + INTERVAL 3 HOUR) -DO - CALL daily_parking_maintenance(); -``` - -### Sincronización manual (Parking) - -```sql -CALL sync_parking_availability(); -``` - ---- - -## 📦 ESTRUCTURA DE ARCHIVOS - -``` -db-mysql/ -├── aiven/ -│ ├── 01_create_database.sql -│ ├── 02_core_tables.sql -│ ├── 03_logbook_tables.sql -│ ├── 04_parking_tables.sql -│ ├── 05_parking_functions_triggers.sql -│ ├── 06_parking_procedures.sql -│ ├── 07_parking_initial_data.sql -│ ├── 08_parking_sample_data.sql -│ ├── 09_conciliation.sql -│ ├── 10_group-tracking.sql -│ ├── 11_cashier.sql -│ ├── 12_blacklist.sql -│ ├── 13_maintenance.sql -│ ├── 14_messages.sql -│ ├── 15_demo_user.sql -│ ├── 16_backoffice.sql -│ ├── 17_notifications.sql -│ ├── 18_user_avatar.sql -│ ├── 19_scheduling.sql -│ └── 99_verification.sql -├── MASTER_INSTALL.sql -├── INDEX.md -└── MIGRATION_GUIDE.md -``` - ---- - -## 🎯 CARACTERÍSTICAS DEL SISTEMA - -### Parking - -- **Generación automática de booking_code**: Formato `PK-YYYYMMDD-0001` -- **Control de disponibilidad**: Triggers mantienen sincronizada la tabla `parking_availability` -- **Integridad referencial flexible**: SET NULL para preservar historial - -### Scheduling - -- **Gestión de turnos mensuales**: Meses en estado `draft` o `published` -- **Bloqueo de celdas**: Assignments con `source_constraint_id` están bloqueados -- **Validación en tiempo real**: Validator evalúa constraints - ---- - -## 🆘 TROUBLESHOOTING - -### Error: "Foreign key constraint fails" - -- Asegúrate de ejecutar los scripts en orden -- Verifica que la tabla `users` existe antes de crear `parking_bookings` - -### Error: "Trigger already exists" - -- Los scripts incluyen `DROP TRIGGER IF EXISTS` - -### Error: "Function does not exist" - -- Verifica que ejecutaste `05_parking_functions_triggers.sql` - -### Disponibilidad no se actualiza (Parking) - -```sql -CALL sync_parking_availability(); -SHOW TRIGGERS LIKE 'parking_bookings'; -``` - ---- - -## 📞 NOTAS IMPORTANTES - -1. **Backup**: Siempre haz backup antes de cualquier migración -2. **Orden**: Los scripts DEBEN ejecutarse en orden -3. **UTF-8**: Todos los scripts usan `utf8mb4_0900_ai_ci` (MySQL 8.0+) -4. **Passwords**: Cambia las contraseñas por defecto -5. **Producción**: Revisa y ajusta las tarifas según tu negocio - ---- - -**Última actualización**: Febrero 2026 -**Versión**: 2.0 -**Estado**: ✅ Producción diff --git a/backend/db-mysql/README.md b/backend/db-mysql/README.md index c9c83c01..d13f342d 100644 --- a/backend/db-mysql/README.md +++ b/backend/db-mysql/README.md @@ -36,7 +36,6 @@ db-mysql/ ├── MASTER_INSTALL.sql # Snapshot 2026-05-20 (base congelada) ├── INDEX.md # Índice general + tabla de incrementales ├── MIGRATIONS_POLICY.md # Política vigente -├── MIGRATION_GUIDE.md # Guía de instalación └── README.md # Este archivo ``` @@ -45,15 +44,14 @@ db-mysql/ ### Instalación completa ```bash -# Un solo comando (recomendado) -mysql -u root -p < MASTER_INSTALL.sql - -# O desde el directorio aiven/ -cd aiven/ -mysql -u root -p < 01_create_database.sql -# ... ejecutar en orden +# Desde backend/: tablas, migraciones, admin local y datos ficticios +pnpm setup:local ``` +No ejecutes `MASTER_INSTALL.sql` a mano: borra `hotel_db`, no aplica las +migraciones de `scripts/` y tiene dos fallos del baseline que solo +`setup:local` corrige (ADR-039). + ### Verificación ```bash @@ -142,7 +140,7 @@ WHERE status = 'completed' ## 🆘 Troubleshooting -Ver [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) para solución de problemas comunes. +Una instalación que falla: vuelve a lanzar `pnpm setup:local -- --force` (ver «Instalación completa»). --- @@ -156,7 +154,7 @@ Ver [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md) para solución de problemas comunes ## 🔗 Enlaces Útiles -- [Guía de Migración](MIGRATION_GUIDE.md) +- [Política de migraciones](MIGRATIONS_POLICY.md) - [Índice de Tablas](INDEX.md) - [Script de Verificación](aiven/99_verification.sql) diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 0711f117..e3261d3a 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -842,3 +842,31 @@ cero, se corrigen en `setup:local` y con `20261005_complete_fresh_install.sql` sin tocar `aiven/` (ADR-021). - **Revisa:** ADR-015 en la BD local. + +## ADR-040 — La documentación obsoleta se archiva fuera del repositorio + +- **Estado:** ✅ aceptada (2026-10-05) +- **Fecha:** 2026-10-05 +- **Decisión:** un documento que ya no describe el proyecto se mueve a + `docs/_archive/` (en `.gitignore`: solo en el equipo del + propietario) y se borra del repositorio. Se apunta aquí con el último commit + que lo contiene: `git show :` lo recupera. +- **Archivados el 2026-10-05** (último commit: `da7c643`): + + | Ruta | Por qué | + | --- | --- | + | `backend/backend.md` | Duplicaba `backend/README.md` en inglés; decía que el backend era privado y describía la demo antigua (lista blanca de rutas) | + | `backend/README.md` | 442 líneas de arquitectura repetidas en `docs/`; sustituido por un README corto | + | `frontend/README.md` | 877 líneas con Next 14 / React 18 y la demo antigua; sustituido por un README corto | + | `backend/db-mysql/MIGRATION_GUIDE.md` | Mandaba instalar con `MASTER_INSTALL.sql` a mano, que falla desde cero; lo sustituye `pnpm setup:local` (ADR-039) | + | `frontend/messages/cmd.md` | Una línea con un comando de búsqueda | + | `frontend/app/dashboard/conciliation/nota.md` | Idea sin hacer: una descripción obligatoria en cada entrada de la conciliación (habitaciones OOO que restan) | + +- **Motivo:** el repositorio va a ser público; un documento con datos falsos + confunde más que su ausencia, y los README largos repetían `docs/` sin + mantenerse. +- **Rechazado:** actualizarlos en el sitio. Era más trabajo y seguían + duplicando `docs/general`. +- **Consecuencias:** los documentos del proceso de publicación (`GITCLEAN.md`, + `VERIFY.md`, `ROADMAP.md`, `SESSION.md`) se archivan igual cuando el + repositorio sea público. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index dadc5132..84448f06 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -55,17 +55,19 @@ Historial de `main` y `claude/compassionate-planck-gh6aof` reescrito y auditado ## Fase 4 — Publicación ← **actual** El historial limpio está en `bpstack/four-points`, todavía privado; Vercel y -Render despliegan desde él y el original está archivado. Pasar a público (con -secret scanning y push protection) espera a la demo de la fase 5 (ADR-037). La -rotación de credenciales (ADR-007) no bloquea: va en «Después de publicar» de -`TODO.md`. +Render despliegan desde él y el original está archivado. La demo de la fase 5 +ya está en producción, así que solo falta pasarlo a público y activar secret +scanning y push protection. Comprobado el 2026-10-05: ningún valor de +`backend/.env` aparece en los 662 commits de todas las ramas, salvo dos valores +públicos por defecto (el usuario `avnadmin` de Aiven y `localhost:11434` de +Ollama). La rotación de credenciales (ADR-007) no bloquea: va en «Después de +publicar» de `TODO.md`. -## Fase 5 — Demo pública +## Fase 5 — Demo pública ✅ cerrada (2026-10-05) Que cualquiera pruebe la aplicación en la propia web, que es solo un escaparate (ADR-037): un admin demo restringido que entra con un botón, datos ficticios, generación de horarios y reinicio diario; y quien quiera verlo todo lo instala -en local con admin completo (ADR-038, ADR-039). Hecho en código y probado en -preview el 2026-10-05; lo que falta, en `TODO.md`, «Demo pública en la propia -web: lo que falta». El repositorio se hace público cuando la demo esté en -producción. +en local con admin completo (ADR-038, ADR-039). En producción desde el +2026-10-05 (PR #3 y #4, `DEMO_MODE` en Render y Vercel), probada en preview y +en la web. diff --git a/docs/SESSION.md b/docs/SESSION.md index cec3f38d..6a4c169b 100644 --- a/docs/SESSION.md +++ b/docs/SESSION.md @@ -21,13 +21,13 @@ y auditado (`GITCLEAN.md`). El historial limpio está en `bpstack/four-points`, **todavía privado**. El original sigue privado como `four-points-archive`. Vercel y Render despliegan ya desde el nuevo, en producción y en preview (`VERIFY.md`, «Cambio al repositorio -nuevo»). Decisiones en `DECISIONS.md`, hasta ADR-039. +nuevo»). Decisiones en `DECISIONS.md`, hasta ADR-040. -La demo pública (fase 5) está hecha en código y probada en preview el -2026-10-05: cuenta demo con botón, bloqueos, límites, reinicio diario, -Configuración → Demo, instalación local con `pnpm setup:local` y `demo-admin` -retirado. Base de horarios guardada (octubre a diciembre de 2026, generados -con el solver). `DEMO_MODE` y `NEXT_PUBLIC_DEMO_MODE` solo están en preview. +La demo pública (fase 5) está en producción desde el 2026-10-05: cuenta demo +con botón, bloqueos, límites, reinicio diario, Configuración → Demo y base de +horarios (octubre a diciembre de 2026). `pnpm setup:local` instala en local +con admin completo y la misma base de horarios (`scheduling-seed.sql`). La +documentación obsoleta está archivada (ADR-040). ## ⚠️ Empieza por aquí @@ -44,10 +44,11 @@ con el solver). `DEMO_MODE` y `NEXT_PUBLIC_DEMO_MODE` solo están en preview. ## Siguiente -1. PR a `main`, poner `DEMO_MODE` y `NEXT_PUBLIC_DEMO_MODE` en producción y - probar el botón demo en la web (`TODO.md`, «Demo pública en la propia web: - lo que falta»). -2. Pasar `four-points` a público y activar secret scanning y push protection. +1. Pasar `four-points` a público y activar secret scanning y push protection. + Historial revisado contra `backend/.env` el 2026-10-05 (`ROADMAP.md`, + fase 4). +2. Archivar `GITCLEAN.md`, `VERIFY.md`, `ROADMAP.md` y `SESSION.md` como + dice ADR-040, cuando ya no hagan falta. 3. «Después de publicar» de `TODO.md`, empezando por la rotación de credenciales (se puede adelantar). @@ -75,7 +76,6 @@ Nada ahora mismo. se bloquea en la ruta con `denyDemo`/`denyDemoWrites` o en `demoRestriction`. Una ruta nueva que escriba fuera de lo que reinicia el mock (usuarios, catálogos, configuración) necesita su guarda. -- **Enlaces rotos conocidos** tras archivar: `backend/README.md` (enlaces a - `docs/backend/…`) y comentarios que citan `Global-Plan.md` o +- **Enlaces rotos conocidos** tras archivar: comentarios que citan `Global-Plan.md` o `SCHEDULING-SOLVER-PLAN.md`. Se arreglan al escribir cada módulo (ADR-010). _Sin repasar desde el 2026-09-28._ diff --git a/docs/TODO.md b/docs/TODO.md index 603982dd..ba4bab6e 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -18,8 +18,9 @@ hace que alguien reimplemente lo que ya existe. ## Rama `claude/compassionate-planck-gh6aof` -Fusionada en `main` el 2026-10-05 (PR #10, commit de merge `0953e94`). La rama -sigue siendo donde se arregla este fichero y se prueba en el entorno preview. +Última fusión en `main`: 2026-10-05 (PR #4, commit de merge `15a8531`). La +rama sigue siendo donde se arregla este fichero y se prueba en el entorno +preview. ## Después de publicar @@ -309,15 +310,6 @@ sigue siendo donde se arregla este fichero y se prueba en el entorno preview. pestaña muestra las rechazadas. Una factura incorrecta hoy se borra. El dueño decidió el 2026-10-04 no añadirlo porque casi nunca pasa; si se añade, hace falta también reabrir (`rejected` → `pending`). -- [ ] **Demo pública en la propia web: lo que falta** (fase 5 de `ROADMAP.md`, - ADR-037 y ADR-038) — el código está hecho y probado en preview el - 2026-10-05 (cuenta demo, bloqueos, límites, reinicio diario, pestaña - Configuración → Demo, `setup:local`) y la base de horarios guardada - (octubre a diciembre de 2026, generados con el solver; los meses que - venían de bajas reales se borraron). Queda: - 1. **Producción:** tras el merge, `DEMO_MODE=true` en el servicio de - producción de Render y `NEXT_PUBLIC_DEMO_MODE=true` en Production de - Vercel (hoy solo están en preview). Probar el botón en la web. - [ ] **Conciliation: `GET /api/conciliations` sin paginar ni filtrar** — devuelve todo el histórico a cualquier rol con acceso. _Según la revisión `security` L3 del 2026-09-28 (fichero y línea en el informe); no repasado diff --git a/docs/general/database/README.md b/docs/general/database/README.md index 338d5b85..e566e8c5 100644 --- a/docs/general/database/README.md +++ b/docs/general/database/README.md @@ -100,6 +100,6 @@ backend (repositories) ─► mysql2 pool ─► TLS ─► MySQL on Aiven (hote quick query scripts were removed on 2026-10-04 (they carried the Aiven password). - **`backup/`**: database dumps, never committed. -- **Five documents** that overlap: `CLAUDE.md`, `README.md`, `INDEX.md`, - `MIGRATIONS_POLICY.md` and `MIGRATION_GUIDE.md`. Several data they give are - outdated. +- **Four documents** that overlap: `CLAUDE.md`, `README.md`, `INDEX.md` and + `MIGRATIONS_POLICY.md`. Several data they give are outdated. + `MIGRATION_GUIDE.md` was archived on 2026-10-05 (ADR-040). diff --git a/frontend/README.md b/frontend/README.md index cbbff8f9..61e9dc9b 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,877 +1,25 @@ -# Four Points - Frontend Architecture +# Four-Points — Frontend -> Hotel Property Management System - Frontend Application +Next.js 16 (App Router) + React 19, Tailwind CSS and NextUI, in Spanish and +English (next-intl). It talks to the backend API with HttpOnly cookies. To +install and run the whole project, see the [root README](../README.md). -**[Live Demo](https://four-points.stackbp.es)** +## Layout -![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=flat&logo=typescript&logoColor=white) -![Next.js](https://img.shields.io/badge/Next.js_14-black?style=flat&logo=next.js&logoColor=white) -![React](https://img.shields.io/badge/React_18-61DAFB?style=flat&logo=react&logoColor=black) -![TailwindCSS](https://img.shields.io/badge/Tailwind_CSS-38B2AC?style=flat&logo=tailwind-css&logoColor=white) -![License](https://img.shields.io/badge/License-MIT-green.svg) +| Folder | What it holds | +| -------------------- | --------------------------------------------------- | +| `app/dashboard/` | One route per module | +| `app/components/` | Module components (some with their own `AGENTS.md`) | +| `app/lib/` | API client, per-module API calls, helpers, types | +| `messages/{es,en}/` | Translations, one file per module | +| `content/checklist/` | Shift guides and references, in Markdown | -A comprehensive, full-stack **Hotel Management System** designed for real-world hotel operations. Features a modern, responsive dashboard with role-based access control, multi-language support (EN/ES), and dark/light mode theming. +## Commands ---- +| Command | Does | +| ---------------------------------------------------- | ----------------------------------------------------- | +| `pnpm dev` | http://localhost:3000, API from `NEXT_PUBLIC_API_URL` | +| `pnpm build` · `pnpm start` | Production build | +| `pnpm typecheck` · `pnpm lint` · `pnpm format:check` | What CI runs | -## Table of Contents - -1. [Tech Stack](#1-tech-stack) -2. [Project Structure](#2-project-structure) -3. [Key Components](#3-key-components) -4. [API Client](#4-api-client) -5. [State Management](#5-state-management) -6. [Internationalization](#6-internationalization) -7. [Features & Modules](#7-features--modules) -8. [Data Export](#8-data-export) -9. [Authentication](#9-authentication) -10. [Demo Mode](#10-demo-mode) -11. [Theme System](#11-theme-system) -12. [Getting Started](#12-getting-started) -13. [Scripts](#13-scripts) -14. [Related Documentation](#14-related-documentation) - ---- - -## 1. Tech Stack - -### Core Technologies - -| Technology | Version | Purpose | -| --------------- | ------- | ---------------------------------------- | -| **Next.js** | 14 | React framework (App Router + Turbopack) | -| **React** | 19 | UI library (latest) | -| **TypeScript** | 5.7.3 | Type-safe development | -| **TailwindCSS** | 3.4.17 | Utility-first styling | -| **NextUI** | 2.6.11 | Component library (Tailwind-based) | -| **HeadlessUI** | 2.2.9 | Unstyled accessible components | -| **Heroicons** | 2.2.0 | SVG icon library | - -### State & Data Management - -| Technology | Version | Purpose | -| ------------------- | ------- | --------------------------------- | -| **Zustand** | 5.0.8 | Lightweight state management | -| **TanStack Query** | 5.90.11 | Server state, caching & mutations | -| **React Hook Form** | 7.66.0 | Form handling | -| **Zod** | 3.25.17 | Schema validation | - -### UI & UX - -| Technology | Version | Purpose | -| -------------------- | -------- | ------------------------------- | -| **Framer Motion** | 12.23.22 | Smooth animations & transitions | -| **React Hot Toast** | 2.6.0 | Toast notifications | -| **React Day Picker** | 9.11.1 | Date selection components | -| **Recharts** | 3.5.1 | Data visualization & charts | - -### Internationalization & Themes - -| Technology | Version | Purpose | -| --------------- | ------- | ------------------------------------- | -| **next-intl** | 4.6.1 | Internationalization (EN/ES) | -| **next-themes** | 0.4.6 | Dark/Light mode with system detection | - -### Documents & Export - -| Technology | Version | Purpose | -| ------------------ | ------- | ------------------------------ | -| **xlsx (SheetJS)** | 0.18.5 | Excel file generation & export | -| **pdf-lib** | 1.17.1 | PDF document generation | -| **pdfjs-dist** | 5.4.449 | PDF viewing & rendering | - -### Utilities - -| Technology | Version | Purpose | -| ------------------ | ------- | ---------------------------- | -| **date-fns** | 4.1.0 | Date manipulation utilities | -| **clsx** | 2.1.1 | Conditional class utilities | -| **tailwind-merge** | 3.3.1 | Tailwind class merging | -| **use-debounce** | 10.0.4 | Input debouncing | -| **uuid** | 13.0.0 | Unique ID generation | -| **js-cookie** | 3.0.5 | Cookie management | -| **react-icons** | 5.5.0 | Icon library (multiple sets) | - -### Analytics & SEO - -| Technology | Version | Purpose | -| --------------------- | ------- | ---------------- | -| **@vercel/analytics** | 1.6.1 | Vercel Analytics | - -### Authentication - -Authentication is handled entirely by the **backend** via JWT tokens in HttpOnly cookies. The frontend has no client-side password hashing and no NextAuth. - -### Dev Dependencies - -| Tool | Version | Purpose | -| -------------- | ------- | ---------------------- | -| **ESLint** | 8.57.1 | Code linting | -| **Prettier** | 3.6.2 | Code formatting | -| **TypeScript** | 5.7.3 | Type checking | -| **@types/** | Various | TypeScript definitions | - ---- - -## 2. Project Structure - -``` -frontend/ -├── app/ -│ ├── (auth)/ # Authentication pages (login) -│ ├── components/ # React components -│ │ ├── auth/ -│ │ │ └── NewUserModal.tsx -│ │ ├── bo/ -│ │ │ ├── StatsCards.tsx -│ │ │ ├── TabsNavigation.tsx -│ │ │ ├── TabContent.tsx -│ │ │ └── tabs/ -│ │ │ ├── PendingInvoicesTabLazy.tsx -│ │ │ ├── PaidInvoicesTabLazy.tsx -│ │ │ ├── SuppliersTabLazy.tsx -│ │ │ └── SettingsTabLazy.tsx -│ │ ├── booking/ -│ │ │ └── BookingWizard/ -│ │ │ ├── index.tsx -│ │ │ ├── steps/ -│ │ │ │ ├── DateSpotStep.tsx -│ │ │ │ ├── VehicleStep.tsx -│ │ │ │ └── ConfirmationStep.tsx -│ │ │ └── types.ts -│ │ ├── cashier/ -│ │ │ ├── CashierCalendarNav.tsx -│ │ │ ├── CloseDayModal.tsx -│ │ │ ├── CloseShiftModal.tsx -│ │ │ ├── CreateVoucherModal.tsx -│ │ │ ├── DateNavigator.tsx -│ │ │ ├── DenominationForm.tsx -│ │ │ ├── ShiftTabs.tsx -│ │ │ ├── ShiftUsersManager.tsx -│ │ │ └── VoucherList.tsx -│ │ ├── conciliation/ -│ │ │ ├── ActionButtons.tsx -│ │ │ ├── ConciliationClient.tsx -│ │ │ ├── ConciliationForm.tsx -│ │ │ ├── ConciliationTable.tsx -│ │ │ ├── DaySummary.tsx -│ │ │ ├── NotePopover.tsx -│ │ │ └── RoomPopover.tsx -│ │ ├── dashboard/ -│ │ │ ├── DashboardHeader.tsx -│ │ │ ├── DashboardSkeleton.tsx -│ │ │ ├── GlobalStatusGrid.tsx -│ │ │ ├── ImportantLogbooksCard.tsx -│ │ │ ├── QuickActionsCard.tsx -│ │ │ └── RecentActivityCard.tsx -│ │ ├── groups/ -│ │ │ ├── GroupDetailClient.tsx -│ │ │ ├── GroupsListClient.tsx -│ │ │ └── tabs/ -│ │ │ ├── OverviewTab.tsx -│ │ │ ├── ContactsTab.tsx -│ │ │ ├── RoomsTab.tsx -│ │ │ ├── PaymentsTab.tsx -│ │ │ └── HistoryTab.tsx -│ │ ├── layout/ -│ │ │ ├── LanguageSwitcher.tsx -│ │ │ └── ProfileDropdown.tsx -│ │ ├── maintenance/ -│ │ │ └── ReportDetailClient.tsx -│ │ ├── notifications/ -│ │ │ ├── items/ -│ │ │ │ └── NotificationItem.tsx -│ │ │ └── lists/ -│ │ │ └── NotificationsList.tsx -│ │ ├── parking/ -│ │ │ ├── StatusBadge.tsx -│ │ │ └── VehicleSearchModal.tsx -│ │ ├── search/ -│ │ │ ├── GlobalSearch.tsx -│ │ │ └── MobileSearchModal.tsx -│ │ └── theme/ -│ │ └── SetThemeButton.tsx -│ ├── dashboard/ # Dashboard pages -│ │ ├── parking/ # Parking management -│ │ │ ├── page.tsx # Main parking page -│ │ │ ├── status/ # Parking status view -│ │ │ │ ├── page.tsx -│ │ │ │ ├── layout.tsx -│ │ │ │ ├── components/ -│ │ │ │ │ ├── ParkingTable.tsx -│ │ │ │ │ ├── ParkingNavigator.tsx -│ │ │ │ │ ├── StatusPanels.tsx -│ │ │ │ │ └── modals/ -│ │ │ │ │ ├── CheckInModal.tsx -│ │ │ │ │ ├── CheckOutModal.tsx -│ │ │ │ │ ├── OverdueModal.tsx -│ │ │ │ │ └── CancelModal.tsx -│ │ │ │ └── utils/ -│ │ │ │ └── statusBadges.tsx -│ │ │ ├── bookings/ # Booking management -│ │ │ │ ├── page.tsx -│ │ │ │ └── [code]/ -│ │ │ │ └── page.tsx -│ │ │ ├── components/ -│ │ │ │ └── ParkingDashboardClient.tsx -│ │ │ └── actions/ -│ │ │ └── getParkingDashboardStats.ts -│ │ ├── logbooks/ # Logbooks (bitácoras) -│ │ │ ├── page.tsx -│ │ │ ├── loading.tsx -│ │ │ └── error.tsx -│ │ ├── groups/ # Groups (reservas grupales) -│ │ │ ├── page.tsx -│ │ │ ├── loading.tsx -│ │ │ ├── error.tsx -│ │ │ ├── [id]/ -│ │ │ │ └── page.tsx -│ │ │ └── actions/ -│ │ │ └── getGroups.ts -│ │ ├── cashier/ # Cashier (caja) -│ │ │ ├── page.tsx -│ │ │ ├── hotel/ -│ │ │ │ ├── layout.tsx -│ │ │ │ └── page.tsx -│ │ │ ├── logs/ # Cashier logs -│ │ │ ├── reports/ # Cashier reports -│ │ │ ├── loading.tsx -│ │ │ └── error.tsx -│ │ ├── maintenance/ # Maintenance (mantenimiento) -│ │ │ ├── page.tsx -│ │ │ ├── loading.tsx -│ │ │ ├── error.tsx -│ │ │ ├── [id]/ -│ │ │ │ └── page.tsx -│ │ │ └── actions/ -│ │ │ ├── createMaintenance.ts -│ │ │ ├── getMaintenance.ts -│ │ │ └── updateMaintenanceStatus.ts -│ │ ├── blacklist/ # Blacklist (lista negra) -│ │ │ ├── page.tsx -│ │ │ ├── new/ -│ │ │ │ └── page.tsx -│ │ │ ├── [id]/ -│ │ │ │ ├── page.tsx -│ │ │ │ └── edit/ -│ │ │ │ └── page.tsx -│ │ │ └── actions/ -│ │ │ ├── createBlacklist.ts -│ │ │ └── getBlacklist.ts -│ │ ├── conciliation/ # Conciliation (conciliación) -│ │ │ ├── page.tsx -│ │ │ ├── loading.tsx -│ │ │ └── error.tsx -│ │ ├── bo/ # Backoffice (facturas) -│ │ │ ├── page.tsx -│ │ │ ├── loading.tsx -│ │ │ └── error.tsx -│ │ ├── profile/ # User profile -│ │ │ ├── page.tsx -│ │ │ ├── loading.tsx -│ │ │ └── error.tsx -│ │ ├── layout.tsx # Dashboard layout -│ │ ├── page.tsx # Dashboard home -│ │ └── loading.tsx -│ ├── i18n/ # Internationalization config -│ │ └── config.ts -│ ├── lib/ # Utilities & hooks -│ │ ├── activity/ -│ │ │ ├── index.ts -│ │ │ ├── queries.ts -│ │ │ └── types.ts -│ │ ├── auth/ -│ │ │ ├── authService.ts -│ │ │ └── useAuth.tsx -│ │ ├── apiClient.ts # Centralized API client (490 líneas) -│ │ ├── backoffice/ -│ │ │ ├── backofficeApi.ts -│ │ │ ├── data.ts -│ │ │ ├── export-utils.ts -│ │ │ └── types.ts -│ │ ├── blacklist/ -│ │ │ ├── blacklistApi.ts -│ │ │ ├── blacklistSchema.ts -│ │ │ └── types.ts -│ │ ├── cashier/ -│ │ │ ├── queries.ts -│ │ │ ├── types.ts -│ │ │ └── exportDailyPdf.ts -│ │ ├── conciliation/ -│ │ │ ├── config.ts -│ │ │ ├── queries.ts -│ │ │ └── types.ts -│ │ ├── departments/ -│ │ │ ├── queries.ts -│ │ │ └── types.ts -│ │ ├── groups/ -│ │ │ ├── queries.ts -│ │ │ ├── types.ts -│ │ │ └── schemas/ -│ │ │ └── group-schemas.ts -│ │ ├── helpers/ -│ │ │ ├── date.ts -│ │ │ └── utils.ts -│ │ ├── logbooks/ -│ │ │ ├── hooks/ -│ │ │ │ ├── useDepartments.ts -│ │ │ │ └── useLogbooks.ts -│ │ │ ├── queries.ts -│ │ │ └── types.ts -│ │ ├── maintenance/ -│ │ │ ├── maintenanceApi.ts -│ │ │ ├── maintenance-schemas.ts -│ │ │ └── maintenance.ts -│ │ ├── messaging/ -│ │ │ ├── hooks/ -│ │ │ │ ├── index.ts -│ │ │ │ ├── useChat.ts -│ │ │ │ └── useConversations.ts -│ │ │ ├── queries.ts -│ │ │ └── types.ts -│ │ ├── notifications/ -│ │ │ ├── queries.ts -│ │ │ ├── types.ts -│ │ │ └── useNotifications.ts -│ │ ├── parking/ -│ │ │ ├── queries.ts -│ │ │ ├── types.ts -│ │ │ └── actions.ts -│ │ ├── theme/ -│ │ │ └── ThemeProvider.tsx -│ │ ├── users/ -│ │ │ ├── queries.ts -│ │ │ └── types.ts -│ │ └── env.ts # Environment variables -│ ├── stores/ # Zustand stores -│ │ ├── useCashierStore.ts -│ │ ├── useGroupStore.ts -│ │ └── useMaintenanceStore.ts -│ ├── ui/ # Base UI components -│ │ ├── calendar/ -│ │ │ ├── DatePickerInput.tsx -│ │ │ ├── HorizontalDatePicker.tsx -│ │ │ ├── SimpleCalendarCompact.tsx -│ │ │ ├── simplecalendar.tsx -│ │ │ └── timepicker.tsx -│ │ ├── dashboard/ -│ │ │ ├── nav-links.tsx -│ │ │ └── sidenav.tsx -│ │ ├── errors/ -│ │ │ └── ModuleError.tsx -│ │ ├── fonts-design/ -│ │ │ ├── design-system.ts -│ │ │ ├── fonts.helper.ts -│ │ │ └── fonts.ts -│ │ ├── panels/ -│ │ │ ├── CenterModal.tsx -│ │ │ ├── SlidePanel.tsx -│ │ │ ├── SlidePanelFooterWithDelete.tsx -│ │ │ └── index.ts -│ │ ├── global.css -│ │ └── skeletons.tsx -│ ├── layout.tsx # Root layout (118 líneas) -│ └── page.tsx # Home page -├── messages/ # Translations -│ ├── en/ # English (19 files) -│ │ ├── auth.json -│ │ ├── cashier.json -│ │ ├── common.json -│ │ ├── conciliation.json -│ │ ├── dashboard.json -│ │ ├── errors.json -│ │ ├── groups.json -│ │ ├── logbook.json -│ │ ├── maintenance.json -│ │ ├── messages.json -│ │ ├── notifications.json -│ │ ├── parking.json -│ │ ├── profile.json -│ │ └── ... -│ └── es/ # Spanish (19 files) -│ ├── auth.json -│ ├── backoffice.json -│ ├── blacklist.json -│ ├── cashier.json -│ ├── common.json -│ ├── conciliation.json -│ ├── dashboard.json -│ ├── errors.json -│ ├── groups.json -│ └── ... -├── public/ -│ ├── screenshots/ # App screenshots -│ ├── icons/ # Favicons -│ └── manifest.json # PWA manifest -├── .env.example -├── .eslintrc.json -├── .prettierrc -├── next.config.ts -├── tailwind.config.ts -├── tsconfig.json -└── package.json -``` - ---- - -## 3. Key Components - -### Dashboard Components - -| Component | File | Purpose | -| ----------------------- | ----------------------------------- | ------------------------------ | -| `DashboardHeader` | dashboard/DashboardHeader.tsx | Header with search and profile | -| `GlobalStatusGrid` | dashboard/GlobalStatusGrid.tsx | KPI cards grid | -| `QuickActionsCard` | dashboard/QuickActionsCard.tsx | Quick action buttons | -| `RecentActivityCard` | dashboard/RecentActivityCard.tsx | Activity feed | -| `ImportantLogbooksCard` | dashboard/ImportantLogbooksCard.tsx | Critical logbooks | - -### Parking Components - -| Component | File | Purpose | -| ------------------------ | ------------------------------------ | ---------------------- | -| `ParkingDashboardClient` | dashboard/parking/components/ | Main parking dashboard | -| `ParkingStatusClient` | dashboard/parking/status/components/ | Status view | -| `ParkingTable` | dashboard/parking/status/components/ | Spaces table | -| `CheckInModal` | modals/ | Check-in modal | -| `CheckOutModal` | modals/ | Check-out modal | -| `VehicleSearchModal` | VehicleSearchModal.tsx | Search vehicle | - -### Cashier Components - -| Component | File | Purpose | -| ------------------- | --------------------- | --------------------- | -| `CloseShiftModal` | CloseShiftModal.tsx | Close shift dialog | -| `DenominationForm` | DenominationForm.tsx | Currency input | -| `ShiftTabs` | ShiftTabs.tsx | Shift navigation | -| `ShiftUsersManager` | ShiftUsersManager.tsx | User shift assignment | - -### Group Components - -| Component | File | Purpose | -| ------------------- | ---------------------------- | ----------------- | -| `GroupDetailClient` | groups/GroupDetailClient.tsx | Group detail view | -| `GroupsListClient` | groups/GroupsListClient.tsx | Groups list | -| `ContactCard` | groups/cards/ | Contact info | -| `PaymentCard` | groups/cards/ | Payment tracking | -| `RoomCard` | groups/cards/ | Room assignment | - -### UI Components - -| Component | File | Purpose | -| ---------------------- | ---------------------------- | ----------------- | -| `SlidePanel` | panels/SlidePanel.tsx | Slide-out panel | -| `CenterModal` | panels/CenterModal.tsx | Modal dialog | -| `DatePickerInput` | calendar/DatePickerInput.tsx | Date selection | -| `HorizontalDatePicker` | calendar/ | Date range picker | -| `ModuleError` | errors/ModuleError.tsx | Error boundary | -| `SkeletonLoader` | skeletons.tsx | Loading states | - ---- - -## 4. API Client - -**File:** `app/lib/apiClient.ts` (490 líneas) - -### Features - -The API client provides centralized HTTP handling with advanced features: - -```typescript -interface FetchOptions extends RequestInit { - skipRefresh?: boolean -} - -// Key features: -- Auto-refresh JWT on 401 responses -- Concurrent refresh handling with queue -- Circuit breaker (max 3 refresh attempts) -- Credentials always included (cookies) -- FormData support for file uploads -- Blob support for file downloads -- Error handling with i18n codes -- Toast notifications for demo mode -``` - -### Auto-Refresh Flow - -```typescript -1. Request → API -2. If 401: - - Check if already refreshing - - If yes, queue request - - If no, attempt refresh - - Retry original request -3. If refresh fails (3 attempts): - - Clear cookies - - Redirect to /login -``` - -### Methods - -| Method | Purpose | -| ----------------------------- | -------------- | -| `apiClient.get()` | GET request | -| `apiClient.post()` | POST request | -| `apiClient.patch()` | PATCH request | -| `apiClient.put()` | PUT request | -| `apiClient.delete()` | DELETE request | -| `apiClient.postFormData()` | File upload | -| `apiClient.getBlob()` | Download file | - -### Error Handling - -```typescript -class ApiError extends Error { - demo: boolean // Is demo mode restriction - status: number // HTTP status - code?: string // Backend error code for i18n -} - -// Demo errors show toast notification -// Regular errors are thrown for handling -``` - ---- - -## 5. State Management - -### Server State (TanStack Query) - -| Module | Hook/Query | Purpose | -| ----------------- | ------------------------ | ------------------ | -| **Activity** | `useActivity()` | Dashboard feed | -| **Parking** | `useParkingSpaces()` | Parking data | -| **Groups** | `useGroups()` | Group reservations | -| **Logbooks** | `useLogbooks()` | Shift notes | -| **Cashier** | `useCashierShifts()` | Shift data | -| **Maintenance** | `useMaintenanceOrders()` | Work orders | -| **Notifications** | `useNotifications()` | Alert system | -| **Backoffice** | `useInvoices()` | Invoice management | - -### Client State (Zustand) - -```typescript -// useCashierStore.ts -interface CashierStore { - selectedShift: Shift | null - selectedDate: Date - denominationMode: boolean - setSelectedShift: (shift: Shift) => void - setSelectedDate: (date: Date) => void - toggleDenominationMode: () => void -} - -// useGroupStore.ts -interface GroupStore { - selectedGroup: Group | null - activeTab: string - filters: GroupFilters - setSelectedGroup: (group: Group) => void - setActiveTab: (tab: string) => void - setFilters: (filters: GroupFilters) => void -} - -// useMaintenanceStore.ts -interface MaintenanceStore { - priorityFilter: string - statusFilter: string - setPriorityFilter: (priority: string) => void - setStatusFilter: (status: string) => void -} -``` - ---- - -## 6. Internationalization - -**Configuration:** `app/i18n/config.ts` - -### Supported Languages - -| Language | Code | Namespace Files | -| ----------- | ---- | --------------- | -| **English** | en | 19 files | -| **Spanish** | es | 19 files | - -### Translation Namespaces - -| Namespace | Module | -| --------------- | ------------------- | -| `auth` | Authentication | -| `cashier` | Cashier module | -| `conciliation` | Conciliation | -| `dashboard` | Dashboard | -| `errors` | Error messages | -| `groups` | Groups/Reservations | -| `logbook` | Logbooks | -| `maintenance` | Maintenance | -| `messages` | Messaging | -| `notifications` | Notifications | -| `parking` | Parking | -| `profile` | User profile | -| `common` | Shared strings | -| `validation` | Form validation | -| `backoffice` | Backoffice | -| `blacklist` | Blacklist | -| `booking` | Booking | -| `restaurant` | Restaurant | - -### Usage - -```typescript -// In components -import { useTranslations } from 'next-intl' - -function MyComponent() { - const t = useTranslations('namespace') - return
{t('key')}
-} - -// In server components -import { getTranslations } from 'next-intl/server' - -export default async function Page() { - const t = await getTranslations('namespace') - return
{t('key')}
-} -``` - ---- - -## 7. Features & Modules - -### Core Modules - -| Module | Description | -| ------------------ | --------------------------------------------------------------------------------------------------------------------- | -| **Authentication** | JWT auth with access/refresh tokens, role-based access (admin, receptionist, maintenance, group-admin), avatar upload | -| **Dashboard** | KPIs, quick actions, activity feed, important alerts | -| **Groups** | Hotel group reservations with contacts, rooms, payments tracking, status workflow, complete audit history | -| **Parking** | Multi-level parking management, bookings with unique codes, check-in/out, rates, analytics | -| **Logbooks** | Digital shift notes with priority levels, comments, read tracking, department organization | -| **Cashier** | Daily cash management with 4 shifts, denomination counting, electronic payments, vouchers, daily reports | -| **Maintenance** | Work order tracking with custom IDs, image uploads, status workflow, assignment to staff/contractors | -| **Blacklist** | Guest incident records with severity levels, document verification, image gallery | -| **Conciliation** | Daily room count reconciliation between Reception and Housekeeping | -| **Backoffice** | Invoice & supplier management with PDF uploads, validation workflow, monthly summaries | -| **Messaging** | Internal communication system with direct messages and group chats | -| **Notifications** | Multi-module alert system with priority levels, scheduled delivery, email integration | - -### Schedule Module - -Staff scheduling system with a CP-SAT solver (Google OR-Tools) for automated monthly schedule generation, real-time validation, constraint management, and PDF export. See `docs/backend/scheduling/README.md`. - -### Special Features - -| Feature | Description | -| ------------------------ | --------------------------------------------------------------- | -| **Internationalization** | Full English and Spanish support with 19 translation namespaces | -| **Dark/Light Mode** | Complete theme support with system preference detection | -| **Role-Based Access** | 4 roles with granular permissions per module | -| **Real-time Updates** | Live notifications, unread counters, activity feeds | -| **Responsive Design** | Mobile-first approach, works on all devices | -| **Demo Mode** | Restricted operations for public deployments | - ---- - -## 8. Data Export - -### Excel Export (xlsx) - -| Module | Export Feature | -| ---------------- | ----------------------------------------------- | -| **Cashier** | Daily shift reports with denomination breakdown | -| **Parking** | Booking history, analytics data | -| **Groups** | Reservation lists, payment summaries | -| **Conciliation** | Monthly room count reports | - -### PDF Generation (pdf-lib) - -| Module | PDF Feature | -| -------------- | ------------------------- | -| **Cashier** | Daily cash reports | -| **Groups** | Reservation confirmations | -| **Backoffice** | Invoice summaries | - -### PDF Viewing (pdfjs-dist) - -- In-app PDF preview for uploaded invoices -- Thumbnail generation for document lists -- Full document viewer with zoom controls - ---- - -## 9. Authentication - -Uses **HttpOnly cookies** for JWT tokens (XSS-protected). No localStorage. - -``` -Login → Access Token (15min) + Refresh Token (7 days) - ↓ -Request fails 401 → Auto-refresh via cookie - ↓ -Refresh expires → Redirect to /login -``` - -### Key Features - -- Automatic token refresh -- Role-based access (admin, receptionist, maintenance, group-admin) -- Centralized `apiClient` with `credentials: 'include'` -- Session management with cookies - -### Auth Hooks - -```typescript -// useAuth.tsx -interface AuthState { - user: User | null - isLoading: boolean - isAuthenticated: boolean - login: (credentials: LoginCredentials) => Promise - logout: () => Promise -} - -// authService.ts -interface LoginCredentials { - username: string - password: string -} - -interface User { - id: string - username: string - role: 'admin' | 'receptionist' | 'maintenance' | 'group-admin' - avatar?: string -} -``` - ---- - -## 10. Demo Mode - -This application includes a **Demo Mode** for safe public deployment. Demo users can **view everything** but have **limited write access**. - -### Allowed Operations - -| Module | Allowed Actions | -| --------------- | ------------------ | -| **Auth** | Logout | -| **Parking** | Create bookings | -| **Logbooks** | Add comments | -| **Maintenance** | Create work orders | - -All other write operations are blocked with a friendly toast message. - ---- - -## 11. Theme System - -**Configuration:** `app/lib/theme/ThemeProvider.tsx` - -### Features - -- Dark/Light mode toggle -- System preference detection -- Persistent storage (localStorage) -- Hydration mismatch prevention -- Tailwind CSS integration - -### Fonts System - -**Files:** - -- `app/ui/fonts-design/fonts.ts` - Font definitions -- `app/ui/fonts-design/fonts.helper.ts` - Font utilities -- `app/ui/fonts-design/design-system.ts` - Design tokens - -```typescript -// Available fonts (configured via CSS variables) ---font - primary // Main font ---font - display // Headings font -``` - -### Responsive Design - -- Mobile-first approach -- Breakpoints: sm (640px), md (768px), lg (1024px), xl (1280px) -- Collapsible sidebar for mobile -- Touch-friendly components - ---- - -## 12. Getting Started - -### Prerequisites - -- Node.js 20+ -- pnpm 9+ - -### Installation - -```bash -# Clone the repository -git clone https://github.com/bpstack/four-points-frontend.git -cd four-points-frontend - -# Install dependencies -pnpm install - -# Copy environment file -cp .env.example .env.local - -# Start development server -pnpm dev -``` - -Open [https://four-points.stackbp.es/](https://four-points.stackbp.es/) to view the application. - -### Environment Variables - -Create a `.env.local` file based on `.env.example`: - -```env -# API Configuration -NEXT_PUBLIC_API_URL=http://localhost:4000 - -# App Configuration -NEXT_PUBLIC_APP_NAME=Four Points -NEXT_PUBLIC_APP_URL=http://localhost:3000 - -# Feature Flags -NEXT_PUBLIC_DEMO_MODE=true -``` - -### Demo Credentials - -> **Note:** The demo user is currently **disabled**. Contact the administrator to enable it for a demo session. - ---- - -## 13. Scripts - -```bash -pnpm dev # Development (Turbopack, port 3000) -pnpm build # Production build -pnpm start # Start production -pnpm lint # ESLint -pnpm format # Prettier -``` - ---- - ---- - -## Author - -**Salvador Pérez** - -- GitHub: [@bpstack](https://github.com/bpstack) -- Email: contact.bpstack@gmail.com - ---- - -

- Made with Next.js, TypeScript, and TailwindCSS -

+The product documentation lives in [`docs/`](../docs/general/README.md). diff --git a/frontend/app/dashboard/conciliation/nota.md b/frontend/app/dashboard/conciliation/nota.md deleted file mode 100644 index e5f24e71..00000000 --- a/frontend/app/dashboard/conciliation/nota.md +++ /dev/null @@ -1,15 +0,0 @@ -### Tengo que implementar una descripción en cada entrada de la conciliación. - -Por ejemplo: Habitaciones que estaban OOO (restan) - -En este caso, las habitaciones marcadas como “OOO” (fuera de orden) deben llevar siempre una descripción, ya que su contabilización implica una resta en el sistema. - -Por ejemplo, una habitación puede no haberse limpiado el día anterior debido a un problema de mantenimiento. Sin embargo, dicho día se registró como “habitación limpiada pendiente” (LS), es decir, se consideró como si ya estuviera realizada. - -Por tanto, no puede incluirse nuevamente dentro del total de habitaciones facturadas de hoy, ya que su limpieza fue contabilizada previamente. - -En la sección “Total de habitaciones realmente limpiadas por Pisos” estas habitaciones sí aparecerán, ya que el departamento de Pisos registra todas las limpiezas efectivamente realizadas. - -Por este motivo, Recepción debe restar estas habitaciones del total indicado por Pisos, para reflejar correctamente la facturación del día actual y evitar duplicidades. - -### La descripción en este caso podría ser: Habitaciones que estaban fuera de orden (OOO) y fueron limpiadas posteriormente. Se restan porque su limpieza ya fue contabilizada en el día anterior. (LS pendientes) diff --git a/frontend/messages/cmd.md b/frontend/messages/cmd.md deleted file mode 100644 index 52ab98a8..00000000 --- a/frontend/messages/cmd.md +++ /dev/null @@ -1 +0,0 @@ -rg "electronic" frontend/messages/