Files
orion/app/modules/billing/schemas/subscription.py
Samir Boulahtit 1db7e8a087 feat(billing): migrate frontend templates to feature provider system
Replace hardcoded subscription fields (orders_limit, products_limit,
team_members_limit) across 5 frontend pages with dynamic feature
provider APIs. Add admin convenience endpoint for store subscription
lookup. Remove legacy stubs (StoreSubscription, FeatureCode, Feature,
TIER_LIMITS, FeatureInfo, FeatureUpgradeInfo) and schema aliases.

Pages updated:
- Admin subscriptions: dynamic feature overrides editor
- Admin tiers: correct feature catalog/limits API URLs
- Store billing: usage metrics from /store/billing/usage
- Merchant subscription detail: tier.feature_limits rendering
- Admin store detail: new GET /admin/subscriptions/store/{id} endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 15:18:16 +01:00

144 lines
3.7 KiB
Python

# app/modules/billing/schemas/subscription.py
"""
Pydantic schemas for merchant-level subscription operations.
Supports subscription management, tier information, and feature summaries.
"""
from datetime import datetime
from pydantic import BaseModel, ConfigDict, Field
# ============================================================================
# Tier Information Schemas
# ============================================================================
class TierFeatureLimitResponse(BaseModel):
"""Feature limit entry for a tier."""
feature_code: str
limit_value: int | None = Field(None, description="None = unlimited")
class TierInfo(BaseModel):
"""Full tier information with feature limits."""
code: str
name: str
description: str | None = None
price_monthly_cents: int
price_annual_cents: int | None
feature_codes: list[str] = Field(default_factory=list)
feature_limits: list[TierFeatureLimitResponse] = Field(default_factory=list)
# ============================================================================
# Subscription Schemas
# ============================================================================
class MerchantSubscriptionCreate(BaseModel):
"""Schema for creating a merchant subscription."""
tier_code: str = Field(default="essential")
is_annual: bool = False
trial_days: int = Field(default=14, ge=0, le=30)
class MerchantSubscriptionUpdate(BaseModel):
"""Schema for updating a merchant subscription."""
tier_code: str | None = None
status: str | None = Field(None, pattern="^(trial|active|past_due|cancelled|expired)$")
is_annual: bool | None = None
class MerchantSubscriptionResponse(BaseModel):
"""Schema for merchant subscription response."""
model_config = ConfigDict(from_attributes=True)
id: int
merchant_id: int
platform_id: int
tier_id: int | None
status: str
is_annual: bool
period_start: datetime
period_end: datetime
trial_ends_at: datetime | None
# Stripe info (optional, may be hidden from client)
stripe_customer_id: str | None = None
# Cancellation
cancelled_at: datetime | None = None
# Computed properties
is_active: bool
is_trial: bool
trial_days_remaining: int | None
created_at: datetime
updated_at: datetime
# ============================================================================
# Feature Summary Schemas
# ============================================================================
class FeatureSummaryResponse(BaseModel):
"""Feature summary for merchant portal display."""
code: str
name_key: str
description_key: str
category: str
feature_type: str
scope: str
enabled: bool
limit: int | None = None
current: int | None = None
remaining: int | None = None
percent_used: float | None = None
is_override: bool = False
unit_key: str | None = None
ui_icon: str | None = None
class MerchantSubscriptionStatusResponse(BaseModel):
"""Full subscription status with tier info and feature summary."""
subscription: MerchantSubscriptionResponse
tier: TierInfo | None = None
features: list[FeatureSummaryResponse] = Field(default_factory=list)
# ============================================================================
# Limit Check Schemas
# ============================================================================
class LimitCheckResult(BaseModel):
"""Result of a limit check."""
allowed: bool
limit: int | None
current: int
remaining: int | None
message: str | None = None
class FeatureCheckResponse(BaseModel):
"""Response for feature check."""
feature_code: str
enabled: bool
message: str | None = None