diff --git a/DOCUMENTACION_LAB.html b/DOCUMENTACION_LAB.html new file mode 100644 index 0000000..de590d3 --- /dev/null +++ b/DOCUMENTACION_LAB.html @@ -0,0 +1,1712 @@ + + + + + + Documentación — Sistema de Laboratorio + + + + +
+ + + + + +
+ + +
+

📋 Documentación del Sistema de Laboratorio

+

Sistema Laboratorio Clínico — Módulos de Agendamiento y Formularios

+
+ 📅 Última actualización: 30/03/2026 + 🔢 Versión: 1.0 + 📦 Módulos: Domicilios · Formularios +
+
+ + +
+
+
🏠
+
+

Módulo 1 — Agendamiento (Domicilios)

+

Gestión de servicios de toma de muestras a domicilio

+
+
+
+ +

¿Qué es?

+

+ El módulo de agendamiento gestiona los servicios de toma de muestras a domicilio. + Permite crear, asignar, seguir y completar visitas médicas domiciliarias. + Los administradores gestionan la agenda desde lab_domicilios.php; + los enfermeros gestionan su propia agenda desde enfermero_portal.php. +

+ + +

Archivos involucrados

+
+ + + + + + + + + + + + + + + + + + + + + + + +
ArchivoDescripción
lab_domicilios.phpVista principal del admin — tabla con filtros, detalle del domicilio, formularios recibidos, exportar CSV
enfermero_portal.phpPortal exclusivo del enfermero — su agenda personal del día, ordenada por hora, con tarjetas colapsables
lab_enfermeras.phpCRUD del personal de enfermería
classes/lab/Domicilio.phpClase ORM — crear, editar, cambiar estado, estadísticas
classes/lab/Enfermera.phpClase ORM — CRUD, agenda por enfermero, carga de trabajo
classes/lab/Asignacion.phpClase ORM — asignar / reasignar / liberar enfermero a domicilio
api/lab/get_domicilios.phpGET — lista de domicilios con filtros y estadísticas del día
api/lab/save_domicilio.phpPOST — crear, actualizar o cambiar estado
api/lab/update_domicilio_enfermero.phpPOST — el enfermero avanza el estado desde su portal
api/lab/my_agenda.phpGET — agenda del enfermero actualmente autenticado
api/lab/save_asignacion.phpPOST — asignar o reasignar enfermero
api/lab/get_asignaciones.phpGET — asignaciones por fecha
api/lab/registrar_pago.phpPOST — registrar pago de un domicilio
api/lab/save_servicio_extra.phpPOST — agregar servicio realizado durante la visita
api/lab/get_notas_domicilio.phpGET — notas clínicas y libres del domicilio
api/lab/upload_nota_imagen.phpPOST — subir imagen adjunta a una nota
api/lab/crear_desde_whatsapp.phpGET/POST — crear paciente u orden desde una conversación de WhatsApp activa
+
+ + +

Base de datos

+ +

Tabla lab_domicilios

+

Tabla principal. Cada fila es un servicio a domicilio.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CampoTipoDescripción
idINT PKIdentificador único
paciente_idINT FKPaciente al que se le realiza el servicio
orden_idINT FK NULLOrden médica adjunta (opcional)
direccionTEXTDirección completa de la visita
ciudadVARCHAR(100)Ciudad
barrioVARCHAR(100)Barrio
indicaciones_dirTEXTReferencias o indicaciones adicionales ("apto 302, tocar campanilla")
fecha_programadaDATEFecha de la visita
hora_programadaTIMEHora de la visita
tipo_servicioVARCHAR(100)Tipo de servicio (toma de muestra, etc.)
tipo_clienteENUMparticular / seguro / eps
examenes_solicitadosTEXTLista de exámenes (cuando no hay orden médica)
seguro_nombreVARCHARNombre del seguro o EPS
autorizacionVARCHARNúmero de autorización
valor_domicilioDECIMALValor del servicio nullable
valor_copagoDECIMALCopago a cargo del cliente nullable
copago_laboratorioDECIMALCopago al laboratorio nullable
pago_estadoENUMpendiente / pagado / exento
pago_modoENUMefectivo / transferencia / otro
pago_montoDECIMALMonto pagado
pago_fechaDATETIMEFecha del pago
pago_notasTEXTNotas sobre el pago
estadoENUMVer flujo de estados
motivo_cancelacionTEXTMotivo si fue cancelado (obligatorio al cancelar)
fecha_reprogramadaDATENueva fecha si fue reprogramado
hora_llegadaTIMERegistrada automáticamente al iniciar la visita
hora_salidaTIMERegistrada automáticamente al completar
observacionesTEXTObservaciones del resultado de la visita
muestras_tomadasTEXTLista de muestras obtenidas
notas_adminTEXTNotas internas del equipo administrativo
creado_porINT FKUsuario que creó el registro
+
+ +

Tabla lab_enfermeras

+

Personal de enfermería disponible para asignación.

+
+ + + + + + + + + +
CamposDescripción
id, numero_documentoPK e identificador del empleado
tipo_documentoCC / CE / TI / PA
nombre_completo, telefono, telefono_altDatos de contacto
email, zona, notasInformación adicional
is_activeSoft-delete del registro
+
+ +

Tabla lab_asignaciones

+

Asignación de enfermero a domicilio. Máximo un enfermero activo por domicilio (UNIQUE KEY en domicilio_id).

+
+ + + + + + + + +
CampoDescripción
id, domicilio_id, enfermera_idPK y relaciones
asignada_porUsuario admin que realizó la asignación
estadoasignada / confirmada / liberada / completada
notasObservaciones de la asignación
+
+ +

Tabla lab_servicios_extra

+

Servicios realizados por el enfermero durante la visita, adicionales a la orden original.

+
+ + + + + + + + +
CampoDescripción
tipoinyeccion / cura / nebulizacion / toma_muestra / tension_arterial / glucometria / otro
descripcion, notasDetalle del servicio
requiere_pago, valorSi genera cobro adicional y su valor
realizado_porFK al enfermero que lo realizó
+
+ +

Tabla lab_domicilio_notas

+

Notas registradas por el enfermero durante la visita. Dos tipos:

+
+
+ 🩺 clinica + Ficha clínica: antecedentes, medicamentos, acudiente (si es menor de edad). +
+
+ 📝 libre + Nota libre con título, cuerpo de texto enriquecido e imagen adjunta. +
+
+
+ + + + + + + + + +
CamposDescripción
id, domicilio_id, enfermera_idPK y relaciones
tipoclinica / libre
antecedentes, medicamentosSección clínica
acudiente_nombre, acudiente_documentoDatos del acudiente si el paciente es menor
titulo, cuerpo, imagen_pathSección de nota libre
+
+ + +

Roles y permisos

+
+
+

🛡️ Admin

+
    +
  • Crear, editar y ver todos los domicilios
  • +
  • Asignar / reasignar enfermeros
  • +
  • Registrar pagos
  • +
  • Exportar Excel
  • +
  • Ver informe completo con notas
  • +
  • Ver agenda de cualquier enfermero con ?eid=X
  • +
+
+
+

👩‍⚕️ Enfermero

+
    +
  • Solo ve su propia agenda en enfermero_portal.php
  • +
  • Avanza el estado de sus domicilios asignados
  • +
  • Agrega servicios extra
  • +
  • Registra notas clínicas y libres
  • +
  • Visualiza órdenes médicas adjuntas
  • +
+
+
+
+ ⚠️ Redirección automática: si el usuario autenticado tiene rol enfermero, + lab_domicilios.php lo redirige inmediatamente a enfermero_portal.php. +
+ + +

Flujo de estados

+
+
programado
+
+
confirmado
+
+
en_camino
+
+
en_domicilio
+
+
completado
+
+
+
⛔ cancelado
+
🔄 reprogramado
+ ← Posibles desde cualquier estado activo +
+ +

Notas automáticas según estado:

+
+ + + + + + + + +
EventoAcción automática
Transición a en_domicilioSe registra hora_llegada automáticamente
Transición a completadoSe registra hora_salida automáticamente
Transición a canceladoCampo motivo_cancelacion obligatorio
Transición a reprogramadoCampo fecha_reprogramada obligatorio
+
+ +

Transiciones permitidas al enfermero (validadas en update_domicilio_enfermero.php):

+
+ + + + + + + + +
Estado actualEstados posibles
programadoconfirmado
confirmadoen_camino · cancelado
en_caminoen_domicilio · cancelado
en_domiciliocompletado · cancelado
+
+
+ El admin puede cambiar a cualquier estado directamente, incluyendo cancelar desde cualquier punto. +
+ + +

Funcionalidades

+
+
🔍 FiltrosPor fecha, estado y enfermero. Botón "Hoy" para filtro rápido.
+
📊 Resumen del díaConteo de domicilios por estado en la parte superior.
+
📋 Panel de detalleAbre al hacer clic: info completa, notas clínicas e informe imprimible.
+
👥 Asignar enfermeroModal con lista del personal disponible. Incluye reasignación.
+
⚠️ Sin asignarBadge con conteo de domicilios sin enfermero asignado.
+
💰 Registrar pagoModal para marcar cobro con modalidad y monto.
+
➕ Servicios extraEl enfermero los agrega desde su portal durante la visita.
+
🩺 Notas del enfermeroFicha clínica y notas libres con imagen adjunta.
+
📄 Informe imprimibleVista imprimible con datos del paciente, ficha clínica y notas.
+
📁 Exportar CSV / ExcelIncluye columnas de notas del enfermero.
+
📝 Formularios recibidosPestaña que muestra formularios enviados con filtros.
+
📱 Portal del enfermeroTarjetas colapsables, separadas en Activos y Finalizados.
+
+ + +

API Endpoints

+
+ + + + + + + + + + + + + + + +
EndpointMétodoDescripción
api/lab/get_domicilios.phpGETLista con filtros. ?id=X para uno solo con detalle completo.
api/lab/save_domicilio.phpPOSTCrear, editar o cambiar estado. Con solo_estado=true solo cambia el estado.
api/lab/update_domicilio_enfermero.phpPOSTEl enfermero avanza el estado de su domicilio.
api/lab/my_agenda.phpGETAgenda del enfermero autenticado con servicios extra.
api/lab/save_asignacion.phpPOSTAsignar o reasignar enfermero a domicilio.
api/lab/get_asignaciones.phpGETAsignaciones por fecha.
api/lab/registrar_pago.phpPOSTRegistrar pago con monto y modalidad.
api/lab/save_servicio_extra.phpPOSTAgregar servicio realizado durante la visita.
api/lab/get_notas_domicilio.phpGETNotas del domicilio (clínicas y libres).
api/lab/upload_nota_imagen.phpPOSTSubir imagen adjunta a una nota libre.
api/lab/crear_desde_whatsapp.phpGET/POSTCrear paciente u orden desde una conversación de WhatsApp activa.
+
+ +
+
+ + + +
+
+
📝
+
+

Módulo 2 — Formularios

+

Builder visual, envío por WhatsApp, firma digital y sello SHA-256

+
+
+
+ +

¿Qué es?

+

+ El módulo de formularios permite crear plantillas de documentos + (consentimientos, historias clínicas, autorizaciones, encuestas) mediante un builder visual, + enviarlas a los pacientes por WhatsApp y recopilar sus respuestas con firma digital. + El documento firmado genera un sello de integridad SHA-256 que puede verificarse públicamente. +

+ + +

Archivos involucrados

+
+ + + + + + + + + + + + + + + +
ArchivoDescripción
lab_formularios.phpVista principal — lista de plantillas y registro de envíos
lab_formulario_builder.phpEditor visual drag & drop (ventana separada, solo admin)
form_cliente.phpPágina pública — el paciente llena y firma sin iniciar sesión
ver_formulario_enviado.phpVista del documento firmado — acceso por ID (admin/enfermero) o token (cliente)
verificar_formulario.phpVerificación pública de autenticidad por hash SHA-256
classes/lab/Formulario.phpClase ORM — CRUD de plantillas, crear envíos, guardar respuestas, generar hash
api/lab/get_formularios.phpGET — lista plantillas o envíos
api/lab/save_formulario.phpPOST — crear, editar y eliminar plantillas (solo admin)
api/lab/send_formulario.phpPOST — crear instancia de envío, devolver URL pública y mensaje WhatsApp
api/lab/submit_formulario.phpGET/POST — cargar el formulario por token / guardar la respuesta del cliente
api/lab/firmar_profesional.phpPOST — guardar firma del profesional (requiere sesión activa)
+
+ + +

Base de datos

+ +

Tabla lab_formularios

+

Plantillas de documentos creadas desde el builder.

+
+ + + + + + + + + + + + + + + + + + + + +
CampoTipoDescripción
idINT PKIdentificador único
nombreVARCHAR(150)Nombre de la plantilla
descripcionTEXTDescripción visible al cliente
categoriaENUMconsentimiento / historia_clinica / autorizacion / encuesta / otro
esquemaLONGTEXTJSON con el array de campos del formulario
permite_firmaTINYINT(1)El formulario tiene sección de firma global
requiere_firmaTINYINT(1)La firma global es obligatoria
firma_modosVARCHAR(50)canvas, foto o canvas,foto (separados por coma)
versionSMALLINTSe incrementa automáticamente al editar el esquema
is_activeTINYINT(1)Soft-delete
creado_porINT FKUsuario que creó la plantilla
doc_encabezadoVARCHAROverride del nombre de empresa en el documento
doc_subtituloVARCHAROverride del subtítulo en el documento
doc_logo_base64LONGTEXTOverride del logo en el documento
doc_colorVARCHAR(20)Override del color del encabezado del documento
doc_pie_paginaTEXTOverride del pie de página
+
+ +

Tabla lab_form_envios

+

Cada fila es una instancia enviada a un paciente.

+
+ + + + + + + + + + + + + + + + + + + + +
CampoTipoDescripción
idINT PKIdentificador único
formulario_idINT FKPlantilla enviada
paciente_idINT FK NULLPaciente asociado
domicilio_idINT FK NULLDomicilio asociado (opcional)
tokenCHAR(64) UNIQUEToken público — 64 chars hex (acceso sin sesión)
datos_prefilledLONGTEXTJSON con datos pre-llenados al enviar (incluye __paciente.*)
datos_clienteLONGTEXTJSON con las respuestas completadas por el cliente
firma_svgLONGTEXTFirma del paciente (PNG base64 — canvas o foto)
ip_clienteVARCHAR(45)IP del cliente al enviar el formulario
user_agentVARCHAR(512)Navegador del cliente
estadoENUMpendiente / completado / firmado / expirado
enviado_porINT FKUsuario que generó el enlace
enviado_viaENUMwhatsapp / email / link
expira_enDATETIME NULLSiempre NULL — el enlace no expira
completado_enDATETIMEFecha y hora en que el cliente completó el formulario
hash_verificacionCHAR(64)Sello de integridad SHA-256 del documento
+
+ + +

Roles y permisos

+
+
+

🛡️ Admin

+
    +
  • Crear, editar y eliminar plantillas en el builder
  • +
  • Enviar formularios a cualquier paciente
  • +
  • Ver todos los envíos
  • +
  • Descargar PDF de cualquier formulario
  • +
+
+
+

👩‍⚕️ Enfermero

+
    +
  • Enviar formularios existentes a sus pacientes
  • +
  • Ver solo sus propios envíos (enviado_por = su user_id)
  • +
  • No puede crear ni editar plantillas
  • +
  • Puede firmar como profesional en formularios que él mismo envió
  • +
+
+
+

🌐 Cliente (público)

+
    +
  • Accede a form_cliente.php?t=TOKEN sin autenticación
  • +
  • Llena y firma el formulario
  • +
  • Puede volver al mismo enlace para ver el documento firmado
  • +
  • Puede descargarlo como PDF
  • +
+
+
+ + +

Flujo completo

+
    +
  1. +
    + Admin crea la plantilla
    + Abre lab_formulario_builder.php (ventana nueva), arrastra campos al canvas, + configura diseño del documento (logo, color, encabezado, pie de página) + y guarda → POST api/lab/save_formulario.php. +
    +
  2. +
  3. +
    + Admin o Enfermero envía el formulario
    + Desde lab_formularios.php → botón "Enviar" → selecciona paciente → + elige canal (WhatsApp o solo link) → POST api/lab/send_formulario.php. + Se genera un token de 64 chars (bin2hex(random_bytes(32))) + y se crea la fila en lab_form_envios con estado = pendiente. +
    +
  4. +
  5. +
    + Cliente recibe el enlace
    + Abre form_cliente.php?t=TOKEN → GET api/lab/submit_formulario.php?t=TOKEN + carga los datos. Si ya fue firmado → muestra pantalla de solo lectura con link al PDF. +
    +
  6. +
  7. +
    + Cliente llena el formulario
    + Campos "linked" llegan pre-llenados con datos del paciente (readonly si tienen valor, editables si están vacíos). + Los campos firma_profesional muestran aviso "uso exclusivo del profesional". + El cliente dibuja su firma o adjunta una foto. +
    +
  8. +
  9. +
    + Cliente envía
    + POST api/lab/submit_formulario.php → se genera hash SHA-256 + (contenido + firma + ID + token + timestamp) → + estado cambia a firmado (con firma) o completado (sin firma). + Pantalla de éxito con hash visible y botón para descargar el PDF. +
    +
  10. +
  11. +
    + Profesional firma (si el formulario lo requiere)
    + Admin/Enfermero abre ver_formulario_enviado.php?id=X con sesión activa, + dibuja su firma en el canvas del campo firma_profesional + → POST api/lab/firmar_profesional.php. +
    +
  12. +
  13. +
    + Admin/Enfermero revisa el resultado
    + lab_formularios.php → pestaña "Envíos" → icono "Ver respuesta" → + ver_formulario_enviado.php?id=X — documento HTML imprimible. +
    +
  14. +
  15. +
    + Verificación pública de integridad
    + verificar_formulario.php?h=HASH_SHA256 → busca en hash_verificacion + → muestra nombre del formulario, paciente, fecha, estado y si el sello es válido. +
    +
  16. +
+ + +

Tipos de campos del Builder

+ +

Campos de entrada

+
+ + + + + + + + + + + + + +
TipoDescripción
textoCampo de texto corto de una sola línea
textareaÁrea de texto largo (varias líneas)
numeroCampo numérico
fechaSelector de fecha
horaSelector de hora
selectLista desplegable con opciones configurables
radioSelección única con opciones configurables
checkboxSelección múltiple con opciones configurables
lista_marcableLista de ítems numerados con checkboxes
+
+ +

Campos de firma

+
+ + + + + + +
TipoDescripción
firmaFirma del paciente — visible y editable en form_cliente.php
firma_profesionalFirma del profesional — bloqueada para el cliente; solo editable desde el panel admin/enfermero
+
+ +

Campos de contenido

+
+ + + + + + + +
TipoDescripción
separadorSeparador visual o título de sección
parrafoBloque de texto estático (pre-formatado o flujo libre)
parrafo_inlinePárrafo con marcadores {nombre_completo}, {telefono}, etc. que se convierten en espacios editables si el valor está vacío
+
+ +

Campos vinculados al paciente (tipo: linked)

+

Se auto-rellenan con los datos del paciente al momento de enviar. Si el valor existe → campo de solo lectura. Si está vacío → el cliente puede completarlo.

+
+ + + + + + + + + + + + +
linked_keyDato que extrae
nombre_completoNombre completo del paciente
numero_documentoNúmero de documento
tipo_documentoTipo de documento
fecha_nacimientoFecha de nacimiento
telefonoTeléfono
emailCorreo electrónico
epsEPS o aseguradora
direccionDirección
+
+ + +

Firma digital

+
+ + + + + + + + + + + + +
ModoFuncionamiento
canvasEl cliente dibuja su firma con el dedo o el mouse. Se captura con canvas.toDataURL('image/png').
fotoEl cliente sube una imagen desde su cámara o galería (<input accept="image/*" capture="environment">). Se convierte a base64 con FileReader.
+
+
+ Ambos modos pueden estar activos simultáneamente en la misma plantilla configurando firma_modos = "canvas,foto". +
+

+ Firma global vs. firma por campo:
+ Si el esquema no incluye campos tipo firma, se muestra una sección de firma global al pie del formulario. + Si el esquema incluye campos firma, cada uno tiene su propio widget canvas en la posición configurada. +

+ + +

Enlace público y vigencia

+
+
🔗 URL públicaform_cliente.php?t=TOKEN
+
🔑 Token64 chars hex — bin2hex(random_bytes(32))
+
🔓 Sin sesiónNo requiere cuenta ni iniciar sesión
+
♾️ Sin vencimientoexpira_en siempre es NULL
+
🔒 Bloqueo por estadoSi ya fue firmado → solo lectura, no permite modificar
+
🔁 IdempotenciaSi el cliente reintenta enviar, devuelve éxito sin duplicar
+
+ + +

PDF y visualización del documento

+
+ ✅ Sin librerías de PDF: el documento es la página ver_formulario_enviado.php + con estilos @media print. El usuario imprime o guarda como PDF directamente desde el navegador. +
+

Contenido del documento impreso:

+
    +
  • Encabezado con logo, nombre, subtítulo, datos de contacto y color corporativo
  • +
  • Datos del paciente (nombre, documento, fecha de nacimiento, teléfono, EPS)
  • +
  • Respuestas del formulario campo por campo en el orden del esquema
  • +
  • Imagen de la firma del paciente
  • +
  • Firma del profesional (si fue completada)
  • +
  • Sello SHA-256 con link para verificar autenticidad
  • +
  • Pie de página con fecha de generación e ID del documento
  • +
+ +

Formas de acceder al documento:

+
+ + + + + + +
URLQuién puede acceder
ver_formulario_enviado.php?id=XAdmin (cualquier formulario) o Enfermero (solo los que él envió). Requiere sesión.
ver_formulario_enviado.php?t=TOKENCliente u cualquier persona con el enlace. Sin sesión. Solo si el estado es firmado o completado.
+
+ + +

Sello de integridad SHA-256

+

Al guardar la respuesta del cliente, el sistema genera un hash SHA-256 que vincula de forma única el contenido del formulario con la firma y el momento en que se completó.

+

Construcción del hash (en Formulario::guardarRespuesta()):

+
SHA-256 de:
+  JSON de los datos del cliente
+  + firma SVG/PNG del paciente
+  + ID interno del envío
+  + token del enlace
+  + timestamp del momento de registro
+
+ ¿Para qué sirve? Cualquier persona con el hash puede ir a + verificar_formulario.php?h=HASH para confirmar que el documento existe, + el nombre del formulario y del paciente, la fecha en que fue completado y el estado actual. + Si el documento fue alterado, el hash no coincidirá y la verificación fallará. +
+ + +

Firma del profesional

+
    +
  1. +
    Al diseñar la plantilla en el builder se agrega un campo tipo: firma_profesional en la posición deseada.
    +
  2. +
  3. +
    Cuando el cliente llena el formulario, ese campo muestra solo un aviso: "Uso exclusivo del profesional de salud". El cliente no puede interactuar con él.
    +
  4. +
  5. +
    Una vez que el cliente ha completado y enviado, el admin o enfermero abre ver_formulario_enviado.php?id=X con sesión activa y verá el canvas de firma en esa posición.
    +
  6. +
  7. +
    El profesional dibuja su firma y hace clic en "Guardar firma" → POST api/lab/firmar_profesional.php.
    +
  8. +
+ +

Validaciones en el servidor:

+
    +
  • Requiere sesión activa (isUserLoggedIn())
  • +
  • Si el usuario es enfermero, solo puede firmar en formularios que él mismo envió
  • +
  • Valida que el campo_id corresponde a un campo tipo: firma_profesional en el esquema
  • +
  • Valida que la imagen enviada sea un data URI de imagen válido (prevención XSS/inyección)
  • +
+
+ Si el formulario se accede via ?t=TOKEN (cliente público), el canvas no aparece. + En su lugar se muestra un aviso "Pendiente de firma del profesional" + (solo visible en pantalla, no en el PDF impreso). +
+ + +

API Endpoints

+
+ + + + + + + + + + +
EndpointMétodoAutenticaciónDescripción
api/lab/get_formularios.phpGETSesiónLista plantillas. ?id=X para una sola. ?envios=1 para lista de envíos.
api/lab/save_formulario.phpPOSTAdminCrear, editar o eliminar una plantilla.
api/lab/send_formulario.phpPOSTSesiónCrear instancia de envío. Devuelve URL pública y mensaje para WhatsApp.
api/lab/submit_formulario.phpGETPúblicoCargar el formulario por token (sin sesión).
api/lab/submit_formulario.phpPOSTPúblicoGuardar la respuesta y firma del cliente.
api/lab/firmar_profesional.phpPOSTSesiónGuardar la firma del profesional en un campo firma_profesional.
+
+ +
+
+ +

+ Documentación generada para uso interno del equipo · 30/03/2026 +

+ + + + +
+
+
🛡️
+
+

Manual de Uso — Administrador · Domicilios

+

🛡️ Admin   Gestión completa desde lab_domicilios.php

+
+
+
+ + +

Crear un nuevo domicilio

+
+
1
+
+
Abrir el formulario
+

Haz clic en el botón + Nuevo Domicilio ubicado en la esquina superior derecha de la pantalla. Se abrirá un modal con el formulario de creación.

+
+
+
+
2
+
+
Buscar y seleccionar el paciente
+

En el campo Paciente, empieza a escribir el nombre o documento del paciente. Aparecerá un listado de sugerencias; haz clic en el paciente correcto para seleccionarlo.

+
✅ El paciente debe existir previamente en el sistema. Si no aparece, créalo primero desde lab_pacientes.php.
+
+
+
+
3
+
+
Llenar los datos del servicio
+

Completa los campos requeridos (*):

+
    +
  • Dirección completa — incluye barrio e indicaciones adicionales si aplica
  • +
  • Fecha programada — se pre-llena con la fecha del filtro activo
  • +
  • Tipo de servicio — ej: "toma de muestras", "inyección"
  • +
  • Tipo de cliente — si estás en Seguro / EPS, aparecerá el campo del nombre del seguro
  • +
  • Valores de cobro — valor domicilio y/o copago (dejar vacío si no aplica)
  • +
+
+
+
+
4
+
+
Adjuntar orden médica (opcional)
+

Si tienes una imagen o PDF con la orden del médico, haz clic en 📎 Adjuntar orden. Se acepta JPG, PNG, PDF y DOC hasta 10 MB.

+
+
+
+
5
+
+
Guardar
+

Haz clic en 💾 Guardar. El domicilio aparecerá en la tabla con estado programado.

+
+
+ + +

Asignar un enfermero

+
+
1
+
+
Seleccionar el domicilio
+

Haz clic en la fila del domicilio en la tabla. Se abrirá el panel de detalle a la derecha. Si el domicilio no tiene enfermero, verás un aviso en naranja: "Sin enfermero asignado".

+
+
+
+
2
+
+
Abrir el modal de asignación
+

Haz clic en 👤 Asignar (o Reasignar si ya tiene uno). Se abrirá un modal con la lista del personal disponible.

+
+
+
+
3
+
+
Seleccionar y confirmar
+

En el desplegable elige el enfermero. Verás cuántos domicilios tiene asignados hoy y su usuario de acceso al portal. Haz clic en ✔ Confirmar asignación.

+
✅ Una vez asignado, el enfermero verá este domicilio en su portal al ingresar.
+
+
+ + +

Cambiar el estado de un domicilio

+

Los botones de avance de estado aparecen en el panel de detalle del domicilio seleccionado:

+
+ programado + + Confirmar + + En camino + + En domicilio + + Completar +
+
+
1
+
+
Seleccionar el domicilio y hacer clic en el botón de estado
+

En el panel de detalle aparecerán uno o dos botones según el estado actual. Haz clic en el que corresponda y confirma en el diálogo que aparece.

+
+
+
+ ⚠️ Al cambiar a cancelado el sistema pedirá un motivo de cancelación. Este campo es obligatorio. +
+
+ ✅ Los estados en_domicilio (hora llegada) y completado (hora salida) registran la hora automáticamente si el enfermero realiza la transición desde su portal. +
+ + +

Registrar un pago

+
+
1
+
+
Domicilio con valores de cobro
+

En el panel de detalle, si el domicilio tiene Valor domicilio o Copago asignados, verás la sección de cobro con un badge ⏳ Pago pendiente.

+
+
+
+
2
+
+
Abrir el registro de pago
+

Haz clic en el botón 💳 Registrar pago dentro del bloque de cobro.

+
+
+
+
3
+
+
Completar los datos del pago
+

Selecciona la modalidad (💵 Efectivo / 📲 Transferencia / 📋 Otro), ingresa el monto recibido y agrega notas si es necesario. Haz clic en Confirmar pago.

+
✅ Si el servicio es para un cliente con seguro y no genera cobro directo, selecciona Exento desde las opciones de pago_estado.
+
+
+ + +

Filtros y exportar a Excel

+
+ + + + + + + + + +
ControlDescripción
📅 FechaFiltra los domicilios por fecha programada. Se carga con la fecha actual al abrir la pantalla.
EstadoFiltra por estado del domicilio. Deja en blanco para ver todos.
EnfermeroFiltra solo los domicilios asignados a ese enfermero.
📅 HoyRestablece el filtro de fecha a la fecha de hoy.
📁 ExcelExporta todos los domicilios que coincidan con los filtros activos, incluyendo notas del enfermero.
+
+
✅ Los cambios en los filtros se aplican automáticamente sin necesidad de hacer clic en un botón de buscar.
+ +
+
+ + +
+
+
👩‍⚕️
+
+

Manual de Uso — Enfermero · Portal

+

👩‍⚕️ Enfermero   Gestión de agenda personal desde enfermero_portal.php

+
+
+
+ + +

Acceder al portal

+
+
1
+
+
Iniciar sesión
+

Ve a login.php e ingresa con el usuario y contraseña que el administrador te asignó. El sistema te redirigirá automáticamente a enfermero_portal.php.

+
✅ Si intentas acceder a otra sección del sistema, serás redirigido de vuelta a tu portal automáticamente.
+
+
+
+
2
+
+
Ver tu agenda del día
+

Encontrarás dos bloques: Activos (domicilios pendientes de hoy) y Finalizados (completados o cancelados). Cada domicilio aparece como una tarjeta colapsable ordenada por hora.

+
+
+ + +

Avanzar el estado de un domicilio

+
+
1
+
+
Expandir la tarjeta
+

Haz clic en la tarjeta del domicilio para expandirla y ver el detalle completo: paciente, dirección, hora, orden médica adjunta y botones de acción.

+
+
+
+
2
+
+
Usar el botón de estado correspondiente
+
+ + + + + + + + +
Estado actualBotón disponible¿Qué ocurre?
programado✔ ConfirmarConfirmas que realizarás la visita
confirmado🚗 En caminoIndicas que saliste hacia el domicilio
en_camino🏠 En domicilioIndicas que llegaste — se registra la hora de llegada automáticamente
en_domicilio✅ CompletarFinalizas la visita — se registra la hora de salida automáticamente
+
+
+
+
+ ⚠️ Si necesitas cancelar un domicilio, deberás ingresar el motivo. Consulta con el administrador si tienes dudas sobre cuándo cancelar. +
+ + +

Registrar notas clínicas y libres

+
+
1
+
+
Ficha clínica del paciente
+

Dentro de la tarjeta del domicilio, busca la sección Ficha clínica. Puedes registrar:

+
    +
  • Antecedentes — patologías previas del paciente
  • +
  • Medicamentos — medicamentos que toma actualmente
  • +
  • Acudiente — nombre y documento del acudiente si el paciente es menor de edad
  • +
+
+
+
+
2
+
+
Nota libre
+

Haz clic en + Agregar nota. Puedes escribir un título, el cuerpo de la nota y adjuntar una imagen (por ejemplo, una foto del estado del paciente o de las muestras tomadas).

+
✅ Las notas quedan visibles para el administrador en el panel de detalle y en el informe imprimible del domicilio.
+
+
+ + +

Agregar servicios extra

+
+
1
+
+
¿Cuándo usar esto?
+

Si durante la visita realizas servicios adicionales a los de la orden original (ej: una inyección, una curación, toma de tensión arterial), puedes registrarlos para que queden en el historial.

+
+
+
+
2
+
+
Agregar el servicio
+

En la tarjeta del domicilio, haz clic en + Servicio extra. Selecciona el tipo, agrega una descripción y, si genera cobro adicional, marca la opción Requiere pago e ingresa el valor.

+
+
+
+ + + + + + + + + + + +
Tipo disponibleDescripción
inyeccionAdministración de medicamento inyectable
curaCuración de herida o procedimiento de limpieza
nebulizacionTerapia de nebulización
toma_muestraToma de muestra adicional
tension_arterialMedición de tensión arterial
glucometriaMedición de glucosa en sangre
otroCualquier otro procedimiento (especificar en descripción)
+
+ +
+
+ + +
+
+
📋
+
+

Manual de Uso — Administrador · Formularios

+

🛡️ Admin 👩‍⚕️ Enfermero (envío)   Desde lab_formularios.php

+
+
+
+ + +

Crear una plantilla de formulario

+
+ Solo el Admin puede crear y editar plantillas. Los enfermeros solo pueden enviar formularios existentes. +
+
+
1
+
+
Ir al builder
+

Desde lab_formularios.php, haz clic en + Nueva plantilla. Se abrirá el editor lab_formulario_builder.php en una ventana nueva.

+
+
+
+
2
+
+
Configurar el encabezado del documento
+

En el panel derecho del builder puedes personalizar: nombre de la empresa, subtítulo, logo (imagen base64), color del encabezado y pie de página. Estos datos aparecerán en el PDF imprimible.

+
+
+
+
3
+
+
Agregar campos al formulario
+

Arrastra campos del panel izquierdo al canvas central. Tipos disponibles:

+
+ Texto + Textarea + Número + Fecha / Hora + Select + Radio + Checkbox + Lista marcable + Vinculado al paciente + Párrafo inline + Firma paciente + Firma profesional +
+
+
+
+
4
+
+
Configurar la firma
+

En la sección Firma del builder puedes activar los modos permitidos: ✍️ Canvas (dibujo) y/o 📷 Foto. Si la firma es obligatoria, marca Requerida.

+
+
+
+
5
+
+
Guardar la plantilla
+

Haz clic en 💾 Guardar. La plantilla quedará disponible en lab_formularios.php para ser enviada a pacientes.

+
✅ Cada vez que edites el esquema de campos, la versión de la plantilla se incrementa automáticamente.
+
+
+ + +

Enviar un formulario a un paciente

+
+
1
+
+
Seleccionar la plantilla y hacer clic en Enviar
+

En lab_formularios.php busca la plantilla que deseas enviar y haz clic en el botón 📤 Enviar. Se abrirá un modal.

+
+
+
+
2
+
+
Buscar y seleccionar el paciente
+

Empieza a escribir el nombre o documento del paciente. Al seleccionarlo, verás una vista previa de los campos que se pre-llenarán automáticamente con sus datos.

+
+
+
+
3
+
+
Elegir el canal de envío
+

Selecciona cómo compartirás el enlace:

+
    +
  • WhatsApp — genera un mensaje pre-redactado con el enlace listo para enviar desde en la conversación del paciente
  • +
  • Solo link — copia el enlace y compártelo como prefieras
  • +
+
+
+
+
4
+
+
Confirmar el envío
+

Haz clic en Generar enlace. Se creará un token único para ese paciente. El enlace generado no tiene fecha de vencimiento y es de un solo uso (no permite modificar la respuesta una vez enviada).

+
✅ El enlace funciona sin que el paciente necesite crear cuenta ni iniciar sesión.
+
+
+ + +

Revisar las respuestas de un formulario

+
+
1
+
+
Ir a la pestaña de envíos
+

En lab_formularios.php, haz clic en la pestaña 📋 Envíos. Verás el historial de todos los formularios enviados con su estado actual.

+
+ ⏳ pendiente + ✅ completado + ✍️ firmado + ❌ expirado +
+
+
+
+
2
+
+
Ver la respuesta del paciente
+

En formularios con estado completado o firmado, aparecerá el botón 👁 Ver. Este abrirá un modal con todas las respuestas campo por campo y la imagen de la firma.

+
+
+
+
3
+
+
Descargar el PDF
+

Haz clic en el ícono 🖨️ junto al botón Ver, o en el botón Ver / Imprimir PDF dentro del modal. Esto abre ver_formulario_enviado.php con el documento completo listo para imprimir o guardar como PDF.

+
+
+ + +

Firmar como profesional

+
+ ⚠️ Esta opción solo aplica cuando la plantilla tiene un campo firma_profesional en su diseño. +
+
+
1
+
+
Abrir el formulario completado
+

Ve a ver_formulario_enviado.php?id=X con tu sesión activa (admin o el enfermero que lo envió). Verás el documento con el canvas de firma del profesional en la posición configurada.

+
✅ Si el paciente aún no ha completado el formulario, el canvas de firma profesional no aparecerá hasta que lo complete.
+
+
+
+
2
+
+
Dibujar la firma y guardar
+

Dibuja tu firma en el canvas usando el mouse o la pantalla táctil. Haz clic en 💾 Guardar firma. La firma quedará vinculada al documento de forma permanente.

+
+
+ + +

Verificar la autenticidad de un documento

+
+
1
+
+
Obtener el hash SHA-256 del documento
+

Al pie del documento en ver_formulario_enviado.php encontrarás el sello de integridad — una cadena hexadecimal de 64 caracteres. Este hash identifica únicamente este documento.

+
+
+
+
2
+
+
Ir al verificador público
+

Accede a verificar_formulario.php?h=HASH_AQUI o comparte ese enlace con la persona que necesita verificar la autenticidad. El sistema mostrará:

+
    +
  • Nombre del formulario y del paciente
  • +
  • Fecha en que fue completado
  • +
  • Estado actual del documento
  • +
  • Confirmación de que el sello es válido ✅ o inválido ❌
  • +
+
✅ Esta verificación es pública — cualquier persona con el hash puede usarla sin necesidad de tener cuenta en el sistema.
+
+
+ +
+
+ +

+ Manual de uso — Sistema de Laboratorio · 30/03/2026 +

+ +
+
+ + + + + + diff --git a/DOCUMENTACION_LAB.md b/DOCUMENTACION_LAB.md new file mode 100644 index 0000000..1bc59de --- /dev/null +++ b/DOCUMENTACION_LAB.md @@ -0,0 +1,522 @@ +# Documentación del Sistema de Laboratorio + +> **Sistema**: Laboratorio Clínico — Módulos de Agendamiento y Formularios +> **Última actualización**: 27/03/2026 + +--- + +## Tabla de contenidos + +1. [Módulo de Agendamiento (Domicilios)](#1-módulo-de-agendamiento-domicilios) + - [¿Qué es?](#qué-es) + - [Archivos involucrados](#archivos-involucrados) + - [Base de datos](#base-de-datos) + - [Roles y permisos](#roles-y-permisos) + - [Flujo de estados](#flujo-de-estados) + - [Funcionalidades](#funcionalidades) + - [API Endpoints](#api-endpoints) +2. [Módulo de Formularios](#2-módulo-de-formularios) + - [¿Qué es?](#qué-es-1) + - [Archivos involucrados](#archivos-involucrados-1) + - [Base de datos](#base-de-datos-1) + - [Roles y permisos](#roles-y-permisos-1) + - [Flujo completo](#flujo-completo) + - [Tipos de campos del Builder](#tipos-de-campos-del-builder) + - [Firma digital](#firma-digital) + - [Enlace público y vigencia](#enlace-público-y-vigencia) + - [PDF y visualización del documento](#pdf-y-visualización-del-documento) + - [Sello de integridad SHA-256](#sello-de-integridad-sha-256) + - [Firma del profesional](#firma-del-profesional) + - [API Endpoints](#api-endpoints-1) + +--- + +## 1. Módulo de Agendamiento (Domicilios) + +### ¿Qué es? + +El módulo de agendamiento gestiona los **servicios de toma de muestras a domicilio**. Permite crear, asignar, seguir y completar visitas médicas domiciliarias. Los administradores gestionan la agenda desde `lab_domicilios.php`; los enfermeros gestionan su propia agenda desde `enfermero_portal.php`. + +--- + +### Archivos involucrados + +| Archivo | Descripción | +|---|---| +| `lab_domicilios.php` | Vista principal del admin — tabla con filtros, detalle del domicilio, formularios recibidos, exportar CSV | +| `enfermero_portal.php` | Portal exclusivo del enfermero — su agenda personal del día, ordenada por hora, con tarjetas colapsables | +| `lab_enfermeras.php` | CRUD del personal de enfermería | +| `classes/lab/Domicilio.php` | Clase ORM — crear, editar, cambiar estado, estadísticas | +| `classes/lab/Enfermera.php` | Clase ORM — CRUD, agenda por enfermero, carga de trabajo | +| `classes/lab/Asignacion.php` | Clase ORM — asignar / reasignar / liberar enfermero a domicilio | +| `api/lab/get_domicilios.php` | GET — lista de domicilios con filtros y estadísticas del día | +| `api/lab/save_domicilio.php` | POST — crear, actualizar o cambiar estado | +| `api/lab/update_domicilio_enfermero.php` | POST — el enfermero avanza el estado desde su portal | +| `api/lab/my_agenda.php` | GET — agenda del enfermero actualmente autenticado | +| `api/lab/save_asignacion.php` | POST — asignar o reasignar enfermero | +| `api/lab/get_asignaciones.php` | GET — asignaciones por fecha | +| `api/lab/registrar_pago.php` | POST — registrar pago de un domicilio | +| `api/lab/save_servicio_extra.php` | POST — agregar servicio realizado durante la visita | +| `api/lab/get_notas_domicilio.php` | GET — notas clínicas y libres del domicilio | +| `api/lab/upload_nota_imagen.php` | POST — subir imagen adjunta a una nota | +| `api/lab/crear_desde_whatsapp.php` | GET/POST — crear paciente u orden desde una conversación de WhatsApp activa | + +--- + +### Base de datos + +#### Tabla `lab_domicilios` + +Tabla principal. Cada fila es un servicio a domicilio. + +| Campo | Tipo | Descripción | +|---|---|---| +| `id` | INT PK | Identificador único | +| `paciente_id` | INT FK | Paciente al que se le realiza el servicio | +| `orden_id` | INT FK NULL | Orden médica adjunta (opcional) | +| `direccion` | TEXT | Dirección completa de la visita | +| `ciudad` | VARCHAR(100) | Ciudad | +| `barrio` | VARCHAR(100) | Barrio | +| `indicaciones_dir` | TEXT | Referencias o indicaciones adicionales ("apto 302, tocar campanilla") | +| `fecha_programada` | DATE | Fecha de la visita | +| `hora_programada` | TIME | Hora de la visita | +| `tipo_servicio` | VARCHAR(100) | Tipo de servicio (toma de muestra, etc.) | +| `tipo_cliente` | ENUM | `particular` / `seguro` / `eps` | +| `examenes_solicitados` | TEXT | Lista de exámenes (cuando no hay orden médica) | +| `seguro_nombre` | VARCHAR | Nombre del seguro o EPS | +| `autorizacion` | VARCHAR | Número de autorización | +| `valor_domicilio` | DECIMAL | Valor del servicio | +| `valor_copago` | DECIMAL | Copago a cargo del cliente | +| `copago_laboratorio` | DECIMAL | Copago al laboratorio | +| `pago_estado` | ENUM | `pending` / `pagado` / `exento` | +| `pago_modo` | ENUM | `efectivo` / `transferencia` / `otro` | +| `pago_monto` | DECIMAL | Monto pagado | +| `pago_fecha` | DATETIME | Fecha del pago | +| `pago_notas` | TEXT | Notas sobre el pago | +| `estado` | ENUM | Ver [Flujo de estados](#flujo-de-estados) | +| `motivo_cancelacion` | TEXT | Motivo si fue cancelado (obligatorio) | +| `fecha_reprogramada` | DATE | Nueva fecha si fue reprogramado | +| `hora_llegada` | TIME | Registrada automáticamente al iniciar la visita | +| `hora_salida` | TIME | Registrada automáticamente al completar | +| `observaciones` | TEXT | Observaciones del resultado de la visita | +| `muestras_tomadas` | TEXT | Lista de muestras obtenidas | +| `notas_admin` | TEXT | Notas internas del equipo administrativo | +| `creado_por` | INT FK | Usuario que creó el registro | + +#### Tabla `lab_enfermeras` + +Personal de enfermería disponible para asignación. + +Campos: `id`, `numero_documento`, `tipo_documento` (CC/CE/TI/PA), `nombre_completo`, `telefono`, `telefono_alt`, `email`, `zona`, `notas`, `is_active`. + +#### Tabla `lab_asignaciones` + +Asignación de enfermero a domicilio. Máximo un enfermero activo por domicilio (`UNIQUE KEY` en `domicilio_id`). + +Campos: `id`, `domicilio_id`, `enfermera_id`, `asignada_por`, `estado` (`asignada` / `confirmada` / `liberada` / `completada`), `notas`. + +#### Tabla `lab_servicios_extra` + +Servicios realizados por el enfermero durante la visita, adicionales a la orden original. + +Tipos disponibles: `inyeccion`, `cura`, `nebulizacion`, `toma_muestra`, `tension_arterial`, `glucometria`, `otro`. + +Campos: `id`, `domicilio_id`, `descripcion`, `tipo`, `notas`, `requiere_pago`, `valor`, `realizado_por`. + +#### Tabla `lab_domicilio_notas` + +Notas registradas por el enfermero durante la visita. + +Dos tipos: +- **`clinica`**: datos de la ficha clínica — antecedentes, medicamentos, acudiente (si el paciente es menor de edad). +- **`libre`**: nota libre con título, cuerpo de texto enriquecido e imagen adjunta. + +Campos: `id`, `domicilio_id`, `enfermera_id`, `tipo`, `antecedentes`, `medicamentos`, `acudiente_nombre`, `acudiente_documento`, `titulo`, `cuerpo`, `imagen_path`. + +--- + +### Roles y permisos + +| Rol | Acceso | +|---|---| +| **Admin** | Crear, editar y ver todos los domicilios. Asignar/reasignar enfermeros. Registrar pagos. Exportar Excel. Ver informe completo con notas. Ver agenda de cualquier enfermero usando `?eid=X`. | +| **Enfermero** | Solo ve su propia agenda (`enfermero_portal.php`). Avanza el estado de sus domicilios asignados. Agrega servicios extra. Registra notas clínicas y libres. Visualiza órdenes médicas adjuntas. | + +> **Redirección automática**: si el usuario autenticado tiene rol `enfermero`, `lab_domicilios.php` lo redirige inmediatamente a `enfermero_portal.php`. + +Los roles se definen en `admin_users.role` (ENUM `admin` / `enfermero`) y `admin_users.enfermera_id` (FK a `lab_enfermeras`). + +--- + +### Flujo de estados + +``` +[programado] + │ + │ El enfermero confirma que realizará la visita + ▼ +[confirmado] + │ + │ El enfermero sale hacia el domicilio + ▼ +[en_camino] + │ + │ El enfermero llega → hora_llegada se registra automáticamente + ▼ +[en_domicilio] + │ + │ El enfermero finaliza → hora_salida se registra automáticamente + ▼ +[completado] + +Desde cualquier estado: + → [cancelado] (requiere motivo_cancelacion como campo obligatorio) + → [reprogramado] (requiere fecha_reprogramada) +``` + +**Transiciones permitidas al enfermero** (validadas en `update_domicilio_enfermero.php`): + +| Estado actual | Estados posibles | +|---|---| +| `programado` | `confirmado` | +| `confirmado` | `en_camino`, `cancelado` | +| `en_camino` | `en_domicilio`, `cancelado` | +| `en_domicilio` | `completado`, `cancelado` | + +El **admin** puede cambiar a cualquier estado directamente, incluyendo cancelar desde cualquier punto. + +--- + +### Funcionalidades + +- **Filtros**: por fecha, estado, enfermero asignado. Botón "Hoy" para filtro rápido. +- **Resumen del día**: conteo de domicilios por estado en la parte superior. +- **Panel de detalle**: al hacer clic en un domicilio se abre el panel lateral con toda la información, notas clínicas, notas libres e informe imprimible. +- **Asignar / Reasignar enfermero**: modal con lista del personal disponible. +- **Sin asignar**: badge con el conteo de domicilios que aún no tienen enfermero. +- **Registrar pago**: modal para marcar el cobro con modalidad y monto. +- **Servicios extra**: el enfermero los agrega desde su portal durante la visita. +- **Notas del enfermero**: ficha clínica con antecedentes, medicamentos, acudiente (si menor) y notas libres con imagen adjunta. +- **Informe de domicilio**: vista imprimible del domicilio con datos del paciente, ficha clínica y notas del enfermero. +- **Exportar CSV (Excel)**: exporta todos los domicilios filtrados, incluyendo las columnas de notas del enfermero (antecedentes, medicamentos, acudiente, notas libres). +- **Formularios recibidos**: pestaña dentro de `lab_domicilios.php` que muestra formularios enviados con filtro por plantilla y estado. +- **Portal del enfermero**: tarjetas colapsables ordenadas por hora, separadas en "Activos" y "Finalizados". Permite avanzar estados, agregar notas y ver órdenes. + +--- + +### API Endpoints + +| Endpoint | Método | Descripción | +|---|---|---| +| `api/lab/get_domicilios.php` | GET | Lista con filtros. `?id=X` para uno solo con detalle completo. | +| `api/lab/save_domicilio.php` | POST | Crear, editar o cambiar estado. `?solo_estado=true` para solo cambiar estado. | +| `api/lab/update_domicilio_enfermero.php` | POST | El enfermero avanza el estado de su domicilio. | +| `api/lab/my_agenda.php` | GET | Agenda del enfermero autenticado con servicios extra. | +| `api/lab/save_asignacion.php` | POST | Asignar o reasignar enfermero a domicilio. | +| `api/lab/get_asignaciones.php` | GET | Asignaciones por fecha. | +| `api/lab/registrar_pago.php` | POST | Registrar pago con monto y modalidad. | +| `api/lab/save_servicio_extra.php` | POST | Agregar servicio realizado durante la visita. | +| `api/lab/get_notas_domicilio.php` | GET | Notas del domicilio (clínicas y libres). | +| `api/lab/upload_nota_imagen.php` | POST | Subir imagen adjunta a una nota libre. | +| `api/lab/crear_desde_whatsapp.php` | GET/POST | Crear paciente u orden desde una conversación de WhatsApp activa. | + +--- + +## 2. Módulo de Formularios + +### ¿Qué es? + +El módulo de formularios permite crear **plantillas de documentos** (consentimientos, historias clínicas, autorizaciones, encuestas) mediante un builder visual, enviarlas a los pacientes por WhatsApp y recopilar sus respuestas con firma digital. El documento firmado genera un **sello de integridad SHA-256** que puede verificarse públicamente. + +--- + +### Archivos involucrados + +| Archivo | Descripción | +|---|---| +| `lab_formularios.php` | Vista principal — lista de plantillas y registro de envíos | +| `lab_formulario_builder.php` | Editor visual drag & drop (ventana separada, solo admin) | +| `form_cliente.php` | Página pública — el paciente llena y firma sin iniciar sesión | +| `ver_formulario_enviado.php` | Vista del documento firmado — acceso por ID (admin/enfermero) o token (cliente) | +| `verificar_formulario.php` | Verificación pública de autenticidad por hash SHA-256 | +| `classes/lab/Formulario.php` | Clase ORM — CRUD de plantillas, crear envíos, guardar respuestas, generar hash | +| `api/lab/get_formularios.php` | GET — lista plantillas o envíos | +| `api/lab/save_formulario.php` | POST — crear, editar y eliminar plantillas (solo admin) | +| `api/lab/send_formulario.php` | POST — crear instancia de envío, devolver URL pública y mensaje WhatsApp | +| `api/lab/submit_formulario.php` | GET/POST — cargar el formulario por token / guardar la respuesta del cliente | +| `api/lab/firmar_profesional.php` | POST — guardar firma del profesional (requiere sesión activa) | + +--- + +### Base de datos + +#### Tabla `lab_formularios` + +Plantillas de documentos creadas desde el builder. + +| Campo | Tipo | Descripción | +|---|---|---| +| `id` | INT PK | Identificador único | +| `nombre` | VARCHAR(150) | Nombre de la plantilla | +| `descripcion` | TEXT | Descripción visible al cliente | +| `categoria` | ENUM | `consentimiento` / `historia_clinica` / `autorizacion` / `encuesta` / `otro` | +| `esquema` | LONGTEXT | JSON con el array de campos del formulario | +| `permite_firma` | TINYINT(1) | El formulario tiene sección de firma global | +| `requiere_firma` | TINYINT(1) | La firma global es obligatoria | +| `firma_modos` | VARCHAR(50) | `canvas`, `foto` o `canvas,foto` (separados por coma) | +| `version` | SMALLINT | Se incrementa automáticamente al editar el esquema | +| `is_active` | TINYINT(1) | Soft-delete | +| `creado_por` | INT FK | Usuario que creó la plantilla | +| `doc_encabezado` | VARCHAR | Override del nombre de empresa en el documento | +| `doc_subtitulo` | VARCHAR | Override del subtítulo en el documento | +| `doc_logo_base64` | LONGTEXT | Override del logo en el documento | +| `doc_color` | VARCHAR(20) | Override del color del encabezado del documento | +| `doc_pie_pagina` | TEXT | Override del pie de página | + +#### Tabla `lab_form_envios` + +Cada fila es una instancia enviada a un paciente. + +| Campo | Tipo | Descripción | +|---|---|---| +| `id` | INT PK | Identificador único | +| `formulario_id` | INT FK | Plantilla enviada | +| `paciente_id` | INT FK NULL | Paciente asociado | +| `domicilio_id` | INT FK NULL | Domicilio asociado (opcional) | +| `token` | CHAR(64) UNIQUE | Token público de 64 caracteres hex (acceso sin sesión) | +| `datos_prefilled` | LONGTEXT | JSON con datos pre-llenados al enviar (incluye `__paciente.*`) | +| `datos_cliente` | LONGTEXT | JSON con las respuestas completadas por el cliente | +| `firma_svg` | LONGTEXT | Firma del paciente (PNG base64 — canvas o foto) | +| `ip_cliente` | VARCHAR(45) | IP del cliente al enviar el formulario | +| `user_agent` | VARCHAR(512) | Navegador del cliente | +| `estado` | ENUM | `pendiente` / `completado` / `firmado` / `expirado` | +| `enviado_por` | INT FK | Usuario que generó el enlace | +| `enviado_via` | ENUM | `whatsapp` / `email` / `link` | +| `expira_en` | DATETIME NULL | Siempre `NULL` — el enlace no expira | +| `completado_en` | DATETIME | Fecha y hora en que el cliente completó el formulario | +| `hash_verificacion` | CHAR(64) | Sello de integridad SHA-256 del documento | + +--- + +### Roles y permisos + +| Rol | Acceso | +|---|---| +| **Admin** | Crear, editar y eliminar plantillas desde el builder. Enviar formularios a cualquier paciente. Ver todos los envíos. Ver y descargar el PDF de cualquier formulario. | +| **Enfermero** | Enviar formularios existentes a sus pacientes. Ver solo sus propios envíos (`enviado_por = su user_id`). No puede crear ni editar plantillas. Puede firmar como profesional en los formularios que él mismo envió. | +| **Cliente (público)** | Accede a `form_cliente.php?t=TOKEN` sin ninguna autenticación. Llena y firma el formulario. Puede volver al mismo enlace en cualquier momento para ver el documento firmado y descargarlo como PDF. | + +--- + +### Flujo completo + +``` +1. ADMIN crea la plantilla + ├─ Abre lab_formulario_builder.php (se abre en ventana nueva) + ├─ Arrastra campos al canvas y los configura + ├─ Configura el diseño del documento (logo, color, encabezado, pie de página) + └─ Guarda → POST api/lab/save_formulario.php → lab_formularios + +2. ADMIN o ENFERMERO envía el formulario + ├─ lab_formularios.php → botón "Enviar" → modal + ├─ Busca y selecciona el paciente + ├─ Previsualiza los campos que llegarán pre-llenados + ├─ Selecciona el canal: WhatsApp o "solo link" + └─ POST api/lab/send_formulario.php + ├─ Genera token de 64 hex chars: bin2hex(random_bytes(32)) + ├─ Crea fila en lab_form_envios (estado=pendiente, expira_en=NULL) + └─ Devuelve URL pública y mensaje preformateado para WhatsApp + +3. CLIENTE recibe el enlace (por WhatsApp u otro medio) + ├─ Abre form_cliente.php?t=TOKEN + ├─ GET api/lab/submit_formulario.php?t=TOKEN → carga datos del formulario + └─ Si ya fue firmado antes → muestra pantalla de solo lectura con link al PDF + +4. CLIENTE llena el formulario + ├─ Campos "linked" llegan pre-llenados con datos del paciente (readonly si tienen valor) + ├─ Campos vacíos linked son editables para que el cliente los complete + ├─ Campos firma_profesional muestran aviso "uso exclusivo del profesional" + └─ Dibuja su firma (canvas) o adjunta una foto de firma + +5. CLIENTE envía + ├─ POST api/lab/submit_formulario.php + ├─ Se genera hash SHA-256 (contenido + firma + ID + token + timestamp) + ├─ Estado → "firmado" (si hay firma) o "completado" (sin firma) + └─ Pantalla de éxito con hash visible y botón para descargar el PDF + +6. PROFESIONAL firma (si el formulario lo requiere) + ├─ Admin/Enfermero abre ver_formulario_enviado.php?id=X con sesión activa + ├─ Aparece canvas de firma en la posición del campo firma_profesional + ├─ Dibuja su firma y hace clic en "Guardar firma" + └─ POST api/lab/firmar_profesional.php → guarda campo_id_svg en datos_cliente + +7. ADMIN/ENFERMERO revisa el resultado + ├─ lab_formularios.php → pestaña "Envíos" → icono "Ver respuesta" + └─ ver_formulario_enviado.php?id=X → documento HTML imprimible + +8. VERIFICACIÓN pública de integridad + └─ verificar_formulario.php?h=HASH_SHA256 + ├─ Busca en lab_form_envios.hash_verificacion + └─ Muestra: nombre del formulario, paciente, fecha, estado y si el sello es válido +``` + +--- + +### Tipos de campos del Builder + +#### Campos de entrada + +| Tipo | Descripción | +|---|---| +| `texto` | Campo de texto corto de una sola línea | +| `textarea` | Área de texto largo (varias líneas) | +| `numero` | Campo numérico | +| `fecha` | Selector de fecha | +| `hora` | Selector de hora | +| `select` | Lista desplegable con opciones configurables | +| `radio` | Selección única con opciones configurables | +| `checkbox` | Selección múltiple con opciones configurables | +| `lista_marcable` | Lista de ítems numerados con checkboxes | + +#### Campos de firma + +| Tipo | Descripción | +|---|---| +| `firma` | Firma del **paciente** — visible y editable en `form_cliente.php` | +| `firma_profesional` | Firma del **profesional** — bloqueada para el cliente; solo editable desde el panel admin/enfermero | + +#### Campos de contenido + +| Tipo | Descripción | +|---|---| +| `separador` | Separador visual o título de sección | +| `parrafo` | Bloque de texto estático (pre-formatado o flujo libre) | +| `parrafo_inline` | Párrafo con marcadores `{nombre_completo}`, `{telefono}`, etc. que se convierten en espacios editables si el valor está vacío | + +#### Campos vinculados al paciente (`tipo: linked`) + +Se auto-rellenan con los datos del paciente al momento de enviar. Si el valor existe → campo de solo lectura. Si está vacío → el cliente puede completarlo. + +| `linked_key` | Dato que extrae | +|---|---| +| `nombre_completo` | Nombre completo del paciente | +| `numero_documento` | Número de documento | +| `tipo_documento` | Tipo de documento | +| `fecha_nacimiento` | Fecha de nacimiento | +| `telefono` | Teléfono | +| `email` | Correo electrónico | +| `eps` | EPS o aseguradora | +| `direccion` | Dirección | + +--- + +### Firma digital + +**Modos disponibles** (configurados en la plantilla mediante `firma_modos`): + +| Modo | Funcionamiento | +|---|---| +| `canvas` | El cliente dibuja su firma con el dedo o el mouse. Se captura con `canvas.toDataURL('image/png')`. | +| `foto` | El cliente sube una imagen desde su cámara o galería (``). Se convierte a base64 con `FileReader`. | + +Ambos modos pueden estar activos simultáneamente en la misma plantilla. + +**Firma global vs. firma por campo:** +- Si el esquema **no incluye** campos tipo `firma`, se muestra una sección de firma global al pie del formulario. +- Si el esquema **incluye** campos `firma`, cada uno tiene su propio widget canvas independiente en la posición configurada dentro del formulario. + +--- + +### Enlace público y vigencia + +- **URL pública**: `form_cliente.php?t=TOKEN` +- **TOKEN**: 64 caracteres hexadecimales generados con `bin2hex(random_bytes(32))`. +- **Sin sesión**: el cliente no necesita crear cuenta ni iniciar sesión. +- **Sin vencimiento**: la columna `expira_en` existe en la tabla pero siempre es `NULL`. El enlace es permanente. +- **Bloqueo por estado**: si el formulario ya fue completado o firmado, el enlace muestra la pantalla de solo lectura. No permite modificar la respuesta. +- **Idempotencia**: si el cliente reintenta enviar (por error de red, por ejemplo), el sistema devuelve éxito con los datos ya guardados en lugar de crear un duplicado. + +--- + +### PDF y visualización del documento + +No se usa ninguna librería de generación de PDF en el backend. El documento es la página `ver_formulario_enviado.php` con estilos `@media print`. El usuario puede imprimirla o guardarla como PDF directamente desde el navegador. + +**Contenido del documento impreso:** +- Encabezado con logo, nombre, subtítulo, datos de contacto y color corporativo +- Datos del paciente (nombre, documento, fecha de nacimiento, teléfono, EPS) +- Respuestas del formulario campo por campo, en el orden del esquema +- Imagen de la firma del paciente +- Firma del profesional (si fue completada) +- Sello SHA-256 con link para verificar autenticidad +- Pie de página con fecha de generación e ID del documento + +**Formas de acceder al documento:** + +| URL | Quién puede acceder | +|---|---| +| `ver_formulario_enviado.php?id=X` | Admin (cualquier formulario) o Enfermero (solo los que él envió). Requiere sesión. | +| `ver_formulario_enviado.php?t=TOKEN` | Cliente u cualquier persona con el enlace. Sin sesión. Solo si el estado es `firmado` o `completado`. | + +--- + +### Sello de integridad SHA-256 + +Al guardar la respuesta del cliente, el sistema genera un hash SHA-256 que vincula de forma única el contenido del formulario con la firma y el momento en que se completó. + +**Construcción del hash** (en `Formulario::guardarRespuesta()`): + +``` +SHA-256 de: + JSON de los datos del cliente + + firma SVG/PNG del paciente + + ID interno del envío + + token del enlace + + timestamp del momento de registro +``` + +**¿Para qué sirve?** Cualquier persona con el hash puede ir a `verificar_formulario.php?h=HASH` para confirmar que: +- El documento existe en la base de datos. +- El nombre del formulario y del paciente. +- La fecha en que fue completado. +- El estado actual (firmado / completado). + +Si el documento fue alterado, el hash no coincidirá y la verificación fallará. + +--- + +### Firma del profesional + +Algunos formularios requieren que un profesional de salud también firme el documento, además del paciente. + +**Flujo:** +1. Al diseñar la plantilla en el builder se agrega un campo `tipo: firma_profesional` en la posición deseada. +2. Cuando el cliente llena el formulario en `form_cliente.php`, ese campo muestra solo un aviso: *"Uso exclusivo del profesional de salud"*. El cliente no puede interactuar con él. +3. Una vez que el cliente ha completado y enviado el formulario, el admin o enfermero abre `ver_formulario_enviado.php?id=X` con sesión activa y verá el canvas de firma en esa posición. +4. El profesional dibuja su firma y hace clic en "Guardar firma". +5. La firma se guarda mediante POST a `api/lab/firmar_profesional.php`. + +**Validaciones en el servidor:** +- Requiere sesión activa (`isUserLoggedIn()`). +- Si el usuario es enfermero, solo puede firmar en formularios que él mismo envió. +- Valida que el `campo_id` corresponde a un campo `tipo: firma_profesional` en el esquema del formulario. +- Valida que la imagen enviada sea un data URI de imagen válido. + +> Si el formulario se accede via `?t=TOKEN` (cliente público), el canvas **no aparece**. En su lugar se muestra un aviso *"Pendiente de firma del profesional"* (solo visible en pantalla, no en el PDF impreso). + +--- + +### API Endpoints + +| Endpoint | Método | Autenticación | Descripción | +|---|---|---|---| +| `api/lab/get_formularios.php` | GET | Sesión | Lista plantillas. `?id=X` para una sola. `?envios=1` para lista de envíos. | +| `api/lab/save_formulario.php` | POST | Admin | Crear, editar o eliminar una plantilla. | +| `api/lab/send_formulario.php` | POST | Admin / Enfermero | Crear instancia de envío. Devuelve URL pública y mensaje para WhatsApp. | +| `api/lab/submit_formulario.php` | GET | Público | Cargar el formulario por token (sin sesión). | +| `api/lab/submit_formulario.php` | POST | Público | Guardar la respuesta y firma del cliente. | +| `api/lab/firmar_profesional.php` | POST | Sesión | Guardar la firma del profesional en un campo `firma_profesional`. | + +--- + +*Documentación generada para uso interno del equipo.* diff --git a/api/block_user.php b/api/block_user.php new file mode 100644 index 0000000..c010f7a --- /dev/null +++ b/api/block_user.php @@ -0,0 +1,36 @@ + false, 'error' => 'user_id and blocked are required']); + exit; + } + $blocked = (bool)$input['blocked']; + + if ($userId <= 0) { + http_response_code(400); + echo json_encode(['success' => false, 'error' => 'Invalid user_id']); + exit; + } + + $db = Database::getInstance(); + $status = $blocked ? 'blocked' : 'active'; + $db->update('users', ['status' => $status], 'id = :id', ['id' => $userId]); + + echo json_encode(['success' => true, 'blocked' => $blocked, 'status' => $status]); +} catch (Exception $e) { + http_response_code(500); + echo json_encode(['success' => false, 'error' => $e->getMessage()]); +} diff --git a/api/get_conversation_list.php b/api/get_conversation_list.php index f065734..ed99eb6 100644 --- a/api/get_conversation_list.php +++ b/api/get_conversation_list.php @@ -26,6 +26,7 @@ try { u.id as user_id, u.phone_number, COALESCE(u.name, u.phone_number) as name, + u.status as user_status, c.content as last_message, c.direction as last_direction, c.message_type as last_message_type, @@ -43,7 +44,7 @@ try { FROM conversations GROUP BY user_id ) - GROUP BY u.id, u.phone_number, u.name, c.content, c.direction, c.message_type, c.created_at, c.status, u.advisor_requested, u.terms_pending, u.terms_accepted_at + GROUP BY u.id, u.phone_number, u.name, u.status, c.content, c.direction, c.message_type, c.created_at, c.status, u.advisor_requested, u.terms_pending, u.terms_accepted_at ORDER BY c.created_at DESC LIMIT 50" ); @@ -59,6 +60,7 @@ try { 'user_id' => intval($conv['user_id']), 'phone_number' => $conv['phone_number'], 'name' => $conv['name'], + 'user_status' => $conv['user_status'] ?? 'active', 'last_message' => $conv['last_message'] ?? '', 'last_direction' => $conv['last_direction'] ?? 'incoming', 'last_message_type' => $conv['last_message_type'] ?? 'text', diff --git a/api/lab/get_reporte_enfermero.php b/api/lab/get_reporte_enfermero.php new file mode 100644 index 0000000..54379f3 --- /dev/null +++ b/api/lab/get_reporte_enfermero.php @@ -0,0 +1,84 @@ +fetchAll( + "SELECT + e.id AS enfermera_id, + e.nombre_completo AS enfermera, + YEAR(d.fecha_programada) AS anio, + MONTH(d.fecha_programada) AS mes, + COUNT(*) AS total, + SUM(d.estado = 'completado') AS completados, + SUM(d.estado = 'cancelado') AS cancelados, + SUM(d.estado NOT IN ('completado','cancelado')) AS en_proceso + FROM lab_domicilios d + JOIN lab_asignaciones a ON a.domicilio_id = d.id + AND a.estado NOT IN ('liberada') + JOIN lab_enfermeras e ON e.id = a.enfermera_id + $where + GROUP BY e.id, YEAR(d.fecha_programada), MONTH(d.fecha_programada) + ORDER BY e.nombre_completo, YEAR(d.fecha_programada) DESC, MONTH(d.fecha_programada) DESC", + $params +); + +$meses_es = [ + 1=>'Ene',2=>'Feb',3=>'Mar',4=>'Abr',5=>'May',6=>'Jun', + 7=>'Jul',8=>'Ago',9=>'Sep',10=>'Oct',11=>'Nov',12=>'Dic', +]; + +foreach ($filas as &$f) { + $t = (int)$f['total']; + $f['tasa_pct'] = $t ? round((int)$f['completados'] / $t * 100) : 0; + $f['mes_nombre'] = $meses_es[(int)$f['mes']] ?? $f['mes']; +} +unset($f); + +// Lista de enfermeras para el selector del filtro +$enfermeras = $db->fetchAll( + "SELECT id, nombre_completo FROM lab_enfermeras WHERE activa = 1 ORDER BY nombre_completo" +); + +echo json_encode([ + 'success' => true, + 'filas' => $filas, + 'enfermeras' => $enfermeras, +], JSON_UNESCAPED_UNICODE); diff --git a/config/config.php b/config/config.php index 30518cd..e4940da 100644 --- a/config/config.php +++ b/config/config.php @@ -485,13 +485,20 @@ function authenticateUser($username, $password) { $updateLogin = $pdo->prepare("UPDATE admin_users SET last_login = NOW() WHERE id = ?"); $updateLogin->execute([$admin['id']]); - // Cargar módulos del rol - $modules = []; + // Cargar módulos del rol (slug y permiso) + $modules = []; + $modulePermissions = []; $roleSlug = $admin['role'] ?? 'admin'; if (!empty($admin['role_id'])) { - $modStmt = $pdo->prepare("SELECT module_slug FROM role_modules WHERE role_id = ?"); + $modStmt = $pdo->prepare( + "SELECT module_slug, COALESCE(permission,'write') AS permission + FROM role_modules WHERE role_id = ?" + ); $modStmt->execute([$admin['role_id']]); - $modules = array_column($modStmt->fetchAll(PDO::FETCH_ASSOC), 'module_slug'); + foreach ($modStmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $modules[] = $row['module_slug']; + $modulePermissions[$row['module_slug']] = $row['permission']; + } } elseif ($roleSlug === 'admin') { // Fallback: admin sin role_id tiene todos los módulos $modules = array_keys(SYSTEM_MODULES); @@ -500,14 +507,15 @@ function authenticateUser($username, $password) { } return [ - 'id' => $admin['id'], - 'username' => $admin['username'], - 'full_name' => $admin['full_name'], - 'email' => $admin['email'], - 'role' => $roleSlug, - 'role_id' => $admin['role_id'] ?? null, - 'enfermera_id' => $admin['enfermera_id'] ?? null, - 'modules' => $modules, + 'id' => $admin['id'], + 'username' => $admin['username'], + 'full_name' => $admin['full_name'], + 'email' => $admin['email'], + 'role' => $roleSlug, + 'role_id' => $admin['role_id'] ?? null, + 'enfermera_id' => $admin['enfermera_id'] ?? null, + 'modules' => $modules, + 'module_permissions' => $modulePermissions, ]; } } else { @@ -609,6 +617,23 @@ function hasModule(string $slug): bool { return in_array($slug, $modules, true); } +/** + * Comprueba si el usuario tiene permiso de escritura (write) en el módulo. + * Los administradores tienen acceso de escritura total. + * Un usuario con permission='read' solo puede ver, no modificar. + */ +function hasModuleWrite(string $slug): bool { + // Admins siempre pueden escribir + if (userHasRole('admin')) return true; + + $permissions = $_SESSION['admin_user']['module_permissions'] ?? null; + if ($permissions === null) { + // Sesión legacy sin 'module_permissions': si tiene el módulo, se asume write + return hasModule($slug); + } + return ($permissions[$slug] ?? '') === 'write'; +} + /** * Detiene la ejecución si el usuario no tiene el rol requerido. */ diff --git a/conversations.php b/conversations.php index 81bb0da..3c6c237 100644 --- a/conversations.php +++ b/conversations.php @@ -594,6 +594,29 @@ if (!isUserLoggedIn()) { .notification-toast.urgent { border-left: 4px solid #e74c3c; } + /* Badge de usuario bloqueado en el header */ + #blocked-indicator { + display: none; + margin-left: 8px; + background: #dc3545; + color: #fff; + font-size: 10px; + font-weight: 700; + padding: 2px 8px; + border-radius: 10px; + vertical-align: middle; + letter-spacing: .3px; + } + /* Botón bloquear — estados */ + #block-user-btn.is-blocked { background: rgba(220,53,69,.18); border-color: #dc3545; color: #dc3545; } + #block-user-btn.is-blocked:hover { background: rgba(220,53,69,.28); } + /* Ítem bloqueado en sidebar */ + .conversation-item.user-blocked { opacity: .6; } + .conversation-item.user-blocked .conversation-name::after { + content: ' 🚫'; + font-size: 10px; + } + /* Visual destacado para notificaciones de tipo "attention" (ej. usuario subió documentos) */ .notification-toast.attention { background: linear-gradient(90deg, #fff7e6, #fff3e0); @@ -912,6 +935,7 @@ if (!isUserLoggedIn()) {
Usuario + BLOQUEADO