- 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
792 lines
28 KiB
Markdown
792 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()`
|
|
|
|
#### 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.
|