Use PostgreSQL-compatible winner menu queries
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-09-14 21:09:36 +09:00
parent a9af9cb010
commit 9b19b82caf
3 changed files with 6 additions and 6 deletions

View File

@@ -11,6 +11,7 @@
- При отклонённой операции `rollback()` инвалидировал загруженные ORM-объекты. Итоговый ответ обращался к `lottery.title` после закрытия сессии. Теперь необходимые значения сохраняются до операции; аналогичные обращения исправлены в одиночных и массовых операциях по пользователям.
- Быстрый ввод `КАРТА СЧЁТ` не создавал отсутствующий счёт, хотя предварительный экран находил владельца карты. Теперь известная карта позволяет создать счёт и участие одной транзакцией. Уникальность счёта защищает работу кассиров в разных розыгрышах; владелец существующего счёта не меняется.
- Широкие фильтры кнопок перехватывали отчёты, подтверждения переигровки и удаления, редактирование победителя. Фильтры теперь проверяют полный формат и допустимый числовой ID. Нераспознанная кнопка получает понятный ответ.
- PostgreSQL-прогон выявил несовместимость `SELECT DISTINCT` по розыгрышам с JSON-полями. Меню редактирования и удаления победителей используют `EXISTS`: розыгрыш выводится один раз без сравнения JSON.
- Удалённые розыгрыши и истёкшие данные диалога обрабатываются без `NoneType` и `IndexError`.
- Добавление и удаление счёта из детального меню используют общие транзакционные проверки открытого розыгрыша и владельца.
- Экраны победителей поддерживают участие без Telegram-профиля. При ручном назначении по счёту сохраняется именно выбранный билет; один счёт нельзя назначить на разные призовые места.

View File

@@ -3069,8 +3069,7 @@ async def edit_winner_start(callback: CallbackQuery):
from sqlalchemy import select
result = await session.execute(
select(Lottery)
.join(Winner)
.distinct()
.where(select(Winner.id).where(Winner.lottery_id == Lottery.id).exists())
.order_by(Lottery.created_at.desc())
.limit(20)
)
@@ -3196,8 +3195,7 @@ async def remove_winner_start(callback: CallbackQuery):
from sqlalchemy import select
result = await session.execute(
select(Lottery)
.join(Winner)
.distinct()
.where(select(Winner.id).where(Winner.lottery_id == Lottery.id).exists())
.order_by(Lottery.created_at.desc())
.limit(20)
)

View File

@@ -119,7 +119,7 @@ async def test_duplicate_account_reports_and_finishes_dialog(route, caplog):
@pytest.mark.parametrize("callback", [
"admin_participants_by_lottery", "admin_participants_report", "admin_edit_winner",
"admin_participants_by_lottery", "admin_participants_report", "admin_edit_winner", "admin_remove_winner",
"admin_edit_winner_lottery_{id}", "admin_edit_lottery_select_{id}",
"admin_lottery_detail_{id}", "admin_participants_{id}", "admin_stats",
"admin_list_all_participants", "admin_users_list:1", "admin_winners",
@@ -244,7 +244,8 @@ async def test_anonymous_winner_admin_pages(caplog):
session.add(winner)
await session.commit()
winner_id = winner.id
for callback in (f"admin_edit_winner_lottery_{lottery_id}", f"admin_edit_winner_id_{winner_id}",
for callback in ("admin_edit_winner", "admin_remove_winner",
f"admin_edit_winner_lottery_{lottery_id}", f"admin_edit_winner_id_{winner_id}",
f"admin_remove_winner_lottery_{lottery_id}", f"admin_confirm_remove_winner_{winner_id}",
f"admin_do_remove_winner_{winner_id}"):
await send(callback=callback)