Major overhaul of drop-and-recreate-database.sh, DDL schema updates, seed data cleanup. Add utility scripts for auth table fixes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1052 lines
47 KiB
SQL
1052 lines
47 KiB
SQL
-- ============================================================================
|
|
-- CONSTRUCTION Schema DDL - Gestión de Obras (COMPLETO)
|
|
-- Modulos: MAI-002, MAI-003, MAI-005, MAI-009, MAI-012
|
|
-- Version: 2.0.0
|
|
-- Fecha: 2025-12-08
|
|
-- ============================================================================
|
|
-- POLITICA: CARGA LIMPIA (ver DIRECTIVA-POLITICA-CARGA-LIMPIA.md)
|
|
-- Este archivo es parte de la fuente de verdad DDL.
|
|
-- ============================================================================
|
|
|
|
-- Verificar que ERP-Core está instalado
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'auth') THEN
|
|
RAISE EXCEPTION 'Schema auth no existe. Ejecutar primero ERP-Core DDL';
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'auth' AND tablename = 'tenants') THEN
|
|
RAISE EXCEPTION 'Tabla auth.tenants no existe. ERP-Core debe estar instalado';
|
|
END IF;
|
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'auth' AND tablename = 'users') THEN
|
|
RAISE EXCEPTION 'Tabla auth.users no existe. ERP-Core debe estar instalado';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- Crear schema si no existe
|
|
CREATE SCHEMA IF NOT EXISTS construction;
|
|
|
|
-- ============================================================================
|
|
-- TYPES (ENUMs)
|
|
-- ============================================================================
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE construction.project_status AS ENUM (
|
|
'draft', 'planning', 'in_progress', 'paused', 'completed', 'cancelled'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE construction.lot_status AS ENUM (
|
|
'available', 'reserved', 'sold', 'under_construction', 'delivered', 'warranty'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE construction.prototype_type AS ENUM (
|
|
'horizontal', 'vertical', 'commercial', 'mixed'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE construction.advance_status AS ENUM (
|
|
'pending', 'captured', 'reviewed', 'approved', 'rejected'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE construction.quality_status AS ENUM (
|
|
'pending', 'in_review', 'approved', 'rejected', 'rework'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE construction.contract_type AS ENUM (
|
|
'fixed_price', 'unit_price', 'cost_plus', 'mixed'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
DO $$ BEGIN
|
|
CREATE TYPE construction.contract_status AS ENUM (
|
|
'draft', 'pending_approval', 'active', 'suspended', 'terminated', 'closed'
|
|
);
|
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
|
|
|
-- ============================================================================
|
|
-- TABLES - ESTRUCTURA DE PROYECTO
|
|
-- ============================================================================
|
|
|
|
-- Tabla: proyectos (proyectos de desarrollo inmobiliario)
|
|
CREATE TABLE IF NOT EXISTS construction.proyectos (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
codigo VARCHAR(20) NOT NULL,
|
|
nombre VARCHAR(200) NOT NULL,
|
|
descripcion TEXT,
|
|
direccion TEXT,
|
|
ciudad VARCHAR(100),
|
|
estado VARCHAR(100),
|
|
fecha_inicio DATE,
|
|
fecha_fin_estimada DATE,
|
|
estado_proyecto VARCHAR(20) NOT NULL DEFAULT 'activo',
|
|
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_proyectos_codigo_tenant UNIQUE (tenant_id, codigo),
|
|
CONSTRAINT chk_proyectos_estado CHECK (estado_proyecto IN ('activo', 'pausado', 'completado', 'cancelado'))
|
|
);
|
|
|
|
-- Tabla: fraccionamientos (desarrollo inmobiliario)
|
|
CREATE TABLE IF NOT EXISTS construction.fraccionamientos (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
proyecto_id UUID NOT NULL REFERENCES construction.proyectos(id) ON DELETE CASCADE,
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
address TEXT,
|
|
city VARCHAR(100),
|
|
state VARCHAR(100),
|
|
zip_code VARCHAR(10),
|
|
location GEOMETRY(POINT, 4326),
|
|
total_area_m2 DECIMAL(12,2),
|
|
buildable_area_m2 DECIMAL(12,2),
|
|
total_lots INTEGER DEFAULT 0,
|
|
status construction.project_status NOT NULL DEFAULT 'draft',
|
|
start_date DATE,
|
|
expected_end_date DATE,
|
|
actual_end_date DATE,
|
|
metadata JSONB DEFAULT '{}',
|
|
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_fraccionamientos_code_tenant UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
-- Tabla: etapas (fases del fraccionamiento)
|
|
CREATE TABLE IF NOT EXISTS construction.etapas (
|
|
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,
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
sequence INTEGER NOT NULL DEFAULT 1,
|
|
total_lots INTEGER DEFAULT 0,
|
|
status construction.project_status NOT NULL DEFAULT 'draft',
|
|
start_date DATE,
|
|
expected_end_date DATE,
|
|
actual_end_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),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_etapas_code_fracc UNIQUE (fraccionamiento_id, code)
|
|
);
|
|
|
|
-- Tabla: manzanas (agrupación de lotes)
|
|
CREATE TABLE IF NOT EXISTS construction.manzanas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
etapa_id UUID NOT NULL REFERENCES construction.etapas(id) ON DELETE CASCADE,
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(100),
|
|
total_lots INTEGER DEFAULT 0,
|
|
polygon GEOMETRY(POLYGON, 4326),
|
|
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_manzanas_code_etapa UNIQUE (etapa_id, code)
|
|
);
|
|
|
|
-- Tabla: prototipos (tipos de vivienda) - definida antes de lotes
|
|
CREATE TABLE IF NOT EXISTS construction.prototipos (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
type construction.prototype_type NOT NULL DEFAULT 'horizontal',
|
|
area_construction_m2 DECIMAL(10,2),
|
|
area_terrain_m2 DECIMAL(10,2),
|
|
bedrooms INTEGER DEFAULT 0,
|
|
bathrooms DECIMAL(3,1) DEFAULT 0,
|
|
parking_spaces INTEGER DEFAULT 0,
|
|
floors INTEGER DEFAULT 1,
|
|
base_price DECIMAL(14,2),
|
|
blueprint_url VARCHAR(500),
|
|
render_url VARCHAR(500),
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
metadata JSONB DEFAULT '{}',
|
|
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_prototipos_code_tenant UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
-- Tabla: lotes (unidades vendibles horizontal)
|
|
CREATE TABLE IF NOT EXISTS construction.lotes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
manzana_id UUID NOT NULL REFERENCES construction.manzanas(id) ON DELETE CASCADE,
|
|
prototipo_id UUID REFERENCES construction.prototipos(id),
|
|
code VARCHAR(30) NOT NULL,
|
|
official_number VARCHAR(50),
|
|
area_m2 DECIMAL(10,2),
|
|
front_m DECIMAL(8,2),
|
|
depth_m DECIMAL(8,2),
|
|
status construction.lot_status NOT NULL DEFAULT 'available',
|
|
location GEOMETRY(POINT, 4326),
|
|
polygon GEOMETRY(POLYGON, 4326),
|
|
price_base DECIMAL(14,2),
|
|
price_final DECIMAL(14,2),
|
|
buyer_id UUID,
|
|
sale_date DATE,
|
|
delivery_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),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_lotes_code_manzana UNIQUE (manzana_id, code)
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- TABLES - ESTRUCTURA VERTICAL (TORRES)
|
|
-- ============================================================================
|
|
|
|
-- Tabla: torres (edificios verticales)
|
|
CREATE TABLE IF NOT EXISTS construction.torres (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
etapa_id UUID NOT NULL REFERENCES construction.etapas(id) ON DELETE CASCADE,
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(100) NOT NULL,
|
|
total_floors INTEGER NOT NULL DEFAULT 1,
|
|
total_units INTEGER DEFAULT 0,
|
|
status construction.project_status NOT NULL DEFAULT 'draft',
|
|
location GEOMETRY(POINT, 4326),
|
|
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_torres_code_etapa UNIQUE (etapa_id, code)
|
|
);
|
|
|
|
-- Tabla: niveles (pisos de torre)
|
|
CREATE TABLE IF NOT EXISTS construction.niveles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
torre_id UUID NOT NULL REFERENCES construction.torres(id) ON DELETE CASCADE,
|
|
floor_number INTEGER NOT NULL,
|
|
name VARCHAR(50),
|
|
total_units INTEGER DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ,
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_niveles_floor_torre UNIQUE (torre_id, floor_number)
|
|
);
|
|
|
|
-- Tabla: departamentos (unidades en torre)
|
|
CREATE TABLE IF NOT EXISTS construction.departamentos (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
nivel_id UUID NOT NULL REFERENCES construction.niveles(id) ON DELETE CASCADE,
|
|
prototipo_id UUID REFERENCES construction.prototipos(id),
|
|
code VARCHAR(30) NOT NULL,
|
|
unit_number VARCHAR(20) NOT NULL,
|
|
area_m2 DECIMAL(10,2),
|
|
status construction.lot_status NOT NULL DEFAULT 'available',
|
|
price_base DECIMAL(14,2),
|
|
price_final DECIMAL(14,2),
|
|
buyer_id UUID,
|
|
sale_date DATE,
|
|
delivery_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),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_departamentos_code_nivel UNIQUE (nivel_id, code)
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- TABLES - CONCEPTOS Y PRESUPUESTOS
|
|
-- ============================================================================
|
|
|
|
-- Tabla: conceptos (catálogo de conceptos de obra)
|
|
CREATE TABLE IF NOT EXISTS construction.conceptos (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
parent_id UUID REFERENCES construction.conceptos(id),
|
|
code VARCHAR(50) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
unit_id UUID,
|
|
unit_price DECIMAL(12,4),
|
|
is_composite BOOLEAN NOT NULL DEFAULT FALSE,
|
|
level INTEGER NOT NULL DEFAULT 0,
|
|
path 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),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_conceptos_code_tenant UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
-- Tabla: presupuestos (presupuesto por prototipo/obra)
|
|
CREATE TABLE IF NOT EXISTS construction.presupuestos (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
fraccionamiento_id UUID REFERENCES construction.fraccionamientos(id),
|
|
prototipo_id UUID REFERENCES construction.prototipos(id),
|
|
code VARCHAR(30) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
version INTEGER NOT NULL DEFAULT 1,
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
total_amount DECIMAL(16,2) DEFAULT 0,
|
|
currency_id UUID,
|
|
approved_at TIMESTAMPTZ,
|
|
approved_by UUID REFERENCES auth.users(id),
|
|
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_presupuestos_code_version UNIQUE (tenant_id, code, version)
|
|
);
|
|
|
|
-- Tabla: presupuesto_partidas (líneas del presupuesto)
|
|
CREATE TABLE IF NOT EXISTS construction.presupuesto_partidas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
presupuesto_id UUID NOT NULL REFERENCES construction.presupuestos(id) ON DELETE CASCADE,
|
|
concepto_id UUID NOT NULL REFERENCES construction.conceptos(id),
|
|
sequence INTEGER NOT NULL DEFAULT 0,
|
|
quantity DECIMAL(12,4) NOT NULL DEFAULT 0,
|
|
unit_price DECIMAL(12,4) NOT NULL DEFAULT 0,
|
|
total_amount DECIMAL(14,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
|
|
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_partidas_presupuesto_concepto UNIQUE (presupuesto_id, concepto_id)
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- TABLES - AVANCES Y CONTROL DE OBRA
|
|
-- ============================================================================
|
|
|
|
-- Tabla: programa_obra (programa maestro)
|
|
CREATE TABLE IF NOT EXISTS construction.programa_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),
|
|
code VARCHAR(30) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
version INTEGER NOT NULL DEFAULT 1,
|
|
start_date DATE NOT NULL,
|
|
end_date DATE NOT NULL,
|
|
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),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_programa_code_version UNIQUE (tenant_id, code, version)
|
|
);
|
|
|
|
-- Tabla: programa_actividades (actividades del programa)
|
|
CREATE TABLE IF NOT EXISTS construction.programa_actividades (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
programa_id UUID NOT NULL REFERENCES construction.programa_obra(id) ON DELETE CASCADE,
|
|
concepto_id UUID REFERENCES construction.conceptos(id),
|
|
parent_id UUID REFERENCES construction.programa_actividades(id),
|
|
name VARCHAR(255) NOT NULL,
|
|
sequence INTEGER NOT NULL DEFAULT 0,
|
|
planned_start DATE,
|
|
planned_end DATE,
|
|
planned_quantity DECIMAL(12,4) DEFAULT 0,
|
|
planned_weight DECIMAL(8,4) DEFAULT 0,
|
|
wbs_code VARCHAR(50),
|
|
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)
|
|
);
|
|
|
|
-- Tabla: avances_obra (captura de avances)
|
|
CREATE TABLE IF NOT EXISTS construction.avances_obra (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
lote_id UUID REFERENCES construction.lotes(id),
|
|
departamento_id UUID REFERENCES construction.departamentos(id),
|
|
concepto_id UUID NOT NULL REFERENCES construction.conceptos(id),
|
|
capture_date DATE NOT NULL,
|
|
quantity_executed DECIMAL(12,4) NOT NULL DEFAULT 0,
|
|
percentage_executed DECIMAL(5,2) DEFAULT 0,
|
|
status construction.advance_status NOT NULL DEFAULT 'pending',
|
|
notes TEXT,
|
|
captured_by UUID NOT NULL REFERENCES auth.users(id),
|
|
reviewed_by UUID REFERENCES auth.users(id),
|
|
reviewed_at TIMESTAMPTZ,
|
|
approved_by UUID REFERENCES auth.users(id),
|
|
approved_at TIMESTAMPTZ,
|
|
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 chk_avances_lote_or_depto CHECK (
|
|
(lote_id IS NOT NULL AND departamento_id IS NULL) OR
|
|
(lote_id IS NULL AND departamento_id IS NOT NULL)
|
|
)
|
|
);
|
|
|
|
-- Tabla: fotos_avance (evidencia fotográfica)
|
|
CREATE TABLE IF NOT EXISTS construction.fotos_avance (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
avance_id UUID NOT NULL REFERENCES construction.avances_obra(id) ON DELETE CASCADE,
|
|
file_url VARCHAR(500) NOT NULL,
|
|
file_name VARCHAR(255),
|
|
file_size INTEGER,
|
|
mime_type VARCHAR(50),
|
|
description TEXT,
|
|
location GEOMETRY(POINT, 4326),
|
|
captured_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES auth.users(id),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id)
|
|
);
|
|
|
|
-- Tabla: bitacora_obra (registro de bitácora)
|
|
CREATE TABLE IF NOT EXISTS construction.bitacora_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),
|
|
entry_date DATE NOT NULL,
|
|
entry_number INTEGER NOT NULL,
|
|
weather VARCHAR(50),
|
|
temperature_max DECIMAL(4,1),
|
|
temperature_min DECIMAL(4,1),
|
|
workers_count INTEGER DEFAULT 0,
|
|
description TEXT NOT NULL,
|
|
observations TEXT,
|
|
incidents TEXT,
|
|
registered_by UUID NOT NULL REFERENCES auth.users(id),
|
|
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_bitacora_fracc_number UNIQUE (fraccionamiento_id, entry_number)
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- TABLES - CALIDAD Y POSTVENTA (MAI-009)
|
|
-- ============================================================================
|
|
|
|
-- Tabla: checklists (plantillas de verificación)
|
|
CREATE TABLE IF NOT EXISTS construction.checklists (
|
|
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(255) NOT NULL,
|
|
description TEXT,
|
|
prototipo_id UUID REFERENCES construction.prototipos(id),
|
|
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),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_checklists_code_tenant UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
-- Tabla: checklist_items (items del checklist)
|
|
CREATE TABLE IF NOT EXISTS construction.checklist_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
checklist_id UUID NOT NULL REFERENCES construction.checklists(id) ON DELETE CASCADE,
|
|
sequence INTEGER NOT NULL DEFAULT 0,
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
is_required 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),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id)
|
|
);
|
|
|
|
-- Tabla: inspecciones (inspecciones de calidad)
|
|
CREATE TABLE IF NOT EXISTS construction.inspecciones (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
checklist_id UUID NOT NULL REFERENCES construction.checklists(id),
|
|
lote_id UUID REFERENCES construction.lotes(id),
|
|
departamento_id UUID REFERENCES construction.departamentos(id),
|
|
inspection_date DATE NOT NULL,
|
|
status construction.quality_status NOT NULL DEFAULT 'pending',
|
|
inspector_id UUID NOT NULL REFERENCES auth.users(id),
|
|
notes TEXT,
|
|
approved_by UUID REFERENCES auth.users(id),
|
|
approved_at TIMESTAMPTZ,
|
|
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)
|
|
);
|
|
|
|
-- Tabla: inspeccion_resultados (resultados por item)
|
|
CREATE TABLE IF NOT EXISTS construction.inspeccion_resultados (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
inspeccion_id UUID NOT NULL REFERENCES construction.inspecciones(id) ON DELETE CASCADE,
|
|
checklist_item_id UUID NOT NULL REFERENCES construction.checklist_items(id),
|
|
is_passed BOOLEAN,
|
|
notes TEXT,
|
|
photo_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)
|
|
);
|
|
|
|
-- Tabla: tickets_postventa (tickets de garantía)
|
|
CREATE TABLE IF NOT EXISTS construction.tickets_postventa (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
lote_id UUID REFERENCES construction.lotes(id),
|
|
departamento_id UUID REFERENCES construction.departamentos(id),
|
|
ticket_number VARCHAR(30) NOT NULL,
|
|
reported_date DATE NOT NULL,
|
|
category VARCHAR(50),
|
|
description TEXT NOT NULL,
|
|
priority VARCHAR(20) DEFAULT 'medium',
|
|
status VARCHAR(20) NOT NULL DEFAULT 'open',
|
|
assigned_to UUID REFERENCES auth.users(id),
|
|
resolution TEXT,
|
|
resolved_at TIMESTAMPTZ,
|
|
resolved_by UUID REFERENCES auth.users(id),
|
|
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_tickets_number_tenant UNIQUE (tenant_id, ticket_number)
|
|
);
|
|
|
|
-- Tabla: no_conformidades (no conformidades detectadas)
|
|
CREATE TABLE IF NOT EXISTS construction.no_conformidades (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
inspeccion_id UUID REFERENCES construction.inspecciones(id),
|
|
lote_id UUID REFERENCES construction.lotes(id),
|
|
nc_number VARCHAR(50) NOT NULL,
|
|
detection_date DATE NOT NULL,
|
|
category VARCHAR(100),
|
|
severity VARCHAR(20) NOT NULL DEFAULT 'minor',
|
|
description TEXT NOT NULL,
|
|
root_cause TEXT,
|
|
photo_url VARCHAR(500),
|
|
contractor_id UUID REFERENCES construction.subcontratistas(id),
|
|
status VARCHAR(20) NOT NULL DEFAULT 'open',
|
|
due_date DATE,
|
|
closed_at TIMESTAMPTZ,
|
|
closed_by UUID REFERENCES auth.users(id),
|
|
verified_at TIMESTAMPTZ,
|
|
verified_by UUID REFERENCES auth.users(id),
|
|
closure_photo_url VARCHAR(500),
|
|
closure_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_nc_number_tenant UNIQUE (tenant_id, nc_number),
|
|
CONSTRAINT chk_nc_severity CHECK (severity IN ('minor', 'major', 'critical')),
|
|
CONSTRAINT chk_nc_status CHECK (status IN ('open', 'in_progress', 'closed', 'verified'))
|
|
);
|
|
|
|
-- Tabla: acciones_correctivas (CAPA)
|
|
CREATE TABLE IF NOT EXISTS construction.acciones_correctivas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
no_conformidad_id UUID NOT NULL REFERENCES construction.no_conformidades(id) ON DELETE CASCADE,
|
|
action_type VARCHAR(20) NOT NULL DEFAULT 'corrective',
|
|
description TEXT NOT NULL,
|
|
responsible_id UUID NOT NULL REFERENCES auth.users(id),
|
|
due_date DATE NOT NULL,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
|
completed_at TIMESTAMPTZ,
|
|
completion_notes TEXT,
|
|
verified_at TIMESTAMPTZ,
|
|
verified_by UUID REFERENCES auth.users(id),
|
|
effectiveness_verified BOOLEAN DEFAULT FALSE,
|
|
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 chk_action_type CHECK (action_type IN ('corrective', 'preventive', 'improvement')),
|
|
CONSTRAINT chk_action_status CHECK (status IN ('pending', 'in_progress', 'completed', 'verified'))
|
|
);
|
|
|
|
-- Tabla: ticket_asignaciones (asignaciones de tickets a técnicos)
|
|
CREATE TABLE IF NOT EXISTS construction.ticket_asignaciones (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
ticket_id UUID NOT NULL REFERENCES construction.tickets_postventa(id) ON DELETE CASCADE,
|
|
technician_id UUID NOT NULL REFERENCES auth.users(id),
|
|
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
assigned_by UUID NOT NULL REFERENCES auth.users(id),
|
|
status VARCHAR(20) NOT NULL DEFAULT 'assigned',
|
|
accepted_at TIMESTAMPTZ,
|
|
scheduled_date DATE,
|
|
scheduled_time TIME,
|
|
completed_at TIMESTAMPTZ,
|
|
work_notes TEXT,
|
|
reassignment_reason TEXT,
|
|
is_current BOOLEAN NOT NULL DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ,
|
|
CONSTRAINT chk_assignment_status CHECK (status IN ('assigned', 'accepted', 'in_progress', 'completed', 'reassigned'))
|
|
);
|
|
|
|
-- Indices adicionales para calidad
|
|
CREATE INDEX IF NOT EXISTS idx_no_conformidades_tenant ON construction.no_conformidades(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_no_conformidades_status ON construction.no_conformidades(status);
|
|
CREATE INDEX IF NOT EXISTS idx_no_conformidades_inspeccion ON construction.no_conformidades(inspeccion_id);
|
|
CREATE INDEX IF NOT EXISTS idx_acciones_correctivas_nc ON construction.acciones_correctivas(no_conformidad_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ticket_asignaciones_ticket ON construction.ticket_asignaciones(ticket_id);
|
|
CREATE INDEX IF NOT EXISTS idx_ticket_asignaciones_technician ON construction.ticket_asignaciones(technician_id);
|
|
|
|
-- ============================================================================
|
|
-- TABLES - CONTRATOS Y SUBCONTRATOS (MAI-012)
|
|
-- ============================================================================
|
|
|
|
-- Tabla: subcontratistas
|
|
CREATE TABLE IF NOT EXISTS construction.subcontratistas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
partner_id UUID,
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
legal_name VARCHAR(255),
|
|
tax_id VARCHAR(20),
|
|
specialty VARCHAR(100),
|
|
contact_name VARCHAR(100),
|
|
contact_phone VARCHAR(20),
|
|
contact_email VARCHAR(100),
|
|
address TEXT,
|
|
rating DECIMAL(3,2),
|
|
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),
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES auth.users(id),
|
|
CONSTRAINT uq_subcontratistas_code_tenant UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
-- Tabla: contratos (contratos con subcontratistas)
|
|
CREATE TABLE IF NOT EXISTS construction.contratos (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
subcontratista_id UUID NOT NULL REFERENCES construction.subcontratistas(id),
|
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
|
contract_number VARCHAR(30) NOT NULL,
|
|
contract_type construction.contract_type NOT NULL DEFAULT 'unit_price',
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
start_date DATE NOT NULL,
|
|
end_date DATE,
|
|
total_amount DECIMAL(16,2),
|
|
advance_percentage DECIMAL(5,2) DEFAULT 0,
|
|
retention_percentage DECIMAL(5,2) DEFAULT 5,
|
|
status construction.contract_status NOT NULL DEFAULT 'draft',
|
|
signed_at TIMESTAMPTZ,
|
|
signed_by UUID REFERENCES auth.users(id),
|
|
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_contratos_number_tenant UNIQUE (tenant_id, contract_number)
|
|
);
|
|
|
|
-- Tabla: contrato_partidas (líneas del contrato)
|
|
CREATE TABLE IF NOT EXISTS construction.contrato_partidas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
contrato_id UUID NOT NULL REFERENCES construction.contratos(id) ON DELETE CASCADE,
|
|
concepto_id UUID NOT NULL REFERENCES construction.conceptos(id),
|
|
quantity DECIMAL(12,4) NOT NULL DEFAULT 0,
|
|
unit_price DECIMAL(12,4) NOT NULL DEFAULT 0,
|
|
total_amount DECIMAL(14,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
|
|
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)
|
|
);
|
|
|
|
-- Tabla: contrato_addendas (addendas/modificaciones a contratos)
|
|
CREATE TABLE IF NOT EXISTS construction.contrato_addendas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
contrato_id UUID NOT NULL REFERENCES construction.contratos(id) ON DELETE CASCADE,
|
|
addendum_number VARCHAR(50) NOT NULL,
|
|
addendum_type VARCHAR(30) NOT NULL,
|
|
title VARCHAR(255) NOT NULL,
|
|
description TEXT NOT NULL,
|
|
effective_date DATE NOT NULL,
|
|
new_end_date DATE,
|
|
amount_change DECIMAL(16,2) DEFAULT 0,
|
|
new_contract_amount DECIMAL(16,2),
|
|
scope_changes TEXT,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'draft',
|
|
approved_at TIMESTAMPTZ,
|
|
approved_by UUID REFERENCES auth.users(id),
|
|
rejection_reason TEXT,
|
|
document_url VARCHAR(500),
|
|
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_contrato_addendas_number_tenant UNIQUE (tenant_id, addendum_number)
|
|
);
|
|
|
|
-- ============================================================================
|
|
-- INDICES
|
|
-- ============================================================================
|
|
|
|
-- Proyectos
|
|
CREATE INDEX IF NOT EXISTS idx_proyectos_tenant_id ON construction.proyectos(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_proyectos_estado ON construction.proyectos(estado_proyecto);
|
|
CREATE INDEX IF NOT EXISTS idx_proyectos_codigo ON construction.proyectos(codigo);
|
|
|
|
-- Fraccionamientos
|
|
CREATE INDEX IF NOT EXISTS idx_fraccionamientos_tenant_id ON construction.fraccionamientos(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_fraccionamientos_proyecto_id ON construction.fraccionamientos(proyecto_id);
|
|
CREATE INDEX IF NOT EXISTS idx_fraccionamientos_status ON construction.fraccionamientos(status);
|
|
CREATE INDEX IF NOT EXISTS idx_fraccionamientos_code ON construction.fraccionamientos(code);
|
|
|
|
-- Etapas
|
|
CREATE INDEX IF NOT EXISTS idx_etapas_tenant_id ON construction.etapas(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_etapas_fraccionamiento_id ON construction.etapas(fraccionamiento_id);
|
|
|
|
-- Manzanas
|
|
CREATE INDEX IF NOT EXISTS idx_manzanas_tenant_id ON construction.manzanas(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_manzanas_etapa_id ON construction.manzanas(etapa_id);
|
|
|
|
-- Lotes
|
|
CREATE INDEX IF NOT EXISTS idx_lotes_tenant_id ON construction.lotes(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_lotes_manzana_id ON construction.lotes(manzana_id);
|
|
CREATE INDEX IF NOT EXISTS idx_lotes_prototipo_id ON construction.lotes(prototipo_id);
|
|
CREATE INDEX IF NOT EXISTS idx_lotes_status ON construction.lotes(status);
|
|
|
|
-- Torres
|
|
CREATE INDEX IF NOT EXISTS idx_torres_tenant_id ON construction.torres(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_torres_etapa_id ON construction.torres(etapa_id);
|
|
|
|
-- Niveles
|
|
CREATE INDEX IF NOT EXISTS idx_niveles_tenant_id ON construction.niveles(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_niveles_torre_id ON construction.niveles(torre_id);
|
|
|
|
-- Departamentos
|
|
CREATE INDEX IF NOT EXISTS idx_departamentos_tenant_id ON construction.departamentos(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_departamentos_nivel_id ON construction.departamentos(nivel_id);
|
|
CREATE INDEX IF NOT EXISTS idx_departamentos_status ON construction.departamentos(status);
|
|
|
|
-- Prototipos
|
|
CREATE INDEX IF NOT EXISTS idx_prototipos_tenant_id ON construction.prototipos(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_prototipos_type ON construction.prototipos(type);
|
|
|
|
-- Conceptos
|
|
CREATE INDEX IF NOT EXISTS idx_conceptos_tenant_id ON construction.conceptos(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conceptos_parent_id ON construction.conceptos(parent_id);
|
|
CREATE INDEX IF NOT EXISTS idx_conceptos_code ON construction.conceptos(code);
|
|
|
|
-- Presupuestos
|
|
CREATE INDEX IF NOT EXISTS idx_presupuestos_tenant_id ON construction.presupuestos(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_presupuestos_fraccionamiento_id ON construction.presupuestos(fraccionamiento_id);
|
|
|
|
-- Avances
|
|
CREATE INDEX IF NOT EXISTS idx_avances_tenant_id ON construction.avances_obra(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_avances_lote_id ON construction.avances_obra(lote_id);
|
|
CREATE INDEX IF NOT EXISTS idx_avances_concepto_id ON construction.avances_obra(concepto_id);
|
|
CREATE INDEX IF NOT EXISTS idx_avances_capture_date ON construction.avances_obra(capture_date);
|
|
|
|
-- Bitacora
|
|
CREATE INDEX IF NOT EXISTS idx_bitacora_tenant_id ON construction.bitacora_obra(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_bitacora_fraccionamiento_id ON construction.bitacora_obra(fraccionamiento_id);
|
|
|
|
-- Inspecciones
|
|
CREATE INDEX IF NOT EXISTS idx_inspecciones_tenant_id ON construction.inspecciones(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_inspecciones_status ON construction.inspecciones(status);
|
|
|
|
-- Tickets
|
|
CREATE INDEX IF NOT EXISTS idx_tickets_tenant_id ON construction.tickets_postventa(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_tickets_status ON construction.tickets_postventa(status);
|
|
|
|
-- Subcontratistas
|
|
CREATE INDEX IF NOT EXISTS idx_subcontratistas_tenant_id ON construction.subcontratistas(tenant_id);
|
|
|
|
-- Contratos
|
|
CREATE INDEX IF NOT EXISTS idx_contratos_tenant_id ON construction.contratos(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_contratos_subcontratista_id ON construction.contratos(subcontratista_id);
|
|
CREATE INDEX IF NOT EXISTS idx_contratos_fraccionamiento_id ON construction.contratos(fraccionamiento_id);
|
|
|
|
-- ============================================================================
|
|
-- ROW LEVEL SECURITY (RLS)
|
|
-- ============================================================================
|
|
|
|
ALTER TABLE construction.proyectos ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.fraccionamientos ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.etapas ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.manzanas ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.lotes ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.torres ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.niveles ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.departamentos ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.prototipos ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.conceptos ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.presupuestos ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.presupuesto_partidas ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.programa_obra ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.programa_actividades ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.avances_obra ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.fotos_avance ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.bitacora_obra ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.checklists ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.checklist_items ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.inspecciones ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.inspeccion_resultados ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.tickets_postventa ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.subcontratistas ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.contratos ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.contrato_partidas ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Policies de tenant isolation usando current_setting
|
|
DO $$ BEGIN
|
|
DROP POLICY IF EXISTS tenant_isolation_proyectos ON construction.proyectos;
|
|
CREATE POLICY tenant_isolation_proyectos ON construction.proyectos
|
|
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_fraccionamientos ON construction.fraccionamientos;
|
|
CREATE POLICY tenant_isolation_fraccionamientos ON construction.fraccionamientos
|
|
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_etapas ON construction.etapas;
|
|
CREATE POLICY tenant_isolation_etapas ON construction.etapas
|
|
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_manzanas ON construction.manzanas;
|
|
CREATE POLICY tenant_isolation_manzanas ON construction.manzanas
|
|
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_lotes ON construction.lotes;
|
|
CREATE POLICY tenant_isolation_lotes ON construction.lotes
|
|
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_torres ON construction.torres;
|
|
CREATE POLICY tenant_isolation_torres ON construction.torres
|
|
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_niveles ON construction.niveles;
|
|
CREATE POLICY tenant_isolation_niveles ON construction.niveles
|
|
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_departamentos ON construction.departamentos;
|
|
CREATE POLICY tenant_isolation_departamentos ON construction.departamentos
|
|
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_prototipos ON construction.prototipos;
|
|
CREATE POLICY tenant_isolation_prototipos ON construction.prototipos
|
|
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_conceptos ON construction.conceptos;
|
|
CREATE POLICY tenant_isolation_conceptos ON construction.conceptos
|
|
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_presupuestos ON construction.presupuestos;
|
|
CREATE POLICY tenant_isolation_presupuestos ON construction.presupuestos
|
|
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_presupuesto_partidas ON construction.presupuesto_partidas;
|
|
CREATE POLICY tenant_isolation_presupuesto_partidas ON construction.presupuesto_partidas
|
|
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_programa_obra ON construction.programa_obra;
|
|
CREATE POLICY tenant_isolation_programa_obra ON construction.programa_obra
|
|
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_programa_actividades ON construction.programa_actividades;
|
|
CREATE POLICY tenant_isolation_programa_actividades ON construction.programa_actividades
|
|
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_avances_obra ON construction.avances_obra;
|
|
CREATE POLICY tenant_isolation_avances_obra ON construction.avances_obra
|
|
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_fotos_avance ON construction.fotos_avance;
|
|
CREATE POLICY tenant_isolation_fotos_avance ON construction.fotos_avance
|
|
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_bitacora_obra ON construction.bitacora_obra;
|
|
CREATE POLICY tenant_isolation_bitacora_obra ON construction.bitacora_obra
|
|
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_checklists ON construction.checklists;
|
|
CREATE POLICY tenant_isolation_checklists ON construction.checklists
|
|
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_checklist_items ON construction.checklist_items;
|
|
CREATE POLICY tenant_isolation_checklist_items ON construction.checklist_items
|
|
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_inspecciones ON construction.inspecciones;
|
|
CREATE POLICY tenant_isolation_inspecciones ON construction.inspecciones
|
|
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_inspeccion_resultados ON construction.inspeccion_resultados;
|
|
CREATE POLICY tenant_isolation_inspeccion_resultados ON construction.inspeccion_resultados
|
|
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_tickets_postventa ON construction.tickets_postventa;
|
|
CREATE POLICY tenant_isolation_tickets_postventa ON construction.tickets_postventa
|
|
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_subcontratistas ON construction.subcontratistas;
|
|
CREATE POLICY tenant_isolation_subcontratistas ON construction.subcontratistas
|
|
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_contratos ON construction.contratos;
|
|
CREATE POLICY tenant_isolation_contratos ON construction.contratos
|
|
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_contrato_partidas ON construction.contrato_partidas;
|
|
CREATE POLICY tenant_isolation_contrato_partidas ON construction.contrato_partidas
|
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
|
|
|
-- ============================================================================
|
|
-- COMENTARIOS
|
|
-- ============================================================================
|
|
|
|
COMMENT ON SCHEMA construction IS 'Schema de construcción: obras, lotes, avances, calidad, contratos';
|
|
COMMENT ON TABLE construction.proyectos IS 'Proyectos de desarrollo inmobiliario';
|
|
COMMENT ON TABLE construction.fraccionamientos IS 'Fraccionamientos/obras dentro de un proyecto';
|
|
COMMENT ON TABLE construction.etapas IS 'Etapas/fases de un fraccionamiento';
|
|
COMMENT ON TABLE construction.manzanas IS 'Manzanas dentro de una etapa';
|
|
COMMENT ON TABLE construction.lotes IS 'Lotes/terrenos vendibles (horizontal)';
|
|
COMMENT ON TABLE construction.torres IS 'Torres/edificios (vertical)';
|
|
COMMENT ON TABLE construction.niveles IS 'Pisos de una torre';
|
|
COMMENT ON TABLE construction.departamentos IS 'Departamentos/unidades en torre';
|
|
COMMENT ON TABLE construction.prototipos IS 'Tipos de vivienda/prototipos';
|
|
COMMENT ON TABLE construction.conceptos IS 'Catálogo de conceptos de obra';
|
|
COMMENT ON TABLE construction.presupuestos IS 'Presupuestos por prototipo u obra';
|
|
COMMENT ON TABLE construction.avances_obra IS 'Captura de avances físicos';
|
|
COMMENT ON TABLE construction.bitacora_obra IS 'Bitácora diaria de obra';
|
|
COMMENT ON TABLE construction.checklists IS 'Plantillas de verificación';
|
|
COMMENT ON TABLE construction.inspecciones IS 'Inspecciones de calidad';
|
|
COMMENT ON TABLE construction.tickets_postventa IS 'Tickets de garantía';
|
|
COMMENT ON TABLE construction.subcontratistas IS 'Catálogo de subcontratistas';
|
|
COMMENT ON TABLE construction.contratos IS 'Contratos con subcontratistas';
|
|
|
|
-- ============================================================================
|
|
-- FIN DEL SCHEMA CONSTRUCTION
|
|
-- Total tablas: 25 (agregada proyectos)
|
|
-- ============================================================================
|