Migración desde erp-vidrio-templado/database - Estándar multi-repo v2

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rckrdmrd 2026-01-16 08:12:04 -06:00
parent 8929bdfc94
commit 431e1273b8
10 changed files with 1352 additions and 2 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules/
dist/
coverage/
.env

191
HERENCIA-ERP-CORE.md Normal file
View File

@ -0,0 +1,191 @@
# Herencia de Base de Datos - ERP Core -> Vidrio Templado
**Fecha:** 2025-12-08
**Versión:** 1.0
**Vertical:** Vidrio Templado
**Nivel:** 2B.2
---
## RESUMEN
La vertical de Vidrio Templado hereda los schemas base del ERP Core y extiende con schemas específicos del dominio de producción de vidrio.
**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 │ │
│ │ 6 tbl │ │ 5 tbl │ │ 10 tbl │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ TOTAL: ~97 tablas heredadas │
└─────────────────────────────────────────────────────────────────┘
│ HEREDA
┌─────────────────────────────────────────────────────────────────┐
│ VIDRIO TEMPLADO (Extensiones) │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ production │ │ quality │ │ glass │ │
│ │ management │ │ control │ │ inventory │ │
│ │ (hornos) │ │ (inspección) │ │ (lotes) │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ EXTENSIONES: ~25 tablas (planificadas) │
└─────────────────────────────────────────────────────────────────┘
```
---
## SCHEMAS HEREDADOS DEL CORE
| Schema | Tablas | Uso en Vidrio Templado |
|--------|--------|------------------------|
| `auth` | 26 | Autenticación, usuarios, roles, permisos |
| `core` | 12 | Partners (clientes), catálogos |
| `financial` | 15 | Facturas, cuentas contables |
| `inventory` | 15 | Base para materia prima y producto terminado |
| `purchase` | 8 | Compras de materiales |
| `sales` | 6 | Cotizaciones, órdenes de venta |
| `analytics` | 5 | Centros de costo |
| `system` | 10 | Mensajes, notificaciones |
**Total heredado:** ~97 tablas
---
## SCHEMAS ESPECÍFICOS DE VIDRIO TEMPLADO (Planificados)
### 1. Schema `production` (estimado 10+ tablas)
**Propósito:** Gestión de producción y hornos de templado
```sql
-- Tablas principales planificadas:
production.production_orders -- Órdenes de producción
production.production_lines -- Líneas de producción (hornos)
production.work_orders -- Órdenes de trabajo
production.cutting_plans -- Planes de corte
production.oven_schedules -- Programación de hornos
production.temperature_logs -- Registros de temperatura
```
### 2. Schema `quality` (estimado 8+ tablas)
**Propósito:** Control de calidad y trazabilidad
```sql
-- Tablas principales planificadas:
quality.inspections -- Inspecciones de calidad
quality.defect_types -- Catálogo de defectos
quality.quality_tests -- Pruebas de calidad
quality.certifications -- Certificaciones de producto
quality.non_conformities -- No conformidades
```
### 3. Schema `glass` (estimado 7+ tablas)
**Propósito:** Inventario especializado de vidrio
```sql
-- Extiende: inventory schema del core
glass.glass_types -- Tipos de vidrio
glass.glass_lots -- Lotes de producción
glass.glass_dimensions -- Dimensiones estándar
glass.raw_materials -- Materia prima
```
---
## 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 Vidrio Templado | SP | Estado |
|-----------|------------------------------|----:|--------|
| SPEC-SISTEMA-SECUENCIAS | Foliado de órdenes y lotes | 8 | ✅ DDL LISTO |
| SPEC-VALORACION-INVENTARIO | Costeo de materia prima y producto | 21 | ✅ DDL LISTO |
| SPEC-SEGURIDAD-API-KEYS-PERMISOS | Control de acceso | 31 | ✅ DDL LISTO |
| SPEC-TRAZABILIDAD-LOTES-SERIES | Lotes de producción de vidrio | 13 | ✅ DDL LISTO |
| SPEC-PRICING-RULES | Precios por dimensiones y tipo | 8 | PENDIENTE |
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | Control de producción | 13 | PENDIENTE |
| SPEC-MAIL-THREAD-TRACKING | Historial de órdenes | 13 | PENDIENTE |
| SPEC-WIZARD-TRANSIENT-MODEL | Wizards de corte y templado | 8 | PENDIENTE |
### SPECS Opcionales
| Spec Core | Decisión | Razón |
|-----------|----------|-------|
| SPEC-INVENTARIOS-CICLICOS | EVALUAR | Útil para materia prima |
| SPEC-FIRMA-ELECTRONICA-NOM151 | EVALUAR | Certificados de calidad |
### SPECS No Aplican
| Spec Core | Razón |
|-----------|-------|
| SPEC-INTEGRACION-CALENDAR | No requiere calendario externo |
| SPEC-CONSOLIDACION-FINANCIERA | Negocio de una sola planta |
---
## 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 Vidrio Templado
cd apps/verticales/vidrio-templado/database
psql $DATABASE_URL -f init/00-extensions.sql
psql $DATABASE_URL -f init/01-create-schemas.sql
psql $DATABASE_URL -f init/02-production-tables.sql
psql $DATABASE_URL -f init/03-quality-tables.sql
psql $DATABASE_URL -f init/04-glass-inventory.sql
```
---
## MAPEO DE NOMENCLATURA
| Core | Vidrio Templado |
|------|-----------------|
| `core.partners` | Clientes, proveedores |
| `inventory.products` | Producto terminado base |
| `inventory.locations` | Almacenes de vidrio |
| `sales.sale_orders` | Pedidos de vidrio |
| `purchase.purchase_orders` | Compras de materia prima |
---
## 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

View File

@ -1,3 +1,90 @@
# erp-vidrio-templado-database-v2
# Base de Datos - ERP Vidrio Templado
Database de erp-vidrio-templado - Workspace V2
## Resumen
| Aspecto | Valor |
|---------|-------|
| **Schema principal** | `vidrio` |
| **Tablas específicas** | 14 |
| **ENUMs** | 5 |
| **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 Vidrio Templado
cd apps/verticales/vidrio-templado/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-vidrio-tables.sql
```
## Tablas Implementadas
### Schema: vidrio (14 tablas)
| Tabla | Módulo | Descripción |
|-------|--------|-------------|
| glass_catalog | VT-001 | Catálogo de tipos de vidrio |
| process_catalog | VT-001 | Catálogo de procesos |
| production_orders | VT-002 | Órdenes de producción |
| production_lines | VT-002 | Líneas de producción |
| furnaces | VT-003 | Hornos de templado |
| furnace_batches | VT-003 | Lotes en horno |
| cutting_machines | VT-003 | Máquinas de corte |
| quality_tests | VT-004 | Tipos de prueba de calidad |
| quality_inspections | VT-004 | Inspecciones de calidad |
| quality_test_results | VT-004 | Resultados de pruebas |
| glass_lots | VT-005 | Lotes de vidrio |
| lot_consumption | VT-005 | Consumo de lotes |
| quotations | VT-006 | Cotizaciones |
| quotation_lines | VT-006 | Líneas de cotización |
## ENUMs
| Enum | Valores |
|------|---------|
| glass_type | clear, tinted, reflective, low_e, laminated, tempered |
| production_status | draft, scheduled, in_progress, cutting, tempering, quality_check, completed, cancelled |
| furnace_status | idle, preheating, heating, cooling, maintenance |
| inspection_result | pending, passed, failed, conditional |
| quotation_status | draft, sent, approved, rejected, expired |
## Row Level Security
Todas las tablas tienen RLS con:
```sql
tenant_id = current_setting('app.current_tenant_id', true)::UUID
```
## Funciones Específicas
### vidrio.calculate_area_m2(width_mm, height_mm)
Calcula el área en metros cuadrados a partir de dimensiones en milímetros.
```sql
SELECT vidrio.calculate_area_m2(1500, 2000);
-- Resultado: 3.00 m²
```
## Consideraciones Especiales
- **Cálculos de área**: Siempre en m² para costeo
- **Trazabilidad**: Lotes vinculados a piezas producidas
- **Control de calidad**: Pruebas obligatorias antes de entrega
- **Capacidad hornos**: Máximo área por batch configurable
## Referencias
- [HERENCIA-ERP-CORE.md](../orchestration/00-guidelines/HERENCIA-ERP-CORE.md)
- [DATABASE_INVENTORY.yml](../orchestration/inventarios/DATABASE_INVENTORY.yml)

23
init/00-extensions.sql Normal file
View File

@ -0,0 +1,23 @@
-- ============================================================================
-- EXTENSIONES PostgreSQL - ERP Vidrio Templado
-- ============================================================================
-- 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 cálculos geométricos (áreas de vidrio)
-- Ya debería estar en ERP-Core, pero por si acaso
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- ============================================================================
-- FIN EXTENSIONES
-- ============================================================================

View File

@ -0,0 +1,15 @@
-- ============================================================================
-- SCHEMAS - ERP Vidrio Templado
-- ============================================================================
-- Versión: 1.0.0
-- Fecha: 2025-12-09
-- ============================================================================
-- Schema principal para operaciones de vidrio templado
CREATE SCHEMA IF NOT EXISTS vidrio;
COMMENT ON SCHEMA vidrio IS 'Schema para operaciones de manufactura de vidrio templado';
-- ============================================================================
-- FIN SCHEMAS
-- ============================================================================

30
init/02-rls-functions.sql Normal file
View File

@ -0,0 +1,30 @@
-- ============================================================================
-- FUNCIONES RLS - ERP Vidrio Templado
-- ============================================================================
-- 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 calcular área de vidrio en m2
CREATE OR REPLACE FUNCTION vidrio.calculate_area_m2(
width_mm DECIMAL,
height_mm DECIMAL
)
RETURNS DECIMAL AS $$
BEGIN
RETURN (width_mm / 1000.0) * (height_mm / 1000.0);
END;
$$ LANGUAGE plpgsql IMMUTABLE;
COMMENT ON FUNCTION vidrio.calculate_area_m2 IS
'Calcula el área en metros cuadrados a partir de dimensiones en milímetros';
-- ============================================================================
-- FIN FUNCIONES RLS
-- ============================================================================

716
init/03-vidrio-tables.sql Normal file
View File

@ -0,0 +1,716 @@
-- ============================================================================
-- TABLAS VIDRIO TEMPLADO - ERP Vidrio
-- ============================================================================
-- Módulos: VT-001 (Producción), VT-002 (Calidad), VT-003 (Inventario),
-- VT-004 (Maquinaria), VT-005 (Trazabilidad), VT-006 (Cotizaciones)
-- Versión: 1.0.0
-- Fecha: 2025-12-09
-- ============================================================================
-- PREREQUISITOS:
-- 1. ERP-Core instalado (auth, core, inventory, sales)
-- 2. Schema vidrio creado
-- ============================================================================
-- ============================================================================
-- TYPES (ENUMs)
-- ============================================================================
DO $$ BEGIN
CREATE TYPE vidrio.glass_type AS ENUM (
'clear', 'tinted', 'reflective', 'low_e', 'laminated', 'tempered', 'insulated'
);
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
CREATE TYPE vidrio.production_status AS ENUM (
'draft', 'scheduled', 'cutting', 'processing', 'tempering', 'quality_check', 'completed', 'cancelled'
);
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
CREATE TYPE vidrio.quality_result AS ENUM (
'pending', 'passed', 'failed', 'conditional'
);
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
CREATE TYPE vidrio.furnace_status AS ENUM (
'idle', 'heating', 'ready', 'in_use', 'cooling', 'maintenance'
);
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
CREATE TYPE vidrio.edge_type AS ENUM (
'flat_polished', 'beveled', 'pencil', 'ogee', 'waterfall', 'raw'
);
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- ============================================================================
-- CATÁLOGOS BASE
-- ============================================================================
-- Tabla: glass_catalog (Catálogo de tipos de vidrio)
CREATE TABLE IF NOT EXISTS vidrio.glass_catalog (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
-- Identificación
code VARCHAR(30) NOT NULL,
name VARCHAR(100) NOT NULL,
description TEXT,
-- Tipo y características
glass_type vidrio.glass_type NOT NULL,
thickness_mm DECIMAL(5,2) NOT NULL, -- 3, 4, 5, 6, 8, 10, 12, 15, 19 mm
color VARCHAR(50),
-- Propiedades físicas
weight_per_m2 DECIMAL(8,3), -- kg/m2
max_width_mm INTEGER,
max_height_mm INTEGER,
-- Precios base
price_per_m2 DECIMAL(12,2),
cost_per_m2 DECIMAL(12,2),
-- Control
is_active BOOLEAN NOT NULL DEFAULT TRUE,
-- 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_glass_catalog_code UNIQUE (tenant_id, code)
);
-- Tabla: process_catalog (Catálogo de procesos)
CREATE TABLE IF NOT EXISTS vidrio.process_catalog (
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 proceso
process_type VARCHAR(50) NOT NULL, -- cutting, edging, drilling, tempering, laminating
-- Costos
cost_per_unit DECIMAL(12,2),
cost_per_m2 DECIMAL(12,2),
time_minutes INTEGER,
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_process_catalog_code UNIQUE (tenant_id, code)
);
-- ============================================================================
-- PRODUCCIÓN (VT-001)
-- ============================================================================
-- Tabla: production_orders (Órdenes de producción)
CREATE TABLE IF NOT EXISTS vidrio.production_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
-- Número de orden
order_number VARCHAR(30) NOT NULL,
-- Referencias
sale_order_id UUID, -- FK a sales.sale_orders (ERP Core)
customer_id UUID, -- FK a core.partners (ERP Core)
-- Estado
status vidrio.production_status NOT NULL DEFAULT 'draft',
-- Fechas
order_date DATE NOT NULL DEFAULT CURRENT_DATE,
due_date DATE NOT NULL,
start_date TIMESTAMPTZ,
end_date TIMESTAMPTZ,
-- Prioridad
priority INTEGER DEFAULT 5 CHECK (priority BETWEEN 1 AND 10),
-- Totales
total_pieces INTEGER DEFAULT 0,
total_area_m2 DECIMAL(12,4) DEFAULT 0,
completed_pieces INTEGER DEFAULT 0,
-- Notas
notes TEXT,
internal_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),
deleted_at TIMESTAMPTZ,
deleted_by UUID REFERENCES auth.users(id),
CONSTRAINT uq_production_orders_number UNIQUE (tenant_id, order_number)
);
-- Tabla: production_lines (Piezas de producción)
CREATE TABLE IF NOT EXISTS vidrio.production_lines (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
production_order_id UUID NOT NULL REFERENCES vidrio.production_orders(id) ON DELETE CASCADE,
-- Identificación de pieza
line_number INTEGER NOT NULL,
piece_code VARCHAR(50),
-- Tipo de vidrio
glass_catalog_id UUID NOT NULL REFERENCES vidrio.glass_catalog(id),
-- Dimensiones (en mm)
width_mm DECIMAL(8,2) NOT NULL,
height_mm DECIMAL(8,2) NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
-- Área calculada
area_m2 DECIMAL(12,6) GENERATED ALWAYS AS (
(width_mm / 1000.0) * (height_mm / 1000.0) * quantity
) STORED,
-- Acabados
edge_type vidrio.edge_type DEFAULT 'flat_polished',
has_holes BOOLEAN DEFAULT FALSE,
hole_count INTEGER DEFAULT 0,
has_cutouts BOOLEAN DEFAULT FALSE,
-- Estado
status vidrio.production_status DEFAULT 'draft',
-- Lote de producción
lot_id UUID,
furnace_batch_id UUID,
-- Notas
notes TEXT,
drawing_url VARCHAR(500),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by UUID REFERENCES auth.users(id),
updated_at TIMESTAMPTZ,
updated_by UUID REFERENCES auth.users(id)
);
-- ============================================================================
-- MAQUINARIA (VT-004)
-- ============================================================================
-- Tabla: furnaces (Hornos de templado)
CREATE TABLE IF NOT EXISTS vidrio.furnaces (
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,
-- Capacidad
max_width_mm INTEGER NOT NULL,
max_height_mm INTEGER NOT NULL,
min_thickness_mm DECIMAL(4,2),
max_thickness_mm DECIMAL(4,2),
-- Estado
status vidrio.furnace_status DEFAULT 'idle',
current_temperature INTEGER,
-- Mantenimiento
last_maintenance_date DATE,
next_maintenance_date DATE,
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_furnaces_code UNIQUE (tenant_id, code)
);
-- Tabla: furnace_batches (Lotes de hornada)
CREATE TABLE IF NOT EXISTS vidrio.furnace_batches (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
furnace_id UUID NOT NULL REFERENCES vidrio.furnaces(id),
-- Identificación
batch_number VARCHAR(30) NOT NULL,
-- Tiempos
start_time TIMESTAMPTZ NOT NULL,
end_time TIMESTAMPTZ,
-- Parámetros
temperature INTEGER NOT NULL, -- Temperatura objetivo
cycle_time_minutes INTEGER, -- Tiempo de ciclo
-- Conteo
pieces_count INTEGER DEFAULT 0,
pieces_passed INTEGER DEFAULT 0,
pieces_failed INTEGER DEFAULT 0,
-- Operador
operator_id UUID REFERENCES auth.users(id),
-- Notas
notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by UUID REFERENCES auth.users(id),
CONSTRAINT uq_furnace_batches_number UNIQUE (tenant_id, batch_number)
);
-- Tabla: cutting_machines (Máquinas de corte)
CREATE TABLE IF NOT EXISTS vidrio.cutting_machines (
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,
machine_type VARCHAR(50), -- manual, semi_auto, cnc
-- Capacidad
max_width_mm INTEGER,
max_height_mm INTEGER,
-- Estado
is_active BOOLEAN NOT NULL DEFAULT TRUE,
-- Mantenimiento
last_maintenance_date DATE,
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_cutting_machines_code UNIQUE (tenant_id, code)
);
-- ============================================================================
-- CALIDAD (VT-002)
-- ============================================================================
-- Tabla: quality_tests (Pruebas de calidad)
CREATE TABLE IF NOT EXISTS vidrio.quality_tests (
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 prueba
test_type VARCHAR(50) NOT NULL, -- visual, fragmentation, impact, heat_soak
-- Parámetros
min_value DECIMAL(12,4),
max_value DECIMAL(12,4),
unit VARCHAR(20),
is_mandatory BOOLEAN DEFAULT TRUE,
is_active BOOLEAN 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_quality_tests_code UNIQUE (tenant_id, code)
);
-- Tabla: quality_inspections (Inspecciones de calidad)
CREATE TABLE IF NOT EXISTS vidrio.quality_inspections (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
-- Referencia
production_line_id UUID REFERENCES vidrio.production_lines(id),
furnace_batch_id UUID REFERENCES vidrio.furnace_batches(id),
-- Número
inspection_number VARCHAR(30) NOT NULL,
inspection_date TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Resultado general
result vidrio.quality_result NOT NULL DEFAULT 'pending',
-- Inspector
inspector_id UUID NOT NULL REFERENCES auth.users(id),
-- Notas
notes TEXT,
rejection_reason 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_quality_inspections_number UNIQUE (tenant_id, inspection_number)
);
-- Tabla: quality_test_results (Resultados de pruebas)
CREATE TABLE IF NOT EXISTS vidrio.quality_test_results (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
inspection_id UUID NOT NULL REFERENCES vidrio.quality_inspections(id) ON DELETE CASCADE,
test_id UUID NOT NULL REFERENCES vidrio.quality_tests(id),
-- Resultado
result vidrio.quality_result NOT NULL,
measured_value DECIMAL(12,4),
-- Notas
notes TEXT,
photo_url VARCHAR(500),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by UUID REFERENCES auth.users(id)
);
-- ============================================================================
-- TRAZABILIDAD (VT-005)
-- ============================================================================
-- Tabla: glass_lots (Lotes de vidrio crudo)
CREATE TABLE IF NOT EXISTS vidrio.glass_lots (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
-- Identificación
lot_number VARCHAR(30) NOT NULL,
glass_catalog_id UUID NOT NULL REFERENCES vidrio.glass_catalog(id),
-- Proveedor
supplier_id UUID, -- FK a core.partners (ERP Core)
supplier_lot VARCHAR(50),
purchase_order_id UUID, -- FK a purchase.purchase_orders (ERP Core)
-- Recepción
receipt_date DATE NOT NULL,
quantity_sheets INTEGER NOT NULL,
quantity_remaining INTEGER NOT NULL,
-- Dimensiones del lote
sheet_width_mm INTEGER,
sheet_height_mm INTEGER,
-- Calidad
quality_certificate VARCHAR(100),
inspection_status vidrio.quality_result DEFAULT 'pending',
-- Fechas
expiry_date DATE,
-- 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_glass_lots_number UNIQUE (tenant_id, lot_number)
);
-- Tabla: lot_consumption (Consumo de lotes)
CREATE TABLE IF NOT EXISTS vidrio.lot_consumption (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
glass_lot_id UUID NOT NULL REFERENCES vidrio.glass_lots(id),
production_line_id UUID NOT NULL REFERENCES vidrio.production_lines(id),
quantity_sheets INTEGER NOT NULL DEFAULT 1,
consumption_date TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by UUID REFERENCES auth.users(id)
);
-- ============================================================================
-- COTIZACIONES (VT-006)
-- ============================================================================
-- Tabla: quotations (Cotizaciones de vidrio)
CREATE TABLE IF NOT EXISTS vidrio.quotations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
-- Número
quotation_number VARCHAR(30) NOT NULL,
-- Cliente
customer_id UUID NOT NULL, -- FK a core.partners (ERP Core)
contact_name VARCHAR(200),
contact_email VARCHAR(255),
contact_phone VARCHAR(20),
-- Estado
status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, sent, confirmed, cancelled, expired
-- Fechas
quotation_date DATE NOT NULL DEFAULT CURRENT_DATE,
valid_until DATE NOT NULL,
-- Proyecto/Obra
project_name VARCHAR(200),
project_location VARCHAR(255),
-- Totales
subtotal DECIMAL(14,2) DEFAULT 0,
discount_percent DECIMAL(5,2) DEFAULT 0,
discount_amount DECIMAL(14,2) DEFAULT 0,
tax_amount DECIMAL(14,2) DEFAULT 0,
total DECIMAL(14,2) DEFAULT 0,
-- Conversión
sale_order_id UUID, -- FK a sales.sale_orders cuando se confirma
-- Notas
notes TEXT,
terms_conditions 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_quotations_number UNIQUE (tenant_id, quotation_number)
);
-- Tabla: quotation_lines (Líneas de cotización)
CREATE TABLE IF NOT EXISTS vidrio.quotation_lines (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
quotation_id UUID NOT NULL REFERENCES vidrio.quotations(id) ON DELETE CASCADE,
-- Tipo de vidrio
glass_catalog_id UUID NOT NULL REFERENCES vidrio.glass_catalog(id),
-- Dimensiones
width_mm DECIMAL(8,2) NOT NULL,
height_mm DECIMAL(8,2) NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
-- Área
area_m2 DECIMAL(12,6) GENERATED ALWAYS AS (
(width_mm / 1000.0) * (height_mm / 1000.0) * quantity
) STORED,
-- Acabados
edge_type vidrio.edge_type DEFAULT 'flat_polished',
has_holes BOOLEAN DEFAULT FALSE,
hole_count INTEGER DEFAULT 0,
has_cutouts BOOLEAN DEFAULT FALSE,
-- Precios
price_per_m2 DECIMAL(12,2) NOT NULL,
processing_cost DECIMAL(12,2) DEFAULT 0,
subtotal DECIMAL(14,2) NOT NULL,
-- Descripción
description TEXT,
-- Orden
sequence INTEGER DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by UUID REFERENCES auth.users(id)
);
-- ============================================================================
-- ÍNDICES
-- ============================================================================
-- Glass catalog
CREATE INDEX IF NOT EXISTS idx_glass_catalog_tenant ON vidrio.glass_catalog(tenant_id);
CREATE INDEX IF NOT EXISTS idx_glass_catalog_type ON vidrio.glass_catalog(glass_type);
-- Process catalog
CREATE INDEX IF NOT EXISTS idx_process_catalog_tenant ON vidrio.process_catalog(tenant_id);
-- Production orders
CREATE INDEX IF NOT EXISTS idx_production_orders_tenant ON vidrio.production_orders(tenant_id);
CREATE INDEX IF NOT EXISTS idx_production_orders_status ON vidrio.production_orders(status);
CREATE INDEX IF NOT EXISTS idx_production_orders_customer ON vidrio.production_orders(customer_id);
CREATE INDEX IF NOT EXISTS idx_production_orders_date ON vidrio.production_orders(order_date);
CREATE INDEX IF NOT EXISTS idx_production_orders_due ON vidrio.production_orders(due_date);
-- Production lines
CREATE INDEX IF NOT EXISTS idx_production_lines_tenant ON vidrio.production_lines(tenant_id);
CREATE INDEX IF NOT EXISTS idx_production_lines_order ON vidrio.production_lines(production_order_id);
CREATE INDEX IF NOT EXISTS idx_production_lines_glass ON vidrio.production_lines(glass_catalog_id);
CREATE INDEX IF NOT EXISTS idx_production_lines_status ON vidrio.production_lines(status);
-- Furnaces
CREATE INDEX IF NOT EXISTS idx_furnaces_tenant ON vidrio.furnaces(tenant_id);
CREATE INDEX IF NOT EXISTS idx_furnaces_status ON vidrio.furnaces(status);
-- Furnace batches
CREATE INDEX IF NOT EXISTS idx_furnace_batches_tenant ON vidrio.furnace_batches(tenant_id);
CREATE INDEX IF NOT EXISTS idx_furnace_batches_furnace ON vidrio.furnace_batches(furnace_id);
CREATE INDEX IF NOT EXISTS idx_furnace_batches_date ON vidrio.furnace_batches(start_time);
-- Quality inspections
CREATE INDEX IF NOT EXISTS idx_quality_inspections_tenant ON vidrio.quality_inspections(tenant_id);
CREATE INDEX IF NOT EXISTS idx_quality_inspections_result ON vidrio.quality_inspections(result);
-- Glass lots
CREATE INDEX IF NOT EXISTS idx_glass_lots_tenant ON vidrio.glass_lots(tenant_id);
CREATE INDEX IF NOT EXISTS idx_glass_lots_glass ON vidrio.glass_lots(glass_catalog_id);
CREATE INDEX IF NOT EXISTS idx_glass_lots_supplier ON vidrio.glass_lots(supplier_id);
-- Quotations
CREATE INDEX IF NOT EXISTS idx_quotations_tenant ON vidrio.quotations(tenant_id);
CREATE INDEX IF NOT EXISTS idx_quotations_customer ON vidrio.quotations(customer_id);
CREATE INDEX IF NOT EXISTS idx_quotations_status ON vidrio.quotations(status);
CREATE INDEX IF NOT EXISTS idx_quotations_date ON vidrio.quotations(quotation_date);
-- Quotation lines
CREATE INDEX IF NOT EXISTS idx_quotation_lines_tenant ON vidrio.quotation_lines(tenant_id);
CREATE INDEX IF NOT EXISTS idx_quotation_lines_quotation ON vidrio.quotation_lines(quotation_id);
-- ============================================================================
-- ROW LEVEL SECURITY
-- ============================================================================
ALTER TABLE vidrio.glass_catalog ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.process_catalog ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.production_orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.production_lines ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.furnaces ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.furnace_batches ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.cutting_machines ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.quality_tests ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.quality_inspections ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.quality_test_results ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.glass_lots ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.lot_consumption ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.quotations ENABLE ROW LEVEL SECURITY;
ALTER TABLE vidrio.quotation_lines ENABLE ROW LEVEL SECURITY;
-- Políticas de aislamiento por tenant
DO $$ BEGIN
DROP POLICY IF EXISTS tenant_isolation_glass_catalog ON vidrio.glass_catalog;
CREATE POLICY tenant_isolation_glass_catalog ON vidrio.glass_catalog
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_process_catalog ON vidrio.process_catalog;
CREATE POLICY tenant_isolation_process_catalog ON vidrio.process_catalog
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_production_orders ON vidrio.production_orders;
CREATE POLICY tenant_isolation_production_orders ON vidrio.production_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_production_lines ON vidrio.production_lines;
CREATE POLICY tenant_isolation_production_lines ON vidrio.production_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_furnaces ON vidrio.furnaces;
CREATE POLICY tenant_isolation_furnaces ON vidrio.furnaces
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_furnace_batches ON vidrio.furnace_batches;
CREATE POLICY tenant_isolation_furnace_batches ON vidrio.furnace_batches
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_cutting_machines ON vidrio.cutting_machines;
CREATE POLICY tenant_isolation_cutting_machines ON vidrio.cutting_machines
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_quality_tests ON vidrio.quality_tests;
CREATE POLICY tenant_isolation_quality_tests ON vidrio.quality_tests
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_quality_inspections ON vidrio.quality_inspections;
CREATE POLICY tenant_isolation_quality_inspections ON vidrio.quality_inspections
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_quality_test_results ON vidrio.quality_test_results;
CREATE POLICY tenant_isolation_quality_test_results ON vidrio.quality_test_results
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_glass_lots ON vidrio.glass_lots;
CREATE POLICY tenant_isolation_glass_lots ON vidrio.glass_lots
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_lot_consumption ON vidrio.lot_consumption;
CREATE POLICY tenant_isolation_lot_consumption ON vidrio.lot_consumption
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_quotations ON vidrio.quotations;
CREATE POLICY tenant_isolation_quotations ON vidrio.quotations
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_quotation_lines ON vidrio.quotation_lines;
CREATE POLICY tenant_isolation_quotation_lines ON vidrio.quotation_lines
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
EXCEPTION WHEN undefined_object THEN NULL; END $$;
-- ============================================================================
-- COMENTARIOS
-- ============================================================================
COMMENT ON TABLE vidrio.glass_catalog IS 'Catálogo de tipos de vidrio';
COMMENT ON TABLE vidrio.process_catalog IS 'Catálogo de procesos de manufactura';
COMMENT ON TABLE vidrio.production_orders IS 'Órdenes de producción';
COMMENT ON TABLE vidrio.production_lines IS 'Piezas individuales de producción';
COMMENT ON TABLE vidrio.furnaces IS 'Hornos de templado';
COMMENT ON TABLE vidrio.furnace_batches IS 'Lotes de hornada';
COMMENT ON TABLE vidrio.cutting_machines IS 'Máquinas de corte';
COMMENT ON TABLE vidrio.quality_tests IS 'Catálogo de pruebas de calidad';
COMMENT ON TABLE vidrio.quality_inspections IS 'Inspecciones de calidad';
COMMENT ON TABLE vidrio.quality_test_results IS 'Resultados de pruebas';
COMMENT ON TABLE vidrio.glass_lots IS 'Lotes de vidrio crudo (trazabilidad)';
COMMENT ON TABLE vidrio.lot_consumption IS 'Consumo de lotes en producción';
COMMENT ON TABLE vidrio.quotations IS 'Cotizaciones de vidrio';
COMMENT ON TABLE vidrio.quotation_lines IS 'Piezas cotizadas';
-- ============================================================================
-- FIN TABLAS VIDRIO
-- Total: 14 tablas, 5 ENUMs
-- ============================================================================

View File

@ -0,0 +1,53 @@
-- ============================================================================
-- FINANCIAL EXTENSIONS - FASE 8 ERP-Core
-- ERP Vidrio Templado
-- ============================================================================
CREATE SCHEMA IF NOT EXISTS financial;
DO $$ BEGIN
CREATE TYPE financial.payment_method_type AS ENUM ('inbound', 'outbound');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
-- Métodos de pago
CREATE TABLE IF NOT EXISTS financial.payment_methods (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
name VARCHAR(100) NOT NULL,
code VARCHAR(20) NOT NULL,
payment_type financial.payment_method_type NOT NULL,
-- Extensiones vidrio
aplica_anticipo BOOLEAN DEFAULT false,
porcentaje_anticipo NUMERIC(5,2) DEFAULT 0,
active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT uq_payment_methods_code UNIQUE(tenant_id, code)
);
-- Términos de pago
CREATE TABLE IF NOT EXISTS financial.payment_term_lines (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
payment_term_id UUID,
value_type VARCHAR(20) DEFAULT 'percent',
value NUMERIC(10,2) DEFAULT 0,
days INTEGER DEFAULT 0,
applies_to VARCHAR(50), -- 'anticipo', 'produccion', 'instalacion', 'finiquito'
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Índices y RLS
CREATE INDEX IF NOT EXISTS idx_payment_methods_tenant ON financial.payment_methods(tenant_id);
CREATE INDEX IF NOT EXISTS idx_payment_term_lines_tenant ON financial.payment_term_lines(tenant_id);
ALTER TABLE financial.payment_methods ENABLE ROW LEVEL SECURITY;
ALTER TABLE financial.payment_term_lines ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS tenant_isolation_payment_methods ON financial.payment_methods;
CREATE POLICY tenant_isolation_payment_methods ON financial.payment_methods
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_payment_term_lines ON financial.payment_term_lines;
CREATE POLICY tenant_isolation_payment_term_lines ON financial.payment_term_lines
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

View File

@ -0,0 +1,181 @@
-- ============================================================================
-- HR EXTENSIONS - FASE 8 ERP-Core
-- ERP Vidrio Templado
-- ============================================================================
CREATE SCHEMA IF NOT EXISTS hr;
DO $$ BEGIN
CREATE TYPE hr.expense_status AS ENUM ('draft', 'submitted', 'approved', 'posted', 'paid', 'rejected');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
CREATE TYPE hr.payslip_status AS ENUM ('draft', 'verify', 'done', 'cancel');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
-- Ubicaciones (áreas de producción)
CREATE TABLE IF NOT EXISTS hr.work_locations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
name VARCHAR(100) NOT NULL,
-- Extensiones vidrio
tipo_area VARCHAR(50), -- 'corte', 'pulido', 'templado', 'almacen', 'instalacion'
capacidad_m2 NUMERIC(10,2),
tiene_horno BOOLEAN DEFAULT false,
temperatura_max INTEGER,
active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Tipos de habilidad
CREATE TABLE IF NOT EXISTS hr.skill_types (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Habilidades
CREATE TABLE IF NOT EXISTS hr.skills (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
skill_type_id UUID REFERENCES hr.skill_types(id),
name VARCHAR(100) NOT NULL,
-- Extensiones vidrio
tipo_proceso VARCHAR(50), -- 'corte', 'biselado', 'templado', 'instalacion'
maquina_habilitado VARCHAR(100),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Niveles
CREATE TABLE IF NOT EXISTS hr.skill_levels (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
skill_type_id UUID REFERENCES hr.skill_types(id),
name VARCHAR(100) NOT NULL,
level INTEGER DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Habilidades de empleado
CREATE TABLE IF NOT EXISTS hr.employee_skills (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
employee_id UUID NOT NULL,
skill_id UUID REFERENCES hr.skills(id),
skill_level_id UUID REFERENCES hr.skill_levels(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Gastos
CREATE TABLE IF NOT EXISTS hr.expense_sheets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
employee_id UUID NOT NULL,
name VARCHAR(100) NOT NULL,
state hr.expense_status DEFAULT 'draft',
total_amount NUMERIC(12,2) DEFAULT 0,
proyecto_id UUID,
obra_id UUID,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS hr.expenses (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
sheet_id UUID REFERENCES hr.expense_sheets(id) ON DELETE CASCADE,
employee_id UUID NOT NULL,
name VARCHAR(200) NOT NULL,
date DATE DEFAULT CURRENT_DATE,
total_amount NUMERIC(12,2) NOT NULL,
state hr.expense_status DEFAULT 'draft',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Nómina
CREATE TABLE IF NOT EXISTS hr.payslip_structures (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
name VARCHAR(100) NOT NULL,
code VARCHAR(20) NOT NULL,
tipo_pago VARCHAR(50),
active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT uq_payslip_structures_code UNIQUE(tenant_id, code)
);
CREATE TABLE IF NOT EXISTS hr.payslips (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
employee_id UUID NOT NULL,
structure_id UUID REFERENCES hr.payslip_structures(id),
date_from DATE NOT NULL,
date_to DATE NOT NULL,
state hr.payslip_status DEFAULT 'draft',
gross NUMERIC(12,2) DEFAULT 0,
net NUMERIC(12,2) DEFAULT 0,
area_id UUID REFERENCES hr.work_locations(id),
metros_producidos NUMERIC(10,2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS hr.payslip_lines (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
payslip_id UUID REFERENCES hr.payslips(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL,
code VARCHAR(20),
amount NUMERIC(12,2) DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Índices
CREATE INDEX IF NOT EXISTS idx_work_locations_tenant ON hr.work_locations(tenant_id);
CREATE INDEX IF NOT EXISTS idx_skill_types_tenant ON hr.skill_types(tenant_id);
CREATE INDEX IF NOT EXISTS idx_skills_tenant ON hr.skills(tenant_id);
CREATE INDEX IF NOT EXISTS idx_expense_sheets_tenant ON hr.expense_sheets(tenant_id);
CREATE INDEX IF NOT EXISTS idx_payslips_tenant ON hr.payslips(tenant_id);
-- RLS
ALTER TABLE hr.work_locations ENABLE ROW LEVEL SECURITY;
ALTER TABLE hr.skill_types ENABLE ROW LEVEL SECURITY;
ALTER TABLE hr.skills ENABLE ROW LEVEL SECURITY;
ALTER TABLE hr.skill_levels ENABLE ROW LEVEL SECURITY;
ALTER TABLE hr.expense_sheets ENABLE ROW LEVEL SECURITY;
ALTER TABLE hr.expenses ENABLE ROW LEVEL SECURITY;
ALTER TABLE hr.payslip_structures ENABLE ROW LEVEL SECURITY;
ALTER TABLE hr.payslips ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS tenant_isolation_work_locations ON hr.work_locations;
CREATE POLICY tenant_isolation_work_locations ON hr.work_locations
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_skill_types ON hr.skill_types;
CREATE POLICY tenant_isolation_skill_types ON hr.skill_types
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_skills ON hr.skills;
CREATE POLICY tenant_isolation_skills ON hr.skills
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_skill_levels ON hr.skill_levels;
CREATE POLICY tenant_isolation_skill_levels ON hr.skill_levels
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_expense_sheets ON hr.expense_sheets;
CREATE POLICY tenant_isolation_expense_sheets ON hr.expense_sheets
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_expenses ON hr.expenses;
CREATE POLICY tenant_isolation_expenses ON hr.expenses
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_payslip_structures ON hr.payslip_structures;
CREATE POLICY tenant_isolation_payslip_structures ON hr.payslip_structures
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
DROP POLICY IF EXISTS tenant_isolation_payslips ON hr.payslips;
CREATE POLICY tenant_isolation_payslips ON hr.payslips
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

View File

@ -0,0 +1,50 @@
-- ============================================================================
-- SEED DATA: Catálogos Vidrio Templado
-- FASE-8 ERP-Core - ERP Vidrio Templado
-- ============================================================================
-- Métodos de pago
INSERT INTO financial.payment_methods (tenant_id, name, code, payment_type, aplica_anticipo, porcentaje_anticipo)
SELECT current_setting('app.current_tenant_id', true)::UUID, name, code, payment_type::financial.payment_method_type, anticipo, porc
FROM (VALUES
('Anticipo 50%', 'anticipo', 'inbound', true, 50),
('Pago Producción', 'produccion', 'inbound', false, 0),
('Finiquito Instalación', 'finiquito', 'inbound', false, 0),
('Transferencia', 'transfer', 'inbound', false, 0),
('Efectivo', 'efectivo', 'inbound', false, 0)
) AS t(name, code, payment_type, anticipo, porc)
WHERE current_setting('app.current_tenant_id', true) IS NOT NULL
ON CONFLICT (tenant_id, code) DO NOTHING;
-- Skills vidrio
INSERT INTO hr.skill_types (tenant_id, name)
SELECT current_setting('app.current_tenant_id', true)::UUID, name
FROM (VALUES ('Proceso de Producción'), ('Instalación'), ('Maquinaria'), ('Calidad')) AS t(name)
WHERE current_setting('app.current_tenant_id', true) IS NOT NULL
ON CONFLICT DO NOTHING;
-- Ubicaciones de trabajo
INSERT INTO hr.work_locations (tenant_id, name, tipo_area, capacidad_m2, tiene_horno, temperatura_max)
SELECT current_setting('app.current_tenant_id', true)::UUID, name, tipo, cap, horno, temp
FROM (VALUES
('Área de Corte', 'corte', 200.0, false, NULL),
('Área de Pulido y Biselado', 'pulido', 100.0, false, NULL),
('Horno de Templado', 'templado', 150.0, true, 700),
('Almacén Materia Prima', 'almacen', 300.0, false, NULL),
('Almacén Producto Terminado', 'almacen', 200.0, false, NULL),
('Cuadrilla Instalación 1', 'instalacion', NULL, false, NULL),
('Cuadrilla Instalación 2', 'instalacion', NULL, false, NULL)
) AS t(name, tipo, cap, horno, temp)
WHERE current_setting('app.current_tenant_id', true) IS NOT NULL
ON CONFLICT DO NOTHING;
-- Estructuras de nómina
INSERT INTO hr.payslip_structures (tenant_id, name, code, tipo_pago)
SELECT current_setting('app.current_tenant_id', true)::UUID, name, code, tipo
FROM (VALUES
('Nómina Semanal', 'NOM-SEM', 'semanal'),
('Destajo Producción', 'DEST-PROD', 'destajo'),
('Destajo Instalación', 'DEST-INST', 'destajo')
) AS t(name, code, tipo)
WHERE current_setting('app.current_tenant_id', true) IS NOT NULL
ON CONFLICT (tenant_id, code) DO NOTHING;