Principios del modelo
Las 9 decisiones técnicas no negociables que sostienen el esquema. Cambiar cualquiera de estas implica re-pensar el sistema entero.
| Decisión | Criterio |
|---|---|
| Base | PostgreSQL 15+ · ACID, JSONB nativo, extensiones para auditoría |
| IDs | UUID v4 en todas las tablas operativas. Permite federación y reduce colisiones. |
| Multiempresa | company_id UUID NOT NULL en toda tabla operativa. Row-Level Security activa. |
| Trazabilidad | RAW nunca se modifica. Cada fila tiene row_hash SHA-256 inmutable. |
| IA | Auditada por ai_requests + ai_responses con modelo, tokens, costo, latencia. |
| Score | Guardado por período + componente + drivers en faro_score_snapshots. |
| Acciones | SIEMPRE con responsable_id, vence_at y evidencia_id al cerrar. |
| Auditoría | Tabla transversal audit_logs con quién, qué, cuándo y de dónde (IP/agent). |
| MVP | Ventas, stock, cobranza, KPIs, alertas, tensiones, acciones, evidencia, Score. v2: compras, gastos, RRHH, proyectos, contabilidad. v3: integraciones nativas con ERPs comunes. |
Timeline de Migraciones V001 — V031
El esquema se construye con 31 migraciones canónicas versionadas tipo Flyway. Cada Vxxx__nombre.sql es idempotente, reproducible y aplicable en orden estricto. Las 31 migraciones se distribuyen en tres bloques temáticos: base del MVP, multiempresa con RLS, y seeds + catálogos canónicos.
faro y audit, audit log, multiempresa básica (companies/branches/areas/users/roles), data sources + RAW, master data, staging, facts, KPIs/signals/rules, tensiones/acciones/evidencia, FARO Score + reports, índices y seed mínimo de catálogos. Fuente: FARO-SQL-001.Tabla maestra · 31 migraciones
| V | Nombre | Propósito | Tablas / funciones | Doc | Depende de |
|---|---|---|---|---|---|
| V001 | extensions_and_schemas | Extensiones base + schemas faro y audit. |
pgcryptocitextschema faroschema audit |
SQL-001 | — |
| V002 | common_functions_and_audit | Funciones comunes + tabla central de auditoría. | faro.set_updated_at()audit.audit_log |
SQL-001 | V001 |
| V003 | companies_org_users_roles | Multiempresa base: companies, branches, areas, users, roles, permissions. | companiesbranchesareasusersrolespermissionsuser_rolesrole_permissions |
SQL-001 | V002 |
| V004 | data_sources_and_ingestion_raw | Data sources, source fields, batches de ingestión y registros RAW inmutables. | data_sourcessource_fieldsingestion_batchesraw_records |
SQL-001 | V003 |
| V005 | master_data | Catálogos vivos: customers, products, sellers, suppliers. | master_customersmaster_productsmaster_sellersmaster_suppliers |
SQL-001 | V003 |
| V006 | staging_tables | Tablas staging por dominio con linaje a RAW. | staging_salesstaging_stockstaging_receivables |
SQL-001 | V004 · V005 |
| V007 | fact_tables | Hechos consolidados particionados por período. | fact_salesfact_stockfact_receivables |
SQL-001 | V006 |
| V008 | kpis_signals_rules | Catálogo de KPIs, cálculos por período, señales V/A/R y reglas. | kpi_catalogkpi_calculationssignalsrules |
SQL-001 | V007 |
| V009 | tensions_actions_evidence | Catálogos de tensiones, acciones, detecciones, instancias, workflow y evidencia. | tension_catalogtension_detectionsaction_catalogaction_instancesaction_workflow_logevidence_filesevidence_index |
SQL-001 | V008 |
| V010 | score_reports | FARO Score snapshots versionados + componentes + reportes ejecutivos. | faro_score_snapshotsfaro_score_componentsexecutive_reports |
SQL-001 | V008 · V009 |
| V011 | indexes_constraints | Índices compuestos por company_id + period y constraints de integridad. |
idx_*_company_periodfk_*uq_* |
SQL-001 | V010 |
| V012 | seed_minimal_catalogs | Seed mínimo: permisos base, roles de sistema, KPIs/tensiones/acciones núcleo. | INSERT permissionsINSERT rolesINSERT kpi_catalog |
SQL-001 | V011 |
| V013 | mvp_kpi_functions | Funciones MVP del motor KPI (cierre del bloque base antes de habilitar RLS). | faro.fn_calculate_kpis_mvpfaro.fn_evaluate_signals |
SQL-001 | V012 |
| V014 | security_context_functions | Funciones de contexto de seguridad para resolver tenant/usuario en sesión. | faro.fn_current_company_id()faro.fn_current_user_id()faro.fn_has_permission() |
SQL-002 | V013 |
| V015 | application_db_roles_and_grants | Roles de base de datos por aplicación (faro_app, faro_readonly) y grants. |
ROLE faro_appROLE faro_readonlyGRANT * |
SQL-002 | V014 |
| V016 | seed_roles_permissions | Seed de roles canónicos (admin, director, gerente, operativo) y mapeo de permissions. | INSERT rolesINSERT permissionsINSERT role_permissions |
SQL-002 | V015 |
| V017 | enable_rls_core_tables | Habilita Row-Level Security en todas las tablas operativas core. | ALTER TABLE … ENABLE RLS |
SQL-002 | V016 |
| V018 | rls_policies_org_sources_raw | Policies RLS sobre organización (companies/branches/areas/users), data sources y RAW. | POLICY org_tenantPOLICY sources_tenantPOLICY raw_tenant |
SQL-002 | V017 |
| V019 | rls_policies_master_staging_facts | Policies RLS para master data, staging y facts. | POLICY master_*POLICY staging_*POLICY fact_* |
SQL-002 | V018 |
| V020 | rls_policies_kpis_rules_tensions_actions | Policies RLS para KPIs, reglas, tensiones y acciones. | POLICY kpi_*POLICY rules_*POLICY tension_*POLICY action_* |
SQL-002 | V019 |
| V021 | rls_policies_score_reports_notifications | Policies RLS para FARO Score, reportes y notificaciones. | POLICY score_*POLICY reports_*POLICY notif_* |
SQL-002 | V020 |
| V022 | secure_views_sensitive_data | Vistas seguras que enmascaran columnas sensibles (PII, tax_id) según permisos. | v_users_safev_customers_safev_suppliers_safe |
SQL-002 | V021 |
| V023 | rls_test_two_tenants | Test funcional con dos tenants ficticios validando aislamiento RLS. | SELECT…ASSERT count() filtered |
SQL-002 | V022 |
| V024 | seed_demo_company | Seed completo de Empresa Demo: company + branches + areas + users + roles + 1 source. | INSERT companiesINSERT branchesINSERT usersINSERT user_roles |
SQL-003 | V023 |
| V025 | create_tension_definitions | Crea tabla tension_definitions para catálogo canónico de tensiones (separada de las detecciones por tenant). |
faro.tension_definitions |
SQL-004 | V024 |
| V026 | seed_tension_definitions_mvp | Seed TNS-001 a TNS-030 (catálogo canónico MVP de tensiones con DSL y KPIs origen). | INSERT tension_definitions ×30 |
SQL-004 | V025 |
| V027 | patch_demo_company_align_canonical_tns | Patch de alineación: reescribe detecciones de la Empresa Demo para usar códigos canónicos TNS-NNN. | UPDATE tension_detectionsFK tension_definitions |
SQL-003.1 | V024 · V026 |
| V028 | create_action_definitions | Crea tabla action_definitions para catálogo canónico de acciones (RACI base, SLA tipo, workflow). |
faro.action_definitions |
SQL-005 | V027 |
| V029 | seed_action_definitions_mvp | Seed ACT-001 a ACT-NNN (catálogo canónico MVP de acciones acopladas a tensiones). | INSERT action_definitions |
SQL-005 | V028 |
| V030 | create_evidence_definitions | Crea tabla evidence_definitions para catálogo canónico de tipos de evidencia (EVD-001+). |
faro.evidence_definitions |
SQL-006 | V029 |
| V031 | seed_evidence_definitions_mvp | Seed EVD-001+ (catálogo canónico MVP de evidencias requeridas al cerrar acciones). | INSERT evidence_definitions |
SQL-006 | V030 |
Detalle expandible · DDL resumido por migración
Selección de migraciones clave con DDL resumido. El bundle ejecutable completo está en Descargas.
V001 · extensions_and_schemas SQL-001
Activa extensiones criptográficas y de texto case-insensitive. Crea los dos schemas raíz del sistema.
CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE EXTENSION IF NOT EXISTS citext; -- CREATE EXTENSION IF NOT EXISTS vector; -- opcional (embeddings) CREATE SCHEMA IF NOT EXISTS faro; CREATE SCHEMA IF NOT EXISTS audit;
V002 · common_functions_and_audit SQL-001
Trigger genérico set_updated_at() + tabla central audit.audit_log con 15 acciones permitidas (insert, update, delete, login, logout, ingest, process, calculate, evaluate, approve, reject, close, reopen, escalate, export).
CREATE OR REPLACE FUNCTION faro.set_updated_at() RETURNS trigger ... CREATE TABLE audit.audit_log ( audit_id uuid PRIMARY KEY DEFAULT gen_random_uuid(), company_id uuid NULL, actor_user_id uuid NULL, entity_name text NOT NULL, action text NOT NULL CHECK (action IN (...)), before_data jsonb, after_data jsonb, metadata jsonb NOT NULL DEFAULT '{}'::jsonb, ip_address inet, user_agent text, created_at timestamptz NOT NULL DEFAULT now() );
V003 · companies_org_users_roles SQL-001
Multiempresa desde el primer día: companies, branches, areas (con jerarquía), users, roles, permissions + tablas puente user_roles y role_permissions. Toda tabla con updated_at via trigger.
CREATE TABLE faro.companies (company_id, company_code, legal_name, country_code, industry_code, status, timezone, currency_code, settings jsonb, ...); CREATE TABLE faro.branches (branch_id, company_id, branch_code, branch_type, ...); CREATE TABLE faro.areas (area_id, company_id, parent_area_id, area_type, ...); CREATE TABLE faro.users (user_id, company_id, email citext, default_branch_id, default_area_id, ...); CREATE TABLE faro.roles (role_id, company_id, role_code, scope, is_system_role, ...); CREATE TABLE faro.permissions (permission_id, permission_code, module_code, action_code, ...); CREATE TABLE faro.user_roles (user_id, role_id, branch_id, area_id, ...); CREATE TABLE faro.role_permissions (role_id, permission_id, ...);
V014 · security_context_functions SQL-002
Funciones que resuelven el contexto de seguridad de la sesión actual (tenant, usuario, permisos). Son la base de las policies RLS de V017-V021.
CREATE FUNCTION faro.fn_current_company_id() RETURNS uuid ... CREATE FUNCTION faro.fn_current_user_id() RETURNS uuid ... CREATE FUNCTION faro.fn_has_permission(p_code text) RETURNS boolean ... -- Usadas por toda CREATE POLICY downstream
V017 · enable_rls_core_tables SQL-002
Switch global: prende RLS en todas las tablas que llevan company_id. Sin policies aún, el efecto es bloqueo total (fail-safe). Las V018-V021 abren los accesos controlados.
ALTER TABLE faro.companies ENABLE ROW LEVEL SECURITY; ALTER TABLE faro.users ENABLE ROW LEVEL SECURITY; ALTER TABLE faro.branches ENABLE ROW LEVEL SECURITY; -- ... 20+ tablas operativas
V024 · seed_demo_company SQL-003
Crea la Empresa Demo end-to-end: company + 3 branches + 6 areas + 8 users + role assignments + 1 data source de prueba. Permite correr el pipeline completo sobre data sintética.
INSERT INTO faro.companies (company_code, legal_name, country_code, industry_code, ...) VALUES ('DEMO-001', 'Empresa Demo SA', 'AR', 'IND-COM-01', ...); INSERT INTO faro.branches ... ; INSERT INTO faro.users ... ; INSERT INTO faro.user_roles ... ; INSERT INTO faro.data_sources ... ;
V025-V026 · tension_definitions (tabla + seed TNS-001..TNS-030) SQL-004
Separa el catálogo canónico de tensiones (compartido entre tenants) de las detecciones (por tenant). V025 crea la tabla, V026 carga las primeras 30 tensiones MVP con DSL ejecutable y KPIs origen.
CREATE TABLE faro.tension_definitions ( tension_code text PRIMARY KEY, -- 'TNS-001' name text NOT NULL, area text, tension_type text, dsl_logic text NOT NULL, sql_logic text, kpi_origin text[], severity smallint CHECK (severity BETWEEN 1 AND 5), applicable_industries text[], action_code text, is_mvp boolean DEFAULT false, version text NOT NULL DEFAULT 'v1.0' ); INSERT INTO faro.tension_definitions VALUES ('TNS-001', 'Caída de margen bruto', ...), ('TNS-002', ...); -- ×30
V028-V029 · action_definitions (tabla + seed ACT-NNN) SQL-005
Catálogo canónico de acciones con RACI base, SLA tipo y workflow esperado. El seed acopla ACT a TNS 1:1 según el flujo Tensión → Acción.
CREATE TABLE faro.action_definitions ( action_code text PRIMARY KEY, -- 'ACT-001' name text, area text, priority text, raci_responsable text, raci_accountable text, sla_type text, sla_hours int, workflow text[], -- estados tension_codes text[], -- 1:1 con TNS evidence_required text[], -- EVD-NNN is_mvp boolean DEFAULT false, version text NOT NULL DEFAULT 'v1.0' );
V030-V031 · evidence_definitions (tabla + seed EVD-NNN) SQL-006
Catálogo canónico de tipos de evidencia requeridos al cerrar acciones. Define qué tipo de archivo o registro es válido para auditar el cierre.
CREATE TABLE faro.evidence_definitions ( evidence_code text PRIMARY KEY, -- 'EVD-001' name text, evidence_type text, -- acta, plan, conciliacion, ... allowed_mime text[], retention_days int, is_mvp boolean DEFAULT false, version text NOT NULL DEFAULT 'v1.0' );
Las 12 capas del esquema
El flujo de datos atraviesa estas capas en orden. Cada una tiene un propósito específico y sus propias tablas. RAW es inmutable; todas las demás se recalculan.
companiesusersrolesbranchesraw_importsraw_rowsraw_errorsstaging_salesstaging_stockstaging_receivablesmaster_customersmaster_productsmaster_sellersfact_salesfact_stockfact_receivableskpi_catalogkpi_calculationsalertsalert_dispatchestension_catalogtension_detectionsaction_catalogaction_instancesaction_workflow_logevidence_filesevidence_indexfaro_score_snapshotsfaro_score_componentsai_requestsai_responsesaudit_logs🗄️ Diagrama visual · 12 capas + ~50 tablas + flujo de datos
El flujo: RAW (inmutable) → STAGING (normalizado) → MAESTROS (UPSERT) → FACTS (particionados) → KPIs calculados → ALERTAS (cruce umbral) → TENSIONES (DSL) → ACCIONES (RACI+SLA) → EVIDENCIA → SCORE. Todo auditado en ai_requests y audit_logs.
Pipeline RAW → Score
El recorrido completo de un dato. Cada paso queda registrado, todo es reversible al origen RAW.
[1] INGESTA archivo.xlsx → raw_imports (status=received) → raw_rows (hash SHA-256 por fila, inmutable) → raw_errors (filas que no pasan validación) [2] NORMALIZACIÓN raw_rows → staging_sales (alias resueltos, tipos coercionados) → staging_stock → staging_receivables [3] MATCHING CON MAESTROS staging.cliente_id → master_customers (UPSERT) staging.producto_id → master_products (UPSERT) [4] CONSOLIDACIÓN A FACTS staging + maestros → fact_sales / fact_stock / fact_receivables (con company_id, period, partition by date) [5] CÁLCULO DE KPIs fact_* → kpi_calculations (run job por KPI, por período) // 400 KPIs canónicos, cálculo incremental [6] EVALUACIÓN DE UMBRALES kpi_calculations → alerts (si valor cruza umbral V/A/R) [7] DETECCIÓN DE TENSIONES alerts + KPIs combinados → tension_detections // motor reglas DSL: SI ΔKPI-001 >= +10% Y ΔKPI-002 <= -5% ENTONCES TNS-001 [8] DISPARO DE ACCIONES tension_detections → action_instances (1:1 con action_catalog) → notificación a RACI.responsable → SLA reloj arrancado [9] WORKFLOW DE LA ACCIÓN action_workflow_log: Nueva → En análisis → En ejecución → En verificación → Cerrada [10] EVIDENCIA AL CIERRE evidence_files (upload binario) → evidence_index (link a action_instance) → action_instance.status = closed [11] RECÁLCULO FARO SCORE kpi_calculations + tension_detections + action_instances cerradas → faro_score_snapshots (nuevo período) → faro_score_components (8 dim MVP) [12] AUDITORÍA EVERYWHERE audit_logs <─ cualquier INSERT/UPDATE/DELETE ai_requests + ai_responses <─ cualquier invocación a IA
CORE · Multiempresa desde día 1
Toda tabla operativa lleva company_id. Row-Level Security en Postgres garantiza que ninguna query salte tenants por accidente.
| Tabla | PK | Campos clave |
|---|---|---|
| companies | id UUID | name, slug, plan, status, settings JSONB |
| users | id UUID | email, password_hash, company_id, last_login_at |
| roles | id UUID | name (admin/director/gerente/operativo), company_id |
| permissions | id UUID | resource, action (kpi.read, action.close, etc.) |
| role_permissions | compuesta | role_id, permission_id |
| user_roles | compuesta | user_id, role_id, branch_id (opcional) |
| branches | id UUID | name, code, company_id, address JSONB |
INGESTA · RAW inmutable
El contrato con la auditoría: RAW nunca se modifica. Si algo se calcula mal río abajo, siempre podemos volver al byte original.
CREATE TABLE raw_imports ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), company_id UUID NOT NULL REFERENCES companies(id), domain TEXT NOT NULL -- 'sales' | 'stock' | 'receivables', filename TEXT NOT NULL, file_hash TEXT NOT NULL -- SHA-256 del archivo completo, status TEXT NOT NULL -- received | parsed | validated | failed, rows_total INTEGER, rows_ok INTEGER, rows_error INTEGER, uploaded_by UUID REFERENCES users(id), uploaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), parsed_at TIMESTAMPTZ, metadata JSONB -- locale, charset, separador, etc. ); CREATE TABLE raw_rows ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), raw_import_id UUID NOT NULL REFERENCES raw_imports(id), row_index INTEGER NOT NULL -- línea en el archivo original, row_hash TEXT NOT NULL -- SHA-256 del row completo, payload JSONB NOT NULL -- el row tal cual entró, sin transformar, ingested_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_raw_rows_import ON raw_rows(raw_import_id); CREATE INDEX idx_raw_imports_company ON raw_imports(company_id, uploaded_at);
STAGING · normalización por dominio
Tipos coercionados, alias resueltos al canónico, validaciones aplicadas. Mantiene raw_row_id como FK para linaje.
CREATE TABLE staging_sales ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), raw_row_id UUID NOT NULL REFERENCES raw_rows(id) -- LINAJE, company_id UUID NOT NULL, factura_id TEXT NOT NULL, fecha DATE NOT NULL, cliente_id TEXT NOT NULL, producto_id TEXT NOT NULL, sucursal_id TEXT NOT NULL, vendedor_id TEXT, cantidad NUMERIC(14,4) NOT NULL CHECK (cantidad > 0), precio_unitario NUMERIC(14,4) NOT NULL CHECK (precio_unitario >= 0), descuento_pct NUMERIC(5,2) DEFAULT 0, importe_bruto NUMERIC(14,4) NOT NULL, importe_neto NUMERIC(14,4) NOT NULL, costo_unitario NUMERIC(14,4), moneda CHAR(3) DEFAULT 'ARS', canal TEXT, condicion_pago TEXT, validations JSONB -- {"unique_id":"ok","date_format":"ok","bruto_neto_match":"warning"}, loaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (company_id, factura_id) -- idempotencia );
MAESTROS · entidades vivas
Clientes, productos, vendedores se actualizan por UPSERT desde staging. Trackean primera/última aparición y agregados básicos.
| Tabla | Campos clave |
|---|---|
| master_customers | customer_id, nombre, segmento, fecha_alta, ltv_pct, mora_max_dias |
| master_products | sku, nombre, categoria_abc, costo_promedio, margen_promedio, rotacion |
| master_sellers | seller_id, nombre, branch_id, ventas_ytd, comision_acumulada |
FACTS · hechos analíticos
Particionadas por fecha. Indexadas por company_id + period. Optimizadas para queries de KPI.
CREATE TABLE fact_sales ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), company_id UUID NOT NULL, period DATE NOT NULL -- truncado a día/mes según granularidad, factura_id TEXT NOT NULL, fecha DATE NOT NULL, cliente_id UUID REFERENCES master_customers(id), producto_id UUID REFERENCES master_products(id), sucursal_id UUID REFERENCES branches(id), cantidad NUMERIC(14,4), importe_neto NUMERIC(14,4), costo_total NUMERIC(14,4) -- calculado, margen_pct NUMERIC(5,2) -- calculado, staging_id UUID REFERENCES staging_sales(id) -- LINAJE ) PARTITION BY RANGE (period); CREATE INDEX idx_fact_sales_company_period ON fact_sales(company_id, period);
KPIs · catálogo + cálculos
El catálogo es la biblioteca de 400 KPIs. Los cálculos se hacen por período y se versionan: cambiar la fórmula crea una nueva versión.
CREATE TABLE kpi_catalog ( codigo TEXT PRIMARY KEY -- 'KPI-001' .. 'KPI-400', nombre TEXT NOT NULL, area TEXT, modulo TEXT, formula_sql TEXT NOT NULL -- query parametrizada, polaridad TEXT NOT NULL -- 'positiva' | 'negativa' | 'neutral', umbral_verde NUMERIC, umbral_ambar NUMERIC, umbral_rojo NUMERIC, dim_score TEXT -- 'DIM-01' .. 'DIM-10', peso_score NUMERIC(3,1), frecuencia TEXT, responsable_rol TEXT, mvp BOOLEAN DEFAULT false, version TEXT NOT NULL DEFAULT 'v1.0' ); CREATE TABLE kpi_calculations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), company_id UUID NOT NULL, kpi_codigo TEXT NOT NULL REFERENCES kpi_catalog(codigo), period DATE NOT NULL, valor NUMERIC(18,4) NOT NULL, banda TEXT -- 'verde' | 'ambar' | 'rojo', confianza NUMERIC(3,2) -- 0..1, baja si faltan datos, computed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), formula_version TEXT -- snapshot de la fórmula usada, UNIQUE (company_id, kpi_codigo, period) );
TENSIONES · catálogo + detecciones
El motor de tensiones evalúa reglas DSL contra los KPIs vigentes. Cada detección guarda los KPIs origen, los valores y el período.
CREATE TABLE tension_catalog ( codigo TEXT PRIMARY KEY -- 'TNS-001' .. 'TNS-300', nombre TEXT NOT NULL, area TEXT, tipo TEXT, definicion_ejecutiva TEXT, logica_dsl TEXT NOT NULL -- pseudocódigo evaluable, logica_sql TEXT -- compilada, kpis_origen TEXT[] -- ['KPI-001','KPI-002'], umbral_ambar TEXT, umbral_rojo TEXT, severidad SMALLINT CHECK (severidad BETWEEN 1 AND 5), peso_score NUMERIC(3,1), ind_aplicable TEXT[] -- ['IND-01','IND-02'] o ['IND-ALL'], accion_canonica TEXT REFERENCES action_catalog(codigo), mvp BOOLEAN DEFAULT false, version TEXT NOT NULL DEFAULT 'v1.0' ); CREATE TABLE tension_detections ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), company_id UUID NOT NULL, tension_codigo TEXT NOT NULL REFERENCES tension_catalog(codigo), period DATE NOT NULL, banda TEXT -- 'ambar' | 'rojo', kpis_evidence JSONB -- {"KPI-001":{"valor":1.12,"delta":"+12%"},...}, detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), resolved_at TIMESTAMPTZ, action_id UUID REFERENCES action_instances(id), UNIQUE (company_id, tension_codigo, period) );
ACCIONES · catálogo + instancias + workflow
Cada acción detectada genera una action_instance con RACI heredado del catálogo + responsable real de la empresa. El workflow log registra cada cambio de estado.
CREATE TABLE action_instances ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), company_id UUID NOT NULL, action_codigo TEXT NOT NULL REFERENCES action_catalog(codigo), tension_detection_id UUID REFERENCES tension_detections(id), responsable_id UUID REFERENCES users(id), accountable_id UUID REFERENCES users(id), estado TEXT NOT NULL -- Nueva|En análisis|En ejecución|En verificación|Cerrada, prioridad TEXT -- P1|P2|P3|P4, vence_at TIMESTAMPTZ NOT NULL, iniciada_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), cerrada_at TIMESTAMPTZ, evidencia_id UUID REFERENCES evidence_files(id) -- OBLIGATORIO al cerrar, score_impact_actual NUMERIC(4,1) -- medido post-cierre ); CREATE TABLE action_workflow_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), action_id UUID NOT NULL REFERENCES action_instances(id), estado_from TEXT, estado_to TEXT NOT NULL, changed_by UUID REFERENCES users(id), changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), comentario TEXT );
EVIDENCIA · auditoría documental
El archivo binario va a object storage (S3/MinIO). La metadata vive en SQL con tipo canónico EVD-NNN. Permite búsqueda y auditoría sin descargar el binario.
| Tabla | Campos clave |
|---|---|
| evidence_files | id, company_id, evd_tipo (EVD-001..012), s3_key, filename, mime, size, sha256, uploaded_by, uploaded_at |
| evidence_index | file_id, action_id, kpi_codigo, tension_codigo, indexed_at, tags JSONB |
FARO SCORE · versionado por período
Cada cierre de período genera un snapshot del FARO Score con sus 8 (o 10) componentes y los drivers que lo movieron. Permite reconstruir por qué el score cambió.
CREATE TABLE faro_score_snapshots ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), company_id UUID NOT NULL, period DATE NOT NULL, score_total NUMERIC(5,2) NOT NULL -- 0..100, nivel SMALLINT NOT NULL -- 1..5, banda TEXT NOT NULL -- 'Reactiva'..'Dirigida por sistema', drivers JSONB -- top movimientos vs período anterior, computed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (company_id, period) ); CREATE TABLE faro_score_components ( snapshot_id UUID NOT NULL REFERENCES faro_score_snapshots(id), dim_codigo TEXT NOT NULL -- 'DIM-01'..'DIM-10', valor NUMERIC(5,2) NOT NULL, peso NUMERIC(4,3) NOT NULL -- 0..1, contribucion NUMERIC(5,2) -- valor × peso, kpis_input JSONB -- {"KPI-001":74.5,...}, PRIMARY KEY (snapshot_id, dim_codigo) );
AI AUDIT · cada llamado a IA queda registrado
FARO usa IA explicativa (no decisional). Cada invocación: contexto, prompt, modelo, tokens, costo, latencia, respuesta. Trazabilidad completa para auditoría regulatoria.
| Tabla | Campos clave |
|---|---|
| ai_requests | id, company_id, contexto (tension_id/action_id/kpi_id), modelo, provider, prompt_hash, tokens_in, started_at |
| ai_responses | request_id, response_text, tokens_out, cost_usd, latency_ms, finish_reason, finished_at |
| audit_logs | id, company_id, user_id, action, resource_type, resource_id, before JSONB, after JSONB, ip, user_agent, occurred_at |
Funciones SQL del motor MVP
21 funciones faro.fn_* que orquestan el ciclo completo: helpers → validación → normalización → facts → KPIs → señales → alertas → tensiones → diagnóstico → acciones → Score → reporte.
Orden recomendado de ejecución
| # | Familia | Función / propósito |
|---|---|---|
| 1 | Helpers | fn_norm_text, fn_norm_code, fn_month_start |
| 2 | Seed MVP | fn_seed_mvp_definitions — carga KPIs, tensiones, acciones base |
| 3 | Validación | fn_validate_mvp_import — valida un raw_import |
| 4 | Normalización | fn_rebuild_masters_from_import — UPSERT clientes, productos, sucursales |
| 5 | Facts | fn_build_facts_from_import — staging → fact_sales/stock/receivables |
| 6 | KPIs | fn_calculate_kpis_mvp, fn_store_kpi_result, fn_get_kpi_value |
| 7 | Señales | fn_evaluate_signals — verde / ámbar / rojo por KPI |
| 8 | Alertas | fn_generate_alerts — emite alertas por umbral cruzado |
| 9 | Tensiones | fn_evaluate_tensions — corre lógica DSL contra alertas + KPIs |
| 10 | Diagnóstico | fn_diagnose, fn_recommend — diagnóstico ejecutivo y recomendaciones |
| 11 | Acciones | fn_create_suggested_actions — instancia ACT-NNN con RACI |
| 12 | FARO Score | fn_calculate_faro_score — recalcula score por período |
| 13 | Reporte | fn_build_executive_report — snapshot ejecutivo semanal |
| 14 | Orquestador | fn_run_mvp_cycle — corre el ciclo completo end-to-end |
Cada función es auditable, reutilizable y reentrante — diseñadas como bloques chicos para que el flujo se pueda parar y reanudar en cualquier capa. El orquestador fn_run_mvp_cycle ata todo en una sola invocación que se puede correr por cron diario.
SELECT faro.fn_run_mvp_cycle( p_company_id => '00000000-0000-0000-0000-000000000001'::uuid, p_period => '2026-05-01'::date, p_seed_mvp => true, -- carga definiciones base si faltan p_recompute_score => true ); -- Devuelve: jsonb con counts por capa, errores y duración por paso
Schemas pendientes · ops · releases · gov · ai
Estos schemas y tablas están definidos en los anexos de arquitectura (OPS-001, DEPLOY-001, GOV-001, AI-001) pero aún no se integraron al pack de migraciones V001-V031. Se sumarán en V032+ del próximo bloque. Aparecen aquí con badge pendiente para no perder trazabilidad.
ops.jobsops.job_runsops.pipeline_metricsfaro.releasesfaro.deploymentsfaro.feature_flagsaudit.data_access_logaudit.exports_logaudit.catalog_changesaudit.sessionsaudit.pii_consentsaudit.compliance_eventsai_requestsai_responsesai_promptsai_modelsai_costsai_feedbackNota: las tablas ai_requests, ai_responses y audit_logs ya están descriptas en Sección 14 a nivel conceptual. La integración formal vía migración Vxxx con DDL completo, RLS y seeds queda como deuda técnica explícita.
Descargas
Esquema completo + recursos relacionados. NDA aplica.
Vxxx__nombre.sql idempotentes aplicables en orden estricto sobre PostgreSQL 15+. Pendiente generación.faro.fn_* orquestadas en 14 pasos: validate, build_facts, calculate_kpis, evaluate_tensions, calculate_score, run_mvp_cycle.kpi_catalog. 400 KPIs con fórmula, umbrales, dimensión.tension_catalog. 300 reglas DSL evaluables.¿Querés validar el esquema con tu CTO?
Workshop técnico de 90 min: revisamos el DDL completo, escalabilidad, multitenancy, integraciones y plan de migración.
📅 Programar workshop técnico →