diff --git a/handlers/admins/product_mgmt.py b/handlers/admins/product_mgmt.py
index ec2c649..af912f9 100644
--- a/handlers/admins/product_mgmt.py
+++ b/handlers/admins/product_mgmt.py
@@ -13,6 +13,7 @@ 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
@@ -120,6 +121,95 @@ async def toggle_product_visibility(
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()
diff --git a/handlers/client/cart.py b/handlers/client/cart.py
index ae6a1fa..4d4e1d9 100644
--- a/handlers/client/cart.py
+++ b/handlers/client/cart.py
@@ -53,7 +53,8 @@ async def cart_cmd(
return
await msg.answer(
- f"total: {cart.total}₽",
+ "🌸 Убедитесь в том, что все желаемые позиции есть в заказе ниже!\n\n"
+ f"Сумма: {cart.total}₽",
reply_markup=render_cart(
cart.items[:PAGE_SIZE],
show_next=len(cart.items) > PAGE_SIZE,
diff --git a/handlers/client/menu.py b/handlers/client/menu.py
index 3de67d9..81aef7c 100644
--- a/handlers/client/menu.py
+++ b/handlers/client/menu.py
@@ -6,7 +6,7 @@ from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message
from sqlalchemy.ext.asyncio import AsyncSession
-from misc.kb import main_menu_kb
+from misc.kb.client import main_menu_kb
from repositories.order_items import OrderItemRepository
from services.orders import OrderService
diff --git a/handlers/client/products.py b/handlers/client/products.py
index cbcfd70..2201b78 100644
--- a/handlers/client/products.py
+++ b/handlers/client/products.py
@@ -64,7 +64,7 @@ async def product_card(
product = await products_repo.get_product_by_id(session, product_id, include_hidden=is_admin)
if not product:
- await cb.answer("Товар недоступен.", show_alert=True)
+ await cb.answer("❌ Товар недоступен.", show_alert=True)
return
if is_admin:
@@ -108,7 +108,7 @@ async def product_card(
if not msg.photo:
logger.warning("didnt get a photo back wahhh :(")
- return # TODO: Add logging
+ return
await products_repo.add_product_file_id(session, product_id, file_id=msg.photo[-1].file_id)
@@ -129,7 +129,7 @@ async def cart_action(
product = await products_repo.get_product_by_id(session, product_id, include_hidden=is_admin)
if not product:
- await cb.answer("Товар недоступен.", show_alert=True)
+ await cb.answer("❌ Товар недоступен.", show_alert=True)
return
show_cart = True
diff --git a/misc/kb/admins.py b/misc/kb/admins.py
index 76a8b1c..fb8ebb7 100644
--- a/misc/kb/admins.py
+++ b/misc/kb/admins.py
@@ -58,6 +58,8 @@ def edit_product(product: Product):
product_id = product.id
prefix = f"edit_product:{product_id}"
visibility_btn = "🙈 Скрыт" if product.is_hidden else "👁️ Видим"
+ photo_btn = "🗑️ Удалить фото" if product.file_id or product.img_path else "📷 Добавить фото"
+ photo_action = "delete_photo" if product.file_id or product.img_path else "photo"
return InlineKeyboardBuilder(
[
@@ -69,6 +71,7 @@ def edit_product(product: Product):
),
],
[InlineKeyboardButton(text="💸 Цена", callback_data=f"{prefix}:price")],
+ [InlineKeyboardButton(text=photo_btn, callback_data=f"{prefix}:{photo_action}")],
[
InlineKeyboardButton(text=visibility_btn, callback_data=f"{prefix}:hide"),
InlineKeyboardButton(text="❌", callback_data=f"{prefix}:delete"),
diff --git a/misc/states.py b/misc/states.py
index b5f324e..a3810fc 100644
--- a/misc/states.py
+++ b/misc/states.py
@@ -18,5 +18,6 @@ class SearchStorage(StatesGroup):
class AdminControlStorage(StatesGroup):
edit_product = State()
+ edit_product_photo = State()
edit_category = State()
new_category = State()
diff --git a/repositories/products.py b/repositories/products.py
index 7c343b5..e68ecd2 100644
--- a/repositories/products.py
+++ b/repositories/products.py
@@ -43,6 +43,17 @@ class ProductRepository:
await session.commit()
+ async def clear_product_photo(self, session: AsyncSession, product_id: int) -> Product | None:
+ product = await self.get_product_by_id(session, product_id)
+ if not product:
+ return None
+
+ product.file_id = None
+ product.img_path = None
+
+ await session.commit()
+ return product
+
async def search(
self,
session: AsyncSession,