Refactor subscription duration calculation in PanelWebhookService and SubscriptionService

- Introduced a utility function `add_months` to handle subscription duration calculations based on calendar months instead of a fixed 30-day period.
- Updated the auto-renewal logic in `PanelWebhookService` to use the new function for extending subscription end dates.
- Adjusted the duration calculation in `SubscriptionService` to derive the end date after a specified number of months, improving accuracy in subscription management.
This commit is contained in:
machka-pasla
2025-08-25 12:30:44 +03:00
parent b23b75b72e
commit 60c6e0e961
3 changed files with 35 additions and 4 deletions
+4 -3
View File
@@ -11,6 +11,7 @@ from config.settings import Settings
from bot.middlewares.i18n import JsonI18n
from bot.keyboards.inline.user_keyboards import get_subscribe_only_markup
from db.dal import user_dal
from bot.utils.date_utils import add_months
EVENT_MAP = {
"user.expires_in_72_hours": (3, "subscription_72h_notification"),
@@ -48,7 +49,7 @@ class PanelWebhookService:
Returns True if an auto-renewal was performed (and renewal message sent), False otherwise.
"""
from db.dal import subscription_dal, payment_dal
from datetime import datetime, timezone, timedelta
from datetime import datetime, timezone
try:
auto_renewed = False
@@ -68,8 +69,8 @@ class PanelWebhookService:
# This user has tribute payments, auto-renew for the same duration
logging.info(f"Auto-renewing tribute subscription for user {user_id} for {last_tribute_duration} months")
# Extend subscription by the last payment duration
new_end_date = datetime.now(timezone.utc) + timedelta(days=last_tribute_duration * 30)
# Extend subscription by the last payment duration (calendar months)
new_end_date = add_months(datetime.now(timezone.utc), last_tribute_duration)
await subscription_dal.update_subscription(
session,
+4 -1
View File
@@ -6,6 +6,7 @@ from aiogram import Bot
from bot.middlewares.i18n import JsonI18n
from db.dal import user_dal, subscription_dal, promo_code_dal, payment_dal
from bot.utils.date_utils import add_months
from db.models import User, Subscription
from config.settings import Settings
@@ -424,7 +425,9 @@ class SubscriptionService:
):
start_date = current_active_sub.end_date
duration_days_total = months * 30
# base duration by months
end_after_months = add_months(start_date, months)
duration_days_total = (end_after_months - start_date).days
applied_promo_bonus_days = 0
if promo_code_id_from_payment:
+27
View File
@@ -0,0 +1,27 @@
from datetime import datetime, timedelta
def add_months(base_dt: datetime, months_to_add: int) -> datetime:
"""Add calendar months to a datetime, clamping the day to the month's length.
Preserves tzinfo from base_dt.
"""
year = base_dt.year
month = base_dt.month + months_to_add
day = base_dt.day
# Normalize year and month
year += (month - 1) // 12
month = ((month - 1) % 12) + 1
# Determine last day of target month by rolling to next month's first day and subtracting 1 day
if month == 12:
next_month_first = datetime(year + 1, 1, 1, tzinfo=base_dt.tzinfo)
else:
next_month_first = datetime(year, month + 1, 1, tzinfo=base_dt.tzinfo)
last_day = (next_month_first - timedelta(days=1)).day
clamped_day = min(day, last_day)
return base_dt.replace(year=year, month=month, day=clamped_day)