diff --git a/app/services/referral_service.py b/app/services/referral_service.py index bbf6e810..2302cf45 100644 --- a/app/services/referral_service.py +++ b/app/services/referral_service.py @@ -89,16 +89,64 @@ async def process_referral_topup( logger.info(f"Пользователь {user_id} не является рефералом") return True - if topup_amount_kopeks < settings.REFERRAL_MINIMUM_TOPUP_KOPEKS: - logger.info(f"Пополнение {user_id} на {topup_amount_kopeks/100}₽ меньше минимума") - return True - referrer = await get_user_by_id(db, user.referred_by_id) if not referrer: logger.error(f"Реферер {user.referred_by_id} не найден") return False - + + qualifies_for_first_bonus = ( + topup_amount_kopeks >= settings.REFERRAL_MINIMUM_TOPUP_KOPEKS + ) + commission_amount = 0 + if settings.REFERRAL_COMMISSION_PERCENT > 0: + commission_amount = int( + topup_amount_kopeks * settings.REFERRAL_COMMISSION_PERCENT / 100 + ) + if not user.has_made_first_topup: + if not qualifies_for_first_bonus: + logger.info( + "Пополнение %s на %s₽ меньше минимума для первого бонуса, но комиссия будет начислена", + user_id, + topup_amount_kopeks / 100, + ) + + if commission_amount > 0: + await add_user_balance( + db, + referrer, + commission_amount, + f"Комиссия {settings.REFERRAL_COMMISSION_PERCENT}% с пополнения {user.full_name}", + bot=bot, + ) + + await create_referral_earning( + db=db, + user_id=referrer.id, + referral_id=user.id, + amount_kopeks=commission_amount, + reason="referral_commission_topup", + ) + + logger.info( + "💰 Комиссия с пополнения: %s получил %s₽ (до первого бонуса)", + referrer.telegram_id, + commission_amount / 100, + ) + + if bot: + commission_notification = ( + f"💰 Реферальная комиссия!\n\n" + f"Ваш реферал {user.full_name} пополнил баланс на " + f"{settings.format_price(topup_amount_kopeks)}\n\n" + f"🎁 Ваша комиссия ({settings.REFERRAL_COMMISSION_PERCENT}%): " + f"{settings.format_price(commission_amount)}\n\n" + f"💎 Средства зачислены на ваш баланс." + ) + await send_referral_notification(bot, referrer.telegram_id, commission_notification) + + return True + user.has_made_first_topup = True await db.commit() @@ -161,36 +209,33 @@ async def process_referral_topup( await send_referral_notification(bot, referrer.telegram_id, inviter_bonus_notification) else: - if settings.REFERRAL_COMMISSION_PERCENT > 0: - commission_amount = int(topup_amount_kopeks * settings.REFERRAL_COMMISSION_PERCENT / 100) - - if commission_amount > 0: - await add_user_balance( - db, referrer, commission_amount, - f"Комиссия {settings.REFERRAL_COMMISSION_PERCENT}% с пополнения {user.full_name}", - bot=bot + if commission_amount > 0: + await add_user_balance( + db, referrer, commission_amount, + f"Комиссия {settings.REFERRAL_COMMISSION_PERCENT}% с пополнения {user.full_name}", + bot=bot + ) + + await create_referral_earning( + db=db, + user_id=referrer.id, + referral_id=user.id, + amount_kopeks=commission_amount, + reason="referral_commission_topup" + ) + + logger.info(f"💰 Комиссия с пополнения: {referrer.telegram_id} получил {commission_amount/100}₽") + + if bot: + commission_notification = ( + f"💰 Реферальная комиссия!\n\n" + f"Ваш реферал {user.full_name} пополнил баланс на " + f"{settings.format_price(topup_amount_kopeks)}\n\n" + f"🎁 Ваша комиссия ({settings.REFERRAL_COMMISSION_PERCENT}%): " + f"{settings.format_price(commission_amount)}\n\n" + f"💎 Средства зачислены на ваш баланс." ) - - await create_referral_earning( - db=db, - user_id=referrer.id, - referral_id=user.id, - amount_kopeks=commission_amount, - reason="referral_commission_topup" - ) - - logger.info(f"💰 Комиссия с пополнения: {referrer.telegram_id} получил {commission_amount/100}₽") - - if bot: - commission_notification = ( - f"💰 Реферальная комиссия!\n\n" - f"Ваш реферал {user.full_name} пополнил баланс на " - f"{settings.format_price(topup_amount_kopeks)}\n\n" - f"🎁 Ваша комиссия ({settings.REFERRAL_COMMISSION_PERCENT}%): " - f"{settings.format_price(commission_amount)}\n\n" - f"💎 Средства зачислены на ваш баланс." - ) - await send_referral_notification(bot, referrer.telegram_id, commission_notification) + await send_referral_notification(bot, referrer.telegram_id, commission_notification) return True diff --git a/tests/services/test_referral_service.py b/tests/services/test_referral_service.py new file mode 100644 index 00000000..c59c5bd5 --- /dev/null +++ b/tests/services/test_referral_service.py @@ -0,0 +1,67 @@ +from pathlib import Path +import sys +from types import SimpleNamespace +from unittest.mock import AsyncMock + +import pytest + +ROOT_DIR = Path(__file__).resolve().parents[2] +if str(ROOT_DIR) not in sys.path: + sys.path.insert(0, str(ROOT_DIR)) + +from app.services import referral_service # noqa: E402 + + +@pytest.mark.asyncio +async def test_commission_accrues_before_minimum_first_topup(monkeypatch): + user = SimpleNamespace( + id=1, + telegram_id=101, + full_name="Test User", + referred_by_id=2, + has_made_first_topup=False, + ) + referrer = SimpleNamespace( + id=2, + telegram_id=202, + full_name="Referrer", + ) + + db = SimpleNamespace( + commit=AsyncMock(), + execute=AsyncMock(), + ) + + get_user_mock = AsyncMock(side_effect=[user, referrer]) + monkeypatch.setattr(referral_service, "get_user_by_id", get_user_mock) + add_user_balance_mock = AsyncMock() + monkeypatch.setattr(referral_service, "add_user_balance", add_user_balance_mock) + create_referral_earning_mock = AsyncMock() + monkeypatch.setattr(referral_service, "create_referral_earning", create_referral_earning_mock) + + monkeypatch.setattr(referral_service.settings, "REFERRAL_MINIMUM_TOPUP_KOPEKS", 20000) + monkeypatch.setattr(referral_service.settings, "REFERRAL_FIRST_TOPUP_BONUS_KOPEKS", 5000) + monkeypatch.setattr(referral_service.settings, "REFERRAL_INVITER_BONUS_KOPEKS", 10000) + monkeypatch.setattr(referral_service.settings, "REFERRAL_COMMISSION_PERCENT", 25) + + topup_amount = 15000 + + result = await referral_service.process_referral_topup(db, user.id, topup_amount) + + assert result is True + assert user.has_made_first_topup is False + + add_user_balance_mock.assert_awaited_once() + add_call = add_user_balance_mock.await_args + assert add_call.args[1] is referrer + assert add_call.args[2] == 3750 + assert "Комиссия" in add_call.args[3] + assert add_call.kwargs.get("bot") is None + + create_referral_earning_mock.assert_awaited_once() + earning_call = create_referral_earning_mock.await_args + assert earning_call.kwargs["amount_kopeks"] == 3750 + assert earning_call.kwargs["reason"] == "referral_commission_topup" + + db.commit.assert_not_awaited() + db.execute.assert_not_awaited()