🔒 Modelo SQL MVP · v1.0  ·  NDA OBLIGATORIO  ·  NO redistribuir
← hub modelos
Modelo 07 · Capa de persistencia · v1.0

Modelo SQL del MVP

El esquema PostgreSQL que sostiene FARO Connect en producción. 12 capas desde RAW inmutable hasta FARO Score versionado. Multiempresa desde el día 1 con company_id en toda tabla operativa.

12
Capas
31
Migraciones V001-V031
80+
Tablas + 21 funciones
5
Schemas (faro · audit · ops · raw · staging)
PG 15+
PostgreSQL
DDL
Descargable
Sección 01

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ónCriterio
BasePostgreSQL 15+ · ACID, JSONB nativo, extensiones para auditoría
IDsUUID v4 en todas las tablas operativas. Permite federación y reduce colisiones.
Multiempresacompany_id UUID NOT NULL en toda tabla operativa. Row-Level Security activa.
TrazabilidadRAW nunca se modifica. Cada fila tiene row_hash SHA-256 inmutable.
IAAuditada por ai_requests + ai_responses con modelo, tokens, costo, latencia.
ScoreGuardado por período + componente + drivers en faro_score_snapshots.
AccionesSIEMPRE con responsable_id, vence_at y evidencia_id al cerrar.
AuditoríaTabla transversal audit_logs con quién, qué, cuándo y de dónde (IP/agent).
MVPVentas, stock, cobranza, KPIs, alertas, tensiones, acciones, evidencia, Score. v2: compras, gastos, RRHH, proyectos, contabilidad. v3: integraciones nativas con ERPs comunes.
Sección 01B · Migraciones canónicas

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.

V001 — V012 · 12 migraciones
Bloque A · Base MVP
Extensiones, schemas 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.
V013 — V023 · 11 migraciones
Bloque B · Multiempresa + RLS
Funciones de motor MVP (KPI), funciones de security context, roles de aplicación + grants, seed roles y permissions, RLS habilitado en tablas core, políticas RLS por capa (org/sources/raw, master/staging/facts, KPIs/rules/tensiones/acciones, score/reports), vistas seguras y test multi-tenant. Fuente: FARO-SQL-002.
V024 — V031 · 8 migraciones
Bloque C · Seeds + Catálogos canónicos
Seed Empresa Demo, catálogo canónico de tensiones (tabla + seed), patch de alineación demo↔canónicas, catálogo canónico de acciones (tabla + seed), catálogo canónico de evidencias (tabla + seed). Fuentes: FARO-SQL-003, 003.1, 004, 005, 006.

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'
);
Sección 02

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.

CORE
Multitenancy, usuarios, roles, permisos, sucursales.
companiesusersrolesbranches
INGESTA / RAW
Archivos crudos inmutables con hash y linaje.
raw_importsraw_rowsraw_errors
STAGING
Datos normalizados por dominio (post-alias, post-validación).
staging_salesstaging_stockstaging_receivables
MAESTROS
Catálogos vivos: clientes, productos, vendedores, proveedores.
master_customersmaster_productsmaster_sellers
FACTS
Hechos consolidados listos para query analítica.
fact_salesfact_stockfact_receivables
KPIs
400 KPIs canónicos + sus cálculos por período.
kpi_catalogkpi_calculations
ALERTAS
KPIs que cruzaron umbral (verde→ámbar→rojo).
alertsalert_dispatches
TENSIONES
300 tensiones canónicas + detecciones activas con KPIs origen.
tension_catalogtension_detections
ACCIONES
300 acciones canónicas + instancias con RACI, SLA, workflow.
action_catalogaction_instancesaction_workflow_log
EVIDENCIA
Archivos cargados al cerrar acciones (actas, planes, conciliaciones).
evidence_filesevidence_index
FARO SCORE
Score versionado por período + componentes + drivers.
faro_score_snapshotsfaro_score_components
AI AUDIT
Toda invocación a IA con modelo, tokens, costo, prompt, respuesta.
ai_requestsai_responsesaudit_logs

🗄️ Diagrama visual · 12 capas + ~50 tablas + flujo de datos

CAPA 1 · CORE companies users roles · permissions branches CAPAS 2-5 · DATOS 📥 RAW (inmutable) raw_imports · raw_rows raw_errors · row_hash SHA-256 ⚙️ STAGING staging_sales · staging_stock staging_receivables 🔗 MAESTROS master_customers · master_products master_sellers · master_branches 📊 FACTS (particionadas) fact_sales · fact_stock fact_receivables (by period) CAPAS 6-9 · INTEL 📈 KPIs kpi_catalog (400) kpi_calculations 🚨 ALERTAS alerts · dispatches ⚡ TENSIONES tension_catalog (300) tension_detections 🎯 ACCIONES action_catalog (300) action_instances workflow_log CAPAS 10-12 · CIERRE 📋 EVIDENCIA evidence_files evidence_index 🧭 FARO SCORE score_snapshots score_components DIM-01..10 versionado 🤖 AI AUDIT ai_requests + responses audit_logs (all CRUD) Todas las tablas tienen: company_id UUID NOT NULL · row_hash SHA-256 · ingested_at TIMESTAMPTZ · Row-Level Security activa ~50 tablas · 21 funciones faro.fn_* · Orquestador único fn_run_mvp_cycle() para correr el pipeline completo end-to-end

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.

Sección 03

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
Sección 04 · Capa 1

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.

TablaPKCampos clave
companiesid UUIDname, slug, plan, status, settings JSONB
usersid UUIDemail, password_hash, company_id, last_login_at
rolesid UUIDname (admin/director/gerente/operativo), company_id
permissionsid UUIDresource, action (kpi.read, action.close, etc.)
role_permissionscompuestarole_id, permission_id
user_rolescompuestauser_id, role_id, branch_id (opcional)
branchesid UUIDname, code, company_id, address JSONB
Sección 05 · Capa 2

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.

DDL · raw_imports + raw_rows
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);
Sección 06 · Capa 3

STAGING · normalización por dominio

Tipos coercionados, alias resueltos al canónico, validaciones aplicadas. Mantiene raw_row_id como FK para linaje.

DDL · staging_sales (ejemplo)
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
);
Sección 07 · Capa 4

MAESTROS · entidades vivas

Clientes, productos, vendedores se actualizan por UPSERT desde staging. Trackean primera/última aparición y agregados básicos.

TablaCampos clave
master_customerscustomer_id, nombre, segmento, fecha_alta, ltv_pct, mora_max_dias
master_productssku, nombre, categoria_abc, costo_promedio, margen_promedio, rotacion
master_sellersseller_id, nombre, branch_id, ventas_ytd, comision_acumulada
Sección 08 · Capa 5

FACTS · hechos analíticos

Particionadas por fecha. Indexadas por company_id + period. Optimizadas para queries de KPI.

DDL · fact_sales (particionada)
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);
Sección 09 · Capa 6

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.

DDL · kpi_catalog + kpi_calculations
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)
);
Sección 10 · Capa 8

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.

DDL · tension_catalog + tension_detections
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)
);
Sección 11 · Capa 9

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.

DDL · action_instances + action_workflow_log
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
);
Sección 12 · Capa 10

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.

TablaCampos clave
evidence_filesid, company_id, evd_tipo (EVD-001..012), s3_key, filename, mime, size, sha256, uploaded_by, uploaded_at
evidence_indexfile_id, action_id, kpi_codigo, tension_codigo, indexed_at, tags JSONB
Sección 13 · Capa 11

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ó.

DDL · faro_score_snapshots + components
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)
);
Sección 14 · Capa 12

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.

TablaCampos clave
ai_requestsid, company_id, contexto (tension_id/action_id/kpi_id), modelo, provider, prompt_hash, tokens_in, started_at
ai_responsesrequest_id, response_text, tokens_out, cost_usd, latency_ms, finish_reason, finished_at
audit_logsid, company_id, user_id, action, resource_type, resource_id, before JSONB, after JSONB, ip, user_agent, occurred_at
Sección 14B

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

#FamiliaFunción / propósito
1Helpersfn_norm_text, fn_norm_code, fn_month_start
2Seed MVPfn_seed_mvp_definitions — carga KPIs, tensiones, acciones base
3Validaciónfn_validate_mvp_import — valida un raw_import
4Normalizaciónfn_rebuild_masters_from_import — UPSERT clientes, productos, sucursales
5Factsfn_build_facts_from_import — staging → fact_sales/stock/receivables
6KPIsfn_calculate_kpis_mvp, fn_store_kpi_result, fn_get_kpi_value
7Señalesfn_evaluate_signals — verde / ámbar / rojo por KPI
8Alertasfn_generate_alerts — emite alertas por umbral cruzado
9Tensionesfn_evaluate_tensions — corre lógica DSL contra alertas + KPIs
10Diagnósticofn_diagnose, fn_recommend — diagnóstico ejecutivo y recomendaciones
11Accionesfn_create_suggested_actions — instancia ACT-NNN con RACI
12FARO Scorefn_calculate_faro_score — recalcula score por período
13Reportefn_build_executive_report — snapshot ejecutivo semanal
14Orquestadorfn_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.

Ejemplo · Orquestador del ciclo MVP
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
Sección 14C · Pendiente integración

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.

schema ops
pendiente · OPS-001
Operaciones internas del producto: jobs, ejecuciones, métricas de pipeline.
ops.jobsops.job_runsops.pipeline_metrics
faro.releases / deployments
pendiente · DEPLOY-001
Versionado de releases, deployments por ambiente y feature flags por tenant.
faro.releasesfaro.deploymentsfaro.feature_flags
audit (extendido)
pendiente · GOV-001
Gobierno extendido: acceso a datos sensibles, exportaciones, cambios de catálogo canónico, sesiones, consentimientos PII.
audit.data_access_logaudit.exports_logaudit.catalog_changesaudit.sessionsaudit.pii_consentsaudit.compliance_events
faro.ai_*
pendiente · AI-001
Trazabilidad IA completa: requests, responses, prompts versionados, modelos, costos, feedback de usuario y evaluaciones.
ai_requestsai_responsesai_promptsai_modelsai_costsai_feedback

Nota: 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.

Sección 15

Descargas

Esquema completo + recursos relacionados. NDA aplica.

¿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 →