181 lines
6.6 KiB
PHP
181 lines
6.6 KiB
PHP
<?php
|
|
/**
|
|
* POST /modules/turnero/api/cambiar_estado.php
|
|
* Avanza o cancela el estado de un turno.
|
|
* Requiere login + módulo turnero.
|
|
*
|
|
* Body JSON:
|
|
* turno_id int requerido
|
|
* nuevo_estado string requerido (ver transiciones válidas abajo)
|
|
* lugar_id int requerido cuando nuevo_estado = "en_espera_lugar"
|
|
*
|
|
* Transiciones permettidas:
|
|
* espera → en_recepcion
|
|
* en_recepcion → en_espera_lugar, ausente, cancelado
|
|
* en_espera_lugar → en_servicio, ausente, cancelado
|
|
* en_servicio → finalizado, ausente, cancelado
|
|
* (cualquier) → ausente, cancelado
|
|
*
|
|
* Bloqueo especial:
|
|
* en_espera_lugar → en_servicio requiere que todos los consentimientos
|
|
* del turno estén en estado 'firmado' o 'rechazado'.
|
|
*/
|
|
|
|
require_once __DIR__ . '/_helpers.php';
|
|
requireMethod('POST');
|
|
requireTurnero();
|
|
|
|
$datos = inputJson();
|
|
$turnoId = isset($datos['turno_id']) ? (int) $datos['turno_id'] : 0;
|
|
$nuevoEstado = isset($datos['nuevo_estado']) ? trim((string) $datos['nuevo_estado']) : '';
|
|
$lugarId = isset($datos['lugar_id']) ? (int) $datos['lugar_id'] : null;
|
|
|
|
// ── Validación básica ─────────────────────────────────────────
|
|
if ($turnoId <= 0) jsonError('turno_id inválido.');
|
|
if ($nuevoEstado === '') jsonError('nuevo_estado es requerido.');
|
|
|
|
// Mapa de transiciones válidas [estado_actual => [estados_destino_permitidos]]
|
|
const TRANSICIONES = [
|
|
'espera' => ['en_recepcion', 'ausente', 'cancelado'],
|
|
'en_recepcion' => ['en_espera_lugar', 'espera', 'ausente', 'cancelado'],
|
|
'en_espera_lugar' => ['en_servicio', 'ausente', 'cancelado'],
|
|
'en_servicio' => ['finalizado', 'ausente', 'cancelado', 'en_espera_lugar'],
|
|
];
|
|
|
|
$pdo = db();
|
|
$pdo->beginTransaction();
|
|
|
|
try {
|
|
// Leer turno actual con lock
|
|
$stmt = $pdo->prepare(
|
|
'SELECT t.*, p.codigo AS prioridad_codigo
|
|
FROM turnero_turnos t
|
|
JOIN turnero_prioridades p ON p.id = t.prioridad_id
|
|
WHERE t.id = ?
|
|
FOR UPDATE'
|
|
);
|
|
$stmt->execute([$turnoId]);
|
|
$turno = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$turno) {
|
|
$pdo->rollBack();
|
|
jsonError('Turno no encontrado.', 404);
|
|
}
|
|
|
|
$estadoActual = $turno['estado'];
|
|
|
|
// Estados finales no modificables
|
|
if (in_array($estadoActual, ['finalizado', 'ausente', 'cancelado'], true)) {
|
|
$pdo->rollBack();
|
|
jsonError("El turno ya está en estado '{$estadoActual}' y no puede modificarse.", 422);
|
|
}
|
|
|
|
// Verificar que la transición sea válida
|
|
$permitidos = TRANSICIONES[$estadoActual] ?? [];
|
|
if (!in_array($nuevoEstado, $permitidos, true)) {
|
|
$pdo->rollBack();
|
|
jsonError(
|
|
"Transición '{$estadoActual}' → '{$nuevoEstado}' no está permitida. "
|
|
. "Transiciones válidas: " . implode(', ', $permitidos),
|
|
422
|
|
);
|
|
}
|
|
|
|
// ── Regla especial: en_espera_lugar → en_servicio ─────────
|
|
if ($nuevoEstado === 'en_servicio') {
|
|
$cStmt = $pdo->prepare(
|
|
"SELECT COUNT(*) FROM turnero_consentimientos
|
|
WHERE turno_id = ?
|
|
AND estado NOT IN ('firmado', 'rechazado')"
|
|
);
|
|
$cStmt->execute([$turnoId]);
|
|
$pendientes = (int) $cStmt->fetchColumn();
|
|
|
|
if ($pendientes > 0) {
|
|
$pdo->rollBack();
|
|
jsonError(
|
|
"Hay {$pendientes} consentimiento(s) pendiente(s). "
|
|
. "El turno no puede pasar a 'en_servicio' hasta que todos estén firmados o rechazados.",
|
|
422
|
|
);
|
|
}
|
|
}
|
|
|
|
// ── Construir UPDATE dinámico ─────────────────────────────
|
|
$sets = ['estado = ?'];
|
|
$binds = [$nuevoEstado];
|
|
|
|
switch ($nuevoEstado) {
|
|
case 'en_recepcion':
|
|
$sets[] = 'llamado_recepcion_at = COALESCE(llamado_recepcion_at, NOW())';
|
|
$sets[] = 'inicio_recepcion_at = COALESCE(inicio_recepcion_at, NOW())';
|
|
$sets[] = 'atendido_recepcion_por = COALESCE(atendido_recepcion_por, ?)';
|
|
$binds[] = adminId();
|
|
break;
|
|
|
|
case 'en_espera_lugar':
|
|
// Requiere lugar_id (al volver desde en_servicio se conserva lugar_destino_id actual)
|
|
if ($lugarId) {
|
|
$sets[] = 'lugar_destino_id = ?';
|
|
$binds[] = $lugarId;
|
|
}
|
|
$sets[] = 'llamado_lugar_at = NULL';
|
|
$sets[] = 'inicio_lugar_at = NULL';
|
|
$sets[] = 'fin_recepcion_at = COALESCE(fin_recepcion_at, NOW())';
|
|
$sets[] = 'atendido_recepcion_por = COALESCE(atendido_recepcion_por, ?)';
|
|
$binds[] = adminId();
|
|
break;
|
|
|
|
case 'en_servicio':
|
|
$sets[] = 'llamado_lugar_at = COALESCE(llamado_lugar_at, NOW())';
|
|
$sets[] = 'inicio_lugar_at = COALESCE(inicio_lugar_at, NOW())';
|
|
$sets[] = 'atendido_lugar_por = COALESCE(atendido_lugar_por, ?)';
|
|
$binds[] = adminId();
|
|
break;
|
|
|
|
case 'finalizado':
|
|
$sets[] = 'fin_lugar_at = COALESCE(fin_lugar_at, NOW())';
|
|
$sets[] = 'atendido_lugar_por = COALESCE(atendido_lugar_por, ?)';
|
|
$binds[] = adminId();
|
|
break;
|
|
|
|
case 'espera':
|
|
// Soltar: liberar el turno para que otro escritorio lo llame
|
|
$sets[] = 'recepcion_desk_id = NULL';
|
|
$sets[] = 'inicio_recepcion_at = NULL';
|
|
$sets[] = 'atendido_recepcion_por = NULL';
|
|
break;
|
|
|
|
case 'ausente':
|
|
case 'cancelado':
|
|
// Sin timestamps adicionales; simplemente cambio de estado
|
|
break;
|
|
}
|
|
|
|
$sql = 'UPDATE turnero_turnos SET ' . implode(', ', $sets) . ' WHERE id = ?';
|
|
$binds[] = $turnoId;
|
|
|
|
$pdo->prepare($sql)->execute($binds);
|
|
$pdo->commit();
|
|
|
|
// Refrescar y devolver 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([$turnoId]);
|
|
$turnoActualizado = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
notificarSSE((int) $turnoActualizado['sesion_id']);
|
|
|
|
jsonOk(['turno' => $turnoActualizado], "Estado actualizado a '{$nuevoEstado}'");
|
|
|
|
} catch (\Throwable $e) {
|
|
if ($pdo->inTransaction()) {
|
|
$pdo->rollBack();
|
|
}
|
|
jsonError('Error al cambiar estado: ' . $e->getMessage(), 500);
|
|
}
|