feat: add LIMITED subscription status and preserve extra devices on tariff switch

- Add SubscriptionStatus.LIMITED for traffic-exhausted subscriptions
- Webhook user.limited now sets LIMITED directly instead of DISABLED
- Add LIMITED to reactivation, extend, resume, auto-purchase, contest eligibility
- Add traffic_exhausted error response in miniapp API
- Fix device_limit being overwritten on tariff switch in all code paths:
  admin change_tariff, user switch-tariff, miniapp, bot tariff_purchase,
  auto_purchase_service — now preserves extra purchased devices via
  calc_device_limit_on_tariff_switch() helper
- Fix truthiness checks on device_limit (0 is valid, use `is not None`)
This commit is contained in:
Fringg
2026-03-12 18:28:32 +03:00
parent a11f492801
commit 8f434525eb
17 changed files with 182 additions and 56 deletions
+9 -1
View File
@@ -123,6 +123,7 @@ class SubscriptionStatus(Enum):
ACTIVE = 'active'
EXPIRED = 'expired'
DISABLED = 'disabled'
LIMITED = 'limited'
PENDING = 'pending'
@@ -1350,6 +1351,9 @@ class Subscription(Base):
if self.status == SubscriptionStatus.DISABLED.value:
return 'disabled'
if self.status == SubscriptionStatus.LIMITED.value:
return 'limited'
if self.status == SubscriptionStatus.ACTIVE.value:
if end is None or end <= current_time:
return 'expired'
@@ -1374,6 +1378,8 @@ class Subscription(Base):
return '🟢 Активна'
if actual_status == 'disabled':
return '⚫ Отключена'
if actual_status == 'limited':
return '⚠️ Трафик исчерпан'
if actual_status == 'trial':
return '🎯 Тестовая'
@@ -1391,6 +1397,8 @@ class Subscription(Base):
return '💎'
if actual_status == 'disabled':
return ''
if actual_status == 'limited':
return '⚠️'
if actual_status == 'trial':
return '🎁'
@@ -1439,7 +1447,7 @@ class Subscription(Base):
else:
self.end_date = datetime.now(UTC) + timedelta(days=days)
if self.status == SubscriptionStatus.EXPIRED.value:
if self.status in (SubscriptionStatus.EXPIRED.value, SubscriptionStatus.LIMITED.value):
self.status = SubscriptionStatus.ACTIVE.value
def add_traffic(self, gb: int):