Fix Decimal precision issue

This commit is contained in:
tcprod 2025-02-04 18:05:54 +01:00
parent 0ca06c244c
commit 43ce42052e
12 changed files with 136 additions and 103 deletions

View File

@ -1,4 +1,5 @@
from typing import Callable from typing import Callable
from decimal import Decimal
import rio import rio
from rio import Component, Row, Text, IconButton, TextStyle from rio import Component, Row, Text, IconButton, TextStyle
@ -9,7 +10,7 @@ MAX_LEN = 24
class CateringCartItem(Component): class CateringCartItem(Component):
article_name: str article_name: str
article_price: int article_price: Decimal
article_id: int article_id: int
list_id: int list_id: int
remove_item_cb: Callable remove_item_cb: Callable

View File

@ -1,3 +1,4 @@
from decimal import Decimal
from typing import Callable from typing import Callable
import rio import rio
@ -7,9 +8,10 @@ from src.ez_lan_manager import AccountingService
MAX_LEN = 24 MAX_LEN = 24
class CateringSelectionItem(Component): class CateringSelectionItem(Component):
article_name: str article_name: str
article_price: int article_price: Decimal
article_id: int article_id: int
on_add_callback: Callable on_add_callback: Callable
is_sensitive: bool is_sensitive: bool
@ -33,15 +35,16 @@ class CateringSelectionItem(Component):
return top.strip(), bottom.strip() return top.strip(), bottom.strip()
def build(self) -> rio.Component: def build(self) -> rio.Component:
article_name_top, article_name_bottom = self.split_article_name(self.article_name) article_name_top, article_name_bottom = self.split_article_name(self.article_name)
return Card( return Card(
content=Column( content=Column(
Row( 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(article_name_top, align_x=0, overflow="wrap", min_width=19,
Text(AccountingService.make_euro_string_from_decimal(self.article_price), style=TextStyle(fill=self.session.theme.background_color, font_size=0.9)), 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( IconButton(
icon="material/add", icon="material/add",
min_size=2, min_size=2,
@ -53,7 +56,10 @@ class CateringSelectionItem(Component):
proportions=(19, 5, 2), proportions=(19, 5, 2),
margin_bottom=0 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( Row(
Text( Text(
self.additional_info, self.additional_info,

View File

@ -1,3 +1,4 @@
from decimal import Decimal
from typing import Optional, Callable from typing import Optional, Callable
from rio import Component, Column, Text, TextStyle, Button, Spacer from rio import Component, Column, Text, TextStyle, Button, Spacer
@ -9,23 +10,30 @@ class SeatingPlanInfoBox(Component):
is_booking_blocked: bool is_booking_blocked: bool
seat_id: Optional[str] = None seat_id: Optional[str] = None
seat_occupant: Optional[str] = None seat_occupant: Optional[str] = None
seat_price: int = 0 seat_price: Decimal = Decimal("0")
is_blocked: bool = False is_blocked: bool = False
def build(self) -> Component: def build(self) -> Component:
if not self.show: if not self.show:
return Spacer() return Spacer()
if self.is_blocked: 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: 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( 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"Dieser Sitzplatz ({self.seat_id}) ist gebucht von:", margin=1,
Text(f"{self.seat_occupant}", margin_bottom=1, style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.4), overflow="wrap", justify="center"), 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 min_height=10
) if self.seat_id and self.seat_occupant else Column( ) 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( Button(
Text( Text(
f"Buchen", f"Buchen",

View File

@ -1,4 +1,5 @@
from asyncio import sleep, create_task from asyncio import sleep, create_task
from decimal import Decimal
import rio import rio
from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, Table from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, Table
@ -158,7 +159,7 @@ class ShoppingCartAndOrders(Component):
), ),
Row( Row(
Text( 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( style=TextStyle(
fill=self.session.theme.background_color, fill=self.session.theme.background_color,
font_size=0.8 font_size=0.8

View File

@ -1,5 +1,6 @@
from functools import partial from functools import partial
from typing import Callable, Optional from typing import Callable, Optional
from decimal import Decimal
import rio import rio
from rio import Component, Card, Column, Text, Row, Button, TextStyle, ProgressBar, event, Spacer 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): class TicketBuyCard(Component):
description: str description: str
additional_info: str additional_info: str
price: int price: Decimal
category: str category: str
pressed_cb: Callable pressed_cb: Callable
is_enabled: bool is_enabled: bool

View File

@ -1,5 +1,6 @@
from random import choice from random import choice
from typing import Optional from typing import Optional
from decimal import Decimal
from rio import Component, TextStyle, Color, Button, Text, Rectangle, Column, Row, Spacer, Link, event, EventHandler 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: def build(self) -> Component:
return Link( return Link(
content=Button( content=Button(
content=Text(self.label, style=self.STYLE, justify="center"), content=Text(self.label, style=self.STYLE, justify="center"),
shape="rectangle", shape="rectangle",
style="major", style="major",
color="success" if self.enabled else "danger", color="success" if self.enabled else "danger",
grow_x=True, grow_x=True,
margin_left=0.6, margin_left=0.6,
margin_right=0.6, margin_right=0.6,
margin_top=0.6 margin_top=0.6
), ),
target_url=self.target_url, target_url=self.target_url,
align_y=0.5, align_y=0.5,
grow_y=False grow_y=False
) )
class UserInfoBox(Component): class UserInfoBox(Component):
status_change_cb: EventHandler = None status_change_cb: EventHandler = None
TEXT_STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9) TEXT_STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9)
user: Optional[User] = None user: Optional[User] = None
user_balance: Optional[int] = 0 user_balance: Optional[Decimal] = Decimal("0")
user_ticket: Optional[Ticket] = None user_ticket: Optional[Ticket] = None
user_seat: Optional[Seat] = None user_seat: Optional[Seat] = None
@ -80,8 +82,10 @@ class UserInfoBox(Component):
return Spacer() return Spacer()
return Rectangle( return Rectangle(
content=Column( content=Column(
Text(f"{self.get_greeting()},", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9), justify="center"), Text(f"{self.get_greeting()},", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9),
Text(f"{self.user.user_name}", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=1.2), justify="center"), justify="center"),
Text(f"{self.user.user_name}", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=1.2),
justify="center"),
Row( Row(
StatusButton(label="TICKET", target_url="./buy_ticket", StatusButton(label="TICKET", target_url="./buy_ticket",
enabled=self.user_ticket is not None), enabled=self.user_ticket is not None),
@ -91,7 +95,9 @@ class UserInfoBox(Component):
grow_y=False grow_y=False
), ),
UserInfoBoxButton("Profil bearbeiten", "./edit-profile"), 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( Button(
content=Text("Ausloggen", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.6)), content=Text("Ausloggen", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.6)),
shape="rectangle", shape="rectangle",

View File

@ -49,7 +49,6 @@ async def run() -> None:
DEMO_USERS[2]["password_clear_text"]) DEMO_USERS[2]["password_clear_text"])
await accounting_service.add_balance(jason.user_id, Decimal("1000.00"), "DEMO EINZAHLUNG") await accounting_service.add_balance(jason.user_id, Decimal("1000.00"), "DEMO EINZAHLUNG")
await ticket_service.purchase_ticket(jason.user_id, "NORMAL") await ticket_service.purchase_ticket(jason.user_id, "NORMAL")
await seating_service.seat_user(jason.user_id, "D10")
# LISA # LISA
lisa = await user_service.create_user(DEMO_USERS[3]["user_name"], DEMO_USERS[3]["user_mail"], 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": if not input("Generate catering menu? (Y/n): ").lower() == "n":
# MAIN_COURSE # 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) 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) 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) 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) CateringMenuItemCategory.MAIN_COURSE)
# SNACK # SNACK
await catering_service.add_menu_item("Käse Schinken Wrap", "", 500, 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", "", 700, 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", "", 600, CateringMenuItemCategory.SNACK) await catering_service.add_menu_item("Tomate Mozzarella Wrap", "", Decimal("6.00"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Portion Pommes", "", 400, CateringMenuItemCategory.SNACK) await catering_service.add_menu_item("Portion Pommes", "", Decimal("4.00"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Rinds-Currywurst", "", 450, 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", "", 650, CateringMenuItemCategory.SNACK) await catering_service.add_menu_item("Rinds-Currywurst mit Pommes", "", Decimal("6.50"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Nudelsalat", "", 450, CateringMenuItemCategory.SNACK) await catering_service.add_menu_item("Nudelsalat", "", Decimal("4.50"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Nudelsalat mit Bockwurst", "", 600, CateringMenuItemCategory.SNACK) await catering_service.add_menu_item("Nudelsalat mit Bockwurst", "", Decimal("6.00"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Kartoffelsalat", "", 450, CateringMenuItemCategory.SNACK) await catering_service.add_menu_item("Kartoffelsalat", "", Decimal("4.50"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Kartoffelsalat mit Bockwurst", "", 600, CateringMenuItemCategory.SNACK) await catering_service.add_menu_item("Kartoffelsalat mit Bockwurst", "", Decimal("6.00"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Schinken", "", 180, CateringMenuItemCategory.SNACK) await catering_service.add_menu_item("Sandwichtoast - Schinken", "", Decimal("1.80"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Käse", "", 180, 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", "", 210, 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", "", 180, 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", "", 210, 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", "", 130, CateringMenuItemCategory.SNACK) await catering_service.add_menu_item("Chips - Western Style", "", Decimal("1.30"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Nachos - Salted", "", 130, CateringMenuItemCategory.SNACK) await catering_service.add_menu_item("Nachos - Salted", "", Decimal("1.30"), CateringMenuItemCategory.SNACK)
# DESSERT # DESSERT
await catering_service.add_menu_item("Panna Cotta mit Erdbeersauce", "", 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", "", 700, 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", "", 700, CateringMenuItemCategory.DESSERT) await catering_service.add_menu_item("Mousse au Chocolat", "", Decimal("7.00"), CateringMenuItemCategory.DESSERT)
# BREAKFAST # BREAKFAST
await catering_service.add_menu_item("Fruit Loops", "", 150, CateringMenuItemCategory.BREAKFAST) await catering_service.add_menu_item("Fruit Loops", "", Decimal("1.50"), CateringMenuItemCategory.BREAKFAST)
await catering_service.add_menu_item("Smacks", "", 150, CateringMenuItemCategory.BREAKFAST) await catering_service.add_menu_item("Smacks", "", Decimal("1.50"), CateringMenuItemCategory.BREAKFAST)
await catering_service.add_menu_item("Knuspermüsli", "Schoko", 200, CateringMenuItemCategory.BREAKFAST) await catering_service.add_menu_item("Knuspermüsli", "Schoko", Decimal("2.00"), CateringMenuItemCategory.BREAKFAST)
await catering_service.add_menu_item("Cini Minis", "", 150, 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", 120, await catering_service.add_menu_item("Brötchen - Schinken", "mit Margarine", Decimal("1.20"),
CateringMenuItemCategory.BREAKFAST) 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) 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) 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) 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) 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) CateringMenuItemCategory.BREAKFAST)
# BEVERAGE_NON_ALCOHOLIC # 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) 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) 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) 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) 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) 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) 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) 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) CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC)
await catering_service.add_menu_item("Red Bull", "", 200, CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) await catering_service.add_menu_item("Red Bull", "", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC)
await catering_service.add_menu_item("Energy", "Hausmarke", 150, await catering_service.add_menu_item("Energy", "Hausmarke", Decimal("1.50"),
CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC) CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC)
# BEVERAGE_ALCOHOLIC # BEVERAGE_ALCOHOLIC
await catering_service.add_menu_item("Pils", "0,33L Flasche", 190, CateringMenuItemCategory.BEVERAGE_ALCOHOLIC) 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", 190, await catering_service.add_menu_item("Radler", "0,33L Flasche", Decimal("1.90"),
CateringMenuItemCategory.BEVERAGE_ALCOHOLIC) 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) 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) 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) 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) CateringMenuItemCategory.BEVERAGE_ALCOHOLIC)
# BEVERAGE_COCKTAIL # BEVERAGE_COCKTAIL
await catering_service.add_menu_item("Vodka Energy", "", 400, CateringMenuItemCategory.BEVERAGE_COCKTAIL) await catering_service.add_menu_item("Vodka Energy", "", Decimal("4.00"), CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Vodka O-Saft", "", 400, 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", 400, await catering_service.add_menu_item("Whiskey Cola", "mit Bourbon", Decimal("4.00"),
CateringMenuItemCategory.BEVERAGE_COCKTAIL) CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Jägermeister Energy", "", 400, 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", "", 550, 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", "", 550, 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", "", 550, CateringMenuItemCategory.BEVERAGE_COCKTAIL) await catering_service.add_menu_item("Caipirinha", "", Decimal("5.50"), CateringMenuItemCategory.BEVERAGE_COCKTAIL)
# BEVERAGE_SHOT # BEVERAGE_SHOT
await catering_service.add_menu_item("Jägermeister", "", 200, CateringMenuItemCategory.BEVERAGE_SHOT) await catering_service.add_menu_item("Jägermeister", "", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_SHOT)
await catering_service.add_menu_item("Tequila", "", 200, 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", 199, await catering_service.add_menu_item("PfEZzi", "Getunter Pfefferminz-Schnaps", Decimal("1.99"),
CateringMenuItemCategory.BEVERAGE_SHOT) CateringMenuItemCategory.BEVERAGE_SHOT)
# NON_FOOD # NON_FOOD
await catering_service.add_menu_item("Zigaretten", "Elixyr", 800, CateringMenuItemCategory.NON_FOOD) 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", 120, await catering_service.add_menu_item("Mentholfilter", "passend für Elixyr", Decimal("1.20"),
CateringMenuItemCategory.NON_FOOD) CateringMenuItemCategory.NON_FOOD)
if not input("Generate default new post? (Y/n): ").lower() == "n": if not input("Generate default new post? (Y/n): ").lower() == "n":

View File

@ -1,5 +1,6 @@
import logging import logging
from asyncio import sleep from asyncio import sleep
from decimal import Decimal
from typing import Optional from typing import Optional
from rio import Text, Column, TextStyle, Component, event, PressEvent, ProgressCircle from rio import Text, Column, TextStyle, Component, event, PressEvent, ProgressCircle
@ -21,7 +22,7 @@ class SeatingPlanPage(Component):
seating_info: Optional[list[Seat]] = None seating_info: Optional[list[Seat]] = None
current_seat_id: Optional[str] = None current_seat_id: Optional[str] = None
current_seat_occupant: 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 current_seat_is_blocked: bool = False
user: Optional[User] = None user: Optional[User] = None
show_info_box: bool = True show_info_box: bool = True
@ -54,7 +55,7 @@ class SeatingPlanPage(Component):
self.current_seat_is_blocked = seat.is_blocked self.current_seat_is_blocked = seat.is_blocked
self.current_seat_id = seat.seat_id self.current_seat_id = seat.seat_id
ticket_info = self.session[TicketingService].get_ticket_info_by_category(seat.category) 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 self.current_seat_price = price
if seat.user: if seat.user:
self.current_seat_occupant = seat.user.user_name self.current_seat_occupant = seat.user.user_name

View File

@ -1,4 +1,5 @@
import logging import logging
from decimal import Decimal
from enum import Enum from enum import Enum
from typing import Optional from typing import Optional
@ -10,11 +11,13 @@ 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): class CateringErrorType(Enum):
INCLUDES_DISABLED_ITEM = 0 INCLUDES_DISABLED_ITEM = 0
INSUFFICIENT_FUNDS = 1 INSUFFICIENT_FUNDS = 1
GENERIC = 99 GENERIC = 99
class CateringError(Exception): class CateringError(Exception):
def __init__(self, message: str, error_type: CateringErrorType = CateringErrorType.GENERIC) -> None: def __init__(self, message: str, error_type: CateringErrorType = CateringErrorType.GENERIC) -> None:
self.message = message self.message = message
@ -30,7 +33,8 @@ class CateringService:
# ORDERS # 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: for menu_item in menu_items:
if menu_item.is_disabled: if menu_item.is_disabled:
raise CateringError("Order includes disabled items", CateringErrorType.INCLUDES_DISABLED_ITEM) raise CateringError("Order includes disabled items", CateringErrorType.INCLUDES_DISABLED_ITEM)
@ -39,14 +43,15 @@ class CateringService:
if not user: if not user:
raise CateringError("User does not exist") 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: if await self._accounting_service.get_balance(user_id) < total_price:
raise CateringError("Insufficient funds", CateringErrorType.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_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 # await self.cancel_order(order) # ToDo: Check if commented out before commit. Un-comment to auto-cancel every placed order
return order return order
@ -68,7 +73,8 @@ class CateringService:
async def cancel_order(self, order: CateringOrder) -> bool: async def cancel_order(self, order: CateringOrder) -> bool:
change_result = await 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: 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
@ -86,7 +92,8 @@ class CateringService:
raise CateringError("Menu item not found") raise CateringError("Menu item not found")
return item 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): if new_item := await self._db_service.add_menu_item(name, info, price, category, is_disabled):
return new_item return new_item
raise CateringError(f"Could not add item '{name}' to the menu.") raise CateringError(f"Could not add item '{name}' to the menu.")
@ -134,3 +141,4 @@ class CateringService:
return self.cached_cart[user_id] return self.cached_cart[user_id]
except KeyError: except KeyError:
return [] return []

View File

@ -520,7 +520,7 @@ class DatabaseService:
is_disabled=bool(raw_data[5]) 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]: is_disabled: bool = False) -> Optional[CateringMenuItem]:
async with self._connection_pool.acquire() as conn: async with self._connection_pool.acquire() as conn:
async with conn.cursor(aiomysql.Cursor) as cursor: async with conn.cursor(aiomysql.Cursor) as cursor:

View File

@ -1,4 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from decimal import Decimal
from enum import StrEnum from enum import StrEnum
from typing import Self from typing import Self
@ -19,7 +20,7 @@ class CateringMenuItemCategory(StrEnum):
class CateringMenuItem: class CateringMenuItem:
item_id: int item_id: int
name: str name: str
price: int price: Decimal
category: CateringMenuItemCategory category: CateringMenuItemCategory
additional_info: str = str() additional_info: str = str()
is_disabled: bool = False is_disabled: bool = False

View File

@ -1,5 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from decimal import Decimal
from enum import StrEnum from enum import StrEnum
from typing import Optional, Iterable, Self from typing import Optional, Iterable, Self
@ -27,10 +28,10 @@ class CateringOrder:
is_delivery: bool = True is_delivery: bool = True
@property @property
def price(self) -> int: def price(self) -> Decimal:
total = 0 total = Decimal("0")
for item, amount in self.items.items(): for item, amount in self.items.items():
total += (item.price * amount) total += (item.price * Decimal(amount))
return total return total
@staticmethod @staticmethod