import logging
from aiogram import F, Router
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message
from sqlalchemy.ext.asyncio import AsyncSession
from dto.catalogue import CategoryActionContext
from dto.control import EditProductContext
from misc.filters import IsVerified
from misc.kb import admins
from misc.kb.admins import back_to_product_kb
from misc.kb.client import get_back_to_catalogue
from misc.mapper import parse_cat_id
from misc.states import AdminControlStorage
from misc.texts import get_product_description
from misc.texts import product_editing_mapping
from repositories.categories import CategoriesRepository
from repositories.products import ProductRepository
logger = logging.getLogger(__name__)
router = Router()
@router.callback_query(IsVerified(), F.data.regexp(r"^edit_product:\d+:(name|description|price)$"))
async def edit_product(cb: CallbackQuery, state: FSMContext):
await state.clear()
data = cb.data.split(":")
product_id = data[1]
mode = data[2]
if cb.message.photo:
await cb.message.delete()
msg = await cb.message.answer(
f"✍️ Введите {product_editing_mapping.get(mode)}",
reply_markup=back_to_product_kb(product_id, cb_factory=lambda p: f"product:{p}"),
)
else:
msg = cb.message
await cb.message.edit_text(
f"✍️ Введите {product_editing_mapping.get(mode)}",
reply_markup=back_to_product_kb(product_id, cb_factory=lambda p: f"product:{p}"),
)
ctx = EditProductContext(int(product_id), mode, msg)
await state.set_state(AdminControlStorage.edit_product)
await state.set_data({"ctx": ctx})
@router.message(AdminControlStorage.edit_product)
async def edit_product_query(
msg: Message,
state: FSMContext,
session: AsyncSession,
products_repo: ProductRepository,
):
data = await state.get_data()
await state.clear()
await msg.delete()
ctx: EditProductContext = data.get("ctx", EditProductContext)
kb = back_to_product_kb(ctx.product_id, cb_factory=lambda p: f"product:{p}")
try:
await ctx.msg.edit_text("⏳")
if ctx.mode == "name":
await products_repo.update_product_name_by_id(session, ctx.product_id, msg.text)
elif ctx.mode == "description":
await products_repo.update_product_description_by_id(session, ctx.product_id, msg.text)
elif ctx.mode == "price":
if msg.text.isdigit():
await products_repo.update_product_price_by_id(
session, ctx.product_id, int(msg.text)
)
else:
await ctx.msg.edit_text(
"❌ Вы ввели не число, попробуйте ещё раз...",
reply_markup=kb,
)
await state.set_state(AdminControlStorage.edit_product)
await state.set_data({"ctx": ctx})
return
else:
await ctx.msg.edit_text("🍃", reply_markup=kb)
return
await ctx.msg.edit_text("✅ Изменения применены.", reply_markup=kb)
except Exception as e:
logger.exception(e)
await ctx.msg.edit_text(text="‼️ Что-то пошло не так.", reply_markup=kb)
@router.callback_query(IsVerified(), F.data.startswith("edit_product:"), F.data.endswith(":hide"))
async def toggle_product_visibility(
cb: CallbackQuery,
state: FSMContext,
session: AsyncSession,
products_repo: ProductRepository,
):
await state.clear()
product_id = int(cb.data.split(":")[1])
product = await products_repo.toggle_product_hidden_by_id(session, product_id)
if not product:
await cb.answer("Товар не найден.", show_alert=True)
return
await cb.answer("Товар скрыт." if product.is_hidden else "Товар снова виден.")
if cb.message.photo:
await cb.message.edit_caption(
caption=cb.message.caption,
reply_markup=admins.edit_product(product),
)
return
await cb.message.edit_reply_markup(reply_markup=admins.edit_product(product))
@router.callback_query(IsVerified(), F.data.startswith("edit_product:"), F.data.endswith(":photo"))
async def request_product_photo(cb: CallbackQuery, state: FSMContext):
await state.clear()
product_id = int(cb.data.split(":")[1])
if cb.message.photo:
await cb.message.delete()
msg = await cb.message.answer(
"📷 Отправьте фотографию товара.",
reply_markup=back_to_product_kb(product_id, cb_factory=lambda p: f"product:{p}"),
)
else:
msg = cb.message
await cb.message.edit_text(
"📷 Отправьте фотографию товара.",
reply_markup=back_to_product_kb(product_id, cb_factory=lambda p: f"product:{p}"),
)
await state.set_state(AdminControlStorage.edit_product_photo)
await state.set_data({"ctx": EditProductContext(product_id, "photo", msg)})
@router.message(AdminControlStorage.edit_product_photo)
async def upload_product_photo(
msg: Message,
state: FSMContext,
session: AsyncSession,
products_repo: ProductRepository,
):
data = await state.get_data()
ctx: EditProductContext = data.get("ctx", EditProductContext)
kb = back_to_product_kb(ctx.product_id, cb_factory=lambda p: f"product:{p}")
if not msg.photo:
await msg.delete()
await ctx.msg.edit_text("❌ Отправьте именно фотографию.", reply_markup=kb)
await state.set_state(AdminControlStorage.edit_product_photo)
await state.set_data({"ctx": ctx})
return
await state.clear()
await msg.delete()
try:
await ctx.msg.edit_text("⏳")
await products_repo.add_product_file_id(
session, ctx.product_id, file_id=msg.photo[-1].file_id
)
await ctx.msg.edit_text("✅ Фотография обновлена.", reply_markup=kb)
except Exception as e:
logger.exception(e)
await ctx.msg.edit_text("‼️ Что-то пошло не так.", reply_markup=kb)
@router.callback_query(
IsVerified(), F.data.startswith("edit_product:"), F.data.endswith(":delete_photo")
)
async def delete_product_photo(
cb: CallbackQuery,
state: FSMContext,
session: AsyncSession,
products_repo: ProductRepository,
):
await state.clear()
product_id = int(cb.data.split(":")[1])
product = await products_repo.clear_product_photo(session, product_id)
if not product:
await cb.answer("Товар не найден.", show_alert=True)
return
await cb.answer("Фото удалено.")
text = get_product_description(product)
reply_markup = admins.edit_product(product)
if cb.message.photo:
await cb.message.delete()
await cb.message.answer(text, reply_markup=reply_markup)
return
if cb.message.caption:
await cb.message.edit_text(text, reply_markup=reply_markup)
return
await cb.message.edit_reply_markup(reply_markup=reply_markup)
@router.callback_query(F.data.startswith("edit:"))
async def edit_entry(cb: CallbackQuery, state: FSMContext):
await state.clear()
data = cb.data.split(":")
mode = data[1]
entry_id = data[2]
if mode == "category":
await cb.message.edit_text(
"✍️ Введите новое название категории.",
reply_markup=get_back_to_catalogue(entry_id),
)
ctx = CategoryActionContext(cb.message, entry_id)
await state.set_state(AdminControlStorage.edit_category)
await state.set_data({"ctx": ctx})
if mode == "product":
...
@router.message(AdminControlStorage.edit_category)
async def edit_category(
msg: Message,
state: FSMContext,
session: AsyncSession,
categories_repo: CategoriesRepository,
):
await msg.delete()
data = await state.get_data()
await state.clear()
ctx: CategoryActionContext | None = data.get("ctx")
if not ctx:
await msg.answer("❌ Ошибка при редактировании категории.")
logger.error("no context found while editing category!")
return
await ctx.msg.edit_text("⏳")
cat = await categories_repo.update_category_name(
session=session, category_id=parse_cat_id(ctx.parent_id), value=msg.text
)
await ctx.msg.edit_text("✅ Успешно.", reply_markup=get_back_to_catalogue(cat.parent_id))