293 lines
9.9 KiB
Python
293 lines
9.9 KiB
Python
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, 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"<b>✍️ Введите {product_editing_mapping.get(mode)}</b>",
|
||
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"<b>✍️ Введите {product_editing_mapping.get(mode)}</b>",
|
||
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(
|
||
"<b>📷 Отправьте фотографию товара.</b>",
|
||
reply_markup=back_to_product_kb(product_id, cb_factory=lambda p: f"product:{p}"),
|
||
)
|
||
else:
|
||
msg = cb.message
|
||
await cb.message.edit_text(
|
||
"<b>📷 Отправьте фотографию товара.</b>",
|
||
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(IsVerified(), F.data.regexp(r"^edit_product:\d+:delete$"))
|
||
async def request_product_deletion(cb: CallbackQuery, state: FSMContext):
|
||
await state.clear()
|
||
product_id = int(cb.data.split(":")[1])
|
||
await cb.message.edit_reply_markup(reply_markup=admins.confirm_product_deletion(product_id))
|
||
await cb.answer("Подтвердите удаление товара.")
|
||
|
||
|
||
@router.callback_query(IsVerified(), F.data.regexp(r"^delete_product:confirm:\d+$"))
|
||
async def delete_product(
|
||
cb: CallbackQuery,
|
||
state: FSMContext,
|
||
session: AsyncSession,
|
||
products_repo: ProductRepository,
|
||
):
|
||
await state.clear()
|
||
product_id = int(cb.data.split(":")[2])
|
||
product = await products_repo.get_product_by_id(session, product_id)
|
||
if not product:
|
||
await cb.answer("Товар уже удалён.", show_alert=True)
|
||
return
|
||
|
||
category_id = product.category_id
|
||
try:
|
||
await products_repo.delete_product(session, product_id)
|
||
except Exception:
|
||
await session.rollback()
|
||
logger.exception("Failed to delete product %s", product_id)
|
||
await cb.answer("Не удалось удалить товар: он используется в заказах.", show_alert=True)
|
||
return
|
||
|
||
await cb.message.delete()
|
||
await cb.message.answer("✅ Товар удалён.", reply_markup=get_back_to_catalogue(category_id))
|
||
await cb.answer()
|
||
|
||
|
||
@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(
|
||
"<b>✍️ Введите новое название категории.</b>",
|
||
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))
|