diff --git a/bot/services/promo_code_service.py b/bot/services/promo_code_service.py index 5e477e8..e56cea9 100644 --- a/bot/services/promo_code_service.py +++ b/bot/services/promo_code_service.py @@ -1,5 +1,5 @@ import logging -from datetime import datetime +from datetime import datetime, timezone from sqlalchemy.ext.asyncio import AsyncSession from typing import Optional, Tuple, Dict from aiogram import Bot @@ -168,6 +168,16 @@ class PromoCodeService: await active_discount_dal.clear_active_discount(session, user_id) return None + # Check if promo code has expired + if promo.valid_until and promo.valid_until <= datetime.now(timezone.utc): + # Promo code expired - clear the discount + logging.info( + f"Promo code {promo.code} expired (valid_until: {promo.valid_until}). " + f"Clearing active discount for user {user_id}" + ) + await active_discount_dal.clear_active_discount(session, user_id) + return None + return (active_discount.discount_percentage, promo.code) def calculate_discounted_price( diff --git a/db/dal/active_discount_dal.py b/db/dal/active_discount_dal.py index 846bd80..5b99c51 100644 --- a/db/dal/active_discount_dal.py +++ b/db/dal/active_discount_dal.py @@ -69,3 +69,20 @@ async def clear_active_discount( if cleared: logging.info(f"Active discount cleared for user {user_id}") return cleared + + +async def clear_active_discounts_by_promo_code( + session: AsyncSession, + promo_code_id: int +) -> int: + """ + Clear all active discounts associated with a specific promo code. + Returns the number of discounts cleared. + """ + stmt = delete(ActiveDiscount).where(ActiveDiscount.promo_code_id == promo_code_id) + result = await session.execute(stmt) + await session.flush() + count = result.rowcount + if count > 0: + logging.info(f"Cleared {count} active discount(s) for promo_code_id={promo_code_id}") + return count diff --git a/db/dal/promo_code_dal.py b/db/dal/promo_code_dal.py index 0f6b174..1f1902e 100644 --- a/db/dal/promo_code_dal.py +++ b/db/dal/promo_code_dal.py @@ -132,16 +132,29 @@ async def update_promo_code(session: AsyncSession, promo_id: int, async def delete_promo_code(session: AsyncSession, promo_id: int) -> Optional[PromoCode]: + from db.dal import active_discount_dal + promo = await get_promo_code_by_id(session, promo_id) if not promo: return None - # First, delete related activations due to foreign key constraint + + # 1. Clear all active discounts referencing this promo code + await active_discount_dal.clear_active_discounts_by_promo_code(session, promo_id) + + # 2. Set promo_code_id to NULL in payments table to avoid FK violation + stmt = update(Payment).where(Payment.promo_code_id == promo_id).values(promo_code_id=None) + await session.execute(stmt) + + # 3. Delete related activations activations = await get_promo_activations_by_code_id(session, promo_id) for activation in activations: await session.delete(activation) - + + # 4. Delete the promo code itself await session.delete(promo) await session.flush() + + logging.info(f"Promo code '{promo.code}' (ID: {promo_id}) deleted successfully") return promo