fix: separate base and purchased traffic in renewal pricing

When a user has 25GB base + 100GB purchased = 125GB total,
the renewal priced it at the 250GB tier (nearest tier >= 125GB)
instead of pricing each component separately at its own tier:
base 25GB + purchased 100GB.

- Split traffic_limit_gb into base and purchased components
- Price each component at its own tier via get_traffic_price()
- Apply same discount percentage to purchased portion
- Log warning when purchased >= total (data corruption)
- Fix in both subscription_renewal_service and subscription CRUD
This commit is contained in:
Fringg
2026-02-27 05:32:08 +03:00
parent f52e6aedac
commit 739ba2986f
2 changed files with 47 additions and 3 deletions
+19 -3
View File
@@ -1391,11 +1391,27 @@ async def get_subscription_renewal_cost(
total_servers_discount = servers_discount_per_month * months_in_period
# В режиме fixed_with_topup при продлении используем фиксированный лимит
purchased_traffic = subscription.purchased_traffic_gb or 0
if settings.is_traffic_fixed():
renewal_traffic_gb = settings.get_fixed_traffic_limit()
traffic_price_per_month = settings.get_traffic_price(settings.get_fixed_traffic_limit())
# Separate base traffic from purchased to avoid wrong tier lookup
elif purchased_traffic > 0:
base_traffic_gb = (subscription.traffic_limit_gb or 0) - purchased_traffic
if base_traffic_gb <= 0:
logger.warning(
'Purchased traffic >= total limit, pricing purchased portion only',
subscription_id=subscription.id,
traffic_limit_gb=subscription.traffic_limit_gb,
purchased_traffic_gb=purchased_traffic,
)
traffic_price_per_month = settings.get_traffic_price(purchased_traffic)
else:
traffic_price_per_month = (
settings.get_traffic_price(base_traffic_gb)
+ settings.get_traffic_price(purchased_traffic)
)
else:
renewal_traffic_gb = subscription.traffic_limit_gb
traffic_price_per_month = settings.get_traffic_price(renewal_traffic_gb)
traffic_price_per_month = settings.get_traffic_price(subscription.traffic_limit_gb)
traffic_discount_percent = _get_discount_percent(
user,
promo_group,
@@ -325,8 +325,22 @@ class SubscriptionRenewalService:
await self._validate_servers_for_user_promo_group(db, user, connected_uuids)
# В режиме fixed_with_topup при продлении используем фиксированный лимит
purchased_traffic = subscription.purchased_traffic_gb or 0
if settings.is_traffic_fixed():
traffic_limit = settings.get_fixed_traffic_limit()
# Separate base traffic from purchased to avoid wrong tier lookup
# e.g. 25GB base + 100GB purchased = 125GB total → would round up to 250GB tier
elif purchased_traffic > 0:
base_traffic = (subscription.traffic_limit_gb or 0) - purchased_traffic
if base_traffic <= 0:
logger.warning(
'Purchased traffic >= total limit, pricing purchased portion only',
subscription_id=subscription.id,
traffic_limit_gb=subscription.traffic_limit_gb,
purchased_traffic_gb=purchased_traffic,
)
base_traffic = settings.DEFAULT_TRAFFIC_LIMIT_GB or 0
traffic_limit = base_traffic
else:
traffic_limit = subscription.traffic_limit_gb
if traffic_limit is None:
@@ -347,6 +361,20 @@ class SubscriptionRenewalService:
months = details.get('months_in_period') or calculate_months_from_days(period_days)
# Add purchased traffic cost separately (uses its own tier price, same discount %)
if purchased_traffic > 0 and not settings.is_traffic_fixed():
purchased_price_per_month = settings.get_traffic_price(purchased_traffic)
traffic_discount_pct = details.get('traffic_discount_percent', 0)
purchased_disc_per_month = purchased_price_per_month * traffic_discount_pct // 100
discounted_purchased_per_month = purchased_price_per_month - purchased_disc_per_month
purchased_total = discounted_purchased_per_month * months
purchased_disc_total = purchased_disc_per_month * months
total_cost += purchased_total
details['traffic_price_per_month'] = details.get('traffic_price_per_month', 0) + purchased_price_per_month
details['total_traffic_price'] = details.get('total_traffic_price', 0) + purchased_total
details['traffic_discount_total'] = details.get('traffic_discount_total', 0) + purchased_disc_total
base_original_total = (
details.get('base_price_original', 0)
+ details.get('traffic_price_per_month', 0) * months