Merge pull request #2467 from BEDOLAGA-DEV/dev

Dev
This commit is contained in:
Egor
2026-01-31 17:06:05 +03:00
committed by GitHub
2 changed files with 130 additions and 51 deletions

View File

@@ -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)})'

View File

@@ -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 = ' (бесплатно)'