Saltar al contenido principal

ProgramaMed → módulo de salud de Xenpia

Parte I — análisis funcional del sistema incumbente. Parte II (§7 en adelante) — especificación de lo que se implementa en Xenpia.

Parte I — Análisis funcional de ProgramaMed

Estudio del sistema incumbente usado por Manta Hospital Center, hecho para extraer requisitos del módulo de salud de Xenpia. Fecha del estudio: 2026-08-18. Sesión analizada: consultorio /16/, perfil médico (Traumatología).

Nota: es un sistema en producción con datos reales de pacientes. Este documento sólo describe estructura y funcionalidad; no contiene datos de pacientes.

1. Arquitectura observada​

Stack legacy: PHP server-rendered + jQuery 3.2 + Bootstrap 4 + CKEditor, AJAX contra endpoints que devuelven fragmentos HTML (no JSON). Sin API pública.

  • URL base multi-tenant por número: programamed.com/{idConsultorio}/web/...
  • Los IDs de recurso viajan en base64 en la URL (/historia/clinica/ODYyMDk=/). No hay control de acceso visible más allá de la sesión → IDOR probable.
  • Auth por sesión PHP, contraseña hasheada en cliente con sha256.pack.js.

Rutas principales:

RutaMódulo
/web/agenda/Dashboard (5 tiles)
/web/agenda/paciente/Pacientes
/web/agenda/citas/Agenda de citas
/web/listaCitasMedicas/{idMedico}/{nombres}/{apellidos}Consultas del día
/web/historia/clinica/{base64 idPaciente}/Historia clínica
/web/agenda/reportes/Reportes
/web/agenda/documentos/Documentos y plantillas

2. Módulo Agenda de Citas​

  • Selector año → mes → día (grid de días como botones), no calendario mensual.
  • Horarios de atención configurables por médico y día de la semana (Lunes 15:00–19:00, Miércoles 9:00–11:00, …), editables en modal (btnEditarHorario / btnGuardarHorario).
  • La agenda del día se genera en slots de 15 minutos derivados del horario configurado. Cada slot muestra PACIENTE - Especialidad o queda vacío (clickable para agendar).
  • Cita = { médico, especialidad, paciente, fecha, hora, observación }. El slot lleva data-tel, data-cel, data-mail, data-obs → contacto a un click.
  • Buscar paciente inline; si no existe, modal de alta rápida de paciente sin salir de la agenda.
  • Eliminar Cita y Imprimir Agenda (listado del día para impresión).
  • Filtro por médico (cmbMedicos) y especialidad (cmbEspecialidades) → un usuario puede ver agendas de varios médicos.

Qué copiar para Xenpia​

  • Configuración de horario de atención por día de semana → generación automática de slots.
  • Impresión de la agenda del día.
  • Alta de paciente en el mismo flujo de agendamiento.
  • Acciones de contacto (tel/cel/mail) embebidas en el slot — encaja directo con los canales de Xenpia (WhatsApp / email) para recordatorios.

3. Módulo Pacientes​

Tabla paginada con búsqueda, columnas: Nombre, Cédula, Género, Edad, Perfil (Completo/Incompleto), Ocupación, Opciones (Editar, Historia Clínica).

Ficha de paciente:

inputCedula, cmbTipoIdentificacion (Cédula|RUC|Pasaporte),
inputNombre, inputApellido, inputFechaNacimiento, cmbGenero,
inputAseguradora,
cmbProvincia → cmbCanton → cmbParroquia (cascada geográfica de Ecuador)
cmbEstadoCivil, inputDireccion, inputSector, inputEmail,
inputCelular, inputTelefono, cmbOcupacion, inputContacto (emergencia)

Notas: catálogo geográfico completo de Ecuador en cascada; el campo Aseguradora es texto libre (debería ser catálogo); Perfil incompleto es un indicador de calidad de dato muy útil.

4. Módulo Consulta / Historia Clínica​

4.a Pantalla de consulta finalizada (observada en vivo)​

URL: /16/web/finConsultaMedica/{especialidad}/{idConsulta}/{idMedico}/{idPaciente}/{estado}/{nombres}/{apellidos}/{idMedico}

  • El estado viaja en la URL (Finalizada) → hay ciclo de vida de la consulta.
  • El nombre del médico va en el path → queda en logs, historial y referrers. No replicar.
  • Cabecera fija de contexto: Consulta: {especialidad} | Paciente | Edad | Ciudad.
  • Secciones de la HC como pestañas superiores: Signos Vitales · Antecedentes Familiares · Antecedentes Personales (con submenú) · Órganos y Sistemas · Examen Físico.
  • Segundo nivel de pestañas: Consulta | {Especialidad} → además de la ficha común hay una ficha propia de cada especialidad. Es el hallazgo estructural más importante: el modelo no es un formulario único de consulta, es núcleo común + formulario por especialidad.
  • Cuerpo de la pestaña Consulta, en orden: Motivo de Consulta · Enfermedad o Problema Actual · tabla Diagnóstico | CIE-10 | Tipo · Planes · Observaciones · Pedidos. Pedidos es el contenedor único de medicamentos + imagen + laboratorio + complementarios.
  • La vista finalizada es sólo lectura, con un único botón Imprimir (→ PDF) y Regresar.

Modelado a tener en cuenta: el texto del diagnóstico y el código CIE-10 son campos independientes. El médico redacta su propio texto y le asocia un código cuyo descriptor oficial puede decir otra cosa. Modelo correcto: diagnostico_texto (libre) + cie10_id (catálogo) + tipo (Presuntivo | Definitivo). No derivar el texto del código.

4.b El PDF de historia clínica (observado en vivo)​

  • Nombre de archivo: {cedula}-HistoriaClinica.pdf → PHI en el nombre del archivo, que después vive en Descargas, WhatsApp y correo. No replicar.

  • ~3 páginas: ficha estructurada, lámina anatómica (esqueleto) de la ficha de especialidad, y el relato clínico.

  • Los informes de imagenología (RX, RNM) están pegados como texto libre dentro de la historia: párrafo corrido con técnica, hallazgos, impresión diagnóstica, nombre y registro MSP del radiólogo y la leyenda de validación electrónica. No hay adjuntos ni resultados estructurados.

    → Hueco claro del sistema y oportunidad para Xenpia: resultados/adjuntos como entidad propia (archivo PDF/imagen/DICOM + estudio + emisor + fecha + estado de validación), no texto pegado.

4.c Modelo interno​

(Reconstruido desde /16/web/js/consulta.js y historia_clinica.js.)

Una consulta (idResumenHistoria) agrupa cinco colecciones, cada una con su propio get / create / delete vía AJAX:

ColecciónCampos
DiagnósticosidCieDiez (catálogo CIE-10 con autocomplete), idTipoDiagnostico (presuntivo/definitivo), texto libre
Medicamentos (receta)medicamento (catálogo o texto libre que se agrega al catálogo), cantidad, indicaciones, tipoPedido: "CITA"
Pedidos de imagenimagen (catálogo o texto libre)
Pedidos de laboratoriolaboratorio (catálogo o texto libre)
Exámenes complementariosexamCompl (texto libre)

Patrón clave: los combos de medicamento/imagen/laboratorio aceptan valor nuevo; al guardarlo se refresca el catálogo (if(isNaN(x)) getCmbX()), es decir el catálogo crece con el uso del médico. Muy buen patrón a replicar.

Historia clínica del paciente = catálogo paginado de consultas previas con un checkbox por consulta; se seleccionan varias y btnHistorial navega a data-pdf + "/" + btoa(lista_de_ids) → PDF consolidado de las consultas elegidas.

Qué copiar para Xenpia​

  • Modelo consulta → N diagnósticos CIE-10 + N medicamentos + N pedidos (imagen/lab/complementarios).
  • Catálogos auto-alimentados por el médico.
  • Selección múltiple de consultas → un solo PDF de historia clínica.
  • Receta impresa = subconjunto (medicamentos + indicaciones) del mismo resumen de consulta.

5. Módulo Documentos — el hallazgo más valioso​

Dos familias distintas. La propia grilla de /agenda/documentos/ las separa visualmente en dos columnas y las distingue por el ícono: los seis de la izquierda llevan ícono de "documento" (plantilla editable con tokens) y los seis de la derecha ícono de "portapapeles" (formulario estructurado, sólo rellenable). Para Xenpia son dos features distintas: un motor de plantillas por un lado, formularios versionados de sistema por el otro.

5.a Plantillas editables con tokens (el patrón a copiar)​

Certificado Médico, Certificado Ocupacional, Certificado Población general, Certificado de Movilidad, Orden de Ingreso, C.I. Hiperbárica.

Cada una es:

  1. Un textarea con CKEditor (txtCertificado) que guarda HTML — la plantilla, propia de cada médico (cmbMedico + cmbEspecialidades).
  2. Un panel de tokens PROM* que el médico debe conservar al editar.
  3. Un formulario con los campos variables de esa emisión.
  4. btnEdit (editar plantilla) / btnGuardar* (guardar plantilla) / btnImprimir (emitir).

Diccionario de tokens observado:

TokenSignificado
PROMDOCTORNombre del médico que certifica
PROMESPECIALIDADEspecialidad del médico
PROMCONSULTORIOConsultorio / clínica / hospital
PROMPACIENTENombre del paciente
PROMCEDPACIENTECédula del paciente
PROMEDADEdad del paciente
PROMNACIONALIDADNacionalidad del paciente
PROMCAUSAEnfermedad / problema
PROMDIAGNOSTICODiagnóstico / examen realizado
PROMACTIVIDADESActividades laborales (cert. ocupacional)
PROMDESDE / PROMHASTARango de reposo
PROMFECHA / PROMPFECHAFecha de emisión
PROMDESTINATARIOPersona/entidad destinataria
PROMEXAMENExamen a realizar
PROMINGRESOF / PROMHORAIFecha/hora de ingreso
PROMCIRFECHA / PROMCIRUGIAHFecha/hora de cirugía
PROMOVILF / PROMDESTINO / PROMOTIVOMovilidad: fecha, destino, motivo
PROMACOMPA / PROMCEDACOMPAAcompañante y su cédula
PROMREGIONESListado de regiones anatómicas

Campos por documento:

  • Certificado Médico: paciente, causa, fecha, desde, hasta.
  • Certificado Ocupacional: paciente, diagnóstico, actividad, edad, nacionalidad, fecha.
  • Certificado Población general: paciente, diagnóstico, edad, nacionalidad, fecha.
  • Certificado de Movilidad: paciente + cédula, fecha emisión, diagnóstico, acompañante + cédula, destino, destinatario, fecha de movilidad, motivo, placa del vehículo.
  • Orden de Ingreso: paciente, destinatario, fecha emisión, diagnóstico, examen, fecha/hora de ingreso, fecha/hora de cirugía.
  • C.I. Hiperbárica: paciente + cédula, médico, regiones anatómicas.

Debilidad detectada: la plantilla es HTML libre con font-family y font-size inline y texto fijo mezclado con tokens (se vio texto de un caso concreto quemado dentro de la plantilla). Xenpia debe separar plantilla ↔ datos de forma estricta.

5.b Formularios oficiales MSP Ecuador (estructura fija)​

Listado paginado por médico + botón "Nuevo"; la URL del nuevo es .../formularioNNN/{btoa(idMedico)}.

  • DNEAIS-HCU-FORM.053 — Referencia, derivación, contrarreferencia y referencia inversa. ~85 campos: datos del paciente (apellidos paterno/materno, nombres, fecha nacimiento desglosada d/m/a, edad, sexo, nacionalidad, país, DNI, provincia/cantón/parroquia, dirección, teléfono), tipo (Referencia | Derivación | Contrarreferencia | Referencia inversa), entidad/establecimiento/servicio/especialidad remitente y receptor, motivo (checkboxes: limitada capacidad resolutiva, ausencia temporal del profesional, falta de profesional, saturación de capacidad instalada, otros), resumen clínico, hallazgos relevantes, diagnóstico + CIE-10 + pre/def, profesional + código MSP. Bloque espejo *2 para la contrarreferencia.
  • DNEAIS-HCU-FORM.024 — Consentimiento Informado. Servicio, tipo de atención (Ambulatoria|Hospitalización), CIE-10, procedimiento, detalle, forma de realización, gráfico adjunto (flGrafico), duración, beneficios, riesgos leves, riesgos graves, otros riesgos, alternativas, cuidados después, consecuencias de no realizarlo, aceptación, representante legal (nombre/cédula/parentesco) en 3 variantes (acepta / no acepta + testigo / revocatoria).
  • Informe Nutricional — antecedentes (clínicos, alergias, quirúrgicos, pérdida de peso), síntomas gastrointestinales, hábitos (apetito, cambio de alimentos, intolerancias, agua, sal, azúcar, aversiones, alcohol, tabaco, drogas, café, actividad física), antropometría (peso, talla, peso ajustado, IMC, músculo, ganancia último año), ingesta habitual (desayuno / media mañana / almuerzo / media tarde / cena), diagnóstico, recomendaciones, plan.
  • Informe de Psicología — antecedentes personales y familiares, psicobiografía por etapas (niñez, adolescencia, adultez, escolar, laboral, sentimental, personalidad), motivo de consulta, enfermedad actual, examen psicopatológico, tests aplicados, impresión diagnóstica, análisis, plan.
  • Indicaciones de seguridad — checklist prequirúrgico (sin gel/fijador, sin cremas, sin perfume, sin uñas pintadas, sin maquillaje, sin joyas/piercing, sin prótesis, sin lentes de contacto, sin toallas/pañales) + observación.
  • Prescripción de ejercicio — tabla por mesociclo/día: estiramiento (tipo, tiempo), series, aeróbico (tiempo, tipo, FC objetivo), estiramiento continuo, total.

6. Módulo Reportes​

Radio de tipo de reporte + filtro por año, resultados en tabla con grupos etarios 0-5 / 6-14 / 15-19 / 20-44 / 45-64 / 65+ (grupos oficiales MSP):

  1. Por género del paciente
  2. Fecha de registro de pacientes
  3. Ubicación de pacientes
  4. Aseguradora de pacientes
  5. Estado civil y edad
  6. Total de pacientes
  7. CIE-10 atendidos
  8. Medicamentos recetados
  9. Por motivo de consulta

Parte II — Especificación de implementación en Xenpia

Esto ya no es análisis: es la referencia de lo que se construye. ProgramaMed es el piso, no el techo. Cada decisión de abajo o mejora algo que ellos hacen mal, o agrega algo que ellos no tienen.

7. Principios: qué NO copiamos​

ProgramaMedXenpiaPor qué
La plantilla se edita in situ; al cambiarla, los certificados viejos ya no se pueden reproducirPlantillas versionadas; el documento emitido apunta a la versión exacta con la que se generóUn certificado es un instrumento legal: hay que poder reimprimirlo idéntico en 3 años
El documento se reimprime desde la plantilla actual cada vezEl documento se emite una vez, se congela (HTML + PDF + hash) y después sólo se reimprime o se anulaReimprimir ≠ regenerar. Si el texto cambia entre dos impresiones, no es el mismo documento
Tokens PROMPACIENTE en texto plano; si el médico borra una letra, se rompe en silencioTokens tipados y namespaced ({{paciente.nombre}}), insertados por botón como bloque atómico y validados al guardarEl médico no debería poder romper su plantilla escribiendo
Plantilla propia de cada médico, creada desde ceroPlantillas de sistema de referencia (semilla) → el médico las clona y modifica; la de sistema queda intactaUn médico nuevo arranca funcionando el día uno
PDF llamado {cédula}-HistoriaClinica.pdfNombre sin PHI (documento-{folio}.pdf) y descarga por URL firmada de corta vidaEse archivo termina en Descargas, WhatsApp y correo
Nombre del paciente y del médico en la URLSólo UUIDs opacos; el estado no viaja en el pathLas URLs quedan en logs, historial y referrers
btoa(id) como "seguridad"UUID + RLS por tenant + RPC auditadabase64 no es control de acceso
Sólo imprimirImprimir + enviar por WhatsApp/email desde la misma pantallaXenpia ya tiene los canales. Es el diferenciador más barato y más visible
Nada verifica que un certificado sea auténticoFolio + QR que resuelve a una página pública de verificaciónLos certificados de reposo se falsifican; esto lo cierra
Informes de imagen pegados como texto en la historiaAdjuntos/resultados como entidad propiaVer §4.b

8. Modelo de datos​

Migración nueva: supabase/migrations/2026XXXX_health_documents.sql. Sigue la regla de la plataforma que ya está en 20260815_health_clinical_records.sql: el módulo crea sus tablas y no toca las del core, no lleva política de SELECT, y todo el acceso pasa por funciones health_* que comprueban permiso y escriben en audit_logs en la misma transacción.

8.1 Catálogo de medicamentos​

health_medications (
id, tenant_id, name, presentation, concentration,
atc_code text, -- opcional, para normalizar contra catálogo oficial
is_active boolean,
usage_count int, -- ordena el autocomplete por lo que el médico más receta
created_by, created_at
)
UNIQUE (tenant_id, lower(name), lower(coalesce(presentation,'')))

Se auto-alimenta como en ProgramaMed (§4.c): si el médico escribe algo que no está, se crea. El plus: el UNIQUE sobre texto normalizado evita el basural de duplicados que ellos tienen, y usage_count hace que el autocomplete sea útil desde la tercera consulta.

8.2 Receta​

health_prescriptions (
id, tenant_id, encounter_id → clinical_encounters,
patient_id → health_patients,
issued_by → profiles, issued_at,
notes text,
document_id → health_documents -- el PDF emitido, cuando se imprime
)

health_prescription_items (
id, prescription_id, medication_id → health_medications,
name_snapshot text NOT NULL, -- congela el nombre por si el catálogo cambia
dose text, quantity text, frequency text, duration text,
instructions text,
sort int
)

name_snapshot es deliberado: si mañana alguien corrige el nombre en el catálogo, la receta emitida no debe mutar.

El plus sobre ProgramaMed: ellos tienen cantidad + indicaciones en un solo campo de texto. Nosotros separamos dosis / cantidad / frecuencia / duración, lo que permite después: alertas de interacción, recordatorio de toma por WhatsApp, y el reporte de medicamentos recetados con datos reales en vez de strings libres.

8.3 Plantillas de documentos​

health_document_templates (
id, tenant_id, -- NULL ⇒ plantilla de sistema (semilla, global)
code text, -- 'certificado_medico', 'receta', 'orden_ingreso', …
name text,
scope text, -- 'system' | 'tenant' | 'professional'
owner_profile_id → profiles,-- sólo si scope='professional'
parent_template_id → self, -- de qué plantilla se clonó
is_active boolean,
created_by, created_at
)

health_document_template_versions (
id, template_id, version int,
body_html text, -- HTML con tokens {{...}}
page_config jsonb, -- tamaño, márgenes, si lleva membrete/pie del tenant
created_by, created_at,
UNIQUE (template_id, version)
)

Editar una plantilla nunca hace UPDATE del body: inserta una versión nueva. Resolución de plantilla al emitir: professional → tenant → system. Así el médico que no configuró nada igual emite un certificado correcto.

8.4 Documentos emitidos​

health_documents (
id, tenant_id, patient_id, encounter_id (nullable),
type text, -- mismo dominio que templates.code
template_version_id → health_document_template_versions,
folio text, -- correlativo por tenant y tipo, visible al paciente
field_data jsonb, -- los valores variables de ESTA emisión
rendered_html text, -- resultado congelado de la sustitución
storage_path text, -- PDF en Supabase Storage, bucket privado
content_hash text, -- sha256 del PDF, para la verificación por QR
status text, -- 'issued' | 'voided'
voided_reason text, voided_by, voided_at,
issued_by → profiles, issued_at,
UNIQUE (tenant_id, type, folio)
)

health_document_deliveries (
id, document_id, channel text, -- 'whatsapp' | 'email' | 'download' | 'print'
destination text, status text, error text,
sent_by, sent_at
)

health_document_deliveries es lo que ProgramaMed no puede tener: trazabilidad de a quién se le entregó el certificado, por qué canal y cuándo. Reutiliza los canales que ya existen en backend/src/channels/.

8.5 Adjuntos y resultados​

health_attachments (
id, tenant_id, patient_id, encounter_id (nullable),
kind text, -- 'imagen' | 'laboratorio' | 'informe' | 'otro'
study_name text, performed_at date,
issuer_name text, issuer_license text, -- radiólogo + registro MSP
storage_path text, mime_type text, size_bytes int,
extracted_text text, -- para búsqueda; NO es el dato primario
uploaded_by, created_at
)

Cierra el hueco de §4.b: el informe de RNM deja de ser un párrafo pegado en la historia y pasa a ser un archivo con emisor, fecha y estudio.

9. Motor de plantillas​

9.1 Tokens​

Se declaran en código, no en la base, en shared/src/types.ts. Un token es:

type DocumentToken = {
key: string; // 'paciente.nombre'
label: string; // 'Nombre del paciente'
source: 'patient' | 'professional' | 'tenant' | 'encounter' | 'field';
type: 'text' | 'date' | 'number' | 'list';
required?: boolean;
};

Cada type de documento declara su set de tokens. De ahí salen tres cosas gratis:

  1. El botón de insertar token en el editor (agrupado por origen).
  2. La validación al guardar: token desconocido → error; token requerido ausente → aviso.
  3. El formulario de emisión: los tokens de source: 'field' generan automáticamente los inputs que el médico debe llenar. En ProgramaMed ese formulario está hardcodeado por documento; acá se deriva.

Equivalencia con el diccionario de ProgramaMed (§5.a), para migrar sus plantillas:

ProgramaMedXenpia
PROMDOCTOR{{profesional.nombre}}
PROMESPECIALIDAD{{profesional.especialidad}}
PROMCONSULTORIO{{tenant.nombre}}
PROMPACIENTE{{paciente.nombre}}
PROMCEDPACIENTE{{paciente.documento}}
PROMEDAD{{paciente.edad}}
PROMNACIONALIDAD{{paciente.nacionalidad}}
PROMCAUSA{{campo.causa}}
PROMDIAGNOSTICO{{campo.diagnostico}}
PROMDESDE / PROMHASTA{{campo.reposo_desde}} / {{campo.reposo_hasta}}
PROMFECHA{{documento.fecha_emision}}
PROMDESTINATARIO{{campo.destinatario}}

Y tokens nuevos que ellos no tienen: {{documento.folio}}, {{documento.qr}}, {{profesional.registro}}, {{profesional.firma}}, {{tenant.membrete}}.

9.2 Editor​

MUI v7 + editor rich-text. Reglas:

  • El token se inserta como nodo atómico (chip), no como texto suelto. No se puede editar por dentro; se borra entero. Esto solo ya elimina el modo de fallo principal de ProgramaMed.
  • Panel lateral con los tokens disponibles, agrupados y con búsqueda.
  • Vista previa en vivo con datos de ejemplo, en el tamaño de página real.
  • Botón "Restaurar plantilla de referencia" → vuelve a clonar de la de sistema.
  • Sin control de fuente ni tamaño arbitrario: la tipografía viene del page_config del tenant. ProgramaMed deja font-family inline y por eso sus plantillas se ven distintas entre sí.

9.3 Render​

Server-side en NestJS (backend/src/modules/health/documents/):

  1. Resolver plantilla (professional → tenant → system) y tomar la última versión activa.
  2. Construir el contexto: paciente, profesional, tenant, encuentro, campos del formulario.
  3. Sustituir tokens sobre HTML escapando el valor (los datos del paciente son entrada de usuario: sin escapar hay inyección en el PDF).
  4. Render a PDF, guardar en Storage, calcular content_hash, asignar folio.
  5. Insertar health_documents con status='issued'. Desde ese momento es inmutable.

10. Recetas imprimibles​

Pantalla: dentro del encuentro, sección Receta.

  • Autocomplete de medicamento sobre health_medications ordenado por usage_count; si no existe, se crea al vuelo (patrón de ProgramaMed, §4.c).
  • Por ítem: dosis, cantidad, frecuencia, duración, indicaciones.
  • Repetir última receta del paciente en un click — el traumatólogo que hace control cada 15 días no debería reescribir todo. ProgramaMed no lo tiene.
  • Botón Imprimir → emite un health_documents de tipo receta usando la plantilla de receta (también modificable por el médico).
  • Botón Enviar → WhatsApp o email al paciente, registrando en health_document_deliveries.

Layout del PDF de receta: membrete del tenant · datos del profesional con registro MSP/ACESS · paciente y documento · fecha · tabla de medicamentos · indicaciones generales · bloque de firma · folio + QR de verificación al pie.

11. Documentos: catálogo inicial​

Plantillas de sistema (semilla) que se entregan listas y el médico puede clonar y modificar:

codeOrigen
recetanuevo
certificado_medico§5.a
certificado_ocupacional§5.a
certificado_poblacion_general§5.a
certificado_movilidad§5.a
orden_ingreso§5.a
consentimiento_informado§5.b (MSP FORM.024)
orden_examenesnuevo — imagen + laboratorio del encuentro en un solo pedido
resumen_consultanuevo

Los formularios MSP estructurados (FORM.053, FORM.024) NO son plantillas editables: son formularios de sistema versionados que el médico sólo rellena. Si el MSP cambia el formato, se publica una versión nueva y las emisiones viejas siguen apuntando a la suya.

12. Seguridad y permisos​

Se suman al healthManifest en backend/src/modules/manifests.ts:

health.documents.read ver documentos emitidos
health.documents.issue emitir / imprimir / enviar
health.documents.void anular un documento emitido
health.templates.manage editar plantillas del tenant
  • Bucket de Storage privado; descarga sólo por URL firmada de vida corta.
  • Nombre de archivo sin PHI.
  • Toda lectura de un documento pasa por RPC auditada, igual que health_get_clinical_record().
  • Anular no borra: status='voided' + motivo + quién y cuándo. Nunca DELETE.
  • La página pública de verificación por QR devuelve sólo: folio, tipo, fecha de emisión, profesional, estado (vigente/anulado) y coincidencia de hash. Nunca datos clínicos ni el nombre completo del paciente.

13. Fases​

FaseAlcanceCierra
F1Catálogo de medicamentos + receta en el encuentro + PDF de recetaEl pedido concreto: recetas imprimibles
F2Motor de plantillas versionadas + editor con tokens + los 6 certificados de referenciaEl pedido concreto: plantillas modificables
F3Envío por WhatsApp/email + folio + QR de verificaciónEl diferenciador frente a ProgramaMed
F4Adjuntos y resultados (health_attachments)El hueco de §4.b
F5Formularios MSP 053 y 024 estructuradosRequisito legal en Ecuador
F6Ficha por especialidad (§4.a)El hallazgo estructural pendiente

Archivos previstos​

supabase/migrations/2026XXXX_health_documents.sql
backend/src/modules/health/documents/ (service, controller, render, tokens)
backend/src/modules/health/prescriptions/
backend/src/modules/manifests.ts (permisos + menú)
shared/src/types.ts (DocumentToken, tipos de documento)
frontend/src/sections/health/documents/ (editor de plantillas, emisión, listado)
frontend/src/sections/health/prescription-form.tsx
frontend/src/app/dashboard/health/documents/

14. Pendiente de observar en ProgramaMed​

Ya se observaron en vivo la consulta finalizada y el PDF de historia clínica (secciones 4.a y 4.b). Queda por ver:

  • La pestaña de ficha por especialidad (Traumatología) — qué campos trae y de dónde sale la lámina anatómica del PDF.
  • Una consulta en curso (no finalizada): la carga de medicamentos y la receta impresa.
  • El contenido de Signos Vitales, Antecedentes y Órganos y Sistemas.