siguienteTurnoEnCola ahora acepta sesion_id opcional. llamar_turno.php pasa la sesión de hoy para que nunca agarre turnos viejos de días anteriores que quedaron en estado 'espera'. Sin el filtro, el primer llamado del día podía agarrar un turno de ayer, la ficha abría en recepción pero la pantalla TV (get_display_recepcion.php) filtra por sesion_id=hoy y no lo encontraba → el número no salía en display. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
149 lines
5.8 KiB
PHP
149 lines
5.8 KiB
PHP
<?php
|
|
/**
|
|
* POST /modules/turnero/api/llamar_turno.php
|
|
* Llama el siguiente turno de una cola con el motor de prioridades.
|
|
* Requiere login + módulo turnero.
|
|
*
|
|
* Body JSON:
|
|
* area string requerido "recepcion" | "lugar"
|
|
* lugar_id int requerido si area = "lugar"
|
|
*/
|
|
|
|
require_once __DIR__ . '/_helpers.php';
|
|
requireMethod('POST');
|
|
requireTurnero();
|
|
|
|
$datos = inputJson();
|
|
$area = $datos['area'] ?? '';
|
|
$lugarId = isset($datos['lugar_id']) ? (int) $datos['lugar_id'] : null;
|
|
$deskId = isset($datos['desk_id']) ? (int) $datos['desk_id'] : null;
|
|
$turnoId = isset($datos['turno_id']) ? (int) $datos['turno_id'] : null; // llamar uno específico
|
|
|
|
// ── Validación ────────────────────────────────────────────────
|
|
if (!in_array($area, ['recepcion', 'lugar'], true)) {
|
|
jsonError('area debe ser "recepcion" o "lugar".');
|
|
}
|
|
if ($area === 'lugar' && !$lugarId) {
|
|
jsonError('lugar_id es obligatorio cuando area = "lugar".');
|
|
}
|
|
|
|
$pdo = db();
|
|
// Obtener sesión del día ANTES de abrir la transacción para que un posible
|
|
// INSERT de sesión nueva no quede dentro del rollback.
|
|
$sesionHoy = obtenerOCrearSesionHoy();
|
|
$pdo->beginTransaction();
|
|
|
|
try {
|
|
if ($area === 'recepcion') {
|
|
// Cola recepción: solo turnos de la sesión activa de hoy
|
|
$turno = siguienteTurnoEnCola('espera', null, null, $sesionHoy);
|
|
|
|
if (!$turno) {
|
|
$pdo->rollBack();
|
|
jsonOk(['turno' => null], 'Cola de recepción vacía');
|
|
}
|
|
|
|
// Actualizar estado + timestamp
|
|
// $deskId puede ser null si el recepcionista no seleccionó escritorio;
|
|
// se guarda igual (puede quedar NULL) pero la query del display ya no
|
|
// usa IS NULL para evitar que aparezca en todos los escritorios.
|
|
$stmt = $pdo->prepare(
|
|
'UPDATE turnero_turnos
|
|
SET estado = "en_recepcion",
|
|
llamado_recepcion_at = NOW(),
|
|
inicio_recepcion_at = NOW(),
|
|
atendido_recepcion_por = ?,
|
|
recepcion_desk_id = ?
|
|
WHERE id = ? AND estado = "espera"'
|
|
);
|
|
$stmt->execute([adminId(), $deskId, $turno['id']]);
|
|
|
|
if ($stmt->rowCount() === 0) {
|
|
// Otro proceso lo tomó primero (race condition)
|
|
$pdo->rollBack();
|
|
jsonError('El turno ya fue tomado por otro operador. Intente de nuevo.', 409);
|
|
}
|
|
|
|
} else {
|
|
// Cola de un Lugar específico: turnos 'en_espera_lugar' asignados a ese lugar
|
|
if ($turnoId) {
|
|
// Llamar uno específico por id (puede ser en_espera_lugar o ya en_servicio para re-llamar)
|
|
$stmt = $pdo->prepare(
|
|
'SELECT t.*, 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.id = ? AND t.estado IN ("en_espera_lugar", "en_servicio")'
|
|
);
|
|
$stmt->execute([$turnoId]);
|
|
$turno = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
if (!$turno) {
|
|
$pdo->rollBack();
|
|
jsonError('El turno no está en espera o no existe.', 404);
|
|
}
|
|
} else {
|
|
$turno = siguienteTurnoEnCola('en_espera_lugar', $lugarId, null, $sesionHoy);
|
|
if (!$turno) {
|
|
$pdo->rollBack();
|
|
jsonOk(['turno' => null], 'Cola del lugar vacía');
|
|
}
|
|
}
|
|
|
|
// Re-llamar: si ya está en_servicio en ESTA estación, solo actualiza llamado_at
|
|
if ($turno['estado'] === 'en_servicio' && $turno['lugar_destino_id'] == $lugarId) {
|
|
$pdo->prepare(
|
|
'UPDATE turnero_turnos SET llamado_lugar_at = NOW() WHERE id = ?'
|
|
)->execute([$turno['id']]);
|
|
$pdo->commit();
|
|
// Refrescar y retornar
|
|
$stmt = $pdo->prepare(
|
|
'SELECT t.*, 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.id = ?'
|
|
);
|
|
$stmt->execute([$turno['id']]);
|
|
$turnoActualizado = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
notificarSSE((int)$turnoActualizado['sesion_id']);
|
|
jsonOk(['turno' => $turnoActualizado], "Turno {$turnoActualizado['codigo']} re-llamado");
|
|
}
|
|
|
|
$stmt = $pdo->prepare(
|
|
'UPDATE turnero_turnos
|
|
SET estado = "en_servicio",
|
|
llamado_lugar_at = NOW(),
|
|
inicio_lugar_at = NOW(),
|
|
atendido_lugar_por= ?,
|
|
lugar_destino_id = ?
|
|
WHERE id = ? AND estado = "en_espera_lugar"'
|
|
);
|
|
$stmt->execute([adminId(), $lugarId, $turno['id']]);
|
|
|
|
if ($stmt->rowCount() === 0) {
|
|
$pdo->rollBack();
|
|
jsonError('El turno ya fue tomado por otro operador. Intente de nuevo.', 409);
|
|
}
|
|
}
|
|
|
|
// Refrescar datos del turno actualizado
|
|
$stmt = $pdo->prepare(
|
|
'SELECT t.*,
|
|
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.id = ?'
|
|
);
|
|
$stmt->execute([$turno['id']]);
|
|
$turnoActualizado = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
$pdo->commit();
|
|
notificarSSE((int) $turnoActualizado['sesion_id']);
|
|
|
|
jsonOk(['turno' => $turnoActualizado], "Turno {$turnoActualizado['codigo']} llamado");
|
|
|
|
} catch (\Throwable $e) {
|
|
if ($pdo->inTransaction()) {
|
|
$pdo->rollBack();
|
|
}
|
|
jsonError('Error al llamar turno: ' . $e->getMessage(), 500);
|
|
}
|