Merge pull request #257 from JustYay/dev
[FEAUTURE] Изменения прорядка касс через админ-панель
This commit is contained in:
@@ -6,6 +6,7 @@ from .settings.modes_config import MODES_CONFIG, load_modes_config, update_modes
|
||||
from .settings.money_config import MONEY_CONFIG, load_money_config, update_money_config
|
||||
from .settings.notifications_config import NOTIFICATIONS_CONFIG, load_notifications_config, update_notifications_config
|
||||
from .settings.payments_config import PAYMENTS_CONFIG, load_payments_config, update_payments_config
|
||||
from .settings.providers_order_config import PROVIDERS_ORDER, load_providers_order, update_providers_order
|
||||
from .settings.tariffs_config import TARIFFS_CONFIG, load_tariffs_config, update_tariffs_config
|
||||
|
||||
|
||||
@@ -15,6 +16,7 @@ async def bootstrap() -> None:
|
||||
await load_notifications_config(session)
|
||||
await load_modes_config(session)
|
||||
await load_payments_config(session)
|
||||
await load_providers_order(session)
|
||||
await load_money_config(session)
|
||||
await load_management_config(session)
|
||||
await load_tariffs_config(session)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database.models import Setting
|
||||
|
||||
PROVIDERS_ORDER: dict[str, int] = {}
|
||||
|
||||
|
||||
async def load_providers_order(session: AsyncSession) -> None:
|
||||
stmt = select(Setting).where(Setting.key == "PROVIDERS_ORDER")
|
||||
result = await session.execute(stmt)
|
||||
setting = result.scalar_one_or_none()
|
||||
|
||||
PROVIDERS_ORDER.clear()
|
||||
if setting and isinstance(setting.value, dict):
|
||||
PROVIDERS_ORDER.update({k: int(v) for k, v in setting.value.items()})
|
||||
await session.flush()
|
||||
|
||||
|
||||
async def update_providers_order(session: AsyncSession, new_order: dict[str, int]) -> None:
|
||||
stmt = select(Setting).where(Setting.key == "PROVIDERS_ORDER")
|
||||
result = await session.execute(stmt)
|
||||
setting = result.scalar_one_or_none()
|
||||
|
||||
if setting is None:
|
||||
setting = Setting(
|
||||
key="PROVIDERS_ORDER",
|
||||
value=new_order,
|
||||
description="Порядок отображения платёжных провайдеров",
|
||||
)
|
||||
session.add(setting)
|
||||
else:
|
||||
setting.value = new_order
|
||||
|
||||
await session.commit()
|
||||
|
||||
PROVIDERS_ORDER.clear()
|
||||
PROVIDERS_ORDER.update(new_order)
|
||||
@@ -97,15 +97,64 @@ def build_settings_buttons_kb(buttons_state: dict[str, bool]) -> InlineKeyboardM
|
||||
|
||||
|
||||
def build_settings_cashboxes_kb(providers_state: dict[str, bool]) -> InlineKeyboardMarkup:
|
||||
order_button = InlineKeyboardButton(
|
||||
text="📋 Порядок касс",
|
||||
callback_data=AdminPanelCallback(action="settings_providers_order").pack(),
|
||||
)
|
||||
return build_toggle_section_keyboard(
|
||||
titles=PAYMENT_PROVIDER_TITLES,
|
||||
state=providers_state,
|
||||
action="settings_cashbox_toggle",
|
||||
columns=2,
|
||||
back_action="settings",
|
||||
extra_rows=[[order_button]],
|
||||
)
|
||||
|
||||
|
||||
def build_providers_order_kb(sorted_names: list[str]) -> InlineKeyboardMarkup:
|
||||
"""Клавиатура для управления порядком отображения касс."""
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
for idx, name in enumerate(sorted_names):
|
||||
title = PAYMENT_PROVIDER_TITLES.get(name, name)
|
||||
pos = idx + 1
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text="⬆️",
|
||||
callback_data=AdminPanelCallback(
|
||||
action="settings_order_up", page=pos,
|
||||
).pack(),
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text=f"{pos}. {title}",
|
||||
callback_data=AdminPanelCallback(
|
||||
action="settings_providers_order",
|
||||
).pack(),
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text="⬇️",
|
||||
callback_data=AdminPanelCallback(
|
||||
action="settings_order_down", page=pos,
|
||||
).pack(),
|
||||
),
|
||||
)
|
||||
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text="🔄 Сбросить порядок",
|
||||
callback_data=AdminPanelCallback(action="settings_order_reset").pack(),
|
||||
)
|
||||
)
|
||||
builder.row(
|
||||
InlineKeyboardButton(
|
||||
text="Назад",
|
||||
callback_data=AdminPanelCallback(action="settings_cashboxes").pack(),
|
||||
)
|
||||
)
|
||||
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def build_settings_notifications_kb(notifications_state: dict[str, object]) -> InlineKeyboardMarkup:
|
||||
intervals_button = InlineKeyboardButton(
|
||||
text="Интервалы",
|
||||
|
||||
@@ -3,10 +3,12 @@ from aiogram.types import CallbackQuery
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from core.bootstrap import PAYMENTS_CONFIG, update_payments_config
|
||||
from core.settings.providers_order_config import PROVIDERS_ORDER, update_providers_order
|
||||
from filters.admin import IsAdminFilter
|
||||
from handlers.payments.providers import PROVIDERS_BASE, _get_effective_order
|
||||
|
||||
from ..panel.keyboard import AdminPanelCallback
|
||||
from .keyboard import PAYMENT_PROVIDER_TITLES, build_settings_cashboxes_kb
|
||||
from .keyboard import PAYMENT_PROVIDER_TITLES, build_providers_order_kb, build_settings_cashboxes_kb
|
||||
|
||||
|
||||
router = Router(name="admin_settings_cashboxes")
|
||||
@@ -18,6 +20,15 @@ async def load_payment_providers_settings() -> dict[str, bool]:
|
||||
return {k: bool(config.get(k, False)) for k in PAYMENT_PROVIDER_TITLES.keys()}
|
||||
|
||||
|
||||
def _get_sorted_provider_names() -> list[str]:
|
||||
"""Возвращает все провайдеры, отсортированные по текущему порядку."""
|
||||
all_names = list(PROVIDERS_BASE.keys())
|
||||
return sorted(
|
||||
all_names,
|
||||
key=lambda n: _get_effective_order(n, PROVIDERS_BASE.get(n, {})),
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(AdminPanelCallback.filter(F.action == "settings_cashboxes"))
|
||||
async def open_settings_cashboxes_menu(callback: CallbackQuery, session: AsyncSession) -> None:
|
||||
providers_state = await load_payment_providers_settings()
|
||||
@@ -56,3 +67,85 @@ async def toggle_cashbox_setting(
|
||||
reply_markup=build_settings_cashboxes_kb(updated_state),
|
||||
)
|
||||
await callback.answer("Настройка обновлена")
|
||||
|
||||
@router.callback_query(AdminPanelCallback.filter(F.action == "settings_providers_order"))
|
||||
async def open_providers_order_menu(callback: CallbackQuery, session: AsyncSession) -> None:
|
||||
sorted_names = _get_sorted_provider_names()
|
||||
text = (
|
||||
"📋 <b>Порядок отображения касс</b>\n\n"
|
||||
"⬆️ — поднять выше\n"
|
||||
"⬇️ — опустить ниже\n\n"
|
||||
"Порядок влияет на меню оплаты и fast flow."
|
||||
)
|
||||
await callback.message.edit_text(
|
||||
text=text,
|
||||
reply_markup=build_providers_order_kb(sorted_names),
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(AdminPanelCallback.filter(F.action == "settings_order_up"))
|
||||
async def move_provider_up(
|
||||
callback: CallbackQuery,
|
||||
callback_data: AdminPanelCallback,
|
||||
session: AsyncSession,
|
||||
) -> None:
|
||||
sorted_names = _get_sorted_provider_names()
|
||||
idx = callback_data.page - 1
|
||||
|
||||
if idx <= 0:
|
||||
await callback.answer("Уже на первом месте", show_alert=False)
|
||||
return
|
||||
|
||||
if idx >= len(sorted_names):
|
||||
await callback.answer("Неизвестная касса", show_alert=True)
|
||||
return
|
||||
|
||||
sorted_names[idx], sorted_names[idx - 1] = sorted_names[idx - 1], sorted_names[idx]
|
||||
|
||||
new_order = {name: (i + 1) * 10 for i, name in enumerate(sorted_names)}
|
||||
await update_providers_order(session, new_order)
|
||||
|
||||
await callback.message.edit_reply_markup(
|
||||
reply_markup=build_providers_order_kb(sorted_names),
|
||||
)
|
||||
await callback.answer("✅ Перемещено выше")
|
||||
|
||||
|
||||
@router.callback_query(AdminPanelCallback.filter(F.action == "settings_order_down"))
|
||||
async def move_provider_down(
|
||||
callback: CallbackQuery,
|
||||
callback_data: AdminPanelCallback,
|
||||
session: AsyncSession,
|
||||
) -> None:
|
||||
sorted_names = _get_sorted_provider_names()
|
||||
idx = callback_data.page - 1
|
||||
|
||||
if idx >= len(sorted_names) - 1:
|
||||
await callback.answer("Уже на последнем месте", show_alert=False)
|
||||
return
|
||||
|
||||
if idx < 0:
|
||||
await callback.answer("Неизвестная касса", show_alert=True)
|
||||
return
|
||||
|
||||
sorted_names[idx], sorted_names[idx + 1] = sorted_names[idx + 1], sorted_names[idx]
|
||||
|
||||
new_order = {name: (i + 1) * 10 for i, name in enumerate(sorted_names)}
|
||||
await update_providers_order(session, new_order)
|
||||
|
||||
await callback.message.edit_reply_markup(
|
||||
reply_markup=build_providers_order_kb(sorted_names),
|
||||
)
|
||||
await callback.answer("✅ Перемещено ниже")
|
||||
|
||||
|
||||
@router.callback_query(AdminPanelCallback.filter(F.action == "settings_order_reset"))
|
||||
async def reset_providers_order(callback: CallbackQuery, session: AsyncSession) -> None:
|
||||
await update_providers_order(session, {})
|
||||
|
||||
sorted_names = _get_sorted_provider_names()
|
||||
await callback.message.edit_reply_markup(
|
||||
reply_markup=build_providers_order_kb(sorted_names),
|
||||
)
|
||||
await callback.answer("✅ Порядок сброшен на дефолтный")
|
||||
|
||||
@@ -27,7 +27,7 @@ from handlers.payments.currency_flow import (
|
||||
shortfall_lead_text,
|
||||
currency_label,
|
||||
)
|
||||
from handlers.payments.providers import get_providers_with_hooks
|
||||
from handlers.payments.providers import get_providers_with_hooks, sort_provider_names
|
||||
from handlers.texts import FAST_PAY_CHOOSE_CURRENCY, FAST_PAY_CHOOSE_PROVIDER, FASTFLOW_COUPON_APPLIED_TEMPLATE
|
||||
from handlers.utils import edit_or_send_message
|
||||
from logger import logger
|
||||
@@ -115,6 +115,7 @@ async def try_fast_payment_flow(
|
||||
cfg = providers_map.get(p_up) or {}
|
||||
if cfg.get("fast") and cfg.get("enabled", True):
|
||||
providers.append(p_up)
|
||||
providers = sort_provider_names(providers, providers_map)
|
||||
|
||||
mode, one_screen = get_currency_mode()
|
||||
multicurrency_mode = mode == "RUB+USD"
|
||||
@@ -425,6 +426,7 @@ async def fastflow_apply_coupon(message: Message, state: FSMContext, session: An
|
||||
cfg = providers_map.get(p_up) or {}
|
||||
if cfg.get("fast") and cfg.get("enabled", True):
|
||||
providers.append(p_up)
|
||||
providers = sort_provider_names(providers, providers_map)
|
||||
|
||||
mode, one_screen = get_currency_mode()
|
||||
multicurrency_mode = mode == "RUB+USD"
|
||||
@@ -541,13 +543,16 @@ async def choose_payment_currency(callback_query: CallbackQuery, state: FSMConte
|
||||
configured = [str(p) for p in (USE_NEW_PAYMENT_FLOW or [])]
|
||||
providers = [p.upper() for p in configured]
|
||||
|
||||
filtered = [
|
||||
provider_upper
|
||||
for provider_upper in (p.upper() for p in providers)
|
||||
if (providers_map.get(provider_upper) or {}).get("currency") == currency
|
||||
and (providers_map.get(provider_upper) or {}).get("fast")
|
||||
and (providers_map.get(provider_upper) or {}).get("enabled", True)
|
||||
]
|
||||
filtered = sort_provider_names(
|
||||
[
|
||||
provider_upper
|
||||
for provider_upper in (p.upper() for p in providers)
|
||||
if (providers_map.get(provider_upper) or {}).get("currency") == currency
|
||||
and (providers_map.get(provider_upper) or {}).get("fast")
|
||||
and (providers_map.get(provider_upper) or {}).get("enabled", True)
|
||||
],
|
||||
providers_map,
|
||||
)
|
||||
await state.update_data(chosen_currency=currency)
|
||||
|
||||
if not filtered:
|
||||
|
||||
@@ -2,63 +2,93 @@ from typing import Any
|
||||
|
||||
from hooks.hooks import run_hooks
|
||||
|
||||
|
||||
PROVIDERS_BASE: dict[str, dict[str, Any]] = {
|
||||
"YOOKASSA": {
|
||||
"currency": "RUB",
|
||||
"value": "pay_yookassa",
|
||||
"fast": "process_custom_amount_input",
|
||||
"order": 10,
|
||||
},
|
||||
"YOOMONEY": {
|
||||
"currency": "RUB",
|
||||
"value": "pay_yoomoney",
|
||||
"fast": "process_custom_amount_input_yoomoney",
|
||||
"order": 20,
|
||||
},
|
||||
"ROBOKASSA": {
|
||||
"currency": "RUB",
|
||||
"value": "pay_robokassa",
|
||||
"fast": "handle_custom_amount_input",
|
||||
"order": 30,
|
||||
},
|
||||
"KASSAI_CARDS": {
|
||||
"currency": "RUB",
|
||||
"value": "pay_kassai_cards",
|
||||
"fast": "handle_custom_amount_input_kassai_cards",
|
||||
"module": "kassai",
|
||||
"order": 40,
|
||||
},
|
||||
"KASSAI_SBP": {
|
||||
"currency": "RUB",
|
||||
"value": "pay_kassai_sbp",
|
||||
"fast": "handle_custom_amount_input_kassai_sbp",
|
||||
"module": "kassai",
|
||||
"order": 50,
|
||||
},
|
||||
"TRIBUTE": {
|
||||
"currency": "RUB+USD",
|
||||
"value": "pay_tribute",
|
||||
"fast": None,
|
||||
"order": 60,
|
||||
},
|
||||
"HELEKET": {
|
||||
"currency": "USD",
|
||||
"value": "pay_heleket_crypto",
|
||||
"fast": "handle_custom_amount_input_heleket",
|
||||
"order": 70,
|
||||
},
|
||||
"CRYPTOBOT": {
|
||||
"currency": "USD",
|
||||
"value": "pay_cryptobot",
|
||||
"fast": "process_custom_amount_input",
|
||||
"order": 80,
|
||||
},
|
||||
"FREEKASSA": {
|
||||
"currency": "USD",
|
||||
"value": "pay_freekassa",
|
||||
"fast": None,
|
||||
"order": 90,
|
||||
},
|
||||
"STARS": {
|
||||
"currency": "STARS",
|
||||
"value": "pay_stars",
|
||||
"fast": "process_custom_amount_input_stars",
|
||||
"order": 100,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _get_effective_order(name: str, cfg: dict[str, Any]) -> int:
|
||||
"""Возвращает эффективный порядок провайдера (админ > модуль > дефолт)."""
|
||||
from core.settings.providers_order_config import PROVIDERS_ORDER
|
||||
|
||||
if name in PROVIDERS_ORDER:
|
||||
return PROVIDERS_ORDER[name]
|
||||
return cfg.get("order", 999)
|
||||
|
||||
|
||||
def _sort_providers(providers: dict[str, dict[str, Any]]) -> dict[str, dict[str, Any]]:
|
||||
"""Сортирует провайдеров по полю 'order' (меньше = выше)."""
|
||||
return dict(
|
||||
sorted(providers.items(), key=lambda item: _get_effective_order(item[0], item[1]))
|
||||
)
|
||||
|
||||
|
||||
def sort_provider_names(names: list[str], providers_map: dict[str, dict[str, Any]]) -> list[str]:
|
||||
"""Сортирует список имён провайдеров по их 'order' из providers_map."""
|
||||
return sorted(names, key=lambda n: _get_effective_order(n, providers_map.get(n) or {}))
|
||||
|
||||
|
||||
def get_providers(flags: dict[str, bool]) -> dict[str, dict[str, Any]]:
|
||||
providers: dict[str, dict[str, Any]] = {}
|
||||
for name, base in PROVIDERS_BASE.items():
|
||||
@@ -81,4 +111,4 @@ async def get_providers_with_hooks(flags: dict[str, bool]) -> dict[str, dict[str
|
||||
base = dict(providers.get(name, {}))
|
||||
base.update(patch)
|
||||
providers[name] = base
|
||||
return providers
|
||||
return _sort_providers(providers)
|
||||
|
||||
Reference in New Issue
Block a user