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:
791
DOCUMENTACION_TECNICA.md
Normal file
791
DOCUMENTACION_TECNICA.md
Normal 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.
|
||||
Reference in New Issue
Block a user