feat: add single endpoint for merchant subscriptions with usage data

Replace N+1 per-platform API calls on merchant detail page with a single
GET /admin/subscriptions/merchants/{id} endpoint. Extract shared
subscription+usage aggregation logic into a reusable service method and
refactor the store endpoint to use it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 20:58:02 +01:00
parent b9ac252a9f
commit 42b894094a
4 changed files with 92 additions and 66 deletions

View File

@@ -286,6 +286,70 @@ class AdminSubscriptionService:
p = db.query(Platform).filter(Platform.id == platform_id).first()
return p.name if p else None
# =========================================================================
# Merchant Subscriptions with Usage
# =========================================================================
def get_merchant_subscriptions_with_usage(
self, db: Session, merchant_id: int
) -> list[dict]:
"""Get all subscriptions for a merchant with tier info and feature usage.
Returns a list of dicts, each containing:
- subscription: serialized MerchantSubscription
- tier: tier info dict (code, name, feature_codes)
- features: list of quantitative usage metrics
- platform_id: int
- platform_name: str
"""
from app.modules.billing.schemas import MerchantSubscriptionAdminResponse
from app.modules.billing.services.feature_service import feature_service
from app.modules.billing.services.subscription_service import (
subscription_service,
)
subs = subscription_service.get_merchant_subscriptions(db, merchant_id)
platforms_map = self.get_platform_names_map(db)
results = []
for sub in subs:
features_summary = feature_service.get_merchant_features_summary(
db, merchant_id, sub.platform_id
)
tier_info = None
if sub.tier:
tier_info = {
"code": sub.tier.code,
"name": sub.tier.name,
"feature_codes": [
fl.feature_code for fl in (sub.tier.feature_limits or [])
],
}
usage_metrics = []
for fs in features_summary:
if fs.feature_type == "quantitative" and fs.enabled:
usage_metrics.append({
"name": fs.name_key.replace("_", " ").title(),
"current": fs.current or 0,
"limit": fs.limit,
"percentage": fs.percent_used or 0,
"is_unlimited": fs.limit is None,
"is_at_limit": fs.remaining == 0 if fs.remaining is not None else False,
"is_approaching_limit": (fs.percent_used or 0) >= 80,
})
results.append({
"subscription": MerchantSubscriptionAdminResponse.model_validate(sub).model_dump(),
"tier": tier_info,
"features": usage_metrics,
"platform_id": sub.platform_id,
"platform_name": platforms_map.get(sub.platform_id, ""),
})
return results
# =========================================================================
# Statistics
# =========================================================================