diff --git a/src/dutylog/application/__main__.py b/src/dutylog/application/__main__.py index a9524db..3d9256a 100644 --- a/src/dutylog/application/__main__.py +++ b/src/dutylog/application/__main__.py @@ -55,7 +55,6 @@ async def main(): setup_dialogs(dp) setup_dishka(container, dp, auto_inject=True) - # Устанавливаем команды бота await set_bot_commands(bot) await dp.start_polling(bot) diff --git a/src/dutylog/application/bot/creator_dialogs/__init__.py b/src/dutylog/application/bot/creator_dialogs/__init__.py index 4fa302d..fbdd606 100644 --- a/src/dutylog/application/bot/creator_dialogs/__init__.py +++ b/src/dutylog/application/bot/creator_dialogs/__init__.py @@ -1,5 +1,8 @@ from dutylog.application.bot.creator_dialogs.creator_menu_dialog import ( creator_menu_dialog, ) +from dutylog.application.bot.creator_dialogs.transactions_history import ( + transactions_history_window, +) -__all__ = ["creator_menu_dialog"] +__all__ = ["creator_menu_dialog", "transactions_history_window"] diff --git a/src/dutylog/application/bot/creator_dialogs/admins_management.py b/src/dutylog/application/bot/creator_dialogs/admins_management.py index 893a050..e71b576 100644 --- a/src/dutylog/application/bot/creator_dialogs/admins_management.py +++ b/src/dutylog/application/bot/creator_dialogs/admins_management.py @@ -289,6 +289,21 @@ admins_list_window = Window( id="add_admin_btn", on_click=on_add_admin_click, ), + Button( + Const("──────────"), + id="separator_btn", + on_click=lambda c, b, m: None, + ), + SwitchTo( + Const("👨💼 Админы"), + id="admins_btn", + state=CreatorMenuSG.admins_list, + ), + SwitchTo( + Const("📜 История"), + id="history_btn", + state=CreatorMenuSG.transactions_history, + ), Button( Const("◀️ Назад"), id="back_to_main_from_admins", diff --git a/src/dutylog/application/bot/creator_dialogs/creator_menu_dialog.py b/src/dutylog/application/bot/creator_dialogs/creator_menu_dialog.py index cedc4a1..b4b747c 100644 --- a/src/dutylog/application/bot/creator_dialogs/creator_menu_dialog.py +++ b/src/dutylog/application/bot/creator_dialogs/creator_menu_dialog.py @@ -7,6 +7,9 @@ from dutylog.application.bot.creator_dialogs.admins_management import ( add_admin_select_user_window, add_admin_confirm_window, ) +from dutylog.application.bot.creator_dialogs.transactions_history import ( + transactions_history_window, +) creator_menu_dialog = Dialog( @@ -15,4 +18,5 @@ creator_menu_dialog = Dialog( remove_admin_confirm_window, add_admin_select_user_window, add_admin_confirm_window, + transactions_history_window, ) diff --git a/src/dutylog/application/bot/creator_dialogs/transactions_history.py b/src/dutylog/application/bot/creator_dialogs/transactions_history.py new file mode 100644 index 0000000..355e3b7 --- /dev/null +++ b/src/dutylog/application/bot/creator_dialogs/transactions_history.py @@ -0,0 +1,79 @@ +from aiogram_dialog import Window +from aiogram_dialog.widgets.text import Format, Const +from aiogram_dialog.widgets.kbd import Back, NumberedPager, Group +from dishka import FromDishka +from dishka.integrations.aiogram_dialog import inject + +from dutylog.application.bot.user_dialogs.states import CreatorMenuSG +from dutylog.infrastructure.database.repositories.hours_transactions_repository import ( + HoursTransactionsRepository, +) +from dutylog.infrastructure.database.repositories.residents_repository import ( + ResidentsRepository, +) +from dutylog.infrastructure.database.repositories.rooms_repository import ( + RoomsRepository, +) +from dutylog.infrastructure.utils.datetime import msk_now + + +@inject +async def get_transactions_history_data( + transactions_repository: FromDishka[HoursTransactionsRepository], + residents_repository: FromDishka[ResidentsRepository], + rooms_repository: FromDishka[RoomsRepository], + dialog_manager, + **kwargs, +) -> dict: + all_transactions = await transactions_repository.get_all_transactions() + all_transactions.sort(key=lambda t: t.created_at, reverse=True) + + page = await dialog_manager.find("transactions_pager").get_page() + per_page = 7 + start_idx = page * per_page + end_idx = start_idx + per_page + + page_transactions = all_transactions[start_idx:end_idx] + + if not page_transactions: + content = "
📜 История всех транзакций\n\nНет транзакций" + else: + content = "
📜 История всех транзакций\n\n" + + for tx in page_transactions: + resident = await residents_repository.get_by_id(tx.resident_id) + if not resident: + continue + + room = await rooms_repository.get_by_id(resident.room) + room_number = room.number if room else "???" + + operation = "Начислено" if tx.transaction_type == "increase" else "Списано" + emoji = "+" if tx.transaction_type == "increase" else "−" + room_mark = " 🚪" if tx.per_room else "" + + msk_time = tx.created_at.astimezone(msk_now().tzinfo).replace(tzinfo=None) + date_str = msk_time.strftime("%d.%m.%Y %H:%M") + + remark_text = f"\n💬 {tx.remark}" if tx.remark else "" + + content += f"
{operation} {emoji}{tx.amount} ч{room_mark}\n👤 {resident.real_name or 'Без имени'} (к. {room_number})\n📅 {date_str}{remark_text}\n"
+
+ total_pages = (len(all_transactions) + per_page - 1) // per_page
+
+ return {
+ "content": content,
+ "pages": total_pages,
+ }
+
+
+transactions_history_window = Window(
+ Format("{content}"),
+ Group(
+ NumberedPager(scroll="transactions_pager", when="pages"),
+ width=8,
+ ),
+ Back(Const("◀️ Назад")),
+ state=CreatorMenuSG.transactions_history,
+ getter=get_transactions_history_data,
+)
diff --git a/src/dutylog/application/bot/user_dialogs/states.py b/src/dutylog/application/bot/user_dialogs/states.py
index 464f3dc..f53c80e 100644
--- a/src/dutylog/application/bot/user_dialogs/states.py
+++ b/src/dutylog/application/bot/user_dialogs/states.py
@@ -67,6 +67,7 @@ class CreatorMenuSG(StatesGroup):
remove_admin_confirm = State()
add_admin_select_user = State()
add_admin_confirm = State()
+ transactions_history = State()
class RegistrationSG(StatesGroup):
diff --git a/src/dutylog/infrastructure/database/dao/hours_transactions_dao.py b/src/dutylog/infrastructure/database/dao/hours_transactions_dao.py
index e95fec1..d27bba4 100644
--- a/src/dutylog/infrastructure/database/dao/hours_transactions_dao.py
+++ b/src/dutylog/infrastructure/database/dao/hours_transactions_dao.py
@@ -1,3 +1,5 @@
+from datetime import datetime, time
+
from sqlalchemy import select, delete
from sqlalchemy.ext.asyncio import AsyncSession
@@ -41,8 +43,6 @@ class HoursTransactionsDAO:
await self.session.commit()
async def get_by_period(self, start_date, end_date) -> list[HoursTransaction]:
- from datetime import datetime, time
-
start_datetime = datetime.combine(start_date, time.min)
end_datetime = datetime.combine(end_date, time.max)
diff --git a/src/dutylog/infrastructure/database/dao/residents_dao.py b/src/dutylog/infrastructure/database/dao/residents_dao.py
index 3dce8d0..cefcd9d 100644
--- a/src/dutylog/infrastructure/database/dao/residents_dao.py
+++ b/src/dutylog/infrastructure/database/dao/residents_dao.py
@@ -2,6 +2,7 @@ from sqlalchemy import select, update, delete
from sqlalchemy.ext.asyncio import AsyncSession
from dutylog.infrastructure.database.models.resident import Resident
+from dutylog.infrastructure.database.models.room import Room
class ResidentsDAO:
@@ -37,7 +38,6 @@ class ResidentsDAO:
return list(result.scalars().all())
async def search_by_room_number(self, room_number: int) -> list[Resident]:
- from dutylog.infrastructure.database.models.room import Room
result = await self.session.execute(
select(Resident)
.join(Room, Resident.room == Room.id)
diff --git a/src/dutylog/services/report_service.py b/src/dutylog/services/report_service.py
index 14166a7..9ff2d22 100644
--- a/src/dutylog/services/report_service.py
+++ b/src/dutylog/services/report_service.py
@@ -43,7 +43,6 @@ class ReportService:
start_date, end_date
)
- # Разделяем транзакции на личные и комнатные
resident_transactions = [t for t in all_transactions if not t.per_room]
room_transactions = [t for t in all_transactions if t.per_room]