mirror of
https://github.com/koloideal/DutyLog.git
synced 2026-06-10 10:25:29 +03:00
update
This commit is contained in:
@@ -17,6 +17,7 @@ from dutylog.infrastructure.ioc import (
|
||||
DatabaseProvider,
|
||||
DAOProvider,
|
||||
RepositoryProvider,
|
||||
ServiceProvider,
|
||||
)
|
||||
from dutylog.infrastructure.utils.config import load_config
|
||||
|
||||
@@ -39,6 +40,7 @@ async def main():
|
||||
DatabaseProvider(),
|
||||
DAOProvider(),
|
||||
RepositoryProvider(),
|
||||
ServiceProvider(),
|
||||
)
|
||||
|
||||
dp.include_router(user_router)
|
||||
|
||||
@@ -38,6 +38,8 @@ from dutylog.application.bot.user_dialogs.admin_dialogs.rooms_management import
|
||||
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,
|
||||
@@ -85,6 +87,8 @@ admin_menu_dialog = Dialog(
|
||||
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,
|
||||
|
||||
+211
-5
@@ -1,15 +1,18 @@
|
||||
from aiogram.types import CallbackQuery
|
||||
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
|
||||
from aiogram_dialog.widgets.kbd import SwitchTo, Button, Row, Select, ScrollingGroup
|
||||
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.reporting_periods_repository import (
|
||||
ReportingPeriodsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.utils.datetime import msk_now
|
||||
from dutylog.services.report_service import ReportService
|
||||
|
||||
|
||||
MONTH_NAMES = {
|
||||
@@ -96,7 +99,7 @@ async def on_make_report_click(
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await callback.answer("⚠️ Функционал в разработке", show_alert=True)
|
||||
await dialog_manager.switch_to(AdminMenuSG.generate_report_select_period)
|
||||
|
||||
|
||||
@inject
|
||||
@@ -173,7 +176,6 @@ reporting_period_window = Window(
|
||||
Const("📊 Сделать отчёт"),
|
||||
id="make_report_btn",
|
||||
on_click=on_make_report_click,
|
||||
when="has_active",
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад"),
|
||||
@@ -201,3 +203,207 @@ next_period_confirm_window = Window(
|
||||
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,
|
||||
)
|
||||
|
||||
@@ -42,6 +42,8 @@ class AdminMenuSG(StatesGroup):
|
||||
create_room_confirm = State()
|
||||
reporting_period = State()
|
||||
next_period_confirm = State()
|
||||
generate_report_select_period = State()
|
||||
generate_report_confirm = State()
|
||||
statistics = State()
|
||||
broadcast = State()
|
||||
broadcast_confirm = State()
|
||||
|
||||
Reference in New Issue
Block a user