# 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*