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:
| Ruta | Mó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 - Especialidado 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 CitayImprimir 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.Pedidoses el contenedor único de medicamentos + imagen + laboratorio + complementarios. - La vista finalizada es sólo lectura, con un único botón
Imprimir(→ PDF) yRegresar.
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ón | Campos |
|---|---|
| Diagnósticos | idCieDiez (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 imagen | imagen (catálogo o texto libre) |
| Pedidos de laboratorio | laboratorio (catálogo o texto libre) |
| Exámenes complementarios | examCompl (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:
- Un textarea con CKEditor (
txtCertificado) que guarda HTML — la plantilla, propia de cada médico (cmbMedico+cmbEspecialidades). - Un panel de tokens
PROM*que el médico debe conservar al editar. - Un formulario con los campos variables de esa emisión.
btnEdit(editar plantilla) /btnGuardar*(guardar plantilla) /btnImprimir(emitir).
Diccionario de tokens observado:
| Token | Significado |
|---|---|
PROMDOCTOR | Nombre del médico que certifica |
PROMESPECIALIDAD | Especialidad del médico |
PROMCONSULTORIO | Consultorio / clínica / hospital |
PROMPACIENTE | Nombre del paciente |
PROMCEDPACIENTE | Cédula del paciente |
PROMEDAD | Edad del paciente |
PROMNACIONALIDAD | Nacionalidad del paciente |
PROMCAUSA | Enfermedad / problema |
PROMDIAGNOSTICO | Diagnóstico / examen realizado |
PROMACTIVIDADES | Actividades laborales (cert. ocupacional) |
PROMDESDE / PROMHASTA | Rango de reposo |
PROMFECHA / PROMPFECHA | Fecha de emisión |
PROMDESTINATARIO | Persona/entidad destinataria |
PROMEXAMEN | Examen a realizar |
PROMINGRESOF / PROMHORAI | Fecha/hora de ingreso |
PROMCIRFECHA / PROMCIRUGIAH | Fecha/hora de cirugía |
PROMOVILF / PROMDESTINO / PROMOTIVO | Movilidad: fecha, destino, motivo |
PROMACOMPA / PROMCEDACOMPA | Acompañante y su cédula |
PROMREGIONES | Listado 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
*2para 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):
- Por género del paciente
- Fecha de registro de pacientes
- Ubicación de pacientes
- Aseguradora de pacientes
- Estado civil y edad
- Total de pacientes
- CIE-10 atendidos
- Medicamentos recetados
- 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
| ProgramaMed | Xenpia | Por qué |
|---|---|---|
| La plantilla se edita in situ; al cambiarla, los certificados viejos ya no se pueden reproducir | Plantillas 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 vez | El documento se emite una vez, se congela (HTML + PDF + hash) y después sólo se reimprime o se anula | Reimprimir ≠ 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 silencio | Tokens tipados y namespaced ({{paciente.nombre}}), insertados por botón como bloque atómico y validados al guardar | El médico no debería poder romper su plantilla escribiendo |
| Plantilla propia de cada médico, creada desde cero | Plantillas de sistema de referencia (semilla) → el médico las clona y modifica; la de sistema queda intacta | Un médico nuevo arranca funcionando el día uno |
PDF llamado {cédula}-HistoriaClinica.pdf | Nombre sin PHI (documento-{folio}.pdf) y descarga por URL firmada de corta vida | Ese archivo termina en Descargas, WhatsApp y correo |
| Nombre del paciente y del médico en la URL | Sólo UUIDs opacos; el estado no viaja en el path | Las URLs quedan en logs, historial y referrers |
btoa(id) como "seguridad" | UUID + RLS por tenant + RPC auditada | base64 no es control de acceso |
| Sólo imprimir | Imprimir + enviar por WhatsApp/email desde la misma pantalla | Xenpia ya tiene los canales. Es el diferenciador más barato y más visible |
| Nada verifica que un certificado sea auténtico | Folio + QR que resuelve a una página pública de verificación | Los certificados de reposo se falsifican; esto lo cierra |
| Informes de imagen pegados como texto en la historia | Adjuntos/resultados como entidad propia | Ver §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:
- El botón de insertar token en el editor (agrupado por origen).
- La validación al guardar: token desconocido → error; token requerido ausente → aviso.
- 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:
| ProgramaMed | Xenpia |
|---|---|
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_configdel tenant. ProgramaMed dejafont-familyinline y por eso sus plantillas se ven distintas entre sí.
9.3 Render
Server-side en NestJS (backend/src/modules/health/documents/):
- Resolver plantilla (professional → tenant → system) y tomar la última versión activa.
- Construir el contexto: paciente, profesional, tenant, encuentro, campos del formulario.
- Sustituir tokens sobre HTML escapando el valor (los datos del paciente son entrada de usuario: sin escapar hay inyección en el PDF).
- Render a PDF, guardar en Storage, calcular
content_hash, asignarfolio. - Insertar
health_documentsconstatus='issued'. Desde ese momento es inmutable.
10. Recetas imprimibles
Pantalla: dentro del encuentro, sección Receta.
- Autocomplete de medicamento sobre
health_medicationsordenado porusage_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_documentsde tiporecetausando 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:
code | Origen |
|---|---|
receta | nuevo |
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_examenes | nuevo — imagen + laboratorio del encuentro en un solo pedido |
resumen_consulta | nuevo |
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. NuncaDELETE. - 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
| Fase | Alcance | Cierra |
|---|---|---|
| F1 | Catálogo de medicamentos + receta en el encuentro + PDF de receta | El pedido concreto: recetas imprimibles |
| F2 | Motor de plantillas versionadas + editor con tokens + los 6 certificados de referencia | El pedido concreto: plantillas modificables |
| F3 | Envío por WhatsApp/email + folio + QR de verificación | El diferenciador frente a ProgramaMed |
| F4 | Adjuntos y resultados (health_attachments) | El hueco de §4.b |
| F5 | Formularios MSP 053 y 024 estructurados | Requisito legal en Ecuador |
| F6 | Ficha 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.