92 lines
2.7 KiB
Python
92 lines
2.7 KiB
Python
"""Лимит устройств (HWID): база 10, каждое сверх — 50 ₽."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
BASE_DEVICES = 10
|
|
EXTRA_DEVICE_PRICE_RUB = 50.0
|
|
|
|
# Сколько устройств панель может зарегистрировать максимум (мягкий потолок).
|
|
# Реальная квота: BASE_DEVICES + оплаченные слоты; при превышении — DISABLE.
|
|
PANEL_SOFT_CAP = 100
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class DevicePack:
|
|
slots: int
|
|
price: float
|
|
|
|
@property
|
|
def id(self) -> str:
|
|
return f"dev_{self.slots}"
|
|
|
|
@property
|
|
def title(self) -> str:
|
|
return f"+{self.slots} устр."
|
|
|
|
def format_price(self) -> str:
|
|
if self.price == int(self.price):
|
|
return f"{int(self.price)} ₽/мес"
|
|
return f"{self.price:.2f} ₽/мес"
|
|
|
|
|
|
DEVICE_PACKS: tuple[DevicePack, ...] = tuple(
|
|
DevicePack(slots=n, price=EXTRA_DEVICE_PRICE_RUB * n) for n in (1, 2, 3, 5)
|
|
)
|
|
|
|
|
|
def get_device_pack(pack_id: str) -> DevicePack | None:
|
|
for p in DEVICE_PACKS:
|
|
if p.id == pack_id or str(p.slots) == pack_id:
|
|
return p
|
|
return None
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class DeviceUsage:
|
|
used: int
|
|
allowed: int
|
|
extra_paid: int
|
|
over_limit: bool
|
|
|
|
@property
|
|
def excess(self) -> int:
|
|
return max(0, self.used - self.allowed)
|
|
|
|
@property
|
|
def excess_price(self) -> float:
|
|
return self.excess * EXTRA_DEVICE_PRICE_RUB
|
|
|
|
def format_line(self) -> str:
|
|
line = (
|
|
f"📱 Устройства: <b>{self.used} / {self.allowed}</b> "
|
|
f"(база {BASE_DEVICES}"
|
|
+ (f" + {self.extra_paid} доп." if self.extra_paid else "")
|
|
+ ")"
|
|
)
|
|
if self.over_limit:
|
|
line += (
|
|
f"\n⚠️ Превышение: <b>+{self.excess}</b> · "
|
|
f"к оплате <b>{int(self.excess_price)} ₽</b>\n"
|
|
"Подписка отключена, пока не оплатишь лишние устройства "
|
|
f"({int(EXTRA_DEVICE_PRICE_RUB)} ₽ за каждое)."
|
|
)
|
|
return line
|
|
|
|
def as_dict(self) -> dict:
|
|
return {
|
|
"used": self.used,
|
|
"allowed": self.allowed,
|
|
"base": BASE_DEVICES,
|
|
"extra_paid": self.extra_paid,
|
|
"over_limit": self.over_limit,
|
|
"excess": self.excess,
|
|
"excess_price": self.excess_price,
|
|
"slot_price": EXTRA_DEVICE_PRICE_RUB,
|
|
}
|
|
|
|
|
|
def allowed_devices(extra_paid: int) -> int:
|
|
return BASE_DEVICES + max(0, int(extra_paid or 0))
|