From 43ce42052e388579fc2e513e21b078ba0288adf9 Mon Sep 17 00:00:00 2001 From: tcprod Date: Tue, 4 Feb 2025 18:05:54 +0100 Subject: [PATCH] Fix Decimal precision issue --- .../components/CateringCartItem.py | 3 +- .../components/CateringSelectionItem.py | 16 ++- .../components/SeatingPlanInfoBox.py | 22 ++- .../components/ShoppingCartAndOrders.py | 3 +- .../components/TicketBuyCard.py | 3 +- src/ez_lan_manager/components/UserInfoBox.py | 32 +++-- .../helpers/create_demo_database_content.py | 125 +++++++++--------- src/ez_lan_manager/pages/SeatingPlanPage.py | 5 +- .../services/CateringService.py | 18 ++- .../services/DatabaseService.py | 2 +- src/ez_lan_manager/types/CateringMenuItem.py | 3 +- src/ez_lan_manager/types/CateringOrder.py | 7 +- 12 files changed, 136 insertions(+), 103 deletions(-) diff --git a/src/ez_lan_manager/components/CateringCartItem.py b/src/ez_lan_manager/components/CateringCartItem.py index f88e0f2..a1eb4e3 100644 --- a/src/ez_lan_manager/components/CateringCartItem.py +++ b/src/ez_lan_manager/components/CateringCartItem.py @@ -1,4 +1,5 @@ from typing import Callable +from decimal import Decimal import rio from rio import Component, Row, Text, IconButton, TextStyle @@ -9,7 +10,7 @@ MAX_LEN = 24 class CateringCartItem(Component): article_name: str - article_price: int + article_price: Decimal article_id: int list_id: int remove_item_cb: Callable diff --git a/src/ez_lan_manager/components/CateringSelectionItem.py b/src/ez_lan_manager/components/CateringSelectionItem.py index bcad5fb..25ed367 100644 --- a/src/ez_lan_manager/components/CateringSelectionItem.py +++ b/src/ez_lan_manager/components/CateringSelectionItem.py @@ -1,3 +1,4 @@ +from decimal import Decimal from typing import Callable import rio @@ -7,9 +8,10 @@ from src.ez_lan_manager import AccountingService MAX_LEN = 24 + class CateringSelectionItem(Component): article_name: str - article_price: int + article_price: Decimal article_id: int on_add_callback: Callable is_sensitive: bool @@ -33,15 +35,16 @@ class CateringSelectionItem(Component): return top.strip(), bottom.strip() - def build(self) -> rio.Component: article_name_top, article_name_bottom = self.split_article_name(self.article_name) return Card( content=Column( Row( - Text(article_name_top, 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_decimal(self.article_price), style=TextStyle(fill=self.session.theme.background_color, font_size=0.9)), + Text(article_name_top, 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_decimal(self.article_price), + style=TextStyle(fill=self.session.theme.background_color, font_size=0.9)), IconButton( icon="material/add", min_size=2, @@ -53,7 +56,10 @@ class CateringSelectionItem(Component): proportions=(19, 5, 2), margin_bottom=0 ), - Spacer() if not article_name_bottom else Text(article_name_bottom, align_x=0, overflow="wrap", min_width=19, style=TextStyle(fill=self.session.theme.background_color, font_size=0.9)), + Spacer() if not article_name_bottom else Text(article_name_bottom, align_x=0, overflow="wrap", + min_width=19, + style=TextStyle(fill=self.session.theme.background_color, + font_size=0.9)), Row( Text( self.additional_info, diff --git a/src/ez_lan_manager/components/SeatingPlanInfoBox.py b/src/ez_lan_manager/components/SeatingPlanInfoBox.py index 0096138..68668f7 100644 --- a/src/ez_lan_manager/components/SeatingPlanInfoBox.py +++ b/src/ez_lan_manager/components/SeatingPlanInfoBox.py @@ -1,3 +1,4 @@ +from decimal import Decimal from typing import Optional, Callable from rio import Component, Column, Text, TextStyle, Button, Spacer @@ -9,23 +10,30 @@ class SeatingPlanInfoBox(Component): is_booking_blocked: bool seat_id: Optional[str] = None seat_occupant: Optional[str] = None - seat_price: int = 0 + seat_price: Decimal = Decimal("0") is_blocked: bool = False - def build(self) -> Component: if not self.show: return Spacer() if self.is_blocked: - return Column(Text(f"Sitzplatz gesperrt", margin=1, style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.4), overflow="wrap", justify="center"), min_height=10) + return Column(Text(f"Sitzplatz gesperrt", margin=1, + style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.4), overflow="wrap", + justify="center"), min_height=10) if self.seat_id is None and self.seat_occupant is None: - return Column(Text(f"Sitzplatz auswählen...", margin=1, style=TextStyle(fill=self.session.theme.neutral_color), overflow="wrap", justify="center"), min_height=10) + return Column( + Text(f"Sitzplatz auswählen...", margin=1, style=TextStyle(fill=self.session.theme.neutral_color), + overflow="wrap", justify="center"), min_height=10) return Column( - Text(f"Dieser Sitzplatz ({self.seat_id}) ist gebucht von:", margin=1, style=TextStyle(fill=self.session.theme.neutral_color), overflow="wrap", justify="center"), - Text(f"{self.seat_occupant}", margin_bottom=1, style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.4), overflow="wrap", justify="center"), + Text(f"Dieser Sitzplatz ({self.seat_id}) ist gebucht von:", margin=1, + style=TextStyle(fill=self.session.theme.neutral_color), overflow="wrap", justify="center"), + Text(f"{self.seat_occupant}", margin_bottom=1, + style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.4), overflow="wrap", + justify="center"), min_height=10 ) if self.seat_id and self.seat_occupant else Column( - Text(f"Dieser Sitzplatz ({self.seat_id}) ist frei", margin=1, style=TextStyle(fill=self.session.theme.neutral_color), overflow="wrap", justify="center"), + Text(f"Dieser Sitzplatz ({self.seat_id}) ist frei", margin=1, + style=TextStyle(fill=self.session.theme.neutral_color), overflow="wrap", justify="center"), Button( Text( f"Buchen", diff --git a/src/ez_lan_manager/components/ShoppingCartAndOrders.py b/src/ez_lan_manager/components/ShoppingCartAndOrders.py index d913cb4..c0a660a 100644 --- a/src/ez_lan_manager/components/ShoppingCartAndOrders.py +++ b/src/ez_lan_manager/components/ShoppingCartAndOrders.py @@ -1,4 +1,5 @@ from asyncio import sleep, create_task +from decimal import Decimal import rio from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, Table @@ -158,7 +159,7 @@ class ShoppingCartAndOrders(Component): ), Row( Text( - text=f"Preis: {AccountingService.make_euro_string_from_decimal(sum(cart_item.price for cart_item in cart))}", + text=f"Preis: {AccountingService.make_euro_string_from_decimal(sum((cart_item.price for cart_item in cart), Decimal(0)))}", style=TextStyle( fill=self.session.theme.background_color, font_size=0.8 diff --git a/src/ez_lan_manager/components/TicketBuyCard.py b/src/ez_lan_manager/components/TicketBuyCard.py index 44c2871..c2b7497 100644 --- a/src/ez_lan_manager/components/TicketBuyCard.py +++ b/src/ez_lan_manager/components/TicketBuyCard.py @@ -1,5 +1,6 @@ from functools import partial from typing import Callable, Optional +from decimal import Decimal import rio from rio import Component, Card, Column, Text, Row, Button, TextStyle, ProgressBar, event, Spacer @@ -12,7 +13,7 @@ from src.ez_lan_manager.types.Ticket import Ticket class TicketBuyCard(Component): description: str additional_info: str - price: int + price: Decimal category: str pressed_cb: Callable is_enabled: bool diff --git a/src/ez_lan_manager/components/UserInfoBox.py b/src/ez_lan_manager/components/UserInfoBox.py index 41a5a14..4d06522 100644 --- a/src/ez_lan_manager/components/UserInfoBox.py +++ b/src/ez_lan_manager/components/UserInfoBox.py @@ -1,5 +1,6 @@ from random import choice from typing import Optional +from decimal import Decimal from rio import Component, TextStyle, Color, Button, Text, Rectangle, Column, Row, Spacer, Link, event, EventHandler @@ -23,26 +24,27 @@ class StatusButton(Component): def build(self) -> Component: return Link( - content=Button( - content=Text(self.label, style=self.STYLE, justify="center"), - shape="rectangle", - style="major", - color="success" if self.enabled else "danger", - grow_x=True, - margin_left=0.6, - margin_right=0.6, - margin_top=0.6 + content=Button( + content=Text(self.label, style=self.STYLE, justify="center"), + shape="rectangle", + style="major", + color="success" if self.enabled else "danger", + grow_x=True, + margin_left=0.6, + margin_right=0.6, + margin_top=0.6 ), target_url=self.target_url, align_y=0.5, grow_y=False ) + class UserInfoBox(Component): status_change_cb: EventHandler = None TEXT_STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9) user: Optional[User] = None - user_balance: Optional[int] = 0 + user_balance: Optional[Decimal] = Decimal("0") user_ticket: Optional[Ticket] = None user_seat: Optional[Seat] = None @@ -80,8 +82,10 @@ class UserInfoBox(Component): return Spacer() return Rectangle( content=Column( - Text(f"{self.get_greeting()},", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9), justify="center"), - Text(f"{self.user.user_name}", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=1.2), justify="center"), + Text(f"{self.get_greeting()},", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9), + justify="center"), + Text(f"{self.user.user_name}", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=1.2), + justify="center"), Row( StatusButton(label="TICKET", target_url="./buy_ticket", enabled=self.user_ticket is not None), @@ -91,7 +95,9 @@ class UserInfoBox(Component): grow_y=False ), UserInfoBoxButton("Profil bearbeiten", "./edit-profile"), - UserInfoBoxButton(f"Guthaben: {self.session[AccountingService].make_euro_string_from_decimal(self.user_balance)}", "./account"), + UserInfoBoxButton( + f"Guthaben: {self.session[AccountingService].make_euro_string_from_decimal(self.user_balance)}", + "./account"), Button( content=Text("Ausloggen", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.6)), shape="rectangle", diff --git a/src/ez_lan_manager/helpers/create_demo_database_content.py b/src/ez_lan_manager/helpers/create_demo_database_content.py index bd07e7c..749702c 100644 --- a/src/ez_lan_manager/helpers/create_demo_database_content.py +++ b/src/ez_lan_manager/helpers/create_demo_database_content.py @@ -49,7 +49,6 @@ async def run() -> None: DEMO_USERS[2]["password_clear_text"]) await accounting_service.add_balance(jason.user_id, Decimal("1000.00"), "DEMO EINZAHLUNG") await ticket_service.purchase_ticket(jason.user_id, "NORMAL") - await seating_service.seat_user(jason.user_id, "D10") # LISA lisa = await user_service.create_user(DEMO_USERS[3]["user_name"], DEMO_USERS[3]["user_mail"], @@ -68,110 +67,110 @@ async def run() -> None: if not input("Generate catering menu? (Y/n): ").lower() == "n": # MAIN_COURSE - await catering_service.add_menu_item("Schnitzel Wiener Art", "mit Pommes", 1050, + await catering_service.add_menu_item("Schnitzel Wiener Art", "mit Pommes", Decimal("10.00"), CateringMenuItemCategory.MAIN_COURSE) - await catering_service.add_menu_item("Jäger Schnitzel mit Champignonrahm Sauce", "mit Pommes", 1150, + await catering_service.add_menu_item("Jäger Schnitzel mit Champignonrahm Sauce", "mit Pommes", Decimal("11.50"), CateringMenuItemCategory.MAIN_COURSE) - await catering_service.add_menu_item("Tortellini in Käsesauce mit Fleischfüllung", "", 1050, + await catering_service.add_menu_item("Tortellini in Käsesauce mit Fleischfüllung", "", Decimal("10.50"), CateringMenuItemCategory.MAIN_COURSE) - await catering_service.add_menu_item("Tortellini in Käsesauce ohne Fleischfüllung", "Vegetarisch", 1050, + await catering_service.add_menu_item("Tortellini in Käsesauce ohne Fleischfüllung", "Vegetarisch", Decimal("10.50"), CateringMenuItemCategory.MAIN_COURSE) # SNACK - await catering_service.add_menu_item("Käse Schinken Wrap", "", 500, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Puten Paprika Wrap", "", 700, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Tomate Mozzarella Wrap", "", 600, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Portion Pommes", "", 400, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Rinds-Currywurst", "", 450, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Rinds-Currywurst mit Pommes", "", 650, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Nudelsalat", "", 450, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Nudelsalat mit Bockwurst", "", 600, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Kartoffelsalat", "", 450, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Kartoffelsalat mit Bockwurst", "", 600, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Sandwichtoast - Schinken", "", 180, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Sandwichtoast - Käse", "", 180, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Sandwichtoast - Schinken/Käse", "", 210, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Sandwichtoast - Salami", "", 180, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Sandwichtoast - Salami/Käse", "", 210, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Chips - Western Style", "", 130, CateringMenuItemCategory.SNACK) - await catering_service.add_menu_item("Nachos - Salted", "", 130, CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Käse Schinken Wrap", "", Decimal("5.00"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Puten Paprika Wrap", "", Decimal("7.00"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Tomate Mozzarella Wrap", "", Decimal("6.00"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Portion Pommes", "", Decimal("4.00"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Rinds-Currywurst", "", Decimal("4.50"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Rinds-Currywurst mit Pommes", "", Decimal("6.50"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Nudelsalat", "", Decimal("4.50"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Nudelsalat mit Bockwurst", "", Decimal("6.00"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Kartoffelsalat", "", Decimal("4.50"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Kartoffelsalat mit Bockwurst", "", Decimal("6.00"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Sandwichtoast - Schinken", "", Decimal("1.80"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Sandwichtoast - Käse", "", Decimal("1.80"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Sandwichtoast - Schinken/Käse", "", Decimal("2.10"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Sandwichtoast - Salami", "", Decimal("1.80"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Sandwichtoast - Salami/Käse", "", Decimal("2.10"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Chips - Western Style", "", Decimal("1.30"), CateringMenuItemCategory.SNACK) + await catering_service.add_menu_item("Nachos - Salted", "", Decimal("1.30"), CateringMenuItemCategory.SNACK) # DESSERT - await catering_service.add_menu_item("Panna Cotta mit Erdbeersauce", "", 700, CateringMenuItemCategory.DESSERT) - await catering_service.add_menu_item("Panna Cotta mit Blaubeersauce", "", 700, CateringMenuItemCategory.DESSERT) - await catering_service.add_menu_item("Mousse au Chocolat", "", 700, CateringMenuItemCategory.DESSERT) + await catering_service.add_menu_item("Panna Cotta mit Erdbeersauce", "", Decimal("7.00"), CateringMenuItemCategory.DESSERT) + await catering_service.add_menu_item("Panna Cotta mit Blaubeersauce", "", Decimal("7.00"), CateringMenuItemCategory.DESSERT) + await catering_service.add_menu_item("Mousse au Chocolat", "", Decimal("7.00"), CateringMenuItemCategory.DESSERT) # BREAKFAST - await catering_service.add_menu_item("Fruit Loops", "", 150, CateringMenuItemCategory.BREAKFAST) - await catering_service.add_menu_item("Smacks", "", 150, CateringMenuItemCategory.BREAKFAST) - await catering_service.add_menu_item("Knuspermüsli", "Schoko", 200, CateringMenuItemCategory.BREAKFAST) - await catering_service.add_menu_item("Cini Minis", "", 150, CateringMenuItemCategory.BREAKFAST) - await catering_service.add_menu_item("Brötchen - Schinken", "mit Margarine", 120, + await catering_service.add_menu_item("Fruit Loops", "", Decimal("1.50"), CateringMenuItemCategory.BREAKFAST) + await catering_service.add_menu_item("Smacks", "", Decimal("1.50"), CateringMenuItemCategory.BREAKFAST) + await catering_service.add_menu_item("Knuspermüsli", "Schoko", Decimal("2.00"), CateringMenuItemCategory.BREAKFAST) + await catering_service.add_menu_item("Cini Minis", "", Decimal("2.50"), CateringMenuItemCategory.BREAKFAST) + await catering_service.add_menu_item("Brötchen - Schinken", "mit Margarine", Decimal("1.20"), CateringMenuItemCategory.BREAKFAST) - await catering_service.add_menu_item("Brötchen - Käse", "mit Margarine", 120, + await catering_service.add_menu_item("Brötchen - Käse", "mit Margarine", Decimal("1.20"), CateringMenuItemCategory.BREAKFAST) - await catering_service.add_menu_item("Brötchen - Schinken/Käse", "mit Margarine", 140, + await catering_service.add_menu_item("Brötchen - Schinken/Käse", "mit Margarine", Decimal("1.40"), CateringMenuItemCategory.BREAKFAST) - await catering_service.add_menu_item("Brötchen - Salami", "mit Margarine", 120, + await catering_service.add_menu_item("Brötchen - Salami", "mit Margarine", Decimal("1.20"), CateringMenuItemCategory.BREAKFAST) - await catering_service.add_menu_item("Brötchen - Salami/Käse", "mit Margarine", 140, + await catering_service.add_menu_item("Brötchen - Salami/Käse", "mit Margarine", Decimal("1.40"), CateringMenuItemCategory.BREAKFAST) - await catering_service.add_menu_item("Brötchen - Nutella", "mit Margarine", 120, + await catering_service.add_menu_item("Brötchen - Nutella", "mit Margarine", Decimal("1.20"), CateringMenuItemCategory.BREAKFAST) # BEVERAGE_NON_ALCOHOLIC - await catering_service.add_menu_item("Wasser - Still", "1L Flasche", 200, + await catering_service.add_menu_item("Wasser - Still", "1L Flasche", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) - await catering_service.add_menu_item("Wasser - Medium", "1L Flasche", 200, + await catering_service.add_menu_item("Wasser - Medium", "1L Flasche", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) - await catering_service.add_menu_item("Wasser - Spritzig", "1L Flasche", 200, + await catering_service.add_menu_item("Wasser - Spritzig", "1L Flasche", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) - await catering_service.add_menu_item("Coca-Cola", "1L Flasche", 200, + await catering_service.add_menu_item("Coca-Cola", "1L Flasche", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) - await catering_service.add_menu_item("Coca-Cola Zero", "1L Flasche", 200, + await catering_service.add_menu_item("Coca-Cola Zero", "1L Flasche", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) - await catering_service.add_menu_item("Fanta", "1L Flasche", 200, + await catering_service.add_menu_item("Fanta", "1L Flasche", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) - await catering_service.add_menu_item("Sprite", "1L Flasche", 200, + await catering_service.add_menu_item("Sprite", "1L Flasche", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) - await catering_service.add_menu_item("Spezi", "von Paulaner, 0,5L Flasche", 150, + await catering_service.add_menu_item("Spezi", "von Paulaner, 0,5L Flasche", Decimal("1.50"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) - await catering_service.add_menu_item("Red Bull", "", 200, CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) - await catering_service.add_menu_item("Energy", "Hausmarke", 150, + await catering_service.add_menu_item("Red Bull", "", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) + await catering_service.add_menu_item("Energy", "Hausmarke", Decimal("1.50"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) # BEVERAGE_ALCOHOLIC - await catering_service.add_menu_item("Pils", "0,33L Flasche", 190, CateringMenuItemCategory.BEVERAGE_ALCOHOLIC) - await catering_service.add_menu_item("Radler", "0,33L Flasche", 190, + await catering_service.add_menu_item("Pils", "0,33L Flasche", Decimal("1.90"), CateringMenuItemCategory.BEVERAGE_ALCOHOLIC) + await catering_service.add_menu_item("Radler", "0,33L Flasche", Decimal("1.90"), CateringMenuItemCategory.BEVERAGE_ALCOHOLIC) - await catering_service.add_menu_item("Diesel", "0,33L Flasche", 190, + await catering_service.add_menu_item("Diesel", "0,33L Flasche", Decimal("1.90"), CateringMenuItemCategory.BEVERAGE_ALCOHOLIC) - await catering_service.add_menu_item("Apfelwein Pur", "0,33L Flasche", 190, + await catering_service.add_menu_item("Apfelwein Pur", "0,33L Flasche", Decimal("1.90"), CateringMenuItemCategory.BEVERAGE_ALCOHOLIC) - await catering_service.add_menu_item("Apfelwein Sauer", "0,33L Flasche", 190, + await catering_service.add_menu_item("Apfelwein Sauer", "0,33L Flasche", Decimal("1.90"), CateringMenuItemCategory.BEVERAGE_ALCOHOLIC) - await catering_service.add_menu_item("Apfelwein Cola", "0,33L Flasche", 190, + await catering_service.add_menu_item("Apfelwein Cola", "0,33L Flasche", Decimal("1.90"), CateringMenuItemCategory.BEVERAGE_ALCOHOLIC) # BEVERAGE_COCKTAIL - await catering_service.add_menu_item("Vodka Energy", "", 400, CateringMenuItemCategory.BEVERAGE_COCKTAIL) - await catering_service.add_menu_item("Vodka O-Saft", "", 400, CateringMenuItemCategory.BEVERAGE_COCKTAIL) - await catering_service.add_menu_item("Whiskey Cola", "mit Bourbon", 400, + await catering_service.add_menu_item("Vodka Energy", "", Decimal("4.00"), CateringMenuItemCategory.BEVERAGE_COCKTAIL) + await catering_service.add_menu_item("Vodka O-Saft", "", Decimal("4.00"), CateringMenuItemCategory.BEVERAGE_COCKTAIL) + await catering_service.add_menu_item("Whiskey Cola", "mit Bourbon", Decimal("4.00"), CateringMenuItemCategory.BEVERAGE_COCKTAIL) - await catering_service.add_menu_item("Jägermeister Energy", "", 400, CateringMenuItemCategory.BEVERAGE_COCKTAIL) - await catering_service.add_menu_item("Sex on the Beach", "", 550, CateringMenuItemCategory.BEVERAGE_COCKTAIL) - await catering_service.add_menu_item("Long Island Ice Tea", "", 550, CateringMenuItemCategory.BEVERAGE_COCKTAIL) - await catering_service.add_menu_item("Caipirinha", "", 550, CateringMenuItemCategory.BEVERAGE_COCKTAIL) + await catering_service.add_menu_item("Jägermeister Energy", "", Decimal("4.00"), CateringMenuItemCategory.BEVERAGE_COCKTAIL) + await catering_service.add_menu_item("Sex on the Beach", "", Decimal("5.50"), CateringMenuItemCategory.BEVERAGE_COCKTAIL) + await catering_service.add_menu_item("Long Island Ice Tea", "", Decimal("5.50"), CateringMenuItemCategory.BEVERAGE_COCKTAIL) + await catering_service.add_menu_item("Caipirinha", "", Decimal("5.50"), CateringMenuItemCategory.BEVERAGE_COCKTAIL) # BEVERAGE_SHOT - await catering_service.add_menu_item("Jägermeister", "", 200, CateringMenuItemCategory.BEVERAGE_SHOT) - await catering_service.add_menu_item("Tequila", "", 200, CateringMenuItemCategory.BEVERAGE_SHOT) - await catering_service.add_menu_item("PfEZzi", "Getunter Pfefferminz-Schnaps", 199, + await catering_service.add_menu_item("Jägermeister", "", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_SHOT) + await catering_service.add_menu_item("Tequila", "", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_SHOT) + await catering_service.add_menu_item("PfEZzi", "Getunter Pfefferminz-Schnaps", Decimal("1.99"), CateringMenuItemCategory.BEVERAGE_SHOT) # NON_FOOD - await catering_service.add_menu_item("Zigaretten", "Elixyr", 800, CateringMenuItemCategory.NON_FOOD) - await catering_service.add_menu_item("Mentholfilter", "passend für Elixyr", 120, + await catering_service.add_menu_item("Zigaretten", "Elixyr", Decimal("8.00"), CateringMenuItemCategory.NON_FOOD) + await catering_service.add_menu_item("Mentholfilter", "passend für Elixyr", Decimal("1.20"), CateringMenuItemCategory.NON_FOOD) if not input("Generate default new post? (Y/n): ").lower() == "n": diff --git a/src/ez_lan_manager/pages/SeatingPlanPage.py b/src/ez_lan_manager/pages/SeatingPlanPage.py index a67834f..cd63a08 100644 --- a/src/ez_lan_manager/pages/SeatingPlanPage.py +++ b/src/ez_lan_manager/pages/SeatingPlanPage.py @@ -1,5 +1,6 @@ import logging from asyncio import sleep +from decimal import Decimal from typing import Optional from rio import Text, Column, TextStyle, Component, event, PressEvent, ProgressCircle @@ -21,7 +22,7 @@ class SeatingPlanPage(Component): seating_info: Optional[list[Seat]] = None current_seat_id: Optional[str] = None current_seat_occupant: Optional[str] = None - current_seat_price: int = 0 + current_seat_price: Decimal = Decimal("0") current_seat_is_blocked: bool = False user: Optional[User] = None show_info_box: bool = True @@ -54,7 +55,7 @@ class SeatingPlanPage(Component): self.current_seat_is_blocked = seat.is_blocked self.current_seat_id = seat.seat_id ticket_info = self.session[TicketingService].get_ticket_info_by_category(seat.category) - price = 0 if not ticket_info else ticket_info.price + price = Decimal("0") if not ticket_info else ticket_info.price self.current_seat_price = price if seat.user: self.current_seat_occupant = seat.user.user_name diff --git a/src/ez_lan_manager/services/CateringService.py b/src/ez_lan_manager/services/CateringService.py index 80efa47..16a1675 100644 --- a/src/ez_lan_manager/services/CateringService.py +++ b/src/ez_lan_manager/services/CateringService.py @@ -1,4 +1,5 @@ import logging +from decimal import Decimal from enum import Enum from typing import Optional @@ -10,11 +11,13 @@ from src.ez_lan_manager.types.CateringMenuItem import CateringMenuItem, Catering logger = logging.getLogger(__name__.split(".")[-1]) + class CateringErrorType(Enum): INCLUDES_DISABLED_ITEM = 0 INSUFFICIENT_FUNDS = 1 GENERIC = 99 + class CateringError(Exception): def __init__(self, message: str, error_type: CateringErrorType = CateringErrorType.GENERIC) -> None: self.message = message @@ -30,7 +33,8 @@ class CateringService: # ORDERS - 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: if menu_item.is_disabled: raise CateringError("Order includes disabled items", CateringErrorType.INCLUDES_DISABLED_ITEM) @@ -39,14 +43,15 @@ class CateringService: if not user: raise CateringError("User does not exist") - 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()], Decimal(0)) if await self._accounting_service.get_balance(user_id) < total_price: raise CateringError("Insufficient funds", CateringErrorType.INSUFFICIENT_FUNDS) order = await self._db_service.add_new_order(menu_items, user_id, is_delivery) if order: 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_decimal(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_decimal(total_price)}") # await self.cancel_order(order) # ToDo: Check if commented out before commit. Un-comment to auto-cancel every placed order return order @@ -68,7 +73,8 @@ class CateringService: async def cancel_order(self, order: CateringOrder) -> bool: 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 False @@ -86,7 +92,8 @@ class CateringService: raise CateringError("Menu item not found") return item - async def add_menu_item(self, name: str, info: str, price: int, category: CateringMenuItemCategory, is_disabled: bool = False) -> CateringMenuItem: + async def add_menu_item(self, name: str, info: str, price: Decimal, category: CateringMenuItemCategory, + is_disabled: bool = False) -> CateringMenuItem: if new_item := await self._db_service.add_menu_item(name, info, price, category, is_disabled): return new_item raise CateringError(f"Could not add item '{name}' to the menu.") @@ -134,3 +141,4 @@ class CateringService: return self.cached_cart[user_id] except KeyError: return [] + diff --git a/src/ez_lan_manager/services/DatabaseService.py b/src/ez_lan_manager/services/DatabaseService.py index 1e5c755..a63213a 100644 --- a/src/ez_lan_manager/services/DatabaseService.py +++ b/src/ez_lan_manager/services/DatabaseService.py @@ -520,7 +520,7 @@ class DatabaseService: is_disabled=bool(raw_data[5]) ) - async def add_menu_item(self, name: str, info: str, price: int, category: CateringMenuItemCategory, + async def add_menu_item(self, name: str, info: str, price: Decimal, category: CateringMenuItemCategory, is_disabled: bool = False) -> Optional[CateringMenuItem]: async with self._connection_pool.acquire() as conn: async with conn.cursor(aiomysql.Cursor) as cursor: diff --git a/src/ez_lan_manager/types/CateringMenuItem.py b/src/ez_lan_manager/types/CateringMenuItem.py index 09119b6..7a4cca2 100644 --- a/src/ez_lan_manager/types/CateringMenuItem.py +++ b/src/ez_lan_manager/types/CateringMenuItem.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from decimal import Decimal from enum import StrEnum from typing import Self @@ -19,7 +20,7 @@ class CateringMenuItemCategory(StrEnum): class CateringMenuItem: item_id: int name: str - price: int + price: Decimal category: CateringMenuItemCategory additional_info: str = str() is_disabled: bool = False diff --git a/src/ez_lan_manager/types/CateringOrder.py b/src/ez_lan_manager/types/CateringOrder.py index 41221bc..7355439 100644 --- a/src/ez_lan_manager/types/CateringOrder.py +++ b/src/ez_lan_manager/types/CateringOrder.py @@ -1,5 +1,6 @@ from dataclasses import dataclass from datetime import datetime +from decimal import Decimal from enum import StrEnum from typing import Optional, Iterable, Self @@ -27,10 +28,10 @@ class CateringOrder: is_delivery: bool = True @property - def price(self) -> int: - total = 0 + def price(self) -> Decimal: + total = Decimal("0") for item, amount in self.items.items(): - total += (item.price * amount) + total += (item.price * Decimal(amount)) return total @staticmethod