\$tomasDetalle solo se inicializaba dentro del if(\$esTomaProg), así que un formulario que NO es de toma progresiva (ej. "Datos Toma de Muestras") heredaba el arreglo del formulario anterior en el mismo turno (ej. "Control de Tomas de Muestras Prolongadas") si ese sí lo era. Se reinicia en cada vuelta del loop junto con tomas_total/tomas_firmadas. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
499 lines
24 KiB
PHP
499 lines
24 KiB
PHP
<?php
|
|
/**
|
|
* GET /modules/turnero/api/get_consentimientos.php
|
|
* Retorna el estado actualizado de los consentimientos de un turno.
|
|
* Usado por lugar.php (polling cada 5 s) y recepcion.php.
|
|
* No requiere autenticación (las pantallas internas ya están protegidas).
|
|
*
|
|
* Query params:
|
|
* turno_id int requerido
|
|
* incluir_solicitud int opcional 1 → también retorna solicitud, paciente y exámenes
|
|
*/
|
|
|
|
require_once __DIR__ . '/_helpers.php';
|
|
requireMethod('GET');
|
|
|
|
$turnoId = isset($_GET['turno_id']) ? (int) $_GET['turno_id'] : 0;
|
|
$incluirSolicitud = isset($_GET['incluir_solicitud']) ? (bool)(int)$_GET['incluir_solicitud'] : false;
|
|
|
|
if ($turnoId <= 0) jsonError('turno_id inválido.');
|
|
|
|
$pdo = db();
|
|
|
|
// ── Verificar que el turno existe ─────────────────────────────
|
|
$stmt = $pdo->prepare("SELECT id, estado, lugar_destino_id, paciente_id FROM turnero_turnos WHERE id = ?");
|
|
$stmt->execute([$turnoId]);
|
|
$turno = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
if (!$turno) jsonError('Turno no encontrado.', 404);
|
|
|
|
// ── Formularios de lugar ──────────────────────────────────────────────────────
|
|
// Solo cuando el turno tiene lugar destino asignado. En recepción (NULL),
|
|
// fidsDeLugar queda vacío → todos los consentimientos reciben origen_lugar_id=null
|
|
// → pasan el filtro !c.origen_lugar_id de recepcion.php.
|
|
$lugarDestinoId = (int)($turno['lugar_destino_id'] ?? 0);
|
|
$fidsDeLugar = [];
|
|
if ($lugarDestinoId) {
|
|
$stmtL = $pdo->prepare(
|
|
"SELECT formulario_id FROM turnero_lugar_consentimientos WHERE lugar_id = ?"
|
|
);
|
|
$stmtL->execute([$lugarDestinoId]);
|
|
$fidsDeLugar = array_map('intval', $stmtL->fetchAll(PDO::FETCH_COLUMN));
|
|
}
|
|
|
|
// ── Consentimientos ───────────────────────────────────────────
|
|
$stmt = $pdo->prepare(
|
|
"SELECT tc.id,
|
|
tc.turno_id,
|
|
tc.formulario_id,
|
|
tc.token,
|
|
tc.estado,
|
|
tc.enviado_at,
|
|
tc.firmado_at,
|
|
(tc.firma_profesional_svg IS NOT NULL) AS tiene_firma_profesional,
|
|
tc.firmado_profesional_at,
|
|
tc.siguiente_toma_at,
|
|
tc.toma_inicio_at,
|
|
tc.datos_respuestas,
|
|
f.nombre AS formulario_nombre,
|
|
f.esquema AS formulario_esquema,
|
|
f.es_toma_progresiva AS formulario_es_toma_prog,
|
|
f.solo_profesional AS formulario_solo_profesional
|
|
FROM turnero_consentimientos tc
|
|
LEFT JOIN lab_formularios f ON f.id = tc.formulario_id
|
|
WHERE tc.turno_id = ?
|
|
ORDER BY tc.id ASC"
|
|
);
|
|
$stmt->execute([$turnoId]);
|
|
$consentimientos = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Compute flags desde el esquema; origen_lugar_id calculado según config actual
|
|
foreach ($consentimientos as &$c) {
|
|
$esquema = $c['formulario_esquema'] ?? null;
|
|
$campos = [];
|
|
if ($esquema) {
|
|
$decoded = json_decode($esquema, true);
|
|
$campos = is_array($decoded) ? $decoded : ($decoded['campos'] ?? []);
|
|
}
|
|
$camposFirmaPro = array_values(array_filter($campos, fn($f) => ($f['tipo'] ?? '') === 'firma_profesional'));
|
|
$tieneFirmaPro = !empty($camposFirmaPro);
|
|
$tieneFirmaPac = !empty(array_filter($campos, fn($f) => ($f['tipo'] ?? '') === 'firma'));
|
|
|
|
// Toma progresiva: override explícito en DB gana; si es NULL, usar heurística (>1 campo firma_profesional)
|
|
$overrideProg = $c['formulario_es_toma_prog'] ?? null;
|
|
$esTomaProg = $overrideProg !== null ? (bool)(int)$overrideProg : count($camposFirmaPro) > 1;
|
|
$dr = $c['datos_respuestas'] ? (json_decode($c['datos_respuestas'], true) ?? []) : [];
|
|
$tomasTotal = 0;
|
|
$tomasFirm = 0;
|
|
$tomasDetalle = []; // reiniciar cada vuelta: si no, un formulario sin tomas hereda las del anterior
|
|
if ($esTomaProg) {
|
|
|
|
// Mapear cada firma_profesional a los valores de examen de su sección condicional
|
|
// (y a su etiqueta + campo hora, para poder mostrar el detalle por toma)
|
|
$firmaCondMap = [];
|
|
$curCondCampo = null;
|
|
$curCondVals = [];
|
|
$curLabel = null;
|
|
$curHoraId = null;
|
|
foreach ($campos as $campo) {
|
|
$ft = $campo['tipo'] ?? '';
|
|
if ($ft === 'separador') {
|
|
$cond = $campo['condicion'] ?? null;
|
|
$curCondCampo = $cond ? ($cond['campo_id'] ?? null) : null;
|
|
$curCondVals = $cond
|
|
? ($cond['valores'] ?? ($cond['valor'] ? [$cond['valor']] : []))
|
|
: [];
|
|
$curLabel = $campo['label'] ?? '';
|
|
$curHoraId = null;
|
|
} elseif ($ft === 'hora') {
|
|
$curHoraId = $campo['id'] ?? null;
|
|
} elseif ($ft === 'firma_profesional' && !empty($campo['id'])) {
|
|
$firmaCondMap[$campo['id']] = [
|
|
'campo' => $curCondCampo, 'valores' => $curCondVals,
|
|
'label' => $curLabel, 'hora_id' => $curHoraId,
|
|
];
|
|
}
|
|
}
|
|
|
|
// Filtrar por exámenes seleccionados en datos_respuestas
|
|
$relevantIds = [];
|
|
foreach ($firmaCondMap as $fid => $info) {
|
|
if ($info['campo'] === null) {
|
|
$relevantIds[] = $fid; // sin condición → siempre aplica
|
|
} else {
|
|
$ctrlVal = $dr[$info['campo']] ?? null;
|
|
$ctrlArr = is_array($ctrlVal) ? $ctrlVal : ($ctrlVal !== null ? [$ctrlVal] : []);
|
|
if (!empty(array_intersect($info['valores'], $ctrlArr))) {
|
|
$relevantIds[] = $fid;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Si hay config de tomas guardada, restringir al subconjunto seleccionado
|
|
$tomasConfig = is_array($dr['_tomas_config'] ?? null) ? $dr['_tomas_config'] : null;
|
|
if ($tomasConfig !== null) {
|
|
$allowedFids = [];
|
|
foreach ($tomasConfig as $__v) {
|
|
if (!is_array($__v)) continue;
|
|
$__inner = reset($__v);
|
|
// nested: exam => { group => [fids] }; flat: group => [fids]
|
|
if (is_array($__inner)) { foreach ($__v as $__fids) foreach ($__fids as $__f) $allowedFids[] = $__f; }
|
|
else { foreach ($__v as $__f) $allowedFids[] = $__f; }
|
|
}
|
|
$allowedFids = array_unique($allowedFids);
|
|
$relevantIds = array_values(array_filter($relevantIds, fn($id) => in_array($id, $allowedFids, true)));
|
|
}
|
|
|
|
$tomasTotal = count($relevantIds);
|
|
$tomasDetalle = [];
|
|
foreach ($relevantIds as $fid) {
|
|
$firmado = (!empty($dr[$fid]) && strlen($dr[$fid]) > 10)
|
|
|| (!empty($dr[$fid.'_svg']) && strlen($dr[$fid.'_svg']) > 10);
|
|
if ($firmado) $tomasFirm++;
|
|
$info = $firmaCondMap[$fid] ?? [];
|
|
$horaId = $info['hora_id'] ?? null;
|
|
$tomasDetalle[] = [
|
|
'label' => $info['label'] ?? '',
|
|
'hora' => $horaId ? ($dr[$horaId] ?? null) : null,
|
|
'firmado' => $firmado,
|
|
'firmante' => $firmado ? ($dr[$fid . '_pro_nombre'] ?? $dr['_pro_nombre'] ?? null) : null,
|
|
];
|
|
}
|
|
}
|
|
|
|
$soloPro = !empty($c['formulario_solo_profesional']);
|
|
$c['requiere_firma_profesional'] = $tieneFirmaPro;
|
|
$c['requiere_firma_paciente'] = $soloPro ? false : !($tieneFirmaPro && !$tieneFirmaPac);
|
|
$c['origen_lugar_id'] = in_array((int)$c['formulario_id'], $fidsDeLugar, true)
|
|
? ($lugarDestinoId ?: -1) : null;
|
|
$c['es_toma_progresiva'] = $esTomaProg;
|
|
$c['tomas_total'] = $tomasTotal;
|
|
$c['tomas_firmadas'] = $tomasFirm;
|
|
$c['tomas_detalle'] = $tomasDetalle ?? [];
|
|
$c['cierre_anticipado'] = $dr['_cierre_anticipado'] ?? null;
|
|
// Exponer campos firma_profesional (ids) para el frontend
|
|
$c['campos_firma_pro'] = array_column($camposFirmaPro, 'id');
|
|
unset($c['formulario_esquema'], $c['formulario_es_toma_prog'], $c['formulario_solo_profesional'], $c['datos_respuestas']);
|
|
}
|
|
unset($c);
|
|
|
|
$respuesta = [
|
|
'turno_id' => $turnoId,
|
|
'turno_estado' => $turno['estado'],
|
|
'consentimientos' => $consentimientos,
|
|
];
|
|
|
|
// ── Datos adicionales si solicita solicitud ───────────────────
|
|
if ($incluirSolicitud) {
|
|
$stmt = $pdo->prepare(
|
|
"SELECT s.id, s.turno_id, s.paciente_id, s.lugar_id,
|
|
s.numero_orden,
|
|
s.total_cobrado, s.metodo_pago, s.observaciones, s.embarazada, s.solo_muestras, s.medico_id, s.creado_at,
|
|
l.nombre AS lugar_nombre,
|
|
CONCAT(m.nombres, ' ', m.apellidos) AS medico_nombre,
|
|
m.cod_especialidad AS medico_especialidad,
|
|
m.codigo AS medico_codigo
|
|
FROM turnero_solicitudes s
|
|
LEFT JOIN turnero_lugares l ON l.id = s.lugar_id
|
|
LEFT JOIN medicos m ON m.id = s.medico_id
|
|
WHERE s.turno_id = ?
|
|
LIMIT 1"
|
|
);
|
|
$stmt->execute([$turnoId]);
|
|
$solicitud = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
|
|
|
|
$paciente = null;
|
|
$examenes = [];
|
|
|
|
if ($solicitud) {
|
|
// Paciente
|
|
$stmt = $pdo->prepare(
|
|
"SELECT id, nombre_completo, tipo_documento, numero_documento,
|
|
fecha_nacimiento, telefono
|
|
FROM lab_pacientes
|
|
WHERE id = ?"
|
|
);
|
|
$stmt->execute([$solicitud['paciente_id']]);
|
|
$paciente = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
|
|
|
|
// Exámenes
|
|
$stmt = $pdo->prepare(
|
|
"SELECT et.id, et.codigo, et.nombre, et.categoria
|
|
FROM turnero_examen_items tei
|
|
JOIN exam_tipos et ON et.id = tei.exam_tipo_id
|
|
WHERE tei.solicitud_id = ?
|
|
ORDER BY et.categoria, et.nombre"
|
|
);
|
|
$stmt->execute([$solicitud['id']]);
|
|
$examenes = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Nota: en visitas de "solo entrega de muestras" no hay exámenes, así que la
|
|
// Fuente 1 (por examen) no aplica, pero la Fuente 2 (por lugar, ej. F-LAB-08
|
|
// Datos Toma de Muestras) sigue siendo obligatoria — se procesa más abajo igual.
|
|
|
|
// Auto-crear consentimientos requeridos desde DOS fuentes:
|
|
// 1. Por examen → exam_tipo_consentimientos
|
|
// 2. Por lugar → turnero_lugar_consentimientos
|
|
$requeridos = [];
|
|
$vistosFIds = [];
|
|
|
|
// Fuente 1: examen
|
|
if (!empty($examenes)) {
|
|
$examIds = array_column($examenes, 'id');
|
|
$ph = implode(',', array_fill(0, count($examIds), '?'));
|
|
$stmtE = $pdo->prepare(
|
|
"SELECT DISTINCT etc.formulario_id, f.nombre AS formulario_nombre
|
|
FROM exam_tipo_consentimientos etc
|
|
JOIN lab_formularios f ON f.id = etc.formulario_id
|
|
WHERE etc.exam_tipo_id IN ($ph) AND f.is_active = 1"
|
|
);
|
|
$stmtE->execute($examIds);
|
|
foreach ($stmtE->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
|
$fid = (int)$r['formulario_id'];
|
|
if (!in_array($fid, $vistosFIds, true)) {
|
|
$requeridos[] = $r;
|
|
$vistosFIds[] = $fid;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fuente 2: lugar destino ($lugarDestinoId y $fidsDeLugar ya calculados arriba)
|
|
if ($lugarDestinoId) {
|
|
$stmtL = $pdo->prepare(
|
|
"SELECT DISTINCT tlc.formulario_id, f.nombre AS formulario_nombre
|
|
FROM turnero_lugar_consentimientos tlc
|
|
JOIN lab_formularios f ON f.id = tlc.formulario_id
|
|
WHERE tlc.lugar_id = ? AND f.is_active = 1"
|
|
);
|
|
$stmtL->execute([$lugarDestinoId]);
|
|
foreach ($stmtL->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
|
$fid = (int)$r['formulario_id'];
|
|
if (!in_array($fid, $vistosFIds, true)) {
|
|
$requeridos[] = $r;
|
|
$vistosFIds[] = $fid;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Auto-crear los registros que falten
|
|
if (!empty($requeridos)) {
|
|
$existFormIds = array_map('intval', array_column($consentimientos, 'formulario_id'));
|
|
$stmtIns = $pdo->prepare(
|
|
"INSERT IGNORE INTO turnero_consentimientos
|
|
(turno_id, formulario_id, token, estado, creado_por)
|
|
VALUES (?, ?, ?, 'pendiente', ?)"
|
|
);
|
|
$creados = 0;
|
|
foreach ($requeridos as $r) {
|
|
if (!in_array((int)$r['formulario_id'], $existFormIds, true)) {
|
|
$token = sprintf(
|
|
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
|
mt_rand(0,0xffff), mt_rand(0,0xffff),
|
|
mt_rand(0,0xffff),
|
|
mt_rand(0,0x0fff)|0x4000,
|
|
mt_rand(0,0x3fff)|0x8000,
|
|
mt_rand(0,0xffff), mt_rand(0,0xffff), mt_rand(0,0xffff)
|
|
);
|
|
$stmtIns->execute([$turnoId, (int)$r['formulario_id'], $token, adminId()]);
|
|
$creados++;
|
|
}
|
|
}
|
|
|
|
// Re-leer si se crearon nuevos registros y recalcular flags
|
|
if ($creados > 0) {
|
|
$stmtC = $pdo->prepare(
|
|
"SELECT tc.id, tc.turno_id, tc.formulario_id, tc.token,
|
|
tc.estado, tc.enviado_at, tc.firmado_at,
|
|
(tc.firma_profesional_svg IS NOT NULL) AS tiene_firma_profesional,
|
|
tc.firmado_profesional_at,
|
|
tc.siguiente_toma_at,
|
|
tc.toma_inicio_at,
|
|
tc.datos_respuestas,
|
|
f.nombre AS formulario_nombre,
|
|
f.esquema AS formulario_esquema,
|
|
f.es_toma_progresiva AS formulario_es_toma_prog,
|
|
f.solo_profesional AS formulario_solo_profesional
|
|
FROM turnero_consentimientos tc
|
|
LEFT JOIN lab_formularios f ON f.id = tc.formulario_id
|
|
WHERE tc.turno_id = ?
|
|
ORDER BY tc.id ASC"
|
|
);
|
|
$stmtC->execute([$turnoId]);
|
|
$consentimientos = $stmtC->fetchAll(PDO::FETCH_ASSOC);
|
|
foreach ($consentimientos as &$c) {
|
|
$esquema = $c['formulario_esquema'] ?? null;
|
|
$campos = [];
|
|
if ($esquema) {
|
|
$decoded = json_decode($esquema, true);
|
|
$campos = is_array($decoded) ? $decoded : ($decoded['campos'] ?? []);
|
|
}
|
|
$camposFirmaPro = array_values(array_filter($campos, fn($f) => ($f['tipo'] ?? '') === 'firma_profesional'));
|
|
$tieneFirmaPro = !empty($camposFirmaPro);
|
|
$tieneFirmaPac = !empty(array_filter($campos, fn($f) => ($f['tipo'] ?? '') === 'firma'));
|
|
$overrideProg = $c['formulario_es_toma_prog'] ?? null;
|
|
$esTomaProg = $overrideProg !== null ? (bool)(int)$overrideProg : count($camposFirmaPro) > 1;
|
|
$dr2 = $c['datos_respuestas'] ? (json_decode($c['datos_respuestas'], true) ?? []) : [];
|
|
$tomasTotal2 = 0;
|
|
$tomasFirm = 0;
|
|
if ($esTomaProg) {
|
|
$firmaCondMap2 = [];
|
|
$curC2 = null; $curV2 = [];
|
|
foreach ($campos as $campo2) {
|
|
$ft2 = $campo2['tipo'] ?? '';
|
|
if ($ft2 === 'separador') {
|
|
$cond2 = $campo2['condicion'] ?? null;
|
|
$curC2 = $cond2 ? ($cond2['campo_id'] ?? null) : null;
|
|
$curV2 = $cond2 ? ($cond2['valores'] ?? ($cond2['valor'] ? [$cond2['valor']] : [])) : [];
|
|
} elseif ($ft2 === 'firma_profesional' && !empty($campo2['id'])) {
|
|
$firmaCondMap2[$campo2['id']] = ['campo' => $curC2, 'valores' => $curV2];
|
|
}
|
|
}
|
|
$relevantIds2 = [];
|
|
foreach ($firmaCondMap2 as $fid2 => $info2) {
|
|
if ($info2['campo'] === null) {
|
|
$relevantIds2[] = $fid2;
|
|
} else {
|
|
$cv2 = $dr2[$info2['campo']] ?? null;
|
|
$ca2 = is_array($cv2) ? $cv2 : ($cv2 !== null ? [$cv2] : []);
|
|
if (!empty(array_intersect($info2['valores'], $ca2))) $relevantIds2[] = $fid2;
|
|
}
|
|
}
|
|
$cfg2 = is_array($dr2['_tomas_config'] ?? null) ? $dr2['_tomas_config'] : null;
|
|
if ($cfg2 !== null) {
|
|
$allowed2 = [];
|
|
foreach ($cfg2 as $__v2) {
|
|
if (!is_array($__v2)) continue;
|
|
$__i2 = reset($__v2);
|
|
if (is_array($__i2)) { foreach ($__v2 as $__fids2) foreach ($__fids2 as $__f2) $allowed2[] = $__f2; }
|
|
else { foreach ($__v2 as $__f2) $allowed2[] = $__f2; }
|
|
}
|
|
$allowed2 = array_unique($allowed2);
|
|
$relevantIds2 = array_values(array_filter($relevantIds2, fn($id) => in_array($id, $allowed2, true)));
|
|
}
|
|
$tomasTotal2 = count($relevantIds2);
|
|
foreach ($relevantIds2 as $fid2) {
|
|
if (!empty($dr2[$fid2]) && strlen($dr2[$fid2]) > 10) $tomasFirm++;
|
|
}
|
|
}
|
|
$soloPro2 = !empty($c['formulario_solo_profesional']);
|
|
$c['requiere_firma_profesional'] = $tieneFirmaPro;
|
|
$c['requiere_firma_paciente'] = $soloPro2 ? false : !($tieneFirmaPro && !$tieneFirmaPac);
|
|
$c['origen_lugar_id'] = in_array((int)$c['formulario_id'], $fidsDeLugar, true)
|
|
? $lugarDestinoId : null;
|
|
$c['es_toma_progresiva'] = $esTomaProg;
|
|
$c['tomas_total'] = $tomasTotal2;
|
|
$c['tomas_firmadas'] = $tomasFirm;
|
|
$c['cierre_anticipado'] = $dr2['_cierre_anticipado'] ?? null;
|
|
$c['campos_firma_pro'] = array_column($camposFirmaPro, 'id');
|
|
unset($c['formulario_esquema'], $c['formulario_es_toma_prog'], $c['formulario_solo_profesional'], $c['datos_respuestas']);
|
|
}
|
|
unset($c);
|
|
$respuesta['consentimientos'] = $consentimientos;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Muestras pendientes / recibidas del turno
|
|
$muestras = [];
|
|
if ($solicitud) {
|
|
try {
|
|
$stmtM = $pdo->prepare(
|
|
"SELECT tm.id, tm.tipo_muestra, tm.estado, tm.motivo_rechazo, tm.recibida_at,
|
|
COALESCE(lt.nombre, tm.tipo_muestra) AS label,
|
|
0 AS es_pendiente_anterior, NULL AS solicitud_orden_anterior
|
|
FROM turnero_muestras tm
|
|
LEFT JOIN lab_tipos_muestra lt ON lt.codigo = tm.tipo_muestra
|
|
WHERE tm.solicitud_id = ?
|
|
ORDER BY tm.id ASC"
|
|
);
|
|
$stmtM->execute([$solicitud['id']]);
|
|
$muestras = $stmtM->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// Si es visita de entrega de muestras, agregar pendientes de solicitudes anteriores
|
|
if (!empty($solicitud['solo_muestras']) && !empty($solicitud['paciente_id'])) {
|
|
$stmtPrev = $pdo->prepare(
|
|
"SELECT tm.id, tm.tipo_muestra, tm.estado, tm.motivo_rechazo, tm.recibida_at,
|
|
COALESCE(lt.nombre, tm.tipo_muestra) AS label,
|
|
1 AS es_pendiente_anterior,
|
|
ts.id AS solicitud_id_anterior,
|
|
ts.numero_orden AS solicitud_orden_anterior
|
|
FROM turnero_muestras tm
|
|
JOIN turnero_solicitudes ts ON ts.id = tm.solicitud_id
|
|
LEFT JOIN lab_tipos_muestra lt ON lt.codigo = tm.tipo_muestra
|
|
WHERE ts.paciente_id = ?
|
|
AND tm.solicitud_id != ?
|
|
AND tm.estado IN ('pendiente','rechazada')
|
|
ORDER BY tm.id ASC"
|
|
);
|
|
$stmtPrev->execute([$solicitud['paciente_id'], $solicitud['id']]);
|
|
$previas = $stmtPrev->fetchAll(PDO::FETCH_ASSOC);
|
|
$muestras = array_merge($previas, $muestras);
|
|
}
|
|
} catch (\Throwable $_) {
|
|
// Tabla aún no existe
|
|
}
|
|
}
|
|
|
|
// Fallback: sin solicitud pero el turno tiene paciente_id → buscar muestras pendientes igualmente
|
|
if (!$solicitud && !empty($turno['paciente_id'])) {
|
|
$pacId = (int)$turno['paciente_id'];
|
|
$stmt = $pdo->prepare(
|
|
"SELECT id, nombre_completo, tipo_documento, numero_documento,
|
|
fecha_nacimiento, telefono
|
|
FROM lab_pacientes WHERE id = ?"
|
|
);
|
|
$stmt->execute([$pacId]);
|
|
$paciente = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
|
|
try {
|
|
$stmtPrev = $pdo->prepare(
|
|
"SELECT tm.id, tm.tipo_muestra, tm.estado, tm.motivo_rechazo, tm.recibida_at,
|
|
COALESCE(lt.nombre, tm.tipo_muestra) AS label,
|
|
1 AS es_pendiente_anterior,
|
|
ts.id AS solicitud_id_anterior,
|
|
ts.numero_orden AS solicitud_orden_anterior
|
|
FROM turnero_muestras tm
|
|
JOIN turnero_solicitudes ts ON ts.id = tm.solicitud_id
|
|
LEFT JOIN lab_tipos_muestra lt ON lt.codigo = tm.tipo_muestra
|
|
WHERE ts.paciente_id = ?
|
|
AND tm.estado IN ('pendiente','rechazada')
|
|
ORDER BY tm.id ASC"
|
|
);
|
|
$stmtPrev->execute([$pacId]);
|
|
$muestras = $stmtPrev->fetchAll(PDO::FETCH_ASSOC);
|
|
} catch (\Throwable $_) {}
|
|
}
|
|
|
|
// Cargar exámenes de la solicitud original para cada muestra pendiente de visita anterior,
|
|
// para que al recibirla se vea el contexto completo del servicio (no solo el tipo de muestra)
|
|
$solIdsAnteriores = array_values(array_unique(array_filter(
|
|
array_column($muestras, 'solicitud_id_anterior')
|
|
)));
|
|
if ($solIdsAnteriores) {
|
|
$ph = implode(',', array_fill(0, count($solIdsAnteriores), '?'));
|
|
$stmtExAnt = $pdo->prepare(
|
|
"SELECT tei.solicitud_id, et.nombre, et.codigo
|
|
FROM turnero_examen_items tei
|
|
JOIN exam_tipos et ON et.id = tei.exam_tipo_id
|
|
WHERE tei.solicitud_id IN ($ph)
|
|
ORDER BY et.nombre ASC"
|
|
);
|
|
$stmtExAnt->execute($solIdsAnteriores);
|
|
$examPorSolicitud = [];
|
|
foreach ($stmtExAnt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|
$examPorSolicitud[(int)$row['solicitud_id']][] = ['nombre' => $row['nombre'], 'codigo' => $row['codigo']];
|
|
}
|
|
foreach ($muestras as &$m) {
|
|
if (!empty($m['solicitud_id_anterior'])) {
|
|
$m['examenes_anteriores'] = $examPorSolicitud[(int)$m['solicitud_id_anterior']] ?? [];
|
|
}
|
|
}
|
|
unset($m);
|
|
}
|
|
|
|
$respuesta['solicitud'] = $solicitud;
|
|
$respuesta['paciente'] = $paciente;
|
|
$respuesta['examenes'] = $examenes;
|
|
$respuesta['muestras'] = $muestras;
|
|
}
|
|
|
|
jsonOk($respuesta);
|