erp-construccion-database-v2/init-scripts/01-init-database.sql
rckrdmrd bf97e26cdf Migración desde erp-construccion/database - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 08:11:21 -06:00

318 lines
12 KiB
PL/PgSQL

-- ============================================================================
-- Archivo: 01-init-database.sql
-- Descripcion: Inicializacion completa de base de datos - Carga Limpia
-- Proyecto: ERP Suite - Vertical Construccion
-- Version: 2.0.0
-- Fecha: 2025-12-06
-- ============================================================================
-- POLITICA: CARGA LIMPIA (ver DIRECTIVA-POLITICA-CARGA-LIMPIA.md)
-- Este archivo es parte de la fuente de verdad DDL.
-- NO usar migrations, fixes o patches incrementales.
-- ============================================================================
-- ============================================================================
-- EXTENSIONES
-- ============================================================================
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- Generacion de UUIDs
CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- Funciones criptograficas
CREATE EXTENSION IF NOT EXISTS "postgis"; -- Geolocalizacion
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- Busqueda fuzzy
CREATE EXTENSION IF NOT EXISTS "btree_gist"; -- Indices GiST avanzados
-- ============================================================================
-- SCHEMA: core_shared (funciones compartidas)
-- ============================================================================
CREATE SCHEMA IF NOT EXISTS core_shared;
COMMENT ON SCHEMA core_shared IS 'Funciones, tipos y utilidades compartidas entre modulos';
-- ============================================================================
-- FUNCIONES DE AUDITORIA
-- ============================================================================
-- Funcion para actualizar updated_at automaticamente
CREATE OR REPLACE FUNCTION core_shared.set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION core_shared.set_updated_at() IS
'Trigger function para actualizar automaticamente el campo updated_at en cada UPDATE';
-- Funcion para establecer tenant_id desde contexto
CREATE OR REPLACE FUNCTION core_shared.set_tenant_id()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.tenant_id IS NULL THEN
NEW.tenant_id = current_setting('app.current_tenant_id', true)::uuid;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION core_shared.set_tenant_id() IS
'Trigger function para establecer tenant_id automaticamente desde el contexto de sesion';
-- Funcion para establecer created_by desde contexto
CREATE OR REPLACE FUNCTION core_shared.set_created_by()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.created_by IS NULL THEN
NEW.created_by = current_setting('app.current_user_id', true)::uuid;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION core_shared.set_created_by() IS
'Trigger function para establecer created_by automaticamente desde el contexto de sesion';
-- ============================================================================
-- FUNCIONES DE CONTEXTO
-- ============================================================================
CREATE OR REPLACE FUNCTION core_shared.get_current_tenant_id()
RETURNS UUID AS $$
BEGIN
RETURN NULLIF(current_setting('app.current_tenant_id', true), '')::UUID;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END;
$$ LANGUAGE plpgsql STABLE;
COMMENT ON FUNCTION core_shared.get_current_tenant_id() IS
'Obtiene el ID del tenant actual desde el contexto de sesion';
CREATE OR REPLACE FUNCTION core_shared.get_current_user_id()
RETURNS UUID AS $$
BEGIN
RETURN NULLIF(current_setting('app.current_user_id', true), '')::UUID;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END;
$$ LANGUAGE plpgsql STABLE;
COMMENT ON FUNCTION core_shared.get_current_user_id() IS
'Obtiene el ID del usuario actual desde el contexto de sesion';
-- ============================================================================
-- FUNCIONES DE UTILIDAD
-- ============================================================================
-- Generar slug desde texto
CREATE OR REPLACE FUNCTION core_shared.generate_slug(input_text TEXT)
RETURNS TEXT AS $$
BEGIN
RETURN LOWER(
REGEXP_REPLACE(
REGEXP_REPLACE(
TRIM(input_text),
'[^a-zA-Z0-9\s-]', '', 'g'
),
'\s+', '-', 'g'
)
);
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- Validar formato de email
CREATE OR REPLACE FUNCTION core_shared.is_valid_email(email TEXT)
RETURNS BOOLEAN AS $$
BEGIN
RETURN email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$';
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- Validar formato de RFC mexicano
CREATE OR REPLACE FUNCTION core_shared.is_valid_rfc(rfc TEXT)
RETURNS BOOLEAN AS $$
BEGIN
RETURN rfc ~* '^[A-Z&Ñ]{3,4}[0-9]{6}[A-Z0-9]{3}$';
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- ============================================================================
-- PERMISOS
-- ============================================================================
GRANT USAGE ON SCHEMA core_shared TO PUBLIC;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA core_shared TO PUBLIC;
-- ============================================================================
-- SCHEMAS DE NEGOCIO - NOMENCLATURA UNIFICADA
-- ============================================================================
-- Segun NAMING-CONVENTIONS.md, usamos nombres cortos y descriptivos:
-- - construction (antes project_management, construction_management)
-- - estimates (antes financial_management)
-- - infonavit (antes infonavit_management)
-- - hr (extension de erp-core)
-- - inventory (extension de erp-core)
-- - purchase (extension de erp-core)
-- - hse (nuevo: seguridad, salud, medio ambiente)
-- ============================================================================
-- Schemas propios de construccion
CREATE SCHEMA IF NOT EXISTS construction;
CREATE SCHEMA IF NOT EXISTS estimates;
CREATE SCHEMA IF NOT EXISTS infonavit;
CREATE SCHEMA IF NOT EXISTS hse;
-- Schemas de extension (extendemos modulos de erp-core)
-- Estos pueden ya existir si erp-core esta instalado
CREATE SCHEMA IF NOT EXISTS hr;
CREATE SCHEMA IF NOT EXISTS inventory;
CREATE SCHEMA IF NOT EXISTS purchase;
-- Schemas legacy (compatibilidad temporal, marcar para deprecacion)
-- NOTA: Estos seran eliminados en version futura
CREATE SCHEMA IF NOT EXISTS auth_management;
CREATE SCHEMA IF NOT EXISTS project_management;
CREATE SCHEMA IF NOT EXISTS financial_management;
CREATE SCHEMA IF NOT EXISTS purchasing_management;
CREATE SCHEMA IF NOT EXISTS inventory_management;
CREATE SCHEMA IF NOT EXISTS construction_management;
CREATE SCHEMA IF NOT EXISTS quality_management;
CREATE SCHEMA IF NOT EXISTS safety_management;
CREATE SCHEMA IF NOT EXISTS infonavit_management;
-- ============================================================================
-- COMENTARIOS EN SCHEMAS
-- ============================================================================
-- Schemas principales (nuevos)
COMMENT ON SCHEMA construction IS
'Gestion de obras: proyectos, fraccionamientos, fases, viviendas, avances';
COMMENT ON SCHEMA estimates IS
'Presupuestos, partidas, estimaciones, control de costos';
COMMENT ON SCHEMA infonavit IS
'Integracion INFONAVIT: tramites, ECUVE, subsidios, normatividad';
COMMENT ON SCHEMA hse IS
'Seguridad, Salud Ocupacional y Medio Ambiente (HSE/EHS)';
COMMENT ON SCHEMA hr IS
'Extension de RRHH para construccion: cuadrillas, destajo, asistencia obra';
COMMENT ON SCHEMA inventory IS
'Extension de inventarios: almacenes de obra, control de materiales';
COMMENT ON SCHEMA purchase IS
'Extension de compras: proveedores de construccion, requisiciones de obra';
-- Schemas legacy (deprecated)
COMMENT ON SCHEMA auth_management IS
'DEPRECATED: Usar core.auth. Schema mantenido para compatibilidad';
COMMENT ON SCHEMA project_management IS
'DEPRECATED: Usar construction. Schema mantenido para compatibilidad';
COMMENT ON SCHEMA financial_management IS
'DEPRECATED: Usar estimates. Schema mantenido para compatibilidad';
COMMENT ON SCHEMA purchasing_management IS
'DEPRECATED: Usar purchase. Schema mantenido para compatibilidad';
COMMENT ON SCHEMA inventory_management IS
'DEPRECATED: Usar inventory. Schema mantenido para compatibilidad';
COMMENT ON SCHEMA construction_management IS
'DEPRECATED: Usar construction. Schema mantenido para compatibilidad';
COMMENT ON SCHEMA quality_management IS
'DEPRECATED: Funcionalidad movida a hse (inspecciones) y construction (calidad)';
COMMENT ON SCHEMA safety_management IS
'DEPRECATED: Usar hse. Schema mantenido para compatibilidad';
COMMENT ON SCHEMA infonavit_management IS
'DEPRECATED: Usar infonavit. Schema mantenido para compatibilidad';
-- ============================================================================
-- FUNCION LEGACY (compatibilidad)
-- ============================================================================
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION update_updated_at_column() IS
'LEGACY: Usar core_shared.set_updated_at() en nuevas tablas';
-- ============================================================================
-- TABLAS CORE MINIMAS (si erp-core no esta instalado)
-- ============================================================================
-- Estas tablas son requeridas como FK por los modulos de construccion
-- Si erp-core esta instalado, estas ya existiran y el IF NOT EXISTS las omitira
-- Schema core para tablas compartidas
CREATE SCHEMA IF NOT EXISTS core;
-- Tabla de tenants (multi-tenancy)
CREATE TABLE IF NOT EXISTS core.tenants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
code VARCHAR(50) NOT NULL UNIQUE,
name VARCHAR(200) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT true,
settings JSONB DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Tabla de usuarios
CREATE TABLE IF NOT EXISTS core.users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID REFERENCES core.tenants(id),
email VARCHAR(255) NOT NULL,
username VARCHAR(100),
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(tenant_id, email)
);
-- ============================================================================
-- VERIFICACION
-- ============================================================================
DO $$
DECLARE
schema_count INTEGER;
ext_count INTEGER;
BEGIN
SELECT COUNT(*) INTO schema_count
FROM pg_namespace
WHERE nspname IN ('construction', 'estimates', 'infonavit', 'hse', 'hr', 'inventory', 'purchase', 'core', 'core_shared');
SELECT COUNT(*) INTO ext_count
FROM pg_extension
WHERE extname IN ('uuid-ossp', 'pgcrypto', 'postgis', 'pg_trgm', 'btree_gist');
RAISE NOTICE '============================================================';
RAISE NOTICE 'ERP CONSTRUCCION - Base de datos inicializada';
RAISE NOTICE '============================================================';
RAISE NOTICE 'Extensiones instaladas: %', ext_count;
RAISE NOTICE 'Schemas principales creados: %', schema_count;
RAISE NOTICE '============================================================';
RAISE NOTICE 'Schemas principales: construction, estimates, infonavit, hse';
RAISE NOTICE 'Schemas extension: hr, inventory, purchase';
RAISE NOTICE 'Schemas compartidos: core, core_shared';
RAISE NOTICE '============================================================';
END $$;
-- ============================================================================
-- FIN
-- ============================================================================