Files
remnawave-bedolaga-telegram…/app/cabinet/schemas/tariffs.py
T
2026-01-16 08:50:08 +03:00

209 lines
7.3 KiB
Python

"""Schemas for tariff management in cabinet."""
from datetime import datetime
from typing import List, Optional, Dict
from pydantic import BaseModel, Field
class PeriodPrice(BaseModel):
"""Price for a specific period."""
days: int = Field(..., ge=1, description="Period in days")
price_kopeks: int = Field(..., ge=0, description="Price in kopeks")
price_rubles: Optional[float] = None
def __init__(self, **data):
super().__init__(**data)
if self.price_rubles is None:
self.price_rubles = self.price_kopeks / 100
class ServerTrafficLimit(BaseModel):
"""Traffic limit for a specific server."""
traffic_limit_gb: int = Field(0, ge=0, description="0 = use default tariff limit")
class ServerInfo(BaseModel):
"""Server info for tariff."""
id: int
squad_uuid: str
display_name: str
country_code: Optional[str] = None
is_selected: bool = False
traffic_limit_gb: Optional[int] = None # Индивидуальный лимит для сервера
class PromoGroupInfo(BaseModel):
"""Promo group info for tariff."""
id: int
name: str
is_selected: bool = False
class TariffListItem(BaseModel):
"""Tariff item for list view."""
id: int
name: str
description: Optional[str] = None
is_active: bool
is_trial_available: bool
is_daily: bool = False
daily_price_kopeks: int = 0
allow_traffic_topup: bool = True
traffic_limit_gb: int
device_limit: int
tier_level: int
display_order: int
servers_count: int
subscriptions_count: int
created_at: datetime
class Config:
from_attributes = True
class TariffListResponse(BaseModel):
"""Response with list of tariffs."""
tariffs: List[TariffListItem]
total: int
class TariffDetailResponse(BaseModel):
"""Detailed tariff response."""
id: int
name: str
description: Optional[str] = None
is_active: bool
is_trial_available: bool
allow_traffic_topup: bool = True
traffic_topup_enabled: bool = False
traffic_topup_packages: Dict[str, int] = Field(default_factory=dict)
max_topup_traffic_gb: int = 0
traffic_limit_gb: int
device_limit: int
device_price_kopeks: Optional[int] = None
max_device_limit: Optional[int] = None
tier_level: int
display_order: int
period_prices: List[PeriodPrice]
allowed_squads: List[str] # UUIDs
server_traffic_limits: Dict[str, ServerTrafficLimit] = Field(default_factory=dict) # {uuid: {traffic_limit_gb}}
servers: List[ServerInfo]
promo_groups: List[PromoGroupInfo]
subscriptions_count: int
# Произвольное количество дней
custom_days_enabled: bool = False
price_per_day_kopeks: int = 0
min_days: int = 1
max_days: int = 365
# Произвольный трафик при покупке
custom_traffic_enabled: bool = False
traffic_price_per_gb_kopeks: int = 0
min_traffic_gb: int = 1
max_traffic_gb: int = 1000
# Дневной тариф
is_daily: bool = False
daily_price_kopeks: int = 0
# Режим сброса трафика
traffic_reset_mode: Optional[str] = None # DAY, WEEK, MONTH, NO_RESET, None = глобальная настройка
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
class TariffCreateRequest(BaseModel):
"""Request to create a tariff."""
name: str = Field(..., min_length=1, max_length=255)
description: Optional[str] = None
is_active: bool = True
allow_traffic_topup: bool = True
traffic_topup_enabled: bool = False
traffic_topup_packages: Dict[str, int] = Field(default_factory=dict)
max_topup_traffic_gb: int = Field(0, ge=0)
traffic_limit_gb: int = Field(0, ge=0, description="0 = unlimited")
device_limit: int = Field(1, ge=1)
device_price_kopeks: Optional[int] = Field(None, ge=0)
max_device_limit: Optional[int] = Field(None, ge=1)
tier_level: int = Field(1, ge=1, le=10)
period_prices: List[PeriodPrice] = Field(default_factory=list)
allowed_squads: List[str] = Field(default_factory=list, description="Server UUIDs")
server_traffic_limits: Dict[str, ServerTrafficLimit] = Field(default_factory=dict, description="Per-server traffic limits")
promo_group_ids: List[int] = Field(default_factory=list)
# Произвольное количество дней
custom_days_enabled: bool = False
price_per_day_kopeks: int = Field(0, ge=0)
min_days: int = Field(1, ge=1)
max_days: int = Field(365, ge=1)
# Произвольный трафик при покупке
custom_traffic_enabled: bool = False
traffic_price_per_gb_kopeks: int = Field(0, ge=0)
min_traffic_gb: int = Field(1, ge=1)
max_traffic_gb: int = Field(1000, ge=1)
# Дневной тариф
is_daily: bool = False
daily_price_kopeks: int = Field(0, ge=0)
# Режим сброса трафика
traffic_reset_mode: Optional[str] = None # DAY, WEEK, MONTH, NO_RESET, None = глобальная настройка
class TariffUpdateRequest(BaseModel):
"""Request to update a tariff."""
name: Optional[str] = Field(None, min_length=1, max_length=255)
description: Optional[str] = None
is_active: Optional[bool] = None
allow_traffic_topup: Optional[bool] = None
traffic_topup_enabled: Optional[bool] = None
traffic_topup_packages: Optional[Dict[str, int]] = None
max_topup_traffic_gb: Optional[int] = Field(None, ge=0)
traffic_limit_gb: Optional[int] = Field(None, ge=0)
device_limit: Optional[int] = Field(None, ge=1)
device_price_kopeks: Optional[int] = Field(None, ge=0)
max_device_limit: Optional[int] = Field(None, ge=1)
tier_level: Optional[int] = Field(None, ge=1, le=10)
display_order: Optional[int] = Field(None, ge=0)
period_prices: Optional[List[PeriodPrice]] = None
allowed_squads: Optional[List[str]] = None
server_traffic_limits: Optional[Dict[str, ServerTrafficLimit]] = None
promo_group_ids: Optional[List[int]] = None
# Произвольное количество дней
custom_days_enabled: Optional[bool] = None
price_per_day_kopeks: Optional[int] = Field(None, ge=0)
min_days: Optional[int] = Field(None, ge=1)
max_days: Optional[int] = Field(None, ge=1)
# Произвольный трафик при покупке
custom_traffic_enabled: Optional[bool] = None
traffic_price_per_gb_kopeks: Optional[int] = Field(None, ge=0)
min_traffic_gb: Optional[int] = Field(None, ge=1)
max_traffic_gb: Optional[int] = Field(None, ge=1)
# Дневной тариф
is_daily: Optional[bool] = None
daily_price_kopeks: Optional[int] = Field(None, ge=0)
# Режим сброса трафика
traffic_reset_mode: Optional[str] = None # DAY, WEEK, MONTH, NO_RESET, None = глобальная настройка
class TariffToggleResponse(BaseModel):
"""Response after toggling tariff."""
id: int
is_active: bool
message: str
class TariffTrialResponse(BaseModel):
"""Response after setting trial tariff."""
id: int
is_trial_available: bool
message: str
class TariffStatsResponse(BaseModel):
"""Tariff statistics."""
id: int
name: str
subscriptions_count: int
active_subscriptions: int
trial_subscriptions: int
revenue_kopeks: int
revenue_rubles: float