Files
shveitechbot/handlers/admins/creation.py
2026-08-13 12:03:56 +07:00

225 lines
8.2 KiB
Python

from aiogram import F, Router
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message
from sqlalchemy.ext.asyncio import AsyncSession
from dto.catalogue import CategoryActionContext, ProductActionContext
from misc.filters import IsVerified
from misc.kb.client import created_category, get_back_to_catalogue
from misc.kb.common import return_menu
from misc.mapper import parse_cat_id
from misc.states import AdminControlStorage
from repositories.categories import CategoriesRepository
from repositories.products import ProductRepository
from services.catalogue import CatalogueService
router = Router()
def skip_product_description_kb(parent_id: str) -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="Пропустить", callback_data="new_product:skip_description")],
[InlineKeyboardButton(text="⬅️ Назад", callback_data=f"cat:{parent_id}")],
]
)
def skip_product_photo_kb(parent_id: str) -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="Пропустить", callback_data="new_product:skip_photo")],
[InlineKeyboardButton(text="⬅️ Назад", callback_data=f"cat:{parent_id}")],
]
)
@router.callback_query(IsVerified(), F.data.regexp(r"^new:(category|product):.+$"))
async def creation_init(
cb: CallbackQuery,
state: FSMContext,
session: AsyncSession,
catalogue_service: CatalogueService,
):
await state.clear()
data = cb.data.split(":")
mode = data[1]
cat_id = data[2]
path = await catalogue_service.get_path(session, cat_id)
if mode == "category":
await cb.message.edit_text(
f"{path} <i>-> ...</i>\n\n<b>🏷️ Введите название новой категории.</b>",
reply_markup=get_back_to_catalogue(cat_id),
)
ctx = CategoryActionContext(cb.message, cat_id)
await state.set_state(AdminControlStorage.new_category)
await state.set_data({"ctx": ctx})
if mode == "product":
await cb.message.edit_text(
f"{path} <i>-> ...</i>\n\n<b>📦 Введите название товара.</b>",
reply_markup=get_back_to_catalogue(cat_id),
)
await state.set_state(AdminControlStorage.new_product_name)
await state.set_data({"ctx": ProductActionContext(cb.message, cat_id)})
@router.message(AdminControlStorage.new_category)
async def create_new_category(
msg: Message,
state: FSMContext,
session: AsyncSession,
categories_repo: CategoriesRepository,
):
await msg.delete()
data = await state.get_data()
ctx: CategoryActionContext | None = data.get("ctx")
if not ctx:
await msg.reply("❌ Ошибка при создании категории.", reply_markup=return_menu)
return
category = await categories_repo.add_category(
session, name=msg.text, parent_id=parse_cat_id(ctx.parent_id)
)
await ctx.msg.edit_text(
f"✅ Категория {msg.text} создана успешно.",
reply_markup=created_category(
category.id, f"🛒 {category.name}", category.parent_id or "root"
),
)
@router.message(IsVerified(), AdminControlStorage.new_product_name)
async def create_product_name(msg: Message, state: FSMContext):
if not msg.text or not msg.text.strip():
await msg.reply("❌ Название не может быть пустым.")
return
data = await state.get_data()
ctx: ProductActionContext | None = data.get("ctx")
if not ctx:
await state.clear()
await msg.reply("❌ Ошибка при создании товара.", reply_markup=return_menu)
return
await msg.delete()
await ctx.msg.edit_text(
"<b>📜 Введите описание товара.</b>",
reply_markup=skip_product_description_kb(str(ctx.parent_id)),
)
await state.update_data(name=msg.text.strip())
await state.set_state(AdminControlStorage.new_product_description)
@router.callback_query(IsVerified(), F.data == "new_product:skip_description")
async def skip_product_description(cb: CallbackQuery, state: FSMContext):
data = await state.get_data()
ctx: ProductActionContext | None = data.get("ctx")
if not ctx:
await state.clear()
await cb.answer("Ошибка при создании товара.", show_alert=True)
return
await cb.message.edit_text(
"<b>💸 Введите цену товара целым числом.</b>",
reply_markup=get_back_to_catalogue(ctx.parent_id),
)
await state.update_data(description=None)
await state.set_state(AdminControlStorage.new_product_price)
await cb.answer()
@router.message(IsVerified(), AdminControlStorage.new_product_description)
async def create_product_description(msg: Message, state: FSMContext):
if not msg.text or not msg.text.strip():
await msg.reply("❌ Описание не может быть пустым.")
return
data = await state.get_data()
ctx: ProductActionContext | None = data.get("ctx")
if not ctx:
await state.clear()
await msg.reply("❌ Ошибка при создании товара.", reply_markup=return_menu)
return
await msg.delete()
await ctx.msg.edit_text(
"<b>💸 Введите цену товара целым числом.</b>",
reply_markup=get_back_to_catalogue(ctx.parent_id),
)
await state.update_data(description=msg.text.strip())
await state.set_state(AdminControlStorage.new_product_price)
@router.message(IsVerified(), AdminControlStorage.new_product_price)
async def create_product_price(
msg: Message,
state: FSMContext,
session: AsyncSession,
products_repo: ProductRepository,
):
if not msg.text or not msg.text.isdigit() or int(msg.text) <= 0:
await msg.reply("❌ Цена должна быть положительным целым числом.")
return
data = await state.get_data()
ctx: ProductActionContext | None = data.get("ctx")
if not ctx:
await state.clear()
await msg.reply("❌ Ошибка при создании товара.", reply_markup=return_menu)
return
await msg.delete()
product = await products_repo.add_product(
session,
category_id=parse_cat_id(ctx.parent_id),
name=data["name"],
description=data["description"],
price=int(msg.text),
)
await ctx.msg.edit_text(
"✅ Основные данные сохранены.\n\n<b>📷 Отправьте фотографию товара или пропустите этот шаг.</b>",
reply_markup=skip_product_photo_kb(str(ctx.parent_id)),
)
await state.update_data(product_id=product.id)
await state.set_state(AdminControlStorage.new_product_photo)
@router.callback_query(IsVerified(), F.data == "new_product:skip_photo")
async def skip_product_photo(cb: CallbackQuery, state: FSMContext):
data = await state.get_data()
ctx: ProductActionContext | None = data.get("ctx")
await state.clear()
parent_id = ctx.parent_id if ctx else "root"
await cb.message.edit_text("✅ Товар добавлен.", reply_markup=get_back_to_catalogue(parent_id))
await cb.answer()
@router.message(IsVerified(), AdminControlStorage.new_product_photo)
async def create_product_photo(
msg: Message,
state: FSMContext,
session: AsyncSession,
products_repo: ProductRepository,
):
data = await state.get_data()
if not msg.photo or not data.get("product_id"):
await msg.delete()
await msg.reply("❌ Отправьте именно фотографию или нажмите «Пропустить».")
return
await state.clear()
await msg.delete()
await products_repo.add_product_file_id(
session, int(data["product_id"]), file_id=msg.photo[-1].file_id
)
ctx: ProductActionContext = data["ctx"]
await ctx.msg.edit_text("✅ Товар добавлен.", reply_markup=get_back_to_catalogue(ctx.parent_id))