feat: auto-load RIPS exams when patient is selected in recepcion
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>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
bffa38e9c0
commit
d0a42dba33
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* GET /api/lab/get_examenes_rips.php?cedula=X
|
||||||
|
* Consulta al RIPS Manager los exámenes registrados en los últimos 5 minutos
|
||||||
|
* para la cédula indicada, y los mapea a exam_tipos locales por codigo_legacy.
|
||||||
|
*/
|
||||||
|
require_once __DIR__ . '/_helpers.php';
|
||||||
|
requireMethod('GET');
|
||||||
|
|
||||||
|
$cedula = trim($_GET['cedula'] ?? '');
|
||||||
|
if (!$cedula) jsonError('cedula requerida', 400);
|
||||||
|
|
||||||
|
$ripsUrl = defined('RIPS_MANAGER_URL') ? rtrim(RIPS_MANAGER_URL, '/') : '';
|
||||||
|
if (!$ripsUrl) jsonError('RIPS_MANAGER_URL no configurado en el servidor', 503);
|
||||||
|
|
||||||
|
// Llamada server-to-server al RIPS Manager
|
||||||
|
$url = $ripsUrl . '/pacientes/examenes?cedula=' . urlencode($cedula);
|
||||||
|
$ctx = stream_context_create([
|
||||||
|
'http' => [
|
||||||
|
'method' => 'GET',
|
||||||
|
'header' => 'X-Lab-Key: ' . LAB_SYNC_KEY . "\r\n",
|
||||||
|
'timeout' => 8,
|
||||||
|
'ignore_errors' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$resp = @file_get_contents($url, false, $ctx);
|
||||||
|
if ($resp === false) jsonError('No se pudo conectar con RIPS Manager', 503);
|
||||||
|
|
||||||
|
$data = json_decode($resp, true);
|
||||||
|
if (!($data['ok'] ?? false)) {
|
||||||
|
jsonError($data['error'] ?? 'Error en RIPS Manager', 502);
|
||||||
|
}
|
||||||
|
|
||||||
|
$examenes = $data['examenes'] ?? [];
|
||||||
|
if (!$examenes) {
|
||||||
|
jsonOk(['encontrados' => [], 'no_mapeados' => [], 'total_rips' => 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mapear COD_EXAMEN → exam_tipos por codigo_legacy
|
||||||
|
$codigos = array_values(array_unique(array_filter(
|
||||||
|
array_map(fn($e) => trim($e['cod_examen'] ?? ''), $examenes)
|
||||||
|
)));
|
||||||
|
|
||||||
|
$encontrados = [];
|
||||||
|
$no_mapeados = [];
|
||||||
|
|
||||||
|
if ($codigos) {
|
||||||
|
$ph = implode(',', array_fill(0, count($codigos), '?'));
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
"SELECT id AS exam_tipo_id, codigo, nombre, codigo_legacy
|
||||||
|
FROM exam_tipos
|
||||||
|
WHERE codigo_legacy IN ($ph) AND activo = 1"
|
||||||
|
);
|
||||||
|
$stmt->execute($codigos);
|
||||||
|
$mapa = [];
|
||||||
|
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||||||
|
$mapa[trim($r['codigo_legacy'])] = $r;
|
||||||
|
}
|
||||||
|
foreach ($codigos as $cod) {
|
||||||
|
if (isset($mapa[$cod])) {
|
||||||
|
$encontrados[] = [
|
||||||
|
'exam_tipo_id' => (int)$mapa[$cod]['exam_tipo_id'],
|
||||||
|
'codigo' => $mapa[$cod]['codigo'],
|
||||||
|
'nombre' => $mapa[$cod]['nombre'],
|
||||||
|
'cod_rips' => $cod,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$no_mapeados[] = $cod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$meta = $examenes[0] ?? [];
|
||||||
|
jsonOk([
|
||||||
|
'encontrados' => $encontrados,
|
||||||
|
'no_mapeados' => $no_mapeados,
|
||||||
|
'total_rips' => count($examenes),
|
||||||
|
'recepcion_id' => $meta['recepcion_id'] ?? null,
|
||||||
|
'hora' => $meta['hora'] ?? null,
|
||||||
|
]);
|
||||||
@@ -406,6 +406,10 @@ if (!defined('MIGRATION_TOKEN')) {
|
|||||||
if (!defined('LAB_SYNC_KEY')) {
|
if (!defined('LAB_SYNC_KEY')) {
|
||||||
define('LAB_SYNC_KEY', getenv('LAB_SYNC_KEY') ?: 'rips-lab-sync-2026');
|
define('LAB_SYNC_KEY', getenv('LAB_SYNC_KEY') ?: 'rips-lab-sync-2026');
|
||||||
}
|
}
|
||||||
|
// URL base del RIPS Manager (para consultas server-to-server)
|
||||||
|
if (!defined('RIPS_MANAGER_URL')) {
|
||||||
|
define('RIPS_MANAGER_URL', getenv('RIPS_MANAGER_URL') ?: '');
|
||||||
|
}
|
||||||
define('TIMEZONE', 'America/Bogota');
|
define('TIMEZONE', 'America/Bogota');
|
||||||
|
|
||||||
// Información del desarrollador
|
// Información del desarrollador
|
||||||
|
|||||||
@@ -625,6 +625,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Banner RIPS: exámenes detectados automáticamente -->
|
||||||
|
<div id="banner-rips" class="d-none mt-1 mb-1 p-2 rounded d-flex align-items-center justify-content-between gap-2"
|
||||||
|
style="background:#f0fdf4;border:1px solid #86efac;font-size:.82rem">
|
||||||
|
<div>
|
||||||
|
<i class="fas fa-flask text-success me-1"></i>
|
||||||
|
<span id="banner-rips-txt" class="fw-semibold"></span>
|
||||||
|
<span id="banner-rips-warn" class="d-none text-warning ms-2"></span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-1 flex-shrink-0">
|
||||||
|
<button class="btn btn-success btn-sm py-0 px-2" onclick="cargarExamenesRips()">
|
||||||
|
<i class="fas fa-check me-1"></i>Cargar
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary btn-sm py-0 px-2" onclick="descartarRips()">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ── Sección 3: Exámenes ── -->
|
<!-- ── Sección 3: Exámenes ── -->
|
||||||
<div class="ficha-section">
|
<div class="ficha-section">
|
||||||
<h6><i class="fas fa-vial me-1"></i>Exámenes solicitados</h6>
|
<h6><i class="fas fa-vial me-1"></i>Exámenes solicitados</h6>
|
||||||
@@ -1437,6 +1455,48 @@ function seleccionarPaciente(pac) {
|
|||||||
// Precargar en background (usuario decide si abre)
|
// Precargar en background (usuario decide si abre)
|
||||||
_historialPacienteCargado = false;
|
_historialPacienteCargado = false;
|
||||||
_historialPacienteId = pac.id;
|
_historialPacienteId = pac.id;
|
||||||
|
|
||||||
|
// Consultar exámenes recientes en RIPS (últimos 5 min)
|
||||||
|
const cedula = (pac.numero_documento || pac.documento || '').toString().trim();
|
||||||
|
if (cedula) consultarExamenesRips(cedula);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Exámenes desde RIPS ───────────────────────────────────────
|
||||||
|
let _ripsData = null;
|
||||||
|
|
||||||
|
async function consultarExamenesRips(cedula) {
|
||||||
|
_ripsData = null;
|
||||||
|
document.getElementById('banner-rips').classList.add('d-none');
|
||||||
|
if (!cedula) return;
|
||||||
|
try {
|
||||||
|
const r = await fetch(`${BASE_WA}api/lab/get_examenes_rips.php?cedula=${encodeURIComponent(cedula)}`);
|
||||||
|
const d = await r.json();
|
||||||
|
if (!d.ok || !d.encontrados?.length) return;
|
||||||
|
_ripsData = d;
|
||||||
|
const hora = d.hora ? ' (' + String(d.hora).slice(0, 5) + ')' : '';
|
||||||
|
document.getElementById('banner-rips-txt').textContent =
|
||||||
|
`${d.encontrados.length} examen(es) de RIPS${hora} — ¿Cargar?`;
|
||||||
|
const warn = document.getElementById('banner-rips-warn');
|
||||||
|
if (d.no_mapeados?.length) {
|
||||||
|
warn.textContent = `⚠ Sin mapeo: ${d.no_mapeados.join(', ')}`;
|
||||||
|
warn.classList.remove('d-none');
|
||||||
|
} else {
|
||||||
|
warn.classList.add('d-none');
|
||||||
|
}
|
||||||
|
document.getElementById('banner-rips').classList.remove('d-none');
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cargarExamenesRips() {
|
||||||
|
if (!_ripsData?.encontrados?.length || !examTS) return;
|
||||||
|
_ripsData.encontrados.forEach(e => examTS.addItem(String(e.exam_tipo_id), true));
|
||||||
|
recalcularPrecios();
|
||||||
|
descartarRips();
|
||||||
|
}
|
||||||
|
|
||||||
|
function descartarRips() {
|
||||||
|
_ripsData = null;
|
||||||
|
document.getElementById('banner-rips').classList.add('d-none');
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePagoCombinado() {
|
function togglePagoCombinado() {
|
||||||
@@ -1463,6 +1523,7 @@ function resetPagoCombinado() {
|
|||||||
function desvincularPaciente() {
|
function desvincularPaciente() {
|
||||||
pacienteActivo = null;
|
pacienteActivo = null;
|
||||||
_historialPacienteId = null;
|
_historialPacienteId = null;
|
||||||
|
descartarRips();
|
||||||
_historialPacienteCargado = false;
|
_historialPacienteCargado = false;
|
||||||
document.getElementById('bloque-pac-no-vinculado').classList.remove('d-none');
|
document.getElementById('bloque-pac-no-vinculado').classList.remove('d-none');
|
||||||
document.getElementById('bloque-pac-seleccionado').classList.add('d-none');
|
document.getElementById('bloque-pac-seleccionado').classList.add('d-none');
|
||||||
@@ -2037,6 +2098,7 @@ function resetCheckboxes() {
|
|||||||
if (examTS) { examTS.clear(); examTS.enable(); }
|
if (examTS) { examTS.clear(); examTS.enable(); }
|
||||||
document.getElementById('wrap-examenes').classList.remove('disabled');
|
document.getElementById('wrap-examenes').classList.remove('disabled');
|
||||||
document.getElementById('bloque-medico').style.display = '';
|
document.getElementById('bloque-medico').style.display = '';
|
||||||
|
descartarRips();
|
||||||
document.getElementById('sel-pago').value = '';
|
document.getElementById('sel-pago').value = '';
|
||||||
document.getElementById('inp-total').value = '';
|
document.getElementById('inp-total').value = '';
|
||||||
document.getElementById('inp-obs').value = '';
|
document.getElementById('inp-obs').value = '';
|
||||||
|
|||||||
Reference in New Issue
Block a user