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); }