feat: consolidación completa del proyecto + documentación técnica inicial

- Incluye backend API (Node.js + Express + PostgreSQL)
- Incluye frontend SPA (React 19 + Vite)
- Documentación técnica completa del sistema
- Configuración de entornos y variables de ejemplo
This commit is contained in:
Consultoria AS
2026-06-09 16:13:11 -07:00
parent f6452aae72
commit f284a5ca27
185 changed files with 2249 additions and 0 deletions

791
DOCUMENTACION_TECNICA.md Normal file
View File

@@ -0,0 +1,791 @@
# 📘 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()`
#### 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.

BIN
backend/hotel_hacienda.zip Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

BIN
frontend/Frontend-Hotel.zip Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More