diff --git a/app/database/crud/promo_group.py b/app/database/crud/promo_group.py index 7e5fab72..d63c3107 100644 --- a/app/database/crud/promo_group.py +++ b/app/database/crud/promo_group.py @@ -1,7 +1,7 @@ import logging -from typing import Any, Dict, List, Optional, Tuple +from typing import Dict, List, Optional, Tuple -from sqlalchemy import desc, func, select, update +from sqlalchemy import func, select, update from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload @@ -27,23 +27,6 @@ def _normalize_period_discounts(period_discounts: Optional[Dict[int, int]]) -> D logger = logging.getLogger(__name__) -_UNSET = object() - - -def _normalize_auto_assign_amount(amount_kopeks: Optional[int]) -> Optional[int]: - if amount_kopeks is None: - return None - - try: - normalized = int(amount_kopeks) - except (TypeError, ValueError): - return None - - if normalized <= 0: - return None - - return normalized - async def get_promo_groups_with_counts( db: AsyncSession, @@ -76,10 +59,8 @@ async def create_promo_group( traffic_discount_percent: int, device_discount_percent: int, period_discounts: Optional[Dict[int, int]] = None, - auto_assign_amount_kopeks: Optional[int] = None, ) -> PromoGroup: normalized_period_discounts = _normalize_period_discounts(period_discounts) - normalized_auto_amount = _normalize_auto_assign_amount(auto_assign_amount_kopeks) promo_group = PromoGroup( name=name.strip(), @@ -87,7 +68,6 @@ async def create_promo_group( traffic_discount_percent=max(0, min(100, traffic_discount_percent)), device_discount_percent=max(0, min(100, device_discount_percent)), period_discounts=normalized_period_discounts or None, - auto_assign_amount_kopeks=normalized_auto_amount, is_default=False, ) @@ -116,7 +96,6 @@ async def update_promo_group( traffic_discount_percent: Optional[int] = None, device_discount_percent: Optional[int] = None, period_discounts: Optional[Dict[int, int]] = None, - auto_assign_amount_kopeks: Any = _UNSET, ) -> PromoGroup: if name is not None: group.name = name.strip() @@ -129,8 +108,6 @@ async def update_promo_group( if period_discounts is not None: normalized_period_discounts = _normalize_period_discounts(period_discounts) group.period_discounts = normalized_period_discounts or None - if auto_assign_amount_kopeks is not _UNSET: - group.auto_assign_amount_kopeks = _normalize_auto_assign_amount(auto_assign_amount_kopeks) await db.commit() await db.refresh(group) @@ -193,23 +170,3 @@ async def count_promo_group_members(db: AsyncSession, group_id: int) -> int: select(func.count(User.id)).where(User.promo_group_id == group_id) ) return result.scalar_one() - - -async def get_auto_assign_promo_group( - db: AsyncSession, - total_amount_kopeks: int, -) -> Optional[PromoGroup]: - if total_amount_kopeks <= 0: - return None - - result = await db.execute( - select(PromoGroup) - .where( - PromoGroup.auto_assign_amount_kopeks.is_not(None), - PromoGroup.auto_assign_amount_kopeks > 0, - PromoGroup.auto_assign_amount_kopeks <= total_amount_kopeks, - ) - .order_by(desc(PromoGroup.auto_assign_amount_kopeks), PromoGroup.id) - ) - - return result.scalars().first() diff --git a/app/database/crud/transaction.py b/app/database/crud/transaction.py index 5102100a..b258f1b2 100644 --- a/app/database/crud/transaction.py +++ b/app/database/crud/transaction.py @@ -98,21 +98,6 @@ async def get_user_transactions_count( return result.scalar() -async def get_user_total_completed_deposits(db: AsyncSession, user_id: int) -> int: - result = await db.execute( - select(func.coalesce(func.sum(Transaction.amount_kopeks), 0)) - .where( - and_( - Transaction.user_id == user_id, - Transaction.type == TransactionType.DEPOSIT.value, - Transaction.is_completed.is_(True), - ) - ) - ) - - return result.scalar_one() - - async def complete_transaction(db: AsyncSession, transaction: Transaction) -> Transaction: transaction.is_completed = True diff --git a/app/database/crud/user.py b/app/database/crud/user.py index 4e228735..582c8695 100644 --- a/app/database/crud/user.py +++ b/app/database/crud/user.py @@ -116,7 +116,6 @@ async def create_user( has_had_paid_subscription=False, has_made_first_topup=False, promo_group_id=promo_group_id, - promo_group_auto_assigned=False, ) db.add(user) diff --git a/app/database/models.py b/app/database/models.py index df882864..9cdeaa86 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -271,7 +271,6 @@ class PromoGroup(Base): traffic_discount_percent = Column(Integer, nullable=False, default=0) device_discount_percent = Column(Integer, nullable=False, default=0) period_discounts = Column(JSON, nullable=True, default=dict) - auto_assign_amount_kopeks = Column(Integer, nullable=True) 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()) @@ -336,7 +335,7 @@ class PromoGroup(Base): class User(Base): __tablename__ = "users" - + id = Column(Integer, primary_key=True, index=True) telegram_id = Column(BigInteger, unique=True, index=True, nullable=False) username = Column(String(255), nullable=True) @@ -366,7 +365,6 @@ class User(Base): has_made_first_topup: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) promo_group_id = Column(Integer, ForeignKey("promo_groups.id", ondelete="RESTRICT"), nullable=False, index=True) promo_group = relationship("PromoGroup", back_populates="users") - promo_group_auto_assigned: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) @property def balance_rubles(self) -> float: diff --git a/app/database/universal_migration.py b/app/database/universal_migration.py index 17179520..ce270f13 100644 --- a/app/database/universal_migration.py +++ b/app/database/universal_migration.py @@ -902,81 +902,6 @@ async def ensure_promo_groups_setup(): logger.error(f"Ошибка настройки промо групп: {e}") return False - -async def add_promo_group_auto_assign_column(): - logger.info("=== ДОБАВЛЕНИЕ ПОЛЯ АВТОВЫДАЧИ ДЛЯ ПРОМОГРУПП ===") - - try: - if await check_column_exists("promo_groups", "auto_assign_amount_kopeks"): - logger.info("Колонка auto_assign_amount_kopeks уже существует в promo_groups") - return True - - db_type = await get_database_type() - - if db_type == "sqlite": - column_definition = "INTEGER" - elif db_type == "postgresql": - column_definition = "INTEGER" - elif db_type == "mysql": - column_definition = "INT" - else: - logger.error(f"Неподдерживаемый тип БД для auto_assign_amount_kopeks: {db_type}") - return False - - async with engine.begin() as conn: - await conn.execute( - text( - f"ALTER TABLE promo_groups ADD COLUMN auto_assign_amount_kopeks {column_definition}" - ) - ) - - logger.info("Добавлена колонка promo_groups.auto_assign_amount_kopeks") - return True - - except Exception as e: - logger.error(f"Ошибка добавления колонки auto_assign_amount_kopeks: {e}") - return False - - -async def add_user_promo_group_auto_flag_column(): - logger.info("=== ДОБАВЛЕНИЕ ФЛАГА АВТО-ПРОМОГРУППЫ ДЛЯ ПОЛЬЗОВАТЕЛЕЙ ===") - - try: - if await check_column_exists("users", "promo_group_auto_assigned"): - logger.info("Колонка promo_group_auto_assigned уже существует в users") - return True - - db_type = await get_database_type() - - if db_type == "sqlite": - column_definition = "BOOLEAN NOT NULL DEFAULT 0" - reset_sql = "UPDATE users SET promo_group_auto_assigned = 0 WHERE promo_group_auto_assigned IS NULL" - elif db_type == "postgresql": - column_definition = "BOOLEAN NOT NULL DEFAULT FALSE" - reset_sql = "UPDATE users SET promo_group_auto_assigned = FALSE WHERE promo_group_auto_assigned IS NULL" - elif db_type == "mysql": - column_definition = "TINYINT(1) NOT NULL DEFAULT 0" - reset_sql = "UPDATE users SET promo_group_auto_assigned = 0 WHERE promo_group_auto_assigned IS NULL" - else: - logger.error(f"Неподдерживаемый тип БД для promo_group_auto_assigned: {db_type}") - return False - - async with engine.begin() as conn: - await conn.execute( - text( - f"ALTER TABLE users ADD COLUMN promo_group_auto_assigned {column_definition}" - ) - ) - await conn.execute(text(reset_sql)) - - logger.info("Добавлена колонка users.promo_group_auto_assigned") - return True - - except Exception as e: - logger.error(f"Ошибка добавления колонки promo_group_auto_assigned: {e}") - return False - - async def add_welcome_text_is_enabled_column(): column_exists = await check_column_exists('welcome_texts', 'is_enabled') if column_exists: @@ -1586,18 +1511,6 @@ async def run_universal_migration(): else: logger.warning("⚠️ Проблемы с настройкой промо групп") - promo_auto_column_added = await add_promo_group_auto_assign_column() - if promo_auto_column_added: - logger.info("✅ Добавлено поле авто-выдачи в промо группах") - else: - logger.warning("⚠️ Не удалось добавить поле авто-выдачи в промо группах") - - user_auto_flag_added = await add_user_promo_group_auto_flag_column() - if user_auto_flag_added: - logger.info("✅ Добавлен флаг auto_assigned у пользователей") - else: - logger.warning("⚠️ Не удалось добавить флаг auto_assigned у пользователей") - logger.info("=== ОБНОВЛЕНИЕ ВНЕШНИХ КЛЮЧЕЙ ===") fk_updated = await fix_foreign_keys_for_user_deletion() if fk_updated: @@ -1672,8 +1585,6 @@ async def check_migration_status(): "promo_groups_table": False, "users_promo_group_column": False, "promo_groups_period_discounts_column": False, - "promo_groups_auto_assign_column": False, - "users_promo_group_auto_flag_column": False, } status["has_made_first_topup_column"] = await check_column_exists('users', 'has_made_first_topup') @@ -1687,8 +1598,6 @@ async def check_migration_status(): status["welcome_texts_is_enabled_column"] = await check_column_exists('welcome_texts', 'is_enabled') 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_amount_kopeks') - status["users_promo_group_auto_flag_column"] = await check_column_exists('users', 'promo_group_auto_assigned') media_fields_exist = ( await check_column_exists('broadcast_history', 'has_media') and @@ -1722,8 +1631,6 @@ async def check_migration_status(): "promo_groups_table": "Таблица промо-групп", "users_promo_group_column": "Колонка promo_group_id у пользователей", "promo_groups_period_discounts_column": "Колонка period_discounts у промо-групп", - "promo_groups_auto_assign_column": "Колонка auto_assign_amount_kopeks у промо-групп", - "users_promo_group_auto_flag_column": "Флаг auto_assigned у пользователей", } for check_key, check_status in status.items(): diff --git a/app/handlers/admin/promo_groups.py b/app/handlers/admin/promo_groups.py index 567eead2..0546550e 100644 --- a/app/handlers/admin/promo_groups.py +++ b/app/handlers/admin/promo_groups.py @@ -1,5 +1,5 @@ import logging -from decimal import Decimal, InvalidOperation, ROUND_HALF_UP +import logging from typing import Dict, Optional from aiogram import Dispatcher, types, F @@ -109,23 +109,6 @@ def _format_period_discounts_value(discounts: Dict[int, int]) -> str: ) -def _format_auto_assign_line(texts, group: PromoGroup) -> Optional[str]: - amount = getattr(group, "auto_assign_amount_kopeks", None) - if not amount: - return None - - return texts.t( - "ADMIN_PROMO_GROUP_AUTO_ASSIGN_LINE", - "🎯 Автовыдача с суммы: {amount}", - ).format(amount=settings.format_price(amount)) - - -def _format_auto_assign_value(amount_kopeks: Optional[int]) -> str: - if not amount_kopeks: - return settings.format_price(0) - return settings.format_price(amount_kopeks) - - def _parse_period_discounts_input(value: str) -> Dict[int, int]: cleaned = (value or "").strip() @@ -157,29 +140,6 @@ def _parse_period_discounts_input(value: str) -> Dict[int, int]: return discounts -def _parse_auto_assign_amount_input(value: str) -> Optional[int]: - cleaned = (value or "").strip() - - if not cleaned: - raise ValueError - - normalized = cleaned.replace(" ", "").replace(",", ".") - - if normalized in {"0", "-", "нет", "off", "disable"}: - return None - - try: - decimal_value = Decimal(normalized) - except (InvalidOperation, ValueError): - raise ValueError - - if decimal_value <= 0: - return None - - kopeks = (decimal_value * Decimal("100")).quantize(Decimal("1"), rounding=ROUND_HALF_UP) - return int(kopeks) - - async def _prompt_for_period_discounts( message: types.Message, state: FSMContext, @@ -201,27 +161,6 @@ async def _prompt_for_period_discounts( await message.answer(prompt_text) -async def _prompt_for_auto_assign_amount( - message: types.Message, - state: FSMContext, - prompt_key: str, - default_text: str, - *, - current_value: Optional[str] = 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: - try: - prompt_text = prompt_text.format(current=current_value) - except KeyError: - pass - - await message.answer(prompt_text) - - @admin_required @error_handler async def show_promo_groups_menu( @@ -252,18 +191,11 @@ async def show_promo_groups_menu( group_lines = [ f"{'⭐' if group.is_default else '🎯'} {group.name}{default_suffix}", _format_discount_line(texts, group), - ] - - auto_line = _format_auto_assign_line(texts, group) - if auto_line: - group_lines.append(auto_line) - - group_lines.extend([ texts.t( "ADMIN_PROMO_GROUPS_MEMBERS_COUNT", "Участников: {count}", ).format(count=member_count), - ]) + ] period_lines = _format_period_discounts_lines(texts, group, db_user.language) group_lines.extend(period_lines) @@ -333,18 +265,11 @@ async def show_promo_group_details( "💳 Промогруппа: {name}", ).format(name=group.name), _format_discount_line(texts, group), - ] - - auto_line = _format_auto_assign_line(texts, group) - if auto_line: - lines.append(auto_line) - - lines.append( texts.t( "ADMIN_PROMO_GROUP_DETAILS_MEMBERS", "Участников: {count}", ).format(count=member_count), - ) + ] period_lines = _format_period_discounts_lines(texts, group, db_user.language) lines.extend(period_lines) @@ -539,39 +464,6 @@ async def process_create_group_period_discounts( ) return - await state.update_data(new_group_period_discounts=period_discounts) - await state.set_state(AdminStates.creating_promo_group_auto_amount) - - await _prompt_for_auto_assign_amount( - message, - state, - "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT", - "Введите сумму пополнений (в рублях) для автоматической выдачи. Отправьте 0, если не нужно.", - ) - - -@admin_required -@error_handler -async def process_create_group_auto_amount( - message: types.Message, - state: FSMContext, - db_user, - db: AsyncSession, -): - data = await state.get_data() - texts = get_texts(data.get("language", db_user.language)) - - try: - auto_amount = _parse_auto_assign_amount_input(message.text) - except ValueError: - await message.answer( - texts.t( - "ADMIN_PROMO_GROUP_INVALID_AUTO_ASSIGN", - "Введите корректную сумму или 0 для отключения.", - ) - ) - return - try: group = await create_promo_group( db, @@ -579,8 +471,7 @@ async def process_create_group_auto_amount( traffic_discount_percent=data["new_group_traffic"], server_discount_percent=data["new_group_servers"], device_discount_percent=data["new_group_devices"], - period_discounts=data.get("new_group_period_discounts"), - auto_assign_amount_kopeks=auto_amount, + period_discounts=period_discounts, ) except Exception as e: logger.error(f"Не удалось создать промогруппу: {e}") @@ -756,46 +647,6 @@ async def process_edit_group_period_discounts( await state.clear() return - await state.update_data(edit_group_period_discounts=period_discounts) - await state.set_state(AdminStates.editing_promo_group_auto_amount) - - await _prompt_for_auto_assign_amount( - message, - state, - "ADMIN_PROMO_GROUP_EDIT_AUTO_ASSIGN_PROMPT", - "Введите новую сумму (текущая: {current}). Отправьте 0, если без автовыдачи.", - current_value=_format_auto_assign_value(getattr(group, "auto_assign_amount_kopeks", None)), - ) - - -@admin_required -@error_handler -async def process_edit_group_auto_amount( - message: types.Message, - state: FSMContext, - db_user, - db: AsyncSession, -): - data = await state.get_data() - texts = get_texts(data.get("language", db_user.language)) - - try: - auto_amount = _parse_auto_assign_amount_input(message.text) - except ValueError: - await message.answer( - texts.t( - "ADMIN_PROMO_GROUP_INVALID_AUTO_ASSIGN", - "Введите корректную сумму или 0 для отключения.", - ) - ) - return - - group = await get_promo_group_by_id(db, data.get("edit_group_id")) - if not group: - await message.answer("❌ Промогруппа не найдена") - await state.clear() - return - await update_promo_group( db, group, @@ -803,8 +654,7 @@ async def process_edit_group_auto_amount( traffic_discount_percent=data["edit_group_traffic"], server_discount_percent=data["edit_group_servers"], device_discount_percent=data["edit_group_devices"], - period_discounts=data.get("edit_group_period_discounts"), - auto_assign_amount_kopeks=auto_amount, + period_discounts=period_discounts, ) await state.clear() @@ -978,10 +828,6 @@ def register_handlers(dp: Dispatcher): process_create_group_period_discounts, AdminStates.creating_promo_group_period_discount, ) - dp.message.register( - process_create_group_auto_amount, - AdminStates.creating_promo_group_auto_amount, - ) dp.message.register(process_edit_group_name, AdminStates.editing_promo_group_name) dp.message.register( @@ -1000,7 +846,3 @@ def register_handlers(dp: Dispatcher): process_edit_group_period_discounts, AdminStates.editing_promo_group_period_discount, ) - dp.message.register( - process_edit_group_auto_amount, - AdminStates.editing_promo_group_auto_amount, - ) diff --git a/app/services/payment_service.py b/app/services/payment_service.py index 17c39bd7..a761ef5c 100644 --- a/app/services/payment_service.py +++ b/app/services/payment_service.py @@ -30,7 +30,6 @@ from app.services.subscription_checkout_service import ( ) from app.services.mulenpay_service import MulenPayService from app.services.pal24_service import Pal24Service, Pal24APIError -from app.services.promo_group_service import maybe_assign_auto_promo_group from app.database.crud.mulenpay import ( create_mulenpay_payment, get_mulenpay_payment_by_local_id, @@ -180,9 +179,7 @@ class PaymentService: logger.error(f"Ошибка обработки реферального пополнения: {e}") else: logger.info(f"❌ Описание '{description_for_referral}' не подходит для реферальной логики") - - await maybe_assign_auto_promo_group(db, user, self.bot) - + if self.bot: try: from app.services.admin_notification_service import AdminNotificationService @@ -464,9 +461,7 @@ class PaymentService: await process_referral_topup(db, user.id, updated_payment.amount_kopeks, self.bot) except Exception as e: logger.error(f"Ошибка обработки реферального пополнения YooKassa: {e}") - - await maybe_assign_auto_promo_group(db, user, self.bot) - + if self.bot: try: from app.services.admin_notification_service import AdminNotificationService @@ -533,8 +528,7 @@ class PaymentService: user = await get_user_by_id(db, payment.user_id) if user: await add_user_balance(db, user, payment.amount_kopeks, f"Пополнение YooKassa: {payment.amount_kopeks//100}₽") - await maybe_assign_auto_promo_group(db, user, self.bot) - + logger.info(f"Успешно обработан платеж YooKassa {payment.yookassa_payment_id}: " f"пользователь {payment.user_id} получил {payment.amount_kopeks/100}₽") @@ -999,8 +993,6 @@ class PaymentService: referral_error, ) - await maybe_assign_auto_promo_group(db, user, self.bot) - await update_mulenpay_payment_status( db, payment=payment, @@ -1190,8 +1182,6 @@ class PaymentService: except Exception as referral_error: logger.error("Ошибка обработки реферального пополнения Pal24: %s", referral_error) - await maybe_assign_auto_promo_group(db, user, self.bot) - if self.bot: try: from app.services.admin_notification_service import AdminNotificationService @@ -1448,9 +1438,7 @@ class PaymentService: await process_referral_topup(db, user.id, amount_kopeks, self.bot) except Exception as e: logger.error(f"Ошибка обработки реферального пополнения CryptoBot: {e}") - - await maybe_assign_auto_promo_group(db, user, self.bot) - + if self.bot: try: from app.services.admin_notification_service import AdminNotificationService diff --git a/app/services/promo_group_service.py b/app/services/promo_group_service.py deleted file mode 100644 index 02ad4dbc..00000000 --- a/app/services/promo_group_service.py +++ /dev/null @@ -1,77 +0,0 @@ -import logging -from datetime import datetime -from typing import Optional - -from aiogram import Bot -from sqlalchemy.ext.asyncio import AsyncSession - -from app.config import settings -from app.database.crud.promo_group import get_auto_assign_promo_group -from app.database.crud.transaction import get_user_total_completed_deposits -from app.database.models import PromoGroup, User -from app.localization.texts import get_texts - -logger = logging.getLogger(__name__) - - -def _format_total_amount(total_amount_kopeks: int) -> str: - return settings.format_price(total_amount_kopeks) - - -async def maybe_assign_auto_promo_group( - db: AsyncSession, - user: User, - bot: Optional[Bot] = None, -) -> Optional[PromoGroup]: - """Назначает промогруппу автоматически при достижении нужной суммы пополнений.""" - try: - if getattr(user, "promo_group_auto_assigned", False): - return None - - total_amount_kopeks = await get_user_total_completed_deposits(db, user.id) - target_group = await get_auto_assign_promo_group(db, total_amount_kopeks) - - if not target_group or target_group.id == user.promo_group_id: - return None - - user.promo_group_id = target_group.id - user.promo_group = target_group - user.promo_group_auto_assigned = True - user.updated_at = datetime.utcnow() - - await db.commit() - await db.refresh(user) - - logger.info( - "Автоматически назначена промогруппа '%s' пользователю %s (сумма пополнений: %s)", - target_group.name, - user.telegram_id, - _format_total_amount(total_amount_kopeks), - ) - - if bot: - try: - texts = get_texts(user.language) - message = texts.t( - "PROMO_GROUP_AUTO_ASSIGN_NOTIFICATION", - "🎉 Вы автоматически переведены в промогруппу «{name}» за пополнения на {amount}.", - ).format(name=target_group.name, amount=_format_total_amount(total_amount_kopeks)) - await bot.send_message(user.telegram_id, message, parse_mode="HTML") - except Exception as notify_error: - logger.error( - "Ошибка отправки уведомления об автоназначении промогруппы пользователю %s: %s", - user.telegram_id, - notify_error, - ) - - return target_group - - except Exception as error: - logger.error( - "Ошибка автоматического назначения промогруппы пользователю %s: %s", - getattr(user, "telegram_id", "unknown"), - error, - exc_info=True, - ) - await db.rollback() - return None diff --git a/app/services/tribute_service.py b/app/services/tribute_service.py index 8dd427f4..6d5de2eb 100644 --- a/app/services/tribute_service.py +++ b/app/services/tribute_service.py @@ -14,7 +14,6 @@ from app.database.crud.transaction import ( from app.database.crud.user import get_user_by_telegram_id, add_user_balance from app.external.tribute import TributeService as TributeAPI from app.services.payment_service import PaymentService -from app.services.promo_group_service import maybe_assign_auto_promo_group logger = logging.getLogger(__name__) @@ -140,10 +139,8 @@ class TributeService: if not user.has_made_first_topup: user.has_made_first_topup = True logger.info(f"Отмечен первый топап для пользователя {user_telegram_id}") - - await maybe_assign_auto_promo_group(session, user, self.bot) - - + + try: from app.services.admin_notification_service import AdminNotificationService notification_service = AdminNotificationService(self.bot) @@ -336,13 +333,11 @@ class TributeService: old_balance = user.balance_kopeks user.balance_kopeks += amount_kopeks user.updated_at = datetime.utcnow() - + await session.commit() - - await maybe_assign_auto_promo_group(session, user, self.bot) - + logger.info(f"💰 ПРИНУДИТЕЛЬНО обновлен баланс: {old_balance} -> {user.balance_kopeks} коп") - + await self._send_success_notification(user_id, amount_kopeks) logger.info(f"✅ Принудительно обработан платеж {payment_id}") diff --git a/app/states.py b/app/states.py index 940986d2..0073a4d4 100644 --- a/app/states.py +++ b/app/states.py @@ -69,14 +69,12 @@ class AdminStates(StatesGroup): creating_promo_group_server_discount = State() creating_promo_group_device_discount = State() creating_promo_group_period_discount = State() - creating_promo_group_auto_amount = State() editing_promo_group_name = State() editing_promo_group_traffic_discount = State() editing_promo_group_server_discount = State() editing_promo_group_device_discount = State() editing_promo_group_period_discount = State() - editing_promo_group_auto_amount = State() editing_squad_price = State() editing_traffic_price = State() diff --git a/locales/en.json b/locales/en.json index 51508f0d..419dbe95 100644 --- a/locales/en.json +++ b/locales/en.json @@ -141,7 +141,6 @@ "ADMIN_PROMO_GROUPS_DEFAULT_LABEL": " (default)", "ADMIN_PROMO_GROUPS_MEMBERS_COUNT": "Members: {count}", "ADMIN_PROMO_GROUPS_EMPTY": "No promo groups found.", - "ADMIN_PROMO_GROUP_AUTO_ASSIGN_LINE": "🎯 Auto assignment from: {amount}", "CREATE_TICKET_BUTTON": "🎫 Create ticket", "MY_TICKETS_BUTTON": "📋 My tickets", "CONTACT_SUPPORT_BUTTON": "💬 Contact support", @@ -227,10 +226,8 @@ "ADMIN_PROMO_GROUP_CREATE_SERVERS_PROMPT": "Enter server discount (0-100):", "ADMIN_PROMO_GROUP_CREATE_DEVICES_PROMPT": "Enter device discount (0-100):", "ADMIN_PROMO_GROUP_CREATE_PERIOD_PROMPT": "Enter subscription period discounts (e.g. 30:10, 90:15). Send 0 if none.", - "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Enter the top-up amount (in rubles) for automatic assignment. Send 0 to disable.", "ADMIN_PROMO_GROUP_INVALID_PERCENT": "Enter a number from 0 to 100.", "ADMIN_PROMO_GROUP_INVALID_PERIOD_DISCOUNTS": "Enter period:discount pairs separated by commas, e.g. 30:10, 90:15, or 0.", - "ADMIN_PROMO_GROUP_INVALID_AUTO_ASSIGN": "Enter a valid amount or 0 to disable.", "ADMIN_PROMO_GROUP_CREATED": "Promo group “{name}” created.", "ADMIN_PROMO_GROUP_CREATED_BACK_BUTTON": "↩️ Back to promo groups", "ADMIN_PROMO_GROUP_EDIT_NAME_PROMPT": "Enter a new name (current: {name}):", @@ -238,7 +235,6 @@ "ADMIN_PROMO_GROUP_EDIT_SERVERS_PROMPT": "Enter new server discount (0-100):", "ADMIN_PROMO_GROUP_EDIT_DEVICES_PROMPT": "Enter new device discount (0-100):", "ADMIN_PROMO_GROUP_EDIT_PERIOD_PROMPT": "Enter new period discounts (current: {current}). Send 0 if none.", - "ADMIN_PROMO_GROUP_EDIT_AUTO_ASSIGN_PROMPT": "Enter a new amount (current: {current}). Send 0 to disable.", "ADMIN_PROMO_GROUP_UPDATED": "Promo group “{name}” updated.", "ADMIN_PROMO_GROUP_MEMBERS_TITLE": "👥 Members of {name}", "ADMIN_PROMO_GROUP_MEMBERS_EMPTY": "This group has no members yet.", @@ -267,7 +263,6 @@ "PROMO_GROUP_DISCOUNT_DEVICES": "📱 Extra devices: {percent}%", "PROMO_GROUP_PERIOD_DISCOUNTS_HEADER": "⏳ Long-term period discounts:", "PROMO_GROUP_PERIOD_DISCOUNT_ITEM": "{period} — {percent}%", - "PROMO_GROUP_AUTO_ASSIGN_NOTIFICATION": "🎉 You have been automatically moved to the promo group “{name}” for topping up {amount}.", "CHANGE_DEVICES_CONFIRM": "\n📱 Confirm change\n\nCurrent amount: {current_devices} devices\nNew amount: {new_devices} devices\n\nAction: {action}\n💰 {cost}\n\nApply this change?\n", "CHANGE_DEVICES_INFO": "\n📱 Adjust device limit\n\nCurrent limit: {current_devices} devices\n\nChoose the new number of devices:\n\n💡 Important:\n• Increasing — extra charge proportional to the remaining time\n• Decreasing — funds are not refunded\n", "CHANGE_DEVICES_SUCCESS_DECREASE": "\n✅ Device limit decreased!\n\n📱 Was: {old_count} → Now: {new_count}\nℹ️ Payments are not refunded\n", diff --git a/locales/ru.json b/locales/ru.json index ac1c6d0d..e69b9d92 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -19,7 +19,6 @@ "ADMIN_PROMO_GROUPS_DEFAULT_LABEL": " (базовая)", "ADMIN_PROMO_GROUPS_MEMBERS_COUNT": "Участников: {count}", "ADMIN_PROMO_GROUPS_EMPTY": "Промогруппы не найдены.", - "ADMIN_PROMO_GROUP_AUTO_ASSIGN_LINE": "🎯 Автовыдача с суммы: {amount}", "CREATE_TICKET_BUTTON": "🎫 Создать тикет", "MY_TICKETS_BUTTON": "📋 Мои тикеты", "CONTACT_SUPPORT_BUTTON": "💬 Связаться с поддержкой", @@ -105,10 +104,8 @@ "ADMIN_PROMO_GROUP_CREATE_SERVERS_PROMPT": "Введите скидку на серверы (0-100):", "ADMIN_PROMO_GROUP_CREATE_DEVICES_PROMPT": "Введите скидку на устройства (0-100):", "ADMIN_PROMO_GROUP_CREATE_PERIOD_PROMPT": "Введите скидки на периоды подписки (например, 30:10, 90:15). Отправьте 0, если без скидок.", - "ADMIN_PROMO_GROUP_CREATE_AUTO_ASSIGN_PROMPT": "Введите сумму пополнений (в рублях) для автоматической выдачи. Отправьте 0, если без автовыдачи.", "ADMIN_PROMO_GROUP_INVALID_PERCENT": "Введите число от 0 до 100.", "ADMIN_PROMO_GROUP_INVALID_PERIOD_DISCOUNTS": "Введите пары период:скидка через запятую, например 30:10, 90:15, или 0.", - "ADMIN_PROMO_GROUP_INVALID_AUTO_ASSIGN": "Введите корректную сумму или 0 для отключения.", "ADMIN_PROMO_GROUP_CREATED": "Промогруппа «{name}» создана.", "ADMIN_PROMO_GROUP_CREATED_BACK_BUTTON": "↩️ К промогруппам", "ADMIN_PROMO_GROUP_EDIT_NAME_PROMPT": "Введите новое название промогруппы (текущее: {name}):", @@ -116,7 +113,6 @@ "ADMIN_PROMO_GROUP_EDIT_SERVERS_PROMPT": "Введите новую скидку на серверы (0-100):", "ADMIN_PROMO_GROUP_EDIT_DEVICES_PROMPT": "Введите новую скидку на устройства (0-100):", "ADMIN_PROMO_GROUP_EDIT_PERIOD_PROMPT": "Введите новые скидки на периоды (текущие: {current}). Отправьте 0, если без скидок.", - "ADMIN_PROMO_GROUP_EDIT_AUTO_ASSIGN_PROMPT": "Введите новую сумму (текущая: {current}). Отправьте 0, если без автовыдачи.", "ADMIN_PROMO_GROUP_UPDATED": "Промогруппа «{name}» обновлена.", "ADMIN_PROMO_GROUP_MEMBERS_TITLE": "👥 Участники группы {name}", "ADMIN_PROMO_GROUP_MEMBERS_EMPTY": "В этой группе пока нет участников.", @@ -150,7 +146,6 @@ "PROMO_GROUP_DISCOUNT_DEVICES": "📱 Доп. устройства: {percent}%", "PROMO_GROUP_PERIOD_DISCOUNTS_HEADER": "⏳ Скидки за длительный период:", "PROMO_GROUP_PERIOD_DISCOUNT_ITEM": "{period} — {percent}%", - "PROMO_GROUP_AUTO_ASSIGN_NOTIFICATION": "🎉 Вы автоматически переведены в промогруппу «{name}» за пополнения на {amount}.", "CANCEL": "❌ Отмена", "CHANGE_DEVICES_BUTTON": "📱 Изменить устройства", "CHANGE_DEVICES_CONFIRM": "\n 📱 Подтверждение изменения\n\n Текущее количество: {current_devices} устройств\n Новое количество: {new_devices} устройств\n\n Действие: {action}\n 💰 {cost}\n\n Подтвердить изменение?\n ",