129 lines
4.7 KiB
Python
129 lines
4.7 KiB
Python
from aiogram import F, Router
|
||
from aiogram.filters import Command, CommandStart
|
||
from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message
|
||
|
||
from app.api import ApiError, ShopApi
|
||
|
||
router = Router()
|
||
|
||
|
||
def _catalog_keyboard(products: list[dict]) -> InlineKeyboardMarkup:
|
||
rows = [
|
||
[InlineKeyboardButton(text=f"{item['title']} — {item['price']} ₽", callback_data=f"prod:{item['id']}")]
|
||
for item in products
|
||
]
|
||
return InlineKeyboardMarkup(inline_keyboard=rows)
|
||
|
||
|
||
def _product_keyboard(product_id: int) -> InlineKeyboardMarkup:
|
||
return InlineKeyboardMarkup(
|
||
inline_keyboard=[
|
||
[
|
||
InlineKeyboardButton(text="В корзину", callback_data=f"add:{product_id}"),
|
||
InlineKeyboardButton(text="Оформить", callback_data="checkout"),
|
||
]
|
||
]
|
||
)
|
||
|
||
|
||
def _cart_text(cart: dict) -> str:
|
||
if not cart["items"]:
|
||
return "Корзина пуста."
|
||
lines = [f"{item['title']} × {item['qty']} = {item['line_total']} ₽" for item in cart["items"]]
|
||
lines.append(f"Итого: {cart['total']} ₽")
|
||
lines.append("Оформление: /checkout")
|
||
return "\n".join(lines)
|
||
|
||
|
||
@router.message(CommandStart())
|
||
async def start(message: Message) -> None:
|
||
await message.answer("Магазин. /catalog — товары, /cart — корзина, /checkout — заказ без оплаты.")
|
||
|
||
|
||
@router.message(Command("catalog"))
|
||
async def catalog(message: Message, api: ShopApi) -> None:
|
||
products = await api.products()
|
||
if not products:
|
||
await message.answer("Каталог пуст.")
|
||
return
|
||
await message.answer("Каталог", reply_markup=_catalog_keyboard(products))
|
||
|
||
|
||
@router.callback_query(F.data.startswith("prod:"))
|
||
async def show_product(query: CallbackQuery, api: ShopApi) -> None:
|
||
product_id = int(query.data.split(":", 1)[1])
|
||
try:
|
||
product = await api.product(product_id)
|
||
except ApiError as exc:
|
||
await query.answer(exc.detail, show_alert=True)
|
||
return
|
||
text = f"{product['title']}\n{product['description']}\n{product['price']} ₽\n{product['category_name']}"
|
||
if query.message is not None:
|
||
await query.message.answer(text, reply_markup=_product_keyboard(product_id))
|
||
await query.answer()
|
||
|
||
|
||
@router.callback_query(F.data.startswith("add:"))
|
||
async def add_product(query: CallbackQuery, api: ShopApi) -> None:
|
||
if query.from_user is None:
|
||
await query.answer()
|
||
return
|
||
product_id = int(query.data.split(":", 1)[1])
|
||
try:
|
||
cart = await api.add(query.from_user.id, product_id)
|
||
except ApiError as exc:
|
||
await query.answer(exc.detail, show_alert=True)
|
||
return
|
||
await query.answer("Добавлено")
|
||
if query.message is not None:
|
||
await query.message.answer(_cart_text(cart))
|
||
|
||
|
||
@router.message(Command("cart"))
|
||
async def cart(message: Message, api: ShopApi) -> None:
|
||
if message.from_user is None:
|
||
return
|
||
data = await api.cart(message.from_user.id)
|
||
await message.answer(_cart_text(data))
|
||
|
||
|
||
@router.message(Command("checkout"))
|
||
@router.callback_query(F.data == "checkout")
|
||
async def checkout_start(event: Message | CallbackQuery, api: ShopApi) -> None:
|
||
user = event.from_user
|
||
if user is None:
|
||
return
|
||
await api.save_dialog(user.id, "name", None)
|
||
text = "Как вас зовут?"
|
||
if isinstance(event, CallbackQuery):
|
||
await event.answer()
|
||
if event.message is not None:
|
||
await event.message.answer(text)
|
||
return
|
||
await event.answer(text)
|
||
|
||
|
||
@router.message(F.text)
|
||
async def dialog(message: Message, api: ShopApi) -> None:
|
||
if message.from_user is None or message.text is None or message.text.startswith("/"):
|
||
return
|
||
state = await api.dialog(message.from_user.id)
|
||
if state is None:
|
||
return
|
||
if state["step"] == "name":
|
||
await api.save_dialog(message.from_user.id, "phone", message.text.strip()[:120])
|
||
await message.answer("Телефон? Можно цифры, пробелы и +()-.")
|
||
return
|
||
if state["step"] != "phone" or not state.get("name"):
|
||
await api.clear_dialog(message.from_user.id)
|
||
return
|
||
try:
|
||
order = await api.order(message.from_user.id, state["name"], message.text.strip())
|
||
except ApiError as exc:
|
||
await message.answer(exc.detail)
|
||
return
|
||
await api.clear_dialog(message.from_user.id)
|
||
await message.answer(
|
||
f"Заказ {order['id']} создан, без оплаты.\nСумма: {order['total']} ₽\nИмя: {order['customer_name']}"
|
||
)
|