aboutsummaryrefslogtreecommitdiff
path: root/handlers/user
diff options
context:
space:
mode:
Diffstat (limited to 'handlers/user')
-rw-r--r--handlers/user/__init__.py2
-rw-r--r--handlers/user/announcements.py21
-rw-r--r--handlers/user/invoices.py179
-rw-r--r--handlers/user/pay_invoice.py22
4 files changed, 217 insertions, 7 deletions
diff --git a/handlers/user/__init__.py b/handlers/user/__init__.py
index a0e719f..7eee69a 100644
--- a/handlers/user/__init__.py
+++ b/handlers/user/__init__.py
@@ -5,6 +5,7 @@ from . import info
5from . import vpn_link 5from . import vpn_link
6from . import pay_invoice 6from . import pay_invoice
7from . import announcements 7from . import announcements
8from . import invoices
8# isort: on 9# isort: on
9 10
10router = Router(name="user") 11router = Router(name="user")
@@ -13,4 +14,5 @@ router.include_routers(
13 vpn_link.router, 14 vpn_link.router,
14 pay_invoice.router, 15 pay_invoice.router,
15 announcements.router, 16 announcements.router,
17 invoices.router,
16) 18)
diff --git a/handlers/user/announcements.py b/handlers/user/announcements.py
index 8f4aa43..dc85b54 100644
--- a/handlers/user/announcements.py
+++ b/handlers/user/announcements.py
@@ -16,11 +16,17 @@ router = Router(name="announcements")
16PAGE_SIZE = 5 16PAGE_SIZE = 5
17 17
18 18
19async def get_reply_markup(page: int, session: AsyncSession) -> InlineKeyboardMarkup: 19async def get_reply_markup(
20 page: int,
21 session: AsyncSession,
22) -> InlineKeyboardMarkup | None:
20 total = await session.scalar(select(count()).select_from(Announcement)) 23 total = await session.scalar(select(count()).select_from(Announcement))
21 assert total is not None 24 assert total is not None
22 total_pages = ceil(total / PAGE_SIZE) 25 total_pages = ceil(total / PAGE_SIZE)
23 26
27 if total == 0:
28 return None
29
24 page = max(0, min(page, total_pages - 1)) 30 page = max(0, min(page, total_pages - 1))
25 query = ( 31 query = (
26 select(Announcement) 32 select(Announcement)
@@ -64,10 +70,13 @@ async def get_reply_markup(page: int, session: AsyncSession) -> InlineKeyboardMa
64 70
65@router.message(Command("announcements")) 71@router.message(Command("announcements"))
66async def command(msg: Message, bot: Bot, session: AsyncSession) -> None: 72async def command(msg: Message, bot: Bot, session: AsyncSession) -> None:
67 await msg.answer( 73 reply_markup = await get_reply_markup(0, session)
68 "Выберете анонс для просмотра.", 74
69 reply_markup=await get_reply_markup(0, session), 75 if reply_markup is None:
70 ) 76 await msg.answer("Нету анонсов для просмотра.")
77 return
78
79 await msg.answer("Выберете анонс для просмотра.", reply_markup=reply_markup)
71 80
72 81
73@router.callback_query(AnnouncePageClb.filter()) 82@router.callback_query(AnnouncePageClb.filter())
@@ -80,7 +89,7 @@ async def page(
80 89
81 reply_markup = await get_reply_markup(callback_data.page, session) 90 reply_markup = await get_reply_markup(callback_data.page, session)
82 await clb.message.edit_text( 91 await clb.message.edit_text(
83 "Выберете анонс для просмотра.", 92 "Выберете анонс для просмотра:",
84 reply_markup=reply_markup, 93 reply_markup=reply_markup,
85 ) 94 )
86 95
diff --git a/handlers/user/invoices.py b/handlers/user/invoices.py
new file mode 100644
index 0000000..cc071bb
--- /dev/null
+++ b/handlers/user/invoices.py
@@ -0,0 +1,179 @@
1from math import ceil
2
3from aiogram import Bot, Router
4from aiogram.filters import Command
5from aiogram.types import (
6 CallbackQuery,
7 InlineKeyboardButton,
8 InlineKeyboardMarkup,
9 Message,
10)
11from sqlalchemy import select
12from sqlalchemy.ext.asyncio import AsyncSession
13from sqlalchemy.sql.functions import count
14
15from libs.invoice import (
16 InvoiceStatus,
17 get_invoice_payments,
18 get_payment_status,
19)
20from libs.msg import eclipse_text
21from libs.user import mention
22from models import Invoice, PaymentStatus, User
23from models.callback_data import InvoiceItemClb, InvoicePageClb, PayInvoiceClb
24
25router = Router(name="invoices")
26PAGE_SIZE = 5
27PAYMENT_STATUS = {
28 PaymentStatus.PENDING: "🟡",
29 PaymentStatus.ACCEPTED: "🟢",
30 PaymentStatus.REJECTED: "🔴",
31}
32INVOICE_STATUS = {
33 InvoiceStatus.PAID: "🟢",
34 InvoiceStatus.UNPAID: "🔴",
35}
36
37
38def get_text(user: User) -> str:
39 if user.is_admin():
40 return "Выберете счёт для просмотра информации:"
41 else:
42 return "Выберете счёт для оплаты:"
43
44
45async def get_reply_markup(
46 page: int,
47 user: User,
48 session: AsyncSession,
49) -> InlineKeyboardMarkup | None:
50 total = await session.scalar(
51 select(count()).select_from(Invoice).where(Invoice.datetime >= user.datetime)
52 )
53 assert total is not None
54 total_pages = ceil(total / PAGE_SIZE)
55
56 if total == 0:
57 return None
58
59 page = max(0, min(page, total_pages - 1))
60 query = (
61 select(Invoice)
62 .where(Invoice.datetime >= user.datetime)
63 .offset(PAGE_SIZE * page)
64 .limit(PAGE_SIZE)
65 .order_by(Invoice.id.desc())
66 )
67 invoices = await session.scalars(query)
68
69 invoice_buttons = []
70 for i in invoices:
71 if user.is_admin():
72 invoice_payments = await get_invoice_payments(session, i)
73 status = INVOICE_STATUS[invoice_payments.status]
74 callback_data = InvoiceItemClb(page=page, invoice_id=i.id).pack()
75 else:
76 status = PAYMENT_STATUS[await get_payment_status(session, i.id, user.id)]
77 callback_data = PayInvoiceClb(invoice_id=i.id).pack()
78
79 button = InlineKeyboardButton(
80 text=(
81 f"{status} "
82 f"{eclipse_text(i.message.text, 10)} "
83 f"({i.datetime.strftime('%d %b %y г.')})"
84 ),
85 callback_data=callback_data,
86 )
87
88 invoice_buttons.append([button])
89
90 page_buttons = []
91 if page > 0:
92 page_buttons.append(
93 InlineKeyboardButton(
94 text="◀️",
95 callback_data=InvoicePageClb(page=page - 1).pack(),
96 )
97 )
98 if page < total_pages - 1:
99 page_buttons.append(
100 InlineKeyboardButton(
101 text="▶️",
102 callback_data=InvoicePageClb(page=page + 1).pack(),
103 )
104 )
105
106 return InlineKeyboardMarkup(inline_keyboard=[*invoice_buttons, page_buttons])
107
108
109@router.message(Command("invoices"))
110async def command(msg: Message, session: AsyncSession, user: User) -> None:
111 reply_markup = await get_reply_markup(0, user, session)
112
113 if reply_markup is None:
114 await msg.answer("Нету счетов для оплаты.")
115 return
116
117 await msg.answer(get_text(user), reply_markup=reply_markup)
118
119
120@router.callback_query(InvoicePageClb.filter())
121async def page(
122 clb: CallbackQuery,
123 callback_data: InvoicePageClb,
124 session: AsyncSession,
125 user: User,
126) -> None:
127 assert isinstance(clb.message, Message)
128 await clb.message.edit_text(
129 get_text(user),
130 reply_markup=await get_reply_markup(callback_data.page, user, session),
131 )
132
133 await clb.answer()
134
135
136@router.callback_query(InvoiceItemClb.filter())
137async def item(
138 clb: CallbackQuery,
139 bot: Bot,
140 callback_data: InvoiceItemClb,
141 session: AsyncSession,
142 user: User,
143) -> None:
144 assert isinstance(clb.message, Message)
145 if not user.is_admin():
146 await clb.answer("У вас нет прав для данного действия.", show_alert=True)
147 return
148
149 invoice = await session.get(Invoice, callback_data.invoice_id)
150 assert invoice is not None
151 invoice_payments = await get_invoice_payments(session, invoice)
152
153 text_template = (
154 f"Статус оплаты счёта: {INVOICE_STATUS[invoice_payments.status]}\n"
155 "Пользователи оплатившие счёт:\n"
156 "{}"
157 )
158 reply_markup = InlineKeyboardMarkup(
159 inline_keyboard=[
160 [
161 InlineKeyboardButton(
162 text="Назад к выбору",
163 callback_data=InvoicePageClb(page=callback_data.page).pack(),
164 )
165 ]
166 ]
167 )
168
169 await clb.message.edit_text(text_template.format("..."), reply_markup=reply_markup)
170 user_status = []
171 for user_id, s in invoice_payments.user_status.items():
172 chat = await bot.get_chat(user_id)
173 user_status.append(f"{PAYMENT_STATUS[s]} - {mention(chat)}")
174 await clb.message.edit_text(
175 text_template.format("\n".join(user_status)),
176 reply_markup=reply_markup,
177 )
178
179 await clb.answer()
diff --git a/handlers/user/pay_invoice.py b/handlers/user/pay_invoice.py
index 98f80a6..db75f47 100644
--- a/handlers/user/pay_invoice.py
+++ b/handlers/user/pay_invoice.py
@@ -20,7 +20,15 @@ from sqlalchemy.ext.asyncio import AsyncSession
20 20
21from libs.fsm import get_data, set_data 21from libs.fsm import get_data, set_data
22from libs.user import mention 22from libs.user import mention
23from models import Payment, PaymentStatus, ReceiptFile, ReceiptFileType, User, UserRole 23from models import (
24 Invoice,
25 Payment,
26 PaymentStatus,
27 ReceiptFile,
28 ReceiptFileType,
29 User,
30 UserRole,
31)
24from models.callback_data import PayInvoiceClb, PaymentStatusClb 32from models.callback_data import PayInvoiceClb, PaymentStatusClb
25 33
26router = Router(name="pay_invoice") 34router = Router(name="pay_invoice")
@@ -44,7 +52,19 @@ async def button(
44 state: FSMContext, 52 state: FSMContext,
45 callback_data: PayInvoiceClb, 53 callback_data: PayInvoiceClb,
46 session: AsyncSession, 54 session: AsyncSession,
55 user: User,
47) -> None: 56) -> None:
57 if user.is_admin():
58 await clb.answer("Администраторы не могут оплачивать счета", show_alert=True)
59 return
60
61 invoice = await session.get(Invoice, callback_data.invoice_id)
62 assert invoice is not None
63
64 if user.datetime > invoice.datetime:
65 await clb.answer("Вы не можете оплатить данный счёт", show_alert=True)
66 return
67
48 payment = await session.scalar( 68 payment = await session.scalar(
49 select(Payment).where( 69 select(Payment).where(
50 and_( 70 and_(