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

359 lines
15 KiB
Markdown

# 🗄️ Documentación de Base de Datos — PostgreSQL
## Índice
1. [Filosofía de Diseño](#filosofía-de-diseño)
2. [Conexión](#conexión)
3. [Funciones SQL por Dominio](#funciones-sql-por-dominio)
4. [Tablas Principales (Inferidas)](#tablas-principales-inferidas)
5. [Patrones de Datos](#patrones-de-datos)
6. [JSONB como Patrón de Intercambio](#jsonb-como-patrón-de-intercambio)
7. [Integraciones que Escriben en JSONB](#integraciones-que-escriben-en-jsonb)
8. [Guía para Modificar Funciones SQL](#guía-para-modificar-funciones-sql)
---
## Filosofía de Diseño
Este sistema sigue un patrón **"Database-First"** donde la lógica de negocio reside principalmente en **funciones SQL almacenadas** de PostgreSQL. Los controllers de Node.js actúan como una capa de presentación HTTP liviana que solo orquesta llamadas a estas funciones.
**Implicaciones:**
- Para modificar lógica de negocio, debes modificar las funciones SQL, no solo el JavaScript.
- Los controllers pasan parámetros posicionales (`$1`, `$2`) y reciben resultados directamente.
- No hay ORM ni capa de abstracción de base de datos.
---
## Conexión
El backend usa `pg` (node-postgres) con un `Pool`:
```javascript
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
});
```
**Variables de entorno requeridas:** `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`.
---
## Funciones SQL por Dominio
### 🔐 Autenticación
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `validarusuario(name_mail_user, user_pass)` | `text`, `text` | `{status, rol, user_id, user_name}` | Valida credenciales |
| `createuser(name_user, id_rol, email, user_pass)` | `text`, `int`, `text`, `text` | `status` | Crea usuario |
| `reppassuser(user_mail, new_pass)` | `text`, `text` | `status` | Reemplaza contraseña |
### 👥 Empleados
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `getemployees()` | — | setof record | Lista paginada de empleados |
| `activeemployeesnumber()` | — | `int` | Total de empleados activos |
| `getoneemployee(rfcEmployee)` | `text` | setof record | Un empleado por RFC |
| `newemployee(...)` | 12 parámetros | `status` | Inserta empleado |
| `updateemployee(...)` | 12 parámetros | `status` | Actualiza empleado |
| `getattendance()` | — | setof record | Registros de asistencia |
### 📄 Contratos
> **Nota (2026-06-25):** La tabla `contracts` ahora incluye las columnas `id_position` e `id_area` para historizar el puesto y área del empleado en el momento del contrato. Las funciones `getcontracts()` y `reportemployeecontract()` muestran estos valores históricos en lugar de la posición/área actual del empleado.
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `getcontracts()` | — | setof record | Lista de contratos con posición/área histórica |
| `getinfocontract(id)` | `int` | setof record | Info de un contrato |
| `neartoend()` | — | setof record | Contratos próximos a vencer |
| `newcontract(...)` | varios | `status` | Crea contrato guardando `id_position` e `id_area` |
| `updatecontract(...)` | varios | `status` | Actualiza contrato incluyendo `id_position` e `id_area` |
| `reportemployeecontract()` | — | setof record | Reporte de empleados-contratos con posición/área histórica |
| `positions()` | — | setof record | Catálogo de puestos |
| `areas()` | — | setof record | Catálogo de áreas |
| `bosses()` | — | setof record | Catálogo de jefes |
### 📦 Productos / Inventario
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `getproducts()` | — | setof record | Lista de productos |
| `getoneproduct(id)` | `int` | setof record | Un producto |
| `newproduct(..., jsonb, ...)` | 11 parámetros | `status` | Crea producto |
| `updateproduct(..., jsonb, ...)` | 12 parámetros | `status` | Actualiza producto |
| `discardproductsstock(id, quantity, reasons)` | `int`, `numeric`, `text` | `status` | Descarta stock |
| `getdiscardproduct()` | — | setof record | Productos descartados |
| `report_inventoryv2()` | — | setof record | Reporte de inventario |
| `stockadjusment()` | — | setof record | Ajustes pendientes |
| `setstockadjusmentsv2(jsonb)` | `jsonb` | `stockadjus` | Aplica ajustes |
| `consumptionstock(product_id, quantity, date, rfc)` | varios | `status` | Registra consumo |
| `getconsumptionreport()` | — | setof record | Reporte de consumos |
| `newsupplier(name, rfc, mail, phone)` | 4 parámetros | `status` | Crea proveedor |
| `updatesupplier(id, name, rfc, mail, phone)` | 5 parámetros | `status` | Actualiza proveedor |
| `disableSuppliers(id)` | `int` | `status` | Deshabilita proveedor |
| `newsuplier(...)` | varios | `status` | (posible duplicado ortográfico) |
### 💰 Gastos (Expenses)
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `newexpensev2(..., jsonb, ...)` | 14 parámetros | `status` | Crea gasto |
| `updateexpensev4(..., jsonb)` | 15 parámetros | `status` | Actualiza gasto |
| `getexpense(id)` | `int` | setof record | Obtiene un gasto |
| `getpendingappexpenses()` | — | setof record | Gastos pendientes de aprobación |
| `getapprovedappexpenses()` | — | setof record | Gastos aprobados |
| `getrejectedappexpenses()` | — | setof record | Gastos rechazados |
| `setapprovalstatus(id, status, approved_by)` | 3 parámetros | `status` | Actualiza estado de aprobación |
| `setpaymentstatusv2(id, status)` | 2 parámetros | `status` | Actualiza estado de pago |
| `pendingapppayments()` | — | setof record | Pagos pendientes |
| `countdelaypayments()` | — | `int` | Pagos con retraso |
| `totalspent()` | — | `numeric` | Total gastado |
| `totalapproved()` | — | `numeric` | Total aprobado |
| `totalrejected()` | — | `numeric` | Total rechazado |
| `report_expenses()` | — | setof record | Reporte de gastos |
| `paymentsreport()` | — | setof record | Reporte de pagos |
| `mainsupplier()` | — | setof record | Proveedor principal |
| `gettaxes()` | — | setof record | Catálogo de impuestos |
### 🛒 Compras / Purchase Entries
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `getpurchases()` | — | setof record | Detalles de compras pendientes |
| `purchaseentry(id, checking)` | `int`, `int` | `status` | Registra recepción física |
### 💳 Pagos Mensuales
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `newmonthlyexpensev2(...)` | 10 parámetros | `status` | Crea gasto mensual |
| `updatemonthlyexpensev2(...)` | 11 parámetros | `status` | Actualiza gasto mensual |
| `setpaymentstatusmonthlyv2(id, status)` | 2 parámetros | `status` | Actualiza estado de pago |
| `setpaymentstatusmonthlyv2(id, status, tax_id, total)` | 4 parámetros | `status` | Actualiza estado y monto |
| `refresh_monthly_expenses()` | — | `status` | Refresca gastos mensuales |
| `getmonthlypayments()` | — | setof record | Lista de pagos mensuales |
| `getONEmonthlypayment(id)` | `int` | setof record | Un pago mensual |
| `needToRefresh(id, boolean)` | 2 parámetros | rows | Controla actualización automática |
### 📧 Emails Programáticos
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `validateendpoint(endpoint_name)` | `text` | `boolean` | Verifica si se puede ejecutar (1x/mes) |
| `nearexpiring()` | — | setof record | Contratos por vencer |
| `paymentdelay()` | — | setof record | Pagos atrasados |
| `expensesneartodeadline()` | — | setof record | Gastos próximos a fecha límite |
| `expensesspecial()` | — | setof record | Gastos especiales |
| `birthdays()` | — | setof record | Cumpleaños |
| `contractexpired()` | — | setof record | Contratos vencidos |
| `expiredcontractsmonth()` | — | setof record | Contratos vencidos del mes |
### 📊 Ingresos (Little Hotelier)
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `getincomes(...)` | varios (filtros) | setof record | Lista de ingresos |
| `totalincomes(...)` | varios | `numeric` | Total de ingresos |
| `channelscards(...)` | varios | setof record | Canales de ingreso |
| `loadincomes(jsonb)` | `jsonb` | `status` | Carga masiva de ingresos |
| `loadproductsales(jsonb)` | `jsonb` | `status` | Carga masiva de ventas de productos |
| `loadchequesdetalle(jsonb)` | `jsonb` | `status` | Carga masiva de cheques |
| `reportincomes(...)` | varios | setof record | Reporte de ingresos |
| `countticket(...)` | varios | `int` | Conteo de tickets |
| `efectivo(...)`, `otros(...)`, `propinas(...)`, `tarjeta(...)`, `vales(...)` | varios | `numeric` | Desglose por tipo de pago |
| `sumatotal(...)` | varios | `numeric` | Suma total |
| `ticketpromedio(...)` | varios | `numeric` | Ticket promedio |
### 🏨 Horux / Facturas / Stripe
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `addhoruxdata(jsonb)` | `jsonb` | `int` (total insertados) | Inserta facturas masivamente |
| `getcategoryincome()` | — | setof record | Categorías de ingreso |
| `getinvoiceincome()` | — | setof record | Facturas de ingreso |
| `getaccountincome()` | — | setof record | Cuentas de ingreso |
| `getincomehorux()` | — | setof record | Ingresos Horux |
| `gettotalincome()` | — | `numeric` | Total de ingresos Horux |
| `getoneincome(id)` | `int` | setof record | Un ingreso Horux |
| `newincome(account_id, amount, date, invoice, area_id, categories::jsonb)` | 6 parámetros | `status` | Crea ingreso Horux |
| `updateincome(id, account_id, amount, date, invoice, area_id, categories::jsonb)` | 7 parámetros | `status` | Actualiza ingreso Horux |
| `addstripedatav2(jsonb)` | `jsonb` | `int` (inserted) | Inserta datos de Stripe |
### 📈 Hotel P&L / Restaurant P&L
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `cogs(...)` | varios (fechas) | setof record | Cost of Goods Sold |
| `ebitda(...)` | varios | setof record | EBITDA |
| `employeeshare(...)` | varios | setof record | Participación de empleados |
| `grossprofit(...)` | varios | setof record | Ganancia bruta |
| `tips(...)` | varios | setof record | Propinas |
| `totalrevenue(...)` | varios | setof record | Ingresos totales |
| `weightedCategoriesCost(...)` | varios | setof record | Costos ponderados por categoría |
### 💱 Tipo de Cambio
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `consultexchange(...)` | varios | setof record | Consulta tipo de cambio |
| `getexchanges()` | — | setof record | Historial de tipos de cambio |
### ⚙️ Configuraciones
| Función | Parámetros | Retorno | Descripción |
|---------|-----------|---------|-------------|
| `newroom(...)` | varios | `status` | Crea habitación |
| `newproperty(...)` | varios | `status` | Crea propiedad |
| `reportrooms()` | — | setof record | Reporte de habitaciones |
| `reportproperties()` | — | setof record | Reporte de propiedades |
| `approveby()` | — | setof record | Usuarios que pueden aprobar |
| `requestby()` | — | setof record | Usuarios que pueden solicitar |
| `categoryexpense()` | — | setof record | Categorías de gasto |
| `currency()` | — | setof record | Monedas |
| `units()` | — | setof record | Unidades de medida |
| `recurrence()` | — | setof record | Recurrencias (mensual, etc.) |
---
## Tablas Principales (Inferidas)
Basado en las funciones SQL y los controllers, las tablas principales son:
| Tabla | Propósito |
|-------|-----------|
| `employees` | Datos de empleados |
| `contracts` | Contratos laborales. Incluye `id_position` e `id_area` para historizar puesto y área al momento del contrato |
| `products` | Catálogo de productos |
| `suppliers` | Proveedores |
| `expenses` | Gastos/egresos |
| `purchase_details` | Líneas de compra asociadas a gastos |
| `inventory_entries` / `stock_movements` | Movimientos de inventario |
| `incomes` | Ingresos de Little Hotelier |
| `income_hrx` | Ingresos de Horux |
| `invoice_income` | Facturas descargadas |
| `stripe_data` | Datos de transfers de Stripe |
| `users` | Usuarios del sistema |
| `roles` | Roles de usuario |
| `areas` | Áreas del hotel |
| `positions` | Puestos de trabajo |
| `endpoint_logs` | Control de ejecución de emails (1x/mes) |
| `settings_rooms` | Habitaciones |
| `settings_properties` | Propiedades |
---
## Patrones de Datos
### Paginación Manual
```sql
-- En los controllers
SELECT * FROM getemployees() LIMIT $1 OFFSET $2;
SELECT COUNT(*) FROM employees;
```
### Estados Numéricos
Muchas funciones retornan un `status` numérico:
- `1` = Éxito
- `0` = Error/No permitido
- `-3` = Condición no cumplida (ej. "gasto no aprobado")
- Otros negativos = Errores específicos
---
## JSONB como Patrón de Intercambio
El sistema usa extensivamente `jsonb` para pasar datos complejos:
### Ejemplo: Productos en un Gasto
```javascript
// Controller
const products = [
{ id_product: 1, quantity: 10 },
{ id_product: 2, quantity: 5 }
];
const productJson = JSON.stringify(products);
await pool.query(
'SELECT newexpensev2($1,$2,$3,$4,$5,$6,$7,$8,$9,$10::jsonb,$11,$12,$13,$14)',
[..., productJson, ...]
);
```
### Ejemplo: Ajustes de Stock
```javascript
const stockproductadjusment = [
{ id_product: 1, new_stock: 50 },
{ id_product: 2, new_stock: 30 }
];
await pool.query(
'SELECT * FROM setstockadjusmentsv2($1::jsonb)',
[JSON.stringify(stockproductadjusment)]
);
```
### Ejemplo: Facturas Masivas
```javascript
const facturas = await getFacturas(desde, hasta);
await pool.query(
'SELECT addhoruxdata($1::jsonb)',
[JSON.stringify(facturas)]
);
```
---
## Integraciones que Escriben en JSONB
| Integración | Función SQL | Tabla Destino |
|-------------|-------------|---------------|
| Little Hotelier (CSV/XLSX) | `loadincomes(jsonb)` | `incomes` |
| Facturas API (México) | `addhoruxdata(jsonb)` | `income_hrx` / `invoice_income` |
| Stripe | `addstripedatav2(jsonb)` | `stripe_data` |
---
## Guía para Modificar Funciones SQL
### Paso 1: Verificar Dependencias
Busca TODOS los controllers que llaman a la función:
```bash
grep -r "nombrefuncion(" backend/hotel_hacienda/src/
```
### Paso 2: Probar en PostgreSQL Directamente
```sql
SELECT * FROM nombrefuncion('param1', 'param2');
-- o
SELECT nombrefuncion('param1') AS status;
```
### Paso 3: Considerar Idempotencia
Si la función modifica datos (INSERT/UPDATE), considera:
- ¿Qué pasa si se llama dos veces con los mismos parámetros?
- ¿Debería usar `ON CONFLICT` o verificar `IF EXISTS`?
### Paso 4: Actualizar Controllers si Cambia la Firma
Si agregas/quitas parámetros, actualiza TODOS los `pool.query(...)` que la llaman.
### Paso 5: Documentar el Cambio
Actualiza este archivo (`docs/DATABASE.md`) y `DOCUMENTACION_TECNICA.md`.