fix bugs, implement placing orders
This commit is contained in:
parent
30b32a4c02
commit
1ca7db6427
@ -1,19 +1,24 @@
|
|||||||
from typing import Optional
|
from asyncio import sleep, create_task
|
||||||
|
|
||||||
import rio
|
import rio
|
||||||
from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer
|
from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, PopupOpenOrCloseEvent
|
||||||
|
|
||||||
from src.ez_lan_manager.components.CateringCartItem import CateringCartItem
|
from src.ez_lan_manager.components.CateringCartItem import CateringCartItem
|
||||||
from src.ez_lan_manager.components.CateringOrderItem import CateringOrderItem
|
from src.ez_lan_manager.components.CateringOrderItem import CateringOrderItem
|
||||||
from src.ez_lan_manager.services.AccountingService import AccountingService
|
from src.ez_lan_manager.services.AccountingService import AccountingService
|
||||||
from src.ez_lan_manager.services.CateringService import CateringService
|
from src.ez_lan_manager.services.CateringService import CateringService, CateringError, CateringErrorType
|
||||||
from src.ez_lan_manager.types.CateringOrder import CateringOrder
|
from src.ez_lan_manager.types.CateringOrder import CateringOrder, CateringMenuItemsWithAmount
|
||||||
from src.ez_lan_manager.types.SessionStorage import SessionStorage
|
from src.ez_lan_manager.types.SessionStorage import SessionStorage
|
||||||
|
|
||||||
|
POPUP_CLOSE_TIMEOUT_SECONDS = 3
|
||||||
|
|
||||||
class ShoppingCartAndOrders(Component):
|
class ShoppingCartAndOrders(Component):
|
||||||
show_cart: bool = True
|
show_cart: bool = True
|
||||||
orders: list[CateringOrder] = []
|
orders: list[CateringOrder] = []
|
||||||
|
order_button_loading: bool = False
|
||||||
|
popup_message: str = ""
|
||||||
|
popup_is_shown: bool = False
|
||||||
|
popup_is_error: bool = True
|
||||||
|
|
||||||
async def switch(self) -> None:
|
async def switch(self) -> None:
|
||||||
self.show_cart = not self.show_cart
|
self.show_cart = not self.show_cart
|
||||||
@ -40,15 +45,53 @@ class ShoppingCartAndOrders(Component):
|
|||||||
if not user_id:
|
if not user_id:
|
||||||
return
|
return
|
||||||
cart = catering_service.get_cart(user_id)
|
cart = catering_service.get_cart(user_id)
|
||||||
cart.append(await catering_service.get_menu_item_by_id(article_id))
|
item_to_add = await catering_service.get_menu_item_by_id(article_id)
|
||||||
|
cart.append(item_to_add)
|
||||||
catering_service.save_cart(user_id, cart)
|
catering_service.save_cart(user_id, cart)
|
||||||
await self.force_refresh()
|
await self.force_refresh()
|
||||||
|
|
||||||
|
async def show_popup(self, text: str, is_error: bool) -> None:
|
||||||
|
self.popup_is_error = is_error
|
||||||
|
self.popup_message = text
|
||||||
|
self.popup_is_shown = True
|
||||||
|
await self.force_refresh()
|
||||||
|
await sleep(POPUP_CLOSE_TIMEOUT_SECONDS)
|
||||||
|
self.popup_is_shown = False
|
||||||
|
await self.force_refresh()
|
||||||
|
|
||||||
|
async def on_order_pressed(self) -> None:
|
||||||
|
self.order_button_loading = True
|
||||||
|
await self.force_refresh()
|
||||||
|
|
||||||
|
user_id = self.session[SessionStorage].user_id
|
||||||
|
cart = self.session[CateringService].get_cart(user_id)
|
||||||
|
if len(cart) < 1:
|
||||||
|
_ = create_task(self.show_popup("Warenkorb leer", True))
|
||||||
|
else:
|
||||||
|
items_with_amounts: CateringMenuItemsWithAmount = {}
|
||||||
|
for item in cart:
|
||||||
|
try:
|
||||||
|
items_with_amounts[item] += 1
|
||||||
|
except KeyError:
|
||||||
|
items_with_amounts[item] = 1
|
||||||
|
try:
|
||||||
|
await self.session[CateringService].place_order(items_with_amounts, user_id)
|
||||||
|
except CateringError as catering_error:
|
||||||
|
if catering_error.error_type == CateringErrorType.INCLUDES_DISABLED_ITEM:
|
||||||
|
_ = create_task(self.show_popup("Warenkorb enthält gesperrte Artikel", True))
|
||||||
|
elif catering_error.error_type == CateringErrorType.INSUFFICIENT_FUNDS:
|
||||||
|
_ = create_task(self.show_popup("Guthaben nicht ausreichend", True))
|
||||||
|
else:
|
||||||
|
_ = create_task(self.show_popup("Unbekannter Fehler", True))
|
||||||
|
self.session[CateringService].save_cart(self.session[SessionStorage].user_id, [])
|
||||||
|
self.order_button_loading = False
|
||||||
|
_ = create_task(self.show_popup("Bestellung erfolgreich aufgegeben!", False))
|
||||||
|
|
||||||
def build(self) -> rio.Component:
|
def build(self) -> rio.Component:
|
||||||
user_id = self.session[SessionStorage].user_id
|
user_id = self.session[SessionStorage].user_id
|
||||||
catering_service = self.session[CateringService]
|
catering_service = self.session[CateringService]
|
||||||
|
cart = catering_service.get_cart(user_id)
|
||||||
if self.show_cart:
|
if self.show_cart:
|
||||||
cart = catering_service.get_cart(user_id)
|
|
||||||
cart_container = ScrollContainer(
|
cart_container = ScrollContainer(
|
||||||
content=Column(
|
content=Column(
|
||||||
*[CateringCartItem(
|
*[CateringCartItem(
|
||||||
@ -66,6 +109,13 @@ class ShoppingCartAndOrders(Component):
|
|||||||
)
|
)
|
||||||
return Column(
|
return Column(
|
||||||
cart_container,
|
cart_container,
|
||||||
|
Popup(
|
||||||
|
anchor=cart_container,
|
||||||
|
content=Text(self.popup_message, style=TextStyle(fill=self.session.theme.danger_color if self.popup_is_error else self.session.theme.success_color), wrap=True, margin=2, justify="center", min_width=20),
|
||||||
|
is_open=self.popup_is_shown,
|
||||||
|
position="center",
|
||||||
|
color=self.session.theme.primary_color
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
Text(
|
Text(
|
||||||
text=f"Preis: {AccountingService.make_euro_string_from_int(sum(cart_item.price for cart_item in cart))}",
|
text=f"Preis: {AccountingService.make_euro_string_from_int(sum(cart_item.price for cart_item in cart))}",
|
||||||
@ -98,7 +148,9 @@ class ShoppingCartAndOrders(Component):
|
|||||||
margin_left=0,
|
margin_left=0,
|
||||||
shape="rectangle",
|
shape="rectangle",
|
||||||
style="major",
|
style="major",
|
||||||
color="primary"
|
color="primary",
|
||||||
|
on_press=self.on_order_pressed,
|
||||||
|
is_loading=self.order_button_loading
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -14,6 +14,7 @@ from src.ez_lan_manager.types.SessionStorage import SessionStorage
|
|||||||
class CateringPage(Component):
|
class CateringPage(Component):
|
||||||
show_cart = True
|
show_cart = True
|
||||||
all_menu_items: Optional[list[CateringMenuItem]] = None
|
all_menu_items: Optional[list[CateringMenuItem]] = None
|
||||||
|
shopping_cart_and_orders: list[ShoppingCartAndOrders] = []
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
self.session[SessionStorage].subscribe_to_logged_in_or_out_event(self.__class__.__name__, self.on_user_logged_in_status_changed)
|
self.session[SessionStorage].subscribe_to_logged_in_or_out_event(self.__class__.__name__, self.on_user_logged_in_status_changed)
|
||||||
@ -28,7 +29,7 @@ class CateringPage(Component):
|
|||||||
await self.force_refresh()
|
await self.force_refresh()
|
||||||
|
|
||||||
async def on_switcher_bar_changed(self, _: SwitcherBarChangeEvent) -> None:
|
async def on_switcher_bar_changed(self, _: SwitcherBarChangeEvent) -> None:
|
||||||
await self.shopping_cart_and_orders.switch()
|
await self.shopping_cart_and_orders[0].switch()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_menu_items_by_category(all_menu_items: list[CateringMenuItem], category: Optional[CateringMenuItemCategory]) -> list[CateringMenuItem]:
|
def get_menu_items_by_category(all_menu_items: list[CateringMenuItem], category: Optional[CateringMenuItemCategory]) -> list[CateringMenuItem]:
|
||||||
@ -37,7 +38,11 @@ class CateringPage(Component):
|
|||||||
|
|
||||||
def build(self) -> Component:
|
def build(self) -> Component:
|
||||||
user_id = self.session[SessionStorage].user_id
|
user_id = self.session[SessionStorage].user_id
|
||||||
self.shopping_cart_and_orders = ShoppingCartAndOrders()
|
if len(self.shopping_cart_and_orders) == 0:
|
||||||
|
self.shopping_cart_and_orders.append(ShoppingCartAndOrders())
|
||||||
|
if len(self.shopping_cart_and_orders) > 1:
|
||||||
|
self.shopping_cart_and_orders.clear()
|
||||||
|
self.shopping_cart_and_orders.append(ShoppingCartAndOrders())
|
||||||
switcher_bar = SwitcherBar(
|
switcher_bar = SwitcherBar(
|
||||||
values=["cart", "orders"],
|
values=["cart", "orders"],
|
||||||
names=["Warenkorb", "Bestellungen"],
|
names=["Warenkorb", "Bestellungen"],
|
||||||
@ -63,7 +68,7 @@ class CateringPage(Component):
|
|||||||
align_x=0.5
|
align_x=0.5
|
||||||
),
|
),
|
||||||
switcher_bar,
|
switcher_bar,
|
||||||
self.shopping_cart_and_orders
|
self.shopping_cart_and_orders[0]
|
||||||
)
|
)
|
||||||
) if user_id else Spacer()
|
) if user_id else Spacer()
|
||||||
|
|
||||||
@ -82,7 +87,7 @@ class CateringPage(Component):
|
|||||||
article_name=catering_menu_item.name,
|
article_name=catering_menu_item.name,
|
||||||
article_price=catering_menu_item.price,
|
article_price=catering_menu_item.price,
|
||||||
article_id=catering_menu_item.item_id,
|
article_id=catering_menu_item.item_id,
|
||||||
on_add_callback=self.shopping_cart_and_orders.on_add_item,
|
on_add_callback=self.shopping_cart_and_orders[0].on_add_item,
|
||||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||||
additional_info=catering_menu_item.additional_info,
|
additional_info=catering_menu_item.additional_info,
|
||||||
is_grey=idx % 2 == 0
|
is_grey=idx % 2 == 0
|
||||||
@ -104,7 +109,7 @@ class CateringPage(Component):
|
|||||||
article_name=catering_menu_item.name,
|
article_name=catering_menu_item.name,
|
||||||
article_price=catering_menu_item.price,
|
article_price=catering_menu_item.price,
|
||||||
article_id=catering_menu_item.item_id,
|
article_id=catering_menu_item.item_id,
|
||||||
on_add_callback=self.shopping_cart_and_orders.on_add_item,
|
on_add_callback=self.shopping_cart_and_orders[0].on_add_item,
|
||||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||||
additional_info=catering_menu_item.additional_info,
|
additional_info=catering_menu_item.additional_info,
|
||||||
is_grey=idx % 2 == 0
|
is_grey=idx % 2 == 0
|
||||||
@ -126,7 +131,7 @@ class CateringPage(Component):
|
|||||||
article_name=catering_menu_item.name,
|
article_name=catering_menu_item.name,
|
||||||
article_price=catering_menu_item.price,
|
article_price=catering_menu_item.price,
|
||||||
article_id=catering_menu_item.item_id,
|
article_id=catering_menu_item.item_id,
|
||||||
on_add_callback=self.shopping_cart_and_orders.on_add_item,
|
on_add_callback=self.shopping_cart_and_orders[0].on_add_item,
|
||||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||||
additional_info=catering_menu_item.additional_info,
|
additional_info=catering_menu_item.additional_info,
|
||||||
is_grey=idx % 2 == 0
|
is_grey=idx % 2 == 0
|
||||||
@ -148,7 +153,7 @@ class CateringPage(Component):
|
|||||||
article_name=catering_menu_item.name,
|
article_name=catering_menu_item.name,
|
||||||
article_price=catering_menu_item.price,
|
article_price=catering_menu_item.price,
|
||||||
article_id=catering_menu_item.item_id,
|
article_id=catering_menu_item.item_id,
|
||||||
on_add_callback=self.shopping_cart_and_orders.on_add_item,
|
on_add_callback=self.shopping_cart_and_orders[0].on_add_item,
|
||||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||||
additional_info=catering_menu_item.additional_info,
|
additional_info=catering_menu_item.additional_info,
|
||||||
is_grey=idx % 2 == 0
|
is_grey=idx % 2 == 0
|
||||||
@ -170,7 +175,7 @@ class CateringPage(Component):
|
|||||||
article_name=catering_menu_item.name,
|
article_name=catering_menu_item.name,
|
||||||
article_price=catering_menu_item.price,
|
article_price=catering_menu_item.price,
|
||||||
article_id=catering_menu_item.item_id,
|
article_id=catering_menu_item.item_id,
|
||||||
on_add_callback=self.shopping_cart_and_orders.on_add_item,
|
on_add_callback=self.shopping_cart_and_orders[0].on_add_item,
|
||||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||||
additional_info=catering_menu_item.additional_info,
|
additional_info=catering_menu_item.additional_info,
|
||||||
is_grey=idx % 2 == 0
|
is_grey=idx % 2 == 0
|
||||||
@ -192,7 +197,7 @@ class CateringPage(Component):
|
|||||||
article_name=catering_menu_item.name,
|
article_name=catering_menu_item.name,
|
||||||
article_price=catering_menu_item.price,
|
article_price=catering_menu_item.price,
|
||||||
article_id=catering_menu_item.item_id,
|
article_id=catering_menu_item.item_id,
|
||||||
on_add_callback=self.shopping_cart_and_orders.on_add_item,
|
on_add_callback=self.shopping_cart_and_orders[0].on_add_item,
|
||||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||||
additional_info=catering_menu_item.additional_info,
|
additional_info=catering_menu_item.additional_info,
|
||||||
is_grey=idx % 2 == 0
|
is_grey=idx % 2 == 0
|
||||||
@ -214,7 +219,7 @@ class CateringPage(Component):
|
|||||||
article_name=catering_menu_item.name,
|
article_name=catering_menu_item.name,
|
||||||
article_price=catering_menu_item.price,
|
article_price=catering_menu_item.price,
|
||||||
article_id=catering_menu_item.item_id,
|
article_id=catering_menu_item.item_id,
|
||||||
on_add_callback=self.shopping_cart_and_orders.on_add_item,
|
on_add_callback=self.shopping_cart_and_orders[0].on_add_item,
|
||||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||||
additional_info=catering_menu_item.additional_info,
|
additional_info=catering_menu_item.additional_info,
|
||||||
is_grey=idx % 2 == 0
|
is_grey=idx % 2 == 0
|
||||||
@ -236,7 +241,7 @@ class CateringPage(Component):
|
|||||||
article_name=catering_menu_item.name,
|
article_name=catering_menu_item.name,
|
||||||
article_price=catering_menu_item.price,
|
article_price=catering_menu_item.price,
|
||||||
article_id=catering_menu_item.item_id,
|
article_id=catering_menu_item.item_id,
|
||||||
on_add_callback=self.shopping_cart_and_orders.on_add_item,
|
on_add_callback=self.shopping_cart_and_orders[0].on_add_item,
|
||||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||||
additional_info=catering_menu_item.additional_info,
|
additional_info=catering_menu_item.additional_info,
|
||||||
is_grey=idx % 2 == 0
|
is_grey=idx % 2 == 0
|
||||||
@ -258,7 +263,7 @@ class CateringPage(Component):
|
|||||||
article_name=catering_menu_item.name,
|
article_name=catering_menu_item.name,
|
||||||
article_price=catering_menu_item.price,
|
article_price=catering_menu_item.price,
|
||||||
article_id=catering_menu_item.item_id,
|
article_id=catering_menu_item.item_id,
|
||||||
on_add_callback=self.shopping_cart_and_orders.on_add_item,
|
on_add_callback=self.shopping_cart_and_orders[0].on_add_item,
|
||||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||||
additional_info=catering_menu_item.additional_info,
|
additional_info=catering_menu_item.additional_info,
|
||||||
is_grey=idx % 2 == 0
|
is_grey=idx % 2 == 0
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from enum import Enum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from src.ez_lan_manager.services.AccountingService import AccountingService
|
from src.ez_lan_manager.services.AccountingService import AccountingService
|
||||||
@ -9,9 +10,15 @@ from src.ez_lan_manager.types.CateringMenuItem import CateringMenuItem, Catering
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__.split(".")[-1])
|
logger = logging.getLogger(__name__.split(".")[-1])
|
||||||
|
|
||||||
|
class CateringErrorType(Enum):
|
||||||
|
INCLUDES_DISABLED_ITEM = 0
|
||||||
|
INSUFFICIENT_FUNDS = 1
|
||||||
|
GENERIC = 99
|
||||||
|
|
||||||
class CateringError(Exception):
|
class CateringError(Exception):
|
||||||
def __init__(self, message: str) -> None:
|
def __init__(self, message: str, error_type: CateringErrorType = CateringErrorType.GENERIC) -> None:
|
||||||
self.message = message
|
self.message = message
|
||||||
|
self.error_type = error_type
|
||||||
|
|
||||||
|
|
||||||
class CateringService:
|
class CateringService:
|
||||||
@ -26,7 +33,7 @@ class CateringService:
|
|||||||
async def place_order(self, menu_items: CateringMenuItemsWithAmount, user_id: int, is_delivery: bool = True) -> CateringOrder:
|
async def place_order(self, menu_items: CateringMenuItemsWithAmount, user_id: int, is_delivery: bool = True) -> CateringOrder:
|
||||||
for menu_item in menu_items:
|
for menu_item in menu_items:
|
||||||
if menu_item.is_disabled:
|
if menu_item.is_disabled:
|
||||||
raise CateringError("Order includes disabled items")
|
raise CateringError("Order includes disabled items", CateringErrorType.INCLUDES_DISABLED_ITEM)
|
||||||
|
|
||||||
user = await self._user_service.get_user(user_id)
|
user = await self._user_service.get_user(user_id)
|
||||||
if not user:
|
if not user:
|
||||||
@ -34,12 +41,13 @@ class CateringService:
|
|||||||
|
|
||||||
total_price = sum([item.price * quantity for item, quantity in menu_items.items()])
|
total_price = sum([item.price * quantity for item, quantity in menu_items.items()])
|
||||||
if await self._accounting_service.get_balance(user_id) < total_price:
|
if await self._accounting_service.get_balance(user_id) < total_price:
|
||||||
raise CateringError("Insufficient funds")
|
raise CateringError("Insufficient funds", CateringErrorType.INSUFFICIENT_FUNDS)
|
||||||
|
|
||||||
order = await self._db_service.add_new_order(menu_items, user_id, is_delivery)
|
order = await self._db_service.add_new_order(menu_items, user_id, is_delivery)
|
||||||
if order:
|
if order:
|
||||||
await self._accounting_service.remove_balance(user_id, total_price, f"CATERING - {order.order_id}")
|
await self._accounting_service.remove_balance(user_id, total_price, f"CATERING - {order.order_id}")
|
||||||
logger.info(f"User '{order.customer.user_name}' (ID:{order.customer.user_id}) ordered from catering for {self._accounting_service.make_euro_string_from_int(total_price)}")
|
logger.info(f"User '{order.customer.user_name}' (ID:{order.customer.user_id}) ordered from catering for {self._accounting_service.make_euro_string_from_int(total_price)}")
|
||||||
|
# await self.cancel_order(order) # ToDo: Check if commented out before commit. Un-comment to auto-cancel every placed order
|
||||||
return order
|
return order
|
||||||
|
|
||||||
async def update_order_status(self, order_id: int, new_status: CateringOrderStatus) -> bool:
|
async def update_order_status(self, order_id: int, new_status: CateringOrderStatus) -> bool:
|
||||||
@ -58,7 +66,8 @@ class CateringService:
|
|||||||
return await self._db_service.get_orders(status=status)
|
return await self._db_service.get_orders(status=status)
|
||||||
|
|
||||||
async def cancel_order(self, order: CateringOrder) -> bool:
|
async def cancel_order(self, order: CateringOrder) -> bool:
|
||||||
if self._db_service.change_order_status(order.order_id, CateringOrderStatus.CANCELED):
|
change_result = await self._db_service.change_order_status(order.order_id, CateringOrderStatus.CANCELED)
|
||||||
|
if change_result:
|
||||||
await self._accounting_service.add_balance(order.customer.user_id, order.price, f"CATERING REFUND - {order.order_id}")
|
await self._accounting_service.add_balance(order.customer.user_id, order.price, f"CATERING REFUND - {order.order_id}")
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|||||||
@ -27,4 +27,7 @@ class CateringOrder:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def price(self) -> int:
|
def price(self) -> int:
|
||||||
return sum([item.price for item in self.items.keys()])
|
total = 0
|
||||||
|
for item, amount in self.items.items():
|
||||||
|
total += (item.price * amount)
|
||||||
|
return total
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user