Ir al contenido

Modelo de datos

El modelo de datos de Vault refleja el alcance del producto: cubre desde el pipeline comercial y los contratos hasta el catálogo de productos del ecosistema, la configuración fiscal por cliente, el audit cross-product y la matriz de acceso a EMI. Todo vive en Supabase repartido entre los schemas emashared (compartido con los demás productos) y emavault (propio). Los datos extraordinariamente sensibles —información clínica de pacientes— no están en Vault: viven en los productos respectivos (Clinic, Lab y, aislada por separado por requerimientos de protección de datos personales, Well).

EMA Vault usa dos schemas dentro del proyecto emahub (bzikofdvfvchntvfzyni):

  • emashared — datos transversales del ecosistema (tenants, users, EMI access, audit log).
  • emavault — datos operacionales del back-office EMA Health.

Adicionalmente conecta a un proyecto Supabase separado emawell (yvfxhrnghajwblyqtoel, sa-east-1) read-only por Ley 21.719 (datos de salud aislados).

Mismas tablas que ya documentamos en Lab/Clinic. Vault es el provisionador de algunas:

emashared:
tenants # Vault provisiona via /api/tenants/:id/provision
tenant_products # Vault asigna products al tenant
tenant_aidbox_credentials # Vault encripta AES-GCM y escribe (S9-3)
tenant_emi_access # Vault administra via /emi-access (F082)
tenant_slug_history # rename audit
emi_apps # status per app (alpha/beta/ga)
emi_invocations # audit log de cada call EMI (F080)
users + user_roles # cross-product
audit_log # destination del audit_trigger() de Vault

Tablas de operación interna:

emavault:
Vault users + permissions:
vault_users # auth.users mirror + role + is_active
vault_permissions # role × module × can_read × can_write
vault_team_members # alias / display info
Negocio · Pipeline:
leads # CRM kanban 7 etapas
lead_emi_conversations # link leads ↔ emi_invocations (F083)
assessments # Discovery-style assessments
organizations # cliente / proveedor / partner / institución
people # contactos × organizations
organization_products # qué products tiene cada org
Negocio · Catálogo y pricing:
catalog_services # servicios EMA
catalog_costs # costos por servicio
catalog_pricing # precios públicos
pricing_costs # pricing tool — costos
pricing_plans # planes
pricing_quotes # cotizaciones generadas
Finanzas:
finance_entries # multi-moneda
exchange_rates # CLP/USD/UF/EUR diarios
mrr_entries # MRR mensual UF
contracts # con alertas vencimiento
invoices # facturación interna
fiscal_config # per-organización (identificador tributario del país, certificado, ambiente fiscal, tipos de documentos)
dte_emissions_log # auditoría de emisiones electrónicas
obligations # pagos, declaraciones, alertas
Producto:
products # catálogo productos EMA + architecture_md
product_layers # capas (frontend/backend/db/etc.)
product_modules # módulos por producto
product_features # features por módulo
product_journeys # user journeys
product_journey_steps # pasos del journey
product_releases # releases con changelog_md
product_release_items # items por release
product_tech_stack # 6 categorías
github_issues_cache # sync horario + webhook
github_sync_log # log de syncs
Editorial:
editorial_posts # blog/LinkedIn drafts + métricas
Operaciones:
checklist_items # tareas operativas
time_entries # horas por proyecto
documents # repository ops
wiki_pages # docs internas
notifications # alertas in-app
infra_resources # CF Workers, dominios, secrets inventory
Quality module (2026-04-05):
product_quality # quality dashboard
product_security # security posture
product_metrics # KPIs producto
Brand:
brand_assets # logo, color tokens, etc.

tiene

firma

transa

aporta

configura

convierte de

usa

emite

via role

registra

asigna

owns

compone

agrupa

detalla

describe

pasos

versiona

detalla

usa

linked_issues

EMI badge

ref

organizations

uuid

id

PK

text

name

text

org_type

cliente · proveedor · partner · institución

text

country

text

preferred_currency

text

slug

subdominio para clientes

numeric

mrr_uf

people

contracts

finance_entries

mrr_entries

fiscal_config

leads

uuid

id

PK

text

source

int

stage

1-7

int

probability

auto by stage

text

status

activo · cerrado_ganado · cerrado_perdido

organization_products

dte_emissions_log

vault_users

uuid

id

PK

uuid

auth_id

FK

text

role

admin · technical · commercial · finance · viewer

bool

is_active

timestamptz

last_login

vault_permissions

text

role

text

module

bool

can_read

bool

can_write

time_entries

checklist_items

products

uuid

id

PK

text

slug

text

name

text

status

text

architecture_md

product_layers

product_modules

product_features

product_journeys

product_journey_steps

product_releases

product_release_items

product_tech_stack

github_issues_cache

lead_emi_conversations

emi_invocations

-- Mirror de auth.users con role y permisos
CREATE TABLE emavault.vault_users (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
auth_id uuid REFERENCES auth.users(id), -- vault_users.id = auth.uid()
email text UNIQUE NOT NULL,
full_name text,
role text CHECK (role IN ('admin','technical','commercial','finance','viewer')),
is_active bool DEFAULT true,
last_login timestamptz,
created_at timestamptz DEFAULT now()
);
-- Matriz role × module
CREATE TABLE emavault.vault_permissions (
role text NOT NULL,
module text NOT NULL, -- dashboard, catalog, finance, dte, etc.
can_read bool DEFAULT false,
can_write bool DEFAULT false,
PRIMARY KEY (role, module)
);

Vault es el escritor canonical de emashared.tenants desde /api/tenants/:id/provision:

  1. INSERT en Aidbox: crear Organization raíz.
  2. Aidbox: crear OAuth client + secret.
  3. AES-GCM encrypt + INSERT tenant_aidbox_credentials.
  4. POST X-Platform-Key a cada producto (clinic, lab, well) → INSERT emashared.tenants + tenant_products en cada uno.
  5. INSERT emashared.audit_log.
CREATE TABLE emashared.emi_invocations (
id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
user_id uuid,
app_slug text NOT NULL, -- 'emavault', 'emaclinic', 'emalab', etc.
agent_name text,
invocation_type text, -- 'chat', 'access-check'
request_tokens int,
response_tokens int,
duration_ms int,
lead_id uuid, -- F083 — link a leads.id si conversación nació allí
created_at timestamptz DEFAULT now()
);

emashared.audit_log es destination de:

  • Vault audit_trigger() SQL (F085) — cubre 12 tablas en emavault.
  • Loggers cross-product (clinic, lab, well, etc.) cuando aplique.
  • Operaciones manuales (/api/audit/*).

Sanitización: campos con nombres password|token|secret|api_key|private_key|aidbox_client_secret_encrypted se reemplazan por [REDACTED] antes de loggear.

lead capturado

fuente identificada

primer contacto

reunión agendada

propuesta enviada

feedback recibido

contrato firmado

descartado

convertido a cliente

nuevo

contactado

reunion

propuesta

negociacion

ganado

perdido

Probabilidad auto por etapa: 10% / 20% / 40% / 60% / 80% / 100% / 0%.

enviado al cliente

firmado

vigente

30d antes de end_date

(alerta)

renovado

end_date alcanzada

cancelado

draft

sent

signed

active

renewing

ended

cancelled

org creada como cliente

fiscal_config + slug ok

POST /provision

Org + OAuth creados

cross-product loops

todos los products ok

PATCH status=suspended

reactivar

cliente termina relación

error en algún step

corregir + retry

draft

ready_to_provision

provisioning

aidbox_done

products_assigning

active

suspended

archived

failed

release planeado

items en backlog

dev complete

deploy a prod

changelog_md publicado

bugs detectados

planning

in_dev

testing

released

Cada organization tipo cliente tiene un slug que define su subdominio:

slug = "aurora"
URL clinic = https://aurora.emahealth.io
URL lab = https://aurora.lab.emahealth.io
URL well = https://aurora.well.emahealth.io

tenant_slug_history registra cambios de slug (F074) — pre-rename sigue redireccionando 12 meses por compatibilidad.

audit_log no se restringe a Vault — clinic/lab/well también escriben ahí. Permite query unificado:

SELECT * FROM emashared.audit_log
WHERE tenant_id = '<id>' AND created_at >= now() - interval '7 days'
ORDER BY created_at DESC;

Cuando un lead inicia conversación con EMI desde el chatbot público:

  1. Lead se crea con source='emi_chat'.
  2. Cada call EMI se registra en emi_invocations con lead_id.
  3. Vault Pipeline muestra badge violeta en el lead.
  4. Click en badge expande “Conversaciones EMI” con todas las invocaciones (F083).