From 411d517ba4cef22e435aff9744d7e4060586aa32 Mon Sep 17 00:00:00 2001 From: Consultoria AS Date: Tue, 9 Jun 2026 16:26:09 -0700 Subject: [PATCH] =?UTF-8?q?docs:=20documentaci=C3=B3n=20extensiva=20del=20?= =?UTF-8?q?proyecto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README.md con guía completa de instalación y arquitectura - docs/BACKEND.md — guía detallada del API - docs/FRONTEND.md — guía del SPA React - docs/DATABASE.md — funciones SQL y patrones de datos - docs/API.md — referencia completa de endpoints REST - docs/DEPLOYMENT.md — guía de despliegue en producción - docs/TROUBLESHOOTING.md — solución de problemas comunes - CHANGELOG.md — historial de cambios - CONTRIBUTING.md — guía de contribución --- CHANGELOG.md | 103 ++++++ CONTRIBUTING.md | 62 ++++ README.md | 529 ++++++++++++++---------------- docs/API.md | 699 ++++++++++++++++++++++++++++++++++++++++ docs/BACKEND.md | 365 +++++++++++++++++++++ docs/DATABASE.md | 355 ++++++++++++++++++++ docs/DEPLOYMENT.md | 262 +++++++++++++++ docs/FRONTEND.md | 431 +++++++++++++++++++++++++ docs/TROUBLESHOOTING.md | 217 +++++++++++++ 9 files changed, 2728 insertions(+), 295 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 docs/API.md create mode 100644 docs/BACKEND.md create mode 100644 docs/DATABASE.md create mode 100644 docs/DEPLOYMENT.md create mode 100644 docs/FRONTEND.md create mode 100644 docs/TROUBLESHOOTING.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..89684a1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,103 @@ +# 📜 Changelog — Sistema de Gestión Hotelera Hacienda San Angel + +> Formato basado en [Keep a Changelog](https://keepachangelog.com/es-ES/1.0.0/). + +--- + +## [1.0.0] - 2026-06-09 + +### Agregado +- Documentación técnica completa del sistema (`DOCUMENTACION_TECNICA.md`). +- README principal con guía de instalación y arquitectura. +- Documentación extendida en carpeta `docs/`: + - `BACKEND.md` — Guía detallada de la API Node.js/Express. + - `FRONTEND.md` — Guía del SPA React + Vite. + - `DATABASE.md` — Documentación de funciones SQL de PostgreSQL. + - `API.md` — Referencia completa de endpoints REST. + - `DEPLOYMENT.md` — Guía de despliegue en producción. + - `TROUBLESHOOTING.md` — Solución de problemas comunes. +- Commit de consolidación con todo el código base del proyecto. +- Repositorio migrado a Gitea (`https://git.consultoria-as.com`). + +### Notas Técnicas +- El proyecto utiliza arquitectura "Database-First" donde la lógica de negocio reside en funciones SQL de PostgreSQL. +- Backend: Node.js + Express 5 + pg (sin ORM). +- Frontend: React 19 + Vite + React Router DOM 7. +- Integraciones: Stripe, Banxico, API de Facturas (México), Nodemailer. + +--- + +## [Pre-1.0] — Historial de Desarrollo + +### Funcionalidades Implementadas + +#### Autenticación +- Login con validación mediante función SQL `validarusuario()`. +- Recuperación de contraseña con generación aleatoria y envío por correo. +- Creación de usuarios con roles numéricos. + +#### Nómina (Payroll) +- CRUD de empleados con RFC, NSS, CURP, contactos de emergencia. +- Gestión de contratos laborales con fechas de vencimiento. +- Catálogos: puestos, áreas, grados de estudio, parentescos. +- Reporte de asistencia. + +#### Gastos (Expenses) +- Creación de gastos con productos asociados (JSONB). +- Flujo de aprobación: Pendiente → Aprobado → Rechazado. +- Flujo de pago con estados separados de aprobación. +- Gastos mensuales recurrentes. +- Reportes de gastos y pagos. + +#### Inventario +- Catálogo de productos con categorías, tipos, proveedores. +- Entradas de compra (`purchaseentry`) con recepción física. +- Ajustes de stock masivos via JSONB. +- Consumos de inventario por empleado. +- Descarte de productos con motivo. +- Reporte de movimientos de inventario. + +#### Ingresos +- Carga masiva desde Little Hotelier (CSV/XLSX). +- Sincronización de facturas electrónicas via API externa. +- Integración con Stripe para transfers. +- Ingresos manuales (Horux) con categorías. +- Reportes P&L para Hotel y Restaurante. + +#### Configuraciones +- Gestión de habitaciones y propiedades. +- Catálogos maestros: monedas, unidades, categorías de gasto, recurrencias. + +#### Emails Programáticos +- Envío controlado de emails (1x/mes) para: + - Contratos por vencer + - Pagos atrasados + - Gastos próximos a fecha límite + - Cumpleaños de empleados + - Contratos vencidos + +--- + +## Problemas Conocidos (Deuda Técnica) + +### Críticos +- Sin autenticación JWT ni middleware de autorización en el backend. +- Ruta malformada: `updatesupplier/:id` falta `/` inicial. +- `useAuth` no exportado desde `AuthContext.jsx`. +- URLs hardcodeadas a `localhost` en varios servicios del frontend. + +### Medios +- Sin `ProtectedRoute` activo en `App.jsx`. +- Mezcla inconsistente de `fetch` y `axios`. +- Código comentado masivo en varios archivos. +- `multer` incluido en dependencias del frontend (es un middleware de Node.js). +- Sin tests unitarios ni e2e. + +### Leves +- Tailwind CSS instalado pero usado esporádicamente junto con Bootstrap y CSS puro. +- Sin manejo de estado de servidor (TanStack Query/SWR). +- Sin TypeScript. + +--- + +> Para ver el historial completo de commits, ejecutar: `git log --oneline --all --graph` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..7240698 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# 🤝 Guía de Contribución + +## Cómo Trabajar en Este Proyecto + +### 1. Flujo de Trabajo con Git + +```bash +# Clonar el repositorio +git clone https://git.consultoria-as.com/consultoria-as/hotel-hacienda.git + +# Crear una rama para tu cambio +git checkout -b feature/nombre-de-la-funcionalidad + +# Hacer commit con mensajes descriptivos +git add . +git commit -m "feat: descripción clara del cambio" + +# Subir y crear Pull Request +git push origin feature/nombre-de-la-funcionalidad +``` + +### 2. Convención de Commits + +| Prefijo | Uso | +|---------|-----| +| `feat:` | Nueva funcionalidad | +| `fix:` | Corrección de bug | +| `docs:` | Cambios en documentación | +| `style:` | Cambios de formato (espacios, comas) | +| `refactor:` | Refactorización de código | +| `test:` | Agregar o corregir tests | +| `chore:` | Tareas de mantenimiento | + +### 3. Antes de Modificar Cualquier Cosa + +1. **Lee `DOCUMENTACION_TECNICA.md`** — entiende la arquitectura. +2. **Si modificas lógica de negocio:** revisa la función SQL correspondiente en PostgreSQL. +3. **Si agregas un endpoint:** sigue el patrón `route → controller → función SQL`. +4. **Si agregas una página:** actualiza `menuconfig.js`, `App.jsx` y `Layout2.jsx`. + +### 4. Reglas de Oro + +- No hardcodees `localhost` en el frontend. Usa `import.meta.env.VITE_API_BASE_URL`. +- Usa queries parametrizadas (`$1`, `$2`) en PostgreSQL para evitar SQL Injection. +- Si cambias una función SQL, busca TODOS los controllers que la usen. +- Mantén la consistencia en la respuesta JSON: `{ message, data, status }`. +- Agrega traducciones EN/ES para todo texto visible. + +### 5. Testing + +Actualmente no hay tests. Si agregas funcionalidad crítica, considera: +- Probar la función SQL directamente en PostgreSQL. +- Probar el endpoint con `curl` o Postman. +- Probar el flujo completo en el navegador. + +### 6. Documentación + +Si tu cambio afecta: +- **Endpoints:** actualiza `docs/API.md` +- **Base de datos:** actualiza `docs/DATABASE.md` +- **Despliegue:** actualiza `docs/DEPLOYMENT.md` +- **Arquitectura:** actualiza `DOCUMENTACION_TECNICA.md` diff --git a/README.md b/README.md index 1a503ac..cca3339 100644 --- a/README.md +++ b/README.md @@ -1,362 +1,301 @@ -# Hacienda San Angel - Sistema de Gestion Hotelera +# 🏨 Hacienda San Angel — Sistema de Gestión Hotelera -Sistema integral de gestion para el hotel Hacienda San Angel. Incluye modulos de administracion de empleados, nomina, inventario, gastos, ingresos, y reporteria. +> **Versión:** 1.0.0 +> **Entorno:** Node.js + PostgreSQL + React 19 +> **Repositorio:** `https://git.consultoria-as.com/consultoria-as/hotel-hacienda` -## Documentacion +--- -- [Arquitectura y Diagramas de Base de Datos](docs/ARQUITECTURA.md) - Diagramas ERD, arquitectura del sistema y flujo de datos +## 📋 Índice -## Tecnologias +1. [Descripción del Proyecto](#descripción-del-proyecto) +2. [Arquitectura General](#arquitectura-general) +3. [Estructura del Repositorio](#estructura-del-repositorio) +4. [Tecnologías](#tecnologías) +5. [Instalación y Configuración](#instalación-y-configuración) +6. [Scripts Disponibles](#scripts-disponibles) +7. [Documentación Adicional](#documentación-adicional) +8. [Integraciones Externas](#integraciones-externas) +9. [Seguridad y Autenticación](#seguridad-y-autenticación) +10. [Créditos](#créditos) + +--- + +## Descripción del Proyecto + +Sistema administrativo integral para la gestión financiera, operativa y de recursos humanos del hotel **Hacienda San Angel**. El sistema gestiona: + +- **Ingresos:** Little Hotelier, Stripe, Facturas electrónicas (Horux), análisis P&L +- **Gastos:** Aprobaciones, pagos, reportes, pagos mensuales recurrentes +- **Nómina:** Empleados, contratos, asistencia, puestos, áreas +- **Inventario:** Productos, proveedores, ajustes de stock, consumos, descartes +- **Configuraciones:** Habitaciones, propiedades, catálogos maestros +- **Reportes:** Dashboards, análisis de habitaciones, análisis de restaurante + +--- + +## Arquitectura General + +``` +┌─────────────────────────────────────────────────────────────┐ +│ CLIENTE (Browser) │ +│ React 19 + Vite + React Router │ +│ Puerto: 5172 (dev) │ +└──────────────────────┬──────────────────────────────────────┘ + │ HTTP / JSON + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ SERVIDOR API (Node.js) │ +│ Express 5 + CORS + Nodemailer │ +│ Puerto: 3000-4000 │ +│ │ +│ Endpoints: /api/auth, /api/employees, /api/expenses, │ +│ /api/products, /api/incomes, /api/incomeshrx, etc. │ +└──────────────────────┬──────────────────────────────────────┘ + │ SQL (funciones PostgreSQL) + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ BASE DE DATOS (PostgreSQL) │ +│ │ +│ Lógica de negocio implementada en funciones SQL: │ +│ newexpensev2(), purchaseentry(), setpaymentstatusv2(), │ +│ getincomehorux(), addstripedatav2(), etc. │ +└─────────────────────────────────────────────────────────────┘ +``` + +**Patrón arquitectónico:** MVC simplificado donde los **Controllers** solo orquestan llamadas a funciones PostgreSQL. La lógica de negocio reside principalmente en la base de datos. + +--- + +## Estructura del Repositorio + +``` +hotel-hacienda/ +├── backend/ +│ └── hotel_hacienda/ # API REST Node.js +│ ├── src/ +│ │ ├── server.js # Entry point +│ │ ├── app.js # Config Express + rutas +│ │ ├── db/connection.js # Pool PostgreSQL +│ │ ├── controllers/ # 18 controllers +│ │ ├── routes/ # 16 archivos de rutas +│ │ ├── middlewares/ # Validaciones +│ │ └── services/ # Nodemailer +│ ├── .env # Variables de entorno (NO subir) +│ └── package.json +│ +├── frontend/ +│ └── Frontend-Hotel/ # SPA React +│ ├── src/ +│ │ ├── main.jsx # Entry point React +│ │ ├── App.jsx # Enrutamiento +│ │ ├── components/ # Layout, Tablas, Modales, etc. +│ │ ├── pages/ # ~50+ páginas por dominio +│ │ ├── context/ # AuthContext, LangContext +│ │ ├── services/ # API clients +│ │ ├── constants/ # Configuración de menú +│ │ └── styles/ # CSS por componente +│ ├── public/ # Assets estáticos +│ ├── .env # VITE_API_BASE_URL +│ └── package.json +│ +├── backupcondatos22122025.sql # Backup de base de datos +├── DOCUMENTACION_TECNICA.md # Documentación técnica completa +├── README.md # Este archivo +└── docs/ # Documentación extendida + ├── BACKEND.md + ├── FRONTEND.md + ├── DATABASE.md + ├── API.md + ├── DEPLOYMENT.md + └── TROUBLESHOOTING.md +``` + +--- + +## Tecnologías ### Backend -- **Node.js** con Express 5 -- **PostgreSQL 15** como base de datos -- **Nodemailer** para envio de correos (Brevo/Sendinblue) -- **Stripe** para procesamiento de pagos -- **Axios** para consultas externas (tipo de cambio Banxico) +| Tecnología | Versión | Propósito | +|-----------|---------|-----------| +| Node.js | — | Runtime | +| Express | ^5.1.0 | Framework web | +| PostgreSQL | — | Base de datos | +| pg (node-postgres) | ^8.16.3 | Driver PostgreSQL | +| Axios | ^1.13.2 | HTTP client para APIs externas | +| Nodemailer | ^7.0.12 | Envío de correos | +| Stripe | ^20.1.0 | Integración de pagos | +| xlsx / csv-parser | ^0.18.5 / ^3.2.0 | Procesamiento de archivos | +| express-validator | ^7.2.1 | Validación de inputs | +| dotenv | ^17.2.2 | Variables de entorno | ### Frontend -- **React 19** con Vite 7 -- **React Router DOM 7** para navegacion -- **React Hook Form** + **Yup** para validacion de formularios -- **Bootstrap 5** + **Tailwind CSS** para estilos -- **Axios** para peticiones HTTP -- **XLSX** para exportacion de datos a Excel - -### Infraestructura -- **Docker** y **Docker Compose** para contenedorizacion -- **PostgreSQL 15** en contenedor +| Tecnología | Versión | Propósito | +|-----------|---------|-----------| +| React | ^19.1.1 | UI Library | +| React DOM | ^19.1.1 | Renderizado | +| React Router DOM | ^7.8.2 | Enrutamiento SPA | +| Vite | ^7.1.2 | Build tool y dev server | +| Axios | ^1.11.0 | HTTP client | +| Tailwind CSS | ^4.1.13 | Framework CSS (uso parcial) | +| Bootstrap | ^5.3.8 | Framework CSS (uso parcial) | +| react-hook-form | ^7.66.1 | Manejo de formularios | +| Yup | ^1.7.1 | Validación de esquemas | +| xlsx | ^0.18.5 | Exportación a Excel | +| react-icons | ^5.5.0 | Iconografía | --- -## Estructura del Proyecto - -``` -Hotel/ -├── backend/ -│ ├── Dockerfile -│ └── hotel_hacienda/ -│ ├── src/ -│ │ ├── app.js # Configuracion Express -│ │ ├── server.js # Punto de entrada -│ │ ├── controllers/ # Logica de negocio -│ │ ├── routes/ # Definicion de rutas API -│ │ ├── middlewares/ # Validadores -│ │ ├── db/ # Conexion a PostgreSQL -│ │ └── services/ # Servicios (email) -│ ├── scripts bd/ # Scripts SQL para la BD -│ └── package.json -├── frontend/ -│ ├── Dockerfile -│ └── Frontend-Hotel/ -│ ├── src/ -│ │ ├── App.jsx # Rutas principales -│ │ ├── main.jsx # Punto de entrada -│ │ ├── components/ # Componentes reutilizables -│ │ ├── pages/ # Paginas de la aplicacion -│ │ ├── services/ # Servicios API -│ │ ├── context/ # Context API -│ │ └── styles/ # Estilos globales -│ └── package.json -├── docker-compose.yml -├── .env.example -└── README.md -``` - ---- - -## Modulos del Sistema - -### 1. Dashboard -- **Income**: Vista general de ingresos -- **Hotel P&L**: Estado de perdidas y ganancias del hotel -- **Restaurant P&L**: Estado de perdidas y ganancias del restaurante -- **Budget**: Presupuesto -- **Cost Per Room**: Costo por habitacion -- **Room Analysis**: Analisis de ocupacion de habitaciones -- **Expenses**: Resumen de gastos - -### 2. Nomina y Empleados (Payroll) -- Gestion de empleados (alta, baja, modificacion) -- Contratos laborales -- Control de asistencia -- Uniformes -- Pago diario - -### 3. Gastos (Expenses) -- Registro de gastos -- Pagos mensuales recurrentes -- Aprobacion de gastos (flujo de aprobacion) -- Proveedores -- Reportes de gastos -- Entradas de compras - -### 4. Inventario (Inventory) -- Catalogo de productos -- Ajustes de inventario -- Salidas de inventario -- Salidas de ama de llaves (Housekeeper) -- Reportes de inventario -- Descarte de productos - -### 5. Ingresos (Income) -- Registro de ingresos -- Reportes de ingresos -- Integracion con sistema Horux - -### 6. Hotel -- Gestion de propiedades -- Gestion de habitaciones - -### 7. Configuracion (Settings) -- Gestion de habitaciones -- Configuracion del sistema -- Gestion de usuarios - ---- - -## API Endpoints - -### Autenticacion -- `POST /api/auth/login` - Iniciar sesion -- `POST /api/auth/create` - Crear usuario -- `POST /api/auth/recover` - Recuperar contrasena - -### Empleados -- `GET /api/employees` - Listar empleados -- `POST /api/employees/new` - Crear empleado -- `PUT /api/employees/update` - Actualizar empleado -- `GET /api/employees/attendance` - Obtener asistencia - -### Contratos -- `GET /api/contracts` - Listar contratos -- `POST /api/contracts` - Crear contrato -- `PUT /api/contracts` - Actualizar contrato - -### Productos/Inventario -- `GET /api/products` - Listar productos -- `POST /api/products` - Crear producto -- `PUT /api/products` - Actualizar producto - -### Gastos -- `GET /api/expenses` - Listar gastos -- `POST /api/expenses` - Crear gasto -- `PUT /api/expenses` - Actualizar gasto -- `GET /api/expenses/pending` - Gastos pendientes de aprobacion - -### Ingresos -- `GET /api/incomes` - Listar ingresos -- `POST /api/incomes` - Registrar ingreso -- `GET /api/incomeshrx` - Ingresos Horux - -### Pagos -- `POST /api/payment` - Procesar pago con Stripe - -### Tipo de Cambio -- `GET /api/exchange` - Obtener tipo de cambio (Banxico) - -### Configuracion -- `GET /api/settings` - Obtener configuracion -- `PUT /api/settings` - Actualizar configuracion - ---- - -## Guia de Instalacion +## Instalación y Configuración ### Requisitos Previos -- Docker y Docker Compose instalados -- Git +- Node.js 18+ +- PostgreSQL 13+ +- npm o pnpm -### Opcion 1: Instalacion con Docker (Recomendado) +### 1. Clonar el Repositorio -1. **Clonar el repositorio** ```bash -git clone https://git.consultoria-as.com/usuario/Hacienda-San-Angel.git -cd Hacienda-San-Angel +git clone https://git.consultoria-as.com/consultoria-as/hotel-hacienda.git +cd hotel-hacienda ``` -2. **Configurar variables de entorno** +### 2. Configurar Base de Datos + +Restaurar el backup de PostgreSQL: + ```bash -cp .env.example .env +psql -U postgres -c "CREATE DATABASE hotel_hacienda;" +psql -U postgres -d hotel_hacienda -f backupcondatos22122025.sql ``` -Editar el archivo `.env` con los valores correspondientes: -```env -POSTGRES_PASSWORD=tu_password_seguro -EMAIL_USER=tu_email@ejemplo.com -EMAIL_PASS=tu_api_key_brevo -BANXICO_TOKEN=tu_token_banxico -``` +> **Nota:** El backup contiene las funciones SQL, tablas, catálogos y datos maestros del sistema. -3. **Construir e iniciar los contenedores** -```bash -docker-compose up -d --build -``` +### 3. Configurar Backend -4. **Verificar que los servicios esten corriendo** -```bash -docker-compose ps -``` - -5. **Importar la base de datos** -```bash -# Copiar el archivo SQL al contenedor -docker cp backupcondatos22122025.sql postgres_db:/tmp/ - -# Ejecutar el script SQL -docker exec -it postgres_db psql -U oposgres -d haciendasanangel -f /tmp/backupcondatos22122025.sql -``` - -6. **Acceder a la aplicacion** -- Frontend: http://localhost:5172 -- Backend API: http://localhost:4000/api - -### Opcion 2: Instalacion Manual (Desarrollo) - -#### Backend - -1. **Navegar al directorio del backend** ```bash cd backend/hotel_hacienda -``` - -2. **Instalar dependencias** -```bash +cp .env.example .env # Si no existe, crear manualmente npm install ``` -3. **Configurar variables de entorno** -```bash -cp .env.example .env -# Editar .env con los valores correspondientes +Archivo `.env` del backend: +```env +PORT=3000 +URL_CORS=https://hotel.consultoria-as.com + +# PostgreSQL +DB_HOST=localhost +DB_PORT=5432 +DB_USER=postgres +DB_PASSWORD=TU_PASSWORD +DB_NAME=hotel_hacienda + +# Email +EMAIL_HOST=smtp.tudominio.com +EMAIL_PORT=587 +EMAIL_USER=soporte@horuxfin.com +EMAIL_PASS=TU_PASSWORD + +# Stripe +STRIPE_SECRET_KEY=sk_test_... + +# API Facturas (México) +FACTURAS_API_URL=https://api.facturas.com/... +FACTURAS_ISSUER_RFC=RFC_EMISOR +FACTURAS_TYPE=I +FACTURAS_API_TOKEN=token_aqui + +# Banxico +BANXICO_TOKEN=token_banxico ``` -4. **Iniciar el servidor** +### 4. Iniciar Backend + ```bash -npm run dev # Desarrollo con nodemon -npm start # Produccion +npm run dev # Desarrollo con nodemon +# o +npm start # Producción ``` -#### Frontend +### 5. Configurar Frontend -1. **Navegar al directorio del frontend** ```bash cd frontend/Frontend-Hotel -``` - -2. **Instalar dependencias** -```bash +cp .env.example .env npm install ``` -3. **Configurar variables de entorno** -```bash -cp .env.example .env -# Editar .env con la URL del API +Archivo `.env` del frontend: +```env +VITE_API_BASE_URL=http://localhost:3000/api ``` -4. **Iniciar la aplicacion** +### 6. Iniciar Frontend + ```bash -npm run dev # Desarrollo -npm run build # Construir para produccion +npm run dev # Puerto 5172 ``` --- -## Base de Datos +## Scripts Disponibles -### Configuracion -- **Motor**: PostgreSQL 15 -- **Base de datos**: haciendasanangel -- **Usuario**: oposgres +### Backend (`backend/hotel_hacienda/`) +| Script | Comando | Descripción | +|--------|---------|-------------| +| `start` | `node src/server.js` | Producción | +| `dev` | `nodemon src/server.js` | Desarrollo con hot reload | -### Scripts SQL -Los scripts de la base de datos se encuentran en `backend/hotel_hacienda/scripts bd/funcionesparaproduccion/`. Incluyen: - -- Funciones para empleados y contratos -- Funciones para gastos e ingresos -- Funciones para inventario -- Funciones para reporteria -- Funciones de autenticacion +### Frontend (`frontend/Frontend-Hotel/`) +| Script | Comando | Descripción | +|--------|---------|-------------| +| `dev` | `vite` | Servidor de desarrollo | +| `build` | `vite build` | Build de producción | +| `preview` | `vite preview` | Previsualizar build | +| `lint` | `eslint .` | Linting del código | --- -## Variables de Entorno +## Documentación Adicional -### Backend (.env) -| Variable | Descripcion | -|----------|-------------| -| PORT | Puerto del servidor (default: 4000) | -| DB_HOST | Host de PostgreSQL | -| DB_PORT | Puerto de PostgreSQL (default: 5432) | -| DB_USER | Usuario de PostgreSQL | -| DB_PASSWORD | Contrasena de PostgreSQL | -| DB_NAME | Nombre de la base de datos | -| EMAIL_HOST | Servidor SMTP | -| EMAIL_PORT | Puerto SMTP | -| EMAIL_USER | Usuario SMTP | -| EMAIL_PASS | Contrasena/API Key SMTP | -| URL_CORS | URL permitida para CORS | -| BANXICO_TOKEN | Token API de Banxico | -| STRIPE_SECRET_KEY | Llave secreta de Stripe | - -### Frontend (.env) -| Variable | Descripcion | -|----------|-------------| -| VITE_API_BASE_URL | URL base del API backend | +- [`DOCUMENTACION_TECNICA.md`](./DOCUMENTACION_TECNICA.md) — Análisis técnico completo, endpoints, roles, integraciones y reglas de oro para no romper integridad. +- [`docs/BACKEND.md`](./docs/BACKEND.md) — Guía detallada del backend: estructura de carpetas, controllers, servicios y configuración. +- [`docs/FRONTEND.md`](./docs/FRONTEND.md) — Guía del frontend: componentes, rutas, manejo de estado, permisos y estilos. +- [`docs/DATABASE.md`](./docs/DATABASE.md) — Documentación de la base de datos: funciones SQL conocidas, tablas principales y relaciones. +- [`docs/API.md`](./docs/API.md) — Referencia completa de todos los endpoints REST. +- [`docs/DEPLOYMENT.md`](./docs/DEPLOYMENT.md) — Guía de despliegue en producción. +- [`docs/TROUBLESHOOTING.md`](./docs/TROUBLESHOOTING.md) — Problemas comunes y soluciones. --- -## Despliegue en Produccion +## Integraciones Externas -### Con Docker Compose - -1. Asegurarse de tener configurado el archivo `.env` con valores de produccion - -2. Modificar `docker-compose.yml` si es necesario: - - Cambiar `URL_CORS` a tu dominio de produccion - - Configurar volumenes persistentes para la base de datos - -3. Iniciar los servicios: -```bash -docker-compose up -d -``` - -### Notas de Seguridad -- Cambiar las contrasenas por defecto -- Usar HTTPS en produccion (configurar con nginx o similar) -- Respaldar la base de datos regularmente -- No exponer el puerto de PostgreSQL al exterior +| Servicio | Uso | Archivo Relacionado | +|----------|-----|---------------------| +| **Banxico API** | Tipo de cambio USD/MXN | `exchange.controller.js` | +| **Stripe API** | Transfers y balance transactions | `incomehrx.controller.js` | +| **API Facturas** | Descarga de facturas electrónicas (México) | `incomehrx.controller.js` | +| **Nodemailer/SMTP** | Correos transaccionales | `mail.controller.js`, `auth.controller.js` | --- -## Mantenimiento +## Seguridad y Autenticación -### Respaldo de Base de Datos -```bash -docker exec postgres_db pg_dump -U oposgres haciendasanangel > backup_$(date +%Y%m%d).sql -``` - -### Ver logs -```bash -docker-compose logs -f backend -docker-compose logs -f frontend -docker-compose logs -f postgres -``` - -### Reiniciar servicios -```bash -docker-compose restart -``` +> ⚠️ **Advertencia de seguridad:** Este sistema utiliza autenticación básica basada en función SQL (`validarusuario`) sin JWT ni middleware de autorización en los endpoints del backend. El control de acceso se realiza principalmente en el frontend mediante un número de rol guardado en `localStorage`. +> +> **Recomendación:** Para producción, considerar implementar: +> - JWT o API Keys en el backend +> - Middleware de autorización por rol +> - HTTPS obligatorio +> - Sanitización adicional de inputs --- -## Licencia +## Créditos -Proyecto privado - Hacienda San Angel +Desarrollado para **Consultoría AS** — Hacienda San Angel. --- -## Contacto - -Para soporte tecnico, contactar al equipo de desarrollo. +> **Mantenimiento:** Si realizas cambios significativos, actualiza tanto el código como la documentación correspondiente para mantener la integridad del sistema. diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..73c8faa --- /dev/null +++ b/docs/API.md @@ -0,0 +1,699 @@ +# 🔌 Referencia Completa de la API REST + +**Base URL:** `https:///api` + +--- + +## Índice +1. [Convenciones](#convenciones) +2. [Autenticación](#autenticación) +3. [Empleados](#empleados) +4. [Contratos](#contratos) +5. [Productos](#productos) +6. [Gastos](#gastos) +7. [Aprobaciones y Pagos](#aprobaciones-y-pagos) +8. [Pagos Mensuales](#pagos-mensuales) +9. [Configuraciones](#configuraciones) +10. [Emails](#emails) +11. [Ingresos (Little Hotelier)](#ingresos-little-hotelier) +12. [Compras](#compras) +13. [Tipo de Cambio](#tipo-de-cambio) +14. [Hotel P&L](#hotel-pl) +15. [Restaurant P&L](#restaurant-pl) +16. [Ingresos Horux](#ingresos-horux) + +--- + +## Convenciones + +- **Formato:** JSON en request body y response. +- **Paginación:** `?page=N&limit=M` (default limit: 500 para empleados, 10 para productos). +- **Errores:** Status `500` con `{ message: "Error descripción" }`. +- **CORS:** Origen controlado por `URL_CORS` en el backend. + +--- + +## Autenticación + +> ⚠️ **Nota:** No hay JWT. El login devuelve un rol numérico que el frontend guarda en `localStorage`. + +### POST `/api/auth/login` + +```json +// Request +{ + "name_mail_user": "usuario_o_email", + "user_pass": "contraseña" +} + +// Response 200 +{ + "rol": 1, + "user_id": 5, + "user_name": "Admin", + "message": "Usuario autenticado correctamente" +} +``` + +**Status codes de la función SQL:** +- `status: 1` = Autenticado +- `status: 2` = Credenciales incorrectas + +### POST `/api/auth/createuser` + +```json +// Request +{ + "name_user": "Nuevo Usuario", + "id_rol": 2, + "email": "user@email.com", + "user_pass": "password123" +} + +// Response +{ + "message": "Se agrego el usuario correctamente", + "status": 1 +} +``` + +### POST `/api/auth/recoverpass` + +Genera una contraseña aleatoria y la envía por correo. + +```json +// Request +{ "user_mail": "user@email.com" } + +// Response +{ + "message": "Correo enviado con nueva contraseña.", + "status": 1 +} +``` + +--- + +## Empleados + +Base: `/api/employees` + +### GET `/api/employees/` + +Lista paginada de empleados. + +**Query params:** `?page=1&limit=500` + +```json +// Response +{ + "page": 1, + "limit": 500, + "total": 45, + "totalPages": 1, + "data": [ + { + "employee_rfc": "RFC123456", + "name_employee": "Juan Pérez", + "nss_employe": "12345678901", + "position_employee": "Recepcionista", + "area_employee": "Front Desk", + "phone_employee": "5551234567", + "end_contract": "2025-12-31", + "daily_pay": 500.00, + "uniforms": 2, + "status": "Activo", + "birthday": "1990-05-15", + "curp": "CURP123456HDF" + } + ] +} +``` + +### GET `/api/employees/activeEmployees` + +```json +{ "message": "Total de empleados activos", "data": 42 } +``` + +### GET `/api/employees/getattendance` + +```json +{ "message": "attendance", "data": [...] } +``` + +### GET `/api/employees/gradeofstudy` + +Catálogo de grados de estudio. + +### GET `/api/employees/relationship` + +Catálogo de parentescos. + +### POST `/api/employees/employee` + +Obtiene un empleado por RFC. + +```json +// Request +{ "rfcEmployee": "RFC123456" } +``` + +### POST `/api/employees/newemployee` + +```json +// Request +{ + "name_emp": "Juan Pérez", + "rfc_emp": "RFC123456", + "nss_emp": "12345678901", + "addres_emp": "Calle 123", + "phone_emp": "5551234567", + "email_emp": "juan@email.com", + "birthday_emp": "1990-05-15", + "curp_emp": "CURP123456HDF", + "study_emp": 1, + "emergency_name": "María Pérez", + "emergency_tel": "5559876543", + "relationship_id": 1 +} +``` + +### POST `/api/employees/updateemployee` + +Misma firma que `newemployee`. Actualiza por RFC. + +--- + +## Contratos + +Base: `/api/contracts` + +### GET `/api/contracts/` + +### GET `/api/contracts/getinfocontract/:id` + +### GET `/api/contracts/neartoend` + +### GET `/api/contracts/positions` + +### GET `/api/contracts/areas` + +### GET `/api/contracts/bosses` + +### GET `/api/contracts/reportempcontract` + +### GET `/api/contracts/disabledcontract` + +### POST `/api/contracts/newcontract` + +### PUT `/api/contracts/updatecontract/:id` + +--- + +## Productos + +Base: `/api/products` + +### GET `/api/products/` + +Paginado (default limit: 10). + +### GET `/api/products/productcategory` + +### GET `/api/products/producttype` + +### GET `/api/products/suppliers` + +### GET `/api/products/gdiscardproducts` + +### GET `/api/products/reportinventory` + +### GET `/api/products/stockadjusment` + +### GET `/api/products/gethousekeeper` + +### GET `/api/products/getproducts` + +### GET `/api/products/getconsumptionstockreport` + +### POST `/api/products/newsupplier` + +```json +{ + "new_name_supp": "Proveedor SA", + "new_rfc_supp": "RFC123456", + "new_mail_supp": "prov@email.com", + "new_phone_supp": "5551234567" +} +``` + +### POST `/api/products/newproduct` + +```json +{ + "new_name_product": "Toallas", + "new_sku_product": "TOW-001", + "product_type": [{"id": 1, "name": "Textil"}], + "new_category": 1, + "suppliers_id": 1, + "unit": 1, + "new_stock": 100, + "newprice_product": 150.00, + "new_tax": 1, + "currency": 1, + "image_product": "url_o_base64" +} +``` + +> Nota: `product_type` se serializa a JSONB. + +### POST `/api/products/stockadjusmentset` + +```json +{ + "stockproductadjusment": [ + { "id_product": 1, "new_stock": 50 }, + { "id_product": 2, "new_stock": 30 } + ] +} +``` + +### POST `/api/products/newconsumptionstock` + +```json +{ + "product_id": 1, + "quantity_consumption": 5, + "date_consumption": "2025-01-15", + "rfc_emp": "RFC123456" +} +``` + +### POST `/api/products/disableSupplier` + +```json +{ "supplier_id": 1 } +``` + +### PUT `/api/products/update_product/:id` + +### PUT `/api/products/discardproduct/:id` + +```json +{ + "quantity": 5, + "reasons": "Dañado" +} +``` + +### PUT `/api/products/product/:id` + +### PUT `/api/products/updatesupplier/:id` + +⚠️ **Bug conocido:** Falta `/` inicial en la ruta del backend. + +--- + +## Gastos + +Base: `/api/expenses` + +### GET `/api/expenses/pendingapproval` + +### GET `/api/expenses/approvedexpenses` + +### GET `/api/expenses/rejectedexpenses` + +### GET `/api/expenses/mainsupplier` + +### PUT `/api/expenses/getexpense/:id` + +⚠️ **Nota:** Es `PUT` pero debería ser `GET`. No requiere body. + +### GET `/api/expenses/getinfo` + +Devuelve catálogos combinados (suppliers, categories, areas, users, currencies, uniforms, taxes). + +### GET `/api/expenses/reportexpenses` + +### POST `/api/expenses/countpending` + +```json +{ "option": 1 } // 1 = aprobaciones, 2 = mensuales +``` + +### GET `/api/expenses/reportpayments` + +### GET `/api/expenses/monthlypayments` + +### POST `/api/expenses/newexpense` + +```json +{ + "new_description": "Compra de amenities", + "suppliers_id": 1, + "new_request_date": "2025-01-01", + "new_payment_deadline": "2025-01-31", + "request_by": 1, + "area": 1, + "expense_cat": 1, + "currency_id": 1, + "products": [ + { "id_product": 1, "quantity": 10 }, + { "id_product": 2, "quantity": 5 } + ], + "new_iva": 16.00, + "new_ieps": 0.00, + "new_subtotal": 1000.00, + "new_total": 1160.00, + "needtoapprove": true +} +``` + +### POST `/api/expenses/totalapproved` + +```json +{ "option": 1 } // 1 = aprobado, 2 = rechazado +``` + +### PUT `/api/expenses/updateexpense/:id` + +Misma estructura que `newexpense`, con campos prefijados con `up_`. + +### GET `/api/expenses/gettaxes` + +--- + +## Aprobaciones y Pagos + +Base: `/api/status` + +### PUT `/api/status/approveupdate/:id` + +```json +{ + "status": 2, + "approved_by": 1 +} +``` + +**Status:** +- `2` = Aprobado +- `3` = Rechazado + +### PUT `/api/status/paymentupdate/:id` + +```json +{ "status": 2 } +``` + +### GET `/api/status/penapppayments` + +### GET `/api/status/countdelaypay` + +### GET `/api/status/totalspent` + +--- + +## Pagos Mensuales + +Base: `/api/payment` + +### POST `/api/payment/newexpmonthly` + +```json +{ + "descriptionex": "Renta", + "recurrence_id": 1, + "payment_type": 1, + "currency_id": 1, + "suppliers_id": 1, + "area": 1, + "expense_category": 1, + "day_expense": 15, + "tax_id": 1, + "new_subtotal": 5000.00 +} +``` + +### GET `/api/payment/refreshmonthly` + +### PUT `/api/payment/paymentstatusmonthly/:id` + +```json +{ + "status_payment": 2, + "tax_id": 1, + "subtotal": 5000.00 +} +``` + +Si `subtotal` es null, solo actualiza el estado sin recalcular total. + +### PUT `/api/payment/updateexpmonthly/:id` + +### GET `/api/payment/onemothlyexpense/:id` + +### PUT `/api/payment/needtorefresh/:id` + +```json +{ "notrefresh": true } +``` + +--- + +## Configuraciones + +Base: `/api/settings` + +### POST `/api/settings/newroom` + +### POST `/api/settings/newproperty` + +### GET `/api/settings/reportrooms` + +### GET `/api/settings/reportproperties` + +### GET `/api/settings/approveby` + +### GET `/api/settings/requestby` + +### GET `/api/settings/categoryexpense` + +### GET `/api/settings/currency` + +### GET `/api/settings/units` + +### GET `/api/settings/recurrence` + +--- + +## Emails + +Base: `/api/emails` + +> ⚠️ **Nota:** Todos estos endpoints tienen validación de frecuencia (1 ejecución por mes) mediante la función `validateendpoint()`. + +### POST `/api/emails/nearexpiring` + +### POST `/api/emails/paymentdelay` + +### POST `/api/emails/expensesneartodeadline` + +### POST `/api/emails/expensesspecial` + +### POST `/api/emails/birthdays` + +### POST `/api/emails/contractexpired` + +### POST `/api/emails/expiredcontractsmonth` + +--- + +## Ingresos (Little Hotelier) + +Base: `/api/incomes` + +> **Nota:** La mayoría de estos endpoints reciben filtros de fecha en el body. + +### POST `/api/incomes/getincomes` + +### POST `/api/incomes/totalincomes` + +### POST `/api/incomes/channelscards` + +### POST `/api/incomes/loadincomes` + +Carga masiva desde CSV/XLSX previamente colocado en `src/resources/littleHotelier/`. + +### POST `/api/incomes/loadproductsales` + +### POST `/api/incomes/loadchequesdetalle` + +### POST `/api/incomes/reportincomes` + +### POST `/api/incomes/countticket` + +### POST `/api/incomes/efectivo` + +### POST `/api/incomes/otros` + +### POST `/api/incomes/propinas` + +### POST `/api/incomes/tarjeta` + +### POST `/api/incomes/vales` + +### POST `/api/incomes/sumatotal` + +### POST `/api/incomes/ticketpromedio` + +### GET `/api/incomes/getproductsales` + +### GET `/api/incomes/getdetallecheque` + +--- + +## Compras + +Base: `/api/purchases` + +### GET `/api/purchases/getpurchases` + +Devuelve líneas de compra pendientes de recepción. + +```json +// Response +{ + "message": "Se obtuvieron todos los Purchases details", + "data": [ + { + "id_purchase_dt": 1, + "id_expense": 5, + "id_product": 2, + "product_name": "Toallas", + "quantity": 10, + "delivered": 0, + "id_tax": 1, + "total": 1500.00 + } + ] +} +``` + +### PUT `/api/purchases/entry/:id` + +Registra la recepción física de una compra. + +```json +// Request +{ "checking": 10 } + +// Response +{ + "message": "Se obtuvo el Purchases details", + "newentry": 1 +} +``` + +--- + +## Tipo de Cambio + +Base: `/api/exchange` + +### POST `/api/exchange/consultexchange` + +Consulta tipo de cambio actual desde API de Banxico. + +### GET `/api/exchange/getexchanges` + +Devuelve historial de tipos de cambio guardados. + +--- + +## Hotel P&L + +Base: `/api/hotelpl` + +### POST `/api/hotelpl/cogs` + +### POST `/api/hotelpl/ebitda` + +### POST `/api/hotelpl/employeeshare` + +### POST `/api/hotelpl/grossprofit` + +### POST `/api/hotelpl/tips` + +### POST `/api/hotelpl/totalrevenue` + +### POST `/api/hotelpl/weightedCategoriesCost` + +--- + +## Restaurant P&L + +Base: `/api/restaurantpl` + +### POST `/api/restaurantpl/cogs` + +### POST `/api/restaurantpl/ebitda` + +### POST `/api/restaurantpl/grossprofit` + +### POST `/api/restaurantpl/totalrevenue` + +### POST `/api/restaurantpl/weightedCategoriesCost` + +--- + +## Ingresos Horux + +Base: `/api/incomeshrx` + +### GET `/api/incomeshrx/accountincome` + +### GET `/api/incomeshrx/categoryincome` + +### GET `/api/incomeshrx/invoiceIncome` + +### GET `/api/incomeshrx/totalIncome` + +### GET `/api/incomeshrx/incomehorux` + +### GET `/api/incomeshrx/oneincomehorux/:id` + +### GET `/api/incomeshrx/stripedata/` + +Obtiene transfers de Stripe y las inserta en PostgreSQL. + +### POST `/api/incomeshrx/stripedatademo/` + +Crea un transfer de demo en Stripe. + +### POST `/api/incomeshrx/insertinvoice` + +Descarga facturas de API externa (año actual) y las inserta. + +### POST `/api/incomeshrx/newincome` + +```json +{ + "account_id": 1, + "amount": 5000.00, + "new_date": "2025-01-15", + "newinvoice": "FAC-001", + "area_id": 1, + "categories": [{"id": 1, "amount": 5000}] +} +``` + +### PUT `/api/incomeshrx/updateincome/:id` + +Misma estructura que `newincome`. + +--- + +> **Fin de la referencia API.** Para ver la lógica interna de cada endpoint, revisa los controllers en `backend/hotel_hacienda/src/controllers/`. diff --git a/docs/BACKEND.md b/docs/BACKEND.md new file mode 100644 index 0000000..6c2e0dd --- /dev/null +++ b/docs/BACKEND.md @@ -0,0 +1,365 @@ +# 📦 Documentación del Backend — API Hotel Hacienda + +## Índice +1. [Introducción](#introducción) +2. [Estructura de Carpetas](#estructura-de-carpetas) +3. [Flujo de una Petición](#flujo-de-una-petición) +4. [Configuración de Express (`app.js`)](#configuración-de-express-appjs) +5. [Conexión a Base de Datos](#conexión-a-base-de-datos) +6. [Controllers](#controllers) +7. [Rutas](#rutas) +8. [Middlewares](#middlewares) +9. [Servicios](#servicios) +10. [Variables de Entorno](#variables-de-entorno) +11. [Patrones y Convenciones](#patrones-y-convenciones) +12. [Guía para Agregar un Nuevo Endpoint](#guía-para-agregar-un-nuevo-endpoint) + +--- + +## Introducción + +El backend es una API REST construida con **Node.js** y **Express.js**. No utiliza ORM; la lógica de negocio reside en **funciones SQL de PostgreSQL** que son invocadas desde los controllers. + +**Entry point:** `index.js` → `src/server.js` → `src/app.js` + +--- + +## Estructura de Carpetas + +``` +hotel_hacienda/ +├── index.js # Solo requiere src/server.js +├── src/ +│ ├── server.js # Levanta el servidor +│ ├── app.js # Configura Express, CORS, rutas +│ ├── db/ +│ │ └── connection.js # Pool de PostgreSQL +│ ├── middlewares/ +│ │ ├── handleValidation.js # Manejo de errores de express-validator +│ │ └── validators.js # Validadores (login) +│ ├── routes/ # 16 archivos de rutas +│ │ ├── auth.routes.js +│ │ ├── employee.routes.js +│ │ ├── contract.routes.js +│ │ ├── product.routes.js +│ │ ├── expense.routes.js +│ │ ├── status.routes.js +│ │ ├── payment.routes.js +│ │ ├── settings.routes.js +│ │ ├── mail.routes.js +│ │ ├── incomes.routes.js +│ │ ├── purchase.routes.js +│ │ ├── exchange.routes.js +│ │ ├── hotelpl.routes.js +│ │ ├── restaurantpl.routes.js +│ │ ├── incomehrx.routes.js +│ │ └── reportcontract.routes.js +│ ├── controllers/ # 18 archivos de controllers +│ │ ├── auth.controller.js +│ │ ├── employee.controller.js +│ │ ├── contract.controller.js +│ │ ├── product.controller.js +│ │ ├── expense.controller.js +│ │ ├── status.controller.js +│ │ ├── payment.controller.js +│ │ ├── settings.controller.js +│ │ ├── mail.controller.js +│ │ ├── incomes.controller.js +│ │ ├── purchase.controller.js +│ │ ├── exchange.controller.js +│ │ ├── hotelp.controller.js +│ │ ├── restaurantpl.controller.js +│ │ ├── incomehrx.controller.js +│ │ ├── reportcontract.controller.js +│ │ └── reporter.controller.js +│ └── services/ +│ └── mailService.js # Transporte de Nodemailer +``` + +--- + +## Flujo de una Petición + +``` +HTTP Request + │ + ▼ +┌─────────────┐ +│ Express │ ← CORS, JSON parser +│ (app.js) │ +└──────┬──────┘ + │ + ▼ +┌─────────────┐ +│ Routes │ ← Prefijo /api/xxx +│ (.routes) │ +└──────┬──────┘ + │ + ▼ +┌─────────────┐ +│ Controllers │ ← Extrae req.params/body +│ (.controller)│ +└──────┬──────┘ + │ + ▼ +┌─────────────┐ +│ Pool │ ← pg (node-postgres) +│ (connection)│ +└──────┬──────┘ + │ + ▼ +┌─────────────┐ +│ PostgreSQL │ ← Funciones SQL +│ (funciones)│ +└─────────────┘ +``` + +--- + +## Configuración de Express (`app.js`) + +```javascript +const express = require('express'); +const app = express(); +const cors = require('cors'); +require("dotenv").config(); + +const corsOptions = { + origin: process.env.URL_CORS, + methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], + allowedHeaders: ["Content-Type", "Authorization"] +}; +app.use(cors(corsOptions)); +app.use(express.json()); +``` + +**Observaciones:** +- `URL_CORS` debe definirse sin slash al final. +- No hay middleware de autenticación global. +- Todas las rutas usan prefijo `/api/`. + +--- + +## Conexión a Base de Datos + +```javascript +// src/db/connection.js +const { Pool } = require('pg'); +require('dotenv').config(); + +const pool = new Pool({ + host: process.env.DB_HOST, + port: process.env.DB_PORT, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME +}); + +pool.connect() + .then(() => console.log('Conectado a PostgreSQL')) + .catch(err => console.error('Error de conexión:', err)); + +module.exports = pool; +``` + +**Nota:** Se usa un `Pool` reutilizable. Cada controller lo importa directamente. + +--- + +## Controllers + +### Patrón General + +```javascript +const pool = require('../db/connection'); + +const nombreFuncion = async (req, res) => { + try { + const { param1, param2 } = req.body; // o req.params / req.query + + const result = await pool.query( + 'SELECT nombrefuncionsql($1, $2) AS status', + [param1, param2] + ); + + const status = result.rows[0].status; + + res.json({ message: 'Éxito', status }); + } catch (error) { + console.error(error); + res.status(500).json({ message: 'Error interno' }); + } +}; + +module.exports = { nombreFuncion }; +``` + +### Paginación Manual + +Algunos endpoints implementan paginación manual: + +```javascript +const page = parseInt(req.query.page) || 1; +const limit = parseInt(req.query.limit) || 500; +const offset = (page - 1) * limit; + +const result = await pool.query( + 'SELECT * FROM getemployees() LIMIT $1 OFFSET $2', + [limit, offset] +); + +const totalResult = await pool.query('SELECT COUNT(*) FROM employees'); +const total = parseInt(totalResult.rows[0].count); +const totalPages = Math.ceil(total / limit); + +res.json({ page, limit, total, totalPages, data: result.rows }); +``` + +### Manejo de JSONB + +Para pasar arrays/objetos a PostgreSQL: + +```javascript +const categoriesJson = categories ? JSON.stringify(categories) : '[]'; +await pool.query( + 'SELECT newincome($1,$2,$3,$4,$5,$6::jsonb) AS status', + [param1, param2, param3, param4, param5, categoriesJson] +); +``` + +--- + +## Rutas + +### Convención de Nombres +- Archivos: `nombreplural.routes.js` +- Prefijo en `app.js`: `/api/nombreplural` + +### Ejemplo + +```javascript +// src/routes/expense.routes.js +const express = require('express'); +const router = express.Router(); +const expenseController = require('../controllers/expense.controller'); + +router.get('/pendingapproval', expenseController.getPendingAppExpenses); +router.post('/newexpense', expenseController.newExpense); +router.put('/updateexpense/:id', expenseController.updateExpense); + +module.exports = router; +``` + +### Registro en `app.js` + +```javascript +const expenseRoutes = require('./routes/expense.routes'); +app.use('/api/expenses', expenseRoutes); +``` + +--- + +## Middlewares + +### Validación de Login + +```javascript +// src/middlewares/validators.js +const { body } = require('express-validator'); + +const loginValidator = [ + body('name_mail_user').notEmpty().withMessage('Usuario requerido'), + body('user_pass').notEmpty().withMessage('Contraseña requerida') +]; + +module.exports = { loginValidator }; +``` + +### Manejo de Errores de Validación + +```javascript +// src/middlewares/handleValidation.js +const { validationResult } = require('express-validator'); + +const handleValidation = (req, res, next) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } + next(); +}; + +module.exports = handleValidation; +``` + +**Nota:** Actualmente solo el login tiene validación. Los demás endpoints no validan inputs. + +--- + +## Servicios + +### Mail Service (`src/services/mailService.js`) + +Configura un transporte SMTP reutilizable con Nodemailer: + +```javascript +const nodemailer = require('nodemailer'); + +const transporter = nodemailer.createTransport({ + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS + } +}); + +module.exports = transporter; +``` + +**Uso:** Importado en controllers que envían correos (`auth.controller.js`, `mail.controller.js`). + +--- + +## Variables de Entorno + +| Variable | Descripción | Requerida | +|----------|-------------|-----------| +| `PORT` | Puerto del servidor | Sí (default 3000) | +| `URL_CORS` | Origen permitido para CORS | Sí | +| `DB_HOST` | Host de PostgreSQL | Sí | +| `DB_PORT` | Puerto de PostgreSQL | Sí | +| `DB_USER` | Usuario de PostgreSQL | Sí | +| `DB_PASSWORD` | Contraseña de PostgreSQL | Sí | +| `DB_NAME` | Nombre de la base de datos | Sí | +| `EMAIL_HOST` | Servidor SMTP | Sí | +| `EMAIL_PORT` | Puerto SMTP | Sí | +| `EMAIL_USER` | Usuario SMTP | Sí | +| `EMAIL_PASS` | Contraseña SMTP | Sí | +| `STRIPE_SECRET_KEY` | Clave secreta de Stripe | Para integración Stripe | +| `FACTURAS_API_URL` | URL API de facturas | Para integración facturas | +| `FACTURAS_ISSUER_RFC` | RFC del emisor | Para integración facturas | +| `FACTURAS_TYPE` | Tipo de factura (I/E) | Para integración facturas | +| `FACTURAS_API_TOKEN` | Token de API facturas | Para integración facturas | +| `BANXICO_TOKEN` | Token de Banxico | Para tipo de cambio | + +--- + +## Patrones y Convenciones + +1. **Un controller por dominio:** Todos los endpoints relacionados con un recurso viven en el mismo controller. +2. **Parámetros posicionales:** Las funciones SQL usan `$1, $2, $3...` en orden. +3. **Respuesta consistente:** Siempre retornar JSON con al menos `{ message, ...data }`. +4. **Errores 500:** Cualquier excepción se captura y responde con status 500. +5. **Sin ORM:** Las consultas son SQL crudo o llamadas a funciones almacenadas. + +--- + +## Guía para Agregar un Nuevo Endpoint + +1. **Crear/actualizar la función SQL** en PostgreSQL. +2. **Crear el método** en el controller correspondiente. +3. **Agregar la ruta** en el archivo `.routes.js` apropiado. +4. **Registrar la ruta** en `src/app.js` con un prefijo `/api/`. +5. **Probar** la función SQL directamente en PostgreSQL antes de probar el endpoint. +6. **Actualizar** `docs/API.md` con el nuevo endpoint. diff --git a/docs/DATABASE.md b/docs/DATABASE.md new file mode 100644 index 0000000..dac09ac --- /dev/null +++ b/docs/DATABASE.md @@ -0,0 +1,355 @@ +# 🗄️ Documentación de Base de Datos — PostgreSQL + +## Índice +1. [Filosofía de Diseño](#filosofía-de-diseño) +2. [Conexión](#conexión) +3. [Funciones SQL por Dominio](#funciones-sql-por-dominio) +4. [Tablas Principales (Inferidas)](#tablas-principales-inferidas) +5. [Patrones de Datos](#patrones-de-datos) +6. [JSONB como Patrón de Intercambio](#jsonb-como-patrón-de-intercambio) +7. [Integraciones que Escriben en JSONB](#integraciones-que-escriben-en-jsonb) +8. [Guía para Modificar Funciones SQL](#guía-para-modificar-funciones-sql) + +--- + +## Filosofía de Diseño + +Este sistema sigue un patrón **"Database-First"** donde la lógica de negocio reside principalmente en **funciones SQL almacenadas** de PostgreSQL. Los controllers de Node.js actúan como una capa de presentación HTTP liviana que solo orquesta llamadas a estas funciones. + +**Implicaciones:** +- Para modificar lógica de negocio, debes modificar las funciones SQL, no solo el JavaScript. +- Los controllers pasan parámetros posicionales (`$1`, `$2`) y reciben resultados directamente. +- No hay ORM ni capa de abstracción de base de datos. + +--- + +## Conexión + +El backend usa `pg` (node-postgres) con un `Pool`: + +```javascript +const { Pool } = require('pg'); + +const pool = new Pool({ + host: process.env.DB_HOST, + port: process.env.DB_PORT, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME +}); +``` + +**Variables de entorno requeridas:** `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`. + +--- + +## Funciones SQL por Dominio + +### 🔐 Autenticación + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `validarusuario(name_mail_user, user_pass)` | `text`, `text` | `{status, rol, user_id, user_name}` | Valida credenciales | +| `createuser(name_user, id_rol, email, user_pass)` | `text`, `int`, `text`, `text` | `status` | Crea usuario | +| `reppassuser(user_mail, new_pass)` | `text`, `text` | `status` | Reemplaza contraseña | + +### 👥 Empleados + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `getemployees()` | — | setof record | Lista paginada de empleados | +| `activeemployeesnumber()` | — | `int` | Total de empleados activos | +| `getoneemployee(rfcEmployee)` | `text` | setof record | Un empleado por RFC | +| `newemployee(...)` | 12 parámetros | `status` | Inserta empleado | +| `updateemployee(...)` | 12 parámetros | `status` | Actualiza empleado | +| `getattendance()` | — | setof record | Registros de asistencia | + +### 📄 Contratos + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `getcontracts()` | — | setof record | Lista de contratos | +| `getinfocontract(id)` | `int` | setof record | Info de un contrato | +| `neartoend()` | — | setof record | Contratos próximos a vencer | +| `newcontract(...)` | varios | `status` | Crea contrato | +| `updatecontract(...)` | varios | `status` | Actualiza contrato | +| `positions()` | — | setof record | Catálogo de puestos | +| `areas()` | — | setof record | Catálogo de áreas | +| `bosses()` | — | setof record | Catálogo de jefes | + +### 📦 Productos / Inventario + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `getproducts()` | — | setof record | Lista de productos | +| `getoneproduct(id)` | `int` | setof record | Un producto | +| `newproduct(..., jsonb, ...)` | 11 parámetros | `status` | Crea producto | +| `updateproduct(..., jsonb, ...)` | 12 parámetros | `status` | Actualiza producto | +| `discardproductsstock(id, quantity, reasons)` | `int`, `numeric`, `text` | `status` | Descarta stock | +| `getdiscardproduct()` | — | setof record | Productos descartados | +| `report_inventoryv2()` | — | setof record | Reporte de inventario | +| `stockadjusment()` | — | setof record | Ajustes pendientes | +| `setstockadjusmentsv2(jsonb)` | `jsonb` | `stockadjus` | Aplica ajustes | +| `consumptionstock(product_id, quantity, date, rfc)` | varios | `status` | Registra consumo | +| `getconsumptionreport()` | — | setof record | Reporte de consumos | +| `newsupplier(name, rfc, mail, phone)` | 4 parámetros | `status` | Crea proveedor | +| `updatesupplier(id, name, rfc, mail, phone)` | 5 parámetros | `status` | Actualiza proveedor | +| `disableSuppliers(id)` | `int` | `status` | Deshabilita proveedor | +| `newsuplier(...)` | varios | `status` | (posible duplicado ortográfico) | + +### 💰 Gastos (Expenses) + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `newexpensev2(..., jsonb, ...)` | 14 parámetros | `status` | Crea gasto | +| `updateexpensev4(..., jsonb)` | 15 parámetros | `status` | Actualiza gasto | +| `getexpense(id)` | `int` | setof record | Obtiene un gasto | +| `getpendingappexpenses()` | — | setof record | Gastos pendientes de aprobación | +| `getapprovedappexpenses()` | — | setof record | Gastos aprobados | +| `getrejectedappexpenses()` | — | setof record | Gastos rechazados | +| `setapprovalstatus(id, status, approved_by)` | 3 parámetros | `status` | Actualiza estado de aprobación | +| `setpaymentstatusv2(id, status)` | 2 parámetros | `status` | Actualiza estado de pago | +| `pendingapppayments()` | — | setof record | Pagos pendientes | +| `countdelaypayments()` | — | `int` | Pagos con retraso | +| `totalspent()` | — | `numeric` | Total gastado | +| `totalapproved()` | — | `numeric` | Total aprobado | +| `totalrejected()` | — | `numeric` | Total rechazado | +| `report_expenses()` | — | setof record | Reporte de gastos | +| `paymentsreport()` | — | setof record | Reporte de pagos | +| `mainsupplier()` | — | setof record | Proveedor principal | +| `gettaxes()` | — | setof record | Catálogo de impuestos | + +### 🛒 Compras / Purchase Entries + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `getpurchases()` | — | setof record | Detalles de compras pendientes | +| `purchaseentry(id, checking)` | `int`, `int` | `status` | Registra recepción física | + +### 💳 Pagos Mensuales + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `newmonthlyexpensev2(...)` | 10 parámetros | `status` | Crea gasto mensual | +| `updatemonthlyexpensev2(...)` | 11 parámetros | `status` | Actualiza gasto mensual | +| `setpaymentstatusmonthlyv2(id, status)` | 2 parámetros | `status` | Actualiza estado de pago | +| `setpaymentstatusmonthlyv2(id, status, tax_id, total)` | 4 parámetros | `status` | Actualiza estado y monto | +| `refresh_monthly_expenses()` | — | `status` | Refresca gastos mensuales | +| `getmonthlypayments()` | — | setof record | Lista de pagos mensuales | +| `getONEmonthlypayment(id)` | `int` | setof record | Un pago mensual | +| `needToRefresh(id, boolean)` | 2 parámetros | rows | Controla actualización automática | + +### 📧 Emails Programáticos + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `validateendpoint(endpoint_name)` | `text` | `boolean` | Verifica si se puede ejecutar (1x/mes) | +| `nearexpiring()` | — | setof record | Contratos por vencer | +| `paymentdelay()` | — | setof record | Pagos atrasados | +| `expensesneartodeadline()` | — | setof record | Gastos próximos a fecha límite | +| `expensesspecial()` | — | setof record | Gastos especiales | +| `birthdays()` | — | setof record | Cumpleaños | +| `contractexpired()` | — | setof record | Contratos vencidos | +| `expiredcontractsmonth()` | — | setof record | Contratos vencidos del mes | + +### 📊 Ingresos (Little Hotelier) + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `getincomes(...)` | varios (filtros) | setof record | Lista de ingresos | +| `totalincomes(...)` | varios | `numeric` | Total de ingresos | +| `channelscards(...)` | varios | setof record | Canales de ingreso | +| `loadincomes(jsonb)` | `jsonb` | `status` | Carga masiva de ingresos | +| `loadproductsales(jsonb)` | `jsonb` | `status` | Carga masiva de ventas de productos | +| `loadchequesdetalle(jsonb)` | `jsonb` | `status` | Carga masiva de cheques | +| `reportincomes(...)` | varios | setof record | Reporte de ingresos | +| `countticket(...)` | varios | `int` | Conteo de tickets | +| `efectivo(...)`, `otros(...)`, `propinas(...)`, `tarjeta(...)`, `vales(...)` | varios | `numeric` | Desglose por tipo de pago | +| `sumatotal(...)` | varios | `numeric` | Suma total | +| `ticketpromedio(...)` | varios | `numeric` | Ticket promedio | + +### 🏨 Horux / Facturas / Stripe + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `addhoruxdata(jsonb)` | `jsonb` | `int` (total insertados) | Inserta facturas masivamente | +| `getcategoryincome()` | — | setof record | Categorías de ingreso | +| `getinvoiceincome()` | — | setof record | Facturas de ingreso | +| `getaccountincome()` | — | setof record | Cuentas de ingreso | +| `getincomehorux()` | — | setof record | Ingresos Horux | +| `gettotalincome()` | — | `numeric` | Total de ingresos Horux | +| `getoneincome(id)` | `int` | setof record | Un ingreso Horux | +| `newincome(account_id, amount, date, invoice, area_id, categories::jsonb)` | 6 parámetros | `status` | Crea ingreso Horux | +| `updateincome(id, account_id, amount, date, invoice, area_id, categories::jsonb)` | 7 parámetros | `status` | Actualiza ingreso Horux | +| `addstripedatav2(jsonb)` | `jsonb` | `int` (inserted) | Inserta datos de Stripe | + +### 📈 Hotel P&L / Restaurant P&L + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `cogs(...)` | varios (fechas) | setof record | Cost of Goods Sold | +| `ebitda(...)` | varios | setof record | EBITDA | +| `employeeshare(...)` | varios | setof record | Participación de empleados | +| `grossprofit(...)` | varios | setof record | Ganancia bruta | +| `tips(...)` | varios | setof record | Propinas | +| `totalrevenue(...)` | varios | setof record | Ingresos totales | +| `weightedCategoriesCost(...)` | varios | setof record | Costos ponderados por categoría | + +### 💱 Tipo de Cambio + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `consultexchange(...)` | varios | setof record | Consulta tipo de cambio | +| `getexchanges()` | — | setof record | Historial de tipos de cambio | + +### ⚙️ Configuraciones + +| Función | Parámetros | Retorno | Descripción | +|---------|-----------|---------|-------------| +| `newroom(...)` | varios | `status` | Crea habitación | +| `newproperty(...)` | varios | `status` | Crea propiedad | +| `reportrooms()` | — | setof record | Reporte de habitaciones | +| `reportproperties()` | — | setof record | Reporte de propiedades | +| `approveby()` | — | setof record | Usuarios que pueden aprobar | +| `requestby()` | — | setof record | Usuarios que pueden solicitar | +| `categoryexpense()` | — | setof record | Categorías de gasto | +| `currency()` | — | setof record | Monedas | +| `units()` | — | setof record | Unidades de medida | +| `recurrence()` | — | setof record | Recurrencias (mensual, etc.) | + +--- + +## Tablas Principales (Inferidas) + +Basado en las funciones SQL y los controllers, las tablas principales son: + +| Tabla | Propósito | +|-------|-----------| +| `employees` | Datos de empleados | +| `contracts` | Contratos laborales | +| `products` | Catálogo de productos | +| `suppliers` | Proveedores | +| `expenses` | Gastos/egresos | +| `purchase_details` | Líneas de compra asociadas a gastos | +| `inventory_entries` / `stock_movements` | Movimientos de inventario | +| `incomes` | Ingresos de Little Hotelier | +| `income_hrx` | Ingresos de Horux | +| `invoice_income` | Facturas descargadas | +| `stripe_data` | Datos de transfers de Stripe | +| `users` | Usuarios del sistema | +| `roles` | Roles de usuario | +| `areas` | Áreas del hotel | +| `positions` | Puestos de trabajo | +| `endpoint_logs` | Control de ejecución de emails (1x/mes) | +| `settings_rooms` | Habitaciones | +| `settings_properties` | Propiedades | + +--- + +## Patrones de Datos + +### Paginación Manual + +```sql +-- En los controllers +SELECT * FROM getemployees() LIMIT $1 OFFSET $2; +SELECT COUNT(*) FROM employees; +``` + +### Estados Numéricos + +Muchas funciones retornan un `status` numérico: +- `1` = Éxito +- `0` = Error/No permitido +- `-3` = Condición no cumplida (ej. "gasto no aprobado") +- Otros negativos = Errores específicos + +--- + +## JSONB como Patrón de Intercambio + +El sistema usa extensivamente `jsonb` para pasar datos complejos: + +### Ejemplo: Productos en un Gasto + +```javascript +// Controller +const products = [ + { id_product: 1, quantity: 10 }, + { id_product: 2, quantity: 5 } +]; +const productJson = JSON.stringify(products); + +await pool.query( + 'SELECT newexpensev2($1,$2,$3,$4,$5,$6,$7,$8,$9,$10::jsonb,$11,$12,$13,$14)', + [..., productJson, ...] +); +``` + +### Ejemplo: Ajustes de Stock + +```javascript +const stockproductadjusment = [ + { id_product: 1, new_stock: 50 }, + { id_product: 2, new_stock: 30 } +]; + +await pool.query( + 'SELECT * FROM setstockadjusmentsv2($1::jsonb)', + [JSON.stringify(stockproductadjusment)] +); +``` + +### Ejemplo: Facturas Masivas + +```javascript +const facturas = await getFacturas(desde, hasta); +await pool.query( + 'SELECT addhoruxdata($1::jsonb)', + [JSON.stringify(facturas)] +); +``` + +--- + +## Integraciones que Escriben en JSONB + +| Integración | Función SQL | Tabla Destino | +|-------------|-------------|---------------| +| Little Hotelier (CSV/XLSX) | `loadincomes(jsonb)` | `incomes` | +| Facturas API (México) | `addhoruxdata(jsonb)` | `income_hrx` / `invoice_income` | +| Stripe | `addstripedatav2(jsonb)` | `stripe_data` | + +--- + +## Guía para Modificar Funciones SQL + +### Paso 1: Verificar Dependencias + +Busca TODOS los controllers que llaman a la función: + +```bash +grep -r "nombrefuncion(" backend/hotel_hacienda/src/ +``` + +### Paso 2: Probar en PostgreSQL Directamente + +```sql +SELECT * FROM nombrefuncion('param1', 'param2'); +-- o +SELECT nombrefuncion('param1') AS status; +``` + +### Paso 3: Considerar Idempotencia + +Si la función modifica datos (INSERT/UPDATE), considera: +- ¿Qué pasa si se llama dos veces con los mismos parámetros? +- ¿Debería usar `ON CONFLICT` o verificar `IF EXISTS`? + +### Paso 4: Actualizar Controllers si Cambia la Firma + +Si agregas/quitas parámetros, actualiza TODOS los `pool.query(...)` que la llaman. + +### Paso 5: Documentar el Cambio + +Actualiza este archivo (`docs/DATABASE.md`) y `DOCUMENTACION_TECNICA.md`. diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..90b03b2 --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,262 @@ +# 🚀 Guía de Despliegue + +## Índice +1. [Requisitos del Servidor](#requisitos-del-servidor) +2. [Despliegue de Base de Datos](#despliegue-de-base-de-datos) +3. [Despliegue del Backend](#despliegue-del-backend) +4. [Despliegue del Frontend](#despliegue-del-frontend) +5. [Configuración de Proxy Inverso (Nginx)](#configuración-de-proxy-inverso-nginx) +6. [Configuración de PM2](#configuración-de-pm2) +7. [SSL con Certbot](#ssl-con-certbot) +8. [Verificación Post-Despliegue](#verificación-post-despliegue) +9. [Rollback](#rollback) + +--- + +## Requisitos del Servidor + +- Ubuntu 22.04 LTS (recomendado) +- 2GB RAM mínimo +- Node.js 18+ +- PostgreSQL 13+ +- Nginx +- PM2 (opcional pero recomendado) +- Git + +--- + +## Despliegue de Base de Datos + +### 1. Instalar PostgreSQL + +```bash +sudo apt update +sudo apt install postgresql postgresql-contrib +sudo systemctl enable postgresql +``` + +### 2. Crear Base de Datos y Usuario + +```bash +sudo -u postgres psql +``` + +```sql +CREATE DATABASE hotel_hacienda; +CREATE USER hoteluser WITH ENCRYPTED PASSWORD 'password_seguro'; +GRANT ALL PRIVILEGES ON DATABASE hotel_hacienda TO hoteluser; +\q +``` + +### 3. Restaurar Backup + +```bash +scp backupcondatos22122025.sql usuario@servidor:/tmp/ +ssh usuario@servidor +sudo -u postgres psql -d hotel_hacienda -f /tmp/backupcondatos22122025.sql +``` + +### 4. Verificar Funciones + +```bash +sudo -u postgres psql -d hotel_hacienda -c "\df" +``` + +--- + +## Despliegue del Backend + +### 1. Clonar Repositorio + +```bash +cd /var/www +git clone https://git.consultoria-as.com/consultoria-as/hotel-hacienda.git +cd hotel-hacienda/backend/hotel_hacienda +``` + +### 2. Instalar Dependencias + +```bash +npm install --production +``` + +### 3. Configurar Variables de Entorno + +```bash +cp .env.example .env +nano .env +``` + +```env +PORT=3000 +URL_CORS=https://hotel.consultoria-as.com + +DB_HOST=localhost +DB_PORT=5432 +DB_USER=hoteluser +DB_PASSWORD=password_seguro +DB_NAME=hotel_hacienda + +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USER=soporte@horuxfin.com +EMAIL_PASS=app_password + +STRIPE_SECRET_KEY=sk_live_... + +FACTURAS_API_URL=https://api.facturas.com/v1/ +FACTURAS_ISSUER_RFC=RFC_DEL_HOTEL +FACTURAS_TYPE=I +FACTURAS_API_TOKEN=token_aqui + +BANXICO_TOKEN=token_banxico +``` + +### 4. Iniciar con PM2 + +```bash +npm install -g pm2 +pm2 start src/server.js --name hotel-api +pm2 save +pm2 startup +``` + +--- + +## Despliegue del Frontend + +### 1. Compilar para Producción + +```bash +cd /var/www/hotel-hacienda/frontend/Frontend-Hotel +npm install +``` + +### 2. Configurar Environment de Producción + +```bash +echo "VITE_API_BASE_URL=https://hotel.consultoria-as.com/api" > .env +``` + +### 3. Build + +```bash +npm run build +``` + +Esto genera la carpeta `dist/` con los archivos estáticos. + +--- + +## Configuración de Proxy Inverso (Nginx) + +### 1. Instalar Nginx + +```bash +sudo apt install nginx +``` + +### 2. Configuración del Sitio + +```bash +sudo nano /etc/nginx/sites-available/hotel-hacienda +``` + +```nginx +server { + listen 80; + server_name hotel.consultoria-as.com; + + # Frontend estático + location / { + root /var/www/hotel-hacienda/frontend/Frontend-Hotel/dist; + index index.html; + try_files $uri $uri/ /index.html; + } + + # Backend API + location /api/ { + proxy_pass http://localhost:3000/api/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } + + # Logs + access_log /var/log/nginx/hotel-access.log; + error_log /var/log/nginx/hotel-error.log; +} +``` + +### 3. Activar Sitio + +```bash +sudo ln -s /etc/nginx/sites-available/hotel-hacienda /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl restart nginx +``` + +--- + +## SSL con Certbot + +```bash +sudo apt install certbot python3-certbot-nginx +sudo certbot --nginx -d hotel.consultoria-as.com +sudo systemctl enable certbot.timer +``` + +--- + +## Verificación Post-Despliegue + +### Checklist + +- [ ] Backend responde: `curl https://hotel.consultoria-as.com/api/employees` +- [ ] Frontend carga sin errores en navegador +- [ ] Login funciona correctamente +- [ ] Base de datos tiene datos correctos +- [ ] Emails se envían (probar recuperación de contraseña) +- [ ] CORS permite el dominio correcto +- [ ] PM2 muestra el proceso activo: `pm2 list` +- [ ] Nginx no muestra errores: `sudo tail -f /var/log/nginx/hotel-error.log` + +--- + +## Rollback + +### Backend + +```bash +pm2 stop hotel-api +# Restaurar código anterior con git +git checkout +npm install +pm2 restart hotel-api +``` + +### Base de Datos + +```bash +# Restaurar backup previo +sudo -u postgres pg_dump hotel_hacienda > backup_antes_del_cambio.sql +sudo -u postgres psql -d hotel_hacienda -f backup_seguro.sql +``` + +### Frontend + +```bash +# Recompilar versión anterior +git checkout +cd frontend/Frontend-Hotel +npm run build +``` + +--- + +> **Nota:** Mantener siempre un backup reciente de la base de datos antes de cualquier despliegue que modifique funciones SQL. diff --git a/docs/FRONTEND.md b/docs/FRONTEND.md new file mode 100644 index 0000000..96bf1de --- /dev/null +++ b/docs/FRONTEND.md @@ -0,0 +1,431 @@ +# 🎨 Documentación del Frontend — Frontend-Hotel + +## Índice +1. [Introducción](#introducción) +2. [Estructura de Carpetas](#estructura-de-carpetas) +3. [Entry Points](#entry-points) +4. [Enrutamiento](#enrutamiento) +5. [Manejo de Estado Global](#manejo-de-estado-global) +6. [Sistema de Permisos (Roles)](#sistema-de-permisos-roles) +7. [Comunicación con el Backend](#comunicación-con-el-backend) +8. [Componentes Reutilizables](#componentes-reutilizables) +9. [Estilos](#estilos) +10. [Páginas por Dominio](#páginas-por-dominio) +11. [Guía para Agregar una Nueva Página](#guía-para-agregar-una-nueva-página) +12. [Bugs y Limitaciones Conocidas](#bugs-y-limitaciones-conocidas) + +--- + +## Introducción + +El frontend es una **Single Page Application (SPA)** construida con **React 19**, **Vite** y **React Router DOM**. Es una aplicación cliente que consume la API REST del backend. + +**Tecnologías clave:** +- React 19.1.1 (JSX, JavaScript puro, sin TypeScript) +- Vite 7.1.2 con SWC para compilación rápida +- React Router DOM 7.8.2 (BrowserRouter) +- Estado global: React Context API +- HTTP: mezcla de `fetch` nativo y `axios` +- Estilos: Tailwind CSS + Bootstrap 5 + CSS puro + +--- + +## Estructura de Carpetas + +``` +Frontend-Hotel/ +├── index.html # Entry HTML (título: "Hacienda San Angel") +├── vite.config.js # Config Vite (puerto 5172) +├── tailwind.config.cjs # Config Tailwind +├── postcss-config.js # PostCSS con Tailwind + Autoprefixer +├── eslint.config.js # ESLint 9 flat config +├── public/ +│ ├── logoHotel.png +│ ├── IconoHotel.svg +│ └── ... +├── src/ +│ ├── main.jsx # Entry point React +│ ├── App.jsx # Definición central de rutas +│ ├── index.css # Tailwind directives + estilos base +│ ├── App.css +│ ├── constants/ +│ │ └── menuconfig.js # Configuración del menú de navegación +│ ├── context/ +│ │ ├── AuthContext.jsx # Estado de autenticación +│ │ └── LenguageContext.jsx # Estado de idioma (EN/ES) +│ ├── routes/ +│ │ └── ProtectedRoute.jsx # Ruta protegida (NO usada en App.jsx) +│ ├── services/ +│ │ ├── api.js # Instancia axios (baseURL desde env) +│ │ ├── employeeService.js +│ │ ├── incomeService.js +│ │ ├── userService.js +│ │ └── contractService.js +│ ├── styles/ +│ │ ├── global.css +│ │ ├── Dashboard.css +│ │ ├── Login.css +│ │ ├── Layout.css +│ │ └── Sidebar.css +│ ├── assets/ +│ │ └── pages/login.jsx +│ ├── components/ +│ │ ├── Layout2.jsx # Layout activo (Sidebar + Topbar + Outlet) +│ │ ├── Layout.jsx # Layout antiguo (comentado) +│ │ ├── Sidebar.jsx +│ │ ├── topbar/Topbar.jsx +│ │ ├── Navbar/Navbar.jsx +│ │ ├── Table/HotelTable.jsx +│ │ ├── ExcelExportButton.jsx +│ │ ├── SummaryCard.jsx +│ │ ├── FormInput.jsx +│ │ ├── FormSelect.jsx +│ │ ├── Switch.jsx +│ │ ├── Modals/ +│ │ ├── Buttons/ +│ │ ├── Filters/ +│ │ └── Inputs/ +│ └── pages/ # ~50+ páginas organizadas por dominio +│ ├── Login.jsx +│ ├── Dashboard.jsx +│ ├── Dashboard/ +│ ├── Expenses/ +│ ├── ExpensesToBeApproval/ +│ ├── Inventory/ +│ ├── Hotel/ +│ ├── Income/ +│ ├── Payroll/ +│ ├── Settings/ +│ └── ... +``` + +--- + +## Entry Points + +### `index.html` + +Carga la fuente Roboto desde Google Fonts y el script `src/main.jsx`. + +### `src/main.jsx` + +```jsx +import React from "react"; +import ReactDOM from "react-dom/client"; +import { BrowserRouter } from "react-router-dom"; +import { AuthProvider } from "./context/AuthContext"; +import { LangProvider } from "./context/LenguageContext"; +import App from "./App.jsx"; + +ReactDOM.createRoot(document.getElementById("root")).render( + + + + + + + +); +``` + +**Jerarquía de providers:** +1. `AuthProvider` — disponible en toda la app +2. `LangProvider` — depende de `AuthContext` (lee el rol para forzar español a housekeepers) +3. `BrowserRouter` — enrutamiento + +--- + +## Enrutamiento + +### `src/App.jsx` + +Define todas las rutas de la aplicación. No usa `ProtectedRoute`. Todas las rutas internas están bajo `/app` y renderizan `` (que es `Layout2.jsx`). + +```jsx +} /> +}> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + {/* ... ~40 rutas más */} + +``` + +**Convenciones de rutas:** +- Páginas de listado: plural simple (`/products`, `/payroll`) +- Páginas de creación: prefijo `new-` (`/new-product`, `/new-expense`) +- Páginas de edición: `:id` como parámetro (`/expenses/edit/:id`, `/payroll/employee/:id`) +- Detalles: segmento fijo + `:id` (`/expenses/:id`, `/properties/:id`) + +--- + +## Manejo de Estado Global + +### AuthContext (`src/context/AuthContext.jsx`) + +```jsx +export const AuthContext = createContext(); + +function AuthProvider({ children }) { + const [user, setUser] = useState(null); // rol numérico + const [userData, setUserData] = useState(null); + + useEffect(() => { + const savedUser = localStorage.getItem("rol"); + if (savedUser) setUser(JSON.parse(savedUser)); + }, []); + + const login = (userData, data) => { + setUserData(data); + setUser(userData); + localStorage.setItem("rol", JSON.stringify(userData)); + }; + + const logout = () => { + setUser(null); + localStorage.removeItem("rol"); + }; + + return ( + + {children} + + ); +} +``` + +**Observaciones:** +- Guarda el rol como número en `localStorage` bajo la clave `"rol"`. +- **NO exporta `useAuth`**, lo cual es un bug conocido. +- No usa JWT ni tokens. + +### LangContext (`src/context/LenguageContext.jsx`) + +```jsx +export const langContext = createContext(); + +export const LangProvider = ({ children }) => { + const authContext = useContext(AuthContext); + const user = authContext?.user || null; + + const [lang, setLang] = useState(user === 6 ? "es" : "en"); + + const toggleLang = (event) => setLang(event.target.value); + + return ( + + {children} + + ); +}; + +export const useLang = () => useContext(langContext); +``` + +**Observaciones:** +- Forza español si `user === 6` (Housekeeper). +- No persiste en localStorage. +- Se accede con `useContext(langContext)` o `useLang()`. + +--- + +## Sistema de Permisos (Roles) + +Los permisos son **numéricos y hardcodeados** en `Layout2.jsx`. No hay backend de ACL. + +### Roles Definidos + +| Rol | Acceso | +|-----|--------| +| 1 | Admin — Todo | +| 2 | Supervisor limitado — Dashboards, Expenses (Report/Monthly Report), Payroll (Report/Attendance/Employees/Contracts), Expenses to be approved | +| 3-4 | Rangos intermedios (según lógica de rangos) | +| 5 | Compras — Solo: New Expense, Purchase Entries, New Suppliers | +| 6 | Housekeeper — Solo Outcomes, forzado a español | + +### Lógica de Permisos en `Layout2.jsx` + +```javascript +const menuConfigWithPermissions = Object.values(menuConfig).map(section => ({ + ...section, + hidden: + section.label === "Dashboards" ? (user >= 1 && user <= 2 ? false : true) : + section.label === "Expenses to be approved" ? (user === 1 || user === 2 ? false : true) : + section.label === "Expenses" ? (user >= 1 && user <= 5 ? false : true) : + section.label === "Inventory" ? (user >= 1 && user <= 5 ? false : true) : + section.label === "Payroll" ? (user >= 1 && user <= 4 ? false : true) : + section.label === "Hotel" ? (user === 1 ? false : true) : + section.label === "Income" ? (user >= 1 && user <= 4 ? false : true) : + section.label === "Housekeeper" ? (user === 6 ? false : true) : + false, + // ... lógica de submenús +})); +``` + +**Regla:** Si `hidden === true`, la sección no aparece en el sidebar. + +--- + +## Comunicación con el Backend + +### Variable de Entorno + +```env +VITE_API_BASE_URL=http://localhost:4000/api +``` + +Se accede en el frontend como: +```javascript +import.meta.env.VITE_API_BASE_URL +``` + +### Instancia Axios (`src/services/api.js`) + +```javascript +import axios from "axios"; + +const api = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL, + timeout: 15000, +}); + +export default api; +``` + +**Problema:** Esta instancia está infrautilizada. Muchas páginas usan `fetch` directo o axios sin la instancia base. + +### Patrón Fetch (más común) + +```javascript +fetch(import.meta.env.VITE_API_BASE_URL + '/purchases/getpurchases') + .then(res => res.json()) + .then(data => setState(data)) + .catch(err => console.error(err)); +``` + +### Patrón Axios + +```javascript +axios.put(`${import.meta.env.VITE_API_BASE_URL}/purchases/entry/${id}`, { + checking: parseInt(checking) +}) +.then(res => { ... }) +.catch(err => { ... }); +``` + +**Problemas conocidos:** +- Varios archivos en `services/` tienen URLs hardcodeadas a `localhost:3000` o `localhost:4000`. +- No hay interceptores para manejo centralizado de errores. +- No se envían headers de autorización. + +--- + +## Componentes Reutilizables + +### `HotelTable.jsx` (`src/components/Table/`) + +Tabla genérica que recibe: +- `columns`: array de `{ header, key, render?, headerStyle? }` +- `data`: array de objetos + +```jsx + } + ]} + data={products} +/> +``` + +### `ExcelExportButton.jsx` + +Exporta datos a `.xlsx` usando la librería `xlsx`. + +### `SummaryCard.jsx` + +Card de resumen con indicador de loading. + +### `FormInput.jsx` / `FormSelect.jsx` + +Wrappers básicos de inputs HTML con estilos consistentes. + +### Modales (`src/components/Modals/`) + +- `ConfirmationModal.jsx` — Confirmación genérica +- `DiscardConfirmModal.jsx` — Confirmación de descarte +- `ConfirmationMontlyPay.jsx` — Confirmación de pago mensual +- `ConfirmationOutcome.jsx` — Confirmación de salida + +--- + +## Estilos + +### Mezcla de Tecnologías + +El proyecto usa tres sistemas de estilos simultáneamente: + +1. **Tailwind CSS** — Configurado pero usado esporádicamente. +2. **Bootstrap 5** — Clases como `btn btn-primary`, `btn btn-secondary`. +3. **CSS Puro** — Un archivo `.css` por página/componente. + +### Convención Actual + +Cada página tiene su propio archivo CSS: +``` +src/pages/Expenses/NewExpense.css +src/pages/Inventory/Products.css +src/components/Table/Table.css +``` + +### Fuente + +Roboto cargada desde Google Fonts en `index.html`. + +--- + +## Páginas por Dominio + +| Dominio | Páginas | Ruta Base | +|---------|---------|-----------| +| **Dashboards** | Income, HotelPL, RestaurantPL, RoomAnalysis, RestaurantAnalysis, Budget, CostPerRoom, Expenses | `/app/income`, `/app/hotelpl`... | +| **Income** | NewIncome, IncomeReport | `/app/new-income-form`, `/app/new-income-report` | +| **Expenses to Approve** | PendingApproval, Approved, Rejected | `/app/pending-approval`... | +| **Expenses** | NewExpense, EditExpense, ExpenseDetail, ReportExpense, Payments, MonthlyPayments, MonthlyReport, NewMonthlyPayment, NewSuppliers, PurchaseEntries | `/app/new-expense`... | +| **Inventory** | Products, NewProduct, AlterProduct, InventoryReport, Adjustments, Outcomes, HousekeeperOutcomes, DiscardProduct | `/app/products`... | +| **Hotel** | Properties, PropertiesId | `/app/properties`... | +| **Payroll** | Payroll, Plantillapayroll, EditPayroll, PayrollEmployees, NewEmployee, PayrollAttendance, PayrollContract, ContractsDetail | `/app/payroll`... | +| **Settings** | Settings, SettingsId, RoomsManagement | `/app/settings`... | + +--- + +## Guía para Agregar una Nueva Página + +1. **Crear el componente** en `src/pages/[Dominio]/NombrePagina.jsx`. +2. **Crear los estilos** opcionales en `src/pages/[Dominio]/NombrePagina.css`. +3. **Agregar la ruta** en `src/App.jsx` dentro de `}>`. +4. **Agregar al menú** (si aplica): + - Editar `src/constants/menuconfig.js` + - Agregar la ruta a la sección correspondiente +5. **Actualizar permisos** en `src/components/Layout2.jsx`: + - Agregar lógica de `hidden` para la sección/submenú + - Agregar detección de ruta en `activeSection` si la ruta no sigue el patrón estándar +6. **Agregar traducciones** EN/ES si es texto visible. +7. **Consumir la API** usando `import.meta.env.VITE_API_BASE_URL`. + +--- + +## Bugs y Limitaciones Conocidas + +1. **`useAuth` no existe** — `AuthContext.jsx` no exporta `useAuth`, pero `ProtectedRoute.jsx` y `Navbar.jsx` lo intentan importar. +2. **No hay rutas protegidas** — `App.jsx` no usa `ProtectedRoute`. Cualquiera puede acceder a `/app/*`. +3. **URLs hardcodeadas** — Varios `*Service.js` apuntan a `localhost` en vez de usar `VITE_API_BASE_URL`. +4. **Código comentado masivo** — Especialmente en `Layout.jsx` y páginas grandes. +5. **`multer` en frontend** — Es un middleware de Node.js, inapropiado para React. +6. **Sin tests** — No hay Jest, Vitest, ni Playwright configurados. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md new file mode 100644 index 0000000..c93e838 --- /dev/null +++ b/docs/TROUBLESHOOTING.md @@ -0,0 +1,217 @@ +# 🔧 Solución de Problemas Comunes + +## Índice +1. [Backend](#backend) +2. [Frontend](#frontend) +3. [Base de Datos](#base-de-datos) +4. [Integraciones Externas](#integraciones-externas) +5. [Producción](#producción) + +--- + +## Backend + +### Error: "Conectado a PostgreSQL" no aparece + +**Causa:** Variables de entorno de DB incorrectas o PostgreSQL no está corriendo. + +**Solución:** +```bash +# Verificar PostgreSQL +sudo systemctl status postgresql + +# Verificar variables de entorno +cat backend/hotel_hacienda/.env | grep DB_ + +# Probar conexión manual +node -e "require('./backend/hotel_hacienda/src/db/connection')" +``` + +### Error: CORS bloquea peticiones del frontend + +**Causa:** `URL_CORS` no coincide con el dominio del frontend. + +**Solución:** +```bash +# En backend/.env +URL_CORS=https://hotel.consultoria-as.com +# Sin slash al final +``` + +### Error: "Cannot find module 'pg'" + +**Causa:** Dependencias no instaladas. + +**Solución:** +```bash +cd backend/hotel_hacienda +npm install +``` + +### Emails no se envían + +**Causa:** Credenciales SMTP incorrectas o servidor bloqueando. + +**Solución:** +1. Verificar variables `EMAIL_HOST`, `EMAIL_PORT`, `EMAIL_USER`, `EMAIL_PASS` +2. Para Gmail, usar "App Password", no la contraseña normal +3. Revisar logs: `pm2 logs hotel-api` + +--- + +## Frontend + +### Error: "Failed to fetch" o "Network Error" + +**Causa:** `VITE_API_BASE_URL` apunta a localhost en producción. + +**Solución:** +```bash +# Verificar .env del frontend +cat frontend/Frontend-Hotel/.env + +# Debe ser: +VITE_API_BASE_URL=https://hotel.consultoria-as.com/api +``` + +### Página en blanco después del build + +**Causa:** Rutas de React Router no configuradas en Nginx. + +**Solución:** Asegurar que Nginx tenga: +```nginx +location / { + try_files $uri $uri/ /index.html; +} +``` + +### Error: `useAuth` is not exported + +**Causa:** Bug conocido. `AuthContext.jsx` no exporta `useAuth`. + +**Solución temporal:** +```jsx +// En vez de: +import { useAuth } from "../context/AuthContext"; + +// Usar: +import { AuthContext } from "../context/AuthContext"; +const { user, login, logout } = useContext(AuthContext); +``` + +### Tailwind no aplica estilos + +**Causa:** Tailwind v4 usa configuración diferente o `index.css` no tiene las directivas. + +**Solución:** Verificar que `src/index.css` contenga: +```css +@tailwind base; +@tailwind components; +@tailwind utilities; +``` + +--- + +## Base de Datos + +### Error: "function newexpensev2 does not exist" + +**Causa:** Backup no restaurado completamente o función no migrada. + +**Solución:** +```bash +# Restaurar backup completo +sudo -u postgres psql -d hotel_hacienda -f backupcondatos22122025.sql + +# Verificar funciones existentes +sudo -u postgres psql -d hotel_hacienda -c "\df public.*" +``` + +### Error: "duplicate key value violates unique constraint" + +**Causa:** Algunas funciones SQL no son idempotentes y se llaman dos veces. + +**Solución:** Revisar la función SQL correspondiente. Agregar `IF EXISTS` o `ON CONFLICT`. + +### Stock duplicado en inventario + +**Causa:** Dos procesos tocan el mismo stock (ver análisis técnico en `DOCUMENTACION_TECNICA.md`, sección "Problema de duplicación en inventario"). + +**Solución:** Decidir si el stock se actualiza en `purchaseentry()` o en `setpaymentstatusv2()`, no en ambos. + +--- + +## Integraciones Externas + +### Stripe no devuelve datos + +**Causa:** `STRIPE_SECRET_KEY` incorrecta o modo live/test mezclado. + +**Solución:** +```bash +# Verificar que la key comience con sk_test_ o sk_live_ +cat backend/.env | grep STRIPE +``` + +### API de Facturas devuelve error + +**Causa:** Token expirado, RFC incorrecto, o fechas fuera de rango. + +**Solución:** +1. Verificar `FACTURAS_API_TOKEN` y `FACTURAS_ISSUER_RFC` +2. Verificar que el rango de fechas no exceda el límite de la API +3. Revisar logs del backend + +### Banxico retorna error + +**Causa:** Token inválido o serie incorrecta. + +**Solución:** +1. Verificar `BANXICO_TOKEN` +2. La serie usada es `SF43718` (Fix de USD/MXN) +3. Verificar que el token no haya expirado + +--- + +## Producción + +### PM2 no inicia al reiniciar servidor + +**Solución:** +```bash +pm2 startup +pm2 save +``` + +### Nginx error 502 Bad Gateway + +**Causa:** Backend no está corriendo o escucha en puerto diferente. + +**Solución:** +```bash +# Verificar proceso +pm2 list + +# Verificar puerto +sudo ss -tlnp | grep 3000 + +# Reiniciar +pm2 restart hotel-api +sudo systemctl restart nginx +``` + +### Certificado SSL expirado + +**Solución:** +```bash +sudo certbot renew --dry-run +sudo certbot renew +sudo systemctl reload nginx +``` + +--- + +> Para problemas no listados aquí, revisar los logs detallados: +> - Backend: `pm2 logs hotel-api` +> - Nginx: `sudo tail -f /var/log/nginx/hotel-error.log` +> - PostgreSQL: `sudo tail -f /var/log/postgresql/postgresql-*.log`