mirror of
https://github.com/koloideal/DutyLog.git
synced 2026-06-10 10:25:29 +03:00
update
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
from dutylog.application.bot.user_dialogs.admin_dialogs.admin_menu_dialog import (
|
||||
admin_menu_dialog,
|
||||
)
|
||||
|
||||
__all__ = ["admin_menu_dialog"]
|
||||
@@ -1,100 +0,0 @@
|
||||
from aiogram_dialog import Dialog
|
||||
|
||||
from dutylog.application.bot.user_dialogs.admin_dialogs.main_menu import (
|
||||
main_menu_window,
|
||||
statistics_window,
|
||||
)
|
||||
from dutylog.application.bot.user_dialogs.admin_dialogs.residents_management import (
|
||||
residents_list_window,
|
||||
resident_info_window,
|
||||
resident_logout_confirm_window,
|
||||
resident_delete_confirm_window,
|
||||
create_resident_name_window,
|
||||
create_resident_floor_window,
|
||||
create_resident_room_window,
|
||||
create_resident_confirm_window,
|
||||
search_input_window,
|
||||
search_results_window,
|
||||
)
|
||||
from dutylog.application.bot.user_dialogs.admin_dialogs.residents_filter import (
|
||||
filter_select_window,
|
||||
filter_hours_input_window,
|
||||
filtered_results_window,
|
||||
)
|
||||
from dutylog.application.bot.user_dialogs.admin_dialogs.floors_management import (
|
||||
floors_list_window,
|
||||
floor_delete_confirm_window,
|
||||
create_floor_input_window,
|
||||
create_floor_confirm_window,
|
||||
)
|
||||
from dutylog.application.bot.user_dialogs.admin_dialogs.rooms_management import (
|
||||
rooms_select_floor_window,
|
||||
rooms_list_window,
|
||||
room_delete_confirm_window,
|
||||
create_room_select_floor_window,
|
||||
create_room_input_window,
|
||||
create_room_confirm_window,
|
||||
)
|
||||
from dutylog.application.bot.user_dialogs.admin_dialogs.reporting_period_management import (
|
||||
reporting_period_window,
|
||||
next_period_confirm_window,
|
||||
generate_report_select_period_window,
|
||||
generate_report_confirm_window,
|
||||
)
|
||||
from dutylog.application.bot.user_dialogs.admin_dialogs.hours_management import (
|
||||
add_hours_select_window,
|
||||
remove_hours_select_window,
|
||||
add_hours_custom_window,
|
||||
remove_hours_custom_window,
|
||||
add_hours_remark_window,
|
||||
remove_hours_remark_window,
|
||||
add_hours_confirm_window,
|
||||
remove_hours_confirm_window,
|
||||
)
|
||||
from dutylog.application.bot.user_dialogs.admin_dialogs.broadcast import (
|
||||
broadcast_window,
|
||||
broadcast_confirm_window,
|
||||
)
|
||||
|
||||
|
||||
admin_menu_dialog = Dialog(
|
||||
main_menu_window,
|
||||
residents_list_window,
|
||||
resident_info_window,
|
||||
resident_logout_confirm_window,
|
||||
resident_delete_confirm_window,
|
||||
add_hours_select_window,
|
||||
remove_hours_select_window,
|
||||
add_hours_custom_window,
|
||||
remove_hours_custom_window,
|
||||
add_hours_remark_window,
|
||||
remove_hours_remark_window,
|
||||
add_hours_confirm_window,
|
||||
remove_hours_confirm_window,
|
||||
create_resident_name_window,
|
||||
create_resident_floor_window,
|
||||
create_resident_room_window,
|
||||
create_resident_confirm_window,
|
||||
floors_list_window,
|
||||
floor_delete_confirm_window,
|
||||
create_floor_input_window,
|
||||
create_floor_confirm_window,
|
||||
rooms_select_floor_window,
|
||||
rooms_list_window,
|
||||
room_delete_confirm_window,
|
||||
create_room_select_floor_window,
|
||||
create_room_input_window,
|
||||
create_room_confirm_window,
|
||||
reporting_period_window,
|
||||
next_period_confirm_window,
|
||||
generate_report_select_period_window,
|
||||
generate_report_confirm_window,
|
||||
statistics_window,
|
||||
broadcast_window,
|
||||
broadcast_confirm_window,
|
||||
search_input_window,
|
||||
search_results_window,
|
||||
filter_select_window,
|
||||
filter_hours_input_window,
|
||||
filtered_results_window,
|
||||
)
|
||||
@@ -1,109 +0,0 @@
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from aiogram import Bot
|
||||
from aiogram.exceptions import TelegramForbiddenError, TelegramBadRequest
|
||||
from aiogram_dialog import Window, DialogManager
|
||||
from aiogram_dialog.widgets.text import Const
|
||||
from aiogram_dialog.widgets.kbd import Row, SwitchTo, Button
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
|
||||
from dutylog.infrastructure.database.repositories.users_repository import (
|
||||
UsersRepository,
|
||||
)
|
||||
|
||||
|
||||
async def on_broadcast_message(
|
||||
message: Message,
|
||||
widget: MessageInput,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
dialog_manager.dialog_data["broadcast_message_id"] = message.message_id
|
||||
dialog_manager.dialog_data["broadcast_chat_id"] = message.chat.id
|
||||
|
||||
await message.copy_to(message.chat.id)
|
||||
await dialog_manager.switch_to(AdminMenuSG.broadcast_confirm)
|
||||
|
||||
|
||||
@inject
|
||||
async def on_broadcast_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
users_repository: FromDishka[UsersRepository],
|
||||
):
|
||||
assert callback.message is not None
|
||||
|
||||
bot: Bot = dialog_manager.middleware_data["bot"]
|
||||
message_id = dialog_manager.dialog_data["broadcast_message_id"]
|
||||
chat_id = dialog_manager.dialog_data["broadcast_chat_id"]
|
||||
admin_id = callback.from_user.id
|
||||
|
||||
all_users = await users_repository.get_all_users()
|
||||
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
|
||||
for user in all_users:
|
||||
if user.id == admin_id:
|
||||
continue
|
||||
|
||||
try:
|
||||
await bot.copy_message(
|
||||
chat_id=user.id,
|
||||
from_chat_id=chat_id,
|
||||
message_id=message_id,
|
||||
)
|
||||
success_count += 1
|
||||
except TelegramForbiddenError:
|
||||
failed_count += 1
|
||||
except TelegramBadRequest:
|
||||
failed_count += 1
|
||||
|
||||
result_text = f"""
|
||||
<blockquote>📢 <b>Результаты рассылки</b></blockquote>
|
||||
|
||||
✅ <b>Успешно отправлено:</b> <code>{success_count}</code>
|
||||
❌ <b>Не удалось отправить:</b> <code>{failed_count}</code>
|
||||
📊 <b>Всего пользователей:</b> <code>{len(all_users) - 1}</code>
|
||||
"""
|
||||
|
||||
await callback.message.answer(result_text)
|
||||
await callback.message.delete()
|
||||
await dialog_manager.start(AdminMenuSG.main)
|
||||
|
||||
|
||||
async def on_broadcast_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.main)
|
||||
|
||||
|
||||
broadcast_window = Window(
|
||||
Const(
|
||||
"<blockquote>📢 <b>Рассылка</b></blockquote>\n\nОтправьте сообщение, которое хотите разослать всем пользователям:"
|
||||
),
|
||||
MessageInput(on_broadcast_message),
|
||||
SwitchTo(Const("◀️ Отмена"), id="cancel_broadcast", state=AdminMenuSG.main),
|
||||
state=AdminMenuSG.broadcast,
|
||||
)
|
||||
|
||||
broadcast_confirm_window = Window(
|
||||
Const(
|
||||
"<blockquote>📢 <b>Подтверждение рассылки</b></blockquote>\n\n⚠️ Вы уверены, что хотите отправить это сообщение всем пользователям?"
|
||||
),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да"), id="confirm_broadcast", on_click=on_broadcast_confirm
|
||||
),
|
||||
Button(
|
||||
Const("❌ Нет"),
|
||||
id="cancel_broadcast_confirm",
|
||||
on_click=on_broadcast_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.broadcast_confirm,
|
||||
)
|
||||
@@ -1,241 +0,0 @@
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from aiogram_dialog import Window, DialogManager
|
||||
from aiogram_dialog.widgets.text import Format, Const
|
||||
from aiogram_dialog.widgets.kbd import SwitchTo, Button, ScrollingGroup, Select, Row
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
|
||||
from dutylog.infrastructure.database.repositories.floors_repository import (
|
||||
FloorsRepository,
|
||||
)
|
||||
|
||||
|
||||
async def on_floors_click(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.floors)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_floors_list_data(
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
all_floors = await floors_repository.get_all_floors()
|
||||
all_floors.sort(key=lambda f: f.number)
|
||||
|
||||
floors_data = [(f"🏢 Этаж {f.number}", f.id) for f in all_floors]
|
||||
|
||||
content = f"""
|
||||
<blockquote>🏢 <b>Этажи</b></blockquote>
|
||||
|
||||
<b>Всего этажей:</b> <code>{len(all_floors)}</code>
|
||||
|
||||
Выберите этаж для удаления:
|
||||
"""
|
||||
|
||||
return {
|
||||
"content": content,
|
||||
"floors": floors_data,
|
||||
}
|
||||
|
||||
|
||||
async def on_floor_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["selected_floor_id"] = int(item_id)
|
||||
await dialog_manager.switch_to(AdminMenuSG.floor_delete_confirm)
|
||||
|
||||
|
||||
async def on_add_floor_click(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.create_floor_input)
|
||||
|
||||
|
||||
async def on_floor_number_input(
|
||||
message: Message,
|
||||
widget: MessageInput,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
if not message.text:
|
||||
await message.answer("⚠️ Пожалуйста, введите номер этажа")
|
||||
return
|
||||
|
||||
try:
|
||||
floor_number = int(message.text)
|
||||
if floor_number <= 0:
|
||||
await message.answer("⚠️ Номер этажа должен быть положительным числом")
|
||||
return
|
||||
|
||||
dialog_manager.dialog_data["new_floor_number"] = floor_number
|
||||
await dialog_manager.switch_to(AdminMenuSG.create_floor_confirm)
|
||||
except ValueError:
|
||||
await message.answer("⚠️ Пожалуйста, введите корректное число")
|
||||
|
||||
|
||||
async def get_create_floor_confirm_data(
|
||||
dialog_manager: DialogManager,
|
||||
**kwargs,
|
||||
):
|
||||
floor_number = dialog_manager.dialog_data.get("new_floor_number", "???")
|
||||
return {"floor_number": floor_number}
|
||||
|
||||
|
||||
@inject
|
||||
async def on_create_floor_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
):
|
||||
floor_number = dialog_manager.dialog_data.get("new_floor_number")
|
||||
|
||||
if floor_number:
|
||||
existing_floor = await floors_repository.get_floor_by_number(floor_number)
|
||||
if existing_floor:
|
||||
await callback.answer(
|
||||
f"⚠️ Этаж {floor_number} уже существует!",
|
||||
show_alert=True
|
||||
)
|
||||
await dialog_manager.switch_to(AdminMenuSG.floors)
|
||||
return
|
||||
|
||||
await floors_repository.create_floor(floor_number)
|
||||
await callback.answer("✅ Этаж создан!")
|
||||
|
||||
await dialog_manager.switch_to(AdminMenuSG.floors)
|
||||
|
||||
|
||||
async def on_create_floor_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.floors)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_floor_delete_confirm_data(
|
||||
dialog_manager: DialogManager,
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
floor_id = dialog_manager.dialog_data.get("selected_floor_id")
|
||||
|
||||
if not floor_id:
|
||||
return {"floor_number": "???"}
|
||||
|
||||
floor = await floors_repository.get_floor_by_id(floor_id)
|
||||
floor_number = floor.number if floor else "???"
|
||||
|
||||
return {"floor_number": floor_number}
|
||||
|
||||
|
||||
@inject
|
||||
async def on_delete_floor_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
):
|
||||
floor_id = dialog_manager.dialog_data.get("selected_floor_id")
|
||||
|
||||
if floor_id:
|
||||
await floors_repository.delete_floor(floor_id)
|
||||
await callback.answer("✅ Этаж удален!")
|
||||
|
||||
await dialog_manager.switch_to(AdminMenuSG.floors)
|
||||
|
||||
|
||||
async def on_delete_floor_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.floors)
|
||||
|
||||
|
||||
floors_list_window = Window(
|
||||
Format("{content}"),
|
||||
ScrollingGroup(
|
||||
Select(
|
||||
Format("{item[0]}"),
|
||||
id="floors_select",
|
||||
item_id_getter=lambda x: x[1],
|
||||
items="floors",
|
||||
on_click=on_floor_selected,
|
||||
),
|
||||
id="floors_scroll",
|
||||
width=1,
|
||||
height=7,
|
||||
),
|
||||
Button(
|
||||
Const("➕ Добавить этаж"),
|
||||
id="add_floor_btn",
|
||||
on_click=on_add_floor_click,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_admin_menu_from_floors",
|
||||
state=AdminMenuSG.main,
|
||||
),
|
||||
state=AdminMenuSG.floors,
|
||||
getter=get_floors_list_data,
|
||||
)
|
||||
|
||||
floor_delete_confirm_window = Window(
|
||||
Format("<blockquote>⚠️ <b>Подтверждение удаления</b></blockquote>\n\nВы точно хотите удалить этаж <code>{floor_number}</code>? Это действие необратимо и удалит все комнаты на этом этаже!"),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да, удалить"),
|
||||
id="confirm_delete_floor",
|
||||
on_click=on_delete_floor_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Отмена"),
|
||||
id="cancel_delete_floor",
|
||||
on_click=on_delete_floor_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.floor_delete_confirm,
|
||||
getter=get_floor_delete_confirm_data,
|
||||
)
|
||||
|
||||
create_floor_input_window = Window(
|
||||
Const("<blockquote>➕ <b>Создание этажа</b></blockquote>\n\nВведите номер этажа:"),
|
||||
MessageInput(on_floor_number_input),
|
||||
SwitchTo(
|
||||
Const("◀️ Отмена"),
|
||||
id="cancel_create_floor_input",
|
||||
state=AdminMenuSG.floors,
|
||||
),
|
||||
state=AdminMenuSG.create_floor_input,
|
||||
)
|
||||
|
||||
create_floor_confirm_window = Window(
|
||||
Format("<blockquote>✅ <b>Подтверждение</b></blockquote>\n\nСоздать этаж <code>{floor_number}</code>?"),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да"),
|
||||
id="confirm_create_floor",
|
||||
on_click=on_create_floor_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Нет"),
|
||||
id="cancel_create_floor",
|
||||
on_click=on_create_floor_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.create_floor_confirm,
|
||||
getter=get_create_floor_confirm_data,
|
||||
)
|
||||
@@ -1,342 +0,0 @@
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from aiogram_dialog import Window, DialogManager
|
||||
from aiogram_dialog.widgets.text import Format, Const
|
||||
from aiogram_dialog.widgets.kbd import Row, SwitchTo, Button, Select, Group
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
|
||||
from dutylog.infrastructure.database.repositories.residents_repository import (
|
||||
ResidentsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.database.repositories.hours_transactions_repository import (
|
||||
HoursTransactionsRepository,
|
||||
)
|
||||
|
||||
|
||||
async def on_add_hours_click(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.add_hours_select)
|
||||
|
||||
|
||||
async def on_remove_hours_click(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.remove_hours_select)
|
||||
|
||||
|
||||
async def get_hours_select_data(**kwargs):
|
||||
hours_options = [
|
||||
(5, "5"), (10, "10"), (15, "15"), (20, "20"),
|
||||
(25, "25"), (30, "30"), (35, "35"), (40, "40"),
|
||||
(45, "45"), (50, "50"), (55, "55"), (60, "60"),
|
||||
(65, "65"), (70, "70"), (75, "75"), (80, "80"),
|
||||
]
|
||||
return {"hours_options": hours_options}
|
||||
|
||||
|
||||
async def on_hours_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["selected_hours"] = int(item_id)
|
||||
|
||||
if dialog_manager.current_context().state == AdminMenuSG.add_hours_select:
|
||||
await dialog_manager.switch_to(AdminMenuSG.add_hours_remark)
|
||||
else:
|
||||
await dialog_manager.switch_to(AdminMenuSG.remove_hours_remark)
|
||||
|
||||
|
||||
async def on_custom_hours_click(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
if dialog_manager.current_context().state == AdminMenuSG.add_hours_select:
|
||||
await dialog_manager.switch_to(AdminMenuSG.add_hours_custom)
|
||||
else:
|
||||
await dialog_manager.switch_to(AdminMenuSG.remove_hours_custom)
|
||||
|
||||
|
||||
async def on_custom_hours_input(
|
||||
message: Message,
|
||||
widget: MessageInput,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
if not message.text:
|
||||
await message.answer("⚠️ Пожалуйста, введите число")
|
||||
return
|
||||
|
||||
try:
|
||||
hours = int(message.text)
|
||||
if hours <= 0:
|
||||
await message.answer("⚠️ Количество часов должно быть положительным числом")
|
||||
return
|
||||
|
||||
dialog_manager.dialog_data["selected_hours"] = hours
|
||||
|
||||
if dialog_manager.current_context().state == AdminMenuSG.add_hours_custom:
|
||||
await dialog_manager.switch_to(AdminMenuSG.add_hours_remark)
|
||||
else:
|
||||
await dialog_manager.switch_to(AdminMenuSG.remove_hours_remark)
|
||||
except ValueError:
|
||||
await message.answer("⚠️ Пожалуйста, введите корректное число")
|
||||
|
||||
|
||||
async def on_add_hours_remark_input(
|
||||
message: Message,
|
||||
widget: MessageInput,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
if not message.text or len(message.text.strip()) < 1:
|
||||
await message.answer("⚠️ Пожалуйста, введите причину добавления часов")
|
||||
return
|
||||
|
||||
dialog_manager.dialog_data["hours_remark"] = message.text.strip()
|
||||
await dialog_manager.switch_to(AdminMenuSG.add_hours_confirm)
|
||||
|
||||
|
||||
async def on_remove_hours_remark_input(
|
||||
message: Message,
|
||||
widget: MessageInput,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
if message.text and len(message.text.strip()) > 0:
|
||||
dialog_manager.dialog_data["hours_remark"] = message.text.strip()
|
||||
else:
|
||||
dialog_manager.dialog_data["hours_remark"] = None
|
||||
|
||||
await dialog_manager.switch_to(AdminMenuSG.remove_hours_confirm)
|
||||
|
||||
|
||||
async def on_skip_remark(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
dialog_manager.dialog_data["hours_remark"] = None
|
||||
await dialog_manager.switch_to(AdminMenuSG.remove_hours_confirm)
|
||||
|
||||
|
||||
async def get_hours_confirm_data(
|
||||
dialog_manager: DialogManager,
|
||||
**kwargs,
|
||||
):
|
||||
hours = dialog_manager.dialog_data.get("selected_hours", 0)
|
||||
remark = dialog_manager.dialog_data.get("hours_remark")
|
||||
|
||||
return {
|
||||
"hours": hours,
|
||||
"remark": remark if remark else "Не указана",
|
||||
}
|
||||
|
||||
|
||||
@inject
|
||||
async def on_add_hours_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
transactions_repository: FromDishka[HoursTransactionsRepository],
|
||||
):
|
||||
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
||||
hours = dialog_manager.dialog_data.get("selected_hours")
|
||||
remark = dialog_manager.dialog_data.get("hours_remark")
|
||||
admin_id = callback.from_user.id
|
||||
|
||||
if resident_id and hours:
|
||||
await transactions_repository.add_hours(
|
||||
resident_id=resident_id,
|
||||
amount=hours,
|
||||
admin_id=admin_id,
|
||||
is_active=True,
|
||||
remark=remark,
|
||||
)
|
||||
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
@inject
|
||||
async def on_remove_hours_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
transactions_repository: FromDishka[HoursTransactionsRepository],
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
):
|
||||
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
||||
hours = dialog_manager.dialog_data.get("selected_hours")
|
||||
remark = dialog_manager.dialog_data.get("hours_remark")
|
||||
admin_id = callback.from_user.id
|
||||
|
||||
if resident_id and hours:
|
||||
resident = await residents_repository.get_resident_by_id(resident_id)
|
||||
if resident and resident.active_hours < hours:
|
||||
await callback.answer(
|
||||
f"⚠️ Недостаточно часов! У резидента {resident.active_hours} неотработанных ч, а вы пытаетесь отнять {hours} ч",
|
||||
show_alert=True
|
||||
)
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
return
|
||||
|
||||
await transactions_repository.move_hours_to_completed(
|
||||
resident_id=resident_id,
|
||||
amount=hours,
|
||||
admin_id=admin_id,
|
||||
remark=remark,
|
||||
)
|
||||
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
async def on_hours_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
add_hours_select_window = Window(
|
||||
Const("<blockquote>➕ <b>Добавить часы</b></blockquote>\n\nВыберите количество часов:"),
|
||||
Group(
|
||||
Select(
|
||||
Format("{item[1]} ч"),
|
||||
id="hours_select_add",
|
||||
item_id_getter=lambda x: x[0],
|
||||
items="hours_options",
|
||||
on_click=on_hours_selected,
|
||||
),
|
||||
width=4,
|
||||
),
|
||||
Button(
|
||||
Const("✏️ Ввести свое количество"),
|
||||
id="custom_hours_add_btn",
|
||||
on_click=on_custom_hours_click,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Отмена"),
|
||||
id="cancel_add_hours",
|
||||
state=AdminMenuSG.resident_info,
|
||||
),
|
||||
state=AdminMenuSG.add_hours_select,
|
||||
getter=get_hours_select_data,
|
||||
)
|
||||
|
||||
remove_hours_select_window = Window(
|
||||
Const("<blockquote>➖ <b>Отнять часы</b></blockquote>\n\nВыберите количество часов:"),
|
||||
Group(
|
||||
Select(
|
||||
Format("{item[1]} ч"),
|
||||
id="hours_select_remove",
|
||||
item_id_getter=lambda x: x[0],
|
||||
items="hours_options",
|
||||
on_click=on_hours_selected,
|
||||
),
|
||||
width=4,
|
||||
),
|
||||
Button(
|
||||
Const("✏️ Ввести свое количество"),
|
||||
id="custom_hours_remove_btn",
|
||||
on_click=on_custom_hours_click,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Отмена"),
|
||||
id="cancel_remove_hours",
|
||||
state=AdminMenuSG.resident_info,
|
||||
),
|
||||
state=AdminMenuSG.remove_hours_select,
|
||||
getter=get_hours_select_data,
|
||||
)
|
||||
|
||||
add_hours_custom_window = Window(
|
||||
Const("<blockquote>✏️ <b>Добавить часы</b></blockquote>\n\nВведите количество часов:"),
|
||||
MessageInput(on_custom_hours_input),
|
||||
SwitchTo(
|
||||
Const("◀️ Отмена"),
|
||||
id="cancel_custom_add",
|
||||
state=AdminMenuSG.add_hours_select,
|
||||
),
|
||||
state=AdminMenuSG.add_hours_custom,
|
||||
)
|
||||
|
||||
remove_hours_custom_window = Window(
|
||||
Const("<blockquote>✏️ <b>Отнять часы</b></blockquote>\n\nВведите количество часов:"),
|
||||
MessageInput(on_custom_hours_input),
|
||||
SwitchTo(
|
||||
Const("◀️ Отмена"),
|
||||
id="cancel_custom_remove",
|
||||
state=AdminMenuSG.remove_hours_select,
|
||||
),
|
||||
state=AdminMenuSG.remove_hours_custom,
|
||||
)
|
||||
|
||||
add_hours_remark_window = Window(
|
||||
Const("<blockquote>📝 <b>Причина добавления часов</b></blockquote>\n\n<blockquote>Укажите причину добавления часов (обязательно).</blockquote>"),
|
||||
MessageInput(on_add_hours_remark_input),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_add_hours_select",
|
||||
state=AdminMenuSG.add_hours_select,
|
||||
),
|
||||
state=AdminMenuSG.add_hours_remark,
|
||||
)
|
||||
|
||||
remove_hours_remark_window = Window(
|
||||
Const("<blockquote>📝 <b>Причина снятия часов</b></blockquote>\n\n<blockquote>Укажите причину снятия часов (необязательно).</blockquote>"),
|
||||
MessageInput(on_remove_hours_remark_input),
|
||||
Button(
|
||||
Const("⏭ Пропустить"),
|
||||
id="skip_remark_btn",
|
||||
on_click=on_skip_remark,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_remove_hours_select",
|
||||
state=AdminMenuSG.remove_hours_select,
|
||||
),
|
||||
state=AdminMenuSG.remove_hours_remark,
|
||||
)
|
||||
|
||||
add_hours_confirm_window = Window(
|
||||
Format("<blockquote>➕ <b>Подтверждение</b></blockquote>\n\nВы уверены, что хотите добавить <code>{hours}</code> часов?\n\n<b>Причина:</b> {remark}"),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да"),
|
||||
id="confirm_add_hours",
|
||||
on_click=on_add_hours_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Нет"),
|
||||
id="cancel_add_hours_confirm",
|
||||
on_click=on_hours_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.add_hours_confirm,
|
||||
getter=get_hours_confirm_data,
|
||||
)
|
||||
|
||||
remove_hours_confirm_window = Window(
|
||||
Format("<blockquote>➖ <b>Подтверждение</b></blockquote>\n\nВы уверены, что хотите отнять <code>{hours}</code> часов?\n\n<b>Причина:</b> {remark}"),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да"),
|
||||
id="confirm_remove_hours",
|
||||
on_click=on_remove_hours_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Нет"),
|
||||
id="cancel_remove_hours_confirm",
|
||||
on_click=on_hours_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.remove_hours_confirm,
|
||||
getter=get_hours_confirm_data,
|
||||
)
|
||||
@@ -1,183 +0,0 @@
|
||||
from aiogram.types import User
|
||||
from aiogram_dialog import Window, DialogManager
|
||||
from aiogram_dialog.widgets.text import Format, Const
|
||||
from aiogram_dialog.widgets.kbd import Row, SwitchTo, Button
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
|
||||
from dutylog.infrastructure.database.repositories.users_repository import (
|
||||
UsersRepository,
|
||||
)
|
||||
from dutylog.infrastructure.database.repositories.residents_repository import (
|
||||
ResidentsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.database.repositories.reporting_periods_repository import (
|
||||
ReportingPeriodsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.utils.config import Config
|
||||
|
||||
|
||||
MONTH_NAMES = {
|
||||
1: "Январь",
|
||||
2: "Февраль",
|
||||
3: "Март",
|
||||
4: "Апрель",
|
||||
5: "Май",
|
||||
6: "Июнь",
|
||||
7: "Июль",
|
||||
8: "Август",
|
||||
9: "Сентябрь",
|
||||
10: "Октябрь",
|
||||
11: "Ноябрь",
|
||||
12: "Декабрь",
|
||||
}
|
||||
|
||||
|
||||
@inject
|
||||
async def get_admin_menu_data(
|
||||
event_from_user: User,
|
||||
users_repository: FromDishka[UsersRepository],
|
||||
reporting_periods_repository: FromDishka[ReportingPeriodsRepository],
|
||||
config: FromDishka[Config],
|
||||
**kwargs,
|
||||
):
|
||||
user = await users_repository.get_or_create_user(
|
||||
user_id=event_from_user.id,
|
||||
username=event_from_user.username,
|
||||
first_name=event_from_user.first_name,
|
||||
last_name=event_from_user.last_name,
|
||||
)
|
||||
|
||||
is_creator = event_from_user.id == config.bot.creator_id
|
||||
|
||||
if is_creator:
|
||||
greeting = "👑 <b>Создатель</b>"
|
||||
else:
|
||||
greeting = "👨💼 <b>Администратор</b>"
|
||||
|
||||
active_period = await reporting_periods_repository.get_active_period()
|
||||
|
||||
if active_period:
|
||||
start_date = active_period.start_date
|
||||
next_day = start_date + timedelta(days=1)
|
||||
reporting_month = MONTH_NAMES[next_day.month]
|
||||
reporting_year = next_day.year
|
||||
|
||||
days_passed = (datetime.now().date() - start_date).days
|
||||
|
||||
period_info = f"""
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📅 <b>Активный отчётный период</b>
|
||||
<blockquote>Месяц: <b>{reporting_month} {reporting_year}</b>
|
||||
Начало: <code>{start_date.strftime('%d.%m.%Y')}</code>
|
||||
Прошло дней: <code>{days_passed}</code></blockquote>
|
||||
"""
|
||||
else:
|
||||
period_info = ""
|
||||
|
||||
content = f"""
|
||||
{greeting}
|
||||
|
||||
<blockquote>📋 <b>Панель управления</b></blockquote>
|
||||
{period_info}
|
||||
Выберите действие:
|
||||
"""
|
||||
|
||||
return {"content": content}
|
||||
|
||||
|
||||
@inject
|
||||
async def get_statistics_data(
|
||||
users_repository: FromDishka[UsersRepository],
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
all_users = await users_repository.get_all_users()
|
||||
all_residents = await residents_repository.get_all_residents()
|
||||
|
||||
total_users = len(all_users)
|
||||
total_residents = len(all_residents)
|
||||
busy_residents = len([r for r in all_residents if r.is_busy])
|
||||
total_active_hours = sum(r.active_hours for r in all_residents)
|
||||
total_inactive_hours = sum(r.inactive_hours for r in all_residents)
|
||||
admins_count = len([u for u in all_users if u.is_admin])
|
||||
|
||||
stats_text = f"""
|
||||
<blockquote>📊 <b>Статистика системы</b></blockquote>
|
||||
|
||||
👥 <b>Всего пользователей:</b> <code>{total_users}</code>
|
||||
👨💼 <b>Администраторов:</b> <code>{admins_count}</code>
|
||||
|
||||
🏠 <b>Всего резидентов:</b> <code>{total_residents}</code>
|
||||
✅ <b>Привязано к пользователям:</b> <code>{busy_residents}</code>
|
||||
❌ <b>Свободных:</b> <code>{total_residents - busy_residents}</code>
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🟢 <b>Всего отработанных часов:</b> <code>{total_inactive_hours}</code> ч
|
||||
🔴 <b>Всего неотработанных часов:</b> <code>{total_active_hours}</code> ч
|
||||
📊 <b>Общий итог:</b> <code>{total_active_hours + total_inactive_hours}</code> ч
|
||||
"""
|
||||
|
||||
return {"stats_content": stats_text}
|
||||
|
||||
|
||||
async def on_rooms_click(callback, button, dialog_manager):
|
||||
await dialog_manager.switch_to(AdminMenuSG.rooms_select_floor)
|
||||
|
||||
|
||||
async def on_floors_click(callback, button, dialog_manager):
|
||||
await dialog_manager.switch_to(AdminMenuSG.floors)
|
||||
|
||||
|
||||
async def on_reporting_period_click(callback, button, dialog_manager):
|
||||
await dialog_manager.switch_to(AdminMenuSG.reporting_period)
|
||||
|
||||
|
||||
main_menu_window = Window(
|
||||
Format("{content}"),
|
||||
SwitchTo(
|
||||
Const("🏠 Резиденты"),
|
||||
id="residents_btn",
|
||||
state=AdminMenuSG.residents,
|
||||
),
|
||||
Row(
|
||||
Button(
|
||||
Const("🚪 Комнаты"),
|
||||
id="rooms_btn",
|
||||
on_click=on_rooms_click,
|
||||
),
|
||||
Button(
|
||||
Const("🏢 Этажи"),
|
||||
id="floors_btn",
|
||||
on_click=on_floors_click,
|
||||
),
|
||||
),
|
||||
Button(
|
||||
Const("📅 Отчётный период"),
|
||||
id="reporting_period_btn",
|
||||
on_click=on_reporting_period_click,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("📊 Статистика"),
|
||||
id="stats_btn",
|
||||
state=AdminMenuSG.statistics,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("📢 Рассылка"),
|
||||
id="broadcast_btn",
|
||||
state=AdminMenuSG.broadcast,
|
||||
),
|
||||
state=AdminMenuSG.main,
|
||||
getter=get_admin_menu_data,
|
||||
)
|
||||
|
||||
statistics_window = Window(
|
||||
Format("{stats_content}"),
|
||||
SwitchTo(Const("◀️ Назад"), id="back_from_stats", state=AdminMenuSG.main),
|
||||
state=AdminMenuSG.statistics,
|
||||
getter=get_statistics_data,
|
||||
)
|
||||
-409
@@ -1,409 +0,0 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from aiogram.types import CallbackQuery, BufferedInputFile
|
||||
from aiogram_dialog import Window, DialogManager
|
||||
from aiogram_dialog.widgets.text import Format, Const
|
||||
from aiogram_dialog.widgets.kbd import SwitchTo, Button, Row, Select, ScrollingGroup
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
|
||||
from dutylog.infrastructure.database.repositories.reporting_periods_repository import (
|
||||
ReportingPeriodsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.utils.datetime import msk_now
|
||||
from dutylog.services.report_service import ReportService
|
||||
|
||||
|
||||
MONTH_NAMES = {
|
||||
1: "Январь",
|
||||
2: "Февраль",
|
||||
3: "Март",
|
||||
4: "Апрель",
|
||||
5: "Май",
|
||||
6: "Июнь",
|
||||
7: "Июль",
|
||||
8: "Август",
|
||||
9: "Сентябрь",
|
||||
10: "Октябрь",
|
||||
11: "Ноябрь",
|
||||
12: "Декабрь",
|
||||
}
|
||||
|
||||
|
||||
async def on_reporting_period_click(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.reporting_period)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_reporting_period_data(
|
||||
reporting_periods_repository: FromDishka[ReportingPeriodsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
active_period = await reporting_periods_repository.get_active_period()
|
||||
|
||||
if active_period:
|
||||
start_date = active_period.start_date
|
||||
next_day = start_date + timedelta(days=1)
|
||||
reporting_month = MONTH_NAMES[next_day.month]
|
||||
reporting_year = next_day.year
|
||||
|
||||
days_passed = (datetime.now().date() - start_date).days
|
||||
|
||||
content = f"""
|
||||
<blockquote>📅 <b>Отчётный период</b></blockquote>
|
||||
|
||||
<b>Статус:</b> 🟢 Активен
|
||||
|
||||
<b>Отчётный месяц:</b> <code>{reporting_month} {reporting_year}</code>
|
||||
<b>Дата начала:</b> <code>{start_date.strftime('%d.%m.%Y')}</code>
|
||||
<b>Прошло дней:</b> <code>{days_passed}</code>
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Используйте кнопки ниже для управления периодом.
|
||||
"""
|
||||
has_active = True
|
||||
else:
|
||||
content = """
|
||||
<blockquote>📅 <b>Отчётный период</b></blockquote>
|
||||
|
||||
<b>Статус:</b> ⚪️ Нет активного периода
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Создайте новый отчётный период, чтобы начать учёт дежурств.
|
||||
"""
|
||||
has_active = False
|
||||
|
||||
return {
|
||||
"content": content,
|
||||
"has_active": has_active,
|
||||
}
|
||||
|
||||
|
||||
async def on_next_period_click(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.next_period_confirm)
|
||||
|
||||
|
||||
async def on_make_report_click(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.generate_report_select_period)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_next_period_confirm_data(
|
||||
reporting_periods_repository: FromDishka[ReportingPeriodsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
active_period = await reporting_periods_repository.get_active_period()
|
||||
|
||||
if active_period:
|
||||
start_date = active_period.start_date
|
||||
next_day = start_date + timedelta(days=1)
|
||||
reporting_month = MONTH_NAMES[next_day.month]
|
||||
reporting_year = next_day.year
|
||||
|
||||
content = f"""
|
||||
<blockquote>⚠️ <b>Подтверждение</b></blockquote>
|
||||
|
||||
Вы уверены, что хотите начать следующий отчётный период?
|
||||
|
||||
<b>Текущий период:</b>
|
||||
• Месяц: <code>{reporting_month} {reporting_year}</code>
|
||||
• Начало: <code>{start_date.strftime('%d.%m.%Y')}</code>
|
||||
|
||||
Текущий период будет закрыт с датой окончания <code>{datetime.now().date().strftime('%d.%m.%Y')}</code>, и будет создан новый период.
|
||||
"""
|
||||
else:
|
||||
content = f"""
|
||||
<blockquote>⚠️ <b>Подтверждение</b></blockquote>
|
||||
|
||||
Вы уверены, что хотите создать новый отчётный период?
|
||||
|
||||
Период начнётся с <code>{datetime.now().date().strftime('%d.%m.%Y')}</code>.
|
||||
"""
|
||||
|
||||
return {"content": content}
|
||||
|
||||
|
||||
@inject
|
||||
async def on_next_period_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
reporting_periods_repository: FromDishka[ReportingPeriodsRepository],
|
||||
):
|
||||
active_period = await reporting_periods_repository.get_active_period()
|
||||
current_date = datetime.now().date()
|
||||
|
||||
if active_period:
|
||||
await reporting_periods_repository.close_period(active_period.id, current_date)
|
||||
|
||||
await reporting_periods_repository.create_period(current_date)
|
||||
|
||||
await callback.answer("✅ Новый отчётный период создан!")
|
||||
await dialog_manager.switch_to(AdminMenuSG.reporting_period)
|
||||
|
||||
|
||||
async def on_next_period_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.reporting_period)
|
||||
|
||||
|
||||
reporting_period_window = Window(
|
||||
Format("{content}"),
|
||||
Button(
|
||||
Const("➡️ Следующий период"),
|
||||
id="next_period_btn",
|
||||
on_click=on_next_period_click,
|
||||
),
|
||||
Button(
|
||||
Const("📊 Сделать отчёт"),
|
||||
id="make_report_btn",
|
||||
on_click=on_make_report_click,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_admin_menu_from_period",
|
||||
state=AdminMenuSG.main,
|
||||
),
|
||||
state=AdminMenuSG.reporting_period,
|
||||
getter=get_reporting_period_data,
|
||||
)
|
||||
|
||||
next_period_confirm_window = Window(
|
||||
Format("{content}"),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да"),
|
||||
id="confirm_next_period",
|
||||
on_click=on_next_period_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Нет"),
|
||||
id="cancel_next_period",
|
||||
on_click=on_next_period_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.next_period_confirm,
|
||||
getter=get_next_period_confirm_data,
|
||||
)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_generate_report_data(
|
||||
reporting_periods_repository: FromDishka[ReportingPeriodsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
all_periods = await reporting_periods_repository.get_all()
|
||||
completed_periods = [p for p in all_periods if p.end_date is not None]
|
||||
active_period = await reporting_periods_repository.get_active_period()
|
||||
|
||||
if not completed_periods and not active_period:
|
||||
content = """
|
||||
<blockquote>📊 <b>Генерация отчёта</b></blockquote>
|
||||
|
||||
⚠️ Нет отчётных периодов.
|
||||
|
||||
Создайте отчётный период, чтобы сгенерировать отчёт.
|
||||
"""
|
||||
has_periods = False
|
||||
periods_list = []
|
||||
elif not completed_periods and active_period:
|
||||
content = """
|
||||
<blockquote>📊 <b>Генерация отчёта</b></blockquote>
|
||||
|
||||
Будет сгенерирован отчёт по активному периоду.
|
||||
"""
|
||||
has_periods = True
|
||||
next_day = active_period.start_date + timedelta(days=1)
|
||||
month_name = MONTH_NAMES[next_day.month]
|
||||
year = next_day.year
|
||||
periods_list = [(
|
||||
f"{month_name} {year} (активный, с {active_period.start_date.strftime('%d.%m.%Y')})",
|
||||
active_period.id
|
||||
)]
|
||||
else:
|
||||
content = """
|
||||
<blockquote>📊 <b>Генерация отчёта</b></blockquote>
|
||||
|
||||
Выберите период для генерации отчёта:
|
||||
"""
|
||||
has_periods = True
|
||||
periods_list = []
|
||||
for period in sorted(completed_periods, key=lambda p: p.start_date, reverse=True):
|
||||
if period.end_date:
|
||||
next_day = period.start_date + timedelta(days=1)
|
||||
month_name = MONTH_NAMES[next_day.month]
|
||||
year = next_day.year
|
||||
periods_list.append((
|
||||
f"{month_name} {year} ({period.start_date.strftime('%d.%m.%Y')} - {period.end_date.strftime('%d.%m.%Y')})",
|
||||
period.id
|
||||
))
|
||||
|
||||
return {
|
||||
"content": content,
|
||||
"has_periods": has_periods,
|
||||
"periods": periods_list,
|
||||
}
|
||||
|
||||
|
||||
async def on_period_selected(
|
||||
callback: CallbackQuery,
|
||||
widget,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["selected_period_id"] = int(item_id)
|
||||
await dialog_manager.switch_to(AdminMenuSG.generate_report_confirm)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_generate_report_confirm_data(
|
||||
reporting_periods_repository: FromDishka[ReportingPeriodsRepository],
|
||||
dialog_manager: DialogManager,
|
||||
**kwargs,
|
||||
):
|
||||
period_id = dialog_manager.dialog_data.get("selected_period_id")
|
||||
if not period_id:
|
||||
return {"content": "⚠️ Период не выбран"}
|
||||
|
||||
period = await reporting_periods_repository.get_by_id(int(period_id))
|
||||
|
||||
if period:
|
||||
next_day = period.start_date + timedelta(days=1)
|
||||
month_name = MONTH_NAMES[next_day.month]
|
||||
year = next_day.year
|
||||
|
||||
if period.end_date:
|
||||
content = f"""
|
||||
<blockquote>📊 <b>Подтверждение генерации</b></blockquote>
|
||||
|
||||
<b>Период:</b> <code>{month_name} {year}</code>
|
||||
<b>Даты:</b> <code>{period.start_date.strftime('%d.%m.%Y')} - {period.end_date.strftime('%d.%m.%Y')}</code>
|
||||
|
||||
Сгенерировать отчёт по начислениям часов за этот период?
|
||||
"""
|
||||
else:
|
||||
content = f"""
|
||||
<blockquote>📊 <b>Подтверждение генерации</b></blockquote>
|
||||
|
||||
<b>Период:</b> <code>{month_name} {year} (активный)</code>
|
||||
<b>Начало:</b> <code>{period.start_date.strftime('%d.%m.%Y')}</code>
|
||||
|
||||
Сгенерировать отчёт по начислениям часов за активный период (до текущей даты)?
|
||||
"""
|
||||
else:
|
||||
content = "⚠️ Период не найден"
|
||||
|
||||
return {"content": content}
|
||||
|
||||
|
||||
@inject
|
||||
async def on_generate_report_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
reporting_periods_repository: FromDishka[ReportingPeriodsRepository],
|
||||
report_service: FromDishka[ReportService],
|
||||
):
|
||||
period_id = dialog_manager.dialog_data.get("selected_period_id")
|
||||
if not period_id:
|
||||
await callback.answer("⚠️ Период не выбран", show_alert=True)
|
||||
return
|
||||
|
||||
period = await reporting_periods_repository.get_by_id(int(period_id))
|
||||
|
||||
if not period:
|
||||
await callback.answer("⚠️ Период не найден", show_alert=True)
|
||||
return
|
||||
|
||||
await callback.answer("⏳ Генерирую отчёт...")
|
||||
|
||||
end_date = period.end_date if period.end_date else msk_now()
|
||||
print(end_date)
|
||||
|
||||
report_file = await report_service.generate_period_report(
|
||||
period.start_date, end_date
|
||||
)
|
||||
|
||||
next_day = period.start_date + timedelta(days=1)
|
||||
month_name = MONTH_NAMES[next_day.month]
|
||||
year = next_day.year
|
||||
filename = f"Отчёт_{month_name}_{year}.xlsx"
|
||||
|
||||
document = BufferedInputFile(report_file.read(), filename=filename)
|
||||
if callback.message:
|
||||
await callback.message.answer_document(
|
||||
document=document,
|
||||
caption=f"📊 Отчёт по начислениям часов за {month_name} {year}"
|
||||
)
|
||||
|
||||
await callback.answer("✅ Отчёт сгенерирован!")
|
||||
await dialog_manager.switch_to(AdminMenuSG.reporting_period)
|
||||
|
||||
|
||||
async def on_generate_report_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.generate_report_select_period)
|
||||
|
||||
|
||||
generate_report_select_period_window = Window(
|
||||
Format("{content}"),
|
||||
ScrollingGroup(
|
||||
Select(
|
||||
Format("{item[0]}"),
|
||||
id="period_select",
|
||||
item_id_getter=lambda x: x[1],
|
||||
items="periods",
|
||||
on_click=on_period_selected,
|
||||
),
|
||||
id="periods_scroll",
|
||||
width=1,
|
||||
height=5,
|
||||
when="has_periods",
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_reporting_period",
|
||||
state=AdminMenuSG.reporting_period,
|
||||
),
|
||||
state=AdminMenuSG.generate_report_select_period,
|
||||
getter=get_generate_report_data,
|
||||
)
|
||||
|
||||
generate_report_confirm_window = Window(
|
||||
Format("{content}"),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да"),
|
||||
id="confirm_generate_report",
|
||||
on_click=on_generate_report_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Нет"),
|
||||
id="cancel_generate_report",
|
||||
on_click=on_generate_report_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.generate_report_confirm,
|
||||
getter=get_generate_report_confirm_data,
|
||||
)
|
||||
@@ -1,233 +0,0 @@
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from aiogram_dialog import Window, DialogManager
|
||||
from aiogram_dialog.widgets.text import Format, Const
|
||||
from aiogram_dialog.widgets.kbd import SwitchTo, Button, ScrollingGroup, Select, Group
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
|
||||
from dutylog.infrastructure.database.repositories.residents_repository import (
|
||||
ResidentsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.database.repositories.rooms_repository import (
|
||||
RoomsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.database.repositories.floors_repository import (
|
||||
FloorsRepository,
|
||||
)
|
||||
|
||||
|
||||
async def on_filter_residents(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents_filter_select)
|
||||
|
||||
|
||||
async def get_filter_options_data(**kwargs):
|
||||
filter_options = [
|
||||
("busy", "🟢 Только занятые"),
|
||||
("free", "⚪️ Только свободные"),
|
||||
("hours_more", "🔴 Неотработанных часов > X"),
|
||||
("hours_less", "🔴 Неотработанных часов < X"),
|
||||
("worked_more", "🟢 Отработанных часов > X"),
|
||||
("worked_less", "🟢 Отработанных часов < X"),
|
||||
]
|
||||
return {"filter_options": filter_options}
|
||||
|
||||
|
||||
async def on_filter_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["filter_type"] = item_id
|
||||
|
||||
if item_id in ["hours_more", "hours_less", "worked_more", "worked_less"]:
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents_filter_hours_input)
|
||||
else:
|
||||
dialog_manager.dialog_data["filter_hours"] = None
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents_filtered_results)
|
||||
|
||||
|
||||
async def on_filter_hours_input(
|
||||
message: Message,
|
||||
widget: MessageInput,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
if not message.text:
|
||||
await message.answer("⚠️ Пожалуйста, введите число")
|
||||
return
|
||||
|
||||
try:
|
||||
hours = int(message.text)
|
||||
if hours < 0:
|
||||
await message.answer("⚠️ Количество часов не может быть отрицательным")
|
||||
return
|
||||
|
||||
dialog_manager.dialog_data["filter_hours"] = hours
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents_filtered_results)
|
||||
except ValueError:
|
||||
await message.answer("⚠️ Пожалуйста, введите корректное число")
|
||||
|
||||
|
||||
@inject
|
||||
async def get_filtered_results_data(
|
||||
dialog_manager: DialogManager,
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
rooms_repository: FromDishka[RoomsRepository],
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
filter_type: str = dialog_manager.dialog_data["filter_type"]
|
||||
filter_hours: int = dialog_manager.dialog_data["filter_hours"]
|
||||
|
||||
all_residents = await residents_repository.get_all_residents()
|
||||
|
||||
if filter_type == "busy":
|
||||
filtered = [r for r in all_residents if r.is_busy]
|
||||
filter_description = "занятым резидентам"
|
||||
elif filter_type == "free":
|
||||
filtered = [r for r in all_residents if not r.is_busy]
|
||||
filter_description = "свободным резидентам"
|
||||
elif filter_type == "hours_more":
|
||||
filtered = [r for r in all_residents if r.active_hours > filter_hours]
|
||||
filter_description = f"неотработанным часам > {filter_hours}"
|
||||
elif filter_type == "hours_less":
|
||||
filtered = [r for r in all_residents if r.active_hours < filter_hours]
|
||||
filter_description = f"неотработанным часам < {filter_hours}"
|
||||
elif filter_type == "worked_more":
|
||||
filtered = [r for r in all_residents if r.inactive_hours > filter_hours]
|
||||
filter_description = f"отработанным часам > {filter_hours}"
|
||||
elif filter_type == "worked_less":
|
||||
filtered = [r for r in all_residents if r.inactive_hours < filter_hours]
|
||||
filter_description = f"отработанным часам < {filter_hours}"
|
||||
else:
|
||||
filtered = all_residents
|
||||
filter_description = "всем резидентам"
|
||||
|
||||
if not filtered:
|
||||
return {
|
||||
"content": f"""
|
||||
<blockquote>🔽 <b>Результаты фильтрации</b></blockquote>
|
||||
|
||||
<b>Фильтр:</b> {filter_description}
|
||||
|
||||
❌ Ничего не найдено
|
||||
""",
|
||||
"residents": [],
|
||||
"has_results": False,
|
||||
}
|
||||
|
||||
residents_with_rooms = []
|
||||
for resident in filtered:
|
||||
room = await rooms_repository.get_room_by_id(resident.room)
|
||||
if room:
|
||||
floor = await floors_repository.get_floor_by_id(room.on_floor)
|
||||
floor_number = floor.number if floor else 999999
|
||||
room_number = room.number
|
||||
else:
|
||||
floor_number = 999999
|
||||
room_number = 999999
|
||||
residents_with_rooms.append((resident, floor_number, room_number))
|
||||
|
||||
residents_with_rooms.sort(key=lambda x: (x[1], x[2]))
|
||||
|
||||
residents_data = []
|
||||
for resident, floor_number, room_number in residents_with_rooms:
|
||||
status = "🟢" if resident.is_busy else "⚪️"
|
||||
name = resident.real_name if resident.real_name else "Без имени"
|
||||
|
||||
residents_data.append(
|
||||
(f"{name} | Комната {room_number} | {status}", resident.id)
|
||||
)
|
||||
|
||||
content = f"""
|
||||
<blockquote>🔽 <b>Результаты фильтрации по {filter_description}</b></blockquote>
|
||||
|
||||
<b>Найдено резидентов:</b> <code>{len(filtered)}</code>
|
||||
|
||||
Выберите резидента для просмотра информации:
|
||||
"""
|
||||
|
||||
return {
|
||||
"content": content,
|
||||
"residents": residents_data,
|
||||
"has_results": True,
|
||||
}
|
||||
|
||||
|
||||
async def on_filtered_resident_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["selected_resident_id"] = int(item_id)
|
||||
dialog_manager.dialog_data["from_filter"] = True
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
filter_select_window = Window(
|
||||
Const("<blockquote>🔽 <b>Фильтрация резидентов</b></blockquote>\n\n<blockquote>Выберите критерий фильтрации для отображения нужных резидентов.</blockquote>"),
|
||||
Group(
|
||||
Select(
|
||||
Format("{item[1]}"),
|
||||
id="filter_select",
|
||||
item_id_getter=lambda x: x[0],
|
||||
items="filter_options",
|
||||
on_click=on_filter_selected,
|
||||
),
|
||||
width=1,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Отмена"),
|
||||
id="cancel_filter",
|
||||
state=AdminMenuSG.residents,
|
||||
),
|
||||
state=AdminMenuSG.residents_filter_select,
|
||||
getter=get_filter_options_data,
|
||||
)
|
||||
|
||||
filter_hours_input_window = Window(
|
||||
Const("<blockquote>🔽 <b>Фильтрация по часам</b></blockquote>\n\n<blockquote>Введите количество часов для фильтрации.</blockquote>"),
|
||||
MessageInput(on_filter_hours_input),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_filter_select",
|
||||
state=AdminMenuSG.residents_filter_select,
|
||||
),
|
||||
state=AdminMenuSG.residents_filter_hours_input,
|
||||
)
|
||||
|
||||
filtered_results_window = Window(
|
||||
Format("{content}"),
|
||||
ScrollingGroup(
|
||||
Select(
|
||||
Format("{item[0]}"),
|
||||
id="filtered_residents_select",
|
||||
item_id_getter=lambda x: x[1],
|
||||
items="residents",
|
||||
on_click=on_filtered_resident_selected,
|
||||
),
|
||||
id="filtered_residents_scroll",
|
||||
width=1,
|
||||
height=7,
|
||||
when="has_results",
|
||||
),
|
||||
SwitchTo(
|
||||
Const("🔽 Новый фильтр"),
|
||||
id="new_filter",
|
||||
state=AdminMenuSG.residents_filter_select,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ К списку резидентов"),
|
||||
id="back_to_residents_from_filter",
|
||||
state=AdminMenuSG.residents,
|
||||
),
|
||||
state=AdminMenuSG.residents_filtered_results,
|
||||
getter=get_filtered_results_data,
|
||||
)
|
||||
@@ -1,645 +0,0 @@
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from aiogram_dialog import Window, DialogManager
|
||||
from aiogram_dialog.widgets.text import Format, Const
|
||||
from aiogram_dialog.widgets.kbd import Row, SwitchTo, Button, ScrollingGroup, Select, Group
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from magic_filter import F
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
|
||||
from dutylog.infrastructure.database.repositories.residents_repository import (
|
||||
ResidentsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.database.repositories.rooms_repository import (
|
||||
RoomsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.database.repositories.floors_repository import (
|
||||
FloorsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.database.repositories.users_repository import (
|
||||
UsersRepository,
|
||||
)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_residents_list_data(
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
rooms_repository: FromDishka[RoomsRepository],
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
all_residents = await residents_repository.get_all_residents()
|
||||
|
||||
residents_with_rooms = []
|
||||
for resident in all_residents:
|
||||
room = await rooms_repository.get_room_by_id(resident.room)
|
||||
if room:
|
||||
floor = await floors_repository.get_floor_by_id(room.on_floor)
|
||||
floor_number = floor.number if floor else 999999
|
||||
room_number = room.number
|
||||
else:
|
||||
floor_number = 999999
|
||||
room_number = 999999
|
||||
residents_with_rooms.append((resident, floor_number, room_number))
|
||||
|
||||
residents_with_rooms.sort(key=lambda x: (x[1], x[2]))
|
||||
|
||||
residents_data = []
|
||||
for resident, floor_number, room_number in residents_with_rooms:
|
||||
status = "🟢" if resident.is_busy else "⚪️"
|
||||
name = resident.real_name if resident.real_name else "Без имени"
|
||||
|
||||
residents_data.append(
|
||||
(f"{name} | Комната {room_number} | {status}", resident.id)
|
||||
)
|
||||
|
||||
content = f"""
|
||||
<blockquote>🏠 <b>Резиденты</b></blockquote>
|
||||
|
||||
<b>Всего резидентов:</b> <code>{len(all_residents)}</code>
|
||||
|
||||
Выберите резидента для просмотра информации:
|
||||
"""
|
||||
|
||||
return {
|
||||
"content": content,
|
||||
"residents": residents_data,
|
||||
}
|
||||
|
||||
|
||||
@inject
|
||||
async def get_resident_info_data(
|
||||
dialog_manager: DialogManager,
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
rooms_repository: FromDishka[RoomsRepository],
|
||||
users_repository: FromDishka[UsersRepository],
|
||||
**kwargs,
|
||||
):
|
||||
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
||||
|
||||
if not resident_id:
|
||||
return {"info_content": "Ошибка: резидент не выбран", "is_busy": False, "from_search": False, "from_filter": False}
|
||||
|
||||
resident = await residents_repository.get_resident_by_id(resident_id)
|
||||
|
||||
if not resident:
|
||||
return {"info_content": "Ошибка: резидент не найден", "is_busy": False, "from_search": False, "from_filter": False}
|
||||
|
||||
room = await rooms_repository.get_room_by_id(resident.room)
|
||||
room_number = room.number if room else "???"
|
||||
|
||||
name = resident.real_name if resident.real_name else "Без имени"
|
||||
status = "🟢 Занят" if resident.is_busy else "⚪️ Свободен"
|
||||
|
||||
user_info = "Не привязан"
|
||||
if resident.user_entity:
|
||||
user = await users_repository.get_user_by_id(resident.user_entity)
|
||||
if user:
|
||||
if user.username:
|
||||
username = f"@{user.username}"
|
||||
else:
|
||||
username = f"ID: {user.id}"
|
||||
user_info = f"{user.first_name} ({username})"
|
||||
|
||||
info_content = f"""
|
||||
<blockquote>👤 <b>Информация о резиденте</b></blockquote>
|
||||
|
||||
<b>ID:</b> <code>{resident.id}</code>
|
||||
<b>Имя:</b> {name}
|
||||
<b>Комната:</b> <code>{room_number}</code>
|
||||
<b>Статус:</b> {status}
|
||||
<b>Пользователь:</b> {user_info}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🟢 <b>Отработанные часы:</b> <code>{resident.inactive_hours}</code> ч
|
||||
🔴 <b>Неотработанные часы:</b> <code>{resident.active_hours}</code> ч
|
||||
"""
|
||||
|
||||
from_search = dialog_manager.dialog_data.get("from_search", False)
|
||||
from_filter = dialog_manager.dialog_data.get("from_filter", False)
|
||||
|
||||
return {
|
||||
"info_content": info_content,
|
||||
"is_busy": resident.is_busy,
|
||||
"from_search": from_search,
|
||||
"from_filter": from_filter,
|
||||
}
|
||||
|
||||
|
||||
async def on_resident_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["selected_resident_id"] = int(item_id)
|
||||
dialog_manager.dialog_data["from_search"] = False
|
||||
dialog_manager.dialog_data["from_filter"] = False
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
async def on_add_resident(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.create_resident_name)
|
||||
|
||||
|
||||
async def on_filter_residents(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents_filter_select)
|
||||
|
||||
|
||||
async def on_search_residents(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents_search_input)
|
||||
|
||||
|
||||
@inject
|
||||
async def on_logout_resident_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
):
|
||||
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
||||
|
||||
if resident_id:
|
||||
await residents_repository.unbind_user_from_resident(resident_id)
|
||||
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
async def on_logout_resident_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
@inject
|
||||
async def on_delete_resident_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
):
|
||||
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
||||
|
||||
if resident_id:
|
||||
await residents_repository.delete_resident(resident_id)
|
||||
await callback.answer("✅ Резидент удален!")
|
||||
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents)
|
||||
|
||||
|
||||
async def on_delete_resident_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
async def on_resident_name_input(
|
||||
message: Message,
|
||||
widget: MessageInput,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
if not message.text or len(message.text.strip()) < 2:
|
||||
await message.answer("⚠️ Пожалуйста, введите корректное имя и фамилию")
|
||||
return
|
||||
|
||||
dialog_manager.dialog_data["new_resident_name"] = message.text.strip()
|
||||
await dialog_manager.switch_to(AdminMenuSG.create_resident_floor)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_create_resident_floors_data(
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
all_floors = await floors_repository.get_all_floors()
|
||||
all_floors.sort(key=lambda f: f.number)
|
||||
|
||||
return {
|
||||
"floors": [(f.id, f"Этаж {f.number}") for f in all_floors],
|
||||
}
|
||||
|
||||
|
||||
async def on_create_resident_floor_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["new_resident_floor_id"] = int(item_id)
|
||||
await dialog_manager.switch_to(AdminMenuSG.create_resident_room)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_create_resident_rooms_data(
|
||||
dialog_manager: DialogManager,
|
||||
rooms_repository: FromDishka[RoomsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
floor_id = dialog_manager.dialog_data.get("new_resident_floor_id")
|
||||
|
||||
if not floor_id:
|
||||
return {"rooms": []}
|
||||
|
||||
rooms = await rooms_repository.get_rooms_by_floor(floor_id)
|
||||
rooms.sort(key=lambda r: r.number)
|
||||
|
||||
return {
|
||||
"rooms": [(r.id, str(r.number)) for r in rooms],
|
||||
}
|
||||
|
||||
|
||||
async def on_create_resident_room_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["new_resident_room_id"] = int(item_id)
|
||||
await dialog_manager.switch_to(AdminMenuSG.create_resident_confirm)
|
||||
|
||||
|
||||
async def get_create_resident_confirm_data(
|
||||
dialog_manager: DialogManager,
|
||||
**kwargs,
|
||||
):
|
||||
name = dialog_manager.dialog_data.get("new_resident_name", "???")
|
||||
return {"resident_name": name}
|
||||
|
||||
|
||||
@inject
|
||||
async def on_create_resident_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
):
|
||||
name = dialog_manager.dialog_data.get("new_resident_name")
|
||||
room_id = dialog_manager.dialog_data.get("new_resident_room_id")
|
||||
|
||||
if name and room_id:
|
||||
await residents_repository.create_resident(
|
||||
room_id=room_id,
|
||||
real_name=name,
|
||||
)
|
||||
await callback.answer("✅ Резидент создан!")
|
||||
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents)
|
||||
|
||||
|
||||
async def on_create_resident_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents)
|
||||
|
||||
|
||||
async def on_search_input(
|
||||
message: Message,
|
||||
widget: MessageInput,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
if not message.text or len(message.text.strip()) < 1:
|
||||
await message.answer("⚠️ Пожалуйста, введите поисковый запрос")
|
||||
return
|
||||
|
||||
query = message.text.strip()
|
||||
if query.startswith("@"):
|
||||
query = query[1:]
|
||||
|
||||
dialog_manager.dialog_data["search_query"] = query
|
||||
dialog_manager.dialog_data["is_search_active"] = True
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents_search_results)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_search_results_data(
|
||||
dialog_manager: DialogManager,
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
rooms_repository: FromDishka[RoomsRepository],
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
users_repository: FromDishka[UsersRepository],
|
||||
**kwargs,
|
||||
):
|
||||
query = dialog_manager.dialog_data.get("search_query", "")
|
||||
|
||||
residents, search_type = await residents_repository.search_residents(
|
||||
query, users_repository
|
||||
)
|
||||
|
||||
if not residents:
|
||||
return {
|
||||
"content": f"""
|
||||
<blockquote>🔍 <b>Результаты поиска</b></blockquote>
|
||||
|
||||
<b>Запрос:</b> <code>{query}</code>
|
||||
|
||||
❌ Ничего не найдено
|
||||
""",
|
||||
"residents": [],
|
||||
"has_results": False,
|
||||
}
|
||||
|
||||
residents_with_rooms = []
|
||||
for resident in residents:
|
||||
room = await rooms_repository.get_room_by_id(resident.room)
|
||||
if room:
|
||||
floor = await floors_repository.get_floor_by_id(room.on_floor)
|
||||
floor_number = floor.number if floor else 999999
|
||||
room_number = room.number
|
||||
else:
|
||||
floor_number = 999999
|
||||
room_number = 999999
|
||||
residents_with_rooms.append((resident, floor_number, room_number))
|
||||
|
||||
residents_with_rooms.sort(key=lambda x: (x[1], x[2]))
|
||||
|
||||
residents_data = []
|
||||
for resident, floor_number, room_number in residents_with_rooms:
|
||||
status = "🟢" if resident.is_busy else "⚪️"
|
||||
name = resident.real_name if resident.real_name else "Без имени"
|
||||
|
||||
residents_data.append(
|
||||
(f"{name} | Комната {room_number} | {status}", resident.id)
|
||||
)
|
||||
|
||||
content = f"""
|
||||
<blockquote>🔍 <b>Результаты поиска по {search_type}</b></blockquote>
|
||||
|
||||
<b>Найдено резидентов:</b> <code>{len(residents)}</code>
|
||||
|
||||
Выберите резидента для просмотра информации:
|
||||
"""
|
||||
|
||||
return {
|
||||
"content": content,
|
||||
"residents": residents_data,
|
||||
"has_results": True,
|
||||
}
|
||||
|
||||
|
||||
async def on_search_resident_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["selected_resident_id"] = int(item_id)
|
||||
dialog_manager.dialog_data["from_search"] = True
|
||||
dialog_manager.dialog_data["from_filter"] = False
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
residents_list_window = Window(
|
||||
Format("{content}"),
|
||||
Row(
|
||||
Button(
|
||||
Const("🔍 Поиск"),
|
||||
id="search_residents_btn",
|
||||
on_click=on_search_residents,
|
||||
),
|
||||
Button(
|
||||
Const("🔽 Фильтр"),
|
||||
id="filter_residents_btn",
|
||||
on_click=on_filter_residents,
|
||||
),
|
||||
),
|
||||
ScrollingGroup(
|
||||
Select(
|
||||
Format("{item[0]}"),
|
||||
id="residents_select",
|
||||
item_id_getter=lambda x: x[1],
|
||||
items="residents",
|
||||
on_click=on_resident_selected,
|
||||
),
|
||||
id="residents_scroll",
|
||||
width=1,
|
||||
height=7,
|
||||
),
|
||||
Button(
|
||||
Const("➕ Добавить резидента"),
|
||||
id="add_resident_btn",
|
||||
on_click=on_add_resident,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_admin_menu",
|
||||
state=AdminMenuSG.main,
|
||||
),
|
||||
state=AdminMenuSG.residents,
|
||||
getter=get_residents_list_data,
|
||||
)
|
||||
|
||||
resident_info_window = Window(
|
||||
Format("{info_content}"),
|
||||
Row(
|
||||
Button(
|
||||
Const("Добавить часы"),
|
||||
id="add_hours_btn",
|
||||
on_click=lambda c, b, m: m.switch_to(AdminMenuSG.add_hours_select),
|
||||
),
|
||||
Button(
|
||||
Const("Отнять часы"),
|
||||
id="remove_hours_btn",
|
||||
on_click=lambda c, b, m: m.switch_to(AdminMenuSG.remove_hours_select),
|
||||
),
|
||||
),
|
||||
Button(
|
||||
Const("🚪 Разлогинить"),
|
||||
id="logout_resident_btn",
|
||||
on_click=lambda c, b, m: m.switch_to(AdminMenuSG.resident_logout_confirm),
|
||||
when="is_busy",
|
||||
),
|
||||
Button(
|
||||
Const("🗑 Удалить резидента"),
|
||||
id="delete_resident_btn",
|
||||
on_click=lambda c, b, m: m.switch_to(AdminMenuSG.resident_delete_confirm),
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад к результатам поиска"),
|
||||
id="back_to_search_results",
|
||||
state=AdminMenuSG.residents_search_results,
|
||||
when="from_search",
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад к результатам фильтра"),
|
||||
id="back_to_filter_results",
|
||||
state=AdminMenuSG.residents_filtered_results,
|
||||
when="from_filter",
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад к списку"),
|
||||
id="back_to_residents_list",
|
||||
state=AdminMenuSG.residents,
|
||||
when=~F["from_search"] & ~F["from_filter"],
|
||||
),
|
||||
state=AdminMenuSG.resident_info,
|
||||
getter=get_resident_info_data,
|
||||
)
|
||||
|
||||
resident_logout_confirm_window = Window(
|
||||
Const(
|
||||
"<blockquote>⚠️ <b>Подтверждение</b></blockquote>\n\nВы уверены, что хотите разлогинить этого резидента?"
|
||||
),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да"),
|
||||
id="confirm_logout",
|
||||
on_click=on_logout_resident_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Нет"),
|
||||
id="cancel_logout",
|
||||
on_click=on_logout_resident_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.resident_logout_confirm,
|
||||
)
|
||||
|
||||
create_resident_name_window = Window(
|
||||
Const("<blockquote>➕ <b>Создание резидента</b></blockquote>\n\nВведите имя и фамилию резидента:"),
|
||||
MessageInput(on_resident_name_input),
|
||||
SwitchTo(
|
||||
Const("◀️ Отмена"),
|
||||
id="cancel_create_resident_name",
|
||||
state=AdminMenuSG.residents,
|
||||
),
|
||||
state=AdminMenuSG.create_resident_name,
|
||||
)
|
||||
|
||||
create_resident_floor_window = Window(
|
||||
Const("<blockquote>🏢 <b>Выбор этажа</b></blockquote>\n\nВыберите этаж для нового резидента:"),
|
||||
Group(
|
||||
Select(
|
||||
Format("{item[1]}"),
|
||||
id="create_resident_floor_select",
|
||||
item_id_getter=lambda x: x[0],
|
||||
items="floors",
|
||||
on_click=on_create_resident_floor_selected,
|
||||
),
|
||||
width=2,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_create_name",
|
||||
state=AdminMenuSG.create_resident_name,
|
||||
),
|
||||
state=AdminMenuSG.create_resident_floor,
|
||||
getter=get_create_resident_floors_data,
|
||||
)
|
||||
|
||||
create_resident_room_window = Window(
|
||||
Const("<blockquote>🚪 <b>Выбор комнаты</b></blockquote>\n\nВыберите комнату для нового резидента:"),
|
||||
Group(
|
||||
Select(
|
||||
Format("{item[1]}"),
|
||||
id="create_resident_room_select",
|
||||
item_id_getter=lambda x: x[0],
|
||||
items="rooms",
|
||||
on_click=on_create_resident_room_selected,
|
||||
),
|
||||
width=3,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_create_floor",
|
||||
state=AdminMenuSG.create_resident_floor,
|
||||
),
|
||||
state=AdminMenuSG.create_resident_room,
|
||||
getter=get_create_resident_rooms_data,
|
||||
)
|
||||
|
||||
create_resident_confirm_window = Window(
|
||||
Format("<blockquote>✅ <b>Подтверждение</b></blockquote>\n\nСоздать резидента <b>{resident_name}</b>?"),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да"),
|
||||
id="confirm_create_resident",
|
||||
on_click=on_create_resident_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Нет"),
|
||||
id="cancel_create_resident",
|
||||
on_click=on_create_resident_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.create_resident_confirm,
|
||||
getter=get_create_resident_confirm_data,
|
||||
)
|
||||
|
||||
search_input_window = Window(
|
||||
Const("<blockquote>🔍 <b>Поиск резидентов</b></blockquote>\n\n<blockquote>Введите номер комнаты, имя резидента или username пользователя. Можно указать только часть имени или username для поиска.</blockquote>"),
|
||||
MessageInput(on_search_input),
|
||||
SwitchTo(
|
||||
Const("◀️ Отмена"),
|
||||
id="cancel_search",
|
||||
state=AdminMenuSG.residents,
|
||||
),
|
||||
state=AdminMenuSG.residents_search_input,
|
||||
)
|
||||
|
||||
search_results_window = Window(
|
||||
Format("{content}"),
|
||||
ScrollingGroup(
|
||||
Select(
|
||||
Format("{item[0]}"),
|
||||
id="search_residents_select",
|
||||
item_id_getter=lambda x: x[1],
|
||||
items="residents",
|
||||
on_click=on_search_resident_selected,
|
||||
),
|
||||
id="search_residents_scroll",
|
||||
width=1,
|
||||
height=7,
|
||||
when="has_results",
|
||||
),
|
||||
SwitchTo(
|
||||
Const("🔍 Новый поиск"),
|
||||
id="new_search",
|
||||
state=AdminMenuSG.residents_search_input,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ К списку резидентов"),
|
||||
id="back_to_residents_from_search",
|
||||
state=AdminMenuSG.residents,
|
||||
),
|
||||
state=AdminMenuSG.residents_search_results,
|
||||
getter=get_search_results_data,
|
||||
)
|
||||
|
||||
resident_delete_confirm_window = Window(
|
||||
Const(
|
||||
"<blockquote>⚠️ <b>Подтверждение удаления</b></blockquote>\n\nВы уверены, что хотите удалить этого резидента? Это действие необратимо!"
|
||||
),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да, удалить"),
|
||||
id="confirm_delete",
|
||||
on_click=on_delete_resident_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Отмена"),
|
||||
id="cancel_delete",
|
||||
on_click=on_delete_resident_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.resident_delete_confirm,
|
||||
)
|
||||
@@ -1,366 +0,0 @@
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from aiogram_dialog import Window, DialogManager
|
||||
from aiogram_dialog.widgets.text import Format, Const
|
||||
from aiogram_dialog.widgets.kbd import SwitchTo, Button, ScrollingGroup, Select, Row, Group
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
|
||||
from dutylog.infrastructure.database.repositories.rooms_repository import (
|
||||
RoomsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.database.repositories.floors_repository import (
|
||||
FloorsRepository,
|
||||
)
|
||||
|
||||
|
||||
async def on_rooms_click(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.rooms_select_floor)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_rooms_floors_data(
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
all_floors = await floors_repository.get_all_floors()
|
||||
all_floors.sort(key=lambda f: f.number)
|
||||
|
||||
floors_data = [(f"🏢 Этаж {f.number}", f.id) for f in all_floors]
|
||||
|
||||
content = """
|
||||
<blockquote>🚪 <b>Комнаты</b></blockquote>
|
||||
|
||||
Выберите этаж для просмотра комнат:
|
||||
"""
|
||||
|
||||
return {
|
||||
"content": content,
|
||||
"floors": floors_data,
|
||||
}
|
||||
|
||||
|
||||
async def on_rooms_floor_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["selected_floor_id"] = int(item_id)
|
||||
await dialog_manager.switch_to(AdminMenuSG.rooms_list)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_rooms_list_data(
|
||||
dialog_manager: DialogManager,
|
||||
rooms_repository: FromDishka[RoomsRepository],
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
floor_id = dialog_manager.dialog_data.get("selected_floor_id")
|
||||
|
||||
if not floor_id:
|
||||
return {
|
||||
"content": "Ошибка: этаж не выбран",
|
||||
"rooms": [],
|
||||
"floor_number": "???",
|
||||
}
|
||||
|
||||
floor = await floors_repository.get_floor_by_id(floor_id)
|
||||
floor_number = floor.number if floor else "???"
|
||||
|
||||
rooms = await rooms_repository.get_rooms_by_floor(floor_id)
|
||||
rooms.sort(key=lambda r: r.number)
|
||||
|
||||
rooms_data = [(f"🚪 Комната {r.number}", r.id) for r in rooms]
|
||||
|
||||
content = f"""
|
||||
<blockquote>🚪 <b>Комнаты на этаже {floor_number}</b></blockquote>
|
||||
|
||||
<b>Всего комнат:</b> <code>{len(rooms)}</code>
|
||||
|
||||
Выберите комнату для удаления:
|
||||
"""
|
||||
|
||||
return {
|
||||
"content": content,
|
||||
"rooms": rooms_data,
|
||||
"floor_number": floor_number,
|
||||
}
|
||||
|
||||
|
||||
async def on_room_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["selected_room_id"] = int(item_id)
|
||||
await dialog_manager.switch_to(AdminMenuSG.room_delete_confirm)
|
||||
|
||||
|
||||
async def on_add_room_click(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.create_room_select_floor)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_create_room_floors_data(
|
||||
floors_repository: FromDishka[FloorsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
all_floors = await floors_repository.get_all_floors()
|
||||
all_floors.sort(key=lambda f: f.number)
|
||||
|
||||
floors_data = [(f"🏢 Этаж {f.number}", f.id) for f in all_floors]
|
||||
|
||||
content = """
|
||||
<blockquote>➕ <b>Создание комнаты</b></blockquote>
|
||||
|
||||
Выберите этаж для новой комнаты:
|
||||
"""
|
||||
|
||||
return {
|
||||
"content": content,
|
||||
"floors": floors_data,
|
||||
}
|
||||
|
||||
|
||||
async def on_create_room_floor_selected(
|
||||
callback: CallbackQuery,
|
||||
widget: Select,
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["new_room_floor_id"] = int(item_id)
|
||||
await dialog_manager.switch_to(AdminMenuSG.create_room_input)
|
||||
|
||||
|
||||
async def on_room_number_input(
|
||||
message: Message,
|
||||
widget: MessageInput,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
if not message.text:
|
||||
await message.answer("⚠️ Пожалуйста, введите номер комнаты")
|
||||
return
|
||||
|
||||
try:
|
||||
room_number = int(message.text)
|
||||
if room_number <= 0:
|
||||
await message.answer("⚠️ Номер комнаты должен быть положительным числом")
|
||||
return
|
||||
|
||||
dialog_manager.dialog_data["new_room_number"] = room_number
|
||||
await dialog_manager.switch_to(AdminMenuSG.create_room_confirm)
|
||||
except ValueError:
|
||||
await message.answer("⚠️ Пожалуйста, введите корректное число")
|
||||
|
||||
|
||||
async def get_create_room_confirm_data(
|
||||
dialog_manager: DialogManager,
|
||||
**kwargs,
|
||||
):
|
||||
room_number = dialog_manager.dialog_data.get("new_room_number", "???")
|
||||
return {"room_number": room_number}
|
||||
|
||||
|
||||
@inject
|
||||
async def on_create_room_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
rooms_repository: FromDishka[RoomsRepository],
|
||||
):
|
||||
room_number = dialog_manager.dialog_data.get("new_room_number")
|
||||
floor_id = dialog_manager.dialog_data.get("new_room_floor_id")
|
||||
|
||||
if room_number and floor_id:
|
||||
existing_room = await rooms_repository.get_room_by_number(room_number)
|
||||
if existing_room:
|
||||
await callback.answer(
|
||||
f"⚠️ Комната {room_number} уже существует!",
|
||||
show_alert=True
|
||||
)
|
||||
await dialog_manager.switch_to(AdminMenuSG.rooms_select_floor)
|
||||
return
|
||||
|
||||
await rooms_repository.create_room(room_number, floor_id)
|
||||
await callback.answer("✅ Комната создана!")
|
||||
|
||||
await dialog_manager.switch_to(AdminMenuSG.rooms_select_floor)
|
||||
|
||||
|
||||
async def on_create_room_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.rooms_select_floor)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_room_delete_confirm_data(
|
||||
dialog_manager: DialogManager,
|
||||
rooms_repository: FromDishka[RoomsRepository],
|
||||
**kwargs,
|
||||
):
|
||||
room_id = dialog_manager.dialog_data.get("selected_room_id")
|
||||
|
||||
if not room_id:
|
||||
return {"room_number": "???"}
|
||||
|
||||
room = await rooms_repository.get_room_by_id(room_id)
|
||||
room_number = room.number if room else "???"
|
||||
|
||||
return {"room_number": room_number}
|
||||
|
||||
|
||||
@inject
|
||||
async def on_delete_room_confirm(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
rooms_repository: FromDishka[RoomsRepository],
|
||||
):
|
||||
room_id = dialog_manager.dialog_data.get("selected_room_id")
|
||||
|
||||
if room_id:
|
||||
await rooms_repository.delete_room(room_id)
|
||||
await callback.answer("✅ Комната удалена!")
|
||||
|
||||
await dialog_manager.switch_to(AdminMenuSG.rooms_list)
|
||||
|
||||
|
||||
async def on_delete_room_cancel(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await dialog_manager.switch_to(AdminMenuSG.rooms_list)
|
||||
|
||||
|
||||
rooms_select_floor_window = Window(
|
||||
Format("{content}"),
|
||||
Group(
|
||||
Select(
|
||||
Format("{item[0]}"),
|
||||
id="rooms_floors_select",
|
||||
item_id_getter=lambda x: x[1],
|
||||
items="floors",
|
||||
on_click=on_rooms_floor_selected,
|
||||
),
|
||||
width=2,
|
||||
),
|
||||
Button(
|
||||
Const("➕ Добавить комнату"),
|
||||
id="add_room_btn",
|
||||
on_click=on_add_room_click,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_admin_menu_from_rooms",
|
||||
state=AdminMenuSG.main,
|
||||
),
|
||||
state=AdminMenuSG.rooms_select_floor,
|
||||
getter=get_rooms_floors_data,
|
||||
)
|
||||
|
||||
rooms_list_window = Window(
|
||||
Format("{content}"),
|
||||
ScrollingGroup(
|
||||
Select(
|
||||
Format("{item[0]}"),
|
||||
id="rooms_select",
|
||||
item_id_getter=lambda x: x[1],
|
||||
items="rooms",
|
||||
on_click=on_room_selected,
|
||||
),
|
||||
id="rooms_scroll",
|
||||
width=1,
|
||||
height=7,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад к этажам"),
|
||||
id="back_to_rooms_floors",
|
||||
state=AdminMenuSG.rooms_select_floor,
|
||||
),
|
||||
state=AdminMenuSG.rooms_list,
|
||||
getter=get_rooms_list_data,
|
||||
)
|
||||
|
||||
room_delete_confirm_window = Window(
|
||||
Format("<blockquote>⚠️ <b>Подтверждение удаления</b></blockquote>\n\nВы точно хотите удалить комнату <code>{room_number}</code>?\nЭто действие необратимо и удалит всех резидентов в этой комнате!"),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да, удалить"),
|
||||
id="confirm_delete_room",
|
||||
on_click=on_delete_room_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Отмена"),
|
||||
id="cancel_delete_room",
|
||||
on_click=on_delete_room_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.room_delete_confirm,
|
||||
getter=get_room_delete_confirm_data,
|
||||
)
|
||||
|
||||
create_room_select_floor_window = Window(
|
||||
Format("{content}"),
|
||||
Group(
|
||||
Select(
|
||||
Format("{item[0]}"),
|
||||
id="create_room_floors_select",
|
||||
item_id_getter=lambda x: x[1],
|
||||
items="floors",
|
||||
on_click=on_create_room_floor_selected,
|
||||
),
|
||||
width=2,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Отмена"),
|
||||
id="cancel_create_room_floor",
|
||||
state=AdminMenuSG.rooms_select_floor,
|
||||
),
|
||||
state=AdminMenuSG.create_room_select_floor,
|
||||
getter=get_create_room_floors_data,
|
||||
)
|
||||
|
||||
create_room_input_window = Window(
|
||||
Const("<blockquote>➕ <b>Создание комнаты</b></blockquote>\n\nВведите номер комнаты:"),
|
||||
MessageInput(on_room_number_input),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
id="back_to_create_room_floor",
|
||||
state=AdminMenuSG.create_room_select_floor,
|
||||
),
|
||||
state=AdminMenuSG.create_room_input,
|
||||
)
|
||||
|
||||
create_room_confirm_window = Window(
|
||||
Format("<blockquote>✅ <b>Подтверждение</b></blockquote>\n\nСоздать комнату <code>{room_number}</code>?"),
|
||||
Row(
|
||||
Button(
|
||||
Const("✅ Да"),
|
||||
id="confirm_create_room",
|
||||
on_click=on_create_room_confirm,
|
||||
),
|
||||
Button(
|
||||
Const("❌ Нет"),
|
||||
id="cancel_create_room",
|
||||
on_click=on_create_room_cancel,
|
||||
),
|
||||
),
|
||||
state=AdminMenuSG.create_room_confirm,
|
||||
getter=get_create_room_confirm_data,
|
||||
)
|
||||
Reference in New Issue
Block a user