1020 lines
29 KiB
Markdown
1020 lines
29 KiB
Markdown
# DDL SPECIFICATION: Schema Construction
|
|
|
|
**Version:** 1.0.0
|
|
**Fecha:** 2025-12-05
|
|
**Schema:** `construction`
|
|
**Modulos:** MAI-002 (Proyectos), MAI-003 (Presupuestos), MAI-005 (Control Obra)
|
|
|
|
---
|
|
|
|
## Resumen
|
|
|
|
| Metrica | Valor |
|
|
|---------|-------|
|
|
| Total Tablas | 25 |
|
|
| ENUMs | 8 |
|
|
| Funciones | 6 |
|
|
| Triggers | 4 |
|
|
| Indices | 35+ |
|
|
|
|
---
|
|
|
|
## 1. ENUMs
|
|
|
|
```sql
|
|
-- Estados de proyecto
|
|
CREATE TYPE construction.project_status AS ENUM (
|
|
'planning', -- En planeacion
|
|
'in_progress', -- En ejecucion
|
|
'paused', -- Pausado
|
|
'completed', -- Completado
|
|
'cancelled' -- Cancelado
|
|
);
|
|
|
|
-- Estados de vivienda
|
|
CREATE TYPE construction.housing_unit_status AS ENUM (
|
|
'land', -- Terreno
|
|
'foundation', -- Cimentacion
|
|
'structure', -- Estructura
|
|
'finishing', -- Acabados
|
|
'completed', -- Terminada
|
|
'delivered' -- Entregada
|
|
);
|
|
|
|
-- Tipos de concepto de presupuesto
|
|
CREATE TYPE construction.concept_type AS ENUM (
|
|
'material', -- Material
|
|
'labor', -- Mano de obra
|
|
'equipment', -- Equipo
|
|
'subcontract', -- Subcontrato
|
|
'indirect' -- Indirecto
|
|
);
|
|
|
|
-- Estados de presupuesto
|
|
CREATE TYPE construction.budget_status AS ENUM (
|
|
'draft', -- Borrador
|
|
'approved', -- Aprobado
|
|
'active', -- Activo (en uso)
|
|
'closed' -- Cerrado
|
|
);
|
|
|
|
-- Tipos de avance
|
|
CREATE TYPE construction.progress_type AS ENUM (
|
|
'quantity', -- Por cantidad
|
|
'percentage' -- Por porcentaje
|
|
);
|
|
|
|
-- Estados de estimacion
|
|
CREATE TYPE construction.estimation_status AS ENUM (
|
|
'draft', -- Borrador
|
|
'submitted', -- Enviada
|
|
'approved', -- Aprobada
|
|
'rejected', -- Rechazada
|
|
'paid' -- Pagada
|
|
);
|
|
|
|
-- Tipos de incidencia
|
|
CREATE TYPE construction.incident_type AS ENUM (
|
|
'delay', -- Retraso
|
|
'quality', -- Calidad
|
|
'safety', -- Seguridad
|
|
'material', -- Material
|
|
'weather', -- Clima
|
|
'other' -- Otro
|
|
);
|
|
|
|
-- Tipos de entrada de bitacora
|
|
CREATE TYPE construction.logbook_entry_type AS ENUM (
|
|
'daily_report', -- Reporte diario
|
|
'incident', -- Incidencia
|
|
'instruction', -- Instruccion
|
|
'observation', -- Observacion
|
|
'visit' -- Visita
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Tablas de Proyectos (MAI-002)
|
|
|
|
### 2.1 projects (Proyectos)
|
|
|
|
```sql
|
|
CREATE TABLE construction.projects (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Ubicacion
|
|
address TEXT,
|
|
city VARCHAR(100),
|
|
state VARCHAR(100),
|
|
postal_code VARCHAR(10),
|
|
location GEOGRAPHY(POINT, 4326), -- PostGIS
|
|
|
|
-- Fechas
|
|
start_date DATE,
|
|
end_date DATE,
|
|
actual_start_date DATE,
|
|
actual_end_date DATE,
|
|
|
|
-- Estado
|
|
status construction.project_status NOT NULL DEFAULT 'planning',
|
|
progress_percentage DECIMAL(5,2) DEFAULT 0,
|
|
|
|
-- Responsables
|
|
project_manager_id UUID REFERENCES core.users(id),
|
|
|
|
-- Financiero
|
|
total_budget DECIMAL(18,2),
|
|
total_spent DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_projects_code UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
-- Indices
|
|
CREATE INDEX idx_projects_tenant ON construction.projects(tenant_id);
|
|
CREATE INDEX idx_projects_status ON construction.projects(tenant_id, status);
|
|
CREATE INDEX idx_projects_location ON construction.projects USING GIST(location);
|
|
CREATE INDEX idx_projects_active ON construction.projects(tenant_id) WHERE deleted_at IS NULL;
|
|
```
|
|
|
|
### 2.2 developments (Fraccionamientos/Desarrollos)
|
|
|
|
```sql
|
|
CREATE TABLE construction.developments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
project_id UUID NOT NULL REFERENCES construction.projects(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Ubicacion
|
|
address TEXT,
|
|
location GEOGRAPHY(POINT, 4326),
|
|
area_m2 DECIMAL(12,2),
|
|
|
|
-- Totales
|
|
total_sections INTEGER DEFAULT 0,
|
|
total_units INTEGER DEFAULT 0,
|
|
completed_units INTEGER DEFAULT 0,
|
|
|
|
-- Estado
|
|
status construction.project_status NOT NULL DEFAULT 'planning',
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_developments_code UNIQUE (tenant_id, project_id, code)
|
|
);
|
|
|
|
CREATE INDEX idx_developments_project ON construction.developments(project_id);
|
|
CREATE INDEX idx_developments_tenant ON construction.developments(tenant_id);
|
|
```
|
|
|
|
### 2.3 sections (Secciones/Manzanas)
|
|
|
|
```sql
|
|
CREATE TABLE construction.sections (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
development_id UUID NOT NULL REFERENCES construction.developments(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(100) NOT NULL,
|
|
|
|
-- Geometria
|
|
boundary GEOGRAPHY(POLYGON, 4326),
|
|
area_m2 DECIMAL(12,2),
|
|
|
|
-- Totales
|
|
total_units INTEGER DEFAULT 0,
|
|
completed_units INTEGER DEFAULT 0,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_sections_code UNIQUE (tenant_id, development_id, code)
|
|
);
|
|
|
|
CREATE INDEX idx_sections_development ON construction.sections(development_id);
|
|
```
|
|
|
|
### 2.4 prototypes (Prototipos de Vivienda)
|
|
|
|
```sql
|
|
CREATE TABLE construction.prototypes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Especificaciones
|
|
bedrooms INTEGER,
|
|
bathrooms DECIMAL(3,1),
|
|
floors INTEGER DEFAULT 1,
|
|
construction_area_m2 DECIMAL(10,2),
|
|
land_area_m2 DECIMAL(10,2),
|
|
|
|
-- Precios
|
|
base_price DECIMAL(18,2),
|
|
construction_cost DECIMAL(18,2),
|
|
|
|
-- Imagen
|
|
image_url TEXT,
|
|
blueprint_url TEXT,
|
|
|
|
-- Estado
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_prototypes_code UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
CREATE INDEX idx_prototypes_tenant ON construction.prototypes(tenant_id);
|
|
CREATE INDEX idx_prototypes_active ON construction.prototypes(tenant_id) WHERE is_active = true;
|
|
```
|
|
|
|
### 2.5 housing_units (Viviendas)
|
|
|
|
```sql
|
|
CREATE TABLE construction.housing_units (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
section_id UUID NOT NULL REFERENCES construction.sections(id),
|
|
prototype_id UUID REFERENCES construction.prototypes(id),
|
|
|
|
-- Identificacion
|
|
unit_number VARCHAR(20) NOT NULL,
|
|
lot_number VARCHAR(20),
|
|
block VARCHAR(20),
|
|
|
|
-- Ubicacion
|
|
location GEOGRAPHY(POINT, 4326),
|
|
address TEXT,
|
|
|
|
-- Estado de construccion
|
|
status construction.housing_unit_status NOT NULL DEFAULT 'land',
|
|
progress_percentage DECIMAL(5,2) DEFAULT 0,
|
|
|
|
-- Fechas
|
|
construction_start_date DATE,
|
|
construction_end_date DATE,
|
|
delivery_date DATE,
|
|
|
|
-- Cliente (si vendida)
|
|
buyer_name VARCHAR(200),
|
|
buyer_contact TEXT,
|
|
sale_price DECIMAL(18,2),
|
|
sale_date DATE,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_housing_units_number UNIQUE (tenant_id, section_id, unit_number)
|
|
);
|
|
|
|
CREATE INDEX idx_housing_units_section ON construction.housing_units(section_id);
|
|
CREATE INDEX idx_housing_units_status ON construction.housing_units(tenant_id, status);
|
|
CREATE INDEX idx_housing_units_prototype ON construction.housing_units(prototype_id);
|
|
CREATE INDEX idx_housing_units_location ON construction.housing_units USING GIST(location);
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Tablas de Presupuestos (MAI-003)
|
|
|
|
### 3.1 budgets (Presupuestos)
|
|
|
|
```sql
|
|
CREATE TABLE construction.budgets (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Vinculacion
|
|
project_id UUID REFERENCES construction.projects(id),
|
|
prototype_id UUID REFERENCES construction.prototypes(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
version INTEGER DEFAULT 1,
|
|
|
|
-- Tipo
|
|
is_base BOOLEAN DEFAULT false, -- true = presupuesto base (por prototipo)
|
|
|
|
-- Totales calculados
|
|
total_direct_cost DECIMAL(18,2) DEFAULT 0,
|
|
total_indirect_cost DECIMAL(18,2) DEFAULT 0,
|
|
total_cost DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Indirectos
|
|
indirect_percentage DECIMAL(5,2) DEFAULT 0,
|
|
profit_percentage DECIMAL(5,2) DEFAULT 0,
|
|
|
|
-- Estado
|
|
status construction.budget_status NOT NULL DEFAULT 'draft',
|
|
approved_at TIMESTAMP,
|
|
approved_by UUID REFERENCES core.users(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_budgets_code UNIQUE (tenant_id, code, version)
|
|
);
|
|
|
|
CREATE INDEX idx_budgets_project ON construction.budgets(project_id);
|
|
CREATE INDEX idx_budgets_prototype ON construction.budgets(prototype_id);
|
|
CREATE INDEX idx_budgets_status ON construction.budgets(tenant_id, status);
|
|
```
|
|
|
|
### 3.2 budget_partidas (Partidas)
|
|
|
|
```sql
|
|
CREATE TABLE construction.budget_partidas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
budget_id UUID NOT NULL REFERENCES construction.budgets(id),
|
|
parent_id UUID REFERENCES construction.budget_partidas(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Orden
|
|
sort_order INTEGER DEFAULT 0,
|
|
level INTEGER DEFAULT 1,
|
|
|
|
-- Totales (calculados)
|
|
total_cost DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
|
|
CONSTRAINT uq_partidas_code UNIQUE (budget_id, code)
|
|
);
|
|
|
|
CREATE INDEX idx_partidas_budget ON construction.budget_partidas(budget_id);
|
|
CREATE INDEX idx_partidas_parent ON construction.budget_partidas(parent_id);
|
|
```
|
|
|
|
### 3.3 budget_concepts (Conceptos)
|
|
|
|
```sql
|
|
CREATE TABLE construction.budget_concepts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
partida_id UUID NOT NULL REFERENCES construction.budget_partidas(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(30) NOT NULL,
|
|
name VARCHAR(300) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Unidad y cantidad
|
|
unit_id UUID REFERENCES core.units_of_measure(id),
|
|
unit_code VARCHAR(10),
|
|
quantity DECIMAL(14,4) NOT NULL DEFAULT 0,
|
|
|
|
-- Precio unitario
|
|
unit_price DECIMAL(14,4) NOT NULL DEFAULT 0,
|
|
total_price DECIMAL(18,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
|
|
|
|
-- Tipo
|
|
concept_type construction.concept_type DEFAULT 'material',
|
|
|
|
-- APU vinculado
|
|
has_apu BOOLEAN DEFAULT false,
|
|
|
|
-- Orden
|
|
sort_order INTEGER DEFAULT 0,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_concepts_partida ON construction.budget_concepts(partida_id);
|
|
CREATE INDEX idx_concepts_type ON construction.budget_concepts(concept_type);
|
|
```
|
|
|
|
### 3.4 apu_items (Analisis de Precios Unitarios)
|
|
|
|
```sql
|
|
CREATE TABLE construction.apu_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
concept_id UUID NOT NULL REFERENCES construction.budget_concepts(id),
|
|
|
|
-- Tipo de insumo
|
|
item_type construction.concept_type NOT NULL,
|
|
|
|
-- Identificacion del insumo
|
|
product_id UUID REFERENCES core.products(id),
|
|
product_code VARCHAR(50),
|
|
product_name VARCHAR(200) NOT NULL,
|
|
|
|
-- Unidad y cantidad
|
|
unit_code VARCHAR(10),
|
|
quantity DECIMAL(14,6) NOT NULL DEFAULT 0,
|
|
|
|
-- Costo
|
|
unit_cost DECIMAL(14,4) NOT NULL DEFAULT 0,
|
|
total_cost DECIMAL(18,2) GENERATED ALWAYS AS (quantity * unit_cost) STORED,
|
|
|
|
-- Rendimiento (para mano de obra)
|
|
yield_factor DECIMAL(10,4) DEFAULT 1,
|
|
|
|
-- Orden
|
|
sort_order INTEGER DEFAULT 0,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_apu_concept ON construction.apu_items(concept_id);
|
|
CREATE INDEX idx_apu_product ON construction.apu_items(product_id);
|
|
CREATE INDEX idx_apu_type ON construction.apu_items(item_type);
|
|
```
|
|
|
|
### 3.5 materials_explosion (Explosion de Materiales)
|
|
|
|
```sql
|
|
CREATE TABLE construction.materials_explosion (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
budget_id UUID NOT NULL REFERENCES construction.budgets(id),
|
|
|
|
-- Producto
|
|
product_id UUID REFERENCES core.products(id),
|
|
product_code VARCHAR(50) NOT NULL,
|
|
product_name VARCHAR(200) NOT NULL,
|
|
|
|
-- Unidad
|
|
unit_code VARCHAR(10),
|
|
|
|
-- Cantidades
|
|
total_quantity DECIMAL(14,4) NOT NULL DEFAULT 0,
|
|
|
|
-- Costo
|
|
unit_cost DECIMAL(14,4),
|
|
total_cost DECIMAL(18,2),
|
|
|
|
-- Fecha de calculo
|
|
calculated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_explosion_budget ON construction.materials_explosion(budget_id);
|
|
CREATE INDEX idx_explosion_product ON construction.materials_explosion(product_id);
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Tablas de Control de Obra (MAI-005)
|
|
|
|
### 4.1 schedules (Programas de Obra)
|
|
|
|
```sql
|
|
CREATE TABLE construction.schedules (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
project_id UUID NOT NULL REFERENCES construction.projects(id),
|
|
|
|
-- Identificacion
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
version INTEGER DEFAULT 1,
|
|
|
|
-- Fechas
|
|
start_date DATE NOT NULL,
|
|
end_date DATE NOT NULL,
|
|
|
|
-- Estado
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_schedules_project ON construction.schedules(project_id);
|
|
```
|
|
|
|
### 4.2 schedule_items (Actividades del Programa)
|
|
|
|
```sql
|
|
CREATE TABLE construction.schedule_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
schedule_id UUID NOT NULL REFERENCES construction.schedules(id),
|
|
parent_id UUID REFERENCES construction.schedule_items(id),
|
|
concept_id UUID REFERENCES construction.budget_concepts(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
|
|
-- Fechas programadas
|
|
planned_start DATE NOT NULL,
|
|
planned_end DATE NOT NULL,
|
|
duration_days INTEGER,
|
|
|
|
-- Fechas reales
|
|
actual_start DATE,
|
|
actual_end DATE,
|
|
|
|
-- Avance
|
|
planned_progress DECIMAL(5,2) DEFAULT 0,
|
|
actual_progress DECIMAL(5,2) DEFAULT 0,
|
|
|
|
-- Dependencias (WBS)
|
|
predecessors TEXT[], -- Array de IDs
|
|
|
|
-- Orden
|
|
sort_order INTEGER DEFAULT 0,
|
|
level INTEGER DEFAULT 1,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_schedule_items_schedule ON construction.schedule_items(schedule_id);
|
|
CREATE INDEX idx_schedule_items_parent ON construction.schedule_items(parent_id);
|
|
CREATE INDEX idx_schedule_items_concept ON construction.schedule_items(concept_id);
|
|
```
|
|
|
|
### 4.3 progress_records (Registros de Avance)
|
|
|
|
```sql
|
|
CREATE TABLE construction.progress_records (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
project_id UUID NOT NULL REFERENCES construction.projects(id),
|
|
|
|
-- Vinculacion
|
|
housing_unit_id UUID REFERENCES construction.housing_units(id),
|
|
concept_id UUID REFERENCES construction.budget_concepts(id),
|
|
schedule_item_id UUID REFERENCES construction.schedule_items(id),
|
|
|
|
-- Fecha del avance
|
|
record_date DATE NOT NULL,
|
|
|
|
-- Tipo de avance
|
|
progress_type construction.progress_type NOT NULL DEFAULT 'percentage',
|
|
|
|
-- Valores
|
|
previous_progress DECIMAL(14,4) DEFAULT 0,
|
|
current_progress DECIMAL(14,4) NOT NULL,
|
|
progress_increment DECIMAL(14,4),
|
|
|
|
-- Para tipo cantidad
|
|
quantity_executed DECIMAL(14,4),
|
|
unit_code VARCHAR(10),
|
|
|
|
-- Notas
|
|
notes TEXT,
|
|
|
|
-- Aprobacion
|
|
approved_at TIMESTAMP,
|
|
approved_by UUID REFERENCES core.users(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_progress_project ON construction.progress_records(project_id);
|
|
CREATE INDEX idx_progress_unit ON construction.progress_records(housing_unit_id);
|
|
CREATE INDEX idx_progress_concept ON construction.progress_records(concept_id);
|
|
CREATE INDEX idx_progress_date ON construction.progress_records(record_date);
|
|
```
|
|
|
|
### 4.4 logbook_entries (Bitacora de Obra)
|
|
|
|
```sql
|
|
CREATE TABLE construction.logbook_entries (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
project_id UUID NOT NULL REFERENCES construction.projects(id),
|
|
|
|
-- Fecha y tipo
|
|
entry_date DATE NOT NULL,
|
|
entry_type construction.logbook_entry_type NOT NULL,
|
|
|
|
-- Contenido
|
|
title VARCHAR(200) NOT NULL,
|
|
description TEXT NOT NULL,
|
|
|
|
-- Clima (para reporte diario)
|
|
weather VARCHAR(50),
|
|
temperature_min DECIMAL(4,1),
|
|
temperature_max DECIMAL(4,1),
|
|
|
|
-- Personal (para reporte diario)
|
|
workers_count INTEGER,
|
|
|
|
-- Vinculacion opcional
|
|
housing_unit_id UUID REFERENCES construction.housing_units(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_logbook_project ON construction.logbook_entries(project_id);
|
|
CREATE INDEX idx_logbook_date ON construction.logbook_entries(entry_date);
|
|
CREATE INDEX idx_logbook_type ON construction.logbook_entries(entry_type);
|
|
```
|
|
|
|
### 4.5 progress_photos (Fotos de Avance)
|
|
|
|
```sql
|
|
CREATE TABLE construction.progress_photos (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Vinculacion
|
|
progress_record_id UUID REFERENCES construction.progress_records(id),
|
|
logbook_entry_id UUID REFERENCES construction.logbook_entries(id),
|
|
housing_unit_id UUID REFERENCES construction.housing_units(id),
|
|
|
|
-- Archivo
|
|
file_url TEXT NOT NULL,
|
|
thumbnail_url TEXT,
|
|
file_name VARCHAR(255),
|
|
file_size INTEGER,
|
|
mime_type VARCHAR(50),
|
|
|
|
-- Metadata
|
|
caption TEXT,
|
|
taken_at TIMESTAMP,
|
|
location GEOGRAPHY(POINT, 4326),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_photos_progress ON construction.progress_photos(progress_record_id);
|
|
CREATE INDEX idx_photos_logbook ON construction.progress_photos(logbook_entry_id);
|
|
CREATE INDEX idx_photos_unit ON construction.progress_photos(housing_unit_id);
|
|
```
|
|
|
|
### 4.6 estimations (Estimaciones)
|
|
|
|
```sql
|
|
CREATE TABLE construction.estimations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
project_id UUID NOT NULL REFERENCES construction.projects(id),
|
|
|
|
-- Identificacion
|
|
estimation_number VARCHAR(20) NOT NULL,
|
|
period_start DATE NOT NULL,
|
|
period_end DATE NOT NULL,
|
|
|
|
-- Montos
|
|
previous_accumulated DECIMAL(18,2) DEFAULT 0,
|
|
current_period DECIMAL(18,2) DEFAULT 0,
|
|
total_accumulated DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Deducciones
|
|
advance_amortization DECIMAL(18,2) DEFAULT 0,
|
|
retentions DECIMAL(18,2) DEFAULT 0,
|
|
other_deductions DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Neto
|
|
net_amount DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Estado
|
|
status construction.estimation_status NOT NULL DEFAULT 'draft',
|
|
|
|
-- Aprobacion
|
|
submitted_at TIMESTAMP,
|
|
submitted_by UUID REFERENCES core.users(id),
|
|
approved_at TIMESTAMP,
|
|
approved_by UUID REFERENCES core.users(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_estimations_number UNIQUE (tenant_id, project_id, estimation_number)
|
|
);
|
|
|
|
CREATE INDEX idx_estimations_project ON construction.estimations(project_id);
|
|
CREATE INDEX idx_estimations_status ON construction.estimations(status);
|
|
CREATE INDEX idx_estimations_period ON construction.estimations(period_start, period_end);
|
|
```
|
|
|
|
### 4.7 estimation_lines (Lineas de Estimacion)
|
|
|
|
```sql
|
|
CREATE TABLE construction.estimation_lines (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
estimation_id UUID NOT NULL REFERENCES construction.estimations(id),
|
|
concept_id UUID NOT NULL REFERENCES construction.budget_concepts(id),
|
|
|
|
-- Cantidades
|
|
budget_quantity DECIMAL(14,4),
|
|
previous_quantity DECIMAL(14,4) DEFAULT 0,
|
|
current_quantity DECIMAL(14,4) NOT NULL,
|
|
accumulated_quantity DECIMAL(14,4),
|
|
|
|
-- Precio
|
|
unit_price DECIMAL(14,4) NOT NULL,
|
|
|
|
-- Montos
|
|
previous_amount DECIMAL(18,2) DEFAULT 0,
|
|
current_amount DECIMAL(18,2) NOT NULL,
|
|
accumulated_amount DECIMAL(18,2),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_estimation_lines_estimation ON construction.estimation_lines(estimation_id);
|
|
CREATE INDEX idx_estimation_lines_concept ON construction.estimation_lines(concept_id);
|
|
```
|
|
|
|
### 4.8 incidents (Incidencias)
|
|
|
|
```sql
|
|
CREATE TABLE construction.incidents (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
project_id UUID NOT NULL REFERENCES construction.projects(id),
|
|
|
|
-- Identificacion
|
|
incident_number VARCHAR(20) NOT NULL,
|
|
incident_type construction.incident_type NOT NULL,
|
|
|
|
-- Descripcion
|
|
title VARCHAR(200) NOT NULL,
|
|
description TEXT NOT NULL,
|
|
|
|
-- Impacto
|
|
severity VARCHAR(20), -- low, medium, high, critical
|
|
estimated_delay_days INTEGER,
|
|
cost_impact DECIMAL(18,2),
|
|
|
|
-- Vinculacion
|
|
housing_unit_id UUID REFERENCES construction.housing_units(id),
|
|
|
|
-- Resolucion
|
|
resolution TEXT,
|
|
resolved_at TIMESTAMP,
|
|
resolved_by UUID REFERENCES core.users(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
|
|
CONSTRAINT uq_incidents_number UNIQUE (tenant_id, project_id, incident_number)
|
|
);
|
|
|
|
CREATE INDEX idx_incidents_project ON construction.incidents(project_id);
|
|
CREATE INDEX idx_incidents_type ON construction.incidents(incident_type);
|
|
CREATE INDEX idx_incidents_unresolved ON construction.incidents(project_id) WHERE resolved_at IS NULL;
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Funciones
|
|
|
|
### 5.1 Calcular Totales de Presupuesto
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION construction.calculate_budget_totals(p_budget_id UUID)
|
|
RETURNS VOID AS $$
|
|
BEGIN
|
|
-- Actualizar totales de partidas
|
|
UPDATE construction.budget_partidas bp
|
|
SET total_cost = (
|
|
SELECT COALESCE(SUM(total_price), 0)
|
|
FROM construction.budget_concepts bc
|
|
WHERE bc.partida_id = bp.id
|
|
)
|
|
WHERE bp.budget_id = p_budget_id;
|
|
|
|
-- Actualizar total del presupuesto
|
|
UPDATE construction.budgets b
|
|
SET
|
|
total_direct_cost = (
|
|
SELECT COALESCE(SUM(total_cost), 0)
|
|
FROM construction.budget_partidas
|
|
WHERE budget_id = p_budget_id AND level = 1
|
|
),
|
|
total_cost = total_direct_cost * (1 + indirect_percentage/100) * (1 + profit_percentage/100),
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = p_budget_id;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
### 5.2 Explosionar Materiales
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION construction.explode_materials(p_budget_id UUID)
|
|
RETURNS INTEGER AS $$
|
|
DECLARE
|
|
v_count INTEGER := 0;
|
|
BEGIN
|
|
-- Limpiar explosion anterior
|
|
DELETE FROM construction.materials_explosion WHERE budget_id = p_budget_id;
|
|
|
|
-- Insertar materiales agrupados
|
|
INSERT INTO construction.materials_explosion (
|
|
tenant_id, budget_id, product_id, product_code, product_name,
|
|
unit_code, total_quantity, unit_cost, total_cost, calculated_at
|
|
)
|
|
SELECT
|
|
b.tenant_id,
|
|
b.id,
|
|
ai.product_id,
|
|
ai.product_code,
|
|
ai.product_name,
|
|
ai.unit_code,
|
|
SUM(ai.quantity * bc.quantity) as total_quantity,
|
|
AVG(ai.unit_cost) as unit_cost,
|
|
SUM(ai.quantity * bc.quantity * ai.unit_cost) as total_cost,
|
|
CURRENT_TIMESTAMP
|
|
FROM construction.budgets b
|
|
JOIN construction.budget_partidas bp ON bp.budget_id = b.id
|
|
JOIN construction.budget_concepts bc ON bc.partida_id = bp.id
|
|
JOIN construction.apu_items ai ON ai.concept_id = bc.id
|
|
WHERE b.id = p_budget_id
|
|
AND ai.item_type = 'material'
|
|
GROUP BY b.tenant_id, b.id, ai.product_id, ai.product_code, ai.product_name, ai.unit_code;
|
|
|
|
GET DIAGNOSTICS v_count = ROW_COUNT;
|
|
RETURN v_count;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
### 5.3 Actualizar Progreso de Vivienda
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION construction.update_housing_progress()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
-- Actualizar porcentaje de avance de la vivienda
|
|
UPDATE construction.housing_units hu
|
|
SET
|
|
progress_percentage = (
|
|
SELECT COALESCE(AVG(current_progress), 0)
|
|
FROM construction.progress_records pr
|
|
WHERE pr.housing_unit_id = hu.id
|
|
),
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = NEW.housing_unit_id;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_update_housing_progress
|
|
AFTER INSERT OR UPDATE ON construction.progress_records
|
|
FOR EACH ROW
|
|
WHEN (NEW.housing_unit_id IS NOT NULL)
|
|
EXECUTE FUNCTION construction.update_housing_progress();
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Row Level Security
|
|
|
|
```sql
|
|
-- Habilitar RLS en todas las tablas
|
|
ALTER TABLE construction.projects ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.developments ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.sections ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.prototypes ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.housing_units ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.budgets ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.budget_partidas ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.budget_concepts ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.apu_items ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.schedules ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.schedule_items ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.progress_records ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.logbook_entries ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.progress_photos ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.estimations ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.estimation_lines ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE construction.incidents ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Crear politicas de aislamiento por tenant
|
|
DO $$
|
|
DECLARE
|
|
t TEXT;
|
|
BEGIN
|
|
FOR t IN SELECT tablename FROM pg_tables WHERE schemaname = 'construction'
|
|
LOOP
|
|
EXECUTE format('
|
|
CREATE POLICY tenant_isolation ON construction.%I
|
|
USING (tenant_id = current_setting(''app.current_tenant_id'')::uuid)
|
|
', t);
|
|
END LOOP;
|
|
END $$;
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Seeds Iniciales
|
|
|
|
```sql
|
|
-- Prototipos de ejemplo
|
|
INSERT INTO construction.prototypes (tenant_id, code, name, bedrooms, bathrooms, construction_area_m2)
|
|
VALUES
|
|
('{{TENANT_ID}}', 'CASA-2R', 'Casa 2 Recamaras', 2, 1, 55.00),
|
|
('{{TENANT_ID}}', 'CASA-3R', 'Casa 3 Recamaras', 3, 1.5, 75.00),
|
|
('{{TENANT_ID}}', 'CASA-4R', 'Casa 4 Recamaras', 4, 2.5, 120.00);
|
|
```
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [MAI-002: Proyectos y Estructura](../../02-definicion-modulos/MAI-002-proyectos-estructura/)
|
|
- [MAI-003: Presupuestos y Costos](../../02-definicion-modulos/MAI-003-presupuestos-costos/)
|
|
- [MAI-005: Control de Obra](../../02-definicion-modulos/MAI-005-control-obra-avances/)
|
|
- [ADR-007: Database Design](../../97-adr/ADR-007-database-design.md)
|
|
|
|
---
|
|
|
|
*Ultima actualizacion: 2025-12-05*
|