Migración desde erp-retail/database - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
aaf5822f19
commit
f7c584278a
196
HERENCIA-ERP-CORE.md
Normal file
196
HERENCIA-ERP-CORE.md
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
# Herencia de Base de Datos - ERP Core -> Retail
|
||||||
|
|
||||||
|
**Fecha:** 2025-12-08
|
||||||
|
**Versión:** 1.0
|
||||||
|
**Vertical:** Retail
|
||||||
|
**Nivel:** 2B.2
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## RESUMEN
|
||||||
|
|
||||||
|
La vertical de Retail hereda los schemas base del ERP Core y extiende con schemas específicos del dominio de punto de venta y comercio minorista.
|
||||||
|
|
||||||
|
**Ubicación DDL Core:** `apps/erp-core/database/ddl/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ARQUITECTURA DE HERENCIA
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ ERP CORE (Base) │
|
||||||
|
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||||
|
│ │ auth │ │ core │ │financial│ │inventory│ │ purchase │ │
|
||||||
|
│ │ 26 tbl │ │ 12 tbl │ │ 15 tbl │ │ 15 tbl │ │ 8 tbl │ │
|
||||||
|
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
||||||
|
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||||
|
│ │ sales │ │analytics│ │ system │ │ crm │ │
|
||||||
|
│ │ 6 tbl │ │ 5 tbl │ │ 10 tbl │ │ 5 tbl │ │
|
||||||
|
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
||||||
|
│ TOTAL: ~102 tablas heredadas │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
│ HEREDA
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ RETAIL (Extensiones) │
|
||||||
|
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
|
||||||
|
│ │ pos │ │ stores │ │ pricing │ │
|
||||||
|
│ │ (punto venta) │ │ (sucursales) │ │ (promociones) │ │
|
||||||
|
│ └───────────────┘ └───────────────┘ └───────────────┘ │
|
||||||
|
│ EXTENSIONES: ~30 tablas (planificadas) │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SCHEMAS HEREDADOS DEL CORE
|
||||||
|
|
||||||
|
| Schema | Tablas | Uso en Retail |
|
||||||
|
|--------|--------|---------------|
|
||||||
|
| `auth` | 26 | Autenticación, usuarios por sucursal |
|
||||||
|
| `core` | 12 | Partners (clientes), catálogos |
|
||||||
|
| `financial` | 15 | Facturas, cuentas, caja |
|
||||||
|
| `inventory` | 15 | Inventario multi-sucursal |
|
||||||
|
| `purchase` | 8 | Compras a proveedores |
|
||||||
|
| `sales` | 6 | Ventas base |
|
||||||
|
| `crm` | 5 | Clientes frecuentes |
|
||||||
|
| `analytics` | 5 | Métricas de venta |
|
||||||
|
| `system` | 10 | Notificaciones |
|
||||||
|
|
||||||
|
**Total heredado:** ~102 tablas
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SCHEMAS ESPECÍFICOS DE RETAIL (Planificados)
|
||||||
|
|
||||||
|
### 1. Schema `pos` (estimado 12+ tablas)
|
||||||
|
|
||||||
|
**Propósito:** Punto de venta y operaciones de caja
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Tablas principales planificadas:
|
||||||
|
pos.cash_registers -- Cajas registradoras
|
||||||
|
pos.cash_sessions -- Sesiones de caja
|
||||||
|
pos.pos_orders -- Tickets/ventas POS
|
||||||
|
pos.pos_order_lines -- Líneas de ticket
|
||||||
|
pos.payment_methods -- Métodos de pago
|
||||||
|
pos.cash_movements -- Movimientos de caja
|
||||||
|
pos.cash_counts -- Cortes de caja
|
||||||
|
pos.receipts -- Recibos
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Schema `stores` (estimado 8+ tablas)
|
||||||
|
|
||||||
|
**Propósito:** Gestión de sucursales
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Tablas principales planificadas:
|
||||||
|
stores.branches -- Sucursales
|
||||||
|
stores.branch_inventory -- Inventario por sucursal
|
||||||
|
stores.transfers -- Transferencias entre sucursales
|
||||||
|
stores.transfer_lines -- Líneas de transferencia
|
||||||
|
stores.branch_employees -- Empleados por sucursal
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Schema `pricing` (estimado 10+ tablas)
|
||||||
|
|
||||||
|
**Propósito:** Precios y promociones
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Extiende: sales schema del core
|
||||||
|
pricing.price_lists -- Listas de precios
|
||||||
|
pricing.promotions -- Promociones
|
||||||
|
pricing.discounts -- Descuentos
|
||||||
|
pricing.loyalty_programs -- Programas de lealtad
|
||||||
|
pricing.coupons -- Cupones
|
||||||
|
pricing.price_history -- Historial de precios
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SPECS DEL CORE APLICABLES
|
||||||
|
|
||||||
|
**Documento detallado:** `orchestration/00-guidelines/HERENCIA-SPECS-CORE.md`
|
||||||
|
|
||||||
|
### Correcciones de DDL Core (2025-12-08)
|
||||||
|
|
||||||
|
El DDL del ERP-Core fue corregido para resolver FK inválidas:
|
||||||
|
|
||||||
|
1. **stock_valuation_layers**: Campos `journal_entry_id` y `journal_entry_line_id` (antes `account_move_*`)
|
||||||
|
2. **stock_move_consume_rel**: Nueva tabla de trazabilidad (antes `move_line_consume_rel`)
|
||||||
|
3. **category_stock_accounts**: FK corregida a `core.product_categories`
|
||||||
|
4. **product_categories**: ALTERs ahora apuntan a schema `core`
|
||||||
|
|
||||||
|
### SPECS Obligatorias
|
||||||
|
|
||||||
|
| Spec Core | Aplicación en Retail | SP | Estado |
|
||||||
|
|-----------|---------------------|----:|--------|
|
||||||
|
| SPEC-SISTEMA-SECUENCIAS | Foliado de tickets y facturas | 8 | ✅ DDL LISTO |
|
||||||
|
| SPEC-VALORACION-INVENTARIO | Costeo de mercancía | 21 | ✅ DDL LISTO |
|
||||||
|
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | Control de acceso por sucursal | 31 | ✅ DDL LISTO |
|
||||||
|
| SPEC-PRICING-RULES | Precios y promociones | 8 | PENDIENTE |
|
||||||
|
| SPEC-INVENTARIOS-CICLICOS | Conteos en sucursales | 13 | ✅ DDL LISTO |
|
||||||
|
| SPEC-TRAZABILIDAD-LOTES-SERIES | Productos con lote/serie | 13 | ✅ DDL LISTO |
|
||||||
|
| SPEC-MAIL-THREAD-TRACKING | Comunicación con clientes | 13 | PENDIENTE |
|
||||||
|
| SPEC-WIZARD-TRANSIENT-MODEL | Wizards de cierre de caja | 8 | PENDIENTE |
|
||||||
|
|
||||||
|
### SPECS Opcionales
|
||||||
|
|
||||||
|
| Spec Core | Decisión | Razón |
|
||||||
|
|-----------|----------|-------|
|
||||||
|
| SPEC-PORTAL-PROVEEDORES | EVALUAR | Para compras centralizadas |
|
||||||
|
| SPEC-TAREAS-RECURRENTES | EVALUAR | Para reorden automático |
|
||||||
|
|
||||||
|
### SPECS No Aplican
|
||||||
|
|
||||||
|
| Spec Core | Razón |
|
||||||
|
|-----------|-------|
|
||||||
|
| SPEC-INTEGRACION-CALENDAR | No requiere calendario de citas |
|
||||||
|
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | No hay proyectos largos |
|
||||||
|
| SPEC-FIRMA-ELECTRONICA-NOM151 | No aplica para tickets POS |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ORDEN DE EJECUCIÓN DDL (Futuro)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# PASO 1: Cargar ERP Core (base)
|
||||||
|
cd apps/erp-core/database
|
||||||
|
./scripts/reset-database.sh --force
|
||||||
|
|
||||||
|
# PASO 2: Cargar extensiones de Retail
|
||||||
|
cd apps/verticales/retail/database
|
||||||
|
psql $DATABASE_URL -f init/00-extensions.sql
|
||||||
|
psql $DATABASE_URL -f init/01-create-schemas.sql
|
||||||
|
psql $DATABASE_URL -f init/02-pos-tables.sql
|
||||||
|
psql $DATABASE_URL -f init/03-stores-tables.sql
|
||||||
|
psql $DATABASE_URL -f init/04-pricing-tables.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MAPEO DE NOMENCLATURA
|
||||||
|
|
||||||
|
| Core | Retail |
|
||||||
|
|------|--------|
|
||||||
|
| `core.partners` | Clientes, proveedores |
|
||||||
|
| `inventory.products` | Productos de venta |
|
||||||
|
| `inventory.locations` | Almacenes de sucursal |
|
||||||
|
| `sales.sale_orders` | Base para POS orders |
|
||||||
|
| `financial.invoices` | Facturas de venta |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## REFERENCIAS
|
||||||
|
|
||||||
|
- ERP Core DDL: `apps/erp-core/database/ddl/`
|
||||||
|
- ERP Core README: `apps/erp-core/database/README.md`
|
||||||
|
- Directivas: `orchestration/directivas/`
|
||||||
|
- Inventarios: `orchestration/inventarios/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Documento de herencia oficial**
|
||||||
|
**Última actualización:** 2025-12-08
|
||||||
84
README.md
84
README.md
@ -1,3 +1,83 @@
|
|||||||
# erp-retail-database-v2
|
# Base de Datos - ERP Retail/POS
|
||||||
|
|
||||||
Database de erp-retail - Workspace V2
|
## Resumen
|
||||||
|
|
||||||
|
| Aspecto | Valor |
|
||||||
|
|---------|-------|
|
||||||
|
| **Schema principal** | `retail` |
|
||||||
|
| **Tablas específicas** | 16 |
|
||||||
|
| **ENUMs** | 6 |
|
||||||
|
| **Hereda de ERP-Core** | 144 tablas (12 schemas) |
|
||||||
|
|
||||||
|
## Prerequisitos
|
||||||
|
|
||||||
|
1. **ERP-Core instalado** con todos sus schemas
|
||||||
|
2. **Extensiones PostgreSQL**: pg_trgm
|
||||||
|
|
||||||
|
## Orden de Ejecución DDL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Instalar ERP-Core primero
|
||||||
|
cd apps/erp-core/database
|
||||||
|
./scripts/reset-database.sh
|
||||||
|
|
||||||
|
# 2. Instalar extensión Retail
|
||||||
|
cd apps/verticales/retail/database
|
||||||
|
psql $DATABASE_URL -f init/00-extensions.sql
|
||||||
|
psql $DATABASE_URL -f init/01-create-schemas.sql
|
||||||
|
psql $DATABASE_URL -f init/02-rls-functions.sql
|
||||||
|
psql $DATABASE_URL -f init/03-retail-tables.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tablas Implementadas
|
||||||
|
|
||||||
|
### Schema: retail (16 tablas)
|
||||||
|
|
||||||
|
| Tabla | Módulo | Descripción |
|
||||||
|
|-------|--------|-------------|
|
||||||
|
| branches | RT-002 | Sucursales |
|
||||||
|
| cash_registers | RT-001 | Cajas registradoras |
|
||||||
|
| pos_sessions | RT-001 | Sesiones de POS |
|
||||||
|
| pos_orders | RT-001 | Ventas/Órdenes |
|
||||||
|
| pos_order_lines | RT-001 | Líneas de venta |
|
||||||
|
| pos_payments | RT-001 | Pagos (mixtos) |
|
||||||
|
| cash_movements | RT-001 | Entradas/salidas efectivo |
|
||||||
|
| branch_stock | RT-002 | Stock por sucursal |
|
||||||
|
| stock_transfers | RT-002 | Transferencias |
|
||||||
|
| stock_transfer_lines | RT-002 | Líneas de transferencia |
|
||||||
|
| product_barcodes | RT-003 | Códigos de barras |
|
||||||
|
| promotions | RT-003 | Promociones |
|
||||||
|
| promotion_products | RT-003 | Productos en promo |
|
||||||
|
| loyalty_programs | RT-004 | Programas fidelización |
|
||||||
|
| loyalty_cards | RT-004 | Tarjetas |
|
||||||
|
| loyalty_transactions | RT-004 | Transacciones puntos |
|
||||||
|
|
||||||
|
## ENUMs
|
||||||
|
|
||||||
|
| Enum | Valores |
|
||||||
|
|------|---------|
|
||||||
|
| pos_session_status | opening, open, closing, closed |
|
||||||
|
| pos_order_status | draft, paid, done, cancelled, refunded |
|
||||||
|
| payment_method | cash, card, transfer, credit, mixed |
|
||||||
|
| cash_movement_type | in, out |
|
||||||
|
| transfer_status | draft, pending, in_transit, received, cancelled |
|
||||||
|
| promotion_type | percentage, fixed_amount, buy_x_get_y, bundle |
|
||||||
|
|
||||||
|
## Row Level Security
|
||||||
|
|
||||||
|
Todas las tablas tienen RLS con:
|
||||||
|
```sql
|
||||||
|
tenant_id = current_setting('app.current_tenant_id', true)::UUID
|
||||||
|
```
|
||||||
|
|
||||||
|
## Consideraciones Especiales
|
||||||
|
|
||||||
|
- **Operación offline**: POS puede operar sin conexión
|
||||||
|
- **Rendimiento**: <100ms por transacción
|
||||||
|
- **Hardware**: Integración con impresoras y lectores
|
||||||
|
- **CFDI 4.0**: Facturación en tiempo real
|
||||||
|
|
||||||
|
## Referencias
|
||||||
|
|
||||||
|
- [HERENCIA-ERP-CORE.md](./HERENCIA-ERP-CORE.md)
|
||||||
|
- [DATABASE_INVENTORY.yml](../orchestration/inventarios/DATABASE_INVENTORY.yml)
|
||||||
|
|||||||
22
init/00-extensions.sql
Normal file
22
init/00-extensions.sql
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- EXTENSIONES PostgreSQL - ERP Retail/POS
|
||||||
|
-- ============================================================================
|
||||||
|
-- Versión: 1.0.0
|
||||||
|
-- Fecha: 2025-12-09
|
||||||
|
-- Prerequisito: ERP-Core debe estar instalado
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Verificar que ERP-Core esté instalado
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'auth') THEN
|
||||||
|
RAISE EXCEPTION 'ERP-Core no instalado. Ejecutar primero DDL de erp-core.';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Extensión para búsqueda de texto (productos, códigos)
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN EXTENSIONES
|
||||||
|
-- ============================================================================
|
||||||
15
init/01-create-schemas.sql
Normal file
15
init/01-create-schemas.sql
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- SCHEMAS - ERP Retail/POS
|
||||||
|
-- ============================================================================
|
||||||
|
-- Versión: 1.0.0
|
||||||
|
-- Fecha: 2025-12-09
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Schema principal para operaciones de punto de venta
|
||||||
|
CREATE SCHEMA IF NOT EXISTS retail;
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA retail IS 'Schema para operaciones de punto de venta y retail';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN SCHEMAS
|
||||||
|
-- ============================================================================
|
||||||
30
init/02-rls-functions.sql
Normal file
30
init/02-rls-functions.sql
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- FUNCIONES RLS - ERP Retail/POS
|
||||||
|
-- ============================================================================
|
||||||
|
-- Versión: 1.0.0
|
||||||
|
-- Fecha: 2025-12-09
|
||||||
|
-- Nota: Usa las funciones de contexto de ERP-Core (auth schema)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Las funciones principales están en ERP-Core:
|
||||||
|
-- auth.get_current_tenant_id()
|
||||||
|
-- auth.get_current_user_id()
|
||||||
|
-- auth.get_current_company_id()
|
||||||
|
|
||||||
|
-- Función para obtener sucursal actual del usuario (para POS)
|
||||||
|
CREATE OR REPLACE FUNCTION retail.get_current_branch_id()
|
||||||
|
RETURNS UUID AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN current_setting('app.current_branch_id', true)::UUID;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN OTHERS THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql STABLE;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION retail.get_current_branch_id IS
|
||||||
|
'Obtiene el ID de la sucursal actual para operaciones POS';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN FUNCIONES RLS
|
||||||
|
-- ============================================================================
|
||||||
723
init/03-retail-tables.sql
Normal file
723
init/03-retail-tables.sql
Normal file
@ -0,0 +1,723 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- TABLAS RETAIL/POS - ERP Retail
|
||||||
|
-- ============================================================================
|
||||||
|
-- Módulos: RT-001 (POS), RT-002 (Inventario), RT-003 (Productos), RT-004 (Clientes)
|
||||||
|
-- Versión: 1.0.0
|
||||||
|
-- Fecha: 2025-12-09
|
||||||
|
-- ============================================================================
|
||||||
|
-- PREREQUISITOS:
|
||||||
|
-- 1. ERP-Core instalado (auth, core, inventory, sales, financial)
|
||||||
|
-- 2. Schema retail creado
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TYPES (ENUMs)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE retail.pos_session_status AS ENUM (
|
||||||
|
'opening', 'open', 'closing', 'closed'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE retail.pos_order_status AS ENUM (
|
||||||
|
'draft', 'paid', 'done', 'cancelled', 'refunded'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE retail.payment_method AS ENUM (
|
||||||
|
'cash', 'card', 'transfer', 'credit', 'mixed'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE retail.cash_movement_type AS ENUM (
|
||||||
|
'in', 'out'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE retail.transfer_status AS ENUM (
|
||||||
|
'draft', 'pending', 'in_transit', 'received', 'cancelled'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE retail.promotion_type AS ENUM (
|
||||||
|
'percentage', 'fixed_amount', 'buy_x_get_y', 'bundle'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- SUCURSALES Y CONFIGURACIÓN
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: branches (Sucursales)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.branches (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
company_id UUID REFERENCES auth.companies(id),
|
||||||
|
|
||||||
|
-- Identificación
|
||||||
|
code VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
|
||||||
|
-- Ubicación
|
||||||
|
address VARCHAR(255),
|
||||||
|
city VARCHAR(100),
|
||||||
|
state VARCHAR(100),
|
||||||
|
zip_code VARCHAR(10),
|
||||||
|
country VARCHAR(100) DEFAULT 'México',
|
||||||
|
latitude DECIMAL(10,8),
|
||||||
|
longitude DECIMAL(11,8),
|
||||||
|
|
||||||
|
-- Contacto
|
||||||
|
phone VARCHAR(20),
|
||||||
|
email VARCHAR(255),
|
||||||
|
manager_id UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
-- Configuración
|
||||||
|
warehouse_id UUID, -- FK a inventory.warehouses (ERP Core)
|
||||||
|
default_pricelist_id UUID,
|
||||||
|
timezone VARCHAR(50) DEFAULT 'America/Mexico_City',
|
||||||
|
|
||||||
|
-- Control
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
opening_date DATE,
|
||||||
|
|
||||||
|
-- Auditoría
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
CONSTRAINT uq_branches_code UNIQUE (tenant_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: cash_registers (Cajas registradoras)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.cash_registers (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
branch_id UUID NOT NULL REFERENCES retail.branches(id),
|
||||||
|
|
||||||
|
-- Identificación
|
||||||
|
code VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
|
||||||
|
-- Configuración
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
default_payment_method retail.payment_method DEFAULT 'cash',
|
||||||
|
|
||||||
|
-- Auditoría
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
CONSTRAINT uq_cash_registers_code UNIQUE (tenant_id, branch_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- PUNTO DE VENTA (RT-001)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: pos_sessions (Sesiones de POS)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.pos_sessions (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
branch_id UUID NOT NULL REFERENCES retail.branches(id),
|
||||||
|
cash_register_id UUID NOT NULL REFERENCES retail.cash_registers(id),
|
||||||
|
|
||||||
|
-- Usuario
|
||||||
|
user_id UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
-- Estado
|
||||||
|
status retail.pos_session_status NOT NULL DEFAULT 'opening',
|
||||||
|
|
||||||
|
-- Apertura
|
||||||
|
opening_date TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
opening_balance DECIMAL(14,2) NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
-- Cierre
|
||||||
|
closing_date TIMESTAMPTZ,
|
||||||
|
closing_balance DECIMAL(14,2),
|
||||||
|
closing_notes TEXT,
|
||||||
|
|
||||||
|
-- Totales calculados
|
||||||
|
total_sales DECIMAL(14,2) DEFAULT 0,
|
||||||
|
total_refunds DECIMAL(14,2) DEFAULT 0,
|
||||||
|
total_cash_in DECIMAL(14,2) DEFAULT 0,
|
||||||
|
total_cash_out DECIMAL(14,2) DEFAULT 0,
|
||||||
|
total_card DECIMAL(14,2) DEFAULT 0,
|
||||||
|
total_transfer DECIMAL(14,2) DEFAULT 0,
|
||||||
|
|
||||||
|
-- Diferencia
|
||||||
|
expected_balance DECIMAL(14,2),
|
||||||
|
difference DECIMAL(14,2),
|
||||||
|
|
||||||
|
-- Auditoría
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: pos_orders (Órdenes/Ventas de POS)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.pos_orders (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
session_id UUID NOT NULL REFERENCES retail.pos_sessions(id),
|
||||||
|
branch_id UUID NOT NULL REFERENCES retail.branches(id),
|
||||||
|
|
||||||
|
-- Número de ticket
|
||||||
|
order_number VARCHAR(30) NOT NULL,
|
||||||
|
order_date TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
|
||||||
|
-- Cliente (opcional)
|
||||||
|
customer_id UUID, -- FK a core.partners (ERP Core)
|
||||||
|
customer_name VARCHAR(200),
|
||||||
|
|
||||||
|
-- Estado
|
||||||
|
status retail.pos_order_status NOT NULL DEFAULT 'draft',
|
||||||
|
|
||||||
|
-- Totales
|
||||||
|
subtotal DECIMAL(14,2) NOT NULL DEFAULT 0,
|
||||||
|
discount_amount DECIMAL(14,2) DEFAULT 0,
|
||||||
|
tax_amount DECIMAL(14,2) DEFAULT 0,
|
||||||
|
total DECIMAL(14,2) NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
-- Pago
|
||||||
|
payment_method retail.payment_method,
|
||||||
|
amount_paid DECIMAL(14,2) DEFAULT 0,
|
||||||
|
change_amount DECIMAL(14,2) DEFAULT 0,
|
||||||
|
|
||||||
|
-- Facturación
|
||||||
|
requires_invoice BOOLEAN DEFAULT FALSE,
|
||||||
|
invoice_id UUID, -- FK a financial.invoices (ERP Core)
|
||||||
|
|
||||||
|
-- Notas
|
||||||
|
notes TEXT,
|
||||||
|
|
||||||
|
-- Auditoría
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
CONSTRAINT uq_pos_orders_number UNIQUE (tenant_id, order_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: pos_order_lines (Líneas de venta)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.pos_order_lines (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
order_id UUID NOT NULL REFERENCES retail.pos_orders(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
-- Producto
|
||||||
|
product_id UUID NOT NULL, -- FK a inventory.products (ERP Core)
|
||||||
|
product_name VARCHAR(255) NOT NULL,
|
||||||
|
barcode VARCHAR(50),
|
||||||
|
|
||||||
|
-- Cantidades
|
||||||
|
quantity DECIMAL(12,4) NOT NULL,
|
||||||
|
unit_price DECIMAL(12,4) NOT NULL,
|
||||||
|
|
||||||
|
-- Descuentos
|
||||||
|
discount_percent DECIMAL(5,2) DEFAULT 0,
|
||||||
|
discount_amount DECIMAL(12,2) DEFAULT 0,
|
||||||
|
|
||||||
|
-- Totales
|
||||||
|
subtotal DECIMAL(14,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
|
||||||
|
tax_amount DECIMAL(12,2) DEFAULT 0,
|
||||||
|
total DECIMAL(14,2) NOT NULL,
|
||||||
|
|
||||||
|
-- Orden
|
||||||
|
sequence INTEGER DEFAULT 1,
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: pos_payments (Pagos de orden - para pagos mixtos)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.pos_payments (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
order_id UUID NOT NULL REFERENCES retail.pos_orders(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
payment_method retail.payment_method NOT NULL,
|
||||||
|
amount DECIMAL(14,2) NOT NULL,
|
||||||
|
|
||||||
|
-- Referencia (para tarjeta/transferencia)
|
||||||
|
reference VARCHAR(100),
|
||||||
|
card_last_four VARCHAR(4),
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: cash_movements (Movimientos de efectivo)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.cash_movements (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
session_id UUID NOT NULL REFERENCES retail.pos_sessions(id),
|
||||||
|
|
||||||
|
-- Tipo y monto
|
||||||
|
movement_type retail.cash_movement_type NOT NULL,
|
||||||
|
amount DECIMAL(14,2) NOT NULL,
|
||||||
|
|
||||||
|
-- Razón
|
||||||
|
reason VARCHAR(255) NOT NULL,
|
||||||
|
notes TEXT,
|
||||||
|
|
||||||
|
-- Autorización
|
||||||
|
authorized_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- INVENTARIO MULTI-SUCURSAL (RT-002)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: branch_stock (Stock por sucursal)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.branch_stock (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
branch_id UUID NOT NULL REFERENCES retail.branches(id),
|
||||||
|
product_id UUID NOT NULL, -- FK a inventory.products (ERP Core)
|
||||||
|
|
||||||
|
-- Cantidades
|
||||||
|
quantity_on_hand DECIMAL(12,4) NOT NULL DEFAULT 0,
|
||||||
|
quantity_reserved DECIMAL(12,4) DEFAULT 0,
|
||||||
|
quantity_available DECIMAL(12,4) GENERATED ALWAYS AS (quantity_on_hand - COALESCE(quantity_reserved, 0)) STORED,
|
||||||
|
|
||||||
|
-- Límites
|
||||||
|
reorder_point DECIMAL(12,4),
|
||||||
|
max_stock DECIMAL(12,4),
|
||||||
|
|
||||||
|
-- Control
|
||||||
|
last_count_date DATE,
|
||||||
|
last_count_qty DECIMAL(12,4),
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
|
||||||
|
CONSTRAINT uq_branch_stock UNIQUE (branch_id, product_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: stock_transfers (Transferencias entre sucursales)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.stock_transfers (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
-- Número
|
||||||
|
transfer_number VARCHAR(30) NOT NULL,
|
||||||
|
|
||||||
|
-- Origen y destino
|
||||||
|
source_branch_id UUID NOT NULL REFERENCES retail.branches(id),
|
||||||
|
destination_branch_id UUID NOT NULL REFERENCES retail.branches(id),
|
||||||
|
|
||||||
|
-- Estado
|
||||||
|
status retail.transfer_status NOT NULL DEFAULT 'draft',
|
||||||
|
|
||||||
|
-- Fechas
|
||||||
|
request_date TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
ship_date TIMESTAMPTZ,
|
||||||
|
receive_date TIMESTAMPTZ,
|
||||||
|
|
||||||
|
-- Responsables
|
||||||
|
requested_by UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
shipped_by UUID REFERENCES auth.users(id),
|
||||||
|
received_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
-- Notas
|
||||||
|
notes TEXT,
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
CONSTRAINT uq_stock_transfers_number UNIQUE (tenant_id, transfer_number),
|
||||||
|
CONSTRAINT chk_different_branches CHECK (source_branch_id != destination_branch_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: stock_transfer_lines (Líneas de transferencia)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.stock_transfer_lines (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
transfer_id UUID NOT NULL REFERENCES retail.stock_transfers(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
product_id UUID NOT NULL, -- FK a inventory.products (ERP Core)
|
||||||
|
quantity_requested DECIMAL(12,4) NOT NULL,
|
||||||
|
quantity_shipped DECIMAL(12,4),
|
||||||
|
quantity_received DECIMAL(12,4),
|
||||||
|
|
||||||
|
notes TEXT,
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- PRODUCTOS RETAIL (RT-003)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: product_barcodes (Códigos de barras múltiples)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.product_barcodes (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
product_id UUID NOT NULL, -- FK a inventory.products (ERP Core)
|
||||||
|
|
||||||
|
barcode VARCHAR(50) NOT NULL,
|
||||||
|
barcode_type VARCHAR(20) DEFAULT 'EAN13', -- EAN13, EAN8, UPC, CODE128, etc.
|
||||||
|
is_primary BOOLEAN DEFAULT FALSE,
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
CONSTRAINT uq_product_barcodes UNIQUE (tenant_id, barcode)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: promotions (Promociones)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.promotions (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
code VARCHAR(30) NOT NULL,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
|
||||||
|
-- Tipo de promoción
|
||||||
|
promotion_type retail.promotion_type NOT NULL,
|
||||||
|
discount_value DECIMAL(10,2), -- Porcentaje o monto fijo
|
||||||
|
|
||||||
|
-- Vigencia
|
||||||
|
start_date TIMESTAMPTZ NOT NULL,
|
||||||
|
end_date TIMESTAMPTZ NOT NULL,
|
||||||
|
|
||||||
|
-- Aplicación
|
||||||
|
applies_to_all BOOLEAN DEFAULT FALSE,
|
||||||
|
min_quantity DECIMAL(12,4),
|
||||||
|
min_amount DECIMAL(14,2),
|
||||||
|
|
||||||
|
-- Sucursales (NULL = todas)
|
||||||
|
branch_ids UUID[],
|
||||||
|
|
||||||
|
-- Control
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
max_uses INTEGER,
|
||||||
|
current_uses INTEGER DEFAULT 0,
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
CONSTRAINT uq_promotions_code UNIQUE (tenant_id, code),
|
||||||
|
CONSTRAINT chk_promotion_dates CHECK (end_date > start_date)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: promotion_products (Productos en promoción)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.promotion_products (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
promotion_id UUID NOT NULL REFERENCES retail.promotions(id) ON DELETE CASCADE,
|
||||||
|
product_id UUID NOT NULL, -- FK a inventory.products (ERP Core)
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- CLIENTES Y FIDELIZACIÓN (RT-004)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: loyalty_programs (Programas de fidelización)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.loyalty_programs (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
code VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
|
||||||
|
-- Configuración de puntos
|
||||||
|
points_per_currency DECIMAL(10,4) DEFAULT 1, -- Puntos por peso gastado
|
||||||
|
currency_per_point DECIMAL(10,4) DEFAULT 0.01, -- Valor del punto en pesos
|
||||||
|
min_points_redeem INTEGER DEFAULT 100,
|
||||||
|
|
||||||
|
-- Vigencia
|
||||||
|
points_expiry_days INTEGER, -- NULL = no expiran
|
||||||
|
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
CONSTRAINT uq_loyalty_programs_code UNIQUE (tenant_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: loyalty_cards (Tarjetas de fidelización)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.loyalty_cards (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
program_id UUID NOT NULL REFERENCES retail.loyalty_programs(id),
|
||||||
|
customer_id UUID NOT NULL, -- FK a core.partners (ERP Core)
|
||||||
|
|
||||||
|
card_number VARCHAR(30) NOT NULL,
|
||||||
|
issue_date DATE NOT NULL DEFAULT CURRENT_DATE,
|
||||||
|
|
||||||
|
-- Balance
|
||||||
|
points_balance INTEGER NOT NULL DEFAULT 0,
|
||||||
|
points_earned INTEGER NOT NULL DEFAULT 0,
|
||||||
|
points_redeemed INTEGER NOT NULL DEFAULT 0,
|
||||||
|
points_expired INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
CONSTRAINT uq_loyalty_cards_number UNIQUE (tenant_id, card_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: loyalty_transactions (Transacciones de puntos)
|
||||||
|
CREATE TABLE IF NOT EXISTS retail.loyalty_transactions (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
card_id UUID NOT NULL REFERENCES retail.loyalty_cards(id),
|
||||||
|
|
||||||
|
-- Tipo
|
||||||
|
transaction_type VARCHAR(20) NOT NULL, -- earn, redeem, expire, adjust
|
||||||
|
points INTEGER NOT NULL,
|
||||||
|
|
||||||
|
-- Referencia
|
||||||
|
order_id UUID REFERENCES retail.pos_orders(id),
|
||||||
|
description TEXT,
|
||||||
|
|
||||||
|
-- Balance después de la transacción
|
||||||
|
balance_after INTEGER NOT NULL,
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- ÍNDICES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Branches
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_branches_tenant ON retail.branches(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_branches_company ON retail.branches(company_id);
|
||||||
|
|
||||||
|
-- Cash registers
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_cash_registers_tenant ON retail.cash_registers(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_cash_registers_branch ON retail.cash_registers(branch_id);
|
||||||
|
|
||||||
|
-- POS sessions
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_sessions_tenant ON retail.pos_sessions(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_sessions_branch ON retail.pos_sessions(branch_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_sessions_user ON retail.pos_sessions(user_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_sessions_status ON retail.pos_sessions(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_sessions_date ON retail.pos_sessions(opening_date);
|
||||||
|
|
||||||
|
-- POS orders
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_orders_tenant ON retail.pos_orders(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_orders_session ON retail.pos_orders(session_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_orders_branch ON retail.pos_orders(branch_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_orders_customer ON retail.pos_orders(customer_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_orders_date ON retail.pos_orders(order_date);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_orders_status ON retail.pos_orders(status);
|
||||||
|
|
||||||
|
-- POS order lines
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_order_lines_tenant ON retail.pos_order_lines(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_order_lines_order ON retail.pos_order_lines(order_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_order_lines_product ON retail.pos_order_lines(product_id);
|
||||||
|
|
||||||
|
-- POS payments
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_payments_tenant ON retail.pos_payments(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pos_payments_order ON retail.pos_payments(order_id);
|
||||||
|
|
||||||
|
-- Cash movements
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_cash_movements_tenant ON retail.cash_movements(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_cash_movements_session ON retail.cash_movements(session_id);
|
||||||
|
|
||||||
|
-- Branch stock
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_branch_stock_tenant ON retail.branch_stock(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_branch_stock_branch ON retail.branch_stock(branch_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_branch_stock_product ON retail.branch_stock(product_id);
|
||||||
|
|
||||||
|
-- Stock transfers
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_stock_transfers_tenant ON retail.stock_transfers(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_stock_transfers_source ON retail.stock_transfers(source_branch_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_stock_transfers_dest ON retail.stock_transfers(destination_branch_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_stock_transfers_status ON retail.stock_transfers(status);
|
||||||
|
|
||||||
|
-- Product barcodes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_product_barcodes_tenant ON retail.product_barcodes(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_product_barcodes_barcode ON retail.product_barcodes(barcode);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_product_barcodes_product ON retail.product_barcodes(product_id);
|
||||||
|
|
||||||
|
-- Promotions
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_promotions_tenant ON retail.promotions(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_promotions_dates ON retail.promotions(start_date, end_date);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_promotions_active ON retail.promotions(is_active);
|
||||||
|
|
||||||
|
-- Loyalty
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_loyalty_cards_tenant ON retail.loyalty_cards(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_loyalty_cards_customer ON retail.loyalty_cards(customer_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_loyalty_transactions_tenant ON retail.loyalty_transactions(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_loyalty_transactions_card ON retail.loyalty_transactions(card_id);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- ROW LEVEL SECURITY
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
ALTER TABLE retail.branches ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.cash_registers ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.pos_sessions ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.pos_orders ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.pos_order_lines ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.pos_payments ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.cash_movements ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.branch_stock ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.stock_transfers ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.stock_transfer_lines ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.product_barcodes ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.promotions ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.promotion_products ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.loyalty_programs ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.loyalty_cards ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE retail.loyalty_transactions ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- Políticas de aislamiento por tenant
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_branches ON retail.branches;
|
||||||
|
CREATE POLICY tenant_isolation_branches ON retail.branches
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_cash_registers ON retail.cash_registers;
|
||||||
|
CREATE POLICY tenant_isolation_cash_registers ON retail.cash_registers
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_pos_sessions ON retail.pos_sessions;
|
||||||
|
CREATE POLICY tenant_isolation_pos_sessions ON retail.pos_sessions
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_pos_orders ON retail.pos_orders;
|
||||||
|
CREATE POLICY tenant_isolation_pos_orders ON retail.pos_orders
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_pos_order_lines ON retail.pos_order_lines;
|
||||||
|
CREATE POLICY tenant_isolation_pos_order_lines ON retail.pos_order_lines
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_pos_payments ON retail.pos_payments;
|
||||||
|
CREATE POLICY tenant_isolation_pos_payments ON retail.pos_payments
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_cash_movements ON retail.cash_movements;
|
||||||
|
CREATE POLICY tenant_isolation_cash_movements ON retail.cash_movements
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_branch_stock ON retail.branch_stock;
|
||||||
|
CREATE POLICY tenant_isolation_branch_stock ON retail.branch_stock
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_stock_transfers ON retail.stock_transfers;
|
||||||
|
CREATE POLICY tenant_isolation_stock_transfers ON retail.stock_transfers
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_stock_transfer_lines ON retail.stock_transfer_lines;
|
||||||
|
CREATE POLICY tenant_isolation_stock_transfer_lines ON retail.stock_transfer_lines
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_product_barcodes ON retail.product_barcodes;
|
||||||
|
CREATE POLICY tenant_isolation_product_barcodes ON retail.product_barcodes
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_promotions ON retail.promotions;
|
||||||
|
CREATE POLICY tenant_isolation_promotions ON retail.promotions
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_promotion_products ON retail.promotion_products;
|
||||||
|
CREATE POLICY tenant_isolation_promotion_products ON retail.promotion_products
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_loyalty_programs ON retail.loyalty_programs;
|
||||||
|
CREATE POLICY tenant_isolation_loyalty_programs ON retail.loyalty_programs
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_loyalty_cards ON retail.loyalty_cards;
|
||||||
|
CREATE POLICY tenant_isolation_loyalty_cards ON retail.loyalty_cards
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_loyalty_transactions ON retail.loyalty_transactions;
|
||||||
|
CREATE POLICY tenant_isolation_loyalty_transactions ON retail.loyalty_transactions
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- COMENTARIOS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
COMMENT ON TABLE retail.branches IS 'Sucursales de la empresa';
|
||||||
|
COMMENT ON TABLE retail.cash_registers IS 'Cajas registradoras por sucursal';
|
||||||
|
COMMENT ON TABLE retail.pos_sessions IS 'Sesiones de punto de venta';
|
||||||
|
COMMENT ON TABLE retail.pos_orders IS 'Órdenes/Ventas de punto de venta';
|
||||||
|
COMMENT ON TABLE retail.pos_order_lines IS 'Líneas de venta';
|
||||||
|
COMMENT ON TABLE retail.pos_payments IS 'Pagos de orden (para pagos mixtos)';
|
||||||
|
COMMENT ON TABLE retail.cash_movements IS 'Entradas/salidas de efectivo';
|
||||||
|
COMMENT ON TABLE retail.branch_stock IS 'Stock por sucursal';
|
||||||
|
COMMENT ON TABLE retail.stock_transfers IS 'Transferencias entre sucursales';
|
||||||
|
COMMENT ON TABLE retail.stock_transfer_lines IS 'Líneas de transferencia';
|
||||||
|
COMMENT ON TABLE retail.product_barcodes IS 'Códigos de barras múltiples por producto';
|
||||||
|
COMMENT ON TABLE retail.promotions IS 'Promociones y descuentos';
|
||||||
|
COMMENT ON TABLE retail.promotion_products IS 'Productos en promoción';
|
||||||
|
COMMENT ON TABLE retail.loyalty_programs IS 'Programas de fidelización';
|
||||||
|
COMMENT ON TABLE retail.loyalty_cards IS 'Tarjetas de fidelización';
|
||||||
|
COMMENT ON TABLE retail.loyalty_transactions IS 'Transacciones de puntos';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN TABLAS RETAIL
|
||||||
|
-- Total: 16 tablas, 6 ENUMs
|
||||||
|
-- ============================================================================
|
||||||
Loading…
Reference in New Issue
Block a user