755 lines
22 KiB
Markdown
755 lines
22 KiB
Markdown
# DDL SPECIFICATION: Schema Compliance (INFONAVIT)
|
|
|
|
**Version:** 1.0.0
|
|
**Fecha:** 2025-12-05
|
|
**Schema:** `compliance`
|
|
**Modulos:** MAI-011 (INFONAVIT Cumplimiento)
|
|
|
|
---
|
|
|
|
## Resumen
|
|
|
|
| Metrica | Valor |
|
|
|---------|-------|
|
|
| Total Tablas | 10 |
|
|
| ENUMs | 6 |
|
|
| Funciones | 4 |
|
|
| Triggers | 3 |
|
|
| Indices | 25+ |
|
|
|
|
---
|
|
|
|
## 1. ENUMs
|
|
|
|
```sql
|
|
-- Tipos de programa INFONAVIT
|
|
CREATE TYPE compliance.program_type AS ENUM (
|
|
'evc', -- Esquemas de Valor Compartido
|
|
'traditional', -- Credito tradicional
|
|
'cofinavit', -- Credito mixto INFONAVIT + bancario
|
|
'infonavit_total', -- Credito con subsidio
|
|
'apoyo_infonavit', -- Solo subsidio
|
|
'mejoravit', -- Mejora de vivienda
|
|
'unamos_creditos' -- Creditos combinados
|
|
);
|
|
|
|
-- Categorias de requisito
|
|
CREATE TYPE compliance.requirement_category AS ENUM (
|
|
'technical', -- Requisitos tecnicos
|
|
'urban', -- Requisitos urbanos
|
|
'services', -- Requisitos de servicios
|
|
'legal', -- Requisitos legales
|
|
'environmental' -- Requisitos ambientales
|
|
);
|
|
|
|
-- Estados de requisito
|
|
CREATE TYPE compliance.requirement_status AS ENUM (
|
|
'pending', -- Pendiente
|
|
'in_progress', -- En proceso
|
|
'compliant', -- Cumple
|
|
'non_compliant', -- No cumple
|
|
'not_applicable' -- No aplica
|
|
);
|
|
|
|
-- Tipos de auditoria
|
|
CREATE TYPE compliance.audit_type AS ENUM (
|
|
'initial', -- Inicial
|
|
'progress', -- Avance
|
|
'final', -- Final
|
|
'special', -- Especial
|
|
'random' -- Aleatoria
|
|
);
|
|
|
|
-- Estados de auditoria
|
|
CREATE TYPE compliance.audit_status AS ENUM (
|
|
'scheduled', -- Programada
|
|
'in_progress', -- En curso
|
|
'completed', -- Completada
|
|
'follow_up', -- En seguimiento
|
|
'cancelled' -- Cancelada
|
|
);
|
|
|
|
-- Severidad de hallazgo
|
|
CREATE TYPE compliance.finding_severity AS ENUM (
|
|
'minor', -- Menor (30 dias para corregir)
|
|
'major', -- Mayor (15 dias para corregir)
|
|
'critical' -- Critico (7 dias para corregir)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Tablas
|
|
|
|
### 2.1 infonavit_programs (Programas INFONAVIT)
|
|
|
|
```sql
|
|
CREATE TABLE compliance.infonavit_programs (
|
|
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,
|
|
program_type compliance.program_type NOT NULL,
|
|
|
|
-- Vigencia
|
|
valid_from DATE,
|
|
valid_to DATE,
|
|
|
|
-- Estado
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
-- Metadata
|
|
requirements_count 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_programs_code UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
CREATE INDEX idx_programs_tenant ON compliance.infonavit_programs(tenant_id);
|
|
CREATE INDEX idx_programs_type ON compliance.infonavit_programs(program_type);
|
|
CREATE INDEX idx_programs_active ON compliance.infonavit_programs(tenant_id) WHERE is_active = true;
|
|
```
|
|
|
|
### 2.2 program_requirements (Requisitos por Programa)
|
|
|
|
```sql
|
|
CREATE TABLE compliance.program_requirements (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
program_id UUID NOT NULL REFERENCES compliance.infonavit_programs(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(30) NOT NULL,
|
|
name VARCHAR(300) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Clasificacion
|
|
category compliance.requirement_category NOT NULL,
|
|
subcategory VARCHAR(100),
|
|
|
|
-- Evidencia requerida
|
|
evidence_required TEXT[], -- Lista de tipos de evidencia
|
|
evidence_description TEXT,
|
|
|
|
-- Orden y jerarquia
|
|
sort_order INTEGER DEFAULT 0,
|
|
parent_id UUID REFERENCES compliance.program_requirements(id),
|
|
|
|
-- Estado
|
|
is_mandatory BOOLEAN DEFAULT true,
|
|
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),
|
|
|
|
CONSTRAINT uq_requirements_code UNIQUE (program_id, code)
|
|
);
|
|
|
|
CREATE INDEX idx_requirements_program ON compliance.program_requirements(program_id);
|
|
CREATE INDEX idx_requirements_category ON compliance.program_requirements(category);
|
|
CREATE INDEX idx_requirements_parent ON compliance.program_requirements(parent_id);
|
|
```
|
|
|
|
### 2.3 project_programs (Proyectos en Programas)
|
|
|
|
```sql
|
|
CREATE TABLE compliance.project_programs (
|
|
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),
|
|
program_id UUID NOT NULL REFERENCES compliance.infonavit_programs(id),
|
|
|
|
-- Registro
|
|
registration_number VARCHAR(50),
|
|
registration_date DATE,
|
|
|
|
-- Vigencia
|
|
valid_from DATE,
|
|
valid_to DATE,
|
|
|
|
-- Estado de cumplimiento
|
|
compliance_percentage DECIMAL(5,2) DEFAULT 0,
|
|
total_requirements INTEGER DEFAULT 0,
|
|
compliant_count INTEGER DEFAULT 0,
|
|
non_compliant_count INTEGER DEFAULT 0,
|
|
pending_count INTEGER DEFAULT 0,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
|
|
-- 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_project_program UNIQUE (project_id, program_id)
|
|
);
|
|
|
|
CREATE INDEX idx_project_programs_project ON compliance.project_programs(project_id);
|
|
CREATE INDEX idx_project_programs_program ON compliance.project_programs(program_id);
|
|
```
|
|
|
|
### 2.4 compliance_items (Items de Cumplimiento)
|
|
|
|
```sql
|
|
CREATE TABLE compliance.compliance_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
project_program_id UUID NOT NULL REFERENCES compliance.project_programs(id),
|
|
requirement_id UUID NOT NULL REFERENCES compliance.program_requirements(id),
|
|
|
|
-- Estado
|
|
status compliance.requirement_status NOT NULL DEFAULT 'pending',
|
|
previous_status compliance.requirement_status,
|
|
|
|
-- Verificacion
|
|
verified_by UUID REFERENCES core.users(id),
|
|
verified_at TIMESTAMP,
|
|
verification_notes TEXT,
|
|
|
|
-- Fechas limite
|
|
due_date DATE,
|
|
completed_date DATE,
|
|
|
|
-- Notas
|
|
notes TEXT,
|
|
is_not_applicable BOOLEAN DEFAULT false,
|
|
na_reason TEXT,
|
|
|
|
-- 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_compliance_item UNIQUE (project_program_id, requirement_id)
|
|
);
|
|
|
|
CREATE INDEX idx_compliance_items_project_program ON compliance.compliance_items(project_program_id);
|
|
CREATE INDEX idx_compliance_items_requirement ON compliance.compliance_items(requirement_id);
|
|
CREATE INDEX idx_compliance_items_status ON compliance.compliance_items(status);
|
|
CREATE INDEX idx_compliance_items_pending ON compliance.compliance_items(project_program_id)
|
|
WHERE status IN ('pending', 'in_progress');
|
|
```
|
|
|
|
### 2.5 compliance_evidence (Evidencias)
|
|
|
|
```sql
|
|
CREATE TABLE compliance.compliance_evidence (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
compliance_item_id UUID NOT NULL REFERENCES compliance.compliance_items(id),
|
|
|
|
-- Archivo
|
|
file_url TEXT NOT NULL,
|
|
file_name VARCHAR(255) NOT NULL,
|
|
file_size INTEGER,
|
|
mime_type VARCHAR(100),
|
|
thumbnail_url TEXT,
|
|
|
|
-- Tipo de evidencia
|
|
evidence_type VARCHAR(50), -- document, photo, video, certificate
|
|
description TEXT,
|
|
|
|
-- Metadata
|
|
taken_at TIMESTAMP,
|
|
location GEOGRAPHY(POINT, 4326),
|
|
metadata JSONB,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active', -- active, replaced, rejected
|
|
|
|
-- Revision
|
|
reviewed_by UUID REFERENCES core.users(id),
|
|
reviewed_at TIMESTAMP,
|
|
review_notes TEXT,
|
|
|
|
-- 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_evidence_compliance_item ON compliance.compliance_evidence(compliance_item_id);
|
|
CREATE INDEX idx_evidence_type ON compliance.compliance_evidence(evidence_type);
|
|
CREATE INDEX idx_evidence_status ON compliance.compliance_evidence(status);
|
|
```
|
|
|
|
### 2.6 audits (Auditorias)
|
|
|
|
```sql
|
|
CREATE TABLE compliance.audits (
|
|
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
|
|
audit_number VARCHAR(30) NOT NULL,
|
|
audit_type compliance.audit_type NOT NULL,
|
|
|
|
-- Programacion
|
|
scheduled_date DATE NOT NULL,
|
|
scheduled_time TIME,
|
|
actual_date DATE,
|
|
|
|
-- Auditor
|
|
auditor_name VARCHAR(200),
|
|
auditor_company VARCHAR(200),
|
|
auditor_contact TEXT,
|
|
|
|
-- Estado
|
|
status compliance.audit_status NOT NULL DEFAULT 'scheduled',
|
|
|
|
-- Resultados
|
|
overall_compliance_percentage DECIMAL(5,2),
|
|
findings_count INTEGER DEFAULT 0,
|
|
critical_findings INTEGER DEFAULT 0,
|
|
major_findings INTEGER DEFAULT 0,
|
|
minor_findings INTEGER DEFAULT 0,
|
|
|
|
-- Documentacion
|
|
report_url TEXT,
|
|
notes TEXT,
|
|
|
|
-- Cierre
|
|
closed_at TIMESTAMP,
|
|
closed_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_audits_number UNIQUE (tenant_id, audit_number)
|
|
);
|
|
|
|
CREATE INDEX idx_audits_project ON compliance.audits(project_id);
|
|
CREATE INDEX idx_audits_status ON compliance.audits(status);
|
|
CREATE INDEX idx_audits_date ON compliance.audits(scheduled_date);
|
|
CREATE INDEX idx_audits_type ON compliance.audits(audit_type);
|
|
```
|
|
|
|
### 2.7 audit_findings (Hallazgos de Auditoria)
|
|
|
|
```sql
|
|
CREATE TABLE compliance.audit_findings (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
audit_id UUID NOT NULL REFERENCES compliance.audits(id),
|
|
compliance_item_id UUID REFERENCES compliance.compliance_items(id),
|
|
|
|
-- Identificacion
|
|
finding_number VARCHAR(30) NOT NULL,
|
|
severity compliance.finding_severity NOT NULL,
|
|
|
|
-- Descripcion
|
|
title VARCHAR(300) NOT NULL,
|
|
description TEXT NOT NULL,
|
|
evidence_refs TEXT[], -- Referencias a evidencias
|
|
|
|
-- Requisito incumplido
|
|
requirement_code VARCHAR(30),
|
|
requirement_name VARCHAR(300),
|
|
|
|
-- Responsable
|
|
responsible_name VARCHAR(200),
|
|
responsible_area VARCHAR(100),
|
|
|
|
-- Fechas
|
|
due_date DATE NOT NULL,
|
|
resolution_date DATE,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'open', -- open, in_progress, resolved, verified, closed
|
|
|
|
-- 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_findings_number UNIQUE (audit_id, finding_number)
|
|
);
|
|
|
|
CREATE INDEX idx_findings_audit ON compliance.audit_findings(audit_id);
|
|
CREATE INDEX idx_findings_compliance_item ON compliance.audit_findings(compliance_item_id);
|
|
CREATE INDEX idx_findings_severity ON compliance.audit_findings(severity);
|
|
CREATE INDEX idx_findings_status ON compliance.audit_findings(status);
|
|
CREATE INDEX idx_findings_open ON compliance.audit_findings(audit_id) WHERE status NOT IN ('verified', 'closed');
|
|
```
|
|
|
|
### 2.8 corrective_actions (Acciones Correctivas)
|
|
|
|
```sql
|
|
CREATE TABLE compliance.corrective_actions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
finding_id UUID NOT NULL REFERENCES compliance.audit_findings(id),
|
|
|
|
-- Identificacion
|
|
action_number VARCHAR(30) NOT NULL,
|
|
|
|
-- Accion
|
|
description TEXT NOT NULL,
|
|
action_type VARCHAR(50), -- correction, prevention, improvement
|
|
|
|
-- Asignacion
|
|
assigned_to UUID REFERENCES core.users(id),
|
|
assigned_to_name VARCHAR(200),
|
|
assigned_at TIMESTAMP,
|
|
|
|
-- Fechas
|
|
due_date DATE NOT NULL,
|
|
completed_date DATE,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'pending', -- pending, in_progress, completed, verified
|
|
|
|
-- Verificacion
|
|
verified_by UUID REFERENCES core.users(id),
|
|
verified_at TIMESTAMP,
|
|
verification_notes TEXT,
|
|
|
|
-- 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_corrective_finding ON compliance.corrective_actions(finding_id);
|
|
CREATE INDEX idx_corrective_assigned ON compliance.corrective_actions(assigned_to);
|
|
CREATE INDEX idx_corrective_status ON compliance.corrective_actions(status);
|
|
CREATE INDEX idx_corrective_due ON compliance.corrective_actions(due_date) WHERE status NOT IN ('completed', 'verified');
|
|
```
|
|
|
|
### 2.9 compliance_alerts (Alertas de Cumplimiento)
|
|
|
|
```sql
|
|
CREATE TABLE compliance.compliance_alerts (
|
|
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),
|
|
|
|
-- Tipo de alerta
|
|
alert_type VARCHAR(50) NOT NULL, -- requirement_due, audit_scheduled, finding_due, low_compliance
|
|
severity VARCHAR(20) NOT NULL, -- info, warning, critical
|
|
|
|
-- Referencia
|
|
reference_type VARCHAR(50), -- compliance_item, audit, finding
|
|
reference_id UUID,
|
|
|
|
-- Contenido
|
|
title VARCHAR(300) NOT NULL,
|
|
message TEXT NOT NULL,
|
|
|
|
-- Destinatarios
|
|
recipients UUID[],
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'active', -- active, acknowledged, dismissed
|
|
acknowledged_by UUID REFERENCES core.users(id),
|
|
acknowledged_at TIMESTAMP,
|
|
|
|
-- Vigencia
|
|
expires_at TIMESTAMP,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_alerts_project ON compliance.compliance_alerts(project_id);
|
|
CREATE INDEX idx_alerts_type ON compliance.compliance_alerts(alert_type);
|
|
CREATE INDEX idx_alerts_status ON compliance.compliance_alerts(status);
|
|
CREATE INDEX idx_alerts_active ON compliance.compliance_alerts(project_id) WHERE status = 'active';
|
|
```
|
|
|
|
### 2.10 compliance_history (Historial de Cambios)
|
|
|
|
```sql
|
|
CREATE TABLE compliance.compliance_history (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Referencia
|
|
entity_type VARCHAR(50) NOT NULL, -- compliance_item, finding, corrective_action
|
|
entity_id UUID NOT NULL,
|
|
|
|
-- Cambio
|
|
action VARCHAR(50) NOT NULL, -- status_change, evidence_added, verified, etc
|
|
previous_value JSONB,
|
|
new_value JSONB,
|
|
change_reason TEXT,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_history_entity ON compliance.compliance_history(entity_type, entity_id);
|
|
CREATE INDEX idx_history_date ON compliance.compliance_history(created_at);
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Funciones
|
|
|
|
### 3.1 Calcular Porcentaje de Cumplimiento
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION compliance.calculate_compliance_percentage(p_project_program_id UUID)
|
|
RETURNS VOID AS $$
|
|
DECLARE
|
|
v_total INTEGER;
|
|
v_compliant INTEGER;
|
|
v_non_compliant INTEGER;
|
|
v_pending INTEGER;
|
|
v_percentage DECIMAL(5,2);
|
|
BEGIN
|
|
-- Contar por estado
|
|
SELECT
|
|
COUNT(*) FILTER (WHERE status != 'not_applicable'),
|
|
COUNT(*) FILTER (WHERE status = 'compliant'),
|
|
COUNT(*) FILTER (WHERE status = 'non_compliant'),
|
|
COUNT(*) FILTER (WHERE status IN ('pending', 'in_progress'))
|
|
INTO v_total, v_compliant, v_non_compliant, v_pending
|
|
FROM compliance.compliance_items
|
|
WHERE project_program_id = p_project_program_id;
|
|
|
|
-- Calcular porcentaje
|
|
IF v_total > 0 THEN
|
|
v_percentage := (v_compliant::DECIMAL / v_total) * 100;
|
|
ELSE
|
|
v_percentage := 0;
|
|
END IF;
|
|
|
|
-- Actualizar project_program
|
|
UPDATE compliance.project_programs
|
|
SET
|
|
total_requirements = v_total,
|
|
compliant_count = v_compliant,
|
|
non_compliant_count = v_non_compliant,
|
|
pending_count = v_pending,
|
|
compliance_percentage = v_percentage,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = p_project_program_id;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
### 3.2 Crear Items de Cumplimiento al Registrar Proyecto
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION compliance.create_compliance_items_for_project()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
-- Crear items para cada requisito del programa
|
|
INSERT INTO compliance.compliance_items (
|
|
tenant_id, project_program_id, requirement_id, status, created_by
|
|
)
|
|
SELECT
|
|
NEW.tenant_id,
|
|
NEW.id,
|
|
pr.id,
|
|
'pending',
|
|
NEW.created_by
|
|
FROM compliance.program_requirements pr
|
|
WHERE pr.program_id = NEW.program_id
|
|
AND pr.is_active = true;
|
|
|
|
-- Calcular porcentaje inicial
|
|
PERFORM compliance.calculate_compliance_percentage(NEW.id);
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_create_compliance_items
|
|
AFTER INSERT ON compliance.project_programs
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION compliance.create_compliance_items_for_project();
|
|
```
|
|
|
|
### 3.3 Actualizar Porcentaje al Cambiar Estado
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION compliance.on_compliance_item_change()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
-- Registrar historial
|
|
INSERT INTO compliance.compliance_history (
|
|
tenant_id, entity_type, entity_id, action,
|
|
previous_value, new_value, created_by
|
|
)
|
|
VALUES (
|
|
NEW.tenant_id,
|
|
'compliance_item',
|
|
NEW.id,
|
|
'status_change',
|
|
jsonb_build_object('status', OLD.status),
|
|
jsonb_build_object('status', NEW.status),
|
|
NEW.updated_by
|
|
);
|
|
|
|
-- Recalcular porcentaje
|
|
PERFORM compliance.calculate_compliance_percentage(NEW.project_program_id);
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_compliance_item_change
|
|
AFTER UPDATE OF status ON compliance.compliance_items
|
|
FOR EACH ROW
|
|
WHEN (OLD.status IS DISTINCT FROM NEW.status)
|
|
EXECUTE FUNCTION compliance.on_compliance_item_change();
|
|
```
|
|
|
|
### 3.4 Generar Alertas de Vencimiento
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION compliance.generate_due_date_alerts()
|
|
RETURNS INTEGER AS $$
|
|
DECLARE
|
|
v_count INTEGER := 0;
|
|
BEGIN
|
|
-- Alertas para requisitos proximos a vencer (7 dias)
|
|
INSERT INTO compliance.compliance_alerts (
|
|
tenant_id, project_id, alert_type, severity,
|
|
reference_type, reference_id, title, message, expires_at
|
|
)
|
|
SELECT
|
|
ci.tenant_id,
|
|
pp.project_id,
|
|
'requirement_due',
|
|
CASE
|
|
WHEN ci.due_date < CURRENT_DATE THEN 'critical'
|
|
WHEN ci.due_date <= CURRENT_DATE + INTERVAL '3 days' THEN 'warning'
|
|
ELSE 'info'
|
|
END,
|
|
'compliance_item',
|
|
ci.id,
|
|
'Requisito proximo a vencer: ' || pr.name,
|
|
format('El requisito %s vence el %s', pr.code, ci.due_date::TEXT),
|
|
ci.due_date + INTERVAL '1 day'
|
|
FROM compliance.compliance_items ci
|
|
JOIN compliance.project_programs pp ON pp.id = ci.project_program_id
|
|
JOIN compliance.program_requirements pr ON pr.id = ci.requirement_id
|
|
WHERE ci.status IN ('pending', 'in_progress')
|
|
AND ci.due_date IS NOT NULL
|
|
AND ci.due_date <= CURRENT_DATE + INTERVAL '7 days'
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM compliance.compliance_alerts ca
|
|
WHERE ca.reference_id = ci.id
|
|
AND ca.alert_type = 'requirement_due'
|
|
AND ca.status = 'active'
|
|
);
|
|
|
|
GET DIAGNOSTICS v_count = ROW_COUNT;
|
|
RETURN v_count;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Row Level Security
|
|
|
|
```sql
|
|
-- Habilitar RLS en todas las tablas
|
|
ALTER TABLE compliance.infonavit_programs ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE compliance.program_requirements ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE compliance.project_programs ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE compliance.compliance_items ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE compliance.compliance_evidence ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE compliance.audits ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE compliance.audit_findings ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE compliance.corrective_actions ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE compliance.compliance_alerts ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE compliance.compliance_history 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 = 'compliance'
|
|
LOOP
|
|
EXECUTE format('
|
|
CREATE POLICY tenant_isolation ON compliance.%I
|
|
USING (tenant_id = current_setting(''app.current_tenant_id'')::uuid)
|
|
', t);
|
|
END LOOP;
|
|
END $$;
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Seeds Iniciales
|
|
|
|
```sql
|
|
-- Programas INFONAVIT base
|
|
INSERT INTO compliance.infonavit_programs (tenant_id, code, name, program_type, is_active)
|
|
VALUES
|
|
('{{TENANT_ID}}', 'EVC', 'Esquemas de Valor Compartido', 'evc', true),
|
|
('{{TENANT_ID}}', 'TRAD', 'Credito Tradicional', 'traditional', true),
|
|
('{{TENANT_ID}}', 'COFI', 'Cofinavit', 'cofinavit', true),
|
|
('{{TENANT_ID}}', 'TOTAL', 'INFONAVIT Total', 'infonavit_total', true),
|
|
('{{TENANT_ID}}', 'APOYO', 'Apoyo INFONAVIT', 'apoyo_infonavit', true);
|
|
|
|
-- Requisitos tecnicos base (ejemplo para EVC)
|
|
INSERT INTO compliance.program_requirements (tenant_id, program_id, code, name, category, is_mandatory)
|
|
SELECT
|
|
'{{TENANT_ID}}',
|
|
p.id,
|
|
code,
|
|
name,
|
|
'technical'::compliance.requirement_category,
|
|
true
|
|
FROM compliance.infonavit_programs p
|
|
CROSS JOIN (VALUES
|
|
('TEC-001', 'Superficie construida minima 38 m2'),
|
|
('TEC-002', 'Altura minima de entrepiso 2.30 m'),
|
|
('TEC-003', 'Recamara principal minimo 7 m2'),
|
|
('TEC-004', 'Bano completo con regadera'),
|
|
('TEC-005', 'Cocina con espacio para estufa y refrigerador'),
|
|
('TEC-006', 'Sistema electrico con centro de carga'),
|
|
('TEC-007', 'Sistema hidraulico con tinaco'),
|
|
('TEC-008', 'Sistema sanitario conectado a drenaje'),
|
|
('TEC-009', 'Canceleria completa en ventanas'),
|
|
('TEC-010', 'Puertas de acceso con chapa de seguridad')
|
|
) AS reqs(code, name)
|
|
WHERE p.code = 'EVC';
|
|
```
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [MAI-011: INFONAVIT Cumplimiento](../../02-definicion-modulos/MAI-011-infonavit-cumplimiento/)
|
|
- [EPIC-MAI-011](../../08-epicas/EPIC-MAI-011-infonavit.md)
|
|
- [ADR-007: Database Design](../../97-adr/ADR-007-database-design.md)
|
|
|
|
---
|
|
|
|
*Ultima actualizacion: 2025-12-05*
|