El arreglo anterior dejó un defecto: obtenerOCrearDesdeWhatsapp() copiaba
users.phone_number al teléfono de la ficha, y para quien oculta su número ahí
va el BSUID. normalizarTelefono() le quitaba el punto y las letras, así que
"CO.1761088155094242" quedaba guardado como "1761088155094242": un número de
16 dígitos, falso, con apariencia de real, dentro de una historia clínica y
sin que nadie lo notara. Ahora esa ficha se crea sin teléfono, que es la
verdad: no lo tenemos.
- esBsuid() queda definido una sola vez, en config.php, que es donde lo ven
tanto el servicio de WhatsApp como las clases del laboratorio.
- La lista y el detalle de pacientes dicen "Solo por WhatsApp" en vez de
mostrar el identificador crudo: recepción necesita entender por qué no
puede llamar, no ver un código.
- Al crear la ficha desde una conversación se le pide el número con el botón
de Meta, una sola vez (users.contacto_pedido_at). El texto es editable
desde configuración. Solo se marca como pedido si Meta aceptó el envío,
para poder reintentar si falló.
- tests/test_bsuid.php: 30 verificaciones sobre identidad, envío, vinculación
y creación de fichas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nuevo módulo `soporte` con la documentación del proyecto en cuatro secciones:
manual de usuario (visible para todos), y documentación técnica, arquitectura
y operación (solo administradores).
- Markdown.php: renderizador propio del subconjunto que usa la documentación
(encabezados, listas anidadas, tablas, código, citas). Escapa todo el texto
antes de aplicar formato, así que los .md no pueden inyectar HTML. Se
prefirió un archivo auditable a incorporar una dependencia externa.
- DocIndex.php: descubre los .md, arma el árbol, resuelve acceso por sección
y construye el índice del buscador.
- Generadores.php: expande marcadores {{modulos}}, {{endpoints}}, {{tablas}},
{{roles}} y {{servicios}} leyendo el código y la base en cada carga, para
que los inventarios no puedan quedar desactualizados.
Se registra en SYSTEM_MODULES y se concede a los 12 roles con permission=read.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Genera par de llaves RSA (cert en config/qz/)
- api/qz_sign.php firma los print jobs con SHA512
- assets/js/qz-print.js conecta QZ Tray y envía trabajo HTML
- ver_formulario_enviado.php carga QZ cuando ?autoprint=1
- Fallback a window.print() si QZ no está instalado
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a patient with a cedula is selected, the system queries RIPS
Manager for exams registered in the last 5 minutes. A green banner
shows the count with a "Cargar" button that pre-fills TomSelect.
Unmapped exam codes shown as a warning. Banner clears on deselect/reset.
- config.php: RIPS_MANAGER_URL constant from env var
- api/lab/get_examenes_rips.php: proxy + codigo_legacy mapper
- recepcion.php: banner HTML + consultarExamenesRips/cargarExamenesRips JS
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Agrega /api/lab/ingest_paciente.php con autenticación por X-Lab-Key
(LAB_SYNC_KEY en config). Hace UPSERT por numero_documento sin requerir
sesión de admin — permite que RIPS Manager sincronice pacientes directo
a lab_pacientes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- authenticateUser incluye turnero_lugar_id en la sesión
- login.php redirige con desk_id/lugar_id si el usuario tiene lugar fijo (sin necesitar IP)
- recepcion.php redirige con desk_id si la sesión tiene turnero_lugar_id, evitando el modal de selección
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>