2562 lines
80 KiB
Markdown
2562 lines
80 KiB
Markdown
# DDL-SPEC: Schema project_management
|
|
|
|
## Identificacion
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| **Schema** | project_management |
|
|
| **Modulo** | MAI-002 |
|
|
| **Version** | 1.0 |
|
|
| **Estado** | En Diseno |
|
|
| **Autor** | Requirements-Analyst |
|
|
| **Fecha** | 2025-12-06 |
|
|
|
|
---
|
|
|
|
## Descripcion General
|
|
|
|
El schema `project_management` contiene todas las tablas para la gestion completa de proyectos de construccion, incluyendo la jerarquia de 5 niveles (Proyecto → Etapa → Manzana → Lote → Vivienda), prototipos de vivienda con versionado, asignacion de equipo con validacion de workload, calendario de hitos y fechas criticas.
|
|
|
|
### Alcance
|
|
|
|
- Catalogo de proyectos con 4 tipos y 5 estados del ciclo de vida
|
|
- Estructura jerarquica flexible (fraccionamiento, conjunto, torre, mixto)
|
|
- Prototipos de vivienda con versionado automatico
|
|
- Asignacion de equipo con validacion de limites por rol
|
|
- Calendario de hitos con dependencias y alertas automaticas
|
|
- Fases constructivas y avances fisicos ponderados
|
|
- Documentos y permisos legales por proyecto
|
|
|
|
### RF Cubiertos
|
|
|
|
| RF | Titulo | Tablas |
|
|
|----|--------|--------|
|
|
| RF-PROJ-001 | Catalogo de Proyectos | projects |
|
|
| RF-PROJ-002 | Estructura Jerarquica de Obra | stages, blocks, lots, housing_units |
|
|
| RF-PROJ-003 | Prototipos de Vivienda | housing_prototypes, prototype_versions |
|
|
| RF-PROJ-004 | Asignacion de Equipo y Calendario | project_team_assignments, project_milestones, critical_dates, construction_phases |
|
|
|
|
---
|
|
|
|
## Diagrama Entidad-Relacion
|
|
|
|
```mermaid
|
|
erDiagram
|
|
%% Jerarquia Principal
|
|
projects ||--o{ stages : "tiene"
|
|
projects ||--o{ housing_prototypes : "define"
|
|
projects ||--o{ project_team_assignments : "asigna"
|
|
projects ||--o{ project_milestones : "planifica"
|
|
projects ||--o{ critical_dates : "registra"
|
|
projects ||--o{ project_documents : "almacena"
|
|
|
|
stages ||--o{ blocks : "contiene"
|
|
stages ||--o{ lots : "contiene_directos"
|
|
|
|
blocks ||--o{ lots : "agrupa"
|
|
|
|
lots ||--o{ housing_units : "construye"
|
|
|
|
%% Prototipos
|
|
housing_prototypes ||--o{ prototype_versions : "versiones"
|
|
housing_prototypes ||--o{ housing_units : "asignado_a"
|
|
|
|
%% Fases Constructivas
|
|
housing_units ||--o{ construction_phases : "progreso"
|
|
|
|
%% Relaciones con Core
|
|
projects }o--|| tenants : "pertenece"
|
|
projects }o--|| contacts : "cliente"
|
|
housing_units }o--|| contacts : "derechohabiente"
|
|
|
|
projects {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
varchar project_code UK
|
|
varchar name
|
|
enum project_type
|
|
enum project_status
|
|
enum client_type
|
|
varchar client_name
|
|
decimal contract_amount
|
|
text address
|
|
date contract_start_date
|
|
date scheduled_end_date
|
|
int total_housing_units
|
|
decimal physical_progress
|
|
boolean is_active
|
|
}
|
|
|
|
stages {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid project_id FK
|
|
varchar code
|
|
varchar name
|
|
enum stage_status
|
|
int stage_number
|
|
date planned_start_date
|
|
date planned_end_date
|
|
decimal progress
|
|
boolean is_active
|
|
}
|
|
|
|
blocks {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid stage_id FK
|
|
varchar code
|
|
varchar name
|
|
varchar block_type
|
|
int total_lots
|
|
decimal total_area
|
|
boolean is_active
|
|
}
|
|
|
|
lots {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid project_id FK
|
|
uuid stage_id FK
|
|
uuid block_id FK
|
|
varchar lot_number
|
|
enum lot_status
|
|
decimal lot_area
|
|
decimal front_meters
|
|
decimal depth_meters
|
|
decimal cadastral_value
|
|
decimal sale_price
|
|
uuid owner_id FK
|
|
date sale_date
|
|
boolean is_active
|
|
}
|
|
|
|
housing_units {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid project_id FK
|
|
uuid lot_id FK
|
|
uuid prototype_id FK
|
|
varchar unit_code
|
|
enum unit_type
|
|
enum unit_status
|
|
decimal construction_area
|
|
decimal total_cost
|
|
decimal physical_progress
|
|
date construction_start_date
|
|
date estimated_delivery_date
|
|
jsonb prototype_snapshot
|
|
boolean is_active
|
|
}
|
|
|
|
housing_prototypes {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid project_id FK
|
|
varchar code UK
|
|
varchar name
|
|
enum category
|
|
enum segment
|
|
int current_version
|
|
decimal construction_area
|
|
decimal land_area
|
|
int bedrooms
|
|
int bathrooms
|
|
decimal estimated_cost
|
|
boolean is_active
|
|
}
|
|
|
|
prototype_versions {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid prototype_id FK
|
|
int version_number
|
|
varchar change_description
|
|
jsonb specifications
|
|
decimal estimated_cost
|
|
timestamptz created_at
|
|
uuid created_by FK
|
|
}
|
|
|
|
project_team_assignments {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid project_id FK
|
|
uuid user_id FK
|
|
enum role_type
|
|
boolean is_primary
|
|
int workload_percentage
|
|
date assignment_start_date
|
|
date assignment_end_date
|
|
boolean is_active
|
|
}
|
|
|
|
project_milestones {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid project_id FK
|
|
enum milestone_type
|
|
varchar name
|
|
date planned_date
|
|
date actual_date
|
|
enum status
|
|
jsonb dependencies
|
|
boolean is_active
|
|
}
|
|
|
|
critical_dates {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid project_id FK
|
|
enum date_type
|
|
varchar name
|
|
date date_value
|
|
int alert_days_before
|
|
boolean alert_sent
|
|
boolean is_active
|
|
}
|
|
|
|
construction_phases {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid housing_unit_id FK
|
|
enum phase_name
|
|
decimal weight_percentage
|
|
decimal progress_percentage
|
|
date start_date
|
|
date end_date
|
|
enum status
|
|
boolean is_active
|
|
}
|
|
|
|
project_documents {
|
|
uuid id PK
|
|
uuid tenant_id FK
|
|
uuid project_id FK
|
|
enum document_type
|
|
varchar name
|
|
varchar file_path
|
|
date issue_date
|
|
date expiration_date
|
|
boolean is_active
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ENUMs
|
|
|
|
### 1. project_type
|
|
|
|
Tipos de proyectos de construccion.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.project_type AS ENUM (
|
|
'fraccionamiento_horizontal',
|
|
'conjunto_habitacional',
|
|
'edificio_vertical',
|
|
'mixto'
|
|
);
|
|
```
|
|
|
|
**Descripcion:**
|
|
- `fraccionamiento_horizontal`: Proyecto con etapas, manzanas y lotes
|
|
- `conjunto_habitacional`: Proyecto sin manzanas (lotes directos)
|
|
- `edificio_vertical`: Torres con niveles y departamentos
|
|
- `mixto`: Combinacion de estructuras anteriores
|
|
|
|
---
|
|
|
|
### 2. project_status
|
|
|
|
Estados del ciclo de vida del proyecto.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.project_status AS ENUM (
|
|
'licitacion',
|
|
'adjudicado',
|
|
'ejecucion',
|
|
'entregado',
|
|
'cerrado'
|
|
);
|
|
```
|
|
|
|
**Descripcion:**
|
|
- `licitacion`: Proyecto en proceso de licitacion
|
|
- `adjudicado`: Proyecto ganado pero no iniciado
|
|
- `ejecucion`: Proyecto en construccion activa
|
|
- `entregado`: Proyecto terminado y entregado al cliente
|
|
- `cerrado`: Proyecto cerrado administrativamente
|
|
|
|
---
|
|
|
|
### 3. client_type
|
|
|
|
Tipo de cliente del proyecto.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.client_type AS ENUM (
|
|
'publico',
|
|
'privado',
|
|
'mixto'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 4. contract_type
|
|
|
|
Tipo de contrato del proyecto.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.contract_type AS ENUM (
|
|
'llave_en_mano',
|
|
'precio_alzado',
|
|
'administracion',
|
|
'mixto'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 5. stage_status
|
|
|
|
Estado de la etapa del proyecto.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.stage_status AS ENUM (
|
|
'pending',
|
|
'in_progress',
|
|
'completed',
|
|
'cancelled'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 6. lot_status
|
|
|
|
Estado del lote/terreno.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.lot_status AS ENUM (
|
|
'available',
|
|
'reserved',
|
|
'sold',
|
|
'in_construction',
|
|
'completed',
|
|
'delivered',
|
|
'cancelled'
|
|
);
|
|
```
|
|
|
|
**Descripcion:**
|
|
- `available`: Lote disponible para venta
|
|
- `reserved`: Lote reservado temporalmente
|
|
- `sold`: Lote vendido
|
|
- `in_construction`: Vivienda en construccion
|
|
- `completed`: Vivienda terminada
|
|
- `delivered`: Vivienda entregada al propietario
|
|
- `cancelled`: Lote cancelado
|
|
|
|
---
|
|
|
|
### 7. unit_type
|
|
|
|
Tipo de unidad habitacional.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.unit_type AS ENUM (
|
|
'casa_unifamiliar',
|
|
'departamento',
|
|
'duplex',
|
|
'triplex',
|
|
'local_comercial',
|
|
'bodega'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 8. unit_status
|
|
|
|
Estado de la unidad habitacional.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.unit_status AS ENUM (
|
|
'planned',
|
|
'in_construction',
|
|
'completed',
|
|
'delivered',
|
|
'cancelled'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 9. prototype_category
|
|
|
|
Categoria del prototipo de vivienda.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.prototype_category AS ENUM (
|
|
'casa_unifamiliar',
|
|
'departamento',
|
|
'duplex',
|
|
'triplex',
|
|
'local_comercial'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 10. prototype_segment
|
|
|
|
Segmento de mercado del prototipo.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.prototype_segment AS ENUM (
|
|
'interes_social',
|
|
'interes_medio',
|
|
'residencial_medio',
|
|
'residencial_alto',
|
|
'premium'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 11. team_role_type
|
|
|
|
Roles del equipo de proyecto.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.team_role_type AS ENUM (
|
|
'director',
|
|
'residente',
|
|
'ingeniero_civil',
|
|
'ingeniero_electrico',
|
|
'ingeniero_hidraulico',
|
|
'supervisor',
|
|
'gerente_compras',
|
|
'coordinador_calidad',
|
|
'coordinador_seguridad'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 12. milestone_type
|
|
|
|
Tipos de hitos del proyecto.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.milestone_type AS ENUM (
|
|
'arranque',
|
|
'preliminares',
|
|
'cimentacion',
|
|
'estructura',
|
|
'albanileria',
|
|
'instalaciones',
|
|
'acabados',
|
|
'exteriores',
|
|
'urbanizacion',
|
|
'entrega_parcial',
|
|
'cierre_administrativo'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 13. critical_date_type
|
|
|
|
Tipos de fechas criticas.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.critical_date_type AS ENUM (
|
|
'contractual',
|
|
'regulatory',
|
|
'financial',
|
|
'milestone',
|
|
'other'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 14. construction_phase_name
|
|
|
|
Fases constructivas para avance fisico.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.construction_phase_name AS ENUM (
|
|
'preliminares',
|
|
'cimentacion',
|
|
'estructura',
|
|
'muros_albanileria',
|
|
'instalaciones_hidraulicas',
|
|
'instalaciones_electricas',
|
|
'instalaciones_sanitarias',
|
|
'acabados_interiores',
|
|
'acabados_exteriores'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 15. construction_phase_status
|
|
|
|
Estado de la fase constructiva.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.construction_phase_status AS ENUM (
|
|
'not_started',
|
|
'in_progress',
|
|
'completed',
|
|
'on_hold'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### 16. document_type
|
|
|
|
Tipos de documentos del proyecto.
|
|
|
|
```sql
|
|
CREATE TYPE project_management.document_type AS ENUM (
|
|
'construction_license',
|
|
'environmental_impact',
|
|
'land_use_permit',
|
|
'approved_plan',
|
|
'infonavit_certification',
|
|
'fovissste_certification',
|
|
'contract',
|
|
'other'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Tablas Principales
|
|
|
|
### 1. projects
|
|
|
|
Catalogo principal de proyectos de construccion.
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: projects
|
|
-- Descripcion: Catalogo principal de proyectos de construccion
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.projects (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Codigo auto-generado
|
|
project_code VARCHAR(20) NOT NULL UNIQUE,
|
|
|
|
-- Informacion basica
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
project_type project_management.project_type NOT NULL,
|
|
status project_management.project_status NOT NULL DEFAULT 'licitacion',
|
|
|
|
-- Cliente
|
|
client_type project_management.client_type NOT NULL,
|
|
client_id UUID REFERENCES core_catalogs.contacts(id),
|
|
client_name VARCHAR(200) NOT NULL,
|
|
client_rfc VARCHAR(13),
|
|
client_contact_name VARCHAR(100),
|
|
client_contact_email VARCHAR(100),
|
|
client_contact_phone VARCHAR(20),
|
|
|
|
-- Contrato
|
|
contract_type project_management.contract_type NOT NULL,
|
|
contract_number VARCHAR(50),
|
|
contract_amount DECIMAL(15,2) NOT NULL,
|
|
contract_currency_id UUID REFERENCES core_catalogs.currencies(id),
|
|
|
|
-- Ubicacion
|
|
address TEXT NOT NULL,
|
|
state VARCHAR(100) NOT NULL,
|
|
municipality VARCHAR(100) NOT NULL,
|
|
postal_code VARCHAR(5),
|
|
country_id UUID REFERENCES core_catalogs.countries(id),
|
|
state_id UUID REFERENCES core_catalogs.states(id),
|
|
latitude DECIMAL(10,6),
|
|
longitude DECIMAL(10,6),
|
|
total_area DECIMAL(12,2) NOT NULL, -- m²
|
|
buildable_area DECIMAL(12,2) NOT NULL, -- m²
|
|
|
|
-- Fechas
|
|
bidding_date DATE,
|
|
award_date DATE,
|
|
contract_start_date DATE NOT NULL,
|
|
actual_start_date DATE,
|
|
contract_duration INTEGER NOT NULL, -- meses
|
|
scheduled_end_date DATE NOT NULL,
|
|
actual_end_date DATE,
|
|
delivery_date DATE,
|
|
closure_date DATE,
|
|
|
|
-- Informacion legal
|
|
construction_license_number VARCHAR(50),
|
|
license_issue_date DATE,
|
|
license_expiration_date DATE,
|
|
environmental_impact_number VARCHAR(50),
|
|
land_use_approved VARCHAR(20),
|
|
approved_plan_number VARCHAR(50),
|
|
infonavit_number VARCHAR(50),
|
|
fovissste_number VARCHAR(50),
|
|
|
|
-- Metricas calculadas (triggers)
|
|
total_housing_units INTEGER NOT NULL DEFAULT 0,
|
|
delivered_housing_units INTEGER NOT NULL DEFAULT 0,
|
|
physical_progress DECIMAL(5,2) NOT NULL DEFAULT 0, -- %
|
|
financial_progress DECIMAL(5,2) NOT NULL DEFAULT 0, -- %
|
|
|
|
-- Configuracion
|
|
settings JSONB DEFAULT '{}',
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT chk_projects_total_area CHECK (total_area > 0),
|
|
CONSTRAINT chk_projects_buildable_area CHECK (buildable_area > 0 AND buildable_area <= total_area),
|
|
CONSTRAINT chk_projects_contract_amount CHECK (contract_amount > 0),
|
|
CONSTRAINT chk_projects_duration CHECK (contract_duration > 0),
|
|
CONSTRAINT chk_projects_progress CHECK (
|
|
physical_progress >= 0 AND physical_progress <= 100 AND
|
|
financial_progress >= 0 AND financial_progress <= 100
|
|
),
|
|
CONSTRAINT chk_projects_dates CHECK (scheduled_end_date > contract_start_date),
|
|
CONSTRAINT chk_projects_actual_dates CHECK (
|
|
actual_end_date IS NULL OR actual_end_date >= actual_start_date
|
|
)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.projects IS 'Catalogo principal de proyectos de construccion';
|
|
COMMENT ON COLUMN project_management.projects.tenant_id IS 'ID de la constructora (multi-tenancy)';
|
|
COMMENT ON COLUMN project_management.projects.project_code IS 'Codigo auto-generado: PROJ-2025-001';
|
|
COMMENT ON COLUMN project_management.projects.project_type IS 'Tipo: fraccionamiento_horizontal, conjunto_habitacional, edificio_vertical, mixto';
|
|
COMMENT ON COLUMN project_management.projects.status IS 'Estado: licitacion, adjudicado, ejecucion, entregado, cerrado';
|
|
COMMENT ON COLUMN project_management.projects.total_housing_units IS 'Total de viviendas (calculado por trigger)';
|
|
COMMENT ON COLUMN project_management.projects.physical_progress IS 'Avance fisico en porcentaje (calculado por trigger)';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: projects
|
|
-- ============================================================================
|
|
|
|
-- Indice obligatorio para RLS
|
|
CREATE INDEX idx_projects_tenant_id ON project_management.projects(tenant_id);
|
|
|
|
-- Indice para codigo unico
|
|
CREATE UNIQUE INDEX idx_projects_code ON project_management.projects(project_code);
|
|
|
|
-- Indices para busqueda y filtros
|
|
CREATE INDEX idx_projects_status ON project_management.projects(status) WHERE is_active = true;
|
|
CREATE INDEX idx_projects_type ON project_management.projects(project_type) WHERE is_active = true;
|
|
CREATE INDEX idx_projects_client_id ON project_management.projects(client_id);
|
|
CREATE INDEX idx_projects_created_at ON project_management.projects(created_at DESC);
|
|
|
|
-- Indice para busqueda full-text
|
|
CREATE INDEX idx_projects_search ON project_management.projects
|
|
USING gin(to_tsvector('spanish',
|
|
name || ' ' ||
|
|
COALESCE(project_code, '') || ' ' ||
|
|
COALESCE(client_name, '') || ' ' ||
|
|
COALESCE(description, '')
|
|
));
|
|
|
|
-- Indice para ubicacion geografica
|
|
CREATE INDEX idx_projects_location ON project_management.projects(state, municipality);
|
|
|
|
-- Indice compuesto para dashboard
|
|
CREATE INDEX idx_projects_tenant_status_active
|
|
ON project_management.projects(tenant_id, status, is_active);
|
|
```
|
|
|
|
---
|
|
|
|
### 2. stages
|
|
|
|
Etapas del proyecto (1er nivel de jerarquia).
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: stages
|
|
-- Descripcion: Etapas del proyecto (1er nivel de jerarquia)
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.stages (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relacion con proyecto
|
|
project_id UUID NOT NULL REFERENCES project_management.projects(id) ON DELETE CASCADE,
|
|
|
|
-- Informacion basica
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
status project_management.stage_status NOT NULL DEFAULT 'pending',
|
|
stage_number INTEGER NOT NULL,
|
|
|
|
-- Fechas planificadas
|
|
planned_start_date DATE NOT NULL,
|
|
planned_end_date DATE NOT NULL,
|
|
actual_start_date DATE,
|
|
actual_end_date DATE,
|
|
|
|
-- Metricas
|
|
total_lots INTEGER NOT NULL DEFAULT 0,
|
|
total_housing_units INTEGER NOT NULL DEFAULT 0,
|
|
progress DECIMAL(5,2) NOT NULL DEFAULT 0, -- %
|
|
|
|
-- Configuracion
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT uq_stages_project_code UNIQUE (project_id, code),
|
|
CONSTRAINT uq_stages_project_number UNIQUE (project_id, stage_number),
|
|
CONSTRAINT chk_stages_dates CHECK (planned_end_date > planned_start_date),
|
|
CONSTRAINT chk_stages_actual_dates CHECK (
|
|
actual_end_date IS NULL OR actual_end_date >= actual_start_date
|
|
),
|
|
CONSTRAINT chk_stages_progress CHECK (progress >= 0 AND progress <= 100),
|
|
CONSTRAINT chk_stages_stage_number CHECK (stage_number > 0)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.stages IS 'Etapas del proyecto (1er nivel de jerarquia)';
|
|
COMMENT ON COLUMN project_management.stages.stage_number IS 'Numero secuencial de la etapa dentro del proyecto';
|
|
COMMENT ON COLUMN project_management.stages.progress IS 'Avance fisico de la etapa en porcentaje';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: stages
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_stages_tenant_id ON project_management.stages(tenant_id);
|
|
CREATE INDEX idx_stages_project_id ON project_management.stages(project_id);
|
|
CREATE INDEX idx_stages_status ON project_management.stages(status) WHERE is_active = true;
|
|
CREATE INDEX idx_stages_project_number ON project_management.stages(project_id, stage_number);
|
|
```
|
|
|
|
---
|
|
|
|
### 3. blocks
|
|
|
|
Manzanas del proyecto (2do nivel de jerarquia, solo para fraccionamientos).
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: blocks
|
|
-- Descripcion: Manzanas (2do nivel jerarquia, solo fraccionamientos)
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.blocks (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relacion con etapa
|
|
stage_id UUID NOT NULL REFERENCES project_management.stages(id) ON DELETE CASCADE,
|
|
|
|
-- Informacion basica
|
|
code VARCHAR(20) NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
block_type VARCHAR(50), -- comercial, residencial, mixto
|
|
|
|
-- Metricas
|
|
total_lots INTEGER NOT NULL DEFAULT 0,
|
|
total_area DECIMAL(12,2), -- m²
|
|
|
|
-- Configuracion
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT uq_blocks_stage_code UNIQUE (stage_id, code),
|
|
CONSTRAINT chk_blocks_total_area CHECK (total_area IS NULL OR total_area > 0)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.blocks IS 'Manzanas del proyecto (solo para fraccionamientos horizontales)';
|
|
COMMENT ON COLUMN project_management.blocks.block_type IS 'Tipo de manzana: comercial, residencial, mixto';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: blocks
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_blocks_tenant_id ON project_management.blocks(tenant_id);
|
|
CREATE INDEX idx_blocks_stage_id ON project_management.blocks(stage_id);
|
|
CREATE INDEX idx_blocks_stage_code ON project_management.blocks(stage_id, code);
|
|
```
|
|
|
|
---
|
|
|
|
### 4. lots
|
|
|
|
Lotes/terrenos individuales (3er nivel de jerarquia).
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: lots
|
|
-- Descripcion: Lotes/terrenos individuales (3er nivel jerarquia)
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.lots (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relacion jerarquica (project_id siempre, block_id opcional)
|
|
project_id UUID NOT NULL REFERENCES project_management.projects(id) ON DELETE CASCADE,
|
|
stage_id UUID NOT NULL REFERENCES project_management.stages(id) ON DELETE CASCADE,
|
|
block_id UUID REFERENCES project_management.blocks(id) ON DELETE CASCADE,
|
|
|
|
-- Identificacion del lote
|
|
lot_number VARCHAR(20) NOT NULL,
|
|
cadastral_key VARCHAR(50),
|
|
|
|
-- Estado
|
|
status project_management.lot_status NOT NULL DEFAULT 'available',
|
|
|
|
-- Dimensiones
|
|
lot_area DECIMAL(12,2) NOT NULL, -- m²
|
|
front_meters DECIMAL(8,2),
|
|
depth_meters DECIMAL(8,2),
|
|
|
|
-- Valores
|
|
cadastral_value DECIMAL(15,2),
|
|
base_price DECIMAL(15,2),
|
|
sale_price DECIMAL(15,2),
|
|
|
|
-- Venta
|
|
owner_id UUID REFERENCES core_catalogs.contacts(id),
|
|
sale_date DATE,
|
|
sale_notes TEXT,
|
|
|
|
-- Configuracion
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT uq_lots_project_stage_number UNIQUE (project_id, stage_id, lot_number),
|
|
CONSTRAINT chk_lots_area CHECK (lot_area > 0),
|
|
CONSTRAINT chk_lots_dimensions CHECK (
|
|
(front_meters IS NULL AND depth_meters IS NULL) OR
|
|
(front_meters > 0 AND depth_meters > 0)
|
|
),
|
|
CONSTRAINT chk_lots_prices CHECK (
|
|
(cadastral_value IS NULL OR cadastral_value > 0) AND
|
|
(base_price IS NULL OR base_price > 0) AND
|
|
(sale_price IS NULL OR sale_price > 0)
|
|
),
|
|
CONSTRAINT chk_lots_sold_has_owner CHECK (
|
|
status != 'sold' OR owner_id IS NOT NULL
|
|
)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.lots IS 'Lotes/terrenos individuales donde se construiran viviendas';
|
|
COMMENT ON COLUMN project_management.lots.status IS 'Estado: available, reserved, sold, in_construction, completed, delivered';
|
|
COMMENT ON COLUMN project_management.lots.block_id IS 'FK a blocks (NULL para conjuntos sin manzanas)';
|
|
COMMENT ON COLUMN project_management.lots.owner_id IS 'FK a contacts (derechohabiente/comprador)';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: lots
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_lots_tenant_id ON project_management.lots(tenant_id);
|
|
CREATE INDEX idx_lots_project_id ON project_management.lots(project_id);
|
|
CREATE INDEX idx_lots_stage_id ON project_management.lots(stage_id);
|
|
CREATE INDEX idx_lots_block_id ON project_management.lots(block_id);
|
|
CREATE INDEX idx_lots_owner_id ON project_management.lots(owner_id);
|
|
CREATE INDEX idx_lots_status ON project_management.lots(status) WHERE is_active = true;
|
|
CREATE INDEX idx_lots_project_stage_number ON project_management.lots(project_id, stage_id, lot_number);
|
|
```
|
|
|
|
---
|
|
|
|
### 5. housing_units
|
|
|
|
Viviendas construidas en los lotes (4to nivel de jerarquia).
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: housing_units
|
|
-- Descripcion: Viviendas construidas en los lotes (4to nivel jerarquia)
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.housing_units (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relacion jerarquica
|
|
project_id UUID NOT NULL REFERENCES project_management.projects(id) ON DELETE CASCADE,
|
|
lot_id UUID NOT NULL REFERENCES project_management.lots(id) ON DELETE CASCADE,
|
|
prototype_id UUID REFERENCES project_management.housing_prototypes(id),
|
|
|
|
-- Identificacion
|
|
unit_code VARCHAR(50) NOT NULL,
|
|
unit_type project_management.unit_type NOT NULL,
|
|
status project_management.unit_status NOT NULL DEFAULT 'planned',
|
|
|
|
-- Caracteristicas (heredadas del prototipo al momento de asignacion)
|
|
construction_area DECIMAL(10,2) NOT NULL, -- m²
|
|
land_area DECIMAL(10,2),
|
|
bedrooms INTEGER,
|
|
bathrooms DECIMAL(3,1),
|
|
parking_spaces INTEGER,
|
|
levels INTEGER,
|
|
|
|
-- Costos
|
|
estimated_cost DECIMAL(15,2),
|
|
actual_cost DECIMAL(15,2),
|
|
total_cost DECIMAL(15,2),
|
|
|
|
-- Avance
|
|
physical_progress DECIMAL(5,2) NOT NULL DEFAULT 0, -- %
|
|
|
|
-- Fechas
|
|
construction_start_date DATE,
|
|
estimated_delivery_date DATE,
|
|
actual_delivery_date DATE,
|
|
|
|
-- Snapshot del prototipo (JSONB)
|
|
prototype_snapshot JSONB,
|
|
|
|
-- Configuracion
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT uq_housing_units_project_code UNIQUE (project_id, unit_code),
|
|
CONSTRAINT chk_housing_units_area CHECK (construction_area > 0),
|
|
CONSTRAINT chk_housing_units_rooms CHECK (
|
|
bedrooms IS NULL OR bedrooms > 0
|
|
),
|
|
CONSTRAINT chk_housing_units_bathrooms CHECK (
|
|
bathrooms IS NULL OR bathrooms > 0
|
|
),
|
|
CONSTRAINT chk_housing_units_costs CHECK (
|
|
(estimated_cost IS NULL OR estimated_cost > 0) AND
|
|
(actual_cost IS NULL OR actual_cost >= 0) AND
|
|
(total_cost IS NULL OR total_cost >= 0)
|
|
),
|
|
CONSTRAINT chk_housing_units_progress CHECK (
|
|
physical_progress >= 0 AND physical_progress <= 100
|
|
),
|
|
CONSTRAINT chk_housing_units_dates CHECK (
|
|
actual_delivery_date IS NULL OR
|
|
construction_start_date IS NULL OR
|
|
actual_delivery_date >= construction_start_date
|
|
)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.housing_units IS 'Viviendas construidas en los lotes';
|
|
COMMENT ON COLUMN project_management.housing_units.prototype_id IS 'FK al prototipo asignado (puede ser NULL si es custom)';
|
|
COMMENT ON COLUMN project_management.housing_units.prototype_snapshot IS 'Snapshot JSON del prototipo al momento de asignacion';
|
|
COMMENT ON COLUMN project_management.housing_units.physical_progress IS 'Avance fisico calculado desde construction_phases';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: housing_units
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_housing_units_tenant_id ON project_management.housing_units(tenant_id);
|
|
CREATE INDEX idx_housing_units_project_id ON project_management.housing_units(project_id);
|
|
CREATE INDEX idx_housing_units_lot_id ON project_management.housing_units(lot_id);
|
|
CREATE INDEX idx_housing_units_prototype_id ON project_management.housing_units(prototype_id);
|
|
CREATE INDEX idx_housing_units_status ON project_management.housing_units(status) WHERE is_active = true;
|
|
CREATE INDEX idx_housing_units_project_code ON project_management.housing_units(project_id, unit_code);
|
|
```
|
|
|
|
---
|
|
|
|
### 6. housing_prototypes
|
|
|
|
Catalogo de prototipos de vivienda.
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: housing_prototypes
|
|
-- Descripcion: Catalogo de prototipos de vivienda con versionado
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.housing_prototypes (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relacion con proyecto (opcional, puede ser catalogo general)
|
|
project_id UUID REFERENCES project_management.projects(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(50) NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Clasificacion
|
|
category project_management.prototype_category NOT NULL,
|
|
segment project_management.prototype_segment NOT NULL,
|
|
|
|
-- Versionado
|
|
current_version INTEGER NOT NULL DEFAULT 1,
|
|
|
|
-- Caracteristicas basicas
|
|
construction_area DECIMAL(10,2) NOT NULL, -- m²
|
|
land_area DECIMAL(10,2),
|
|
sellable_area DECIMAL(10,2),
|
|
bedrooms INTEGER NOT NULL,
|
|
bathrooms DECIMAL(3,1) NOT NULL,
|
|
half_bathrooms INTEGER DEFAULT 0,
|
|
parking_spaces INTEGER DEFAULT 0,
|
|
levels INTEGER DEFAULT 1,
|
|
|
|
-- Distribucion
|
|
has_kitchen BOOLEAN DEFAULT true,
|
|
has_dining_room BOOLEAN DEFAULT true,
|
|
has_living_room BOOLEAN DEFAULT true,
|
|
has_laundry_area BOOLEAN DEFAULT false,
|
|
has_patio BOOLEAN DEFAULT false,
|
|
has_garden BOOLEAN DEFAULT false,
|
|
has_balcony BOOLEAN DEFAULT false,
|
|
has_roof_garden BOOLEAN DEFAULT false,
|
|
|
|
-- Acabados
|
|
floor_type VARCHAR(50),
|
|
wall_finish VARCHAR(50),
|
|
kitchen_finish VARCHAR(50),
|
|
bathroom_finish VARCHAR(50),
|
|
|
|
-- Costos estimados
|
|
estimated_cost DECIMAL(15,2),
|
|
cost_per_sqm DECIMAL(10,2),
|
|
|
|
-- Archivos
|
|
floor_plan_url TEXT,
|
|
facade_image_url TEXT,
|
|
render_images JSONB DEFAULT '[]',
|
|
|
|
-- Especificaciones tecnicas (JSONB)
|
|
specifications JSONB DEFAULT '{}',
|
|
|
|
-- Configuracion
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT uq_prototypes_tenant_code UNIQUE (tenant_id, code),
|
|
CONSTRAINT chk_prototypes_areas CHECK (
|
|
construction_area > 0 AND
|
|
(land_area IS NULL OR land_area > 0) AND
|
|
(sellable_area IS NULL OR sellable_area > 0)
|
|
),
|
|
CONSTRAINT chk_prototypes_rooms CHECK (
|
|
bedrooms > 0 AND
|
|
bathrooms > 0 AND
|
|
half_bathrooms >= 0 AND
|
|
parking_spaces >= 0 AND
|
|
levels > 0
|
|
),
|
|
CONSTRAINT chk_prototypes_costs CHECK (
|
|
(estimated_cost IS NULL OR estimated_cost > 0) AND
|
|
(cost_per_sqm IS NULL OR cost_per_sqm > 0)
|
|
),
|
|
CONSTRAINT chk_prototypes_version CHECK (current_version > 0)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.housing_prototypes IS 'Catalogo de prototipos de vivienda con versionado automatico';
|
|
COMMENT ON COLUMN project_management.housing_prototypes.project_id IS 'FK a proyecto (NULL = prototipo general del catalogo)';
|
|
COMMENT ON COLUMN project_management.housing_prototypes.current_version IS 'Version actual del prototipo';
|
|
COMMENT ON COLUMN project_management.housing_prototypes.specifications IS 'Especificaciones tecnicas en formato JSON';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: housing_prototypes
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_prototypes_tenant_id ON project_management.housing_prototypes(tenant_id);
|
|
CREATE INDEX idx_prototypes_project_id ON project_management.housing_prototypes(project_id);
|
|
CREATE INDEX idx_prototypes_category ON project_management.housing_prototypes(category) WHERE is_active = true;
|
|
CREATE INDEX idx_prototypes_segment ON project_management.housing_prototypes(segment) WHERE is_active = true;
|
|
CREATE INDEX idx_prototypes_tenant_code ON project_management.housing_prototypes(tenant_id, code);
|
|
|
|
-- Indice para busqueda full-text
|
|
CREATE INDEX idx_prototypes_search ON project_management.housing_prototypes
|
|
USING gin(to_tsvector('spanish',
|
|
name || ' ' ||
|
|
COALESCE(code, '') || ' ' ||
|
|
COALESCE(description, '')
|
|
));
|
|
```
|
|
|
|
---
|
|
|
|
### 7. prototype_versions
|
|
|
|
Historial de versiones de prototipos.
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: prototype_versions
|
|
-- Descripcion: Historial de versiones de prototipos
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.prototype_versions (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relacion con prototipo
|
|
prototype_id UUID NOT NULL REFERENCES project_management.housing_prototypes(id) ON DELETE CASCADE,
|
|
|
|
-- Version
|
|
version_number INTEGER NOT NULL,
|
|
change_description TEXT NOT NULL,
|
|
|
|
-- Snapshot completo de las especificaciones
|
|
specifications JSONB NOT NULL,
|
|
|
|
-- Costos en esta version
|
|
estimated_cost DECIMAL(15,2),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT uq_prototype_versions_prototype_version
|
|
UNIQUE (prototype_id, version_number),
|
|
CONSTRAINT chk_prototype_versions_number CHECK (version_number > 0)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.prototype_versions IS 'Historial de versiones de prototipos de vivienda';
|
|
COMMENT ON COLUMN project_management.prototype_versions.specifications IS 'Snapshot completo del prototipo en esta version';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: prototype_versions
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_prototype_versions_tenant_id ON project_management.prototype_versions(tenant_id);
|
|
CREATE INDEX idx_prototype_versions_prototype_id ON project_management.prototype_versions(prototype_id);
|
|
CREATE INDEX idx_prototype_versions_created_at ON project_management.prototype_versions(created_at DESC);
|
|
```
|
|
|
|
---
|
|
|
|
### 8. project_team_assignments
|
|
|
|
Asignacion de equipo de trabajo al proyecto.
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: project_team_assignments
|
|
-- Descripcion: Asignacion de equipo de trabajo al proyecto
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.project_team_assignments (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relaciones
|
|
project_id UUID NOT NULL REFERENCES project_management.projects(id) ON DELETE CASCADE,
|
|
user_id UUID NOT NULL REFERENCES core_users.users(id) ON DELETE CASCADE,
|
|
|
|
-- Rol en el proyecto
|
|
role_type project_management.team_role_type NOT NULL,
|
|
is_primary BOOLEAN NOT NULL DEFAULT false,
|
|
|
|
-- Carga de trabajo
|
|
workload_percentage INTEGER NOT NULL DEFAULT 100,
|
|
|
|
-- Vigencia
|
|
assignment_start_date DATE NOT NULL,
|
|
assignment_end_date DATE,
|
|
|
|
-- Notas
|
|
notes TEXT,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT uq_team_assignments_project_user_role
|
|
UNIQUE (project_id, user_id, role_type),
|
|
CONSTRAINT chk_team_assignments_workload
|
|
CHECK (workload_percentage > 0 AND workload_percentage <= 100),
|
|
CONSTRAINT chk_team_assignments_dates
|
|
CHECK (assignment_end_date IS NULL OR assignment_end_date > assignment_start_date)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.project_team_assignments IS 'Asignacion de equipo de trabajo a proyectos con validacion de workload';
|
|
COMMENT ON COLUMN project_management.project_team_assignments.is_primary IS 'Indica si es el responsable principal (ej: Director principal)';
|
|
COMMENT ON COLUMN project_management.project_team_assignments.workload_percentage IS 'Porcentaje de tiempo dedicado al proyecto (1-100)';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: project_team_assignments
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_team_assignments_tenant_id ON project_management.project_team_assignments(tenant_id);
|
|
CREATE INDEX idx_team_assignments_project_id ON project_management.project_team_assignments(project_id);
|
|
CREATE INDEX idx_team_assignments_user_id ON project_management.project_team_assignments(user_id);
|
|
CREATE INDEX idx_team_assignments_role ON project_management.project_team_assignments(role_type);
|
|
CREATE INDEX idx_team_assignments_active ON project_management.project_team_assignments(is_active, assignment_end_date);
|
|
```
|
|
|
|
---
|
|
|
|
### 9. project_milestones
|
|
|
|
Hitos del proyecto con dependencias.
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: project_milestones
|
|
-- Descripcion: Hitos del proyecto con dependencias y estado
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.project_milestones (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relacion con proyecto
|
|
project_id UUID NOT NULL REFERENCES project_management.projects(id) ON DELETE CASCADE,
|
|
|
|
-- Tipo de hito
|
|
milestone_type project_management.milestone_type NOT NULL,
|
|
|
|
-- Informacion
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Fechas
|
|
planned_date DATE NOT NULL,
|
|
actual_date DATE,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending, in_progress, completed, delayed
|
|
|
|
-- Dependencias (array de IDs)
|
|
dependencies JSONB DEFAULT '[]',
|
|
|
|
-- Responsable
|
|
responsible_user_id UUID REFERENCES core_users.users(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT chk_milestones_status
|
|
CHECK (status IN ('pending', 'in_progress', 'completed', 'delayed', 'cancelled'))
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.project_milestones IS 'Hitos del proyecto con dependencias y seguimiento';
|
|
COMMENT ON COLUMN project_management.project_milestones.dependencies IS 'Array JSON de IDs de hitos prerequisitos';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: project_milestones
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_milestones_tenant_id ON project_management.project_milestones(tenant_id);
|
|
CREATE INDEX idx_milestones_project_id ON project_management.project_milestones(project_id);
|
|
CREATE INDEX idx_milestones_type ON project_management.project_milestones(milestone_type);
|
|
CREATE INDEX idx_milestones_status ON project_management.project_milestones(status) WHERE is_active = true;
|
|
CREATE INDEX idx_milestones_planned_date ON project_management.project_milestones(planned_date);
|
|
CREATE INDEX idx_milestones_responsible ON project_management.project_milestones(responsible_user_id);
|
|
```
|
|
|
|
---
|
|
|
|
### 10. critical_dates
|
|
|
|
Fechas criticas con alertas automaticas.
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: critical_dates
|
|
-- Descripcion: Fechas criticas del proyecto con alertas automaticas
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.critical_dates (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relacion con proyecto
|
|
project_id UUID NOT NULL REFERENCES project_management.projects(id) ON DELETE CASCADE,
|
|
|
|
-- Tipo de fecha
|
|
date_type project_management.critical_date_type NOT NULL,
|
|
|
|
-- Informacion
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Fecha
|
|
date_value DATE NOT NULL,
|
|
|
|
-- Alertas
|
|
alert_days_before INTEGER NOT NULL DEFAULT 7,
|
|
alert_sent BOOLEAN NOT NULL DEFAULT false,
|
|
alert_sent_at TIMESTAMPTZ,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT chk_critical_dates_alert_days
|
|
CHECK (alert_days_before >= 0 AND alert_days_before <= 365)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.critical_dates IS 'Fechas criticas con sistema de alertas automaticas';
|
|
COMMENT ON COLUMN project_management.critical_dates.alert_days_before IS 'Dias antes de la fecha para enviar alerta';
|
|
COMMENT ON COLUMN project_management.critical_dates.alert_sent IS 'Indica si ya se envio la alerta';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: critical_dates
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_critical_dates_tenant_id ON project_management.critical_dates(tenant_id);
|
|
CREATE INDEX idx_critical_dates_project_id ON project_management.critical_dates(project_id);
|
|
CREATE INDEX idx_critical_dates_type ON project_management.critical_dates(date_type);
|
|
CREATE INDEX idx_critical_dates_date ON project_management.critical_dates(date_value);
|
|
CREATE INDEX idx_critical_dates_alerts ON project_management.critical_dates(alert_sent, date_value)
|
|
WHERE is_active = true;
|
|
```
|
|
|
|
---
|
|
|
|
### 11. construction_phases
|
|
|
|
Fases constructivas para avance fisico detallado.
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: construction_phases
|
|
-- Descripcion: Fases constructivas para calculo de avance fisico
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.construction_phases (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relacion con vivienda
|
|
housing_unit_id UUID NOT NULL REFERENCES project_management.housing_units(id) ON DELETE CASCADE,
|
|
|
|
-- Fase constructiva
|
|
phase_name project_management.construction_phase_name NOT NULL,
|
|
weight_percentage DECIMAL(5,2) NOT NULL, -- % de peso en avance total
|
|
progress_percentage DECIMAL(5,2) NOT NULL DEFAULT 0, -- % de avance de esta fase
|
|
|
|
-- Fechas
|
|
start_date DATE,
|
|
end_date DATE,
|
|
|
|
-- Estado
|
|
status project_management.construction_phase_status NOT NULL DEFAULT 'not_started',
|
|
|
|
-- Notas
|
|
notes TEXT,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT uq_construction_phases_unit_phase
|
|
UNIQUE (housing_unit_id, phase_name),
|
|
CONSTRAINT chk_construction_phases_weight
|
|
CHECK (weight_percentage >= 0 AND weight_percentage <= 100),
|
|
CONSTRAINT chk_construction_phases_progress
|
|
CHECK (progress_percentage >= 0 AND progress_percentage <= 100),
|
|
CONSTRAINT chk_construction_phases_dates
|
|
CHECK (end_date IS NULL OR start_date IS NULL OR end_date >= start_date)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.construction_phases IS 'Fases constructivas para calculo de avance fisico ponderado';
|
|
COMMENT ON COLUMN project_management.construction_phases.weight_percentage IS 'Peso de la fase en el avance total (suma debe ser 100%)';
|
|
COMMENT ON COLUMN project_management.construction_phases.progress_percentage IS 'Avance de esta fase especifica';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: construction_phases
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_construction_phases_tenant_id ON project_management.construction_phases(tenant_id);
|
|
CREATE INDEX idx_construction_phases_unit_id ON project_management.construction_phases(housing_unit_id);
|
|
CREATE INDEX idx_construction_phases_status ON project_management.construction_phases(status) WHERE is_active = true;
|
|
```
|
|
|
|
---
|
|
|
|
### 12. project_documents
|
|
|
|
Documentos y permisos del proyecto.
|
|
|
|
#### DDL Completo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Schema: project_management
|
|
-- Tabla: project_documents
|
|
-- Descripcion: Documentos legales y administrativos del proyecto
|
|
-- Modulo: MAI-002
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS project_management.project_documents (
|
|
-- Primary Key
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Relacion con proyecto
|
|
project_id UUID NOT NULL REFERENCES project_management.projects(id) ON DELETE CASCADE,
|
|
|
|
-- Tipo de documento
|
|
document_type project_management.document_type NOT NULL,
|
|
|
|
-- Informacion
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
document_number VARCHAR(50),
|
|
|
|
-- Archivo
|
|
file_path TEXT,
|
|
file_size BIGINT,
|
|
file_mime_type VARCHAR(100),
|
|
|
|
-- Fechas
|
|
issue_date DATE,
|
|
expiration_date DATE,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_by UUID REFERENCES core_users.users(id),
|
|
updated_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Soft delete
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
deleted_at TIMESTAMPTZ,
|
|
deleted_by UUID REFERENCES core_users.users(id),
|
|
|
|
-- Constraints
|
|
CONSTRAINT chk_project_documents_file_size
|
|
CHECK (file_size IS NULL OR file_size > 0),
|
|
CONSTRAINT chk_project_documents_dates
|
|
CHECK (expiration_date IS NULL OR issue_date IS NULL OR expiration_date >= issue_date)
|
|
);
|
|
|
|
-- Comentarios
|
|
COMMENT ON TABLE project_management.project_documents IS 'Documentos legales y administrativos del proyecto';
|
|
COMMENT ON COLUMN project_management.project_documents.document_type IS 'Tipo: construction_license, environmental_impact, contract, etc.';
|
|
```
|
|
|
|
#### Indices
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- INDICES: project_documents
|
|
-- ============================================================================
|
|
|
|
CREATE INDEX idx_project_documents_tenant_id ON project_management.project_documents(tenant_id);
|
|
CREATE INDEX idx_project_documents_project_id ON project_management.project_documents(project_id);
|
|
CREATE INDEX idx_project_documents_type ON project_management.project_documents(document_type);
|
|
CREATE INDEX idx_project_documents_expiration ON project_management.project_documents(expiration_date)
|
|
WHERE is_active = true AND expiration_date IS NOT NULL;
|
|
```
|
|
|
|
---
|
|
|
|
## Row Level Security (RLS)
|
|
|
|
### Habilitar RLS en Todas las Tablas
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- HABILITAR RLS
|
|
-- ============================================================================
|
|
|
|
ALTER TABLE project_management.projects ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.stages ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.blocks ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.lots ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.housing_units ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.housing_prototypes ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.prototype_versions ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.project_team_assignments ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.project_milestones ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.critical_dates ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.construction_phases ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE project_management.project_documents ENABLE ROW LEVEL SECURITY;
|
|
```
|
|
|
|
### Politicas RLS por Tabla
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- RLS POLICIES: projects
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_projects ON project_management.projects
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_projects ON project_management.projects
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_projects ON project_management.projects
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_projects ON project_management.projects
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: stages
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_stages ON project_management.stages
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_stages ON project_management.stages
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_stages ON project_management.stages
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_stages ON project_management.stages
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: blocks
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_blocks ON project_management.blocks
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_blocks ON project_management.blocks
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_blocks ON project_management.blocks
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_blocks ON project_management.blocks
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: lots
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_lots ON project_management.lots
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_lots ON project_management.lots
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_lots ON project_management.lots
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_lots ON project_management.lots
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: housing_units
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_housing_units ON project_management.housing_units
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_housing_units ON project_management.housing_units
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_housing_units ON project_management.housing_units
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_housing_units ON project_management.housing_units
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: housing_prototypes
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_prototypes ON project_management.housing_prototypes
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_prototypes ON project_management.housing_prototypes
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_prototypes ON project_management.housing_prototypes
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_prototypes ON project_management.housing_prototypes
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: prototype_versions
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_prototype_versions ON project_management.prototype_versions
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_prototype_versions ON project_management.prototype_versions
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_prototype_versions ON project_management.prototype_versions
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_prototype_versions ON project_management.prototype_versions
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: project_team_assignments
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_team_assignments ON project_management.project_team_assignments
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_team_assignments ON project_management.project_team_assignments
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_team_assignments ON project_management.project_team_assignments
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_team_assignments ON project_management.project_team_assignments
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: project_milestones
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_milestones ON project_management.project_milestones
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_milestones ON project_management.project_milestones
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_milestones ON project_management.project_milestones
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_milestones ON project_management.project_milestones
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: critical_dates
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_critical_dates ON project_management.critical_dates
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_critical_dates ON project_management.critical_dates
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_critical_dates ON project_management.critical_dates
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_critical_dates ON project_management.critical_dates
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: construction_phases
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_construction_phases ON project_management.construction_phases
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_construction_phases ON project_management.construction_phases
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_construction_phases ON project_management.construction_phases
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_construction_phases ON project_management.construction_phases
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- ============================================================================
|
|
-- RLS POLICIES: project_documents
|
|
-- ============================================================================
|
|
|
|
CREATE POLICY tenant_isolation_select_project_documents ON project_management.project_documents
|
|
FOR SELECT
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_insert_project_documents ON project_management.project_documents
|
|
FOR INSERT
|
|
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_update_project_documents ON project_management.project_documents
|
|
FOR UPDATE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_delete_project_documents ON project_management.project_documents
|
|
FOR DELETE
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
```
|
|
|
|
---
|
|
|
|
## Funciones SQL
|
|
|
|
### 1. Actualizar updated_at
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- FUNCION: set_updated_at
|
|
-- Descripcion: Actualiza automaticamente el timestamp updated_at
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.set_updated_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Generar Codigo de Proyecto
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- FUNCION: generate_project_code
|
|
-- Descripcion: Genera codigo auto-incremental por tenant y año (PROJ-2025-001)
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.generate_project_code(p_tenant_id UUID)
|
|
RETURNS VARCHAR AS $$
|
|
DECLARE
|
|
v_year INTEGER;
|
|
v_sequence INTEGER;
|
|
v_code VARCHAR(20);
|
|
BEGIN
|
|
v_year := EXTRACT(YEAR FROM CURRENT_DATE);
|
|
|
|
-- Obtener siguiente numero secuencial del año actual
|
|
SELECT COALESCE(MAX(
|
|
CAST(
|
|
SUBSTRING(project_code FROM '\d+$') AS INTEGER
|
|
)
|
|
), 0) + 1
|
|
INTO v_sequence
|
|
FROM project_management.projects
|
|
WHERE tenant_id = p_tenant_id
|
|
AND project_code LIKE 'PROJ-' || v_year || '-%';
|
|
|
|
-- Generar codigo: PROJ-2025-001
|
|
v_code := 'PROJ-' || v_year || '-' || LPAD(v_sequence::TEXT, 3, '0');
|
|
|
|
RETURN v_code;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
---
|
|
|
|
### 3. Calcular Avance Fisico de Proyecto
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- FUNCION: calculate_project_physical_progress
|
|
-- Descripcion: Calcula avance fisico del proyecto desde housing_units
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.calculate_project_physical_progress(p_project_id UUID)
|
|
RETURNS DECIMAL AS $$
|
|
DECLARE
|
|
v_progress DECIMAL(5,2);
|
|
BEGIN
|
|
SELECT COALESCE(AVG(physical_progress), 0)
|
|
INTO v_progress
|
|
FROM project_management.housing_units
|
|
WHERE project_id = p_project_id
|
|
AND is_active = true;
|
|
|
|
RETURN ROUND(v_progress, 2);
|
|
END;
|
|
$$ LANGUAGE plpgsql STABLE;
|
|
```
|
|
|
|
---
|
|
|
|
### 4. Calcular Avance Fisico de Vivienda
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- FUNCION: calculate_housing_unit_progress
|
|
-- Descripcion: Calcula avance ponderado desde construction_phases
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.calculate_housing_unit_progress(p_housing_unit_id UUID)
|
|
RETURNS DECIMAL AS $$
|
|
DECLARE
|
|
v_progress DECIMAL(5,2);
|
|
BEGIN
|
|
SELECT COALESCE(SUM(
|
|
(weight_percentage / 100.0) * (progress_percentage / 100.0)
|
|
) * 100, 0)
|
|
INTO v_progress
|
|
FROM project_management.construction_phases
|
|
WHERE housing_unit_id = p_housing_unit_id
|
|
AND is_active = true;
|
|
|
|
RETURN ROUND(v_progress, 2);
|
|
END;
|
|
$$ LANGUAGE plpgsql STABLE;
|
|
```
|
|
|
|
---
|
|
|
|
### 5. Validar Workload del Usuario
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- FUNCION: validate_user_workload
|
|
-- Descripcion: Valida que el usuario no exceda limite de workload por rol
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.validate_user_workload(
|
|
p_user_id UUID,
|
|
p_role_type project_management.team_role_type,
|
|
p_new_workload INTEGER,
|
|
p_exclude_assignment_id UUID DEFAULT NULL
|
|
)
|
|
RETURNS BOOLEAN AS $$
|
|
DECLARE
|
|
v_current_workload INTEGER;
|
|
v_max_workload INTEGER;
|
|
BEGIN
|
|
-- Limites por rol
|
|
v_max_workload := CASE p_role_type
|
|
WHEN 'director' THEN 500
|
|
WHEN 'residente' THEN 200
|
|
WHEN 'ingeniero_civil' THEN 800
|
|
WHEN 'ingeniero_electrico' THEN 800
|
|
WHEN 'ingeniero_hidraulico' THEN 800
|
|
WHEN 'supervisor' THEN 300
|
|
WHEN 'gerente_compras' THEN 400
|
|
WHEN 'coordinador_calidad' THEN 500
|
|
WHEN 'coordinador_seguridad' THEN 500
|
|
ELSE 100
|
|
END;
|
|
|
|
-- Calcular workload actual
|
|
SELECT COALESCE(SUM(workload_percentage), 0)
|
|
INTO v_current_workload
|
|
FROM project_management.project_team_assignments
|
|
WHERE user_id = p_user_id
|
|
AND role_type = p_role_type
|
|
AND is_active = true
|
|
AND (assignment_end_date IS NULL OR assignment_end_date >= CURRENT_DATE)
|
|
AND (p_exclude_assignment_id IS NULL OR id != p_exclude_assignment_id);
|
|
|
|
-- Validar que no exceda el limite
|
|
RETURN (v_current_workload + p_new_workload) <= v_max_workload;
|
|
END;
|
|
$$ LANGUAGE plpgsql STABLE;
|
|
```
|
|
|
|
---
|
|
|
|
### 6. Snapshot de Prototipo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- FUNCION: create_prototype_snapshot
|
|
-- Descripcion: Crea snapshot JSON del prototipo para housing_unit
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.create_prototype_snapshot(p_prototype_id UUID)
|
|
RETURNS JSONB AS $$
|
|
DECLARE
|
|
v_snapshot JSONB;
|
|
BEGIN
|
|
SELECT jsonb_build_object(
|
|
'prototype_id', id,
|
|
'code', code,
|
|
'name', name,
|
|
'category', category,
|
|
'segment', segment,
|
|
'version', current_version,
|
|
'construction_area', construction_area,
|
|
'land_area', land_area,
|
|
'bedrooms', bedrooms,
|
|
'bathrooms', bathrooms,
|
|
'specifications', specifications,
|
|
'estimated_cost', estimated_cost,
|
|
'snapshot_date', NOW()
|
|
)
|
|
INTO v_snapshot
|
|
FROM project_management.housing_prototypes
|
|
WHERE id = p_prototype_id;
|
|
|
|
RETURN v_snapshot;
|
|
END;
|
|
$$ LANGUAGE plpgsql STABLE;
|
|
```
|
|
|
|
---
|
|
|
|
## Triggers
|
|
|
|
### 1. Auto-generar Codigo de Proyecto
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- TRIGGER: Auto-generar project_code
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.trg_generate_project_code()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF NEW.project_code IS NULL OR NEW.project_code = '' THEN
|
|
NEW.project_code := project_management.generate_project_code(NEW.tenant_id);
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_projects_generate_code
|
|
BEFORE INSERT ON project_management.projects
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.trg_generate_project_code();
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Actualizar updated_at
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- TRIGGERS: Actualizar updated_at en todas las tablas
|
|
-- ============================================================================
|
|
|
|
CREATE TRIGGER trg_projects_update_timestamp
|
|
BEFORE UPDATE ON project_management.projects
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
|
|
CREATE TRIGGER trg_stages_update_timestamp
|
|
BEFORE UPDATE ON project_management.stages
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
|
|
CREATE TRIGGER trg_blocks_update_timestamp
|
|
BEFORE UPDATE ON project_management.blocks
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
|
|
CREATE TRIGGER trg_lots_update_timestamp
|
|
BEFORE UPDATE ON project_management.lots
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
|
|
CREATE TRIGGER trg_housing_units_update_timestamp
|
|
BEFORE UPDATE ON project_management.housing_units
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
|
|
CREATE TRIGGER trg_prototypes_update_timestamp
|
|
BEFORE UPDATE ON project_management.housing_prototypes
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
|
|
CREATE TRIGGER trg_team_assignments_update_timestamp
|
|
BEFORE UPDATE ON project_management.project_team_assignments
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
|
|
CREATE TRIGGER trg_milestones_update_timestamp
|
|
BEFORE UPDATE ON project_management.project_milestones
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
|
|
CREATE TRIGGER trg_critical_dates_update_timestamp
|
|
BEFORE UPDATE ON project_management.critical_dates
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
|
|
CREATE TRIGGER trg_construction_phases_update_timestamp
|
|
BEFORE UPDATE ON project_management.construction_phases
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
|
|
CREATE TRIGGER trg_project_documents_update_timestamp
|
|
BEFORE UPDATE ON project_management.project_documents
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.set_updated_at();
|
|
```
|
|
|
|
---
|
|
|
|
### 3. Validar Workload en Asignacion
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- TRIGGER: Validar workload antes de asignar
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.trg_validate_team_assignment_workload()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF NOT project_management.validate_user_workload(
|
|
NEW.user_id,
|
|
NEW.role_type,
|
|
NEW.workload_percentage,
|
|
NEW.id
|
|
) THEN
|
|
RAISE EXCEPTION 'User workload exceeds limit for role %', NEW.role_type;
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_team_assignments_validate_workload
|
|
BEFORE INSERT OR UPDATE ON project_management.project_team_assignments
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.trg_validate_team_assignment_workload();
|
|
```
|
|
|
|
---
|
|
|
|
### 4. Actualizar Metricas de Proyecto
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- TRIGGER: Actualizar metricas del proyecto
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.trg_update_project_metrics()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_project_id UUID;
|
|
BEGIN
|
|
-- Obtener project_id segun la tabla
|
|
IF TG_TABLE_NAME = 'housing_units' THEN
|
|
v_project_id := COALESCE(NEW.project_id, OLD.project_id);
|
|
ELSIF TG_TABLE_NAME = 'lots' THEN
|
|
v_project_id := COALESCE(NEW.project_id, OLD.project_id);
|
|
END IF;
|
|
|
|
-- Actualizar total_housing_units
|
|
UPDATE project_management.projects
|
|
SET total_housing_units = (
|
|
SELECT COUNT(*)
|
|
FROM project_management.housing_units
|
|
WHERE project_id = v_project_id
|
|
AND is_active = true
|
|
),
|
|
physical_progress = project_management.calculate_project_physical_progress(v_project_id)
|
|
WHERE id = v_project_id;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_housing_units_update_project_metrics
|
|
AFTER INSERT OR UPDATE OR DELETE ON project_management.housing_units
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.trg_update_project_metrics();
|
|
```
|
|
|
|
---
|
|
|
|
### 5. Actualizar Avance de Vivienda desde Fases
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- TRIGGER: Actualizar avance de vivienda desde construction_phases
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.trg_update_housing_unit_progress()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_housing_unit_id UUID;
|
|
BEGIN
|
|
v_housing_unit_id := COALESCE(NEW.housing_unit_id, OLD.housing_unit_id);
|
|
|
|
UPDATE project_management.housing_units
|
|
SET physical_progress = project_management.calculate_housing_unit_progress(v_housing_unit_id)
|
|
WHERE id = v_housing_unit_id;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_construction_phases_update_progress
|
|
AFTER INSERT OR UPDATE OR DELETE ON project_management.construction_phases
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.trg_update_housing_unit_progress();
|
|
```
|
|
|
|
---
|
|
|
|
### 6. Crear Version de Prototipo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- TRIGGER: Crear version automatica al actualizar prototipo
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.trg_create_prototype_version()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
-- Solo si cambio algo relevante
|
|
IF OLD.construction_area IS DISTINCT FROM NEW.construction_area OR
|
|
OLD.bedrooms IS DISTINCT FROM NEW.bedrooms OR
|
|
OLD.estimated_cost IS DISTINCT FROM NEW.estimated_cost OR
|
|
OLD.specifications IS DISTINCT FROM NEW.specifications THEN
|
|
|
|
-- Incrementar version
|
|
NEW.current_version := OLD.current_version + 1;
|
|
|
|
-- Crear registro de version
|
|
INSERT INTO project_management.prototype_versions (
|
|
tenant_id,
|
|
prototype_id,
|
|
version_number,
|
|
change_description,
|
|
specifications,
|
|
estimated_cost,
|
|
created_by
|
|
) VALUES (
|
|
NEW.tenant_id,
|
|
NEW.id,
|
|
NEW.current_version,
|
|
'Auto-generated version',
|
|
jsonb_build_object(
|
|
'construction_area', NEW.construction_area,
|
|
'bedrooms', NEW.bedrooms,
|
|
'bathrooms', NEW.bathrooms,
|
|
'specifications', NEW.specifications
|
|
),
|
|
NEW.estimated_cost,
|
|
NEW.updated_by
|
|
);
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_prototypes_create_version
|
|
BEFORE UPDATE ON project_management.housing_prototypes
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.trg_create_prototype_version();
|
|
```
|
|
|
|
---
|
|
|
|
### 7. Crear Snapshot al Asignar Prototipo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- TRIGGER: Crear snapshot del prototipo al asignar a housing_unit
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION project_management.trg_create_housing_unit_snapshot()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF NEW.prototype_id IS NOT NULL AND NEW.prototype_snapshot IS NULL THEN
|
|
NEW.prototype_snapshot := project_management.create_prototype_snapshot(NEW.prototype_id);
|
|
|
|
-- Heredar caracteristicas del prototipo
|
|
SELECT
|
|
construction_area,
|
|
land_area,
|
|
bedrooms,
|
|
bathrooms,
|
|
estimated_cost
|
|
INTO
|
|
NEW.construction_area,
|
|
NEW.land_area,
|
|
NEW.bedrooms,
|
|
NEW.bathrooms,
|
|
NEW.estimated_cost
|
|
FROM project_management.housing_prototypes
|
|
WHERE id = NEW.prototype_id;
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_housing_units_create_snapshot
|
|
BEFORE INSERT ON project_management.housing_units
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION project_management.trg_create_housing_unit_snapshot();
|
|
```
|
|
|
|
---
|
|
|
|
## Seed Data
|
|
|
|
### Prototipos de Ejemplo
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- SEED: Prototipos de ejemplo por tenant
|
|
-- ============================================================================
|
|
|
|
-- Nota: Estos seeds se ejecutan durante el onboarding del tenant
|
|
-- El tenant_id se sustituye en tiempo de ejecucion
|
|
|
|
INSERT INTO project_management.housing_prototypes (
|
|
tenant_id,
|
|
code,
|
|
name,
|
|
category,
|
|
segment,
|
|
construction_area,
|
|
land_area,
|
|
bedrooms,
|
|
bathrooms,
|
|
parking_spaces,
|
|
estimated_cost
|
|
) VALUES
|
|
(
|
|
'{{tenant_id}}',
|
|
'CASA-A-001',
|
|
'Casa Tipo A - Interes Social',
|
|
'casa_unifamiliar',
|
|
'interes_social',
|
|
50.00,
|
|
90.00,
|
|
2,
|
|
1.0,
|
|
1,
|
|
450000.00
|
|
),
|
|
(
|
|
'{{tenant_id}}',
|
|
'CASA-B-001',
|
|
'Casa Tipo B - Interes Medio',
|
|
'casa_unifamiliar',
|
|
'interes_medio',
|
|
75.00,
|
|
120.00,
|
|
3,
|
|
2.0,
|
|
2,
|
|
850000.00
|
|
),
|
|
(
|
|
'{{tenant_id}}',
|
|
'DEPTO-A-001',
|
|
'Departamento Tipo A',
|
|
'departamento',
|
|
'interes_medio',
|
|
60.00,
|
|
NULL,
|
|
2,
|
|
1.0,
|
|
1,
|
|
650000.00
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Consideraciones de Performance
|
|
|
|
| Tabla | Volumen Esperado | Estrategia |
|
|
|-------|------------------|------------|
|
|
| projects | Medio (~100-500/tenant) | Indices compuestos, cache |
|
|
| stages | Medio (~3-10 por proyecto) | Indices en project_id |
|
|
| blocks | Medio (~5-20 por etapa) | Indices en stage_id |
|
|
| lots | Alto (~100-5000 por proyecto) | Particionamiento futuro, bulk operations |
|
|
| housing_units | Alto (~100-5000 por proyecto) | Indices GIN, paginacion |
|
|
| housing_prototypes | Bajo (~10-50/tenant) | Cache agresivo |
|
|
| project_team_assignments | Medio (~5-15 por proyecto) | Indices compuestos |
|
|
| construction_phases | Alto (~9 por vivienda) | Indices en housing_unit_id |
|
|
|
|
### Optimizaciones Recomendadas
|
|
|
|
1. **Bulk Operations:** Crear lotes en lote (hasta 500) usando `INSERT ... SELECT`
|
|
2. **Materialized Views:** Para dashboards con metricas agregadas
|
|
3. **Particionamiento:** Considerar particionar `lots` y `housing_units` por proyecto en instalaciones grandes
|
|
4. **Cache:** Cachear prototipos, ENUMs y metadatos del proyecto
|
|
5. **Indices Parciales:** Usar `WHERE is_active = true` en indices de busqueda
|
|
|
|
---
|
|
|
|
## Historial de Cambios
|
|
|
|
| Version | Fecha | Autor | Cambios |
|
|
|---------|-------|-------|---------|
|
|
| 1.0 | 2025-12-06 | Requirements-Analyst | Creacion inicial |
|
|
|
|
---
|
|
|
|
## Aprobaciones
|
|
|
|
| Rol | Nombre | Fecha | Firma |
|
|
|-----|--------|-------|-------|
|
|
| DBA | - | - | [ ] |
|
|
| Tech Lead | - | - | [ ] |
|
|
| Architect | - | - | [ ] |
|