Files
whatsapp/modules/turnero/api/get_historial.php
T
Lizandro GuarnizoandClaude Sonnet 4.6 0b8418ec76 Historial turnero: desglose de tomas de muestra y modal para documentos firmados
Muestra el detalle de cada toma (tipo, quien la recibió, tiempo) cuando un
turno tuvo varias, para explicar tiempos de servicio prolongados. Los
documentos firmados ahora se abren en un modal en vez de nueva pestaña,
preservando los filtros de búsqueda activos.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-03 11:44:31 -05:00

266 lines
10 KiB
PHP

<?php
/**
* modules/turnero/api/get_historial.php
* GET Historial paginado de turnos con filtros multi-día.
*
* Parámetros (todos opcionales):
* fecha_desde YYYY-MM-DD (default: hace 7 días)
* fecha_hasta YYYY-MM-DD (default: hoy)
* estado string ("finalizado","ausente","cancelado","en_servicio",... o vacío = todos)
* lugar_id int (0 = todos)
* prioridad_id int (0 = todos)
* q string búsqueda libre por nombre/código
* page int (default 1)
* per_page int (default 50, max 200)
*/
require_once __DIR__ . '/_helpers.php';
requireTurnero();
requireMethod('GET');
$pdo = db();
// ── Parámetros ────────────────────────────────────────────────
$hoy = date('Y-m-d');
$fechaDesde = trim($_GET['fecha_desde'] ?? '');
$fechaHasta = trim($_GET['fecha_hasta'] ?? '');
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $fechaDesde)) $fechaDesde = $hoy;
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $fechaHasta)) $fechaHasta = $hoy;
if ($fechaDesde > $fechaHasta) $fechaDesde = $fechaHasta;
$estadoFiltro = trim($_GET['estado'] ?? '');
$lugarId = (int)($_GET['lugar_id'] ?? 0);
$prioridadId = (int)($_GET['prioridad_id'] ?? 0);
$pacienteId = (int)($_GET['paciente_id'] ?? 0);
$q = trim($_GET['q'] ?? '');
$perPage = min(200, max(10, (int)($_GET['per_page'] ?? 50)));
$page = max(1, (int)($_GET['page'] ?? 1));
$offset = ($page - 1) * $perPage;
// ── Base WHERE ───────────────────────────────────────────────
// Unimos turnos con sesiones para filtrar por fecha de sesión
$where = ['DATE(s.fecha) BETWEEN ? AND ?'];
$binds = [$fechaDesde, $fechaHasta];
$estadosValidos = ['espera','en_recepcion','en_espera_lugar','en_servicio','finalizado','ausente','cancelado'];
if ($estadoFiltro && in_array($estadoFiltro, $estadosValidos, true)) {
$where[] = 't.estado = ?';
$binds[] = $estadoFiltro;
}
if ($lugarId > 0) {
$where[] = 't.lugar_destino_id = ?';
$binds[] = $lugarId;
}
if ($prioridadId > 0) {
$where[] = 't.prioridad_id = ?';
$binds[] = $prioridadId;
}
if ($pacienteId > 0) {
$where[] = 'ts2.paciente_id = ?';
$binds[] = $pacienteId;
}
if ($q !== '') {
$where[] = '(t.codigo LIKE ? OR t.paciente_nombre LIKE ? OR s2.nombre_completo LIKE ?)';
$like = '%' . $q . '%';
$binds[] = $like;
$binds[] = $like;
$binds[] = $like;
}
$whereStr = 'WHERE ' . implode(' AND ', $where);
// ── Conteo total ──────────────────────────────────────────────
$sqlCount = "
SELECT COUNT(*) AS total
FROM turnero_turnos t
JOIN turnero_sesiones s ON s.id = t.sesion_id
LEFT JOIN turnero_solicitudes ts2 ON ts2.turno_id = t.id
LEFT JOIN lab_pacientes s2 ON s2.id = ts2.paciente_id
$whereStr";
$stmtCount = $pdo->prepare($sqlCount);
$stmtCount->execute($binds);
$totalRows = (int)$stmtCount->fetchColumn();
$totalPages = max(1, (int)ceil($totalRows / $perPage));
if ($page > $totalPages) $page = $totalPages;
$offset = ($page - 1) * $perPage;
// ── Lista paginada ────────────────────────────────────────────
$sqlTurnos = "
SELECT
t.id,
t.codigo,
t.numero,
t.estado,
t.paciente_nombre,
t.paciente_cel,
t.creado_at,
t.inicio_recepcion_at,
t.fin_recepcion_at,
t.inicio_lugar_at,
t.fin_lugar_at,
s.fecha AS fecha_sesion,
p.codigo AS prioridad_codigo,
p.nombre AS prioridad_nombre,
p.color AS prioridad_color,
l.nombre AS lugar_nombre,
s2.nombre_completo AS paciente_bd,
s2.tipo_documento AS paciente_tipo_doc,
s2.numero_documento AS paciente_doc,
rd.nombre AS recepcion_desk_nombre,
ur.full_name AS atendido_recepcion_nombre,
ul.full_name AS atendido_lugar_nombre,
ts2.observaciones AS notas,
ROUND(TIMESTAMPDIFF(SECOND, t.creado_at,
COALESCE(t.inicio_recepcion_at, t.fin_recepcion_at)) / 60.0, 1)
AS espera_min,
ROUND(TIMESTAMPDIFF(SECOND, t.inicio_lugar_at,
COALESCE(t.fin_lugar_at, NOW())) / 60.0, 1)
AS servicio_min,
ROUND(TIMESTAMPDIFF(SECOND, t.creado_at, t.fin_lugar_at) / 60.0, 1)
AS total_min
FROM turnero_turnos t
JOIN turnero_sesiones s ON s.id = t.sesion_id
JOIN turnero_prioridades p ON p.id = t.prioridad_id
LEFT JOIN turnero_lugares l ON l.id = t.lugar_destino_id
LEFT JOIN turnero_lugares rd ON rd.id = t.recepcion_desk_id
LEFT JOIN admin_users ur ON ur.id = t.atendido_recepcion_por
LEFT JOIN admin_users ul ON ul.id = t.atendido_lugar_por
LEFT JOIN turnero_solicitudes ts2 ON ts2.turno_id = t.id
LEFT JOIN lab_pacientes s2 ON s2.id = ts2.paciente_id
$whereStr
ORDER BY t.creado_at DESC
LIMIT ? OFFSET ?";
$bindsPage = array_merge($binds, [$perPage, $offset]);
$stmtTurnos = $pdo->prepare($sqlTurnos);
$stmtTurnos->execute($bindsPage);
$turnos = $stmtTurnos->fetchAll(PDO::FETCH_ASSOC);
// ── Enriquecer con exámenes, consentimientos, comentarios y tomas (batch) ─
$turnoIds = array_column($turnos, 'id');
$examenes = [];
$consentimientos = [];
$comentarios = [];
$tomas = [];
if ($turnoIds) {
$ph = implode(',', array_fill(0, count($turnoIds), '?'));
// Exámenes — batch
$stmtExam = $pdo->prepare(
"SELECT ts.turno_id, et.nombre, et.codigo
FROM turnero_solicitudes ts
JOIN turnero_examen_items tei ON tei.solicitud_id = ts.id
JOIN exam_tipos et ON et.id = tei.exam_tipo_id
WHERE ts.turno_id IN ($ph)
ORDER BY et.nombre ASC"
);
$stmtExam->execute($turnoIds);
foreach ($stmtExam->fetchAll(PDO::FETCH_ASSOC) as $row) {
$examenes[(int)$row['turno_id']][] = ['nombre' => $row['nombre'], 'codigo' => $row['codigo']];
}
// Consentimientos — batch
$stmtCons = $pdo->prepare(
"SELECT tc.turno_id, tc.id, f.nombre, tc.estado, tc.firmado_at
FROM turnero_consentimientos tc
LEFT JOIN lab_formularios f ON f.id = tc.formulario_id
WHERE tc.turno_id IN ($ph)
ORDER BY tc.estado DESC, tc.firmado_at DESC"
);
$stmtCons->execute($turnoIds);
foreach ($stmtCons->fetchAll(PDO::FETCH_ASSOC) as $row) {
$tid = (int)$row['turno_id'];
unset($row['turno_id']);
$consentimientos[$tid][] = $row;
}
// Comentarios — batch (últimos 3 por turno via subquery)
$stmtCom = $pdo->prepare(
"SELECT tc.turno_id, tc.usuario_nombre, tc.comentario, tc.tipo, tc.creado_at
FROM turnero_comentarios tc
WHERE tc.turno_id IN ($ph)
ORDER BY tc.turno_id, tc.creado_at DESC"
);
$stmtCom->execute($turnoIds);
foreach ($stmtCom->fetchAll(PDO::FETCH_ASSOC) as $row) {
$tid = (int)$row['turno_id'];
if (!isset($comentarios[$tid])) $comentarios[$tid] = [];
if (count($comentarios[$tid]) < 3) {
unset($row['turno_id']);
$comentarios[$tid][] = $row;
}
}
// Tomas de muestra — batch (una solicitud por turno, varias tomas por solicitud)
$stmtTom = $pdo->prepare(
"SELECT ts.turno_id, tm.tipo_muestra, tm.estado, tm.creado_at, tm.recibida_at,
ar.full_name AS recibida_por_nombre
FROM turnero_muestras tm
JOIN turnero_solicitudes ts ON ts.id = tm.solicitud_id
LEFT JOIN admin_users ar ON ar.id = tm.recibida_por
WHERE ts.turno_id IN ($ph)
ORDER BY tm.creado_at ASC"
);
$stmtTom->execute($turnoIds);
foreach ($stmtTom->fetchAll(PDO::FETCH_ASSOC) as $row) {
$tid = (int)$row['turno_id'];
unset($row['turno_id']);
$row['tiempo_min'] = ($row['creado_at'] && $row['recibida_at'])
? round((strtotime($row['recibida_at']) - strtotime($row['creado_at'])) / 60, 1)
: null;
$tomas[$tid][] = $row;
}
}
foreach ($turnos as &$turno) {
$tid = (int)$turno['id'];
$turno['examenes'] = $examenes[$tid] ?? [];
$turno['consentimientos'] = $consentimientos[$tid] ?? [];
$turno['comentarios'] = $comentarios[$tid] ?? [];
$turno['tomas'] = $tomas[$tid] ?? [];
}
// ── Resumen por estado (del rango filtrado) ───────────────────
$sqlRes = "
SELECT t.estado, COUNT(*) AS cnt
FROM turnero_turnos t
JOIN turnero_sesiones s ON s.id = t.sesion_id
LEFT JOIN turnero_solicitudes ts2 ON ts2.turno_id = t.id
LEFT JOIN lab_pacientes s2 ON s2.id = ts2.paciente_id
$whereStr
GROUP BY t.estado";
$stmtRes = $pdo->prepare($sqlRes);
$stmtRes->execute($binds);
$resumenEstados = [];
foreach ($stmtRes->fetchAll(PDO::FETCH_ASSOC) as $r) {
$resumenEstados[$r['estado']] = (int)$r['cnt'];
}
// ── Catálogos para los selectores (si pide con ?meta=1) ──────
$meta = [];
if (!empty($_GET['meta'])) {
$meta['lugares'] = $pdo->query(
"SELECT id, nombre FROM turnero_lugares WHERE activo=1 ORDER BY sort_order ASC, nombre ASC"
)->fetchAll(PDO::FETCH_ASSOC);
$meta['prioridades'] = $pdo->query(
"SELECT id, nombre, codigo, color FROM turnero_prioridades ORDER BY orden_peso ASC"
)->fetchAll(PDO::FETCH_ASSOC);
}
jsonOk([
'fecha_desde' => $fechaDesde,
'fecha_hasta' => $fechaHasta,
'total' => $totalRows,
'page' => $page,
'per_page' => $perPage,
'total_pages' => $totalPages,
'resumen_estados'=> $resumenEstados,
'turnos' => $turnos,
'meta' => $meta,
]);