# 📘 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.
