erp-construccion/docs/04-modelado/database-design/schemas/DDL-SPEC-documents.md

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*