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:
@@ -11,6 +11,7 @@ from config.settings import Settings
|
|||||||
from bot.middlewares.i18n import JsonI18n
|
from bot.middlewares.i18n import JsonI18n
|
||||||
from bot.keyboards.inline.user_keyboards import get_subscribe_only_markup
|
from bot.keyboards.inline.user_keyboards import get_subscribe_only_markup
|
||||||
from db.dal import user_dal
|
from db.dal import user_dal
|
||||||
|
from bot.utils.date_utils import add_months
|
||||||
|
|
||||||
EVENT_MAP = {
|
EVENT_MAP = {
|
||||||
"user.expires_in_72_hours": (3, "subscription_72h_notification"),
|
"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.
|
Returns True if an auto-renewal was performed (and renewal message sent), False otherwise.
|
||||||
"""
|
"""
|
||||||
from db.dal import subscription_dal, payment_dal
|
from db.dal import subscription_dal, payment_dal
|
||||||
from datetime import datetime, timezone, timedelta
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
try:
|
try:
|
||||||
auto_renewed = False
|
auto_renewed = False
|
||||||
@@ -68,8 +69,8 @@ class PanelWebhookService:
|
|||||||
# This user has tribute payments, auto-renew for the same duration
|
# 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")
|
logging.info(f"Auto-renewing tribute subscription for user {user_id} for {last_tribute_duration} months")
|
||||||
|
|
||||||
# Extend subscription by the last payment duration
|
# Extend subscription by the last payment duration (calendar months)
|
||||||
new_end_date = datetime.now(timezone.utc) + timedelta(days=last_tribute_duration * 30)
|
new_end_date = add_months(datetime.now(timezone.utc), last_tribute_duration)
|
||||||
|
|
||||||
await subscription_dal.update_subscription(
|
await subscription_dal.update_subscription(
|
||||||
session,
|
session,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from aiogram import Bot
|
|||||||
from bot.middlewares.i18n import JsonI18n
|
from bot.middlewares.i18n import JsonI18n
|
||||||
|
|
||||||
from db.dal import user_dal, subscription_dal, promo_code_dal, payment_dal
|
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 db.models import User, Subscription
|
||||||
|
|
||||||
from config.settings import Settings
|
from config.settings import Settings
|
||||||
@@ -424,7 +425,9 @@ class SubscriptionService:
|
|||||||
):
|
):
|
||||||
start_date = current_active_sub.end_date
|
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
|
applied_promo_bonus_days = 0
|
||||||
|
|
||||||
if promo_code_id_from_payment:
|
if promo_code_id_from_payment:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user