Add Catering Management (#4)

Co-authored-by: David Rodenkirchen <davidr.develop@gmail.com>
Reviewed-on: Vereins-IT/ez-lan-manager#4
This commit was merged in pull request #4.
This commit is contained in:
2025-02-01 14:23:12 +00:00
parent d86a3da135
commit f0f8a08f87
8 changed files with 222 additions and 10 deletions
@@ -25,6 +25,6 @@ class CateringCartItem(Component):
return Row(
Text(self.ellipsize_string(self.article_name), align_x=0, overflow="wrap", min_width=19, style=TextStyle(fill=self.session.theme.background_color, font_size=0.9)),
Text(AccountingService.make_euro_string_from_int(self.article_price), style=TextStyle(fill=self.session.theme.background_color, font_size=0.9)),
IconButton(icon="material/close", size=2, color=self.session.theme.danger_color, style="plain-text", on_press=lambda: self.remove_item_cb(self.list_id)),
IconButton(icon="material/close", min_size=2, color=self.session.theme.danger_color, style="plain-text", on_press=lambda: self.remove_item_cb(self.list_id)),
proportions=(19, 5, 2)
)
@@ -0,0 +1,100 @@
from functools import partial
from typing import Optional, Callable
from rio import Component, Row, Card, Column, Text, TextStyle, Spacer, PointerEventListener, Button
from src.ez_lan_manager.services.CateringService import CateringService
from src.ez_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus
from src.ez_lan_manager.types.Seat import Seat
class CateringManagementOrderDisplayStatusButton(Component):
status: CateringOrderStatus
clicked_cb: Callable
def build(self) -> Component:
return Button(
content=Text(
CateringOrder.translate_order_status(self.status)
),
shape="rectangle",
on_press=partial(self.clicked_cb, self.status)
)
class CateringManagementOrderDisplay(Component):
order: CateringOrder
seat: Optional[Seat]
clicked_cb: Callable
def format_order_status(self, status: CateringOrderStatus) -> Text:
status_text = CateringOrder.translate_order_status(status)
color = self.session.theme.warning_color
if status == CateringOrderStatus.DELAYED or status == CateringOrderStatus.CANCELED:
color = self.session.theme.danger_color
elif status == CateringOrderStatus.COMPLETED:
color = self.session.theme.success_color
return Text(text=status_text, style=TextStyle(fill=color))
async def status_button_clicked(self, new_status: CateringOrderStatus) -> None:
if self.order.status == CateringOrderStatus.CANCELED:
return
if new_status == CateringOrderStatus.CANCELED:
# ToDo: Hier sollten wir nochmal nachfragen ob der Bediener sich wirklich sicher ist,
# und anwarnen das eine stornierte Bestellung nicht ent-storniert werden kann.
pass
if self.order.status != new_status:
if new_status == CateringOrderStatus.CANCELED:
success = await self.session[CateringService].cancel_order(self.order)
else:
success = await self.session[CateringService].update_order_status(self.order.order_id, new_status)
if success:
self.order = CateringOrder(
order_id=self.order.order_id,
order_date=self.order.order_date,
status=new_status,
items=self.order.items,
customer=self.order.customer,
is_delivery=self.order.is_delivery
)
def build(self) -> Component:
return PointerEventListener(
content=Card(
content=Column(
Row(
Text(f"Status: ", margin_left=0.3, margin_top=0.2),
self.format_order_status(self.order.status),
Spacer(),
Text(self.order.order_date.strftime("%d.%m. - %H:%M Uhr"), margin_right=0.3),
),
Row(
Text(f"Gast: {self.order.customer.user_name}", margin_left=0.3),
Spacer(),
Text(f"Sitzplatz: {'-' if not self.seat else self.seat.seat_id}", margin_right=0.3),
),
Row(
Text("Diese Bestellung wird:", margin_left=0.3, margin_bottom=0.5),
Spacer(),
Text("Geliefert" if self.order.is_delivery else "Abgeholt", margin_right=0.3, margin_bottom=0.5),
),
Row(
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.RECEIVED, self.status_button_clicked),
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.CANCELED, self.status_button_clicked),
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.EN_ROUTE, self.status_button_clicked)
),
Row(
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.READY_FOR_PICKUP, self.status_button_clicked),
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.COMPLETED, self.status_button_clicked),
CateringManagementOrderDisplayStatusButton(CateringOrderStatus.DELAYED, self.status_button_clicked),
)
),
color=self.session.theme.hud_color,
colorize_on_hover=True,
margin=1
),
on_press=partial(self.clicked_cb, self.order)
)
@@ -65,8 +65,9 @@ class ShoppingCartAndOrders(Component):
user_id = self.session[SessionStorage].user_id
cart = self.session[CateringService].get_cart(user_id)
show_popup_task = None
if len(cart) < 1:
_ = create_task(self.show_popup("Warenkorb leer", True))
show_popup_task = create_task(self.show_popup("Warenkorb leer", True))
else:
items_with_amounts: CateringMenuItemsWithAmount = {}
for item in cart:
@@ -78,14 +79,15 @@ class ShoppingCartAndOrders(Component):
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))
show_popup_task = 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))
show_popup_task = create_task(self.show_popup("Guthaben nicht ausreichend", True))
else:
_ = create_task(self.show_popup("Unbekannter Fehler", True))
show_popup_task = 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))
if not show_popup_task:
show_popup_task = create_task(self.show_popup("Bestellung erfolgreich aufgegeben!", False))
async def _create_order_info_modal(self, order: CateringOrder) -> None:
def build_dialog_content() -> rio.Component:
@@ -65,6 +65,8 @@ class UserInfoBox(Component):
self.session[AccountingService].add_update_hook(self.update)
async def update(self) -> None:
if not self.user:
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
self.user_balance = await self.session[AccountingService].get_balance(self.user.user_id)
self.user_ticket = await self.session[TicketingService].get_user_ticket(self.user.user_id)
self.user_seat = await self.session[SeatingService].get_user_seat(self.user.user_id)