- Flujo de inventario corregido - Ordenamiento de report_inventoryv2 - Historial de posicion/area en contratos - CHANGELOG actualizado a version 1.0.1
359 lines
15 KiB
Markdown
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`.
|