From d0596366a44f15a080eefcd18d691551ad519413 Mon Sep 17 00:00:00 2001 From: Arthur Oleinikov Date: Wed, 24 Sep 2025 11:49:18 +0300 Subject: [PATCH] feat: enhance Telegram notifications --- notifications/tasks.py | 58 ++++++++++++++++++++++++++++++++---------- payments/views.py | 51 ++++++++++++++++++++++--------------- 2 files changed, 75 insertions(+), 34 deletions(-) diff --git a/notifications/tasks.py b/notifications/tasks.py index ac38360..dfd7bbb 100644 --- a/notifications/tasks.py +++ b/notifications/tasks.py @@ -12,33 +12,63 @@ def notify_new_borrowing(borrowing_id): id=borrowing_id ) message = ( - f"New borrowing created!\n" - f"{borrowing.user}\n" - f"Book: {borrowing.book.title}\n" - f"Borrowed: {borrowing.borrow_date}\n" - f"Expected return: {borrowing.expected_return_date}" + f"📚 New Borrowing Created!\n" + f"👤 User: {borrowing.user.get_full_name()}\n" + f"📖 Book: {borrowing.book.title}\n" + f"📅 Borrowed: {borrowing.borrow_date}\n" + f"📅 Expected Return: {borrowing.expected_return_date}\n" + f"💰 Daily Fee: ${borrowing.book.daily_fee}" ) send_telegram_message(message) except Borrowing.DoesNotExist: pass +@shared_task +def notify_successful_payment(payment_id): + try: + from payments.models import Payment + + payment = Payment.objects.select_related( + "borrowing__book", "borrowing__user" + ).get(id=payment_id) + + borrowing = payment.borrowing + message = ( + f"💰 Payment Completed!\n" + f"👤 User: {borrowing.user.get_full_name()}\n" + f"📚 Book: {borrowing.book.title}\n" + f"💵 Amount: ${payment.money_to_pay}\n" + f"💳 Payment Type: {payment.payment_type}\n" + f"✅ Status: {payment.status}\n" + f"📅 Borrowed: {borrowing.borrow_date}\n" + f"📅 Expected Return: {borrowing.expected_return_date}" + ) + send_telegram_message(message) + except Exception: + pass + + @shared_task def check_overdue_borrowings(): today = timezone.now().date() overdues = Borrowing.objects.filter( expected_return_date__lte=today, actual_return_date__isnull=True ).select_related("book", "user") + if overdues.exists(): - for b in overdues: - msg = ( - f"Overdue borrowing!\n" - f"{b.user}\n" - f"Book: {b.book.title}\n" - f"Borrowed: {b.borrow_date}\n" - f"Expected return: {b.expected_return_date}\n" - f"Status: Not returned!" + for borrowing in overdues: + days_overdue = (today - borrowing.expected_return_date).days + message = ( + f"⚠️ Overdue Borrowing Alert!\n" + f"👤 User: {borrowing.user.get_full_name()}\n" + f"📚 Book: {borrowing.book.title}\n" + f"📅 Borrowed: {borrowing.borrow_date}\n" + f"📅 Expected Return: {borrowing.expected_return_date}\n" + f"⏰ Days Overdue: {days_overdue}\n" + f"❌ Status: Not returned!\n" + f"💸 Daily Fee: ${borrowing.book.daily_fee}" ) - send_telegram_message(msg) + send_telegram_message(message) else: send_telegram_message("✅ No borrowings overdue today!") diff --git a/payments/views.py b/payments/views.py index 5156a46..ffa196d 100644 --- a/payments/views.py +++ b/payments/views.py @@ -1,5 +1,6 @@ import stripe from django.conf import settings +from django.db import transaction from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator @@ -15,6 +16,7 @@ PaymentListSerializer, PaymentDetailSerializer, ) +from notifications.tasks import notify_successful_payment @extend_schema_view( @@ -67,23 +69,28 @@ class PaymentSuccessView(APIView): def _update_payment_status(self, session_id, is_test=False): """Common logic for updating payment status""" try: - payment = Payment.objects.get(session_id=session_id) - payment.status = "PAID" - payment.save() - - message = ( - "Payment status updated to PAID (TEST MODE)" - if is_test - else "Payment successful!" - ) + with transaction.atomic(): + payment = Payment.objects.select_for_update().get( + session_id=session_id + ) + payment.status = "PAID" + payment.save() - return Response( - { - "message": message, - "payment_id": payment.id, - "status": payment.status, - } - ) + notify_successful_payment.delay(payment.id) + + message = ( + "Payment status updated to PAID (TEST MODE)" + if is_test + else "Payment successful!" + ) + + return Response( + { + "message": message, + "payment_id": payment.id, + "status": payment.status, + } + ) except Payment.DoesNotExist: return Response( {"error": "Payment not found"}, @@ -186,10 +193,14 @@ def post(self, request): session_id = session["id"] try: - payment = Payment.objects.get(session_id=session_id) - if session["payment_status"] == "paid": - payment.status = "PAID" - payment.save() + with transaction.atomic(): + payment = Payment.objects.select_for_update().get( + session_id=session_id + ) + if session["payment_status"] == "paid": + payment.status = "PAID" + payment.save() + notify_successful_payment.delay(payment.id) except Payment.DoesNotExist: pass