Ir al contenido

Modelo de datos

El modelo de datos de EMA Lab refleja la dualidad arquitectónica del producto. Lo clínico canónico —observaciones, reportes, especímenes, diagnósticos microbiológicos, antibiogramas— vive como recursos FHIR R4 en Aidbox con perfiles del CL Core y del EMA IG cuando aplican. Lo operacional —catálogos, configuración, QC, cultivos en seguimiento, inventario, conectores, no-conformidades, stewardship, billing— vive en Supabase con RLS estricta por tenant. La división es deliberada: forzar todo el estado a FHIR habría sido contraproducente para conceptos que el estándar no modela bien (workflows operacionales, configuración de equipos, retry queues), y forzar todo a Supabase habría sacrificado la portabilidad clínica.

EMA Lab opera con dos schemas dentro del proyecto emahub (bzikofdvfvchntvfzyni). El schema emashared agrupa lo transversal al ecosistema —tenants, users, configuración EMI, credenciales encriptadas de Aidbox—, y lo escriben tanto Vault durante el provisioning como cada producto cuando registra cambios. El schema emalab contiene las 62 tablas operacionales propias del LIS, todas con columna tenant_id y políticas RLS que aplican tenant_id = (select tenant_id from user_roles where user_id = auth.uid() and product = 'lab').

emashared (compartido cross-product):
tenants # uuid id, slug, country, status
tenant_products # tenants × products activos
tenant_aidbox_credentials # AES-GCM encrypted OAuth (S9)
tenant_emi_access # is_active, rate-limit
tenant_slug_history # rename audit
emi_apps # apps × tenant (lab='alpha' hasta Wave 5)
users # auth.users mirror + perfil
user_roles # user × tenant × product × role
user_client_access # filtro adicional para roles client-isolation
client_config # client externos (ej. clínicas que envían órdenes)
workplace_config # config por sede
emalab (62 tablas — Wave 3 v2.0):
Catálogo y profiles:
test_catalog # 50 tests LOINC seed, per-tenant
specimen_types # 6 (sangre, orina, etc.)
containers # 11 tipos de tubo / contenedor
lab_profiles # 5 perfiles (perfil bioquímico, hematológico, etc.)
lab_config # whitelabel, billing, thresholds, auto_consume_enabled
barcode_strategy # estrategia per-tenant Code 128B (L010)
label_templates # plantillas etiqueta tubo
specimen_label_map # mapping specimen → template
Órdenes y muestras:
routing_rules # reglas inter-sede (ADR-006)
transport_routes # rutas físicas
lab_sites # sedes del tenant
exclusions # tests excluidos por cliente
QC:
qc_lots # lots reactivos QC
qc_lot_targets # mean/SD por lot
qc_config # Westgard rules per-tenant
breakpoint_config # antibiograma breakpoints
Microbiología (Fase 4 + S4):
culture_tracking # 7 stages (ADR-020)
micro_organism_catalog # 80+ SNOMED seed (L052)
antibiotic_catalog # antibióticos
antibiogram_panels # 3 paneles seed
micro_config # timeouts, qualitative thresholds
Inventario:
reagent_catalog # reactivos master
reagent_inventory # lots con FIFO
reagent_consumption # historial consumo
Validación y autoverify:
validation_audit # cada decisión de cada stage
autoverify_conditions # reglas autoverify per-test
emi_rules # reglas EMI per-tenant
emi_section_config # config EMI por sección
tech_section_auth # auth TM por sección (L028)
Critical values:
critical_value_log # log con communication ack
critical_value_source # fuente del trigger (L047)
Billing y DTE:
billing_config # 7 cols DTE per-tenant (L026)
price_lists # listas precio per-cliente
test_prices # precio per-test per-list
invoices, invoice_lines # facturación
Conectores:
connector_subscriptions # FHIR Subs activos
connector_hl7v2_config # endpoints HL7v2 per-cliente
connector_log # log con duration_ms (L046)
connector_retry_queue # retry FHIR + HL7v2 outbound (L048)
hub_registry # tokens Hub on-prem
dlq_alert_log # Loops DLQ alerts con debounce 15min
Molecular (S5):
molecular_imports # cada importación parsed
molecular_critical_targets # targets críticos admin
molecular_review_entries # pending review queue
Stewardship (S5/S6):
stewardship_alerts # alertas generadas
stewardship_alert_config # config trimestral
stewardship_signers # multi-signer reorder/toggle (L075)
stewardship_reports # reportes generados
stewardship_templates # templates print
Equipos y mantenimiento:
maintenance_plan # plan preventivo
maintenance_history # ejecuciones
equipment_documents # certificados, manuales
sla_config # SLA per-cliente per-sección
No conformidades:
nonconformities # CLSI GP41 (16 razones, ADR-008)
detected_issue_emission # status emisión FHIR
Patient sequence:
patient_sequence # generador identifier MR per-tenant

El estado actual de migraciones aplicadas es 001 a 022, con la migración 010 deprecada y descartada. Todas están aplicadas en el hub de producción bzikofdvfvchntvfzyni (decisión L103). Las políticas RLS sobre cada tabla con tenant_id aplican tenant_id = (select tenant_id from user_roles where user_id = auth.uid() and product = 'lab'), lo que aísla cada laboratorio del resto y, dentro del ecosistema, distingue entre productos (un mismo usuario puede tener roles en Lab y Clinic con tenants distintos sin que las queries se mezclen).

tiene

ofrece

opera

negocia

valida

cultiva

firma

precio per-cliente

detalla

lista activa

mean/SD

from/to

panel aplicado

abx incluidos

stock per-lot

consumo por result

decisión per stage

result

informe firmado

tenants

uuid

id

PK

text

slug

text

country

text

status

user_roles

test_catalog

uuid

id

PK

uuid

tenant_id

FK

text

loinc_code

text

section

boolean

is_microbiology

lab_sites

price_lists

qc_lots

culture_tracking

uuid

id

PK

uuid

tenant_id

FK

uuid

service_request_id

text

stage

incubation · growth · organism · panel · final

timestamptz

timeout_at

stewardship_reports

uuid

id

PK

uuid

tenant_id

FK

text

period

Q1-2026

jsonb

signers

text

fhir_emission_status

test_prices

client_config

qc_lot_targets

routing_rules

antibiogram_panels

antibiotic_catalog

reagent_catalog

reagent_inventory

reagent_consumption

validation_audit

ServiceRequest

Observation

DiagnosticReport

EMA Lab trabaja con 29 recursos FHIR R4 distintos, cada uno con un perfil aplicado del CL Core o del EMA IG según corresponda. El SDK emafhir versión 0.6.0 se encarga de inyectar meta.tag = urn:ema:tenant|<tenantId> en cada recurso al crearlo o actualizarlo, lo que sostiene una de las capas del aislamiento. La adopción del helper createEmaClient es opt-in por handler (decisión L102), lo que permite migrar progresivamente sin romper los handlers que todavía usan el patrón anterior. Los value sets canónicos (LOINC para tests, SNOMED para microorganismos y patologías, ICD-10 para diagnósticos) viven en inglés según los emite cada organismo y se presentan con display localizado al idioma del tenant.

RecursoNotas
Patientidentifier MR per-tenant (sequence con Luhn), demografía e identificador nacional según país
ServiceRequestPer test (ADR-003), no per profile. code LOINC, subject, requester, performer, basedOn
Specimensubject, type, collection, container, accessionIdentifier
EncounterHeredado del workplace (ADR-004) — para LIS no se crea uno nuevo
Observationcode LOINC, value, referenceRange, interpretation, status (registered → preliminary → final → amended)
DiagnosticReportcode, subject, result[], presentedForm (PDF), status (partial → preliminary → final → amended)
ConditionPara microbiología — diagnóstico microbiológico
AllergyIntoleranceRead-only desde EHR cliente
RecursoNotas
DiagnosticReport (micro)partial → preliminary → final con cascada Observation
Observation (culture)parent observation con category=culture
Observation (organism)child con code SNOMED de organismo identificado
Observation (panel)child con SIR + MIC/zone como component[] (v3-ObservationInterpretation)
Task (critical)critical-value-notification para hemocultivo+ (L051)
RecursoNotas
TaskWorkflow tasks — Hub pull, retry, etc.
CommunicationCritical value notification con ack del MD
DocumentReferenceReportes en R2 (DOCS_BUCKET), categorías clinical-note/lab-report/stewardship/molecular
DetectedIssueStewardship alerts canonical FHIR (L071)
RecursoNotas
Observation (QC)category=qc, value comparado contra qc_lot_targets (Westgard 7-T L024)
ProvenanceQuién firmó qué — multi-signer stewardship (L075)
AuditEventInmutable — todo log clínico

nueva orden recepción

check-in muestra

en worklist técnica

DR firmado

anulación con razón

validación stage 4 hold

director firma

draft

active

in_progress

completed

revoked

on_hold

tubo etiquetado

check-in lab central

pre-análisis ok

rechazo (CLSI GP41 razón)

available

received

processed

unsatisfactory

micro etapa intermedia

ej. Gram + crecimiento

firma director / autoverify

corrección post-firma

orden anulada

registered

partial

preliminary

final

amended

cancelled

muestra sembrada

24-72h

MALDI-TOF / colonia

panel cargado

SIR results

DR final firmado

timeout (TM confirma manual)

contaminación

incubation

growth_detected

organism_identified

susceptibility_pending

susceptibility_complete

negative

cancelled

result ingresado

tech_section_auth ok

TM no autorizado

EMI ok / warn

EMI fail (hold)

cumple condiciones

no cumple

firma director

director descarta

stage_1_tech

stage_2_emi

rejected

stage_3_autoverify

stage_4_director

final

cron trimestral genera

dry-run ok (L072)

1+ firma

todos los signers (L075)

DocumentReference + DetectedIssue

anulación admin

reporte siguiente

draft

ready_for_signing

partially_signed

fully_signed

emitted

cancelled

superseded

Cada test es una ServiceRequest, no agrupados por profile. Razón: facturación, routing, validación y reportes son granular por test. Profiles son una agrupación visual en lab_profiles que expanden a múltiples ServiceRequests al crear orden.

EMA Lab no crea Encounters propios — hereda el Encounter del EHR que originó la orden (típicamente emaclinic). Si la orden viene sin Encounter, se asocia a workplace_config.default_encounter.

Code 128B con check digit Luhn — formato <8-char-ID><Luhn> sin prefijo de sede. Razón: muestras se mueven entre sedes (routing inter-sede) y un prefijo fijo crearía conflictos.

Comunicación de valores críticos modelada como Communication con ack del MD receptor — no como flag silenciosa. Permite trazar plazo de notificación regulatorio (Ley 20.584) + audit completo.

Las no-conformidades CLSI GP41 (16 razones) se modelan como DetectedIssue FHIR — permite query cross-product y agregación en reportes regulatorios.

CLSI M39 alerts viven en Supabase stewardship_alerts (primary, operacional) + se emiten como DetectedIssue FHIR (canonical). 3 columnas en Supabase tracking emisión: fhir_emission_status / _error / _at. Retry sweep cron */15min re-emite pendientes (L079).