Some checks failed
CI Pipeline / Lint & Type Check (push) Has been cancelled
CI Pipeline / Validate SSOT Constants (push) Has been cancelled
CI Pipeline / Backend Tests (push) Has been cancelled
CI Pipeline / Frontend Tests (push) Has been cancelled
CI Pipeline / Build (push) Has been cancelled
CI Pipeline / Docker Build (push) Has been cancelled
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1022 lines
43 KiB
Markdown
1022 lines
43 KiB
Markdown
# FASE 6: Plan Refinado - ERP Construcción
|
|
|
|
**Proyecto:** erp-construccion
|
|
**Fecha:** 2026-01-04
|
|
**Estado:** Completado
|
|
**Base:** FASE-5-ANALISIS-DEPENDENCIAS.md
|
|
|
|
---
|
|
|
|
## 1. Ajustes al Plan Original
|
|
|
|
### 1.1 Cambios Incorporados
|
|
|
|
| # | Cambio | Razón |
|
|
|---|--------|-------|
|
|
| 1 | `proyecto_id` → `fraccionamiento_id` | No existe tabla proyectos |
|
|
| 2 | `projects.collaborators` → `construction.colaboradores_obra` | Adaptación al giro |
|
|
| 3 | FKs opcionales a Core | Permitir instalación independiente |
|
|
| 4 | Trigger adaptado | Usar avances_obra en lugar de partidas |
|
|
| 5 | Orden de ejecución revisado | Respetar dependencias |
|
|
|
|
### 1.2 Archivos Finales
|
|
|
|
| Archivo | Acción | Contenido |
|
|
|---------|--------|-----------|
|
|
| 08-financial-ext-schema-ddl.sql | Crear | 5 tablas, 2 ENUMs |
|
|
| 09-projects-ext-schema-ddl.sql | Crear | 3 tablas, 2 funciones (adaptado) |
|
|
| 02-hr-schema-ddl.sql | Modificar | +11 tablas, +3 ENUMs |
|
|
| 06-inventory-ext-schema-ddl.sql | Modificar | +5 tablas |
|
|
| 07-purchase-ext-schema-ddl.sql | Modificar | +1 tabla, +1 función |
|
|
| migrations/20260104_fase8.sql | Crear | Migración consolidada |
|
|
| seeds/*.sql | Crear | 4 archivos seed |
|
|
|
|
---
|
|
|
|
## 2. Scripts Finales Refinados
|
|
|
|
### 2.1 08-financial-ext-schema-ddl.sql (REFINADO)
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- FINANCIAL EXTENSION Schema DDL - Extensiones Financieras para Construcción
|
|
-- Modulos: FASE-8 ERP-Core (COR-035 a COR-039)
|
|
-- Version: 1.0.0
|
|
-- Fecha: 2026-01-04
|
|
-- ============================================================================
|
|
-- PREREQUISITOS:
|
|
-- 1. ERP-Core instalado (auth.tenants, auth.users)
|
|
-- 2. Schema construction instalado
|
|
-- NOTA: FKs a tablas de ERP-Core son opcionales
|
|
-- ============================================================================
|
|
|
|
-- Verificar prerequisitos mínimos
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'auth') THEN
|
|
RAISE EXCEPTION 'Schema auth no existe. ERP-Core debe estar instalado';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- Crear schema si no existe
|
|
CREATE SCHEMA IF NOT EXISTS financial;
|
|
|
|
-- ============================================================================
|
|
-- TYPES (ENUMs)
|
|
-- ============================================================================
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE financial.payment_method_type AS ENUM ('inbound', 'outbound');
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE financial.reconcile_model_type AS ENUM (
|
|
'writeoff_button', 'writeoff_suggestion', 'invoice_matching'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
-- ============================================================================
|
|
-- TABLAS
|
|
-- ============================================================================
|
|
|
|
-- 1. Incoterms (COR-036) - Catálogo estándar
|
|
CREATE TABLE IF NOT EXISTS financial.incoterms (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
code VARCHAR(3) NOT NULL,
|
|
name VARCHAR(100) NOT NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT uq_incoterms_code UNIQUE (code)
|
|
);
|
|
|
|
COMMENT ON TABLE financial.incoterms IS 'ERP-Core FASE-8 COR-036: Términos de comercio internacional';
|
|
|
|
-- 2. Payment Methods (COR-037)
|
|
CREATE TABLE IF NOT EXISTS financial.payment_methods (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
name VARCHAR(100) NOT NULL,
|
|
code VARCHAR(50) NOT NULL,
|
|
payment_type financial.payment_method_type NOT NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT uq_payment_methods_code UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_payment_methods_tenant ON financial.payment_methods(tenant_id);
|
|
|
|
ALTER TABLE financial.payment_methods ENABLE ROW LEVEL SECURITY;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_payment_methods ON financial.payment_methods;
|
|
CREATE POLICY tenant_isolation_payment_methods ON financial.payment_methods
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
COMMENT ON TABLE financial.payment_methods IS 'ERP-Core FASE-8 COR-037: Métodos de pago';
|
|
|
|
-- 3. Payment Term Lines (COR-035)
|
|
CREATE TABLE IF NOT EXISTS financial.payment_term_lines (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
payment_term_id UUID, -- FK opcional a financial.payment_terms (ERP-Core)
|
|
sequence INTEGER NOT NULL DEFAULT 10,
|
|
value VARCHAR(20) NOT NULL DEFAULT 'percent',
|
|
value_amount DECIMAL(10,4) NOT NULL DEFAULT 100,
|
|
days INTEGER NOT NULL DEFAULT 0,
|
|
day_of_month INTEGER,
|
|
end_month BOOLEAN DEFAULT FALSE,
|
|
-- Extensión construcción
|
|
applies_to VARCHAR(50),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT chk_ptl_value CHECK (value IN ('percent', 'fixed', 'balance')),
|
|
CONSTRAINT chk_ptl_applies CHECK (applies_to IS NULL OR applies_to IN ('anticipo', 'estimacion', 'retencion', 'finiquito'))
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_payment_term_lines_tenant ON financial.payment_term_lines(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_payment_term_lines_term ON financial.payment_term_lines(payment_term_id);
|
|
|
|
ALTER TABLE financial.payment_term_lines ENABLE ROW LEVEL SECURITY;
|
|
|
|
DO $$ BEGIN
|
|
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
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
COMMENT ON TABLE financial.payment_term_lines IS 'ERP-Core FASE-8 COR-035: Líneas de términos de pago';
|
|
|
|
-- 4. Reconcile Models (COR-038)
|
|
CREATE TABLE IF NOT EXISTS financial.reconcile_models (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
company_id UUID, -- FK opcional a core.companies (ERP-Core)
|
|
name VARCHAR(100) NOT NULL,
|
|
sequence INTEGER DEFAULT 10,
|
|
rule_type financial.reconcile_model_type NOT NULL DEFAULT 'writeoff_suggestion',
|
|
auto_reconcile BOOLEAN DEFAULT FALSE,
|
|
match_amount VARCHAR(20) DEFAULT 'percentage',
|
|
match_amount_min DECIMAL(5,2),
|
|
match_amount_max DECIMAL(5,2),
|
|
match_label VARCHAR(100),
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT chk_rm_match CHECK (match_amount IN ('percentage', 'fixed', 'any'))
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_reconcile_models_tenant ON financial.reconcile_models(tenant_id);
|
|
|
|
ALTER TABLE financial.reconcile_models ENABLE ROW LEVEL SECURITY;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_reconcile_models ON financial.reconcile_models;
|
|
CREATE POLICY tenant_isolation_reconcile_models ON financial.reconcile_models
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
COMMENT ON TABLE financial.reconcile_models IS 'ERP-Core FASE-8 COR-038: Modelos de conciliación';
|
|
|
|
-- 5. Reconcile Model Lines (COR-038)
|
|
CREATE TABLE IF NOT EXISTS financial.reconcile_model_lines (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
model_id UUID NOT NULL REFERENCES financial.reconcile_models(id) ON DELETE CASCADE,
|
|
sequence INTEGER DEFAULT 10,
|
|
account_id UUID, -- FK opcional a financial.accounts (ERP-Core)
|
|
amount_type VARCHAR(20) NOT NULL DEFAULT 'percentage',
|
|
amount_value DECIMAL(18,4) NOT NULL DEFAULT 100,
|
|
label VARCHAR(100),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT chk_rml_amount CHECK (amount_type IN ('percentage', 'fixed', 'regex'))
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_reconcile_model_lines_model ON financial.reconcile_model_lines(model_id);
|
|
|
|
COMMENT ON TABLE financial.reconcile_model_lines IS 'ERP-Core FASE-8 COR-038: Líneas de modelos de conciliación';
|
|
|
|
-- ============================================================================
|
|
-- FIN FINANCIAL EXTENSION
|
|
-- Total: 5 tablas, 2 ENUMs
|
|
-- ============================================================================
|
|
```
|
|
|
|
---
|
|
|
|
### 2.2 09-projects-ext-schema-ddl.sql (REFINADO)
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- PROJECTS/CONSTRUCTION EXTENSION Schema DDL
|
|
-- Modulos: FASE-8 ERP-Core (COR-056 a COR-060) - ADAPTADO A CONSTRUCCIÓN
|
|
-- Version: 1.0.0
|
|
-- Fecha: 2026-01-04
|
|
-- ============================================================================
|
|
-- ADAPTACIONES:
|
|
-- - proyecto_id → fraccionamiento_id
|
|
-- - projects.collaborators → construction.colaboradores_obra
|
|
-- - burndown_chart_data → construction.avance_programado
|
|
-- ============================================================================
|
|
|
|
-- Verificar prerequisitos
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'construction') THEN
|
|
RAISE EXCEPTION 'Schema construction no existe';
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'construction' AND tablename = 'fraccionamientos') THEN
|
|
RAISE EXCEPTION 'Tabla construction.fraccionamientos no existe';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- Crear schema projects si no existe (para ratings genéricos)
|
|
CREATE SCHEMA IF NOT EXISTS projects;
|
|
|
|
-- ============================================================================
|
|
-- 1. COLABORADORES DE OBRA (COR-056 adaptado)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS construction.colaboradores_obra (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id) ON DELETE CASCADE,
|
|
partner_id UUID, -- FK opcional a core.partners
|
|
user_id UUID REFERENCES auth.users(id),
|
|
nombre VARCHAR(100),
|
|
email VARCHAR(255),
|
|
telefono VARCHAR(20),
|
|
can_read BOOLEAN DEFAULT TRUE,
|
|
can_write BOOLEAN DEFAULT FALSE,
|
|
rol VARCHAR(50),
|
|
vigencia_desde DATE,
|
|
vigencia_hasta DATE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
CONSTRAINT chk_colob_partner_or_user CHECK (partner_id IS NOT NULL OR user_id IS NOT NULL OR nombre IS NOT NULL),
|
|
CONSTRAINT chk_colob_rol CHECK (rol IS NULL OR rol IN ('supervisor', 'perito', 'representante', 'infonavit', 'contratista', 'auditor', 'cliente'))
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_colaboradores_obra_tenant ON construction.colaboradores_obra(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_colaboradores_obra_fracc ON construction.colaboradores_obra(fraccionamiento_id);
|
|
CREATE INDEX IF NOT EXISTS idx_colaboradores_obra_user ON construction.colaboradores_obra(user_id);
|
|
|
|
ALTER TABLE construction.colaboradores_obra ENABLE ROW LEVEL SECURITY;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_colaboradores_obra ON construction.colaboradores_obra;
|
|
CREATE POLICY tenant_isolation_colaboradores_obra ON construction.colaboradores_obra
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
COMMENT ON TABLE construction.colaboradores_obra IS 'ERP-Core FASE-8 COR-056 adaptado: Colaboradores externos de obra';
|
|
|
|
-- ============================================================================
|
|
-- 2. RATINGS/CALIFICACIONES (COR-059)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS projects.ratings (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
res_model VARCHAR(100) NOT NULL,
|
|
res_id UUID NOT NULL,
|
|
rating DECIMAL(3,2) NOT NULL,
|
|
feedback TEXT,
|
|
partner_id UUID, -- FK opcional a core.partners
|
|
rated_by UUID REFERENCES auth.users(id),
|
|
fraccionamiento_id UUID REFERENCES construction.fraccionamientos(id),
|
|
tipo_trabajo VARCHAR(50),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT chk_rating_range CHECK (rating >= 1 AND rating <= 5)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_ratings_tenant ON projects.ratings(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ratings_res ON projects.ratings(res_model, res_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ratings_fracc ON projects.ratings(fraccionamiento_id);
|
|
|
|
ALTER TABLE projects.ratings ENABLE ROW LEVEL SECURITY;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_ratings ON projects.ratings;
|
|
CREATE POLICY tenant_isolation_ratings ON projects.ratings
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
COMMENT ON TABLE projects.ratings IS 'ERP-Core FASE-8 COR-059: Calificaciones de proveedores/contratistas';
|
|
|
|
-- ============================================================================
|
|
-- 3. AVANCE PROGRAMADO (COR-060 adaptado)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS construction.avance_programado (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id) ON DELETE CASCADE,
|
|
fecha DATE NOT NULL,
|
|
-- Avance físico
|
|
total_conceptos INTEGER DEFAULT 0,
|
|
conceptos_completados INTEGER DEFAULT 0,
|
|
conceptos_pendientes INTEGER DEFAULT 0,
|
|
conceptos_en_proceso INTEGER DEFAULT 0,
|
|
-- Avance financiero
|
|
presupuesto_total DECIMAL(20,2) DEFAULT 0,
|
|
ejercido DECIMAL(20,2) DEFAULT 0,
|
|
por_ejercer DECIMAL(20,2) DEFAULT 0,
|
|
estimado_actual DECIMAL(20,2) DEFAULT 0,
|
|
-- Porcentajes
|
|
avance_fisico_programado DECIMAL(5,2) DEFAULT 0,
|
|
avance_fisico_real DECIMAL(5,2) DEFAULT 0,
|
|
avance_financiero_pct DECIMAL(5,2) DEFAULT 0,
|
|
-- Horas
|
|
horas_programadas DECIMAL(10,2) DEFAULT 0,
|
|
horas_ejecutadas DECIMAL(10,2) DEFAULT 0,
|
|
-- Metadatos
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_avance_fracc_fecha UNIQUE (fraccionamiento_id, fecha)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_avance_programado_tenant ON construction.avance_programado(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_avance_programado_fracc ON construction.avance_programado(fraccionamiento_id);
|
|
CREATE INDEX IF NOT EXISTS idx_avance_programado_fecha ON construction.avance_programado(fecha);
|
|
|
|
ALTER TABLE construction.avance_programado ENABLE ROW LEVEL SECURITY;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_avance_programado ON construction.avance_programado;
|
|
CREATE POLICY tenant_isolation_avance_programado ON construction.avance_programado
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
COMMENT ON TABLE construction.avance_programado IS 'ERP-Core FASE-8 COR-060 adaptado: Snapshots de avance de obra';
|
|
|
|
-- ============================================================================
|
|
-- 4. CAMPOS ADICIONALES EN FRACCIONAMIENTOS (COR-057)
|
|
-- ============================================================================
|
|
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'construction' AND table_name = 'fraccionamientos' AND column_name = 'sequence') THEN
|
|
ALTER TABLE construction.fraccionamientos ADD COLUMN sequence INTEGER DEFAULT 10;
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'construction' AND table_name = 'fraccionamientos' AND column_name = 'is_favorite') THEN
|
|
ALTER TABLE construction.fraccionamientos ADD COLUMN is_favorite BOOLEAN DEFAULT FALSE;
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'construction' AND table_name = 'fraccionamientos' AND column_name = 'avance_count') THEN
|
|
ALTER TABLE construction.fraccionamientos ADD COLUMN avance_count INTEGER DEFAULT 0;
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'construction' AND table_name = 'fraccionamientos' AND column_name = 'avance_pct') THEN
|
|
ALTER TABLE construction.fraccionamientos ADD COLUMN avance_pct DECIMAL(5,2) DEFAULT 0;
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'construction' AND table_name = 'fraccionamientos' AND column_name = 'status_avance') THEN
|
|
ALTER TABLE construction.fraccionamientos ADD COLUMN status_avance VARCHAR(20) DEFAULT 'on_track';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- 5. FUNCIÓN SNAPSHOT DE AVANCE (COR-060)
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION construction.generate_avance_snapshot(
|
|
p_fraccionamiento_id UUID,
|
|
p_fecha DATE DEFAULT CURRENT_DATE
|
|
)
|
|
RETURNS UUID AS $$
|
|
DECLARE
|
|
v_id UUID;
|
|
v_tenant_id UUID;
|
|
v_total INTEGER := 0;
|
|
v_completados INTEGER := 0;
|
|
v_pendientes INTEGER := 0;
|
|
v_en_proceso INTEGER := 0;
|
|
v_presupuesto DECIMAL(20,2) := 0;
|
|
v_ejercido DECIMAL(20,2) := 0;
|
|
BEGIN
|
|
-- Obtener tenant
|
|
SELECT tenant_id INTO v_tenant_id
|
|
FROM construction.fraccionamientos
|
|
WHERE id = p_fraccionamiento_id;
|
|
|
|
IF v_tenant_id IS NULL THEN
|
|
RAISE EXCEPTION 'Fraccionamiento no encontrado: %', p_fraccionamiento_id;
|
|
END IF;
|
|
|
|
-- Contar avances por estado
|
|
SELECT
|
|
COUNT(*),
|
|
COUNT(*) FILTER (WHERE status = 'approved'),
|
|
COUNT(*) FILTER (WHERE status = 'pending'),
|
|
COUNT(*) FILTER (WHERE status IN ('captured', 'reviewed'))
|
|
INTO v_total, v_completados, v_pendientes, v_en_proceso
|
|
FROM construction.avances_obra ao
|
|
JOIN construction.lotes l ON ao.lote_id = l.id
|
|
JOIN construction.manzanas m ON l.manzana_id = m.id
|
|
JOIN construction.etapas e ON m.etapa_id = e.id
|
|
WHERE e.fraccionamiento_id = p_fraccionamiento_id;
|
|
|
|
-- Obtener montos de presupuesto
|
|
SELECT
|
|
COALESCE(SUM(pp.quantity * pp.unit_price), 0),
|
|
0 -- ejercido se calcularía de otra forma
|
|
INTO v_presupuesto, v_ejercido
|
|
FROM construction.presupuestos p
|
|
JOIN construction.presupuesto_partidas pp ON pp.presupuesto_id = p.id
|
|
WHERE p.fraccionamiento_id = p_fraccionamiento_id
|
|
AND p.is_active = TRUE;
|
|
|
|
-- Insertar o actualizar snapshot
|
|
INSERT INTO construction.avance_programado (
|
|
tenant_id, fraccionamiento_id, fecha,
|
|
total_conceptos, conceptos_completados, conceptos_pendientes, conceptos_en_proceso,
|
|
presupuesto_total, ejercido, por_ejercer,
|
|
avance_fisico_real
|
|
) VALUES (
|
|
v_tenant_id, p_fraccionamiento_id, p_fecha,
|
|
v_total, v_completados, v_pendientes, v_en_proceso,
|
|
v_presupuesto, v_ejercido, v_presupuesto - v_ejercido,
|
|
CASE WHEN v_total > 0 THEN (v_completados::DECIMAL / v_total) * 100 ELSE 0 END
|
|
)
|
|
ON CONFLICT (fraccionamiento_id, fecha) DO UPDATE SET
|
|
total_conceptos = EXCLUDED.total_conceptos,
|
|
conceptos_completados = EXCLUDED.conceptos_completados,
|
|
conceptos_pendientes = EXCLUDED.conceptos_pendientes,
|
|
conceptos_en_proceso = EXCLUDED.conceptos_en_proceso,
|
|
presupuesto_total = EXCLUDED.presupuesto_total,
|
|
ejercido = EXCLUDED.ejercido,
|
|
por_ejercer = EXCLUDED.por_ejercer,
|
|
avance_fisico_real = EXCLUDED.avance_fisico_real
|
|
RETURNING id INTO v_id;
|
|
|
|
RETURN v_id;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
COMMENT ON FUNCTION construction.generate_avance_snapshot IS 'Genera snapshot diario de avance de obra';
|
|
|
|
-- ============================================================================
|
|
-- FIN PROJECTS/CONSTRUCTION EXTENSION
|
|
-- Total: 3 tablas, 2 funciones
|
|
-- ============================================================================
|
|
```
|
|
|
|
---
|
|
|
|
### 2.3 Adiciones a 02-hr-schema-ddl.sql (REFINADO)
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- HR EXTENSION - FASE-8 ERP-Core
|
|
-- Agregar al final de 02-hr-schema-ddl.sql
|
|
-- ============================================================================
|
|
|
|
-- ============================================================================
|
|
-- ENUMS ADICIONALES
|
|
-- ============================================================================
|
|
|
|
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.resume_line_type AS ENUM ('experience', 'education', 'certification', 'internal');
|
|
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 $$;
|
|
|
|
-- ============================================================================
|
|
-- 1. WORK LOCATIONS (COR-062)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS hr.work_locations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
company_id UUID, -- FK opcional
|
|
name VARCHAR(100) NOT NULL,
|
|
location_type VARCHAR(50) DEFAULT 'office',
|
|
address_id UUID, -- FK opcional
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT chk_wl_type CHECK (location_type IN ('office', 'home', 'obra', 'other'))
|
|
);
|
|
|
|
ALTER TABLE hr.work_locations ENABLE ROW LEVEL SECURITY;
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_work_locations ON hr.work_locations;
|
|
CREATE POLICY tenant_isolation_work_locations ON hr.work_locations
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
-- Extensión: Ubicaciones de obra
|
|
CREATE TABLE IF NOT EXISTS construction.ubicaciones_obra (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
work_location_id UUID REFERENCES hr.work_locations(id),
|
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
|
tipo VARCHAR(50) NOT NULL,
|
|
nombre VARCHAR(100),
|
|
coordenadas_lat DECIMAL(10,8),
|
|
coordenadas_lng DECIMAL(11,8),
|
|
radio_metros INTEGER DEFAULT 100,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT chk_uo_tipo CHECK (tipo IN ('frente', 'almacen', 'oficina_obra', 'caseta', 'acceso'))
|
|
);
|
|
|
|
ALTER TABLE construction.ubicaciones_obra ENABLE ROW LEVEL SECURITY;
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_ubicaciones_obra ON construction.ubicaciones_obra;
|
|
CREATE POLICY tenant_isolation_ubicaciones_obra ON construction.ubicaciones_obra
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
-- ============================================================================
|
|
-- 2. SKILLS SYSTEM (COR-063)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS hr.skill_types (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
name VARCHAR(100) NOT NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS hr.skills (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
skill_type_id UUID NOT NULL REFERENCES hr.skill_types(id) ON DELETE CASCADE,
|
|
name VARCHAR(100) NOT NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS hr.skill_levels (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
skill_type_id UUID NOT NULL REFERENCES hr.skill_types(id) ON DELETE CASCADE,
|
|
name VARCHAR(50) NOT NULL,
|
|
level INTEGER NOT NULL DEFAULT 1,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS hr.employee_skills (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
employee_id UUID NOT NULL REFERENCES hr.employees(id) ON DELETE CASCADE,
|
|
skill_id UUID NOT NULL REFERENCES hr.skills(id),
|
|
skill_level_id UUID REFERENCES hr.skill_levels(id),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT uq_employee_skill UNIQUE (employee_id, skill_id)
|
|
);
|
|
|
|
-- RLS para skills
|
|
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;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_skill_types ON hr.skill_types;
|
|
CREATE POLICY tenant_isolation_skill_types ON hr.skill_types
|
|
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_skills ON hr.skills;
|
|
CREATE POLICY tenant_isolation_skills ON hr.skills
|
|
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_skill_levels ON hr.skill_levels;
|
|
CREATE POLICY tenant_isolation_skill_levels ON hr.skill_levels
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
-- ============================================================================
|
|
-- 3. EXPENSE SYSTEM (COR-064)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS hr.expense_sheets (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
company_id UUID,
|
|
employee_id UUID NOT NULL REFERENCES hr.employees(id),
|
|
name VARCHAR(100) NOT NULL,
|
|
status hr.expense_status DEFAULT 'draft',
|
|
total_amount DECIMAL(18,4) DEFAULT 0,
|
|
currency_id UUID,
|
|
approved_by UUID REFERENCES auth.users(id),
|
|
approved_at TIMESTAMPTZ,
|
|
fraccionamiento_id UUID REFERENCES construction.fraccionamientos(id),
|
|
centro_costo VARCHAR(50),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS hr.expenses (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
expense_sheet_id UUID REFERENCES hr.expense_sheets(id) ON DELETE SET NULL,
|
|
employee_id UUID NOT NULL REFERENCES hr.employees(id),
|
|
name VARCHAR(200) NOT NULL,
|
|
product_id UUID,
|
|
unit_amount DECIMAL(18,4) NOT NULL,
|
|
quantity DECIMAL(10,4) DEFAULT 1,
|
|
total_amount DECIMAL(18,4) GENERATED ALWAYS AS (unit_amount * quantity) STORED,
|
|
currency_id UUID,
|
|
date DATE NOT NULL DEFAULT CURRENT_DATE,
|
|
reference VARCHAR(100),
|
|
description TEXT,
|
|
fraccionamiento_id UUID REFERENCES construction.fraccionamientos(id),
|
|
concepto_id UUID REFERENCES construction.conceptos(id),
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
ALTER TABLE hr.expense_sheets ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE hr.expenses ENABLE ROW LEVEL SECURITY;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_expense_sheets ON hr.expense_sheets;
|
|
CREATE POLICY tenant_isolation_expense_sheets ON hr.expense_sheets
|
|
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_expenses ON hr.expenses;
|
|
CREATE POLICY tenant_isolation_expenses ON hr.expenses
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
-- ============================================================================
|
|
-- 4. RESUME LINES (COR-065)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS hr.employee_resume_lines (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
employee_id UUID NOT NULL REFERENCES hr.employees(id) ON DELETE CASCADE,
|
|
name VARCHAR(200) NOT NULL,
|
|
date_start DATE,
|
|
date_end DATE,
|
|
line_type hr.resume_line_type NOT NULL,
|
|
description TEXT,
|
|
company_name VARCHAR(100),
|
|
job_title VARCHAR(100),
|
|
institution VARCHAR(100),
|
|
degree VARCHAR(100),
|
|
certificate_number VARCHAR(50),
|
|
issuing_authority VARCHAR(100),
|
|
expiry_date DATE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- 5. PAYSLIP SYSTEM (COR-066)
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS hr.payslip_structures (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
name VARCHAR(100) NOT NULL,
|
|
code VARCHAR(50) NOT NULL,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
tipo_pago VARCHAR(50),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT uq_payslip_struct_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 REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
company_id UUID,
|
|
employee_id UUID NOT NULL REFERENCES hr.employees(id),
|
|
contract_id UUID, -- FK opcional a hr.contracts
|
|
structure_id UUID REFERENCES hr.payslip_structures(id),
|
|
name VARCHAR(100) NOT NULL,
|
|
number VARCHAR(50),
|
|
status hr.payslip_status DEFAULT 'draft',
|
|
date_from DATE NOT NULL,
|
|
date_to DATE NOT NULL,
|
|
fraccionamiento_id UUID REFERENCES construction.fraccionamientos(id),
|
|
is_destajo BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS hr.payslip_lines (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
payslip_id UUID NOT NULL REFERENCES hr.payslips(id) ON DELETE CASCADE,
|
|
name VARCHAR(100) NOT NULL,
|
|
code VARCHAR(50) NOT NULL,
|
|
category VARCHAR(50) NOT NULL,
|
|
quantity DECIMAL(10,4) DEFAULT 1,
|
|
rate DECIMAL(18,4) DEFAULT 0,
|
|
amount DECIMAL(18,4) NOT NULL DEFAULT 0,
|
|
sequence INTEGER DEFAULT 10,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
ALTER TABLE hr.payslip_structures ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE hr.payslips ENABLE ROW LEVEL SECURITY;
|
|
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_payslip_structures ON hr.payslip_structures;
|
|
CREATE POLICY tenant_isolation_payslip_structures ON hr.payslip_structures
|
|
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_payslips ON hr.payslips;
|
|
CREATE POLICY tenant_isolation_payslips ON hr.payslips
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
-- ============================================================================
|
|
-- 6. CAMPOS ADICIONALES EN EMPLOYEES (COR-061)
|
|
-- ============================================================================
|
|
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'hr' AND table_name = 'employees' AND column_name = 'work_location_id') THEN
|
|
ALTER TABLE hr.employees ADD COLUMN work_location_id UUID REFERENCES hr.work_locations(id);
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'hr' AND table_name = 'employees' AND column_name = 'badge_id') THEN
|
|
ALTER TABLE hr.employees ADD COLUMN badge_id VARCHAR(50);
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'hr' AND table_name = 'employees' AND column_name = 'pin') THEN
|
|
ALTER TABLE hr.employees ADD COLUMN pin VARCHAR(10);
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'hr' AND table_name = 'employees' AND column_name = 'barcode') THEN
|
|
ALTER TABLE hr.employees ADD COLUMN barcode VARCHAR(50);
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'hr' AND table_name = 'employees' AND column_name = 'vehicle') THEN
|
|
ALTER TABLE hr.employees ADD COLUMN vehicle VARCHAR(100);
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'hr' AND table_name = 'employees' AND column_name = 'vehicle_license_plate') THEN
|
|
ALTER TABLE hr.employees ADD COLUMN vehicle_license_plate VARCHAR(20);
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = 'hr' AND table_name = 'employees' AND column_name = 'certificate') THEN
|
|
ALTER TABLE hr.employees ADD COLUMN certificate VARCHAR(50);
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================================================
|
|
-- COMENTARIOS
|
|
-- ============================================================================
|
|
|
|
COMMENT ON TABLE hr.work_locations IS 'ERP-Core FASE-8 COR-062: Ubicaciones de trabajo';
|
|
COMMENT ON TABLE construction.ubicaciones_obra IS 'Extensión: Ubicaciones específicas de obra';
|
|
COMMENT ON TABLE hr.skill_types IS 'ERP-Core FASE-8 COR-063: Tipos de habilidad';
|
|
COMMENT ON TABLE hr.skills IS 'ERP-Core FASE-8 COR-063: Habilidades';
|
|
COMMENT ON TABLE hr.skill_levels IS 'ERP-Core FASE-8 COR-063: Niveles de habilidad';
|
|
COMMENT ON TABLE hr.employee_skills IS 'ERP-Core FASE-8 COR-063: Habilidades de empleado';
|
|
COMMENT ON TABLE hr.expense_sheets IS 'ERP-Core FASE-8 COR-064: Hojas de gastos';
|
|
COMMENT ON TABLE hr.expenses IS 'ERP-Core FASE-8 COR-064: Gastos individuales';
|
|
COMMENT ON TABLE hr.employee_resume_lines IS 'ERP-Core FASE-8 COR-065: CV de empleado';
|
|
COMMENT ON TABLE hr.payslip_structures IS 'ERP-Core FASE-8 COR-066: Estructuras de nómina';
|
|
COMMENT ON TABLE hr.payslips IS 'ERP-Core FASE-8 COR-066: Recibos de nómina';
|
|
COMMENT ON TABLE hr.payslip_lines IS 'ERP-Core FASE-8 COR-066: Líneas de nómina';
|
|
|
|
-- ============================================================================
|
|
-- FIN HR EXTENSION FASE-8
|
|
-- Total: 13 tablas, 3 ENUMs
|
|
-- ============================================================================
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Seed Data Refinado
|
|
|
|
### 3.1 seeds/00-fase8-incoterms.sql
|
|
|
|
```sql
|
|
-- Incoterms estándar (sin tenant_id)
|
|
INSERT INTO financial.incoterms (code, name) VALUES
|
|
('EXW', 'Ex Works'),
|
|
('FCA', 'Free Carrier'),
|
|
('CPT', 'Carriage Paid To'),
|
|
('CIP', 'Carriage and Insurance Paid To'),
|
|
('DAP', 'Delivered at Place'),
|
|
('DPU', 'Delivered at Place Unloaded'),
|
|
('DDP', 'Delivered Duty Paid'),
|
|
('FAS', 'Free Alongside Ship'),
|
|
('FOB', 'Free On Board'),
|
|
('CFR', 'Cost and Freight'),
|
|
('CIF', 'Cost, Insurance and Freight')
|
|
ON CONFLICT (code) DO NOTHING;
|
|
```
|
|
|
|
### 3.2 seeds/01-fase8-removal-strategies.sql
|
|
|
|
```sql
|
|
-- Estrategias de remoción (sin tenant_id)
|
|
INSERT INTO inventory.removal_strategies (code, name, description) VALUES
|
|
('fifo', 'First In First Out', 'Salida por fecha de entrada más antigua'),
|
|
('lifo', 'Last In First Out', 'Salida por fecha de entrada más reciente'),
|
|
('fefo', 'First Expired First Out', 'Salida por fecha de caducidad más próxima'),
|
|
('closest', 'Closest Location', 'Salida por ubicación más cercana')
|
|
ON CONFLICT (code) DO NOTHING;
|
|
```
|
|
|
|
### 3.3 seeds/02-fase8-construccion-skills.sql
|
|
|
|
```sql
|
|
-- Seed con tenant específico (usar en contexto de sesión)
|
|
-- Ejecutar después de SET app.current_tenant_id = 'UUID-DEL-TENANT';
|
|
|
|
-- Tipos de habilidad
|
|
INSERT INTO hr.skill_types (tenant_id, name)
|
|
SELECT current_setting('app.current_tenant_id', true)::UUID, name
|
|
FROM (VALUES
|
|
('Albañilería'),
|
|
('Plomería'),
|
|
('Electricidad'),
|
|
('Herrería'),
|
|
('Carpintería'),
|
|
('Acabados'),
|
|
('Maquinaria Pesada'),
|
|
('Topografía'),
|
|
('Soldadura')
|
|
) AS t(name)
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- Niveles (crear para cada tipo)
|
|
INSERT INTO hr.skill_levels (tenant_id, skill_type_id, name, level)
|
|
SELECT
|
|
current_setting('app.current_tenant_id', true)::UUID,
|
|
st.id,
|
|
l.name,
|
|
l.level
|
|
FROM hr.skill_types st
|
|
CROSS JOIN (VALUES
|
|
('Ayudante', 1),
|
|
('Oficial', 2),
|
|
('Maestro', 3),
|
|
('Especialista', 4)
|
|
) AS l(name, level)
|
|
WHERE st.tenant_id = current_setting('app.current_tenant_id', true)::UUID
|
|
ON CONFLICT DO NOTHING;
|
|
```
|
|
|
|
### 3.4 seeds/03-fase8-construccion-catalogos.sql
|
|
|
|
```sql
|
|
-- Categorías de almacén
|
|
INSERT INTO inventory.storage_categories (tenant_id, name, max_weight, allow_new_product)
|
|
SELECT current_setting('app.current_tenant_id', true)::UUID, name, max_weight, allow_new_product
|
|
FROM (VALUES
|
|
('Área Techada', 10000.0, 'mixed'),
|
|
('Área Descubierta', 50000.0, 'mixed'),
|
|
('Bodega Cerrada', 5000.0, 'mixed'),
|
|
('Caseta Herramienta', 500.0, 'same'),
|
|
('Área Inflamables', 200.0, 'same')
|
|
) AS t(name, max_weight, allow_new_product)
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- Tipos de paquete
|
|
INSERT INTO inventory.package_types (tenant_id, name, height, width, length, base_weight, max_weight, sequence)
|
|
SELECT current_setting('app.current_tenant_id', true)::UUID, name, height, width, length, base_weight, max_weight, seq
|
|
FROM (VALUES
|
|
('Tarima Block', 150.0, 100.0, 100.0, 5.0, 500.0, 10),
|
|
('Paquete Varilla', 600.0, 30.0, 30.0, 2.0, 1000.0, 20),
|
|
('Rollo Cable', 50.0, 50.0, 20.0, 0.5, 50.0, 30),
|
|
('Saco Cemento', 60.0, 40.0, 15.0, 0.2, 50.0, 40),
|
|
('Cubeta Pintura', 40.0, 30.0, 30.0, 0.3, 25.0, 50),
|
|
('Caja Herrajes', 40.0, 30.0, 20.0, 0.5, 30.0, 60)
|
|
) AS t(name, height, width, length, base_weight, max_weight, seq)
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- Métodos de pago
|
|
INSERT INTO financial.payment_methods (tenant_id, name, code, payment_type)
|
|
SELECT current_setting('app.current_tenant_id', true)::UUID, name, code, payment_type::financial.payment_method_type
|
|
FROM (VALUES
|
|
('Anticipo de Obra', 'anticipo_obra', 'outbound'),
|
|
('Pago Estimación', 'pago_estimacion', 'outbound'),
|
|
('Pago Destajo', 'pago_destajo', 'outbound'),
|
|
('Pago Finiquito', 'pago_finiquito', 'outbound'),
|
|
('Retención 5%', 'retencion_5', 'outbound'),
|
|
('Cobro Cliente', 'cobro_cliente', 'inbound'),
|
|
('Anticipo Cliente', 'anticipo_cliente', 'inbound')
|
|
) AS t(name, code, payment_type)
|
|
ON CONFLICT (tenant_id, code) DO NOTHING;
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Script de Rollback
|
|
|
|
### 4.1 rollback/20260104_fase8_rollback.sql
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- ROLLBACK FASE-8 ERP-CONSTRUCCIÓN
|
|
-- PRECAUCIÓN: Elimina todas las tablas y datos de FASE-8
|
|
-- ============================================================================
|
|
|
|
BEGIN;
|
|
|
|
-- Eliminar seed data primero
|
|
DELETE FROM financial.payment_methods WHERE code LIKE '%_obra' OR code LIKE '%_estimacion' OR code LIKE '%_destajo' OR code LIKE '%_finiquito' OR code LIKE 'retencion_%' OR code LIKE '%_cliente';
|
|
DELETE FROM inventory.package_types WHERE name IN ('Tarima Block', 'Paquete Varilla', 'Rollo Cable', 'Saco Cemento', 'Cubeta Pintura', 'Caja Herrajes');
|
|
DELETE FROM inventory.storage_categories WHERE name IN ('Área Techada', 'Área Descubierta', 'Bodega Cerrada', 'Caseta Herramienta', 'Área Inflamables');
|
|
|
|
-- Eliminar tablas en orden inverso de dependencias
|
|
DROP TABLE IF EXISTS hr.payslip_lines CASCADE;
|
|
DROP TABLE IF EXISTS hr.payslips CASCADE;
|
|
DROP TABLE IF EXISTS hr.payslip_structures CASCADE;
|
|
DROP TABLE IF EXISTS hr.employee_resume_lines CASCADE;
|
|
DROP TABLE IF EXISTS hr.expenses CASCADE;
|
|
DROP TABLE IF EXISTS hr.expense_sheets CASCADE;
|
|
DROP TABLE IF EXISTS hr.employee_skills CASCADE;
|
|
DROP TABLE IF EXISTS hr.skill_levels CASCADE;
|
|
DROP TABLE IF EXISTS hr.skills CASCADE;
|
|
DROP TABLE IF EXISTS hr.skill_types CASCADE;
|
|
DROP TABLE IF EXISTS construction.ubicaciones_obra CASCADE;
|
|
DROP TABLE IF EXISTS hr.work_locations CASCADE;
|
|
DROP TABLE IF EXISTS construction.avance_programado CASCADE;
|
|
DROP TABLE IF EXISTS projects.ratings CASCADE;
|
|
DROP TABLE IF EXISTS construction.colaboradores_obra CASCADE;
|
|
DROP TABLE IF EXISTS purchase.product_supplierinfo CASCADE;
|
|
DROP TABLE IF EXISTS inventory.putaway_rules CASCADE;
|
|
DROP TABLE IF EXISTS inventory.packages CASCADE;
|
|
DROP TABLE IF EXISTS inventory.package_types CASCADE;
|
|
DROP TABLE IF EXISTS inventory.storage_categories CASCADE;
|
|
DROP TABLE IF EXISTS inventory.removal_strategies CASCADE;
|
|
DROP TABLE IF EXISTS financial.reconcile_model_lines CASCADE;
|
|
DROP TABLE IF EXISTS financial.reconcile_models CASCADE;
|
|
DROP TABLE IF EXISTS financial.payment_term_lines CASCADE;
|
|
DROP TABLE IF EXISTS financial.payment_methods CASCADE;
|
|
DROP TABLE IF EXISTS financial.incoterms CASCADE;
|
|
|
|
-- Eliminar funciones
|
|
DROP FUNCTION IF EXISTS construction.generate_avance_snapshot CASCADE;
|
|
DROP FUNCTION IF EXISTS purchase.action_create_stock_moves CASCADE;
|
|
|
|
-- Eliminar ENUMs
|
|
DROP TYPE IF EXISTS hr.payslip_status CASCADE;
|
|
DROP TYPE IF EXISTS hr.resume_line_type CASCADE;
|
|
DROP TYPE IF EXISTS hr.expense_status CASCADE;
|
|
DROP TYPE IF EXISTS financial.reconcile_model_type CASCADE;
|
|
DROP TYPE IF EXISTS financial.payment_method_type CASCADE;
|
|
|
|
-- Revertir campos adicionales (opcional - comentar si quiere mantenerlos)
|
|
-- ALTER TABLE hr.employees DROP COLUMN IF EXISTS work_location_id;
|
|
-- ALTER TABLE hr.employees DROP COLUMN IF EXISTS badge_id;
|
|
-- etc.
|
|
|
|
COMMIT;
|
|
|
|
\echo 'Rollback FASE-8 completado'
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Checklist de Ejecución
|
|
|
|
### 5.1 Pre-Ejecución
|
|
|
|
- [ ] Backup de base de datos
|
|
- [ ] Verificar que auth.tenants existe
|
|
- [ ] Verificar que construction.fraccionamientos existe
|
|
- [ ] Verificar que hr.employees existe
|
|
- [ ] Confirmar tenant_id para seed data
|
|
|
|
### 5.2 Ejecución
|
|
|
|
- [ ] Ejecutar 08-financial-ext-schema-ddl.sql
|
|
- [ ] Ejecutar 09-projects-ext-schema-ddl.sql
|
|
- [ ] Ejecutar adiciones a 02-hr-schema-ddl.sql
|
|
- [ ] Ejecutar adiciones a 06-inventory-ext-schema-ddl.sql
|
|
- [ ] Ejecutar adiciones a 07-purchase-ext-schema-ddl.sql
|
|
- [ ] Ejecutar seeds/00-fase8-incoterms.sql
|
|
- [ ] Ejecutar seeds/01-fase8-removal-strategies.sql
|
|
- [ ] SET app.current_tenant_id = 'UUID';
|
|
- [ ] Ejecutar seeds/02-fase8-construccion-skills.sql
|
|
- [ ] Ejecutar seeds/03-fase8-construccion-catalogos.sql
|
|
|
|
### 5.3 Post-Ejecución
|
|
|
|
- [ ] Verificar creación de tablas
|
|
- [ ] Verificar creación de funciones
|
|
- [ ] Verificar seed data
|
|
- [ ] Actualizar documentación
|
|
|
|
---
|
|
|
|
## 6. Resumen de Cambios vs Plan Original
|
|
|
|
| Elemento | Plan Original | Plan Refinado |
|
|
|----------|---------------|---------------|
|
|
| proyecto_id | Usado | Cambiado a fraccionamiento_id |
|
|
| construction.proyectos | Referenciado | Eliminado, usar fraccionamientos |
|
|
| construction.partidas | Trigger | Adaptado a avances_obra |
|
|
| projects.collaborators | Heredado | Nueva tabla colaboradores_obra |
|
|
| FKs a Core | Obligatorias | Opcionales |
|
|
| ENUMs | Sin protección | Con IF NOT EXISTS |
|
|
| RLS policies | Sin DROP | Con DROP IF EXISTS previo |
|
|
|
|
---
|
|
|
|
**Estado:** FASE 6 COMPLETADA
|
|
**Siguiente:** FASE 7 - Ejecución del Plan
|
|
**Fecha:** 2026-01-04
|