Files
hotel-hacienda/DOCUMENTACION_TECNICA.md
Consultoria AS 9fa6561659 docs: actualizar documentacion con cambios recientes
- Flujo de inventario corregido
- Ordenamiento de report_inventoryv2
- Historial de posicion/area en contratos
- CHANGELOG actualizado a version 1.0.1
2026-07-28 06:21:07 -07:00

794 lines
28 KiB
Markdown

# 📘 DOCUMENTACIÓN TÉCNICA — Sistema Hotel Hacienda San Angel
> **Versión:** 1.0
> **Fecha:** 2026-06-09
> **Proyecto:** `/home/Hotel`
> **Autor:** Análisis automático del codebase
---
## 1. RESUMEN EJECUTIVO
Este es un **sistema administrativo financiero y operativo** para un hotel ("Hacienda San Angel"). Gestiona:
- **Ingresos** (Little Hotelier, Stripe, facturas electrónicas, Horux)
- **Gastos** (aprovisionamiento, pagos mensuales, aprobaciones)
- **Nómina** (empleados, contratos, asistencia)
- **Inventario** (productos, ajustes, salidas, descartes)
- **P&L** (Hotel y Restaurante)
- **Configuraciones** (habitaciones, propiedades, catálogos)
**Arquitectura:** Monolito clásico. Backend liviano en Express que **delega casi toda la lógica de negocio a PostgreSQL mediante funciones SQL**. Frontend SPA en React con Vite.
**⚠️ Advertencia crítica:** No hay ORM. Los controllers solo orquestan llamadas a funciones SQL. Si modificas algo en el backend sin conocer la función PostgreSQL correspondiente, romperás el sistema.
---
## 2. STACK TECNOLÓGICO
### Backend (`/home/Hotel/backend/hotel_hacienda/`)
| Capa | Tecnología | Versión |
|------|-----------|---------|
| Runtime | Node.js | — |
| Framework | Express.js | ^5.1.0 |
| Base de datos | PostgreSQL | — |
| Driver DB | `pg` (node-postgres) | ^8.16.3 |
| ORM | **NINGUNO** | — |
| HTTP client | Axios | ^1.13.2 |
| Email | Nodemailer | ^7.0.12 |
| Pagos | Stripe SDK | ^20.1.0 |
| Excel/CSV | `xlsx`, `csv-parser` | ^0.18.5, ^3.2.0 |
| Validación | `express-validator` | ^7.2.1 |
| Dev | Nodemon | ^3.1.10 |
### Frontend (`/home/Hotel/frontend/Frontend-Hotel/`)
| Capa | Tecnología | Versión |
|------|-----------|---------|
| Framework | React | ^19.1.1 |
| Build tool | Vite | ^7.1.2 |
| Router | `react-router-dom` | ^7.8.2 |
| Estilos | Tailwind CSS + Bootstrap 5 + CSS puro | ^4.1.13, ^5.3.8 |
| Forms | `react-hook-form` + Yup | ^7.66.1, ^1.7.1 |
| HTTP | Axios + `fetch` nativo | ^1.11.0 |
| Estado global | React Context API | — |
| Excel export | `xlsx` | ^0.18.5 |
| Íconos | `react-icons` | ^5.5.0 |
---
## 3. ESTRUCTURA DE CARPETAS
### Backend
```
hotel_hacienda/
├── index.js # Solo requiere src/server.js
├── src/
│ ├── server.js # Levanta servidor en PORT
│ ├── app.js # Config Express, CORS, monta rutas /api/*
│ ├── db/
│ │ └── connection.js # Pool de PostgreSQL (pg)
│ ├── middlewares/
│ │ ├── handleValidation.js
│ │ └── validators.js # Solo valida login por ahora
│ ├── routes/ # 16 archivos de rutas
│ ├── controllers/ # 18 archivos de controllers
│ └── services/
│ └── mailService.js # Transporte Nodemailer
```
### Frontend
```
Frontend-Hotel/
├── index.html
├── vite.config.js
├── src/
│ ├── main.jsx # Entry point (AuthProvider > LangProvider > BrowserRouter)
│ ├── App.jsx # Definición de todas las rutas
│ ├── index.css # Tailwind directives + estilos base
│ ├── constants/
│ │ └── menuconfig.js # Menú de navegación con permisos
│ ├── context/
│ │ ├── AuthContext.jsx # Estado de usuario (rol en localStorage)
│ │ └── LenguageContext.jsx # Idioma EN/ES
│ ├── components/
│ │ ├── Layout2.jsx # Layout activo (Sidebar + Topbar + Outlet)
│ │ ├── Sidebar.jsx
│ │ ├── Table/
│ │ │ └── HotelTable.jsx
│ │ ├── ExcelExportButton.jsx
│ │ ├── SummaryCard.jsx
│ │ ├── Modals/ # Confirmaciones genéricas
│ │ └── ...
│ ├── pages/ # ~50+ páginas por dominio
│ │ ├── Login.jsx
│ │ ├── Dashboard/
│ │ ├── Expenses/
│ │ ├── Inventory/
│ │ ├── Payroll/
│ │ ├── Income/
│ │ ├── Settings/
│ │ └── ...
│ ├── services/
│ │ ├── api.js # Instancia axios (infrautilizada)
│ │ └── ...Service.js # Algunos con URLs hardcodeadas a localhost
│ └── styles/ # CSS puro por página
```
---
## 4. BASE DE DATOS — EL CORAZÓN DEL SISTEMA
**⚠️ ESTO ES LO MÁS IMPORTANTE:** El backend NO tiene lógica de negocio en JavaScript. Los controllers llaman directamente a **funciones SQL de PostgreSQL**.
### Patrón general de un controller
```javascript
const pool = require('../db/connection');
const algunaFuncion = async (req, res) => {
try {
const { param1, param2 } = req.body;
const result = await pool.query(
'SELECT nombrefuncionsql($1, $2) AS status',
[param1, param2]
);
const status = result.rows[0].status;
res.json({ message: 'OK', status });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Error' });
}
};
```
### Conexión a PostgreSQL
```javascript
// src/db/connection.js
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
});
```
### Funciones SQL conocidas (mapeo por dominio)
> **Nota:** Esta lista se deriva de las llamadas en controllers. Si necesitas modificar lógica de negocio, DEBES revisar primero la función en PostgreSQL.
#### Auth
- `validarusuario(name_mail_user, user_pass)` → Devuelve `{status, rol, user_id, user_name}`
- `createuser(name_user, id_rol, email, user_pass)` → Devuelve `status`
- `reppassuser(user_mail, new_pass)` → Devuelve `status`
#### Empleados
- `getemployees()` → Lista con paginación manual (LIMIT/OFFSET)
- `activeemployeesnumber()` → Total de activos
- `getoneemployee(rfcEmployee)` → Un empleado
- `newemployee(...12 params...)` → Inserta empleado
- `updateemployee(...12 params...)` → Actualiza empleado
- `getattendance()` → Registros de asistencia
#### Contratos
- `getcontracts()`, `getinfocontract(id)`, `neartoend()`
- `newcontract(...)`, `updatecontract(...)`
- `positions()`, `areas()`, `bosses()`
- `reportempcontract()`, `disabledcontract()`
> **Nota (2026-06-25):** La tabla `contracts` fue extendida con `id_position` e `id_area` para historizar el puesto y área del empleado al momento del contrato. Esto evita que al actualizar la posición actual del empleado se modifiquen visualmente los contratos históricos.
#### Productos / Inventario
- `getproducts()`, `newproduct(...)`, `update_product(...)`
- `productcategory()`, `producttype()`, `suppliers()`
- `stockadjusment()`, `stockadjusmentset(...)`
- `discardproduct(...)`, `gdiscardproducts()`
- `newconsumptionstock(...)`, `getconsumptionstockreport()`
- `newsuplier(...)`, `updatesupplier(...)`
#### Gastos
- `pendingapproval()`, `approvedexpenses()`, `rejectedexpenses()`
- `newexpense(...)`, `updateexpense(...)`
- `mainsupplier()`, `getexpense(id)`, `gettaxes()`
- `reportexpenses()`, `reportpayments()`, `monthlypayments()`
- `countpending()`, `totalapproved()`
#### Status / Aprobaciones
- `approveupdate(id)`, `paymentupdate(id)`
- `penapppayments()`, `countdelaypay()`, `totalspent()`
#### Pagos Mensuales
- `newexpmonthly(...)`, `refreshmonthly()`
- `paymentstatusmonthly(id)`, `updateexpmonthly(...)`
- `onemothlyexpense(id)`, `needtorefresh(id)`
#### Configuraciones
- `newroom(...)`, `newproperty(...)`
- `reportrooms()`, `reportproperties()`
- `approveby()`, `requestby()`, `categoryexpense()`
- `currency()`, `units()`, `recurrence()`
#### Emails (envío limitado por tabla `endpoint_logs`)
- `validateendpoint(endpoint_name)` → Controla ejecución 1 vez por mes
- `nearexpiring()`, `paymentdelay()`, `expensesneartodeadline()`
- `expensesspecial()`, `birthdays()`, `contractexpired()`
- `expiredcontractsmonth()`
#### Ingresos (Little Hotelier + Horux)
- `getincomes(...)`, `totalincomes(...)`, `channelscards(...)`
- `loadincomes(...)`, `loadproductsales(...)`, `loadchequesdetalle(...)`
- `reportincomes(...)`, `countticket(...)`, `efectivo(...)`, `otros(...)`
- `propinas(...)`, `tarjeta(...)`, `vales(...)`, `sumatotal(...)`, `ticketpromedio(...)`
#### Horux / Facturas / Stripe
- `addhoruxdata(jsonb)` → Inserta facturas masivamente
- `getcategoryincome()`, `getinvoiceincome()`, `getaccountincome()`
- `getincomehorux()`, `gettotalincome()`, `getoneincome(id)`
- `newincome(...)`, `updateincome(...)`
- `addstripedatav2(jsonb)` → Inserta datos de Stripe
#### Hotel P&L / Restaurant P&L
- `cogs(...)`, `ebitda(...)`, `employeeshare(...)`
- `grossprofit(...)`, `tips(...)`, `totalrevenue(...)`
- `weightedCategoriesCost(...)`
#### Tipo de cambio
- `consultexchange(...)`, `getexchanges()`
#### Compras
- `getpurchases()`, `entry(id)`
---
## 5. MAPA COMPLETO DE ENDPOINTS (Backend)
Prefijo base: `/api`
### Auth → `/api/auth`
| Método | Ruta | Controller | Función SQL |
|--------|------|------------|-------------|
| POST | `/login` | `auth.controller.js` | `validarusuario($1,$2)` |
| POST | `/createuser` | `auth.controller.js` | `createuser($1,$2,$3,$4)` |
| POST | `/recoverpass` | `auth.controller.js` | `reppassuser($1,$2)` |
### Empleados → `/api/employees`
| Método | Ruta | Notas |
|--------|------|-------|
| GET | `/` | Paginación manual (`?page=&limit=`) |
| GET | `/activeEmployees` | |
| GET | `/getattendance` | |
| GET | `/gradeofstudy` | Tabla directa `degreeofstudy` |
| GET | `/relationship` | Tabla directa `relationship_employee` |
| POST | `/employee` | Obtiene un empleado por RFC |
| POST | `/newemployee` | 12 parámetros |
| POST | `/updateemployee` | 12 parámetros |
### Contratos → `/api/contracts`
| Método | Ruta | Notas |
|--------|------|-------|
| GET | `/` | |
| GET | `/getinfocontract/:id` | |
| GET | `/neartoend` | |
| GET | `/positions` | |
| GET | `/areas` | |
| GET | `/bosses` | |
| GET | `/reportempcontract` | |
| GET | `/disabledcontract` | |
| POST | `/newcontract` | |
| PUT | `/updatecontract/:id` | |
### Reporte Contratos → `/api/reportcontracts`
| Método | Ruta |
|--------|------|
| GET | `/` |
### Productos → `/api/products`
| Método | Ruta | Notas |
|--------|------|-------|
| GET | `/` | |
| GET | `/productcategory` | |
| GET | `/producttype` | |
| GET | `/suppliers` | |
| GET | `/gdiscardproducts` | |
| GET | `/reportinventory` | |
| GET | `/stockadjusment` | |
| GET | `/gethousekeeper` | |
| GET | `/getproducts` | |
| GET | `/getconsumptionstockreport` | |
| POST | `/newsupplier` | |
| POST | `/newproduct` | |
| POST | `/stockadjusmentset` | |
| POST | `/newconsumptionstock` | |
| POST | `/disableSupplier` | |
| PUT | `/update_product/:id` | |
| PUT | `/discardproduct/:id` | |
| PUT | `/product/:id` | |
| PUT | `updatesupplier/:id` | ⚠️ **Falta `/` inicial en la ruta** |
### Gastos → `/api/expenses`
| Método | Ruta | Notas |
|--------|------|-------|
| GET | `/pendingapproval` | |
| GET | `/approvedexpenses` | |
| GET | `/rejectedexpenses` | |
| GET | `/mainsupplier` | |
| PUT | `/getexpense/:id` | ⚠️ Es PUT pero debería ser GET |
| GET | `/getinfo` | |
| GET | `/reportexpenses` | |
| POST | `/countpending` | |
| GET | `/reportpayments` | |
| GET | `/monthlypayments` | |
| POST | `/newexpense` | |
| POST | `/totalapproved` | |
| PUT | `/updateexpense/:id` | |
| GET | `/gettaxes` | |
### Status → `/api/status`
| Método | Ruta |
|--------|------|
| PUT | `/approveupdate/:id` |
| PUT | `/paymentupdate/:id` |
| GET | `/penapppayments` |
| GET | `/countdelaypay` |
| GET | `/totalspent` |
### Pagos Mensuales → `/api/payment`
| Método | Ruta |
|--------|------|
| POST | `/newexpmonthly` |
| GET | `/refreshmonthly` |
| PUT | `/paymentstatusmonthly/:id` |
| PUT | `/updateexpmonthly/:id` |
| GET | `/onemothlyexpense/:id` |
| PUT | `/needtorefresh/:id` |
### Settings → `/api/settings`
| Método | Ruta |
|--------|------|
| POST | `/newroom` |
| POST | `/newproperty` |
| GET | `/reportrooms` |
| GET | `/reportproperties` |
| GET | `/approveby` |
| GET | `/requestby` |
| GET | `/categoryexpense` |
| GET | `/currency` |
| GET | `/units` |
| GET | `/recurrence` |
### Emails → `/api/emails`
| Método | Ruta | Notas |
|--------|------|-------|
| POST | `/nearexpiring` | Validado 1x/mes por `endpoint_logs` |
| POST | `/paymentdelay` | Validado 1x/mes |
| POST | `/expensesneartodeadline` | Validado 1x/mes |
| POST | `/expensesspecial` | Validado 1x/mes |
| POST | `/birthdays` | Validado 1x/mes |
| POST | `/contractexpired` | Validado 1x/mes |
| POST | `/expiredcontractsmonth` | Validado 1x/mes |
### Ingresos (Little Hotelier) → `/api/incomes`
| Método | Ruta | Notas |
|--------|------|-------|
| POST | `/getincomes` | Recibe filtros de fecha en body |
| POST | `/totalincomes` | |
| POST | `/channelscards` | |
| POST | `/loadincomes` | Carga masiva desde CSV/XLSX |
| POST | `/loadproductsales` | |
| POST | `/loadchequesdetalle` | |
| POST | `/reportincomes` | |
| POST | `/countticket` | |
| POST | `/efectivo` | |
| POST | `/otros` | |
| POST | `/propinas` | |
| POST | `/tarjeta` | |
| POST | `/vales` | |
| POST | `/sumatotal` | |
| POST | `/ticketpromedio` | |
| GET | `/getproductsales` | |
| GET | `/getdetallecheque` | |
### Compras → `/api/purchases`
| Método | Ruta |
|--------|------|
| GET | `/getpurchases` |
| PUT | `/entry/:id` |
### Tipo de Cambio → `/api/exchange`
| Método | Ruta | Notas |
|--------|------|-------|
| POST | `/consultexchange` | Llama API Banxico |
| GET | `/getexchanges` | |
### Hotel P&L → `/api/hotelpl`
| Método | Ruta |
|--------|------|
| POST | `/cogs` |
| POST | `/ebitda` |
| POST | `/employeeshare` |
| POST | `/grossprofit` |
| POST | `/tips` |
| POST | `/totalrevenue` |
| POST | `/weightedCategoriesCost` |
### Restaurant P&L → `/api/restaurantpl`
| Método | Ruta |
|--------|------|
| POST | `/cogs` |
| POST | `/ebitda` |
| POST | `/grossprofit` |
| POST | `/totalrevenue` |
| POST | `/weightedCategoriesCost` |
### Ingresos Horux → `/api/incomeshrx`
| Método | Ruta | Notas |
|--------|------|-------|
| GET | `/accountincome` | |
| GET | `/categoryincome` | |
| GET | `/invoiceIncome` | |
| GET | `/totalIncome` | |
| GET | `/incomehorux` | |
| GET | `/oneincomehorux/:id` | |
| GET | `/stripedata/` | Obtiene transfers de Stripe y las guarda |
| POST | `/stripedatademo/` | Crea transfer demo en Stripe |
| POST | `/insertinvoice` | Descarga facturas de API externa y las inserta |
| POST | `/newincome` | |
| PUT | `/updateincome/:id` | |
---
## 6. INTEGRACIONES EXTERNAS
### 6.1 Banxico (Tipo de cambio USD/MXN)
- **Endpoint:** `https://www.banxico.org.mx/SieAPIRest/service/v1/series/SF43718/datos/...`
- **Token:** `process.env.BANXICO_TOKEN`
- **Uso:** `exchange.controller.js`
### 6.2 Stripe
- **Secret Key:** `process.env.STRIPE_SECRET_KEY`
- **Operaciones:**
- Listar transfers (`stripe.transfers.list()`)
- Insertar en DB mediante `addstripedatav2(jsonb)`
- Crear transfers demo (`stripe.transfers.create()`)
- **Uso:** `incomehrx.controller.js`
### 6.3 API de Facturas (México)
- **URL:** `process.env.FACTURAS_API_URL`
- **Parámetros:** `issuerRfc`, `type`, `initialDate`, `finalDate`
- **Auth:** Bearer token (`FACTURAS_API_TOKEN`)
- **Uso:** Descarga facturas del año actual y las inserta vía `addhoruxdata(jsonb)`
### 6.4 Nodemailer
- **Host:** Configurable por env (`EMAIL_HOST`, `EMAIL_PORT`, `EMAIL_USER`, `EMAIL_PASS`)
- **From:** `soporte@horuxfin.com`
- **Uso:** Recuperación de contraseña, emails programáticos (cumpleaños, contratos por vencer, etc.)
---
## 7. SISTEMA DE AUTENTICACIÓN Y ROLES
### Backend
- **NO usa JWT**. El login devuelve `{rol, user_id, user_name, message}`.
- **NO hay middleware de autorización** en las rutas. Cualquiera puede llamar a cualquier endpoint si conoce la URL.
- La función `validarusuario(name_mail_user, user_pass)` valida contra PostgreSQL.
### Frontend
- El rol se guarda en `localStorage` bajo la clave `"rol"`.
- Los permisos son **numéricos y hardcodeados** en `Layout2.jsx`:
| Rol | Nombre implícito | Acceso |
|-----|-----------------|--------|
| 1 | Admin | Todo |
| 2 | Supervisor limitado | Dashboards, Expenses (solo Report/Monthly Report), Payroll (Report/Attendance/Employees/Contracts), Expenses to be approved |
| 3 | — | Similar a supervisor (según rangos) |
| 4 | — | Payroll, Income |
| 5 | Compras/Proveedores | Solo: New Expense, Purchase Entries, New Suppliers (en Expenses) |
| 6 | Housekeeper | Forzado a español. Solo sección "Housekeeper" → Outcomes |
### Lógica de permisos en `Layout2.jsx`
```javascript
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
```
---
## 8. FLUJOS DE DATOS CRÍTICOS
### 8.1 Carga de Ingresos desde Little Hotelier
1. Se sube archivo CSV o XLSX a `src/resources/littleHotelier/`
2. Endpoints `POST /api/incomes/loadincomes`, `/loadproductsales`, `/loadchequesdetalle`
3. Los controllers leen el archivo, lo parsean y llaman funciones SQL que reciben `jsonb`
4. PostgreSQL procesa e inserta los datos masivamente
### 8.2 Sincronización de Facturas (Horux)
1. Endpoint `POST /api/incomeshrx/insertinvoice`
2. Controller calcula fechas: inicio de año → hoy
3. Llama API externa de facturas con esas fechas
4. Recibe JSON y lo pasa a `addhoruxdata($1::jsonb)`
5. PostgreSQL inserta/actualiza facturas
### 8.3 Sincronización de Stripe
1. Endpoint `GET /api/incomeshrx/stripedata/`
2. Llama `stripe.transfers.list()`
3. Serializa a JSON y pasa a `addstripedatav2($1::jsonb)`
4. PostgreSQL inserta transacciones
### 8.4 Emails Programáticos
1. Los endpoints en `/api/emails/*` tienen lógica de "una ejecución por mes"
2. Usan la función `validateendpoint(endpoint_name)` en PostgreSQL
3. Esta consulta una tabla `endpoint_logs` para ver si ya se ejecutó
4. Si es válido, genera el email (consultando otras funciones SQL) y envía vía Nodemailer
---
## 9. VARIABLES DE ENTORNO
### Backend (`/home/Hotel/backend/hotel_hacienda/.env`)
```env
PORT=3000
URL_CORS=https://tudominio.com # Sin slash al final
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=******
DB_NAME=hotel_hacienda
EMAIL_HOST=smtp.tudominio.com
EMAIL_PORT=587
EMAIL_USER=soporte@horuxfin.com
EMAIL_PASS=******
STRIPE_SECRET_KEY=sk_******
FACTURAS_API_URL=https://api.facturas.com/...
FACTURAS_ISSUER_RFC=RFC_EMISOR
FACTURAS_TYPE=I # o E
FACTURAS_API_TOKEN=token_aqui
BANXICO_TOKEN=token_banxico
```
### Frontend (`/home/Hotel/frontend/Frontend-Hotel/.env`)
```env
VITE_API_BASE_URL=http://localhost:4000/api
```
### Frontend dev server
- Puerto: `5172` (hardcodeado en `vite.config.js`)
- Hosts permitidos: `hacienda.consultoria-as.com`, `hotel.consultoria-as.com`
---
## 10. BUGS Y DEUDA TÉCNICA CONOCIDA
### 🔴 Críticos
1. **Sin autenticación en endpoints backend**
- Cualquiera puede llamar a cualquier API si conoce la URL.
- **Impacto:** Seguridad. No modifiques datos sensibles sin agregar al menos un middleware de API key o JWT.
2. **`useAuth` no existe pero se intenta importar**
- `AuthContext.jsx` NO exporta `useAuth`.
- `ProtectedRoute.jsx` y `Navbar.jsx` intentan importarlo.
- **Impacto:** Crash si se renderizan esos componentes.
3. **Ruta malformada en backend**
- `product.routes.js`: `router.put('updatesupplier/:id', ...)` le falta `/` inicial.
- **Impacto:** Ese endpoint nunca funcionará correctamente.
4. **URLs hardcodeadas en services del frontend**
- `incomeService.js`, `contractService.js`, `userService.js`, `employeeService.js` apuntan a `http://localhost:3000` o `http://localhost:4000`.
- **Impacto:** Rompen en producción.
5. **Método HTTP incorrecto**
- `expense.routes.js`: `PUT /getexpense/:id` debería ser `GET`.
### 🟡 Medios
6. **No hay `ProtectedRoute` activo en `App.jsx`**
- Todas las rutas internas son accesibles sin login.
7. **Mezcla de `fetch` y `axios`**
- No hay estándar. Algunas páginas usan `fetch`, otras `axios`, otras la instancia `api.js`.
8. **Código comentado masivo**
- `Layout.jsx` tiene ~300 líneas comentadas.
- Varios controllers y páginas tienen versiones antiguas comentadas.
- **Impacto:** Dificulta lectura y aumenta bundle size innecesariamente.
9. **`multer` en frontend**
- Es un middleware de Node.js, no tiene sentido en React. Debería estar solo en backend.
10. **Paginación manual en controllers**
- Se hace `COUNT(*)` + `LIMIT/OFFSET` manual en cada controller.
- **Riesgo:** Si dos personas agregan paginación de forma distinta, la API se vuelve inconsistente.
### 🟢 Leves
11. **No hay tests** (ni unitarios ni e2e).
12. **No hay TypeScript** — riesgo de errores de tipo en runtime.
13. **Tailwind instalado pero poco usado** — la mayoría de estilos son CSS puro o Bootstrap.
14. **No hay manejo de estado de servidor** (TanStack Query, SWR) — cada componente maneja su propio `useEffect + fetch`.
---
## 11. REGLAS DE ORO PARA NO ROMPER INTEGRIDAD
### Antes de tocar cualquier archivo, pregúntate:
1. **¿Es un cambio de lógica de negocio?**
- Si SÍ → Revisa la **función SQL correspondiente** en PostgreSQL ANTES de tocar el controller.
- Los controllers solo orquestan. La lógica vive en la base de datos.
2. **¿Es un nuevo endpoint?**
- Sigue el patrón existente: `route``controller``pool.query('SELECT funcion_sql($1)')`.
- Usa queries parametrizadas (`$1`, `$2`) para evitar SQL Injection.
- AGREGA la ruta en `app.js` con el prefijo `/api/`.
3. **¿Es un cambio en el frontend que consume datos?**
- Verifica si la página usa `fetch`, `axios` o la instancia `api.js`.
- Si usas `fetch`, usa `import.meta.env.VITE_API_BASE_URL`.
- NO hardcodees `localhost`.
4. **¿Es un cambio en roles/permisos?**
- Hay DOS lugares donde tocar:
- `frontend/src/components/Layout2.jsx` (permisos del menú sidebar)
- `frontend/src/constants/menuconfig.js` (estructura del menú)
- Si agregas una nueva ruta en `App.jsx`, agrega su lógica de detección en `activeSection` de `Layout2.jsx`.
5. **¿Es un cambio en la base de datos?**
- Si modificas una función SQL, verifica TODOS los controllers que la llaman.
- Si cambias la firma de una función (nuevos parámetros), debes actualizar TODOS los `pool.query(...)` que la usan.
- Los controllers pasan arrays/objetos como `JSON.stringify()` para parámetros `jsonb`.
6. **¿Es un cambio en emails?**
- Los endpoints de email tienen validación de frecuencia (`validateendpoint`).
- Si quieres probar un email múltiples veces, necesitas limpiar la tabla `endpoint_logs` o modificar la función SQL.
7. **¿Es un cambio en integraciones externas (Stripe, Facturas, Banxico)?**
- Verifica las variables de entorno.
- Los endpoints de Stripe y Facturas son destructivos (insertan masivamente).
- Prueba en ambiente de desarrollo primero.
---
## 12. CHECKLIST ANTES DE HACER UN CAMBIO
### Backend
- [ ] ¿Agregué la ruta en `src/app.js` con `app.use('/api/...', ...)`?
- [ ] ¿Usé `pool.query` con parámetros `$1, $2`?
- [ ] ¿Si creé una función SQL nueva, la probé directamente en PostgreSQL?
- [ ] ¿No rompí la firma de una función SQL existente?
- [ ] ¿El método HTTP es coherente? (GET para leer, POST para crear, PUT para actualizar)
- [ ] ¿El response mantiene la estructura esperada por el frontend?
### Frontend
- [ ] ¿Agregué la ruta en `src/App.jsx`?
- [ ] ¿Agregué la entrada en `menuconfig.js` si es una nueva sección?
- [ ] ¿Actualicé `Layout2.jsx` para detectar la nueva ruta en `activeSection`?
- [ ] ¿Usé `import.meta.env.VITE_API_BASE_URL` en vez de `localhost`?
- [ ] ¿Agregué la traducción EN/ES si es texto visible?
- [ ] ¿Verifiqué que el rol apropiado pueda ver la nueva página?
### Base de datos
- [ ] ¿La función SQL compila y ejecuta correctamente?
- [ ] ¿Los tipos de parámetros coinciden con lo que envía el controller?
- [ ] ¿Si modifico una tabla, revisé las funciones que dependen de ella?
---
## 13. GUÍA DE CAMBIOS POR ÁREA
### "Quiero agregar un nuevo campo a un formulario existente"
1. **Frontend:** Modifica la página JSX donde está el formulario.
2. **Backend:** Modifica el controller para recibir el nuevo campo en `req.body`.
3. **Base de datos:** Modifica la función SQL para aceptar el nuevo parámetro.
4. **Verificación:** Busca con `grep` todos los lugares donde se llama esa función SQL.
### "Quiero agregar una nueva página"
1. Crea el componente en `src/pages/[dominio]/Nombre.jsx`.
2. Agrégalo en `src/App.jsx` dentro de `<Route path="/app" element={<Layout />}>`.
3. Si va en el menú:
- Agrégalo en `menuconfig.js` en la sección correspondiente.
- Verifica permisos en `Layout2.jsx`.
- Agrega detección de ruta en `activeSection` de `Layout2.jsx`.
4. Crea el endpoint backend si es necesario.
### "Quiero modificar la lógica de un reporte"
1. **NO modifiques el controller** para cambiar lógica de filtrado/agrupación.
2. Modifica la **función SQL** que genera el reporte.
3. El controller solo pasa parámetros (fechas, filtros) y devuelve lo que PostgreSQL responda.
### "Quiero cambiar quién ve qué"
1. Edita `Layout2.jsx`, líneas ~18-36.
2. Los permisos usan números de rol. Asegúrate de entender qué número corresponde a qué usuario.
3. Si quieres ocultar un submenú específico, usa la lógica de `submenu.map(...)`.
---
## 14. NOTAS DE DESPLIEGUE
### Backend
```bash
cd /home/Hotel/backend/hotel_hacienda
npm install
node src/server.js # o nodemon para dev
```
### Frontend
```bash
cd /home/Hotel/frontend/Frontend-Hotel
npm install
npm run dev # Puerto 5172
npm run build # Genera dist/
```
### Proxy / CORS
- El backend permite CORS desde `URL_CORS` (definido en `.env`).
- Si el frontend y backend están en dominios distintos, asegúrate de que `URL_CORS` incluya el dominio del frontend.
---
## 15. HALLAZGOS ESPECIALES
### JSONB como patrón de intercambio
Muchos endpoints que manejan datos complejos (facturas, Stripe, productos con categorías) usan `JSON.stringify()` en el controller y funciones SQL que aceptan `jsonb`:
```javascript
const categoriesJson = categories ? JSON.stringify(categories) : '[]';
await pool.query('SELECT newincome($1,$2,$3,$4,$5,$6::jsonb)', [...params, categoriesJson]);
```
**Consecuencia:** Si cambias la estructura del JSON en el frontend, DEBES actualizar la función PostgreSQL para parsearla correctamente.
### Paginación manual consistente
El patrón usado es:
```javascript
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 500;
const offset = (page - 1) * limit;
// Query principal con LIMIT $1 OFFSET $2
// Query COUNT(*) para total
// Respuesta: { page, limit, total, totalPages, data }
```
Si agregas paginación a un endpoint nuevo, sigue EXACTAMENTE esta estructura de respuesta para que `HotelTable.jsx` u otros componentes la entiendan.
---
## 16. PRÓXIMOS PASOS RECOMENDADOS (NO URGENTES)
Si el usuario quiere mejorar la salud del proyecto, priorizaría:
1. **Unificar HTTP client:** Estandarizar todo en `api.js` (axios) con interceptores para errores.
2. **Eliminar código comentado:** Especialmente `Layout.jsx` y páginas grandes.
3. **Arreglar `useAuth`:** Exportar un hook `useAuth` desde `AuthContext.jsx`.
4. **Arreglar ruta malformada:** `updatesupplier/:id``/updatesupplier/:id`.
5. **Eliminar `multer` del frontend** y mover lógica de upload al backend si es necesario.
6. **Agregar middleware de autenticación** mínimo (API key o JWT) en rutas sensibles.
7. **Documentar funciones SQL:** Este documento lista las funciones conocidas, pero no sus firmas exactas en PostgreSQL.
---
> **Fin del documento.** Si realizas cambios significativos en el sistema, actualiza este archivo para mantenerlo vivo.