mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-03-03 20:34:10 +00:00
Merge pull request #759 from Fr1ngg/bedolaga/fix-promo-discount-handling-and-messages
Fix promo discount consumption race and refresh promo offer texts
This commit is contained in:
@@ -10,6 +10,36 @@ from app.config import settings
|
||||
from app.database.models import PromoOfferTemplate
|
||||
|
||||
|
||||
UPDATED_TEMPLATE_MESSAGES = {
|
||||
"extend_discount": (
|
||||
"💎 Экономия {discount_percent}% при продлении\n\n"
|
||||
"Скидка суммируется с промогруппой и действует один раз.\n"
|
||||
"Срок действия предложения — {valid_hours} ч."
|
||||
),
|
||||
"purchase_discount": (
|
||||
"🎯 Вернитесь со скидкой {discount_percent}%\n\n"
|
||||
"Скидка суммируется с промогруппой и действует один раз.\n"
|
||||
"Предложение действует {valid_hours} ч."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
LEGACY_TEMPLATE_MESSAGES = {
|
||||
"extend_discount": (
|
||||
"💎 <b>Экономия {discount_percent}% при продлении</b>\n\n"
|
||||
"Активируйте предложение и получите дополнительную скидку на оплату продления. "
|
||||
"Она суммируется с вашими промогрупповыми скидками и действует один раз.\n"
|
||||
"Срок действия предложения — {valid_hours} ч."
|
||||
),
|
||||
"purchase_discount": (
|
||||
"🎯 <b>Вернитесь со скидкой {discount_percent}%</b>\n\n"
|
||||
"После активации мы применим дополнительную скидку к вашей следующей оплате подписки. "
|
||||
"Скидка суммируется с промогруппой и действует один раз.\n"
|
||||
"Предложение действует {valid_hours} ч."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_TEMPLATES: tuple[dict, ...] = (
|
||||
{
|
||||
"offer_type": "test_access",
|
||||
@@ -29,12 +59,7 @@ DEFAULT_TEMPLATES: tuple[dict, ...] = (
|
||||
{
|
||||
"offer_type": "extend_discount",
|
||||
"name": "Скидка на продление",
|
||||
"message_text": (
|
||||
"💎 <b>Экономия {discount_percent}% при продлении</b>\n\n"
|
||||
"Активируйте предложение и получите дополнительную скидку на оплату продления. "
|
||||
"Она суммируется с вашими промогрупповыми скидками и действует один раз.\n"
|
||||
"Срок действия предложения — {valid_hours} ч."
|
||||
),
|
||||
"message_text": UPDATED_TEMPLATE_MESSAGES["extend_discount"],
|
||||
"button_text": "🎁 Получить скидку",
|
||||
"valid_hours": 24,
|
||||
"discount_percent": 20,
|
||||
@@ -45,12 +70,7 @@ DEFAULT_TEMPLATES: tuple[dict, ...] = (
|
||||
{
|
||||
"offer_type": "purchase_discount",
|
||||
"name": "Скидка на покупку",
|
||||
"message_text": (
|
||||
"🎯 <b>Вернитесь со скидкой {discount_percent}%</b>\n\n"
|
||||
"После активации мы применим дополнительную скидку к вашей следующей оплате подписки. "
|
||||
"Скидка суммируется с промогруппой и действует один раз.\n"
|
||||
"Предложение действует {valid_hours} ч."
|
||||
),
|
||||
"message_text": UPDATED_TEMPLATE_MESSAGES["purchase_discount"],
|
||||
"button_text": "🎁 Забрать скидку",
|
||||
"valid_hours": 48,
|
||||
"discount_percent": 25,
|
||||
@@ -80,6 +100,21 @@ async def ensure_default_templates(db: AsyncSession, *, created_by: Optional[int
|
||||
)
|
||||
existing = result.scalars().first()
|
||||
if existing:
|
||||
new_message = UPDATED_TEMPLATE_MESSAGES.get(template_data["offer_type"])
|
||||
legacy_message = LEGACY_TEMPLATE_MESSAGES.get(template_data["offer_type"])
|
||||
should_update = False
|
||||
|
||||
if new_message and legacy_message and existing.message_text == legacy_message:
|
||||
should_update = True
|
||||
elif new_message and (
|
||||
"{bonus_amount" in existing.message_text or "Мы начислим" in existing.message_text
|
||||
):
|
||||
should_update = True
|
||||
|
||||
if should_update and new_message:
|
||||
existing.message_text = new_message
|
||||
existing.updated_at = datetime.utcnow()
|
||||
await db.flush()
|
||||
templates.append(existing)
|
||||
continue
|
||||
|
||||
|
||||
@@ -273,6 +273,8 @@ async def subtract_user_balance(
|
||||
description: str,
|
||||
create_transaction: bool = False,
|
||||
payment_method: Optional[PaymentMethod] = None,
|
||||
*,
|
||||
consume_promo_offer: bool = False,
|
||||
) -> bool:
|
||||
logger.error(f"💸 ОТЛАДКА subtract_user_balance:")
|
||||
logger.error(f" 👤 User ID: {user.id} (TG: {user.telegram_id})")
|
||||
@@ -287,6 +289,11 @@ async def subtract_user_balance(
|
||||
try:
|
||||
old_balance = user.balance_kopeks
|
||||
user.balance_kopeks -= amount_kopeks
|
||||
|
||||
if consume_promo_offer and getattr(user, "promo_offer_discount_percent", 0):
|
||||
user.promo_offer_discount_percent = 0
|
||||
user.promo_offer_discount_source = None
|
||||
|
||||
user.updated_at = datetime.utcnow()
|
||||
|
||||
await db.commit()
|
||||
|
||||
@@ -113,7 +113,7 @@ def _build_offer_detail_keyboard(template: PromoOfferTemplate, language: str) ->
|
||||
InlineKeyboardButton(text="📬 Отправить", callback_data=f"promo_offer_send_menu_{template.id}"),
|
||||
])
|
||||
rows.append([
|
||||
InlineKeyboardButton(text=texts.BACK, callback_data="admin_messages"),
|
||||
InlineKeyboardButton(text=texts.BACK, callback_data="admin_promo_offers"),
|
||||
])
|
||||
return InlineKeyboardMarkup(inline_keyboard=rows)
|
||||
|
||||
|
||||
@@ -133,18 +133,6 @@ def _apply_promo_offer_discount(user: Optional[User], amount: int) -> Dict[str,
|
||||
return {"discounted": discounted, "discount": discount_value, "percent": percent}
|
||||
|
||||
|
||||
async def _consume_promo_offer_discount(db: AsyncSession, user: User) -> None:
|
||||
if _get_promo_offer_discount_percent(user) <= 0:
|
||||
return
|
||||
|
||||
user.promo_offer_discount_percent = 0
|
||||
user.promo_offer_discount_source = None
|
||||
user.updated_at = datetime.utcnow()
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(user)
|
||||
|
||||
|
||||
def _get_period_hint_from_subscription(subscription: Optional[Subscription]) -> Optional[int]:
|
||||
if not subscription:
|
||||
return None
|
||||
@@ -2891,8 +2879,11 @@ async def confirm_extend_subscription(
|
||||
|
||||
try:
|
||||
success = await subtract_user_balance(
|
||||
db, db_user, price,
|
||||
f"Продление подписки на {days} дней"
|
||||
db,
|
||||
db_user,
|
||||
price,
|
||||
f"Продление подписки на {days} дней",
|
||||
consume_promo_offer=promo_component["discount"] > 0,
|
||||
)
|
||||
|
||||
if not success:
|
||||
@@ -2989,9 +2980,6 @@ async def confirm_extend_subscription(
|
||||
f" -{texts.format_price(promo_component['discount'])})"
|
||||
)
|
||||
|
||||
if promo_component["discount"] > 0:
|
||||
await _consume_promo_offer_discount(db, db_user)
|
||||
|
||||
await callback.message.edit_text(
|
||||
success_message,
|
||||
reply_markup=get_back_keyboard(db_user.language)
|
||||
@@ -3760,8 +3748,11 @@ async def confirm_purchase(
|
||||
|
||||
try:
|
||||
success = await subtract_user_balance(
|
||||
db, db_user, final_price,
|
||||
f"Покупка подписки на {data['period_days']} дней"
|
||||
db,
|
||||
db_user,
|
||||
final_price,
|
||||
f"Покупка подписки на {data['period_days']} дней",
|
||||
consume_promo_offer=promo_offer_discount_value > 0,
|
||||
)
|
||||
|
||||
if not success:
|
||||
@@ -4043,9 +4034,6 @@ async def confirm_purchase(
|
||||
callback_data="back_to_menu")],
|
||||
])
|
||||
|
||||
if promo_offer_discount_value > 0:
|
||||
await _consume_promo_offer_discount(db, db_user)
|
||||
|
||||
await callback.message.edit_text(
|
||||
success_text,
|
||||
reply_markup=connect_keyboard,
|
||||
@@ -4055,9 +4043,6 @@ async def confirm_purchase(
|
||||
purchase_text = texts.SUBSCRIPTION_PURCHASED
|
||||
if discount_note:
|
||||
purchase_text = f"{purchase_text}\n\n{discount_note}"
|
||||
if promo_offer_discount_value > 0:
|
||||
await _consume_promo_offer_discount(db, db_user)
|
||||
|
||||
await callback.message.edit_text(
|
||||
texts.t(
|
||||
"SUBSCRIPTION_LINK_GENERATING_NOTICE",
|
||||
|
||||
Reference in New Issue
Block a user