From 1bb5ef85aaf84239f9fec9b770d91c8c3b89cf46 Mon Sep 17 00:00:00 2001 From: Egor Date: Sat, 31 Jan 2026 17:04:59 +0300 Subject: [PATCH 1/2] Update inline.py --- app/keyboards/inline.py | 43 +++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/app/keyboards/inline.py b/app/keyboards/inline.py index 5d8b7a19..5450ad99 100644 --- a/app/keyboards/inline.py +++ b/app/keyboards/inline.py @@ -1924,12 +1924,28 @@ def get_change_devices_keyboard( 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} мес)' + # Проверяем является ли тариф суточным + is_daily_tariff = tariff and getattr(tariff, 'is_daily', False) + + # Для суточных тарифов считаем по дням, для обычных - по месяцам + if is_daily_tariff and subscription_end_date: + # Суточный тариф: цена за оставшиеся дни (обычно 1 день) + from datetime import datetime + + now = datetime.utcnow() + days_left = max(1, (subscription_end_date - now).days) + # Множитель = days_left / 30 (как в кабинете) + price_multiplier = days_left / 30 + period_text = f' (за {days_left} дн.)' if days_left > 1 else ' (за 1 день)' + else: + # Обычный тариф: цена за оставшиеся месяцы + 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} мес)' + price_multiplier = months_multiplier # Используем цену из тарифа если есть, иначе глобальную настройку tariff_device_price = getattr(tariff, 'device_price_kopeks', None) if tariff else None @@ -1943,7 +1959,12 @@ def get_change_devices_keyboard( buttons = [] - max_devices = settings.MAX_DEVICES_LIMIT if settings.MAX_DEVICES_LIMIT > 0 else 20 + # Используем max_device_limit из тарифа если есть, иначе глобальную настройку + tariff_max_devices = getattr(tariff, 'max_device_limit', None) if tariff else None + if tariff_max_devices and tariff_max_devices > 0: + max_devices = tariff_max_devices + else: + max_devices = settings.MAX_DEVICES_LIMIT if settings.MAX_DEVICES_LIMIT > 0 else 20 start_range = max(1, min(current_devices - 3, max_devices - 6)) end_range = min(max_devices + 1, max(current_devices + 4, 7)) @@ -1967,10 +1988,12 @@ def get_change_devices_keyboard( price_per_month, discount_percent, ) - total_price = discounted_per_month * months_multiplier + total_price = int(discounted_per_month * price_multiplier) + total_price = max(100, total_price) # Минимум 1 рубль price_text = f' (+{total_price // 100}₽{period_text})' - if discount_percent > 0 and discount_per_month * months_multiplier > 0: - price_text += f' (скидка {discount_percent}%: -{(discount_per_month * months_multiplier) // 100}₽)' + total_discount = int(discount_per_month * price_multiplier) + if discount_percent > 0 and total_discount > 0: + price_text += f' (скидка {discount_percent}%: -{total_discount // 100}₽)' action_text = '' else: price_text = ' (бесплатно)' From 11eb27437bab522b4f1d558a1153cce079a3e091 Mon Sep 17 00:00:00 2001 From: Egor Date: Sat, 31 Jan 2026 17:05:35 +0300 Subject: [PATCH 2/2] Update devices.py --- app/handlers/subscription/devices.py | 138 +++++++++++++++++++-------- 1 file changed, 97 insertions(+), 41 deletions(-) diff --git a/app/handlers/subscription/devices.py b/app/handlers/subscription/devices.py index dc16b7c0..372937fb 100644 --- a/app/handlers/subscription/devices.py +++ b/app/handlers/subscription/devices.py @@ -290,26 +290,54 @@ async def confirm_change_devices(callback: types.CallbackQuery, db_user: User, d chargeable_devices = additional_devices devices_price_per_month = chargeable_devices * price_per_device - months_hint = get_remaining_months(subscription.end_date) - period_hint_days = months_hint * 30 if months_hint > 0 else None - devices_discount_percent = _get_addon_discount_percent_for_user( - db_user, - 'devices', - period_hint_days, - ) - discounted_per_month, discount_per_month = apply_percentage_discount( - devices_price_per_month, - devices_discount_percent, - ) - price, charged_months = calculate_prorated_price( - discounted_per_month, - subscription.end_date, - ) - total_discount = discount_per_month * charged_months + + # Проверяем является ли тариф суточным + is_daily_tariff = tariff and getattr(tariff, 'is_daily', False) + + if is_daily_tariff: + # Для суточных тарифов считаем по дням (как в кабинете) + now = datetime.utcnow() + days_left = max(1, (subscription.end_date - now).days) + period_hint_days = days_left + + devices_discount_percent = _get_addon_discount_percent_for_user( + db_user, + 'devices', + period_hint_days, + ) + discounted_per_month, discount_per_month = apply_percentage_discount( + devices_price_per_month, + devices_discount_percent, + ) + # Цена = месячная_цена * days_left / 30 + price = int(discounted_per_month * days_left / 30) + price = max(100, price) # Минимум 1 рубль + total_discount = int(discount_per_month * days_left / 30) + period_label = f'{days_left} дн.' if days_left > 1 else '1 день' + else: + # Для обычных тарифов - по месяцам + months_hint = get_remaining_months(subscription.end_date) + period_hint_days = months_hint * 30 if months_hint > 0 else None + + devices_discount_percent = _get_addon_discount_percent_for_user( + db_user, + 'devices', + period_hint_days, + ) + discounted_per_month, discount_per_month = apply_percentage_discount( + devices_price_per_month, + devices_discount_percent, + ) + price, charged_months = calculate_prorated_price( + discounted_per_month, + subscription.end_date, + ) + total_discount = discount_per_month * charged_months + period_label = f'{charged_months} мес' if price > 0 and db_user.balance_kopeks < price: missing_kopeks = price - db_user.balance_kopeks - required_text = f'{texts.format_price(price)} (за {charged_months} мес)' + required_text = f'{texts.format_price(price)} (за {period_label})' message_text = texts.t( 'ADDON_INSUFFICIENT_FUNDS_MESSAGE', ( @@ -343,10 +371,10 @@ async def confirm_change_devices(callback: types.CallbackQuery, db_user: User, d if price > 0: cost_text = texts.t( 'DEVICE_CHANGE_EXTRA_COST', - 'Доплата: {amount} (за {months} мес)', + 'Доплата: {amount} (за {period})', ).format( amount=texts.format_price(price), - months=charged_months, + period=period_label, ) if total_discount > 0: cost_text += texts.t( @@ -949,35 +977,63 @@ async def confirm_add_devices(callback: types.CallbackQuery, db_user: User, db: return devices_price_per_month = devices_count * price_per_device - months_hint = get_remaining_months(subscription.end_date) - period_hint_days = months_hint * 30 if months_hint > 0 else None - devices_discount_percent = _get_addon_discount_percent_for_user( - db_user, - 'devices', - period_hint_days, - ) - discounted_per_month, discount_per_month = apply_percentage_discount( - devices_price_per_month, - devices_discount_percent, - ) - price, charged_months = calculate_prorated_price( - discounted_per_month, - subscription.end_date, - ) - total_discount = discount_per_month * charged_months + + # Проверяем является ли тариф суточным + is_daily_tariff = tariff and getattr(tariff, 'is_daily', False) + + if is_daily_tariff: + # Для суточных тарифов считаем по дням (как в кабинете) + now = datetime.utcnow() + days_left = max(1, (subscription.end_date - now).days) + period_hint_days = days_left + + devices_discount_percent = _get_addon_discount_percent_for_user( + db_user, + 'devices', + period_hint_days, + ) + discounted_per_month, discount_per_month = apply_percentage_discount( + devices_price_per_month, + devices_discount_percent, + ) + # Цена = месячная_цена * days_left / 30 + price = int(discounted_per_month * days_left / 30) + price = max(100, price) # Минимум 1 рубль + total_discount = int(discount_per_month * days_left / 30) + period_label = f'{days_left} дн.' if days_left > 1 else '1 день' + else: + # Для обычных тарифов - по месяцам + months_hint = get_remaining_months(subscription.end_date) + period_hint_days = months_hint * 30 if months_hint > 0 else None + + devices_discount_percent = _get_addon_discount_percent_for_user( + db_user, + 'devices', + period_hint_days, + ) + discounted_per_month, discount_per_month = apply_percentage_discount( + devices_price_per_month, + devices_discount_percent, + ) + price, charged_months = calculate_prorated_price( + discounted_per_month, + subscription.end_date, + ) + total_discount = discount_per_month * charged_months + period_label = f'{charged_months} мес' logger.info( - 'Добавление %s устройств: %.2f₽/мес × %s мес = %.2f₽ (скидка %.2f₽)', + 'Добавление %s устройств: %.2f₽/мес × %s = %.2f₽ (скидка %.2f₽)', devices_count, discounted_per_month / 100, - charged_months, + period_label, price / 100, total_discount / 100, ) if db_user.balance_kopeks < price: missing_kopeks = price - db_user.balance_kopeks - required_text = f'{texts.format_price(price)} (за {charged_months} мес)' + required_text = f'{texts.format_price(price)} (за {period_label})' message_text = texts.t( 'ADDON_INSUFFICIENT_FUNDS_MESSAGE', ( @@ -1007,7 +1063,7 @@ async def confirm_add_devices(callback: types.CallbackQuery, db_user: User, db: try: success = await subtract_user_balance( - db, db_user, price, f'Добавление {devices_count} устройств на {charged_months} мес' + db, db_user, price, f'Добавление {devices_count} устройств на {period_label}' ) if not success: @@ -1024,7 +1080,7 @@ async def confirm_add_devices(callback: types.CallbackQuery, db_user: User, db: user_id=db_user.id, type=TransactionType.SUBSCRIPTION_PAYMENT, amount_kopeks=price, - description=f'Добавление {devices_count} устройств на {charged_months} мес', + description=f'Добавление {devices_count} устройств на {period_label}', ) await db.refresh(db_user) @@ -1035,7 +1091,7 @@ async def confirm_add_devices(callback: types.CallbackQuery, db_user: User, db: f'📱 Добавлено: {devices_count} устройств\n' f'Новый лимит: {subscription.device_limit} устройств\n' ) - success_text += f'💰 Списано: {texts.format_price(price)} (за {charged_months} мес)' + success_text += f'💰 Списано: {texts.format_price(price)} (за {period_label})' if total_discount > 0: success_text += f' (скидка {devices_discount_percent}%: -{texts.format_price(total_discount)})'