rate configurator/ purchase of conditions/ fixed payments/ refactoring/ major improvements and fixes
This commit is contained in:
+6
-3
@@ -1,10 +1,12 @@
|
||||
from database import async_session_maker
|
||||
|
||||
from .settings.buttons_config import BUTTONS_CONFIG, load_buttons_config, update_buttons_config
|
||||
from .settings.notifications_config import NOTIFICATIONS_CONFIG, load_notifications_config, update_notifications_config
|
||||
from .settings.management_config import MANAGEMENT_CONFIG, load_management_config, update_management_config
|
||||
from .settings.modes_config import MODES_CONFIG, load_modes_config, update_modes_config
|
||||
from .settings.payments_config import PAYMENTS_CONFIG, load_payments_config, update_payments_config
|
||||
from .settings.money_config import MONEY_CONFIG, load_money_config, update_money_config
|
||||
from .settings.management_config import MANAGEGENT_CONFIG, load_management_config, update_management_config
|
||||
from .settings.notifications_config import NOTIFICATIONS_CONFIG, load_notifications_config, update_notifications_config
|
||||
from .settings.payments_config import PAYMENTS_CONFIG, load_payments_config, update_payments_config
|
||||
from .settings.tariffs_config import TARIFFS_CONFIG, load_tariffs_config, update_tariffs_config
|
||||
|
||||
|
||||
async def bootstrap() -> None:
|
||||
@@ -15,4 +17,5 @@ async def bootstrap() -> None:
|
||||
await load_payments_config(session)
|
||||
await load_money_config(session)
|
||||
await load_management_config(session)
|
||||
await load_tariffs_config(session)
|
||||
await session.commit()
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
PAYMENT_SYSTEMS_EXCLUDED = ("referral", "coupon", "cashback", "admin")
|
||||
PAYMENT_SYSTEMS_EXCLUDED = ("referral", "coupon", "cashback", "admin")
|
||||
|
||||
@@ -2,10 +2,12 @@ from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database.models import Setting
|
||||
|
||||
from ..defaults import DEFAULT_BUTTONS_CONFIG
|
||||
|
||||
|
||||
BUTTONS_CONFIG: dict[str, bool] = DEFAULT_BUTTONS_CONFIG.copy()
|
||||
BUTTONS_CONFIG.setdefault("ANDROID_TV_BUTTON_ENABLE", False)
|
||||
|
||||
|
||||
async def load_buttons_config(session: AsyncSession) -> None:
|
||||
@@ -15,6 +17,7 @@ async def load_buttons_config(session: AsyncSession) -> None:
|
||||
|
||||
if setting is None:
|
||||
buttons_config = DEFAULT_BUTTONS_CONFIG.copy()
|
||||
buttons_config.setdefault("ANDROID_TV_BUTTON_ENABLE", False)
|
||||
setting = Setting(
|
||||
key="BUTTONS_CONFIG",
|
||||
value=buttons_config,
|
||||
@@ -25,6 +28,7 @@ async def load_buttons_config(session: AsyncSession) -> None:
|
||||
stored = setting.value or {}
|
||||
buttons_config = DEFAULT_BUTTONS_CONFIG.copy()
|
||||
buttons_config.update(stored)
|
||||
buttons_config.setdefault("ANDROID_TV_BUTTON_ENABLE", False)
|
||||
setting.value = buttons_config
|
||||
|
||||
BUTTONS_CONFIG.clear()
|
||||
@@ -47,10 +51,11 @@ async def update_buttons_config(session: AsyncSession, new_values: dict[str, boo
|
||||
else:
|
||||
setting.value = new_values
|
||||
|
||||
await session.flush()
|
||||
await session.commit()
|
||||
|
||||
buttons_config = DEFAULT_BUTTONS_CONFIG.copy()
|
||||
buttons_config.update(new_values)
|
||||
buttons_config.setdefault("ANDROID_TV_BUTTON_ENABLE", False)
|
||||
|
||||
BUTTONS_CONFIG.clear()
|
||||
BUTTONS_CONFIG.update(buttons_config)
|
||||
|
||||
@@ -4,21 +4,23 @@ from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database.models import Setting
|
||||
|
||||
from ..defaults import DEFAULT_MANAGEMENT_CONFIG
|
||||
|
||||
|
||||
MANAGEGENT_CONFIG: dict[str, Any] = DEFAULT_MANAGEMENT_CONFIG.copy()
|
||||
MANAGEMENT_CONFIG: dict[str, Any] = DEFAULT_MANAGEMENT_CONFIG.copy()
|
||||
MANAGEMENT_SETTING_KEY = "MANAGEGENT_CONFIG"
|
||||
|
||||
|
||||
async def load_management_config(session: AsyncSession) -> None:
|
||||
stmt = select(Setting).where(Setting.key == "management")
|
||||
stmt = select(Setting).where(Setting.key == MANAGEMENT_SETTING_KEY)
|
||||
result = await session.execute(stmt)
|
||||
setting = result.scalar_one_or_none()
|
||||
|
||||
if setting is None:
|
||||
management_config = DEFAULT_MANAGEMENT_CONFIG.copy()
|
||||
setting = Setting(
|
||||
key="management",
|
||||
key=MANAGEMENT_SETTING_KEY,
|
||||
value=management_config,
|
||||
description="Конфигурация управления ботом",
|
||||
)
|
||||
@@ -29,19 +31,19 @@ async def load_management_config(session: AsyncSession) -> None:
|
||||
management_config.update(stored)
|
||||
setting.value = management_config
|
||||
|
||||
MANAGEGENT_CONFIG.clear()
|
||||
MANAGEGENT_CONFIG.update(management_config)
|
||||
MANAGEMENT_CONFIG.clear()
|
||||
MANAGEMENT_CONFIG.update(management_config)
|
||||
await session.flush()
|
||||
|
||||
|
||||
async def update_management_config(session: AsyncSession, new_values: dict[str, Any]) -> None:
|
||||
stmt = select(Setting).where(Setting.key == "management")
|
||||
stmt = select(Setting).where(Setting.key == MANAGEMENT_SETTING_KEY)
|
||||
result = await session.execute(stmt)
|
||||
setting = result.scalar_one_or_none()
|
||||
|
||||
if setting is None:
|
||||
setting = Setting(
|
||||
key="management",
|
||||
key=MANAGEMENT_SETTING_KEY,
|
||||
value=new_values,
|
||||
description="Конфигурация управления ботом",
|
||||
)
|
||||
@@ -49,11 +51,10 @@ async def update_management_config(session: AsyncSession, new_values: dict[str,
|
||||
else:
|
||||
setting.value = new_values
|
||||
|
||||
await session.flush()
|
||||
await session.commit()
|
||||
|
||||
management_config = DEFAULT_MANAGEMENT_CONFIG.copy()
|
||||
management_config.update(new_values)
|
||||
|
||||
MANAGEGENT_CONFIG.clear()
|
||||
MANAGEGENT_CONFIG.update(management_config)
|
||||
MANAGEMENT_CONFIG.clear()
|
||||
MANAGEMENT_CONFIG.update(management_config)
|
||||
|
||||
@@ -2,6 +2,7 @@ from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database.models import Setting
|
||||
|
||||
from ..defaults import DEFAULT_MODES_CONFIG
|
||||
|
||||
|
||||
@@ -47,7 +48,7 @@ async def update_modes_config(session: AsyncSession, new_values: dict[str, bool]
|
||||
else:
|
||||
setting.value = new_values
|
||||
|
||||
await session.flush()
|
||||
await session.commit()
|
||||
|
||||
modes_config = DEFAULT_MODES_CONFIG.copy()
|
||||
modes_config.update(new_values)
|
||||
|
||||
@@ -4,6 +4,7 @@ from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database.models import Setting
|
||||
|
||||
from ..defaults import DEFAULT_MONEY_CONFIG
|
||||
|
||||
|
||||
@@ -49,7 +50,7 @@ async def update_money_config(session: AsyncSession, new_values: dict[str, Any])
|
||||
else:
|
||||
setting.value = new_values
|
||||
|
||||
await session.flush()
|
||||
await session.commit()
|
||||
|
||||
money_config = DEFAULT_MONEY_CONFIG.copy()
|
||||
money_config.update(new_values)
|
||||
|
||||
@@ -4,6 +4,7 @@ from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database.models import Setting
|
||||
|
||||
from ..defaults import DEFAULT_NOTIFICATIONS_CONFIG
|
||||
|
||||
|
||||
@@ -49,7 +50,7 @@ async def update_notifications_config(session: AsyncSession, new_values: dict[st
|
||||
else:
|
||||
setting.value = new_values
|
||||
|
||||
await session.flush()
|
||||
await session.commit()
|
||||
|
||||
notifications_config = DEFAULT_NOTIFICATIONS_CONFIG.copy()
|
||||
notifications_config.update(new_values)
|
||||
|
||||
@@ -2,6 +2,7 @@ from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database.models import Setting
|
||||
|
||||
from ..defaults import DEFAULT_PAYMENTS_CONFIG
|
||||
|
||||
|
||||
@@ -47,7 +48,7 @@ async def update_payments_config(session: AsyncSession, new_values: dict[str, bo
|
||||
else:
|
||||
setting.value = new_values
|
||||
|
||||
await session.flush()
|
||||
await session.commit()
|
||||
|
||||
payments_config = DEFAULT_PAYMENTS_CONFIG.copy()
|
||||
payments_config.update(new_values)
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
from math import ceil
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database.models import Setting
|
||||
|
||||
|
||||
TARIFFS_CONFIG: dict[str, Any] = {
|
||||
"ALLOW_DOWNGRADE": True,
|
||||
"KEY_ADDONS_PACK_MODE": "all",
|
||||
}
|
||||
|
||||
|
||||
async def load_tariffs_config(session: AsyncSession) -> None:
|
||||
"""Загружает конфиг тарифов из БД."""
|
||||
stmt = select(Setting).where(Setting.key == "TARIFFS_CONFIG")
|
||||
result = await session.execute(stmt)
|
||||
setting = result.scalar_one_or_none()
|
||||
|
||||
if setting is None:
|
||||
tariffs_config = TARIFFS_CONFIG.copy()
|
||||
setting = Setting(
|
||||
key="TARIFFS_CONFIG",
|
||||
value=tariffs_config,
|
||||
description="Конфигурация тарифов",
|
||||
)
|
||||
session.add(setting)
|
||||
else:
|
||||
stored = setting.value or {}
|
||||
tariffs_config = TARIFFS_CONFIG.copy()
|
||||
tariffs_config.update(stored)
|
||||
setting.value = tariffs_config
|
||||
|
||||
TARIFFS_CONFIG.clear()
|
||||
TARIFFS_CONFIG.update(tariffs_config)
|
||||
await session.flush()
|
||||
|
||||
|
||||
async def update_tariffs_config(session: AsyncSession, new_values: dict[str, Any]) -> None:
|
||||
"""Обновляет конфиг тарифов."""
|
||||
stmt = select(Setting).where(Setting.key == "TARIFFS_CONFIG")
|
||||
result = await session.execute(stmt)
|
||||
setting = result.scalar_one_or_none()
|
||||
|
||||
if setting is None:
|
||||
setting = Setting(
|
||||
key="TARIFFS_CONFIG",
|
||||
value=new_values,
|
||||
description="Конфигурация тарифов",
|
||||
)
|
||||
session.add(setting)
|
||||
else:
|
||||
setting.value = new_values
|
||||
|
||||
await session.commit()
|
||||
|
||||
tariffs_config = TARIFFS_CONFIG.copy()
|
||||
tariffs_config.update(new_values)
|
||||
|
||||
TARIFFS_CONFIG.clear()
|
||||
TARIFFS_CONFIG.update(tariffs_config)
|
||||
|
||||
|
||||
def calc_extra_devices_price(tariff: dict[str, Any], device_limit: int) -> int:
|
||||
base_device_limit = int(tariff.get("device_limit") or 1)
|
||||
extra_devices = max(0, device_limit - base_device_limit)
|
||||
if extra_devices <= 0:
|
||||
return 0
|
||||
|
||||
step_price = int(tariff.get("device_step_rub") or 0)
|
||||
overrides = tariff.get("device_overrides") or {}
|
||||
|
||||
override_total = overrides.get(str(device_limit))
|
||||
if override_total is not None:
|
||||
return int(ceil(float(override_total)))
|
||||
|
||||
return int(ceil(extra_devices * step_price))
|
||||
|
||||
|
||||
def calc_extra_traffic_price(tariff: dict[str, Any], traffic_gb: int | None) -> int:
|
||||
if traffic_gb is None:
|
||||
return 0
|
||||
|
||||
traffic_limit_bytes = tariff.get("traffic_limit")
|
||||
if traffic_limit_bytes:
|
||||
base_traffic_gb = ceil(traffic_limit_bytes / 1024 / 1024 / 1024)
|
||||
else:
|
||||
base_traffic_gb = 0
|
||||
|
||||
step_price = int(tariff.get("traffic_step_rub") or 0)
|
||||
overrides = tariff.get("traffic_overrides") or {}
|
||||
|
||||
override_total = overrides.get(str(traffic_gb))
|
||||
if override_total is not None:
|
||||
return int(ceil(float(override_total)))
|
||||
|
||||
if traffic_gb == 0:
|
||||
return 0
|
||||
|
||||
extra_gb = max(0, traffic_gb - base_traffic_gb)
|
||||
if extra_gb <= 0:
|
||||
return 0
|
||||
|
||||
return int(ceil(extra_gb * step_price))
|
||||
|
||||
|
||||
def calculate_config_price(
|
||||
tariff: dict[str, Any],
|
||||
duration_days: int,
|
||||
device_limit: int,
|
||||
traffic_gb: int | None,
|
||||
) -> int:
|
||||
base_duration = int(tariff.get("duration_days") or 0) or duration_days or 30
|
||||
if base_duration <= 0:
|
||||
base_duration = duration_days or 30
|
||||
|
||||
base_price = int(tariff.get("price_rub") or 0)
|
||||
|
||||
duration_multiplier = duration_days / base_duration
|
||||
base_price_scaled = base_price * duration_multiplier
|
||||
|
||||
extra_devices_price = calc_extra_devices_price(tariff, device_limit)
|
||||
extra_traffic_price = calc_extra_traffic_price(tariff, traffic_gb)
|
||||
|
||||
total = base_price_scaled + extra_devices_price + extra_traffic_price
|
||||
return int(ceil(total))
|
||||
|
||||
|
||||
def normalize_tariff_config(tariff: dict[str, Any]) -> dict[str, Any]:
|
||||
raw_duration_options = tariff.get("duration_options") or []
|
||||
duration_options: list[int] = []
|
||||
for value in raw_duration_options:
|
||||
try:
|
||||
v = int(value)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if v > 0:
|
||||
duration_options.append(v)
|
||||
if not duration_options:
|
||||
base_duration = int(tariff.get("duration_days") or 0) or 30
|
||||
duration_options = [base_duration]
|
||||
duration_options = sorted(set(duration_options))
|
||||
|
||||
raw_device_options = tariff.get("device_options") or []
|
||||
device_options: list[int] = []
|
||||
for value in raw_device_options:
|
||||
try:
|
||||
v = int(value)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if v > 0:
|
||||
device_options.append(v)
|
||||
if not device_options:
|
||||
base_device_limit = int(tariff.get("device_limit") or 0)
|
||||
if base_device_limit > 0:
|
||||
device_options = [base_device_limit]
|
||||
else:
|
||||
device_options = []
|
||||
device_options = sorted(set(device_options))
|
||||
|
||||
raw_traffic_options = tariff.get("traffic_options_gb")
|
||||
traffic_options_gb: list[int] | None
|
||||
if raw_traffic_options is None:
|
||||
traffic_options_gb = None
|
||||
else:
|
||||
traffic_values: list[int] = []
|
||||
has_unlimited = False
|
||||
for value in raw_traffic_options:
|
||||
try:
|
||||
v = int(value)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if v == 0:
|
||||
has_unlimited = True
|
||||
elif v > 0:
|
||||
traffic_values.append(v)
|
||||
if not traffic_values and not has_unlimited:
|
||||
traffic_options_gb = None
|
||||
else:
|
||||
unique_values = sorted(set(traffic_values))
|
||||
if has_unlimited:
|
||||
traffic_options_gb = [0] + unique_values
|
||||
else:
|
||||
traffic_options_gb = unique_values
|
||||
|
||||
return {
|
||||
"duration_options": duration_options,
|
||||
"device_options": device_options,
|
||||
"traffic_options_gb": traffic_options_gb,
|
||||
}
|
||||
Reference in New Issue
Block a user