feat: group referral bonus display by tariff

This commit is contained in:
3252a8
2026-05-29 22:55:01 +03:00
parent 6803c7801f
commit 7fe8e676cd
8 changed files with 511 additions and 60 deletions
+54 -20
View File
@@ -163,32 +163,66 @@ def _legacy_referral_bonus_periods(settings: Settings) -> List[int]:
return sorted(int(months) for months in settings.subscription_options) return sorted(int(months) for months in settings.subscription_options)
def _serialize_tariff_period_referral_bonus_details(
tariff: Any, lang: str
) -> List[Dict[str, Any]]:
details: List[Dict[str, Any]] = []
for months in sorted(int(month) for month in tariff.enabled_periods):
inviter_days = tariff.referral_inviter_bonus_days(months)
friend_days = tariff.referral_referee_bonus_days(months)
if inviter_days is None and friend_days is None:
continue
details.append(
{
"id": f"{tariff.key}:{months}",
"tariff_key": tariff.key,
"tariff_name": tariff.name(lang),
"months": int(months),
"title": _format_months_title(int(months), lang),
"inviter_days": int(inviter_days or 0),
"friend_days": int(friend_days or 0),
}
)
return details
def _serialize_tariff_referral_bonus_details(settings: Settings, lang: str) -> List[Dict[str, Any]]: def _serialize_tariff_referral_bonus_details(settings: Settings, lang: str) -> List[Dict[str, Any]]:
tariffs_config = settings.tariffs_config tariffs_config = settings.tariffs_config
if not tariffs_config: if not tariffs_config:
return [] return []
details: List[Dict[str, Any]] = [] period_tariffs = [
for tariff in tariffs_config.enabled_tariffs: tariff for tariff in tariffs_config.enabled_tariffs if tariff.billing_model == "period"
if tariff.billing_model != "period": ]
if len(period_tariffs) <= 1:
return (
_serialize_tariff_period_referral_bonus_details(period_tariffs[0], lang)
if period_tariffs
else []
)
summaries: List[Dict[str, Any]] = []
for tariff in period_tariffs:
details = _serialize_tariff_period_referral_bonus_details(tariff, lang)
if not details:
continue continue
for months in sorted(int(month) for month in tariff.enabled_periods): inviter_values = [int(item["inviter_days"]) for item in details]
inviter_days = tariff.referral_inviter_bonus_days(months) friend_values = [int(item["friend_days"]) for item in details]
friend_days = tariff.referral_referee_bonus_days(months) summaries.append(
if inviter_days is None and friend_days is None: {
continue "id": f"tariff:{tariff.key}",
details.append( "type": "tariff_summary",
{ "tariff_key": tariff.key,
"id": f"{tariff.key}:{months}", "tariff_name": tariff.name(lang),
"tariff_key": tariff.key, "title": tariff.name(lang),
"tariff_name": tariff.name(lang), "inviter_min_days": min(inviter_values),
"months": int(months), "inviter_max_days": max(inviter_values),
"title": f"{tariff.name(lang)} - {_format_months_title(int(months), lang)}", "friend_min_days": min(friend_values),
"inviter_days": int(inviter_days or 0), "friend_max_days": max(friend_values),
"friend_days": int(friend_days or 0), "details": details,
} }
) )
return details return summaries
def _serialize_referral_bonus_details(settings: Settings, lang: str) -> List[Dict[str, Any]]: def _serialize_referral_bonus_details(settings: Settings, lang: str) -> List[Dict[str, Any]]:
+128 -23
View File
@@ -1,5 +1,5 @@
import logging import logging
from typing import Optional, Union from typing import Any, Callable, Optional, Union
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
from aiogram import Bot, F, Router, types from aiogram import Bot, F, Router, types
@@ -76,31 +76,10 @@ async def referral_command_handler(
await event.answer() await event.answer()
return return
bonus_info_parts = []
if getattr(settings, "traffic_sale_mode", False): if getattr(settings, "traffic_sale_mode", False):
bonus_details_str = _("referral_not_available_for_traffic") bonus_details_str = _("referral_not_available_for_traffic")
else: else:
if settings.subscription_options: bonus_details_str = _build_referral_bonus_details_text(settings, _, current_lang)
for months_period_key, _price in sorted(settings.subscription_options.items()):
inv_bonus = settings.referral_bonus_inviter.get(months_period_key)
ref_bonus = settings.referral_bonus_referee.get(months_period_key)
if inv_bonus is not None or ref_bonus is not None:
bonus_info_parts.append(
_(
"referral_bonus_per_period",
months=months_period_key,
inviter_bonus_days=inv_bonus
if inv_bonus is not None
else _("no_bonus_placeholder"),
referee_bonus_days=ref_bonus
if ref_bonus is not None
else _("no_bonus_placeholder"),
)
)
bonus_details_str = (
"\n".join(bonus_info_parts) if bonus_info_parts else _("referral_no_bonuses_configured")
)
referral_stats = await referral_service.get_referral_stats(session, inviter_user_id) referral_stats = await referral_service.get_referral_stats(session, inviter_user_id)
@@ -208,6 +187,132 @@ async def referral_action_handler(
await callback.answer() await callback.answer()
Translator = Callable[..., str]
def _period_bonus_text(
translator: Translator,
*,
months: int,
inviter_days: Optional[int],
referee_days: Optional[int],
) -> str:
return translator(
"referral_bonus_per_period",
months=months,
inviter_bonus_days=(
inviter_days if inviter_days is not None else translator("no_bonus_placeholder")
),
referee_bonus_days=(
referee_days if referee_days is not None else translator("no_bonus_placeholder")
),
)
def _tariff_period_bonus_entries(tariff: Any) -> list[dict[str, Optional[int]]]:
entries: list[dict[str, Optional[int]]] = []
for months in sorted(int(month) for month in getattr(tariff, "enabled_periods", [])):
inviter_days = tariff.referral_inviter_bonus_days(months)
referee_days = tariff.referral_referee_bonus_days(months)
if inviter_days is None and referee_days is None:
continue
entries.append(
{
"months": months,
"inviter_days": inviter_days,
"referee_days": referee_days,
}
)
return entries
def _legacy_period_bonus_entries(settings: Settings) -> list[dict[str, Optional[int]]]:
entries: list[dict[str, Optional[int]]] = []
for months, _price in sorted(settings.subscription_options.items()):
inviter_days = settings.referral_bonus_inviter.get(months)
referee_days = settings.referral_bonus_referee.get(months)
if inviter_days is None and referee_days is None:
continue
entries.append(
{
"months": int(months),
"inviter_days": inviter_days,
"referee_days": referee_days,
}
)
return entries
def _bonus_days_range(translator: Translator, values: list[int]) -> str:
return translator(
"referral_bonus_days_range",
min_days=min(values),
max_days=max(values),
)
def _build_referral_bonus_details_text(
settings: Settings, translator: Translator, current_lang: str
) -> str:
tariffs_config = settings.tariffs_config
if not tariffs_config:
bonus_info_parts = [
_period_bonus_text(
translator,
months=int(entry["months"] or 0),
inviter_days=entry["inviter_days"],
referee_days=entry["referee_days"],
)
for entry in _legacy_period_bonus_entries(settings)
]
return (
"\n".join(bonus_info_parts)
if bonus_info_parts
else translator("referral_no_bonuses_configured")
)
period_tariffs = [
tariff for tariff in tariffs_config.enabled_tariffs if tariff.billing_model == "period"
]
if len(period_tariffs) <= 1:
entries = _tariff_period_bonus_entries(period_tariffs[0]) if period_tariffs else []
bonus_info_parts = [
_period_bonus_text(
translator,
months=int(entry["months"] or 0),
inviter_days=entry["inviter_days"],
referee_days=entry["referee_days"],
)
for entry in entries
]
return (
"\n".join(bonus_info_parts)
if bonus_info_parts
else translator("referral_no_bonuses_configured")
)
bonus_info_parts = []
for tariff in period_tariffs:
entries = _tariff_period_bonus_entries(tariff)
if not entries:
continue
inviter_values = [int(entry["inviter_days"] or 0) for entry in entries]
referee_values = [int(entry["referee_days"] or 0) for entry in entries]
bonus_info_parts.append(
translator(
"referral_bonus_tariff_range",
tariff_name=tariff.name(current_lang),
inviter_bonus_range=_bonus_days_range(translator, inviter_values),
referee_bonus_range=_bonus_days_range(translator, referee_values),
)
)
return (
"\n".join(bonus_info_parts)
if bonus_info_parts
else translator("referral_no_bonuses_configured")
)
def _build_webapp_referral_link( def _build_webapp_referral_link(
base_url: Optional[str], referral_code: Optional[str] base_url: Optional[str], referral_code: Optional[str]
) -> Optional[str]: ) -> Optional[str]:
+83
View File
@@ -1537,6 +1537,89 @@ a {
font-size: 12px; font-size: 12px;
} }
.referral-tariff-dropdown {
display: grid;
min-width: 0;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--surface-sheen-soft);
}
.referral-tariff-summary {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 8px;
align-items: center;
min-width: 0;
padding: 10px 11px;
cursor: pointer;
list-style: none;
-webkit-tap-highlight-color: transparent;
}
.referral-tariff-summary::-webkit-details-marker {
display: none;
}
.referral-tariff-copy {
display: grid;
min-width: 0;
gap: 3px;
}
.referral-tariff-copy strong {
overflow: hidden;
font-size: 13px;
text-overflow: ellipsis;
white-space: nowrap;
}
.referral-tariff-copy small {
color: var(--muted);
font-size: 12px;
}
.referral-tariff-dropdown summary:hover .premium-server-help-icon,
.referral-tariff-dropdown[open] .premium-server-help-icon {
color: var(--accent);
background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.referral-tariff-details {
display: grid;
grid-template-rows: 0fr;
padding: 0 8px;
opacity: 0;
transform: translateY(-2px);
transform-origin: top;
transition:
grid-template-rows 0.22s ease,
padding 0.22s ease,
opacity 0.18s ease,
transform 0.22s ease;
}
.referral-tariff-details > div {
min-height: 0;
overflow: hidden;
}
.referral-tariff-detail-list {
display: grid;
gap: 8px;
}
.referral-tariff-dropdown[open] .referral-tariff-details {
grid-template-rows: 1fr;
padding: 0 8px 8px;
opacity: 1;
transform: translateY(0);
}
.referral-bonus-row-nested {
background: var(--surface);
}
.settings-profile { .settings-profile {
display: flex; display: flex;
align-items: center; align-items: center;
+72 -13
View File
@@ -1,5 +1,5 @@
<script> <script>
import { Copy, Gift, Ticket, TriangleAlert } from "$components/ui/icons.js"; import { CircleQuestionMark, Copy, Gift, Ticket, TriangleAlert } from "$components/ui/icons.js";
import { Tooltip } from "$components/ui/primitives.js"; import { Tooltip } from "$components/ui/primitives.js";
import Button from "$components/ui/button.svelte"; import Button from "$components/ui/button.svelte";
@@ -21,6 +21,17 @@
export let clearPromoFieldError = () => {}; export let clearPromoFieldError = () => {};
export let copyText = () => {}; export let copyText = () => {};
export let t = (key) => key; export let t = (key) => key;
$: tariffBonusSummaries = referralBonusDetails.filter((bonus) => Array.isArray(bonus.details));
$: periodBonusDetails = referralBonusDetails.filter((bonus) => !Array.isArray(bonus.details));
$: usesTariffBonusSummaries = tariffBonusSummaries.length > 0;
function daysRange(minDays, maxDays) {
return t("wa_referral_bonus_range_days", {
min: Number(minDays || 0),
max: Number(maxDays || 0),
});
}
</script> </script>
<main class="content with-nav"> <main class="content with-nav">
@@ -55,20 +66,68 @@
<small>{t("wa_referral_bonus_friend_days", { days: referralWelcomeBonusDays })}</small> <small>{t("wa_referral_bonus_friend_days", { days: referralWelcomeBonusDays })}</small>
</div> </div>
{/if} {/if}
{#if referralBonusDetails.length} {#if usesTariffBonusSummaries}
<p class="referral-bonus-intro">{t("wa_referral_bonus_depends_on_tariff")}</p>
{:else if periodBonusDetails.length}
<p class="referral-bonus-intro">{t("wa_referral_bonus_paid_intro")}</p> <p class="referral-bonus-intro">{t("wa_referral_bonus_paid_intro")}</p>
{/if} {/if}
{#each referralBonusDetails as bonus, index (bonus.id || `${bonus.tariff_key || "legacy"}:${bonus.months || index}`)} {#if usesTariffBonusSummaries}
<div class="referral-bonus-row"> {#each tariffBonusSummaries as tariffBonus, index (tariffBonus.id || `tariff:${tariffBonus.tariff_key || index}`)}
<strong>{bonus.title || `${bonus.months || "?"}`}</strong> <details class="referral-tariff-dropdown">
<small <summary class="referral-tariff-summary">
>{t("wa_referral_bonus_you_days", { days: Number(bonus.inviter_days || 0) })}</small <span class="referral-tariff-copy">
> <strong>{tariffBonus.title || tariffBonus.tariff_name}</strong>
<small <small>
>{t("wa_referral_bonus_friend_days", { days: Number(bonus.friend_days || 0) })}</small {t("wa_referral_bonus_you_range", {
> range: daysRange(tariffBonus.inviter_min_days, tariffBonus.inviter_max_days),
</div> })}
{/each} </small>
<small>
{t("wa_referral_bonus_friend_range", {
range: daysRange(tariffBonus.friend_min_days, tariffBonus.friend_max_days),
})}
</small>
</span>
<CircleQuestionMark class="premium-server-help-icon" size={16} />
</summary>
<div class="referral-tariff-details">
<div class="referral-tariff-detail-list">
{#each tariffBonus.details || [] as bonus, detailIndex (bonus.id || `${tariffBonus.tariff_key || index}:${bonus.months || detailIndex}`)}
<div class="referral-bonus-row referral-bonus-row-nested">
<strong>{bonus.title || `${bonus.months || "?"}`}</strong>
<small
>{t("wa_referral_bonus_you_days", {
days: Number(bonus.inviter_days || 0),
})}</small
>
<small
>{t("wa_referral_bonus_friend_days", {
days: Number(bonus.friend_days || 0),
})}</small
>
</div>
{/each}
</div>
</div>
</details>
{/each}
{:else}
{#each periodBonusDetails as bonus, index (bonus.id || `${bonus.tariff_key || "legacy"}:${bonus.months || index}`)}
<div class="referral-bonus-row">
<strong>{bonus.title || `${bonus.months || "?"}`}</strong>
<small
>{t("wa_referral_bonus_you_days", {
days: Number(bonus.inviter_days || 0),
})}</small
>
<small
>{t("wa_referral_bonus_friend_days", {
days: Number(bonus.friend_days || 0),
})}</small
>
</div>
{/each}
{/if}
</div> </div>
{:else} {:else}
<StatusMessage>{t("wa_referral_bonus_not_configured")}</StatusMessage> <StatusMessage>{t("wa_referral_bonus_not_configured")}</StatusMessage>
+6
View File
@@ -97,6 +97,8 @@
"no_button": "No", "no_button": "No",
"referral_program_info_new": "🎁 <b>Referral Program</b>\n\n📊 <b>Your stats:</b>\n👥 Friends invited: <b>{invited_count}</b>\n💳 Purchased subscription: <b>{purchased_count}</b>\n\n🔗 Telegram link:\n<code>{referral_link}</code>{webapp_link_section}\n\n💰 <b>Invitation bonuses:</b>\n{bonus_details}\n\n📢 Share the link with friends and get bonuses!", "referral_program_info_new": "🎁 <b>Referral Program</b>\n\n📊 <b>Your stats:</b>\n👥 Friends invited: <b>{invited_count}</b>\n💳 Purchased subscription: <b>{purchased_count}</b>\n\n🔗 Telegram link:\n<code>{referral_link}</code>{webapp_link_section}\n\n💰 <b>Invitation bonuses:</b>\n{bonus_details}\n\n📢 Share the link with friends and get bonuses!",
"referral_bonus_per_period": "\n\n🎁 For a friend's {months}-month subscription:\n ➢ You: <b>{inviter_bonus_days} days</b>\n ➢ Friend: <b>{referee_bonus_days} days</b>", "referral_bonus_per_period": "\n\n🎁 For a friend's {months}-month subscription:\n ➢ You: <b>{inviter_bonus_days} days</b>\n ➢ Friend: <b>{referee_bonus_days} days</b>",
"referral_bonus_days_range": "from {min_days} to {max_days} days",
"referral_bonus_tariff_range": "\n\n🎁 {tariff_name}:\n ➢ You: <b>{inviter_bonus_range}</b>\n ➢ Friend: <b>{referee_bonus_range}</b>",
"referral_not_available_for_traffic": "Referral bonuses are not available for traffic packages.", "referral_not_available_for_traffic": "Referral bonuses are not available for traffic packages.",
"referral_share_message_button": "📩 Message for friend", "referral_share_message_button": "📩 Message for friend",
"referral_friend_message": "🚀 Hey! Try this service - it's fast, reliable and affordable!\n\n🎁 Use my link to get bonus days with your subscription!\n\n{referral_link}", "referral_friend_message": "🚀 Hey! Try this service - it's fast, reliable and affordable!\n\n🎁 Use my link to get bonus days with your subscription!\n\n{referral_link}",
@@ -778,8 +780,12 @@
"wa_referral_bonus_once_note": "Bonus is granted only once per invited user after their first payment.", "wa_referral_bonus_once_note": "Bonus is granted only once per invited user after their first payment.",
"wa_referral_bonus_registration_title": "For registration via referral link", "wa_referral_bonus_registration_title": "For registration via referral link",
"wa_referral_bonus_paid_intro": "If a friend pays for a subscription:", "wa_referral_bonus_paid_intro": "If a friend pays for a subscription:",
"wa_referral_bonus_depends_on_tariff": "Bonus depends on the tariff and payment period your friend chooses",
"wa_referral_bonus_range_days": "from {min} to {max} days",
"wa_referral_bonus_you_days": "You: +{days} days", "wa_referral_bonus_you_days": "You: +{days} days",
"wa_referral_bonus_friend_days": "Friend: +{days} days", "wa_referral_bonus_friend_days": "Friend: +{days} days",
"wa_referral_bonus_you_range": "You: {range}",
"wa_referral_bonus_friend_range": "Friend: {range}",
"wa_referral_bonus_not_configured": "Referral bonuses are not configured", "wa_referral_bonus_not_configured": "Referral bonuses are not configured",
"wa_promo_enter": "Enter promo code", "wa_promo_enter": "Enter promo code",
"wa_promo_activated_until": "Promo code activated. Subscription until {date}", "wa_promo_activated_until": "Promo code activated. Subscription until {date}",
+6
View File
@@ -97,6 +97,8 @@
"no_button": "Нет", "no_button": "Нет",
"referral_program_info_new": "🎁 <b>Реферальная программа</b>\n\n📊 <b>Твоя статистика:</b>\n👥 Приглашено друзей: <b>{invited_count}</b>\n💳 Купили подписку: <b>{purchased_count}</b>\n\n🔗 Telegram ссылка:\n<code>{referral_link}</code>{webapp_link_section}\n\n💰 <b>Бонусы за приглашения:</b>\n{bonus_details}\n\n📢 Поделись ссылкой с друзьями и получай бонусы!", "referral_program_info_new": "🎁 <b>Реферальная программа</b>\n\n📊 <b>Твоя статистика:</b>\n👥 Приглашено друзей: <b>{invited_count}</b>\n💳 Купили подписку: <b>{purchased_count}</b>\n\n🔗 Telegram ссылка:\n<code>{referral_link}</code>{webapp_link_section}\n\n💰 <b>Бонусы за приглашения:</b>\n{bonus_details}\n\n📢 Поделись ссылкой с друзьями и получай бонусы!",
"referral_bonus_per_period": "\n\n🎁 За {months}-мес. подписку друга:\n ➢ Вы: <b>{inviter_bonus_days} дн.</b>\n ➢ Друг: <b>{referee_bonus_days} дн.</b>", "referral_bonus_per_period": "\n\n🎁 За {months}-мес. подписку друга:\n ➢ Вы: <b>{inviter_bonus_days} дн.</b>\n ➢ Друг: <b>{referee_bonus_days} дн.</b>",
"referral_bonus_days_range": "от {min_days} до {max_days} дн.",
"referral_bonus_tariff_range": "\n\n🎁 {tariff_name}:\n ➢ Вы: <b>{inviter_bonus_range}</b>\n ➢ Друг: <b>{referee_bonus_range}</b>",
"referral_not_available_for_traffic": "Для пакетов трафика реферальные бонусы не начисляются.", "referral_not_available_for_traffic": "Для пакетов трафика реферальные бонусы не начисляются.",
"referral_share_message_button": "📩 Сообщение для друга", "referral_share_message_button": "📩 Сообщение для друга",
"referral_friend_message": "🚀 Привет! Попробуй этот сервис - быстрый, надёжный и доступный!\n\n🎁 По моей ссылке тебе дадут бонусные дни к подписке!\n\n{referral_link}", "referral_friend_message": "🚀 Привет! Попробуй этот сервис - быстрый, надёжный и доступный!\n\n🎁 По моей ссылке тебе дадут бонусные дни к подписке!\n\n{referral_link}",
@@ -778,8 +780,12 @@
"wa_referral_bonus_once_note": "Бонус за приглашённого начисляется только один раз, после его первой оплаты.", "wa_referral_bonus_once_note": "Бонус за приглашённого начисляется только один раз, после его первой оплаты.",
"wa_referral_bonus_registration_title": "За регистрацию по реферальной ссылке", "wa_referral_bonus_registration_title": "За регистрацию по реферальной ссылке",
"wa_referral_bonus_paid_intro": "Если друг оплатит подписку:", "wa_referral_bonus_paid_intro": "Если друг оплатит подписку:",
"wa_referral_bonus_depends_on_tariff": "Бонус зависит от тарифа и периода оплаты друга",
"wa_referral_bonus_range_days": "от {min} до {max} дней",
"wa_referral_bonus_you_days": "Вам: +{days} дней", "wa_referral_bonus_you_days": "Вам: +{days} дней",
"wa_referral_bonus_friend_days": "Другу: +{days} дней", "wa_referral_bonus_friend_days": "Другу: +{days} дней",
"wa_referral_bonus_you_range": "Вам: {range}",
"wa_referral_bonus_friend_range": "Другу: {range}",
"wa_referral_bonus_not_configured": "Реферальные бонусы не настроены", "wa_referral_bonus_not_configured": "Реферальные бонусы не настроены",
"wa_promo_enter": "Введите промокод", "wa_promo_enter": "Введите промокод",
"wa_promo_activated_until": "Промокод активирован. Подписка до {date}", "wa_promo_activated_until": "Промокод активирован. Подписка до {date}",
+93
View File
@@ -20,6 +20,7 @@ from bot.keyboards.inline.user_keyboards import (
payment_methods_back_callback, payment_methods_back_callback,
payment_options_back_callback, payment_options_back_callback,
) )
from config.tariffs_config import TariffsConfig
class JsonI18nStub: class JsonI18nStub:
@@ -304,3 +305,95 @@ class UserBotMenuTests(unittest.TestCase):
self.assertLess(text.index("Telegram link:"), text.index("Web link:")) self.assertLess(text.index("Telegram link:"), text.index("Web link:"))
self.assertLess(text.index("Web link:"), text.index("Invitation bonuses:")) self.assertLess(text.index("Web link:"), text.index("Invitation bonuses:"))
def test_single_tariff_referral_text_uses_period_rows_without_tariff_name(self):
settings = SimpleNamespace(
tariffs_config=TariffsConfig.model_validate(
{
"default_tariff": "standard",
"tariffs": [
{
"key": "standard",
"names": {"en": "Standard"},
"descriptions": {},
"squad_uuids": ["standard"],
"billing_model": "period",
"monthly_gb": 100,
"prices_rub": {"2": 400, "4": 800},
"prices_stars": {},
"referral_bonus_days_inviter": {"2": 5, "4": 10},
"referral_bonus_days_referee": {"2": 1, "4": 2},
"enabled_periods": [2, 4],
"enabled": True,
}
],
}
),
subscription_options={},
referral_bonus_inviter={},
referral_bonus_referee={},
)
text = referral._build_referral_bonus_details_text(
settings,
lambda key, **kwargs: self.i18n.gettext("en", key, **kwargs),
"en",
)
self.assertIn("For a friend's 2-month subscription", text)
self.assertIn("For a friend's 4-month subscription", text)
self.assertNotIn("Standard", text)
def test_multiple_tariff_referral_text_uses_tariff_ranges(self):
settings = SimpleNamespace(
tariffs_config=TariffsConfig.model_validate(
{
"default_tariff": "standard",
"tariffs": [
{
"key": "standard",
"names": {"en": "Standard"},
"descriptions": {},
"squad_uuids": ["standard"],
"billing_model": "period",
"monthly_gb": 100,
"prices_rub": {"2": 400, "4": 800},
"prices_stars": {},
"referral_bonus_days_inviter": {"2": 5, "4": 10},
"referral_bonus_days_referee": {"2": 1, "4": 2},
"enabled_periods": [2, 4],
"enabled": True,
},
{
"key": "premium",
"names": {"en": "Premium"},
"descriptions": {},
"squad_uuids": ["premium"],
"billing_model": "period",
"monthly_gb": 500,
"prices_rub": {"1": 700, "3": 1800},
"prices_stars": {},
"referral_bonus_days_inviter": {"1": 8, "3": 24},
"referral_bonus_days_referee": {"1": 3, "3": 9},
"enabled_periods": [1, 3],
"enabled": True,
},
],
}
),
subscription_options={},
referral_bonus_inviter={},
referral_bonus_referee={},
)
text = referral._build_referral_bonus_details_text(
settings,
lambda key, **kwargs: self.i18n.gettext("en", key, **kwargs),
"en",
)
self.assertIn("Standard", text)
self.assertIn("Premium", text)
self.assertIn("from 5 to 10 days", text)
self.assertIn("from 8 to 24 days", text)
self.assertNotIn("2-month subscription", text)
+69 -4
View File
@@ -141,7 +141,7 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase):
"tariff_key": "standard", "tariff_key": "standard",
"tariff_name": "Standard", "tariff_name": "Standard",
"months": 2, "months": 2,
"title": "Standard - 2 months", "title": "2 months",
"inviter_days": 5, "inviter_days": 5,
"friend_days": 1, "friend_days": 1,
}, },
@@ -150,7 +150,7 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase):
"tariff_key": "standard", "tariff_key": "standard",
"tariff_name": "Standard", "tariff_name": "Standard",
"months": 4, "months": 4,
"title": "Standard - 4 months", "title": "4 months",
"inviter_days": 10, "inviter_days": 10,
"friend_days": 2, "friend_days": 2,
}, },
@@ -159,7 +159,7 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase):
"tariff_key": "standard", "tariff_key": "standard",
"tariff_name": "Standard", "tariff_name": "Standard",
"months": 8, "months": 8,
"title": "Standard - 8 months", "title": "8 months",
"inviter_days": 0, "inviter_days": 0,
"friend_days": 4, "friend_days": 4,
}, },
@@ -168,13 +168,78 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase):
"tariff_key": "standard", "tariff_key": "standard",
"tariff_name": "Standard", "tariff_name": "Standard",
"months": 16, "months": 16,
"title": "Standard - 16 months", "title": "16 months",
"inviter_days": 40, "inviter_days": 40,
"friend_days": 0, "friend_days": 0,
}, },
], ],
) )
def test_referral_bonus_details_group_multiple_tariffs(self):
with tempfile.TemporaryDirectory() as tmpdir:
path = Path(tmpdir) / "tariffs.json"
path.write_text(
json.dumps(
{
"default_tariff": "standard",
"tariffs": [
{
"key": "standard",
"names": {"en": "Standard"},
"descriptions": {},
"squad_uuids": ["standard"],
"billing_model": "period",
"monthly_gb": 100,
"prices_rub": {"2": 400, "4": 800},
"prices_stars": {},
"referral_bonus_days_inviter": {"2": 5, "4": 10},
"referral_bonus_days_referee": {"2": 1, "4": 2},
"enabled_periods": [2, 4],
"enabled": True,
},
{
"key": "premium",
"names": {"en": "Premium"},
"descriptions": {},
"squad_uuids": ["premium"],
"billing_model": "period",
"monthly_gb": 500,
"prices_rub": {"1": 700, "3": 1800},
"prices_stars": {},
"referral_bonus_days_inviter": {"1": 8, "3": 24},
"referral_bonus_days_referee": {"1": 3, "3": 9},
"enabled_periods": [1, 3],
"enabled": True,
},
],
}
),
encoding="utf-8",
)
settings = Settings(
_env_file=None,
BOT_TOKEN="token",
POSTGRES_USER="app_user",
POSTGRES_PASSWORD="app_password",
TARIFFS_CONFIG_PATH=str(path),
)
details = subscription_webapp._serialize_referral_bonus_details(settings, "en")
self.assertEqual([item["type"] for item in details], ["tariff_summary", "tariff_summary"])
self.assertEqual(details[0]["title"], "Standard")
self.assertEqual(details[0]["inviter_min_days"], 5)
self.assertEqual(details[0]["inviter_max_days"], 10)
self.assertEqual(details[0]["friend_min_days"], 1)
self.assertEqual(details[0]["friend_max_days"], 2)
self.assertEqual(
[item["title"] for item in details[0]["details"]],
["2 months", "4 months"],
)
self.assertEqual(details[1]["title"], "Premium")
self.assertEqual(details[1]["inviter_min_days"], 8)
self.assertEqual(details[1]["inviter_max_days"], 24)
def test_subscription_template_does_not_block_on_telegram_sdk(self): def test_subscription_template_does_not_block_on_telegram_sdk(self):
html = subscription_webapp.TEMPLATE_PATH.read_text(encoding="utf-8") html = subscription_webapp.TEMPLATE_PATH.read_text(encoding="utf-8")