Product rendering, cart functionality
This commit is contained in:
@@ -1,13 +1,19 @@
|
|||||||
|
import os
|
||||||
from re import Match
|
from re import Match
|
||||||
from aiogram import Router, F
|
from aiogram import Router, F
|
||||||
from aiogram.types import CallbackQuery, Message
|
from aiogram.types import CallbackQuery, FSInputFile, Message
|
||||||
from aiogram.filters import Command
|
from aiogram.filters import Command
|
||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.fsm.context import FSMContext
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from config import PAGE_SIZE
|
from config import PAGE_SIZE
|
||||||
from misc.kb import main_menu_kb, render_category
|
from misc.kb import main_menu_kb, render_category
|
||||||
from misc.kb.client import get_back_to_catalogue, render_products
|
from misc.kb.client import (
|
||||||
|
get_back_to_catalogue,
|
||||||
|
render_product_interactions,
|
||||||
|
render_products,
|
||||||
|
)
|
||||||
|
from misc.texts import get_product_description
|
||||||
from repositories.categories import CategoriesRepository
|
from repositories.categories import CategoriesRepository
|
||||||
from repositories.products import ProductRepository
|
from repositories.products import ProductRepository
|
||||||
from services.orders import OrderService
|
from services.orders import OrderService
|
||||||
@@ -86,10 +92,26 @@ async def subcatalogue(
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if cb.message.photo:
|
||||||
|
await cb.message.delete()
|
||||||
|
await cb.message.answer(
|
||||||
|
"hya",
|
||||||
|
reply_markup=render_products(
|
||||||
|
products[:PAGE_SIZE],
|
||||||
|
parent_id=parent_id,
|
||||||
|
cat_id=cat_id,
|
||||||
|
page=0,
|
||||||
|
show_prev=products_len < 0,
|
||||||
|
show_next=products_len > PAGE_SIZE,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
await cb.message.edit_text(
|
await cb.message.edit_text(
|
||||||
"hya",
|
"hya",
|
||||||
reply_markup=render_products(
|
reply_markup=render_products(
|
||||||
products[:PAGE_SIZE],
|
products[:PAGE_SIZE],
|
||||||
|
parent_id=parent_id,
|
||||||
cat_id=cat_id,
|
cat_id=cat_id,
|
||||||
page=0,
|
page=0,
|
||||||
show_prev=products_len < 0,
|
show_prev=products_len < 0,
|
||||||
@@ -117,11 +139,13 @@ async def products_pagination(
|
|||||||
pagination_match: Match[str],
|
pagination_match: Match[str],
|
||||||
session: AsyncSession,
|
session: AsyncSession,
|
||||||
products_repo: ProductRepository,
|
products_repo: ProductRepository,
|
||||||
|
categories_repo: CategoriesRepository,
|
||||||
):
|
):
|
||||||
await state.clear()
|
await state.clear()
|
||||||
|
|
||||||
pagination_data = pagination_match.groupdict()
|
pagination_data = pagination_match.groupdict()
|
||||||
category_id = int(pagination_data.get("category_id"))
|
category_id = int(pagination_data.get("category_id"))
|
||||||
|
category = await categories_repo.get_category_by_id(session, category_id)
|
||||||
page_id = int(pagination_data.get("page_id", 0))
|
page_id = int(pagination_data.get("page_id", 0))
|
||||||
|
|
||||||
products = await products_repo.get_product_by_category(
|
products = await products_repo.get_product_by_category(
|
||||||
@@ -132,9 +156,108 @@ async def products_pagination(
|
|||||||
await cb.message.edit_reply_markup(
|
await cb.message.edit_reply_markup(
|
||||||
reply_markup=render_products(
|
reply_markup=render_products(
|
||||||
products[:PAGE_SIZE],
|
products[:PAGE_SIZE],
|
||||||
|
parent_id=category.parent_id,
|
||||||
cat_id=category_id,
|
cat_id=category_id,
|
||||||
page=page_id,
|
page=page_id,
|
||||||
show_prev=page_id > 0,
|
show_prev=page_id > 0,
|
||||||
show_next=products_len > PAGE_SIZE,
|
show_next=products_len > PAGE_SIZE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data.startswith("product:"))
|
||||||
|
async def product_card(
|
||||||
|
cb: CallbackQuery,
|
||||||
|
state: FSMContext,
|
||||||
|
session: AsyncSession,
|
||||||
|
products_repo: ProductRepository,
|
||||||
|
order_service: OrderService,
|
||||||
|
):
|
||||||
|
await state.clear()
|
||||||
|
|
||||||
|
product_id = int(cb.data.split(":")[1])
|
||||||
|
product = await products_repo.get_product_by_id(session, product_id)
|
||||||
|
|
||||||
|
order_item = await order_service.get_cart_product_amount(
|
||||||
|
session, customer=cb.from_user.id, product_id=product_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if product.file_id:
|
||||||
|
await cb.message.delete()
|
||||||
|
|
||||||
|
msg = await cb.message.answer_photo(
|
||||||
|
product.file_id,
|
||||||
|
caption=get_product_description(product),
|
||||||
|
reply_markup=render_product_interactions(
|
||||||
|
product_id=product.id,
|
||||||
|
category_id=product.category_id,
|
||||||
|
show_cart=bool(order_item),
|
||||||
|
cart_amount=order_item,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.path.isfile(product.img_path):
|
||||||
|
await cb.answer("something has gone wrong, try again later...")
|
||||||
|
return # TODO: Add logging + Admin notifications
|
||||||
|
|
||||||
|
await cb.message.delete()
|
||||||
|
msg = await cb.message.answer_photo(
|
||||||
|
FSInputFile(product.img_path),
|
||||||
|
caption=get_product_description(product),
|
||||||
|
reply_markup=render_product_interactions(
|
||||||
|
product_id=product.id,
|
||||||
|
category_id=product.category_id,
|
||||||
|
show_cart=bool(order_item),
|
||||||
|
cart_amount=order_item,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if not msg.photo:
|
||||||
|
return # TODO: Add logging
|
||||||
|
|
||||||
|
await products_repo.add_product_file_id(
|
||||||
|
session, product_id, file_id=msg.photo[-1].file_id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(F.data.startswith("cart_action:"))
|
||||||
|
async def add_to_cart(
|
||||||
|
cb: CallbackQuery,
|
||||||
|
state: FSMContext,
|
||||||
|
session: AsyncSession,
|
||||||
|
order_service: OrderService,
|
||||||
|
products_repo: ProductRepository,
|
||||||
|
):
|
||||||
|
await state.clear()
|
||||||
|
|
||||||
|
action = cb.data.split(":")[1]
|
||||||
|
product_id = int(cb.data.split(":")[2])
|
||||||
|
product = await products_repo.get_product_by_id(session, product_id)
|
||||||
|
show_cart = True
|
||||||
|
|
||||||
|
if action == "add":
|
||||||
|
quantity = await order_service.add_to_cart(
|
||||||
|
session, customer=cb.from_user.id, product_id=product_id
|
||||||
|
)
|
||||||
|
elif action == "remove":
|
||||||
|
quantity = await order_service.remove_from_cart(
|
||||||
|
session, customer=cb.from_user.id, product_id=product_id
|
||||||
|
)
|
||||||
|
show_cart = bool(quantity)
|
||||||
|
else:
|
||||||
|
show_cart = False
|
||||||
|
quantity = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
await cb.message.edit_reply_markup(
|
||||||
|
reply_markup=render_product_interactions(
|
||||||
|
product_id=product_id,
|
||||||
|
category_id=product.category_id,
|
||||||
|
show_cart=show_cart,
|
||||||
|
cart_amount=quantity,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
await cb.answer("🍃 Корзина уже пуста.")
|
||||||
|
return
|
||||||
|
|||||||
22
main.py
22
main.py
@@ -2,6 +2,7 @@ import os
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from aiogram import Bot, Dispatcher
|
from aiogram import Bot, Dispatcher
|
||||||
|
from aiogram.client.default import DefaultBotProperties
|
||||||
|
|
||||||
from config import BOT_TOKEN
|
from config import BOT_TOKEN
|
||||||
from handlers import init_handlers
|
from handlers import init_handlers
|
||||||
@@ -10,6 +11,7 @@ from middlewares.di import DIMiddleware
|
|||||||
from middlewares.repository import RepositoryMiddleware
|
from middlewares.repository import RepositoryMiddleware
|
||||||
from middlewares.session import DBSessionMiddleware
|
from middlewares.session import DBSessionMiddleware
|
||||||
from repositories.categories import CategoriesRepository
|
from repositories.categories import CategoriesRepository
|
||||||
|
from repositories.order_items import OrderItemRepository
|
||||||
from repositories.orders import OrderRepository
|
from repositories.orders import OrderRepository
|
||||||
from repositories.products import ProductRepository
|
from repositories.products import ProductRepository
|
||||||
from services import OrderService
|
from services import OrderService
|
||||||
@@ -21,14 +23,16 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
os.makedirs("static/img", exist_ok=True)
|
os.makedirs("static/img", exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
dp = Dispatcher()
|
dp = Dispatcher()
|
||||||
|
|
||||||
bot = Bot(token=BOT_TOKEN)
|
bot = Bot(token=BOT_TOKEN, default=DefaultBotProperties(parse_mode="HTML"))
|
||||||
|
|
||||||
# DB services creation
|
# DB services creation
|
||||||
order_repository = OrderRepository()
|
order_repository = OrderRepository()
|
||||||
order_service = OrderService(order_repository)
|
order_items_repository = OrderItemRepository()
|
||||||
|
order_service = OrderService(order_repository, order_items_repository)
|
||||||
|
|
||||||
category_repository = CategoriesRepository()
|
category_repository = CategoriesRepository()
|
||||||
|
|
||||||
@@ -38,13 +42,23 @@ async def main():
|
|||||||
dp.message.middleware(DIMiddleware(order_service))
|
dp.message.middleware(DIMiddleware(order_service))
|
||||||
dp.message.middleware(DBSessionMiddleware(async_session))
|
dp.message.middleware(DBSessionMiddleware(async_session))
|
||||||
dp.message.middleware(
|
dp.message.middleware(
|
||||||
RepositoryMiddleware(order_repository, category_repository, product_repository)
|
RepositoryMiddleware(
|
||||||
|
order_repository,
|
||||||
|
category_repository,
|
||||||
|
product_repository,
|
||||||
|
order_items_repository,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
dp.callback_query.middleware(DIMiddleware(order_service))
|
dp.callback_query.middleware(DIMiddleware(order_service))
|
||||||
dp.callback_query.middleware(DBSessionMiddleware(async_session))
|
dp.callback_query.middleware(DBSessionMiddleware(async_session))
|
||||||
dp.callback_query.middleware(
|
dp.callback_query.middleware(
|
||||||
RepositoryMiddleware(order_repository, category_repository, product_repository)
|
RepositoryMiddleware(
|
||||||
|
order_repository,
|
||||||
|
category_repository,
|
||||||
|
product_repository,
|
||||||
|
order_items_repository,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
init_handlers(dp)
|
init_handlers(dp)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from aiogram import BaseMiddleware
|
from aiogram import BaseMiddleware
|
||||||
|
|
||||||
from repositories import OrderRepository, CategoriesRepository
|
from repositories import OrderRepository, CategoriesRepository
|
||||||
|
from repositories.order_items import OrderItemRepository
|
||||||
from repositories.products import ProductRepository
|
from repositories.products import ProductRepository
|
||||||
|
|
||||||
|
|
||||||
@@ -10,13 +11,16 @@ class RepositoryMiddleware(BaseMiddleware):
|
|||||||
order_repo: OrderRepository,
|
order_repo: OrderRepository,
|
||||||
categories_repo: CategoriesRepository,
|
categories_repo: CategoriesRepository,
|
||||||
products_repo: ProductRepository,
|
products_repo: ProductRepository,
|
||||||
|
order_items_repo: OrderItemRepository,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._order_repo = order_repo
|
self._order_repo = order_repo
|
||||||
self._categories_repo = categories_repo
|
self._categories_repo = categories_repo
|
||||||
self._products_repo = products_repo
|
self._products_repo = products_repo
|
||||||
|
self._order_items_repo = order_items_repo
|
||||||
|
|
||||||
async def __call__(self, handler, event, data):
|
async def __call__(self, handler, event, data):
|
||||||
data["order_repo"] = self._order_repo
|
data["order_repo"] = self._order_repo
|
||||||
data["categories_repo"] = self._categories_repo
|
data["categories_repo"] = self._categories_repo
|
||||||
data["products_repo"] = self._products_repo
|
data["products_repo"] = self._products_repo
|
||||||
|
data["order_items_repo"] = self._order_items_repo
|
||||||
return await handler(event, data)
|
return await handler(event, data)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ def render_category(
|
|||||||
def render_products(
|
def render_products(
|
||||||
products: list[Product],
|
products: list[Product],
|
||||||
*,
|
*,
|
||||||
|
parent_id: int,
|
||||||
cat_id: int,
|
cat_id: int,
|
||||||
page: int = 0,
|
page: int = 0,
|
||||||
show_prev: Optional[bool] = False,
|
show_prev: Optional[bool] = False,
|
||||||
@@ -97,10 +98,50 @@ def render_products(
|
|||||||
[
|
[
|
||||||
InlineKeyboardButton(
|
InlineKeyboardButton(
|
||||||
text="⬅️ Назад",
|
text="⬅️ Назад",
|
||||||
callback_data=f"cat:{cat_id}",
|
callback_data=f"cat:{parent_id}",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
+ btns
|
+ btns
|
||||||
+ [nav]
|
+ [nav]
|
||||||
).as_markup()
|
).as_markup()
|
||||||
|
|
||||||
|
|
||||||
|
def render_product_interactions(
|
||||||
|
*, product_id: int, category_id: int, show_cart: bool = False, cart_amount: int = 0
|
||||||
|
) -> InlineKeyboardMarkup:
|
||||||
|
btns: list[list[InlineKeyboardButton]] = []
|
||||||
|
|
||||||
|
if show_cart:
|
||||||
|
btns.append(
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text="➖", callback_data=f"cart_action:remove:{product_id}"
|
||||||
|
),
|
||||||
|
InlineKeyboardButton(text=f"🛒 {cart_amount}", callback_data="..."),
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text="➕", callback_data=f"cart_action:add:{product_id}"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
btns.append(
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text="❌ Удалить из корзины",
|
||||||
|
callback_data=f"cart_action:clear:{product_id}",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
btns.append(
|
||||||
|
[
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text="🛒 В корзину", callback_data=f"cart_action:add:{product_id}"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
btns.append([InlineKeyboardButton(text="⬅️", callback_data=f"cat:{category_id}")])
|
||||||
|
|
||||||
|
return InlineKeyboardBuilder(btns).as_markup()
|
||||||
|
|||||||
9
misc/texts.py
Normal file
9
misc/texts.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from db.models import Product
|
||||||
|
|
||||||
|
|
||||||
|
def get_product_description(product: Product) -> str:
|
||||||
|
output = f"📦 <b>{product.name}</b>\n\n<b><i>🏷️ {product.price}₽</i></b>"
|
||||||
|
if product.description:
|
||||||
|
output += f"\n\n📜 {product.description}"
|
||||||
|
|
||||||
|
return output
|
||||||
52
repositories/order_items.py
Normal file
52
repositories/order_items.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy import select, update
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from db.models.orders import OrderItem
|
||||||
|
|
||||||
|
|
||||||
|
class OrderItemRepository:
|
||||||
|
async def get_item_by_id(
|
||||||
|
self, session: AsyncSession, item_id: int
|
||||||
|
) -> Optional[OrderItem]:
|
||||||
|
stmt = select(OrderItem).where(OrderItem.id == item_id)
|
||||||
|
return await session.scalar(stmt)
|
||||||
|
|
||||||
|
async def get_item_by_order_and_product(
|
||||||
|
self, session: AsyncSession, *, order_id: int, product_id: int
|
||||||
|
) -> Optional[OrderItem]:
|
||||||
|
stmt = (
|
||||||
|
select(OrderItem)
|
||||||
|
.where(OrderItem.order_id == order_id)
|
||||||
|
.where(OrderItem.product_id == product_id)
|
||||||
|
)
|
||||||
|
return await session.scalar(stmt)
|
||||||
|
|
||||||
|
async def update_order_item_quantity(
|
||||||
|
self, session: AsyncSession, order_item_id: int, *, quantity: int = 0
|
||||||
|
):
|
||||||
|
stmt = (
|
||||||
|
update(OrderItem.__table__)
|
||||||
|
.where(OrderItem.id == order_item_id)
|
||||||
|
.values(quantity=quantity)
|
||||||
|
)
|
||||||
|
|
||||||
|
await session.execute(stmt)
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
async def create_order_item(
|
||||||
|
self,
|
||||||
|
session: AsyncSession,
|
||||||
|
*,
|
||||||
|
order_id: int,
|
||||||
|
product_id: int,
|
||||||
|
quantity: int = 1,
|
||||||
|
) -> OrderItem:
|
||||||
|
order_item = OrderItem(
|
||||||
|
order_id=order_id, product_id=product_id, quantity=quantity
|
||||||
|
)
|
||||||
|
|
||||||
|
session.add(order_item)
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
return order_item
|
||||||
@@ -2,7 +2,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from db.models import Order
|
from db.models import Order, OrderStatus
|
||||||
|
|
||||||
|
|
||||||
class OrderRepository:
|
class OrderRepository:
|
||||||
@@ -13,9 +13,33 @@ class OrderRepository:
|
|||||||
return await session.scalar(stmt)
|
return await session.scalar(stmt)
|
||||||
|
|
||||||
async def get_orders_by_user(
|
async def get_orders_by_user(
|
||||||
self, session: AsyncSession, user_id: int
|
self, session: AsyncSession, customer: int
|
||||||
) -> list[Order]:
|
) -> list[Order]:
|
||||||
stmt = select(Order).where(Order.customer == user_id)
|
stmt = select(Order).where(Order.customer == customer)
|
||||||
result = await session.scalars(stmt)
|
result = await session.scalars(stmt)
|
||||||
|
|
||||||
return list(result)
|
return list(result)
|
||||||
|
|
||||||
|
async def get_draft_order_by_user(
|
||||||
|
self, session: AsyncSession, customer: int
|
||||||
|
) -> Optional[Order]:
|
||||||
|
stmt = (
|
||||||
|
select(Order)
|
||||||
|
.where(Order.customer == customer)
|
||||||
|
.where(Order.status == OrderStatus.DRAFT)
|
||||||
|
.limit(1)
|
||||||
|
)
|
||||||
|
return await session.scalar(stmt)
|
||||||
|
|
||||||
|
async def create_order(
|
||||||
|
self,
|
||||||
|
session: AsyncSession,
|
||||||
|
customer: int,
|
||||||
|
status: OrderStatus = OrderStatus.DRAFT,
|
||||||
|
):
|
||||||
|
order = Order(customer=customer, status=status)
|
||||||
|
|
||||||
|
session.add(order)
|
||||||
|
await session.commit()
|
||||||
|
|
||||||
|
return order
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select, update
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from db.models import Product
|
from db.models import Product
|
||||||
@@ -26,3 +26,15 @@ class ProductRepository:
|
|||||||
result = await session.scalars(stmt)
|
result = await session.scalars(stmt)
|
||||||
|
|
||||||
return list(result)
|
return list(result)
|
||||||
|
|
||||||
|
async def add_product_file_id(
|
||||||
|
self, session: AsyncSession, product_id: int, *, file_id: str
|
||||||
|
) -> None:
|
||||||
|
stmt = (
|
||||||
|
update(Product.__table__)
|
||||||
|
.where(Product.id == product_id)
|
||||||
|
.where(Product.file_id is None)
|
||||||
|
.values(file_id=file_id)
|
||||||
|
)
|
||||||
|
await session.execute(stmt)
|
||||||
|
await session.commit()
|
||||||
|
|||||||
@@ -1,11 +1,117 @@
|
|||||||
|
import logging
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from db.models.orders import OrderStatus
|
||||||
|
from repositories.order_items import OrderItemRepository
|
||||||
from repositories.orders import OrderRepository
|
from repositories.orders import OrderRepository
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class OrderServiceException(Exception): ...
|
||||||
|
|
||||||
|
|
||||||
class OrderService:
|
class OrderService:
|
||||||
def __init__(self, order_repo: OrderRepository):
|
def __init__(
|
||||||
|
self, order_repo: OrderRepository, order_items_repo: OrderItemRepository
|
||||||
|
):
|
||||||
self.order_repo = order_repo
|
self.order_repo = order_repo
|
||||||
|
self.order_items_repo = order_items_repo
|
||||||
|
|
||||||
async def get_order_count(self, session: AsyncSession, user_id: int) -> int:
|
async def get_order_count(self, session: AsyncSession, customer: int) -> int:
|
||||||
orders = await self.order_repo.get_orders_by_user(session, user_id)
|
orders = await self.order_repo.get_orders_by_user(session, customer)
|
||||||
return len(orders)
|
return len(orders)
|
||||||
|
|
||||||
|
async def add_to_cart(
|
||||||
|
self,
|
||||||
|
session: AsyncSession,
|
||||||
|
*,
|
||||||
|
customer: int,
|
||||||
|
product_id: int,
|
||||||
|
quantity: int = 1,
|
||||||
|
) -> int:
|
||||||
|
order = await self.order_repo.get_draft_order_by_user(session, customer)
|
||||||
|
|
||||||
|
if not order:
|
||||||
|
order = await self.order_repo.create_order(
|
||||||
|
session, customer, OrderStatus.DRAFT
|
||||||
|
)
|
||||||
|
|
||||||
|
order_item = await self.order_items_repo.get_item_by_order_and_product(
|
||||||
|
session, order_id=order.id, product_id=product_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not order_item:
|
||||||
|
order_item = await self.order_items_repo.create_order_item(
|
||||||
|
session, order_id=order.id, product_id=product_id, quantity=quantity
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await self.order_items_repo.update_order_item_quantity(
|
||||||
|
session, order_item.id, quantity=order_item.quantity + quantity
|
||||||
|
)
|
||||||
|
|
||||||
|
return order_item.quantity + quantity
|
||||||
|
|
||||||
|
async def remove_from_cart(
|
||||||
|
self,
|
||||||
|
session: AsyncSession,
|
||||||
|
*,
|
||||||
|
customer: int,
|
||||||
|
product_id: int,
|
||||||
|
quantity: int = 1,
|
||||||
|
) -> int:
|
||||||
|
order = await self.order_repo.get_draft_order_by_user(session, customer)
|
||||||
|
|
||||||
|
if not order:
|
||||||
|
raise OrderServiceException(
|
||||||
|
"attempt of removing product from non-existing cart, potential callback_query exploit, ignoring..."
|
||||||
|
)
|
||||||
|
|
||||||
|
order_item = await self.order_items_repo.get_item_by_order_and_product(
|
||||||
|
session, order_id=order.id, product_id=product_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not order_item:
|
||||||
|
raise OrderServiceException(
|
||||||
|
"attempt of decreasing quantity of non-existing order_item, potential callback_query exploit, ignoring..."
|
||||||
|
)
|
||||||
|
|
||||||
|
new_quantity = max(0, order_item.quantity - quantity)
|
||||||
|
await self.order_items_repo.update_order_item_quantity(
|
||||||
|
session, order_item_id=order_item.id, quantity=new_quantity
|
||||||
|
)
|
||||||
|
|
||||||
|
return new_quantity
|
||||||
|
|
||||||
|
async def get_cart_product_amount(
|
||||||
|
self, session: AsyncSession, *, customer: int, product_id: int
|
||||||
|
) -> int:
|
||||||
|
order = await self.order_repo.get_draft_order_by_user(session, customer)
|
||||||
|
|
||||||
|
if not order:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
order_item = await self.order_items_repo.get_item_by_order_and_product(
|
||||||
|
session, order_id=order.id, product_id=product_id
|
||||||
|
)
|
||||||
|
return order_item.quantity
|
||||||
|
|
||||||
|
async def clear_cart_item(self, session, *, customer: int, product_id: int) -> None:
|
||||||
|
order = await self.order_repo.get_draft_order_by_user(session, customer)
|
||||||
|
|
||||||
|
if not order:
|
||||||
|
raise OrderServiceException(
|
||||||
|
"attempt of clearing a non-existing cart, potential callback_query exploit, ignoring..."
|
||||||
|
)
|
||||||
|
|
||||||
|
order_item = await self.order_items_repo.get_item_by_order_and_product(
|
||||||
|
session, order_id=order.id, product_id=product_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not order_item:
|
||||||
|
raise OrderServiceException(
|
||||||
|
"attempt of clearing an order_item that isn't in a cart, potential callback_query exploit, ignoring..."
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.order_items_repo.update_order_item_quantity(
|
||||||
|
session, order_item_id=order_item.id, quantity=0
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user