9a2aea038a
- Add pyproject.toml with uv and ruff configuration - Pin Python version to 3.13 via .python-version - Add Makefile commands: lint, format, fix - Apply ruff formatting to entire codebase - Remove unused imports (base64 in yookassa/simple_subscription) - Update .gitignore for new config files
149 lines
4.0 KiB
Python
149 lines
4.0 KiB
Python
"""Subscription schemas for cabinet."""
|
|
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class ServerInfo(BaseModel):
|
|
"""Server info for display."""
|
|
|
|
uuid: str
|
|
name: str
|
|
country_code: str | None = None
|
|
|
|
|
|
class TrafficPurchaseInfo(BaseModel):
|
|
"""Purchased traffic package info."""
|
|
|
|
id: int
|
|
traffic_gb: int
|
|
expires_at: datetime
|
|
created_at: datetime
|
|
days_remaining: int
|
|
progress_percent: float
|
|
|
|
|
|
class SubscriptionResponse(BaseModel):
|
|
"""User subscription data."""
|
|
|
|
id: int
|
|
status: str
|
|
is_trial: bool
|
|
start_date: datetime
|
|
end_date: datetime
|
|
days_left: int
|
|
hours_left: int = 0
|
|
minutes_left: int = 0
|
|
time_left_display: str = '' # Human readable format like "2д 5ч" or "5ч 30м"
|
|
traffic_limit_gb: int
|
|
traffic_used_gb: float
|
|
traffic_used_percent: float
|
|
device_limit: int
|
|
connected_squads: list[str] = []
|
|
servers: list[ServerInfo] = [] # Server display info
|
|
autopay_enabled: bool
|
|
autopay_days_before: int
|
|
subscription_url: str | None = None
|
|
hide_subscription_link: bool = False # Скрывать ли отображение ссылки (но кнопки работают)
|
|
is_active: bool
|
|
is_expired: bool
|
|
traffic_purchases: list[TrafficPurchaseInfo] = []
|
|
# Daily tariff fields
|
|
is_daily: bool = False
|
|
is_daily_paused: bool = False
|
|
daily_price_kopeks: int | None = None
|
|
next_daily_charge_at: datetime | None = None # When next daily charge will happen
|
|
tariff_id: int | None = None
|
|
tariff_name: str | None = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class RenewalOptionResponse(BaseModel):
|
|
"""Available subscription renewal option."""
|
|
|
|
period_days: int
|
|
price_kopeks: int
|
|
price_rubles: float
|
|
discount_percent: int = 0
|
|
original_price_kopeks: int | None = None
|
|
|
|
|
|
class RenewalRequest(BaseModel):
|
|
"""Request to renew subscription."""
|
|
|
|
period_days: int = Field(..., description='Renewal period in days')
|
|
|
|
|
|
class TrafficPackageResponse(BaseModel):
|
|
"""Available traffic package."""
|
|
|
|
gb: int
|
|
price_kopeks: int
|
|
price_rubles: float
|
|
is_unlimited: bool = False
|
|
|
|
|
|
class TrafficPurchaseRequest(BaseModel):
|
|
"""Request to purchase additional traffic."""
|
|
|
|
gb: int = Field(..., ge=0, description='GB to purchase (0 = unlimited)')
|
|
|
|
|
|
class DevicePurchaseRequest(BaseModel):
|
|
"""Request to purchase additional device slots."""
|
|
|
|
devices: int = Field(..., ge=1, description='Number of additional devices')
|
|
|
|
|
|
class AutopayUpdateRequest(BaseModel):
|
|
"""Request to update autopay settings."""
|
|
|
|
enabled: bool
|
|
days_before: int | None = Field(None, ge=1, le=30, description='Days before expiration to charge')
|
|
|
|
|
|
class TrialInfoResponse(BaseModel):
|
|
"""Trial subscription info."""
|
|
|
|
is_available: bool
|
|
duration_days: int
|
|
traffic_limit_gb: int
|
|
device_limit: int
|
|
requires_payment: bool = False
|
|
price_kopeks: int = 0
|
|
price_rubles: float = 0.0
|
|
reason_unavailable: str | None = None
|
|
|
|
|
|
# ============ Purchase Options Schemas ============
|
|
|
|
|
|
class PurchaseSelectionRequest(BaseModel):
|
|
"""User's selection for subscription purchase."""
|
|
|
|
period_id: str | None = Field(None, description="Period ID like 'days:30'")
|
|
period_days: int | None = Field(None, description='Period in days')
|
|
traffic_value: int | None = Field(None, description='Traffic in GB (0 = unlimited)')
|
|
servers: list[str] | None = Field(default_factory=list, description='Server UUIDs')
|
|
devices: int | None = Field(None, description='Device limit')
|
|
|
|
|
|
class PurchasePreviewRequest(BaseModel):
|
|
"""Request to preview purchase pricing."""
|
|
|
|
selection: PurchaseSelectionRequest
|
|
|
|
|
|
# ============ Tariff Purchase Schemas ============
|
|
|
|
|
|
class TariffPurchaseRequest(BaseModel):
|
|
"""Request to purchase a tariff."""
|
|
|
|
tariff_id: int = Field(..., description='Tariff ID to purchase')
|
|
period_days: int = Field(..., description='Period in days')
|
|
traffic_gb: int | None = Field(None, ge=0, description='Custom traffic in GB (for custom_traffic_enabled tariffs)')
|