843 lines
24 KiB
Markdown
843 lines
24 KiB
Markdown
# DDL SPECIFICATION: Schema Documents (DMS)
|
|
|
|
**Version:** 1.0.0
|
|
**Fecha:** 2025-12-05
|
|
**Schema:** `documents`
|
|
**Modulos:** MAE-016 (Gestion Documental y Planos)
|
|
|
|
---
|
|
|
|
## Resumen
|
|
|
|
| Metrica | Valor |
|
|
|---------|-------|
|
|
| Total Tablas | 10 |
|
|
| ENUMs | 5 |
|
|
| Funciones | 4 |
|
|
| Triggers | 3 |
|
|
| Indices | 30+ |
|
|
|
|
---
|
|
|
|
## 1. ENUMs
|
|
|
|
```sql
|
|
-- Tipos de documento
|
|
CREATE TYPE documents.document_type AS ENUM (
|
|
'plan', -- Plano (arquitectonico, estructural, etc.)
|
|
'specification', -- Especificacion tecnica
|
|
'contract', -- Contrato
|
|
'permit', -- Permiso/licencia
|
|
'invoice', -- Factura
|
|
'report', -- Reporte
|
|
'photo', -- Fotografia
|
|
'drawing', -- Dibujo/croquis
|
|
'manual', -- Manual
|
|
'certificate', -- Certificado
|
|
'other' -- Otro
|
|
);
|
|
|
|
-- Estados de documento
|
|
CREATE TYPE documents.document_status AS ENUM (
|
|
'draft', -- Borrador
|
|
'in_review', -- En revision
|
|
'approved', -- Aprobado
|
|
'rejected', -- Rechazado
|
|
'obsolete', -- Obsoleto
|
|
'archived' -- Archivado
|
|
);
|
|
|
|
-- Acciones de aprobacion
|
|
CREATE TYPE documents.approval_action AS ENUM (
|
|
'submit', -- Enviar a revision
|
|
'approve', -- Aprobar
|
|
'reject', -- Rechazar
|
|
'request_changes', -- Solicitar cambios
|
|
'withdraw' -- Retirar
|
|
);
|
|
|
|
-- Tipos de plano
|
|
CREATE TYPE documents.plan_type AS ENUM (
|
|
'architectural', -- Arquitectonico
|
|
'structural', -- Estructural
|
|
'electrical', -- Electrico
|
|
'plumbing', -- Hidraulico
|
|
'hvac', -- Aire acondicionado
|
|
'landscape', -- Paisajismo
|
|
'civil', -- Civil
|
|
'detail', -- Detalle
|
|
'as_built' -- As-built
|
|
);
|
|
|
|
-- Niveles de acceso
|
|
CREATE TYPE documents.access_level AS ENUM (
|
|
'public', -- Publico (todos los del proyecto)
|
|
'restricted', -- Restringido (roles especificos)
|
|
'confidential', -- Confidencial (solo propietario y admins)
|
|
'private' -- Privado (solo propietario)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Tablas de Carpetas y Documentos
|
|
|
|
### 2.1 folders (Carpetas)
|
|
|
|
```sql
|
|
CREATE TABLE documents.folders (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Identificacion
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Jerarquia
|
|
parent_id UUID REFERENCES documents.folders(id),
|
|
full_path TEXT,
|
|
level INTEGER DEFAULT 1,
|
|
|
|
-- Vinculacion
|
|
project_id UUID REFERENCES construction.projects(id),
|
|
|
|
-- Tipo de contenido
|
|
folder_type VARCHAR(50), -- plans, contracts, permits, reports, etc.
|
|
|
|
-- Acceso
|
|
access_level documents.access_level DEFAULT 'public',
|
|
allowed_roles TEXT[],
|
|
|
|
-- Orden
|
|
sort_order INTEGER DEFAULT 0,
|
|
|
|
-- Estado
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_folders_tenant ON documents.folders(tenant_id);
|
|
CREATE INDEX idx_folders_parent ON documents.folders(parent_id);
|
|
CREATE INDEX idx_folders_project ON documents.folders(project_id);
|
|
CREATE INDEX idx_folders_path ON documents.folders(full_path);
|
|
```
|
|
|
|
### 2.2 documents (Documentos)
|
|
|
|
```sql
|
|
CREATE TABLE documents.documents (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
folder_id UUID REFERENCES documents.folders(id),
|
|
|
|
-- Identificacion
|
|
document_number VARCHAR(50),
|
|
title VARCHAR(300) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Tipo
|
|
document_type documents.document_type NOT NULL,
|
|
subcategory VARCHAR(100),
|
|
|
|
-- Vinculacion
|
|
project_id UUID REFERENCES construction.projects(id),
|
|
housing_unit_id UUID REFERENCES construction.housing_units(id),
|
|
|
|
-- Version actual
|
|
current_version_id UUID, -- Referencia a document_versions
|
|
current_version_number VARCHAR(20),
|
|
total_versions INTEGER DEFAULT 1,
|
|
|
|
-- Archivo original
|
|
file_url TEXT NOT NULL,
|
|
file_name VARCHAR(255) NOT NULL,
|
|
file_size BIGINT,
|
|
mime_type VARCHAR(100),
|
|
file_extension VARCHAR(20),
|
|
|
|
-- Thumbnail y preview
|
|
thumbnail_url TEXT,
|
|
preview_url TEXT,
|
|
|
|
-- Metadata
|
|
tags TEXT[],
|
|
custom_fields JSONB,
|
|
|
|
-- Estado
|
|
status documents.document_status NOT NULL DEFAULT 'draft',
|
|
access_level documents.access_level DEFAULT 'public',
|
|
|
|
-- Fechas
|
|
document_date DATE,
|
|
expiry_date DATE,
|
|
|
|
-- OCR y busqueda
|
|
ocr_text TEXT,
|
|
is_ocr_processed BOOLEAN DEFAULT false,
|
|
ocr_processed_at TIMESTAMP,
|
|
|
|
-- 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_documents_number UNIQUE (tenant_id, project_id, document_number)
|
|
);
|
|
|
|
CREATE INDEX idx_documents_tenant ON documents.documents(tenant_id);
|
|
CREATE INDEX idx_documents_folder ON documents.documents(folder_id);
|
|
CREATE INDEX idx_documents_project ON documents.documents(project_id);
|
|
CREATE INDEX idx_documents_type ON documents.documents(document_type);
|
|
CREATE INDEX idx_documents_status ON documents.documents(status);
|
|
CREATE INDEX idx_documents_tags ON documents.documents USING GIN(tags);
|
|
CREATE INDEX idx_documents_search ON documents.documents USING GIN(to_tsvector('spanish', coalesce(title, '') || ' ' || coalesce(description, '') || ' ' || coalesce(ocr_text, '')));
|
|
```
|
|
|
|
### 2.3 document_versions (Versiones)
|
|
|
|
```sql
|
|
CREATE TABLE documents.document_versions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
document_id UUID NOT NULL REFERENCES documents.documents(id),
|
|
|
|
-- Version
|
|
version_number VARCHAR(20) NOT NULL, -- 1.0, Rev. A, etc.
|
|
version_sequence INTEGER NOT NULL,
|
|
|
|
-- Archivo
|
|
file_url TEXT NOT NULL,
|
|
file_name VARCHAR(255) NOT NULL,
|
|
file_size BIGINT,
|
|
mime_type VARCHAR(100),
|
|
|
|
-- Thumbnail y preview
|
|
thumbnail_url TEXT,
|
|
preview_url TEXT,
|
|
|
|
-- Cambios
|
|
change_summary TEXT,
|
|
change_type VARCHAR(50), -- minor, major, revision
|
|
|
|
-- Estado
|
|
status documents.document_status NOT NULL DEFAULT 'draft',
|
|
is_current BOOLEAN DEFAULT false,
|
|
|
|
-- Aprobacion
|
|
approved_by UUID REFERENCES core.users(id),
|
|
approved_at TIMESTAMP,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
|
|
CONSTRAINT uq_version_sequence UNIQUE (document_id, version_sequence)
|
|
);
|
|
|
|
CREATE INDEX idx_versions_document ON documents.document_versions(document_id);
|
|
CREATE INDEX idx_versions_current ON documents.document_versions(document_id) WHERE is_current = true;
|
|
CREATE INDEX idx_versions_status ON documents.document_versions(status);
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Tablas de Planos
|
|
|
|
### 3.1 plans (Planos)
|
|
|
|
```sql
|
|
CREATE TABLE documents.plans (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
document_id UUID NOT NULL REFERENCES documents.documents(id),
|
|
|
|
-- Tipo de plano
|
|
plan_type documents.plan_type NOT NULL,
|
|
discipline VARCHAR(50),
|
|
|
|
-- Identificacion tecnica
|
|
plan_number VARCHAR(50) NOT NULL,
|
|
sheet_number VARCHAR(20),
|
|
total_sheets INTEGER,
|
|
|
|
-- Escala
|
|
scale VARCHAR(20),
|
|
|
|
-- Dimensiones
|
|
format VARCHAR(20), -- A0, A1, A2, A3, A4, etc.
|
|
width_mm DECIMAL(10,2),
|
|
height_mm DECIMAL(10,2),
|
|
|
|
-- Coordenadas (para planos georeferenciados)
|
|
north_coordinate DECIMAL(18,8),
|
|
east_coordinate DECIMAL(18,8),
|
|
coordinate_system VARCHAR(50),
|
|
|
|
-- Software origen
|
|
source_software VARCHAR(100), -- AutoCAD, Revit, etc.
|
|
source_file_url TEXT, -- Archivo fuente (.dwg, .rvt)
|
|
|
|
-- Revision actual
|
|
current_revision VARCHAR(10),
|
|
|
|
-- Estado
|
|
is_for_construction BOOLEAN DEFAULT false,
|
|
is_as_built BOOLEAN DEFAULT false,
|
|
|
|
-- 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_plans_number UNIQUE (tenant_id, project_id, plan_number)
|
|
);
|
|
|
|
CREATE INDEX idx_plans_document ON documents.plans(document_id);
|
|
CREATE INDEX idx_plans_type ON documents.plans(plan_type);
|
|
CREATE INDEX idx_plans_number ON documents.plans(plan_number);
|
|
```
|
|
|
|
### 3.2 plan_annotations (Anotaciones en Planos)
|
|
|
|
```sql
|
|
CREATE TABLE documents.plan_annotations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
plan_id UUID NOT NULL REFERENCES documents.plans(id),
|
|
version_id UUID REFERENCES documents.document_versions(id),
|
|
|
|
-- Tipo de anotacion
|
|
annotation_type VARCHAR(50) NOT NULL, -- comment, markup, measurement, symbol
|
|
|
|
-- Posicion
|
|
x_position DECIMAL(10,4),
|
|
y_position DECIMAL(10,4),
|
|
width DECIMAL(10,4),
|
|
height DECIMAL(10,4),
|
|
rotation DECIMAL(6,2),
|
|
|
|
-- Contenido
|
|
content TEXT,
|
|
color VARCHAR(20),
|
|
style JSONB,
|
|
|
|
-- Forma (para markups)
|
|
shape_type VARCHAR(30), -- rectangle, circle, arrow, freehand, etc.
|
|
shape_points JSONB, -- Array de puntos
|
|
|
|
-- Estado
|
|
is_resolved BOOLEAN DEFAULT false,
|
|
resolved_by UUID REFERENCES core.users(id),
|
|
resolved_at TIMESTAMP,
|
|
|
|
-- 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_annotations_plan ON documents.plan_annotations(plan_id);
|
|
CREATE INDEX idx_annotations_version ON documents.plan_annotations(version_id);
|
|
CREATE INDEX idx_annotations_unresolved ON documents.plan_annotations(plan_id) WHERE is_resolved = false;
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Tablas de Flujo de Aprobacion
|
|
|
|
### 4.1 approval_workflows (Flujos de Aprobacion)
|
|
|
|
```sql
|
|
CREATE TABLE documents.approval_workflows (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Identificacion
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Configuracion
|
|
document_types documents.document_type[],
|
|
total_levels INTEGER NOT NULL DEFAULT 2,
|
|
|
|
-- Estado
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_workflows_tenant ON documents.approval_workflows(tenant_id);
|
|
CREATE INDEX idx_workflows_active ON documents.approval_workflows(tenant_id) WHERE is_active = true;
|
|
```
|
|
|
|
### 4.2 workflow_levels (Niveles de Aprobacion)
|
|
|
|
```sql
|
|
CREATE TABLE documents.workflow_levels (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
workflow_id UUID NOT NULL REFERENCES documents.approval_workflows(id),
|
|
|
|
-- Nivel
|
|
level_number INTEGER NOT NULL,
|
|
level_name VARCHAR(100) NOT NULL,
|
|
|
|
-- Aprobadores
|
|
approver_type VARCHAR(30) NOT NULL, -- user, role, department
|
|
approver_ids UUID[],
|
|
approver_roles TEXT[],
|
|
|
|
-- Configuracion
|
|
requires_all BOOLEAN DEFAULT false, -- true = todos deben aprobar
|
|
can_skip BOOLEAN DEFAULT false,
|
|
|
|
-- Orden
|
|
sort_order INTEGER DEFAULT 0,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
|
|
CONSTRAINT uq_workflow_level UNIQUE (workflow_id, level_number)
|
|
);
|
|
|
|
CREATE INDEX idx_workflow_levels_workflow ON documents.workflow_levels(workflow_id);
|
|
```
|
|
|
|
### 4.3 document_approvals (Aprobaciones)
|
|
|
|
```sql
|
|
CREATE TABLE documents.document_approvals (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
document_id UUID NOT NULL REFERENCES documents.documents(id),
|
|
version_id UUID REFERENCES documents.document_versions(id),
|
|
|
|
-- Workflow
|
|
workflow_id UUID REFERENCES documents.approval_workflows(id),
|
|
current_level INTEGER DEFAULT 1,
|
|
|
|
-- Estado general
|
|
status VARCHAR(30) DEFAULT 'pending', -- pending, in_progress, approved, rejected
|
|
|
|
-- Fechas
|
|
submitted_at TIMESTAMP,
|
|
submitted_by UUID REFERENCES core.users(id),
|
|
completed_at TIMESTAMP,
|
|
|
|
-- Resultado
|
|
final_action documents.approval_action,
|
|
final_comments 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_approvals_document ON documents.document_approvals(document_id);
|
|
CREATE INDEX idx_approvals_status ON documents.document_approvals(status);
|
|
CREATE INDEX idx_approvals_pending ON documents.document_approvals(tenant_id) WHERE status = 'in_progress';
|
|
```
|
|
|
|
### 4.4 approval_actions (Acciones de Aprobacion)
|
|
|
|
```sql
|
|
CREATE TABLE documents.approval_actions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
approval_id UUID NOT NULL REFERENCES documents.document_approvals(id),
|
|
|
|
-- Nivel
|
|
level_number INTEGER NOT NULL,
|
|
|
|
-- Accion
|
|
action documents.approval_action NOT NULL,
|
|
action_by UUID NOT NULL REFERENCES core.users(id),
|
|
action_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
-- Comentarios
|
|
comments TEXT,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_approval_actions_approval ON documents.approval_actions(approval_id);
|
|
CREATE INDEX idx_approval_actions_user ON documents.approval_actions(action_by);
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Tablas de Acceso y Compartir
|
|
|
|
### 5.1 document_shares (Compartidos)
|
|
|
|
```sql
|
|
CREATE TABLE documents.document_shares (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
document_id UUID NOT NULL REFERENCES documents.documents(id),
|
|
|
|
-- Destinatario
|
|
share_type VARCHAR(30) NOT NULL, -- user, role, external
|
|
shared_with_user_id UUID REFERENCES core.users(id),
|
|
shared_with_role VARCHAR(100),
|
|
shared_with_email VARCHAR(255),
|
|
|
|
-- Permisos
|
|
can_view BOOLEAN DEFAULT true,
|
|
can_download BOOLEAN DEFAULT false,
|
|
can_annotate BOOLEAN DEFAULT false,
|
|
can_share BOOLEAN DEFAULT false,
|
|
|
|
-- Vigencia
|
|
expires_at TIMESTAMP,
|
|
access_token VARCHAR(100), -- Para links externos
|
|
|
|
-- Estado
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_shares_document ON documents.document_shares(document_id);
|
|
CREATE INDEX idx_shares_user ON documents.document_shares(shared_with_user_id);
|
|
CREATE INDEX idx_shares_token ON documents.document_shares(access_token);
|
|
```
|
|
|
|
### 5.2 document_access_logs (Registro de Accesos)
|
|
|
|
```sql
|
|
CREATE TABLE documents.document_access_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
document_id UUID NOT NULL REFERENCES documents.documents(id),
|
|
|
|
-- Accion
|
|
action VARCHAR(50) NOT NULL, -- view, download, print, annotate, share
|
|
|
|
-- Usuario
|
|
user_id UUID REFERENCES core.users(id),
|
|
user_ip VARCHAR(45),
|
|
user_agent TEXT,
|
|
|
|
-- Detalles
|
|
version_id UUID REFERENCES documents.document_versions(id),
|
|
details JSONB,
|
|
|
|
-- Timestamp
|
|
accessed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX idx_access_logs_document ON documents.document_access_logs(document_id);
|
|
CREATE INDEX idx_access_logs_user ON documents.document_access_logs(user_id);
|
|
CREATE INDEX idx_access_logs_date ON documents.document_access_logs(accessed_at);
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Funciones
|
|
|
|
### 6.1 Actualizar Version Actual
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION documents.update_current_version()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
-- Si es version actual, quitar el flag de otras versiones
|
|
IF NEW.is_current = true THEN
|
|
UPDATE documents.document_versions
|
|
SET is_current = false
|
|
WHERE document_id = NEW.document_id
|
|
AND id != NEW.id
|
|
AND is_current = true;
|
|
|
|
-- Actualizar documento
|
|
UPDATE documents.documents
|
|
SET
|
|
current_version_id = NEW.id,
|
|
current_version_number = NEW.version_number,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = NEW.document_id;
|
|
END IF;
|
|
|
|
-- Actualizar contador de versiones
|
|
UPDATE documents.documents
|
|
SET total_versions = (
|
|
SELECT COUNT(*) FROM documents.document_versions
|
|
WHERE document_id = NEW.document_id
|
|
)
|
|
WHERE id = NEW.document_id;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_update_current_version
|
|
AFTER INSERT OR UPDATE ON documents.document_versions
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION documents.update_current_version();
|
|
```
|
|
|
|
### 6.2 Actualizar Path de Carpeta
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION documents.update_folder_path()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_parent_path TEXT;
|
|
BEGIN
|
|
IF NEW.parent_id IS NULL THEN
|
|
NEW.full_path := '/' || NEW.name;
|
|
NEW.level := 1;
|
|
ELSE
|
|
SELECT full_path, level + 1 INTO v_parent_path, NEW.level
|
|
FROM documents.folders
|
|
WHERE id = NEW.parent_id;
|
|
|
|
NEW.full_path := v_parent_path || '/' || NEW.name;
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_update_folder_path
|
|
BEFORE INSERT OR UPDATE OF parent_id, name ON documents.folders
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION documents.update_folder_path();
|
|
```
|
|
|
|
### 6.3 Procesar Flujo de Aprobacion
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION documents.process_approval_action(
|
|
p_approval_id UUID,
|
|
p_action documents.approval_action,
|
|
p_user_id UUID,
|
|
p_comments TEXT DEFAULT NULL
|
|
)
|
|
RETURNS BOOLEAN AS $$
|
|
DECLARE
|
|
v_approval RECORD;
|
|
v_workflow RECORD;
|
|
v_next_level INTEGER;
|
|
v_max_level INTEGER;
|
|
BEGIN
|
|
SELECT * INTO v_approval FROM documents.document_approvals WHERE id = p_approval_id;
|
|
|
|
-- Registrar accion
|
|
INSERT INTO documents.approval_actions (
|
|
tenant_id, approval_id, level_number, action, action_by, comments
|
|
)
|
|
VALUES (
|
|
v_approval.tenant_id, p_approval_id, v_approval.current_level, p_action, p_user_id, p_comments
|
|
);
|
|
|
|
-- Procesar segun accion
|
|
CASE p_action
|
|
WHEN 'approve' THEN
|
|
-- Verificar si hay mas niveles
|
|
SELECT total_levels INTO v_max_level
|
|
FROM documents.approval_workflows
|
|
WHERE id = v_approval.workflow_id;
|
|
|
|
IF v_approval.current_level >= v_max_level THEN
|
|
-- Aprobacion completa
|
|
UPDATE documents.document_approvals
|
|
SET
|
|
status = 'approved',
|
|
final_action = 'approve',
|
|
final_comments = p_comments,
|
|
completed_at = CURRENT_TIMESTAMP,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = p_approval_id;
|
|
|
|
-- Actualizar documento y version
|
|
UPDATE documents.documents SET status = 'approved', updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = v_approval.document_id;
|
|
|
|
UPDATE documents.document_versions SET status = 'approved', approved_by = p_user_id, approved_at = CURRENT_TIMESTAMP
|
|
WHERE id = v_approval.version_id;
|
|
ELSE
|
|
-- Avanzar al siguiente nivel
|
|
UPDATE documents.document_approvals
|
|
SET
|
|
current_level = current_level + 1,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = p_approval_id;
|
|
END IF;
|
|
|
|
WHEN 'reject' THEN
|
|
UPDATE documents.document_approvals
|
|
SET
|
|
status = 'rejected',
|
|
final_action = 'reject',
|
|
final_comments = p_comments,
|
|
completed_at = CURRENT_TIMESTAMP,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = p_approval_id;
|
|
|
|
UPDATE documents.documents SET status = 'rejected', updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = v_approval.document_id;
|
|
|
|
UPDATE documents.document_versions SET status = 'rejected'
|
|
WHERE id = v_approval.version_id;
|
|
|
|
WHEN 'request_changes' THEN
|
|
UPDATE documents.document_approvals
|
|
SET
|
|
status = 'pending',
|
|
final_comments = p_comments,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = p_approval_id;
|
|
|
|
UPDATE documents.documents SET status = 'in_review', updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = v_approval.document_id;
|
|
END CASE;
|
|
|
|
RETURN true;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
### 6.4 Busqueda Full-Text
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION documents.search_documents(
|
|
p_tenant_id UUID,
|
|
p_query TEXT,
|
|
p_project_id UUID DEFAULT NULL,
|
|
p_document_type documents.document_type DEFAULT NULL,
|
|
p_limit INTEGER DEFAULT 50
|
|
)
|
|
RETURNS TABLE (
|
|
document_id UUID,
|
|
title VARCHAR,
|
|
document_type documents.document_type,
|
|
relevance REAL
|
|
) AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT
|
|
d.id,
|
|
d.title,
|
|
d.document_type,
|
|
ts_rank(
|
|
to_tsvector('spanish', coalesce(d.title, '') || ' ' || coalesce(d.description, '') || ' ' || coalesce(d.ocr_text, '')),
|
|
plainto_tsquery('spanish', p_query)
|
|
) as relevance
|
|
FROM documents.documents d
|
|
WHERE d.tenant_id = p_tenant_id
|
|
AND d.deleted_at IS NULL
|
|
AND (p_project_id IS NULL OR d.project_id = p_project_id)
|
|
AND (p_document_type IS NULL OR d.document_type = p_document_type)
|
|
AND to_tsvector('spanish', coalesce(d.title, '') || ' ' || coalesce(d.description, '') || ' ' || coalesce(d.ocr_text, ''))
|
|
@@ plainto_tsquery('spanish', p_query)
|
|
ORDER BY relevance DESC
|
|
LIMIT p_limit;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Row Level Security
|
|
|
|
```sql
|
|
-- Habilitar RLS en todas las tablas
|
|
ALTER TABLE documents.folders ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE documents.documents ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE documents.document_versions ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE documents.plans ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE documents.plan_annotations ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE documents.approval_workflows ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE documents.workflow_levels ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE documents.document_approvals ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE documents.approval_actions ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE documents.document_shares ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE documents.document_access_logs 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 = 'documents'
|
|
LOOP
|
|
EXECUTE format('
|
|
CREATE POLICY tenant_isolation ON documents.%I
|
|
USING (tenant_id = current_setting(''app.current_tenant_id'')::uuid)
|
|
', t);
|
|
END LOOP;
|
|
END $$;
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Seeds Iniciales
|
|
|
|
```sql
|
|
-- Carpetas base por defecto
|
|
INSERT INTO documents.folders (tenant_id, name, folder_type, sort_order)
|
|
VALUES
|
|
('{{TENANT_ID}}', 'Planos', 'plans', 1),
|
|
('{{TENANT_ID}}', 'Contratos', 'contracts', 2),
|
|
('{{TENANT_ID}}', 'Permisos y Licencias', 'permits', 3),
|
|
('{{TENANT_ID}}', 'Especificaciones', 'specifications', 4),
|
|
('{{TENANT_ID}}', 'Reportes', 'reports', 5),
|
|
('{{TENANT_ID}}', 'Fotografias', 'photos', 6);
|
|
|
|
-- Workflow de aprobacion por defecto
|
|
INSERT INTO documents.approval_workflows (tenant_id, name, document_types, total_levels)
|
|
VALUES
|
|
('{{TENANT_ID}}', 'Aprobacion de Planos', ARRAY['plan']::documents.document_type[], 3);
|
|
|
|
-- Niveles del workflow
|
|
INSERT INTO documents.workflow_levels (tenant_id, workflow_id, level_number, level_name, approver_type, approver_roles)
|
|
SELECT
|
|
'{{TENANT_ID}}',
|
|
w.id,
|
|
level_number,
|
|
level_name,
|
|
'role',
|
|
approver_roles
|
|
FROM documents.approval_workflows w
|
|
CROSS JOIN (VALUES
|
|
(1, 'Revision Tecnica', ARRAY['supervisor', 'residente']),
|
|
(2, 'Revision Gerencia', ARRAY['gerente_proyecto']),
|
|
(3, 'Aprobacion Final', ARRAY['director'])
|
|
) AS levels(level_number, level_name, approver_roles)
|
|
WHERE w.name = 'Aprobacion de Planos';
|
|
```
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [MAE-016: Gestion Documental](../../02-definicion-modulos/MAE-016-gestion-documental/)
|
|
- [ADR-007: Database Design](../../97-adr/ADR-007-database-design.md)
|
|
|
|
---
|
|
|
|
*Ultima actualizacion: 2025-12-05*
|