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>
266 lines
10 KiB
PHP
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,
|
|
]);
|