beginTransaction(); try { if ($area === 'recepcion') { // Cola recepción: turnos en estado 'espera' sin filtro de lugar $turno = siguienteTurnoEnCola('espera'); if (!$turno) { $pdo->rollBack(); jsonOk(['turno' => null], 'Cola de recepción vacía'); } // Actualizar estado + timestamp $stmt = $pdo->prepare( 'UPDATE turnero_turnos SET estado = "en_recepcion", llamado_recepcion_at = NOW(), inicio_recepcion_at = NOW(), atendido_recepcion_por = ?, recepcion_desk_id = COALESCE(?, 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 $turno = siguienteTurnoEnCola('en_espera_lugar', $lugarId); if (!$turno) { $pdo->rollBack(); jsonOk(['turno' => null], 'Cola del lugar vacía'); } $stmt = $pdo->prepare( 'UPDATE turnero_turnos SET estado = "en_servicio", llamado_lugar_at = NOW(), inicio_lugar_at = NOW(), atendido_lugar_por = ? WHERE id = ? AND estado = "en_espera_lugar"' ); $stmt->execute([adminId(), $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); }