diff --git a/app/cabinet/routes/subscription.py b/app/cabinet/routes/subscription.py index d8f65811..ec464ea9 100644 --- a/app/cabinet/routes/subscription.py +++ b/app/cabinet/routes/subscription.py @@ -1038,6 +1038,16 @@ async def update_autopay( detail='No subscription found', ) + # Суточные подписки имеют свой механизм продления (DailySubscriptionService), + # глобальный autopay для них запрещён + if request.enabled: + await db.refresh(user.subscription, ['tariff']) + if user.subscription.tariff and getattr(user.subscription.tariff, 'is_daily', False): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail='Autopay is not available for daily subscriptions', + ) + user.subscription.autopay_enabled = request.enabled if request.days_before is not None: diff --git a/app/database/crud/subscription.py b/app/database/crud/subscription.py index 8bcd6c1d..6b94c8b4 100644 --- a/app/database/crud/subscription.py +++ b/app/database/crud/subscription.py @@ -714,7 +714,10 @@ async def get_subscriptions_for_autopay(db: AsyncSession) -> list[Subscription]: result = await db.execute( select(Subscription) - .options(selectinload(Subscription.user)) + .options( + selectinload(Subscription.user), + selectinload(Subscription.tariff), + ) .where( and_( Subscription.status == SubscriptionStatus.ACTIVE.value, @@ -727,6 +730,11 @@ async def get_subscriptions_for_autopay(db: AsyncSession) -> list[Subscription]: ready_for_autopay = [] for subscription in all_autopay_subscriptions: + # Суточные подписки имеют свой механизм продления (DailySubscriptionService), + # глобальный autopay на них не распространяется + if subscription.tariff and getattr(subscription.tariff, 'is_daily', False): + continue + days_until_expiry = (subscription.end_date - current_time).days if days_until_expiry <= subscription.autopay_days_before and subscription.end_date > current_time: diff --git a/app/handlers/subscription/autopay.py b/app/handlers/subscription/autopay.py index 2343324b..a99439b1 100644 --- a/app/handlers/subscription/autopay.py +++ b/app/handlers/subscription/autopay.py @@ -39,6 +39,21 @@ async def handle_autopay_menu(callback: types.CallbackQuery, db_user: User, db: ) return + # Суточные подписки имеют свой механизм продления, глобальный autopay не применяется + try: + await db.refresh(subscription, ['tariff']) + except Exception: + pass + if subscription.tariff and getattr(subscription.tariff, 'is_daily', False): + await callback.answer( + texts.t( + 'AUTOPAY_NOT_AVAILABLE_FOR_DAILY', + 'Автоплатеж недоступен для суточных тарифов. Списание происходит автоматически раз в сутки.', + ), + show_alert=True, + ) + return + status = ( texts.t('AUTOPAY_STATUS_ENABLED', 'включен') if subscription.autopay_enabled @@ -68,6 +83,24 @@ async def toggle_autopay(callback: types.CallbackQuery, db_user: User, db: Async subscription = db_user.subscription enable = callback.data == 'autopay_enable' + # Суточные подписки имеют свой механизм продления (DailySubscriptionService), + # глобальный autopay для них запрещён + if enable: + try: + await db.refresh(subscription, ['tariff']) + except Exception: + pass + if subscription.tariff and getattr(subscription.tariff, 'is_daily', False): + texts = get_texts(db_user.language) + await callback.answer( + texts.t( + 'AUTOPAY_NOT_AVAILABLE_FOR_DAILY', + 'Автоплатеж недоступен для суточных тарифов. Списание происходит автоматически раз в сутки.', + ), + show_alert=True, + ) + return + await update_subscription_autopay(db, subscription, enable) texts = get_texts(db_user.language) diff --git a/app/services/monitoring_service.py b/app/services/monitoring_service.py index 4781f595..6939f361 100644 --- a/app/services/monitoring_service.py +++ b/app/services/monitoring_service.py @@ -966,7 +966,8 @@ class MonitoringService: selectinload(Subscription.user).options( selectinload(User.promo_group), selectinload(User.user_promo_groups).selectinload(UserPromoGroup.promo_group), - ) + ), + selectinload(Subscription.tariff), ) .where( and_( @@ -980,6 +981,16 @@ class MonitoringService: autopay_subscriptions = [] for sub in all_autopay_subscriptions: + # Суточные подписки имеют свой собственный механизм продления + # (DailySubscriptionService), глобальный autopay на них не распространяется + if sub.tariff and getattr(sub.tariff, 'is_daily', False): + logger.debug( + 'Пропускаем суточную подписку %s (тариф %s) в глобальном autopay', + sub.id, + sub.tariff.name, + ) + continue + days_before_expiry = (sub.end_date - current_time).days if days_before_expiry <= min(sub.autopay_days_before, 3): autopay_subscriptions.append(sub) diff --git a/app/webapi/routes/miniapp.py b/app/webapi/routes/miniapp.py index d1fd6724..17f3977f 100644 --- a/app/webapi/routes/miniapp.py +++ b/app/webapi/routes/miniapp.py @@ -3698,7 +3698,22 @@ async def update_subscription_autopay_endpoint( subscription = _ensure_paid_subscription(user) _validate_subscription_id(payload.subscription_id, subscription) + # Суточные подписки имеют свой механизм продления (DailySubscriptionService), + # глобальный autopay для них запрещён target_enabled = bool(payload.enabled) if payload.enabled is not None else bool(subscription.autopay_enabled) + if target_enabled: + try: + await db.refresh(subscription, ['tariff']) + except Exception: + pass + if subscription.tariff and getattr(subscription.tariff, 'is_daily', False): + raise HTTPException( + status.HTTP_400_BAD_REQUEST, + detail={ + 'code': 'autopay_not_available_for_daily', + 'message': 'Autopay is not available for daily subscriptions', + }, + ) requested_days = payload.days_before normalized_days = _normalize_autopay_days(requested_days)