Files
whatsapp/modules/turnero/api/get_display_global.php
Lizandro GuarnizoandClaude Opus 5 2a87f66bb7 Pantalla TV: dejar de llamar a pacientes que ya se fueron
Reportan que a veces llama a otro paciente. Sí ocurre, y hay 26 turnos sin
cerrar en estado "en servicio" desde el 28 de julio, varios de ellos dos y
tres en el mismo puesto a la vez.

La pantalla pregunta por cada puesto cuál turno está en servicio y se queda
con el más reciente. Mientras hay uno real todo va bien, pero al cerrarse
ese, el puesto queda con el viejo como único en servicio y sube al panel como
si lo estuvieran llamando. Peor: al cargar, la pantalla anunciaba POR VOZ el
turno que encontrara arriba, así que cada recarga —y se recarga sola— podía
gritar el nombre de alguien que se fue hace días.

- Al abrir ya no se anuncia nada: los turnos presentes se dan por vistos y
  solo se anuncia lo que pase de ahí en adelante. Mostrar el estado actual es
  correcto; declamarlo como si fuera un llamado nuevo, no.
- Un turno vale hasta la medianoche de su día, tanto en recepción como en los
  puestos. De los 26 atascados, 25 dejan de aparecer; queda el único de hoy.

El filtro es una red, no la solución: esos 26 turnos hay que cerrarlos, y
mientras existan siguen inflando el "en proceso" del dashboard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 16:19:47 -05:00

198 lines
7.3 KiB
PHP

<?php
/**
* GET /modules/turnero/api/get_display_global.php
* Devuelve el estado GLOBAL del sistema para la pantalla TV unificada.
* Retorna: turno activo en Recepción + turno activo en cada Lugar + cola.
* No requiere autenticación (acceso público).
*/
require_once __DIR__ . '/_helpers.php';
requireMethod('GET');
$pdo = db();
// Si no hay sesión hoy o está cerrada → $sesionId = 0 (queries devuelven vacío).
$_ses = sesionHoy();
$sesionCerrada = (!$_ses || $_ses['fin_at'] !== null);
$sesionId = ($sesionCerrada) ? 0 : (int)$_ses['id'];
// ── 1. Escritorios de Recepción activos + su turno en_recepcion ─
// Cada escritorio puede tener un turno distinto (varios recepcionistas).
$stmtDesks = $pdo->query(
"SELECT id, nombre, sort_order
FROM turnero_lugares
WHERE activo = 1 AND tipo = 'recepcion'
ORDER BY sort_order ASC"
);
$escritoriosRec = $stmtDesks->fetchAll(PDO::FETCH_ASSOC);
$activosRecepcion = [];
foreach ($escritoriosRec as $desk) {
// El turno activo de este escritorio: el más reciente asociado a él
$stmtT = $pdo->prepare(
"SELECT t.id,
t.codigo,
t.numero,
t.estado,
t.paciente_nombre,
t.paciente_id,
t.llamado_recepcion_at AS llamado_at,
p.codigo AS prioridad_codigo,
p.nombre AS prioridad_nombre,
p.color AS prioridad_color
FROM turnero_turnos t
JOIN turnero_prioridades p ON p.id = t.prioridad_id
WHERE t.sesion_id = ?
AND t.estado = 'en_recepcion'
AND t.recepcion_desk_id = ?
ORDER BY t.llamado_recepcion_at DESC
LIMIT 1"
);
$stmtT->execute([$sesionId, (int)$desk['id']]);
$turno = $stmtT->fetch(PDO::FETCH_ASSOC) ?: null;
$activosRecepcion[] = [
'desk_id' => (int)$desk['id'],
'desk_nombre'=> $desk['nombre'],
'sort_order' => (int)$desk['sort_order'],
'turno' => $turno,
];
}
// Si no hay escritorios configurados, mostrar todos en_recepcion sin desk
if (empty($escritoriosRec)) {
$stmtRec = $pdo->prepare(
"SELECT t.id,
t.codigo,
t.numero,
t.estado,
t.paciente_nombre,
t.paciente_id,
t.llamado_recepcion_at AS llamado_at,
p.codigo AS prioridad_codigo,
p.nombre AS prioridad_nombre,
p.color AS prioridad_color
FROM turnero_turnos t
JOIN turnero_prioridades p ON p.id = t.prioridad_id
WHERE t.sesion_id = ?
AND t.estado = 'en_recepcion'
-- Misma regla que en los puestos: el turno vale hasta medianoche
AND DATE(t.llamado_recepcion_at) = CURDATE()
ORDER BY t.llamado_recepcion_at DESC"
);
$stmtRec->execute([$sesionId]);
foreach ($stmtRec->fetchAll(PDO::FETCH_ASSOC) as $i => $t) {
$activosRecepcion[] = [
'desk_id' => 0,
'desk_nombre'=> 'Recepción ' . ($i + 1),
'sort_order' => $i,
'turno' => $t,
];
}
// Si no hay ninguno activo, mostrar un panel vacío
if (empty($activosRecepcion)) {
$activosRecepcion[] = ['desk_id' => 0, 'desk_nombre' => 'Recepción', 'sort_order' => 0, 'turno' => null];
}
}
// ── 2. Estaciones de Muestras activas + su turno ─────────────
$stmtLugares = $pdo->query(
"SELECT id, nombre, sort_order
FROM turnero_lugares
WHERE activo = 1 AND tipo = 'muestras'
ORDER BY sort_order ASC"
);
$lugares = $stmtLugares->fetchAll(PDO::FETCH_ASSOC);
$lugaresActivos = [];
foreach ($lugares as $lugar) {
$stmtActivo = $pdo->prepare(
"SELECT t.id,
t.codigo,
t.numero,
t.estado,
t.paciente_nombre,
t.paciente_id,
t.llamado_lugar_at AS llamado_at,
p.codigo AS prioridad_codigo,
p.nombre AS prioridad_nombre,
p.color AS prioridad_color
FROM turnero_turnos t
JOIN turnero_prioridades p ON p.id = t.prioridad_id
WHERE t.sesion_id = ?
AND t.estado = 'en_servicio'
AND t.lugar_destino_id = ?
-- Un turno vale hasta la medianoche de su día. Quedan bastantes sin
-- cerrar, y sin este filtro el puesto mostraba como \"llamado ahora\"
-- a un paciente de días atrás en cuanto se cerraba el turno real.
AND DATE(t.llamado_lugar_at) = CURDATE()
ORDER BY t.llamado_lugar_at DESC
LIMIT 1"
);
$stmtActivo->execute([$sesionId, (int)$lugar['id']]);
$turnoActivo = $stmtActivo->fetch(PDO::FETCH_ASSOC) ?: null;
$lugaresActivos[] = [
'lugar_id' => (int)$lugar['id'],
'lugar_nombre'=> $lugar['nombre'],
'sort_order' => (int)$lugar['sort_order'],
'turno' => $turnoActivo,
];
}
// ── 3. Cola en espera (pendientes por llamar) ─────────────────
$stmtCola = $pdo->prepare(
"SELECT t.id,
t.codigo,
t.numero,
t.estado,
t.paciente_nombre,
t.creado_at,
p.codigo AS prioridad_codigo,
p.nombre AS prioridad_nombre,
p.color AS prioridad_color,
p.orden_peso
FROM turnero_turnos t
JOIN turnero_prioridades p ON p.id = t.prioridad_id
WHERE t.sesion_id = ?
AND t.estado = 'espera'
ORDER BY p.orden_peso ASC, t.creado_at ASC"
);
$stmtCola->execute([$sesionId]);
$cola = $stmtCola->fetchAll(PDO::FETCH_ASSOC);
// ── 4. Estadísticas ───────────────────────────────────────────
$stmtStats = $pdo->prepare(
"SELECT
COUNT(*) AS total,
SUM(estado = 'espera') AS en_espera,
SUM(estado = 'en_recepcion') AS en_recepcion,
SUM(estado = 'en_espera_lugar') AS en_espera_lugar,
SUM(estado = 'en_servicio') AS en_servicio,
SUM(estado IN ('en_recepcion','en_espera_lugar',
'en_servicio')) AS en_atencion,
SUM(estado = 'finalizado') AS finalizados,
SUM(estado IN ('ausente','cancelado')) AS no_atendidos,
SEC_TO_TIME(
AVG(
CASE WHEN fin_lugar_at IS NOT NULL AND creado_at IS NOT NULL
THEN TIMESTAMPDIFF(SECOND, creado_at, fin_lugar_at)
ELSE NULL END
)
) AS tiempo_promedio_atencion
FROM turnero_turnos
WHERE sesion_id = ?"
);
$stmtStats->execute([$sesionId]);
$stats = $stmtStats->fetch(PDO::FETCH_ASSOC);
jsonOk([
'sesion_id' => $sesionId,
'sesion_cerrada' => $sesionCerrada,
'activos_recepcion'=> $activosRecepcion, // array de desks con su turno
'lugares' => $lugaresActivos, // array de estaciones de muestras
'cola' => $cola,
'stats' => $stats,
'timestamp' => date('c'),
]);