mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-04-28 16:50:08 +00:00
Merge pull request #376 from Fr1ngg/revert-375-bedolaga/add-discount-toggle-for-additional-services-pqdau3
Revert "Add localization for promo group add-on discount toggle"
This commit is contained in:
@@ -60,7 +60,6 @@ async def create_promo_group(
|
||||
device_discount_percent: int,
|
||||
period_discounts: Optional[Dict[int, int]] = None,
|
||||
auto_assign_total_spent_kopeks: Optional[int] = None,
|
||||
apply_discounts_to_addons: bool = False,
|
||||
) -> PromoGroup:
|
||||
normalized_period_discounts = _normalize_period_discounts(period_discounts)
|
||||
|
||||
@@ -77,7 +76,6 @@ async def create_promo_group(
|
||||
device_discount_percent=max(0, min(100, device_discount_percent)),
|
||||
period_discounts=normalized_period_discounts or None,
|
||||
auto_assign_total_spent_kopeks=auto_assign_total_spent_kopeks,
|
||||
apply_discounts_to_addons=bool(apply_discounts_to_addons),
|
||||
is_default=False,
|
||||
)
|
||||
|
||||
@@ -86,15 +84,13 @@ async def create_promo_group(
|
||||
await db.refresh(promo_group)
|
||||
|
||||
logger.info(
|
||||
"Создана промогруппа '%s' с скидками (servers=%s%%, traffic=%s%%, devices=%s%%, periods=%s) и порогом автоприсвоения %s₽,"
|
||||
" скидки на доп. услуги: %s",
|
||||
"Создана промогруппа '%s' с скидками (servers=%s%%, traffic=%s%%, devices=%s%%, periods=%s) и порогом автоприсвоения %s₽",
|
||||
promo_group.name,
|
||||
promo_group.server_discount_percent,
|
||||
promo_group.traffic_discount_percent,
|
||||
promo_group.device_discount_percent,
|
||||
normalized_period_discounts,
|
||||
(auto_assign_total_spent_kopeks or 0) / 100,
|
||||
"включены" if promo_group.apply_discounts_to_addons else "выключены",
|
||||
)
|
||||
|
||||
return promo_group
|
||||
@@ -110,7 +106,6 @@ async def update_promo_group(
|
||||
device_discount_percent: Optional[int] = None,
|
||||
period_discounts: Optional[Dict[int, int]] = None,
|
||||
auto_assign_total_spent_kopeks: Optional[int] = None,
|
||||
apply_discounts_to_addons: Optional[bool] = None,
|
||||
) -> PromoGroup:
|
||||
if name is not None:
|
||||
group.name = name.strip()
|
||||
@@ -125,8 +120,6 @@ async def update_promo_group(
|
||||
group.period_discounts = normalized_period_discounts or None
|
||||
if auto_assign_total_spent_kopeks is not None:
|
||||
group.auto_assign_total_spent_kopeks = max(0, auto_assign_total_spent_kopeks)
|
||||
if apply_discounts_to_addons is not None:
|
||||
group.apply_discounts_to_addons = bool(apply_discounts_to_addons)
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(group)
|
||||
|
||||
@@ -504,35 +504,15 @@ def _get_discount_percent(
|
||||
category: str,
|
||||
*,
|
||||
period_days: Optional[int] = None,
|
||||
for_addon: bool = False,
|
||||
) -> int:
|
||||
effective_group = promo_group
|
||||
|
||||
if user is not None and effective_group is None:
|
||||
effective_group = getattr(user, "promo_group", None)
|
||||
|
||||
if for_addon:
|
||||
if user is not None:
|
||||
try:
|
||||
return user.get_addon_discount(category, period_days)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if effective_group is not None:
|
||||
try:
|
||||
return effective_group.get_addon_discount_percent(category, period_days)
|
||||
except AttributeError:
|
||||
return 0
|
||||
return 0
|
||||
|
||||
if user is not None:
|
||||
try:
|
||||
return user.get_promo_discount(category, period_days)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if effective_group is not None:
|
||||
return effective_group.get_discount_percent(category, period_days)
|
||||
if promo_group is not None:
|
||||
return promo_group.get_discount_percent(category, period_days)
|
||||
|
||||
return 0
|
||||
|
||||
@@ -872,7 +852,6 @@ async def calculate_addon_cost_for_remaining_period(
|
||||
promo_group,
|
||||
"traffic",
|
||||
period_days=period_hint_days,
|
||||
for_addon=True,
|
||||
)
|
||||
traffic_discount_per_month = traffic_price_per_month * traffic_discount_percent // 100
|
||||
discounted_traffic_per_month = traffic_price_per_month - traffic_discount_per_month
|
||||
@@ -894,7 +873,6 @@ async def calculate_addon_cost_for_remaining_period(
|
||||
promo_group,
|
||||
"devices",
|
||||
period_days=period_hint_days,
|
||||
for_addon=True,
|
||||
)
|
||||
devices_discount_per_month = devices_price_per_month * devices_discount_percent // 100
|
||||
discounted_devices_per_month = devices_price_per_month - devices_discount_per_month
|
||||
@@ -924,7 +902,6 @@ async def calculate_addon_cost_for_remaining_period(
|
||||
promo_group,
|
||||
"servers",
|
||||
period_days=period_hint_days,
|
||||
for_addon=True,
|
||||
)
|
||||
server_discount_per_month = server_price_per_month * servers_discount_percent // 100
|
||||
discounted_server_per_month = server_price_per_month - server_discount_per_month
|
||||
|
||||
@@ -292,7 +292,6 @@ class PromoGroup(Base):
|
||||
device_discount_percent = Column(Integer, nullable=False, default=0)
|
||||
period_discounts = Column(JSON, nullable=True, default=dict)
|
||||
auto_assign_total_spent_kopeks = Column(Integer, nullable=True, default=None)
|
||||
apply_discounts_to_addons = Column(Boolean, nullable=False, default=False)
|
||||
is_default = Column(Boolean, nullable=False, default=False)
|
||||
created_at = Column(DateTime, default=func.now())
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
|
||||
@@ -360,15 +359,6 @@ class PromoGroup(Base):
|
||||
|
||||
return max(0, min(100, percent))
|
||||
|
||||
def get_addon_discount_percent(
|
||||
self,
|
||||
category: str,
|
||||
period_days: Optional[int] = None,
|
||||
) -> int:
|
||||
if not self.apply_discounts_to_addons:
|
||||
return 0
|
||||
return self.get_discount_percent(category, period_days)
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
@@ -418,15 +408,7 @@ class User(Base):
|
||||
if not self.promo_group:
|
||||
return 0
|
||||
return self.promo_group.get_discount_percent(category, period_days)
|
||||
|
||||
def get_addon_discount(self, category: str, period_days: Optional[int] = None) -> int:
|
||||
if not self.promo_group:
|
||||
return 0
|
||||
try:
|
||||
return self.promo_group.get_addon_discount_percent(category, period_days)
|
||||
except AttributeError:
|
||||
return 0
|
||||
|
||||
|
||||
def add_balance(self, kopeks: int) -> None:
|
||||
self.balance_kopeks += kopeks
|
||||
|
||||
|
||||
@@ -931,39 +931,6 @@ async def ensure_promo_groups_setup():
|
||||
"Добавлена колонка promo_groups.auto_assign_total_spent_kopeks"
|
||||
)
|
||||
|
||||
addon_discount_column_exists = await check_column_exists(
|
||||
"promo_groups", "apply_discounts_to_addons"
|
||||
)
|
||||
|
||||
if not addon_discount_column_exists:
|
||||
if db_type == "sqlite":
|
||||
await conn.execute(
|
||||
text(
|
||||
"ALTER TABLE promo_groups ADD COLUMN apply_discounts_to_addons INTEGER NOT NULL DEFAULT 0"
|
||||
)
|
||||
)
|
||||
elif db_type == "postgresql":
|
||||
await conn.execute(
|
||||
text(
|
||||
"ALTER TABLE promo_groups ADD COLUMN apply_discounts_to_addons BOOLEAN NOT NULL DEFAULT FALSE"
|
||||
)
|
||||
)
|
||||
elif db_type == "mysql":
|
||||
await conn.execute(
|
||||
text(
|
||||
"ALTER TABLE promo_groups ADD COLUMN apply_discounts_to_addons TINYINT(1) NOT NULL DEFAULT 0"
|
||||
)
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"Неподдерживаемый тип БД для promo_groups.apply_discounts_to_addons: {db_type}"
|
||||
)
|
||||
return False
|
||||
|
||||
logger.info(
|
||||
"Добавлена колонка promo_groups.apply_discounts_to_addons"
|
||||
)
|
||||
|
||||
column_exists = await check_column_exists("users", "promo_group_id")
|
||||
|
||||
if not column_exists:
|
||||
@@ -2027,7 +1994,6 @@ async def check_migration_status():
|
||||
"users_promo_group_column": False,
|
||||
"promo_groups_period_discounts_column": False,
|
||||
"promo_groups_auto_assign_column": False,
|
||||
"promo_groups_addon_discount_column": False,
|
||||
"users_auto_promo_group_assigned_column": False,
|
||||
"subscription_crypto_link_column": False,
|
||||
}
|
||||
@@ -2045,7 +2011,6 @@ async def check_migration_status():
|
||||
status["users_promo_group_column"] = await check_column_exists('users', 'promo_group_id')
|
||||
status["promo_groups_period_discounts_column"] = await check_column_exists('promo_groups', 'period_discounts')
|
||||
status["promo_groups_auto_assign_column"] = await check_column_exists('promo_groups', 'auto_assign_total_spent_kopeks')
|
||||
status["promo_groups_addon_discount_column"] = await check_column_exists('promo_groups', 'apply_discounts_to_addons')
|
||||
status["users_auto_promo_group_assigned_column"] = await check_column_exists('users', 'auto_promo_group_assigned')
|
||||
status["subscription_crypto_link_column"] = await check_column_exists('subscriptions', 'subscription_crypto_link')
|
||||
|
||||
@@ -2083,7 +2048,6 @@ async def check_migration_status():
|
||||
"users_promo_group_column": "Колонка promo_group_id у пользователей",
|
||||
"promo_groups_period_discounts_column": "Колонка period_discounts у промо-групп",
|
||||
"promo_groups_auto_assign_column": "Колонка auto_assign_total_spent_kopeks у промо-групп",
|
||||
"promo_groups_addon_discount_column": "Колонка apply_discounts_to_addons у промо-групп",
|
||||
"users_auto_promo_group_assigned_column": "Флаг автоназначения промогруппы у пользователей",
|
||||
"subscription_crypto_link_column": "Колонка subscription_crypto_link в subscriptions",
|
||||
}
|
||||
|
||||
@@ -39,23 +39,6 @@ def _format_discount_line(texts, group) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _format_addon_discount_status(texts, enabled: bool) -> str:
|
||||
return (
|
||||
texts.t("ADMIN_PROMO_GROUP_ADDON_DISCOUNT_STATUS_ON", "включены")
|
||||
if enabled
|
||||
else texts.t("ADMIN_PROMO_GROUP_ADDON_DISCOUNT_STATUS_OFF", "отключены")
|
||||
)
|
||||
|
||||
|
||||
def _format_addon_discount_line(texts, group: PromoGroup) -> str:
|
||||
enabled = bool(getattr(group, "apply_discounts_to_addons", False))
|
||||
status = _format_addon_discount_status(texts, enabled)
|
||||
return texts.t(
|
||||
"ADMIN_PROMO_GROUP_ADDON_DISCOUNT_LINE",
|
||||
"Скидки на доп. услуги: {status}",
|
||||
).format(status=status)
|
||||
|
||||
|
||||
def _normalize_periods_dict(raw: Optional[Dict]) -> Dict[int, int]:
|
||||
if not raw or not isinstance(raw, dict):
|
||||
return {}
|
||||
@@ -240,39 +223,6 @@ def _parse_auto_assign_threshold_input(value: str) -> int:
|
||||
return max(0, kopeks)
|
||||
|
||||
|
||||
def _parse_boolean_choice(value: str) -> bool:
|
||||
cleaned = (value or "").strip().lower()
|
||||
|
||||
if cleaned in {"1", "да", "yes", "y", "true", "on", "+"}:
|
||||
return True
|
||||
if cleaned in {"0", "нет", "no", "n", "false", "off", "-"}:
|
||||
return False
|
||||
|
||||
raise ValueError
|
||||
|
||||
|
||||
async def _prompt_for_addon_discount_toggle(
|
||||
message: types.Message,
|
||||
state: FSMContext,
|
||||
prompt_key: str,
|
||||
default_text: str,
|
||||
*,
|
||||
current_value: Optional[bool] = None,
|
||||
):
|
||||
data = await state.get_data()
|
||||
texts = get_texts(data.get("language", "ru"))
|
||||
prompt_text = texts.t(prompt_key, default_text)
|
||||
|
||||
if current_value is not None:
|
||||
status = _format_addon_discount_status(texts, current_value)
|
||||
try:
|
||||
prompt_text = prompt_text.format(current=status)
|
||||
except KeyError:
|
||||
prompt_text = f"{prompt_text} ({status})"
|
||||
|
||||
await message.answer(prompt_text)
|
||||
|
||||
|
||||
async def _prompt_for_auto_assign_threshold(
|
||||
message: types.Message,
|
||||
state: FSMContext,
|
||||
@@ -307,7 +257,6 @@ def _build_edit_menu_content(
|
||||
lines = [
|
||||
header,
|
||||
_format_discount_line(texts, group),
|
||||
_format_addon_discount_line(texts, group),
|
||||
_format_auto_assign_line(texts, group),
|
||||
]
|
||||
|
||||
@@ -360,15 +309,6 @@ def _build_edit_menu_content(
|
||||
callback_data=f"promo_group_edit_field_{group.id}_devices",
|
||||
)
|
||||
],
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text=texts.t(
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_ADDONS",
|
||||
"🎁 Скидки на доп. услуги",
|
||||
),
|
||||
callback_data=f"promo_group_edit_field_{group.id}_addons",
|
||||
)
|
||||
],
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text=texts.t(
|
||||
@@ -459,7 +399,6 @@ async def show_promo_groups_menu(
|
||||
group_lines = [
|
||||
f"{'⭐' if group.is_default else '🎯'} <b>{group.name}</b>{default_suffix}",
|
||||
_format_discount_line(texts, group),
|
||||
_format_addon_discount_line(texts, group),
|
||||
_format_auto_assign_line(texts, group),
|
||||
texts.t(
|
||||
"ADMIN_PROMO_GROUPS_MEMBERS_COUNT",
|
||||
@@ -535,7 +474,6 @@ async def show_promo_group_details(
|
||||
"💳 <b>Промогруппа:</b> {name}",
|
||||
).format(name=group.name),
|
||||
_format_discount_line(texts, group),
|
||||
_format_addon_discount_line(texts, group),
|
||||
_format_auto_assign_line(texts, group),
|
||||
texts.t(
|
||||
"ADMIN_PROMO_GROUP_DETAILS_MEMBERS",
|
||||
@@ -769,41 +707,6 @@ async def process_create_group_auto_assign(
|
||||
)
|
||||
return
|
||||
|
||||
await state.update_data(new_group_auto_assign=auto_assign_kopeks)
|
||||
await state.set_state(AdminStates.creating_promo_group_addon_discount)
|
||||
|
||||
await _prompt_for_addon_discount_toggle(
|
||||
message,
|
||||
state,
|
||||
"ADMIN_PROMO_GROUP_CREATE_ADDON_DISCOUNT_PROMPT",
|
||||
"Включить скидки на докупку доп. услуг? (да/нет)",
|
||||
)
|
||||
|
||||
|
||||
@admin_required
|
||||
@error_handler
|
||||
async def process_create_group_addon_discount(
|
||||
message: types.Message,
|
||||
state: FSMContext,
|
||||
db_user,
|
||||
db: AsyncSession,
|
||||
):
|
||||
data = await state.get_data()
|
||||
texts = get_texts(data.get("language", db_user.language))
|
||||
|
||||
try:
|
||||
addons_enabled = _parse_boolean_choice(message.text)
|
||||
except ValueError:
|
||||
await message.answer(
|
||||
texts.t(
|
||||
"ADMIN_PROMO_GROUP_INVALID_ADDON_DISCOUNT",
|
||||
"Введите «да» или «нет» для включения скидок на доп. услуги.",
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
auto_assign_kopeks = data.get("new_group_auto_assign")
|
||||
|
||||
try:
|
||||
group = await create_promo_group(
|
||||
db,
|
||||
@@ -813,7 +716,6 @@ async def process_create_group_addon_discount(
|
||||
device_discount_percent=data["new_group_devices"],
|
||||
period_discounts=data.get("new_group_period_discounts"),
|
||||
auto_assign_total_spent_kopeks=auto_assign_kopeks,
|
||||
apply_discounts_to_addons=addons_enabled,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Не удалось создать промогруппу: {e}")
|
||||
@@ -917,16 +819,6 @@ async def prompt_edit_promo_group_field(
|
||||
"ADMIN_PROMO_GROUP_EDIT_DEVICES_PROMPT",
|
||||
"Введите новую скидку на устройства (текущее значение: {current}%):",
|
||||
).format(current=group.device_discount_percent)
|
||||
elif field == "addons":
|
||||
await state.set_state(AdminStates.editing_promo_group_addon_discount)
|
||||
prompt = texts.t(
|
||||
"ADMIN_PROMO_GROUP_EDIT_ADDONS_PROMPT",
|
||||
"Включить скидки на доп. услуги? Текущее значение: {current}.",
|
||||
).format(
|
||||
current=_format_addon_discount_status(
|
||||
texts, getattr(group, "apply_discounts_to_addons", False)
|
||||
)
|
||||
)
|
||||
elif field == "periods":
|
||||
await state.set_state(AdminStates.editing_promo_group_period_discount)
|
||||
current_discounts = _normalize_periods_dict(getattr(group, "period_discounts", None))
|
||||
@@ -1171,50 +1063,6 @@ async def process_edit_group_auto_assign(
|
||||
)
|
||||
|
||||
|
||||
@admin_required
|
||||
@error_handler
|
||||
async def process_edit_group_addon_discount(
|
||||
message: types.Message,
|
||||
state: FSMContext,
|
||||
db_user,
|
||||
db: AsyncSession,
|
||||
):
|
||||
data = await state.get_data()
|
||||
texts = get_texts(data.get("language", db_user.language))
|
||||
|
||||
try:
|
||||
addons_enabled = _parse_boolean_choice(message.text)
|
||||
except ValueError:
|
||||
await message.answer(
|
||||
texts.t(
|
||||
"ADMIN_PROMO_GROUP_INVALID_ADDON_DISCOUNT",
|
||||
"Введите «да» или «нет» для включения скидок на доп. услуги.",
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
group = await get_promo_group_by_id(db, data.get("edit_group_id"))
|
||||
if not group:
|
||||
await message.answer("❌ Промогруппа не найдена")
|
||||
await state.clear()
|
||||
return
|
||||
|
||||
group = await update_promo_group(
|
||||
db,
|
||||
group,
|
||||
apply_discounts_to_addons=addons_enabled,
|
||||
)
|
||||
await state.set_state(AdminStates.editing_promo_group_menu)
|
||||
|
||||
await _send_edit_menu_after_update(
|
||||
message,
|
||||
texts,
|
||||
group,
|
||||
data.get("language", db_user.language),
|
||||
texts.t("ADMIN_PROMO_GROUP_UPDATED", "Промогруппа «{name}» обновлена.").format(name=group.name),
|
||||
)
|
||||
|
||||
|
||||
@admin_required
|
||||
@error_handler
|
||||
async def show_promo_group_members(
|
||||
@@ -1391,10 +1239,6 @@ def register_handlers(dp: Dispatcher):
|
||||
process_create_group_auto_assign,
|
||||
AdminStates.creating_promo_group_auto_assign,
|
||||
)
|
||||
dp.message.register(
|
||||
process_create_group_addon_discount,
|
||||
AdminStates.creating_promo_group_addon_discount,
|
||||
)
|
||||
|
||||
dp.message.register(process_edit_group_name, AdminStates.editing_promo_group_name)
|
||||
dp.message.register(
|
||||
@@ -1417,7 +1261,3 @@ def register_handlers(dp: Dispatcher):
|
||||
process_edit_group_auto_assign,
|
||||
AdminStates.editing_promo_group_auto_assign,
|
||||
)
|
||||
dp.message.register(
|
||||
process_edit_group_addon_discount,
|
||||
AdminStates.editing_promo_group_addon_discount,
|
||||
)
|
||||
|
||||
@@ -1142,12 +1142,11 @@ async def handle_add_countries(
|
||||
await callback.message.edit_text(
|
||||
text,
|
||||
reply_markup=get_manage_countries_keyboard(
|
||||
countries,
|
||||
current_countries.copy(),
|
||||
current_countries,
|
||||
countries,
|
||||
current_countries.copy(),
|
||||
current_countries,
|
||||
db_user.language,
|
||||
subscription.end_date,
|
||||
getattr(db_user, "promo_group", None),
|
||||
subscription.end_date
|
||||
),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
@@ -1233,11 +1232,10 @@ async def handle_manage_country(
|
||||
await callback.message.edit_reply_markup(
|
||||
reply_markup=get_manage_countries_keyboard(
|
||||
countries,
|
||||
current_selected,
|
||||
subscription.connected_squads,
|
||||
current_selected,
|
||||
subscription.connected_squads,
|
||||
db_user.language,
|
||||
subscription.end_date,
|
||||
getattr(db_user, "promo_group", None),
|
||||
subscription.end_date
|
||||
)
|
||||
)
|
||||
logger.info(f"✅ Клавиатура обновлена")
|
||||
@@ -1253,11 +1251,7 @@ async def apply_countries_changes(
|
||||
db: AsyncSession,
|
||||
state: FSMContext
|
||||
):
|
||||
from app.utils.pricing_utils import (
|
||||
get_remaining_months,
|
||||
calculate_prorated_price,
|
||||
apply_percentage_discount,
|
||||
)
|
||||
from app.utils.pricing_utils import get_remaining_months, calculate_prorated_price
|
||||
|
||||
logger.info(f"🔧 Применение изменений стран")
|
||||
|
||||
@@ -1294,50 +1288,27 @@ async def apply_countries_changes(
|
||||
logger.info(f"🔧 Добавлено: {added}, Удалено: {removed}")
|
||||
|
||||
months_to_pay = get_remaining_months(subscription.end_date)
|
||||
|
||||
promo_group = getattr(db_user, "promo_group", None)
|
||||
addons_enabled = bool(promo_group and getattr(promo_group, "apply_discounts_to_addons", False))
|
||||
period_hint_days = months_to_pay * 30 if months_to_pay > 0 else None
|
||||
servers_discount_percent = 0
|
||||
if addons_enabled:
|
||||
try:
|
||||
servers_discount_percent = promo_group.get_addon_discount_percent(
|
||||
"servers",
|
||||
period_days=period_hint_days,
|
||||
)
|
||||
except AttributeError:
|
||||
servers_discount_percent = 0
|
||||
|
||||
from app.utils.pricing_utils import apply_percentage_discount
|
||||
|
||||
|
||||
cost_per_month = 0
|
||||
added_names = []
|
||||
removed_names = []
|
||||
|
||||
|
||||
added_server_prices = []
|
||||
|
||||
|
||||
for country in countries:
|
||||
if country['uuid'] in added:
|
||||
server_price_per_month = country['price_kopeks']
|
||||
discounted_per_month, _ = apply_percentage_discount(
|
||||
server_price_per_month,
|
||||
servers_discount_percent,
|
||||
)
|
||||
cost_per_month += discounted_per_month
|
||||
cost_per_month += server_price_per_month
|
||||
added_names.append(country['name'])
|
||||
if country['uuid'] in removed:
|
||||
removed_names.append(country['name'])
|
||||
|
||||
|
||||
total_cost, charged_months = calculate_prorated_price(cost_per_month, subscription.end_date)
|
||||
|
||||
|
||||
for country in countries:
|
||||
if country['uuid'] in added:
|
||||
server_price_per_month = country['price_kopeks']
|
||||
discounted_per_month, _ = apply_percentage_discount(
|
||||
server_price_per_month,
|
||||
servers_discount_percent,
|
||||
)
|
||||
server_total_price = discounted_per_month * charged_months
|
||||
server_total_price = server_price_per_month * charged_months
|
||||
added_server_prices.append(server_total_price)
|
||||
|
||||
logger.info(f"Стоимость новых серверов: {cost_per_month/100}₽/мес × {charged_months} мес = {total_cost/100}₽")
|
||||
@@ -1483,11 +1454,7 @@ async def handle_add_traffic(
|
||||
f"📈 <b>Добавить трафик к подписке</b>\n\n"
|
||||
f"Текущий лимит: {texts.format_traffic(current_traffic)}\n"
|
||||
f"Выберите дополнительный трафик:",
|
||||
reply_markup=get_add_traffic_keyboard(
|
||||
db_user.language,
|
||||
subscription.end_date,
|
||||
getattr(db_user, "promo_group", None),
|
||||
),
|
||||
reply_markup=get_add_traffic_keyboard(db_user.language, subscription.end_date),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
@@ -1515,12 +1482,7 @@ async def handle_change_devices(
|
||||
f"💡 <b>Важно:</b>\n"
|
||||
f"• При увеличении - доплата пропорционально оставшемуся времени\n"
|
||||
f"• При уменьшении - возврат средств не производится",
|
||||
reply_markup=get_change_devices_keyboard(
|
||||
current_devices,
|
||||
db_user.language,
|
||||
subscription.end_date,
|
||||
getattr(db_user, "promo_group", None),
|
||||
),
|
||||
reply_markup=get_change_devices_keyboard(current_devices, db_user.language, subscription.end_date),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
@@ -1531,11 +1493,7 @@ async def confirm_change_devices(
|
||||
db_user: User,
|
||||
db: AsyncSession
|
||||
):
|
||||
from app.utils.pricing_utils import (
|
||||
get_remaining_months,
|
||||
calculate_prorated_price,
|
||||
apply_percentage_discount,
|
||||
)
|
||||
from app.utils.pricing_utils import get_remaining_months, calculate_prorated_price
|
||||
|
||||
new_devices_count = int(callback.data.split('_')[2])
|
||||
texts = get_texts(db_user.language)
|
||||
@@ -1566,31 +1524,7 @@ async def confirm_change_devices(
|
||||
chargeable_devices = additional_devices
|
||||
|
||||
devices_price_per_month = chargeable_devices * settings.PRICE_PER_DEVICE
|
||||
|
||||
promo_group = getattr(db_user, "promo_group", None)
|
||||
addons_enabled = bool(promo_group and getattr(promo_group, "apply_discounts_to_addons", False))
|
||||
period_hint_days = None
|
||||
if subscription.end_date:
|
||||
months_remaining = get_remaining_months(subscription.end_date)
|
||||
period_hint_days = months_remaining * 30 if months_remaining > 0 else None
|
||||
devices_discount_percent = 0
|
||||
if addons_enabled:
|
||||
try:
|
||||
devices_discount_percent = promo_group.get_addon_discount_percent(
|
||||
"devices",
|
||||
period_days=period_hint_days,
|
||||
)
|
||||
except AttributeError:
|
||||
devices_discount_percent = 0
|
||||
|
||||
discounted_per_month, _ = apply_percentage_discount(
|
||||
devices_price_per_month,
|
||||
devices_discount_percent,
|
||||
)
|
||||
price, charged_months = calculate_prorated_price(
|
||||
discounted_per_month,
|
||||
subscription.end_date,
|
||||
)
|
||||
price, charged_months = calculate_prorated_price(devices_price_per_month, subscription.end_date)
|
||||
|
||||
if price > 0 and db_user.balance_kopeks < price:
|
||||
missing_kopeks = price - db_user.balance_kopeks
|
||||
@@ -2205,45 +2139,9 @@ async def confirm_add_devices(
|
||||
return
|
||||
|
||||
devices_price_per_month = devices_count * settings.PRICE_PER_DEVICE
|
||||
|
||||
promo_group = getattr(db_user, "promo_group", None)
|
||||
addons_enabled = bool(promo_group and getattr(promo_group, "apply_discounts_to_addons", False))
|
||||
period_hint_days = None
|
||||
if subscription.end_date:
|
||||
months_remaining = get_remaining_months(subscription.end_date)
|
||||
period_hint_days = months_remaining * 30 if months_remaining > 0 else None
|
||||
devices_discount_percent = 0
|
||||
if addons_enabled:
|
||||
try:
|
||||
devices_discount_percent = promo_group.get_addon_discount_percent(
|
||||
"devices",
|
||||
period_days=period_hint_days,
|
||||
)
|
||||
except AttributeError:
|
||||
devices_discount_percent = 0
|
||||
|
||||
discounted_per_month, _ = apply_percentage_discount(
|
||||
devices_price_per_month,
|
||||
devices_discount_percent,
|
||||
)
|
||||
price, charged_months = calculate_prorated_price(
|
||||
discounted_per_month,
|
||||
subscription.end_date,
|
||||
)
|
||||
|
||||
if devices_discount_percent > 0:
|
||||
logger.info(
|
||||
"Добавление %s устройств: %s₽/мес → %s₽/мес × %s мес = %s₽",
|
||||
devices_count,
|
||||
devices_price_per_month / 100,
|
||||
discounted_per_month / 100,
|
||||
charged_months,
|
||||
price / 100,
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
f"Добавление {devices_count} устройств: {devices_price_per_month/100}₽/мес × {charged_months} мес = {price/100}₽"
|
||||
)
|
||||
price, charged_months = calculate_prorated_price(devices_price_per_month, subscription.end_date)
|
||||
|
||||
logger.info(f"Добавление {devices_count} устройств: {devices_price_per_month/100}₽/мес × {charged_months} мес = {price/100}₽")
|
||||
|
||||
if db_user.balance_kopeks < price:
|
||||
missing_kopeks = price - db_user.balance_kopeks
|
||||
@@ -3604,23 +3502,11 @@ async def add_traffic(
|
||||
subscription = db_user.subscription
|
||||
|
||||
price = settings.get_traffic_price(traffic_gb)
|
||||
|
||||
|
||||
if price == 0 and traffic_gb != 0:
|
||||
await callback.answer("⚠️ Цена для этого пакета не настроена", show_alert=True)
|
||||
return
|
||||
|
||||
promo_group = getattr(db_user, "promo_group", None)
|
||||
addons_enabled = bool(promo_group and getattr(promo_group, "apply_discounts_to_addons", False))
|
||||
if addons_enabled:
|
||||
from app.utils.pricing_utils import apply_percentage_discount
|
||||
|
||||
try:
|
||||
discount_percent = promo_group.get_addon_discount_percent("traffic")
|
||||
except AttributeError:
|
||||
discount_percent = 0
|
||||
|
||||
price, _ = apply_percentage_discount(price, discount_percent)
|
||||
|
||||
|
||||
if db_user.balance_kopeks < price:
|
||||
missing_kopeks = price - db_user.balance_kopeks
|
||||
message_text = texts.t(
|
||||
@@ -4039,34 +3925,11 @@ async def handle_add_country_to_subscription(
|
||||
selected_countries.append(country_uuid)
|
||||
logger.info(f"🔍 Добавлена страна: {country_uuid}")
|
||||
|
||||
from app.utils.pricing_utils import apply_percentage_discount
|
||||
|
||||
subscription = db_user.subscription
|
||||
months_multiplier = get_remaining_months(subscription.end_date) if subscription else 1
|
||||
period_hint_days = months_multiplier * 30 if months_multiplier > 0 else None
|
||||
promo_group = getattr(db_user, "promo_group", None)
|
||||
addons_enabled = bool(promo_group and getattr(promo_group, "apply_discounts_to_addons", False))
|
||||
servers_discount_percent = 0
|
||||
if addons_enabled:
|
||||
try:
|
||||
servers_discount_percent = promo_group.get_addon_discount_percent(
|
||||
"servers",
|
||||
period_days=period_hint_days,
|
||||
)
|
||||
except AttributeError:
|
||||
servers_discount_percent = 0
|
||||
|
||||
total_price = 0
|
||||
for country in countries:
|
||||
if country['uuid'] in selected_countries and country['uuid'] not in db_user.subscription.connected_squads:
|
||||
price_per_month = country['price_kopeks']
|
||||
discounted_per_month, discount_per_month = apply_percentage_discount(
|
||||
price_per_month,
|
||||
servers_discount_percent,
|
||||
)
|
||||
discounted_total = discounted_per_month * months_multiplier
|
||||
total_price += discounted_total
|
||||
|
||||
total_price += country['price_kopeks']
|
||||
|
||||
data['countries'] = selected_countries
|
||||
data['total_price'] = total_price
|
||||
await state.set_data(data)
|
||||
@@ -4077,14 +3940,7 @@ async def handle_add_country_to_subscription(
|
||||
try:
|
||||
from app.keyboards.inline import get_manage_countries_keyboard
|
||||
await callback.message.edit_reply_markup(
|
||||
reply_markup=get_manage_countries_keyboard(
|
||||
countries,
|
||||
selected_countries,
|
||||
db_user.subscription.connected_squads,
|
||||
db_user.language,
|
||||
subscription.end_date if subscription else None,
|
||||
promo_group,
|
||||
)
|
||||
reply_markup=get_manage_countries_keyboard(countries, selected_countries, db_user.subscription.connected_squads, db_user.language)
|
||||
)
|
||||
logger.info(f"✅ Клавиатура обновлена")
|
||||
except Exception as e:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import List, Optional, TYPE_CHECKING
|
||||
from typing import List, Optional
|
||||
from aiogram import types
|
||||
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from datetime import datetime
|
||||
@@ -8,7 +8,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.config import settings, PERIOD_PRICES, TRAFFIC_PRICES
|
||||
from app.localization.loader import DEFAULT_LANGUAGE
|
||||
from app.localization.texts import get_texts
|
||||
from app.utils.pricing_utils import format_period_description, apply_percentage_discount
|
||||
from app.utils.pricing_utils import format_period_description
|
||||
from app.utils.subscription_utils import (
|
||||
get_display_subscription_link,
|
||||
get_happ_cryptolink_redirect_link,
|
||||
@@ -17,9 +17,6 @@ import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.database.models import PromoGroup
|
||||
|
||||
def get_rules_keyboard(language: str = DEFAULT_LANGUAGE) -> InlineKeyboardMarkup:
|
||||
texts = get_texts(language)
|
||||
return InlineKeyboardMarkup(inline_keyboard=[
|
||||
@@ -1126,30 +1123,21 @@ def get_extend_subscription_keyboard(language: str = DEFAULT_LANGUAGE) -> Inline
|
||||
return InlineKeyboardMarkup(inline_keyboard=keyboard)
|
||||
|
||||
|
||||
def get_add_traffic_keyboard(
|
||||
language: str = DEFAULT_LANGUAGE,
|
||||
subscription_end_date: datetime = None,
|
||||
promo_group: Optional["PromoGroup"] = None,
|
||||
) -> InlineKeyboardMarkup:
|
||||
def get_add_traffic_keyboard(language: str = DEFAULT_LANGUAGE, subscription_end_date: datetime = None) -> InlineKeyboardMarkup:
|
||||
from app.utils.pricing_utils import get_remaining_months
|
||||
from app.config import settings
|
||||
texts = get_texts(language)
|
||||
|
||||
|
||||
months_multiplier = 1
|
||||
period_text = ""
|
||||
if subscription_end_date:
|
||||
months_multiplier = get_remaining_months(subscription_end_date)
|
||||
if months_multiplier > 1:
|
||||
period_text = f" (за {months_multiplier} мес)"
|
||||
|
||||
period_hint_days = months_multiplier * 30 if months_multiplier > 0 else None
|
||||
addons_enabled = bool(
|
||||
promo_group and getattr(promo_group, "apply_discounts_to_addons", False)
|
||||
)
|
||||
|
||||
|
||||
packages = settings.get_traffic_packages()
|
||||
enabled_packages = [pkg for pkg in packages if pkg['enabled']]
|
||||
|
||||
|
||||
if not enabled_packages:
|
||||
return InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(
|
||||
@@ -1163,50 +1151,23 @@ def get_add_traffic_keyboard(
|
||||
])
|
||||
|
||||
buttons = []
|
||||
|
||||
|
||||
for package in enabled_packages:
|
||||
gb = package['gb']
|
||||
price_per_month = package['price']
|
||||
original_total = price_per_month * months_multiplier
|
||||
|
||||
discount_percent = 0
|
||||
if addons_enabled:
|
||||
try:
|
||||
discount_percent = promo_group.get_addon_discount_percent(
|
||||
"traffic",
|
||||
period_days=period_hint_days,
|
||||
)
|
||||
except AttributeError:
|
||||
discount_percent = 0
|
||||
|
||||
discounted_per_month, discount_per_month = apply_percentage_discount(
|
||||
price_per_month,
|
||||
discount_percent,
|
||||
)
|
||||
total_price = discounted_per_month * months_multiplier
|
||||
total_discount = discount_per_month * months_multiplier
|
||||
|
||||
if total_discount > 0:
|
||||
price_display = f"{original_total//100} ₽ → {total_price//100} ₽"
|
||||
else:
|
||||
price_display = f"{total_price//100} ₽"
|
||||
|
||||
if period_text and total_discount <= 0:
|
||||
price_display += period_text
|
||||
elif period_text and total_discount > 0:
|
||||
price_display += period_text
|
||||
|
||||
total_price = price_per_month * months_multiplier
|
||||
|
||||
if gb == 0:
|
||||
if language == "ru":
|
||||
text = f"♾️ Безлимитный трафик - {price_display}"
|
||||
text = f"♾️ Безлимитный трафик - {total_price//100} ₽{period_text}"
|
||||
else:
|
||||
text = f"♾️ Unlimited traffic - {price_display}"
|
||||
text = f"♾️ Unlimited traffic - {total_price//100} ₽{period_text}"
|
||||
else:
|
||||
if language == "ru":
|
||||
text = f"📊 +{gb} ГБ трафика - {price_display}"
|
||||
text = f"📊 +{gb} ГБ трафика - {total_price//100} ₽{period_text}"
|
||||
else:
|
||||
text = f"📊 +{gb} GB traffic - {price_display}"
|
||||
|
||||
text = f"📊 +{gb} GB traffic - {total_price//100} ₽{period_text}"
|
||||
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text=text, callback_data=f"add_traffic_{gb}")
|
||||
])
|
||||
@@ -1220,29 +1181,20 @@ def get_add_traffic_keyboard(
|
||||
|
||||
return InlineKeyboardMarkup(inline_keyboard=buttons)
|
||||
|
||||
def get_change_devices_keyboard(
|
||||
current_devices: int,
|
||||
language: str = DEFAULT_LANGUAGE,
|
||||
subscription_end_date: datetime = None,
|
||||
promo_group: Optional["PromoGroup"] = None,
|
||||
) -> InlineKeyboardMarkup:
|
||||
def get_change_devices_keyboard(current_devices: int, language: str = DEFAULT_LANGUAGE, subscription_end_date: datetime = None) -> InlineKeyboardMarkup:
|
||||
from app.utils.pricing_utils import get_remaining_months
|
||||
from app.config import settings
|
||||
texts = get_texts(language)
|
||||
|
||||
|
||||
months_multiplier = 1
|
||||
period_text = ""
|
||||
if subscription_end_date:
|
||||
months_multiplier = get_remaining_months(subscription_end_date)
|
||||
if months_multiplier > 1:
|
||||
period_text = f" (за {months_multiplier} мес)"
|
||||
|
||||
|
||||
device_price_per_month = settings.PRICE_PER_DEVICE
|
||||
period_hint_days = months_multiplier * 30 if months_multiplier > 0 else None
|
||||
addons_enabled = bool(
|
||||
promo_group and getattr(promo_group, "apply_discounts_to_addons", False)
|
||||
)
|
||||
|
||||
|
||||
buttons = []
|
||||
|
||||
min_devices = 1
|
||||
@@ -1259,37 +1211,15 @@ def get_change_devices_keyboard(
|
||||
elif devices_count > current_devices:
|
||||
emoji = "➕"
|
||||
additional_devices = devices_count - current_devices
|
||||
|
||||
|
||||
current_chargeable = max(0, current_devices - settings.DEFAULT_DEVICE_LIMIT)
|
||||
new_chargeable = max(0, devices_count - settings.DEFAULT_DEVICE_LIMIT)
|
||||
chargeable_devices = new_chargeable - current_chargeable
|
||||
|
||||
|
||||
if chargeable_devices > 0:
|
||||
price_per_month = chargeable_devices * device_price_per_month
|
||||
discount_percent = 0
|
||||
if addons_enabled:
|
||||
try:
|
||||
discount_percent = promo_group.get_addon_discount_percent(
|
||||
"devices",
|
||||
period_days=period_hint_days,
|
||||
)
|
||||
except AttributeError:
|
||||
discount_percent = 0
|
||||
|
||||
discounted_per_month, discount_per_month = apply_percentage_discount(
|
||||
price_per_month,
|
||||
discount_percent,
|
||||
)
|
||||
total_price = discounted_per_month * months_multiplier
|
||||
total_discount = discount_per_month * months_multiplier
|
||||
|
||||
if total_discount > 0:
|
||||
original_total = price_per_month * months_multiplier
|
||||
price_display = f"+{original_total//100}₽ → {total_price//100}₽"
|
||||
else:
|
||||
price_display = f"+{total_price//100}₽"
|
||||
|
||||
price_text = f" ({price_display}{period_text})"
|
||||
total_price = price_per_month * months_multiplier
|
||||
price_text = f" (+{total_price//100}₽{period_text})"
|
||||
action_text = ""
|
||||
else:
|
||||
price_text = " (бесплатно)"
|
||||
@@ -1366,8 +1296,7 @@ def get_manage_countries_keyboard(
|
||||
selected: List[str],
|
||||
current_subscription_countries: List[str],
|
||||
language: str = DEFAULT_LANGUAGE,
|
||||
subscription_end_date: datetime = None,
|
||||
promo_group: Optional["PromoGroup"] = None
|
||||
subscription_end_date: datetime = None
|
||||
) -> InlineKeyboardMarkup:
|
||||
from app.utils.pricing_utils import get_remaining_months
|
||||
|
||||
@@ -1377,37 +1306,15 @@ def get_manage_countries_keyboard(
|
||||
if subscription_end_date:
|
||||
months_multiplier = get_remaining_months(subscription_end_date)
|
||||
logger.info(f"🔍 Расчет для управления странами: осталось {months_multiplier} месяцев до {subscription_end_date}")
|
||||
|
||||
period_hint_days = months_multiplier * 30 if months_multiplier > 0 else None
|
||||
addons_enabled = bool(
|
||||
promo_group and getattr(promo_group, "apply_discounts_to_addons", False)
|
||||
)
|
||||
|
||||
|
||||
buttons = []
|
||||
total_cost = 0
|
||||
|
||||
|
||||
for country in countries:
|
||||
uuid = country['uuid']
|
||||
name = country['name']
|
||||
price_per_month = country['price_kopeks']
|
||||
|
||||
discount_percent = 0
|
||||
if addons_enabled:
|
||||
try:
|
||||
discount_percent = promo_group.get_addon_discount_percent(
|
||||
"servers",
|
||||
period_days=period_hint_days,
|
||||
)
|
||||
except AttributeError:
|
||||
discount_percent = 0
|
||||
|
||||
discounted_per_month, discount_per_month = apply_percentage_discount(
|
||||
price_per_month,
|
||||
discount_percent,
|
||||
)
|
||||
discounted_total = discounted_per_month * months_multiplier
|
||||
discount_total = discount_per_month * months_multiplier
|
||||
|
||||
|
||||
if uuid in current_subscription_countries:
|
||||
if uuid in selected:
|
||||
icon = "✅"
|
||||
@@ -1416,32 +1323,21 @@ def get_manage_countries_keyboard(
|
||||
else:
|
||||
if uuid in selected:
|
||||
icon = "➕"
|
||||
total_cost += discounted_total
|
||||
total_cost += price_per_month * months_multiplier
|
||||
else:
|
||||
icon = "⚪"
|
||||
|
||||
|
||||
if uuid not in current_subscription_countries and uuid in selected:
|
||||
total_price = discounted_total
|
||||
total_price = price_per_month * months_multiplier
|
||||
if months_multiplier > 1:
|
||||
if discount_total > 0:
|
||||
original_total = price_per_month * months_multiplier
|
||||
price_text = (
|
||||
f" ({price_per_month//100}₽/мес × {months_multiplier}"
|
||||
f" = {original_total//100}₽ → {total_price//100}₽)"
|
||||
)
|
||||
else:
|
||||
price_text = f" ({price_per_month//100}₽/мес × {months_multiplier} = {total_price//100}₽)"
|
||||
price_text = f" ({price_per_month//100}₽/мес × {months_multiplier} = {total_price//100}₽)"
|
||||
logger.info(f"🔍 Сервер {name}: {price_per_month/100}₽/мес × {months_multiplier} мес = {total_price/100}₽")
|
||||
else:
|
||||
if discount_total > 0:
|
||||
original_total = price_per_month * months_multiplier
|
||||
price_text = f" ({original_total//100}₽ → {total_price//100}₽)"
|
||||
else:
|
||||
price_text = f" ({total_price//100}₽)"
|
||||
price_text = f" ({total_price//100}₽)"
|
||||
display_name = f"{icon} {name}{price_text}"
|
||||
else:
|
||||
display_name = f"{icon} {name}"
|
||||
|
||||
|
||||
buttons.append([
|
||||
InlineKeyboardButton(
|
||||
text=display_name,
|
||||
|
||||
@@ -26,35 +26,15 @@ def _resolve_discount_percent(
|
||||
category: str,
|
||||
*,
|
||||
period_days: Optional[int] = None,
|
||||
for_addon: bool = False,
|
||||
) -> int:
|
||||
effective_group = promo_group
|
||||
|
||||
if user is not None and effective_group is None:
|
||||
effective_group = getattr(user, "promo_group", None)
|
||||
|
||||
if for_addon:
|
||||
if user is not None:
|
||||
try:
|
||||
return user.get_addon_discount(category, period_days)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if effective_group is not None:
|
||||
try:
|
||||
return effective_group.get_addon_discount_percent(category, period_days)
|
||||
except AttributeError:
|
||||
return 0
|
||||
return 0
|
||||
|
||||
if user is not None:
|
||||
try:
|
||||
return user.get_promo_discount(category, period_days)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if effective_group is not None:
|
||||
return effective_group.get_discount_percent(category, period_days)
|
||||
if promo_group is not None:
|
||||
return promo_group.get_discount_percent(category, period_days)
|
||||
|
||||
return 0
|
||||
|
||||
@@ -883,7 +863,6 @@ class SubscriptionService:
|
||||
promo_group,
|
||||
"traffic",
|
||||
period_days=period_hint_days,
|
||||
for_addon=True,
|
||||
)
|
||||
traffic_discount_per_month = traffic_price_per_month * traffic_discount_percent // 100
|
||||
discounted_traffic_per_month = traffic_price_per_month - traffic_discount_per_month
|
||||
@@ -907,7 +886,6 @@ class SubscriptionService:
|
||||
promo_group,
|
||||
"devices",
|
||||
period_days=period_hint_days,
|
||||
for_addon=True,
|
||||
)
|
||||
devices_discount_per_month = devices_price_per_month * devices_discount_percent // 100
|
||||
discounted_devices_per_month = devices_price_per_month - devices_discount_per_month
|
||||
@@ -935,7 +913,6 @@ class SubscriptionService:
|
||||
promo_group,
|
||||
"servers",
|
||||
period_days=period_hint_days,
|
||||
for_addon=True,
|
||||
)
|
||||
server_discount_per_month = (
|
||||
server_price_per_month * servers_discount_percent // 100
|
||||
|
||||
@@ -70,7 +70,6 @@ class AdminStates(StatesGroup):
|
||||
creating_promo_group_device_discount = State()
|
||||
creating_promo_group_period_discount = State()
|
||||
creating_promo_group_auto_assign = State()
|
||||
creating_promo_group_addon_discount = State()
|
||||
|
||||
editing_promo_group_menu = State()
|
||||
editing_promo_group_name = State()
|
||||
@@ -79,7 +78,6 @@ class AdminStates(StatesGroup):
|
||||
editing_promo_group_device_discount = State()
|
||||
editing_promo_group_period_discount = State()
|
||||
editing_promo_group_auto_assign = State()
|
||||
editing_promo_group_addon_discount = State()
|
||||
|
||||
editing_squad_price = State()
|
||||
editing_traffic_price = State()
|
||||
|
||||
@@ -151,9 +151,6 @@
|
||||
"ADMIN_PROMO_GROUPS_TITLE": "💳 <b>Promo groups</b>",
|
||||
"ADMIN_PROMO_GROUPS_SUMMARY": "Groups total: {count}\nMembers total: {members}",
|
||||
"ADMIN_PROMO_GROUPS_DISCOUNTS": "Discounts — servers: {servers}%, traffic: {traffic}%, devices: {devices}%",
|
||||
"ADMIN_PROMO_GROUP_ADDON_DISCOUNT_STATUS_ON": "enabled",
|
||||
"ADMIN_PROMO_GROUP_ADDON_DISCOUNT_STATUS_OFF": "disabled",
|
||||
"ADMIN_PROMO_GROUP_ADDON_DISCOUNT_LINE": "Add-on discounts: {status}",
|
||||
"ADMIN_PROMO_GROUP_PERIOD_DISCOUNTS_HEADER": "⏳ Period discounts:",
|
||||
"ADMIN_PROMO_GROUPS_DEFAULT_LABEL": " (default)",
|
||||
"ADMIN_PROMO_GROUPS_MEMBERS_COUNT": "Members: {count}",
|
||||
@@ -265,14 +262,10 @@
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_TRAFFIC": "🌐 Traffic discount",
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_SERVERS": "🖥 Server discount",
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_DEVICES": "📱 Device discount",
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_ADDONS": "🎁 Add-on discounts",
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_PERIODS": "⏳ Period discounts",
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_AUTO_ASSIGN": "🤖 Auto assignment by spending",
|
||||
"ADMIN_PROMO_GROUP_CREATE_ADDON_DISCOUNT_PROMPT": "Enable discounts for add-on purchases? (yes/no)",
|
||||
"ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Enter total spending (in ₽) required for automatic assignment. Send 0 to disable.",
|
||||
"ADMIN_PROMO_GROUP_INVALID_AUTO_ASSIGN": "Enter a non-negative amount in rubles or 0 to disable.",
|
||||
"ADMIN_PROMO_GROUP_INVALID_ADDON_DISCOUNT": "Please enter 'yes' or 'no' to toggle add-on discounts.",
|
||||
"ADMIN_PROMO_GROUP_EDIT_ADDONS_PROMPT": "Enable add-on discounts? Current value: {current}.",
|
||||
"ADMIN_PROMO_GROUP_EDIT_AUTO_ASSIGN_PROMPT": "Enter total spending (in ₽) for auto assignment. Current value: {current}.",
|
||||
"ADMIN_PROMO_GROUP_MEMBERS_TITLE": "👥 Members of {name}",
|
||||
"ADMIN_PROMO_GROUP_MEMBERS_EMPTY": "This group has no members yet.",
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
"ADMIN_PROMO_GROUPS_TITLE": "💳 <b>Промогруппы</b>",
|
||||
"ADMIN_PROMO_GROUPS_SUMMARY": "Всего групп: {count}\nВсего участников: {members}",
|
||||
"ADMIN_PROMO_GROUPS_DISCOUNTS": "Скидки — серверы: {servers}%, трафик: {traffic}%, устройства: {devices}%",
|
||||
"ADMIN_PROMO_GROUP_ADDON_DISCOUNT_STATUS_ON": "включены",
|
||||
"ADMIN_PROMO_GROUP_ADDON_DISCOUNT_STATUS_OFF": "отключены",
|
||||
"ADMIN_PROMO_GROUP_ADDON_DISCOUNT_LINE": "Скидки на доп. услуги: {status}",
|
||||
"ADMIN_PROMO_GROUP_PERIOD_DISCOUNTS_HEADER": "⏳ Скидки по периодам:",
|
||||
"ADMIN_PROMO_GROUPS_DEFAULT_LABEL": " (базовая)",
|
||||
"ADMIN_PROMO_GROUPS_MEMBERS_COUNT": "Участников: {count}",
|
||||
@@ -131,14 +128,10 @@
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_TRAFFIC": "🌐 Скидка на трафик",
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_SERVERS": "🖥 Скидка на серверы",
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_DEVICES": "📱 Скидка на устройства",
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_ADDONS": "🎁 Скидки на доп. услуги",
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_PERIODS": "⏳ Скидки по периодам",
|
||||
"ADMIN_PROMO_GROUP_EDIT_FIELD_AUTO_ASSIGN": "🤖 Автовыдача по тратам",
|
||||
"ADMIN_PROMO_GROUP_CREATE_ADDON_DISCOUNT_PROMPT": "Включить скидки на докупку доп. услуг? (да/нет)",
|
||||
"ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Введите сумму общих трат (в ₽) для автоматической выдачи этой группы. Отправьте 0, чтобы отключить.",
|
||||
"ADMIN_PROMO_GROUP_INVALID_AUTO_ASSIGN": "Введите неотрицательное число в рублях или 0 для отключения.",
|
||||
"ADMIN_PROMO_GROUP_INVALID_ADDON_DISCOUNT": "Введите «да» или «нет» для включения скидок на доп. услуги.",
|
||||
"ADMIN_PROMO_GROUP_EDIT_ADDONS_PROMPT": "Включить скидки на доп. услуги? Текущее значение: {current}.",
|
||||
"ADMIN_PROMO_GROUP_EDIT_AUTO_ASSIGN_PROMPT": "Введите сумму общих трат (в ₽) для автовыдачи. Текущее значение: {current}.",
|
||||
"ADMIN_PROMO_GROUP_MEMBERS_TITLE": "👥 Участники группы {name}",
|
||||
"ADMIN_PROMO_GROUP_MEMBERS_EMPTY": "В этой группе пока нет участников.",
|
||||
|
||||
Reference in New Issue
Block a user