from __future__ import annotations import logging from asyncio import sleep from datetime import datetime from decimal import Decimal, ROUND_DOWN from typing import Optional from rio import Component, Column, Row, Text, Spacer, page, Rectangle, GuardEvent, Revealer, Image, NumberInput from rio.event import on_populate from elm.types import UserSession, Transaction from elm.services import AccountingService from elm.components import ElmButton logger = logging.getLogger(__name__.split(".")[-1]) class TransactionRow(Component): transaction_time: datetime transaction_title: str transaction_amount: Decimal is_debit: bool def build(self) -> Component: color = self.session.theme.danger_color if self.is_debit else self.session.theme.success_color return Rectangle( content=Row( Text( f"{self.transaction_time.strftime('%d.%m.%y')} /", justify="left", font_size=0.8, margin_left=0.5, fill=color ), Text( self.transaction_title, justify="left", font_size=0.8, margin_left=0.5, fill=color, overflow="ellipsize", grow_x=True ), Text( f"{'-' if self.is_debit else '+'}{str(self.transaction_amount.quantize(Decimal('.01'), rounding=ROUND_DOWN))} €", justify="right", font_size=0.8, margin_right=0.5, fill=color ), margin_bottom=0.5, margin_top=0.5 ), hover_fill=self.session.theme.background_color, transition_time=0.2 ) def my_balance_page_guard(event: GuardEvent) -> Optional[str]: try: _ = event.session[UserSession].user_name return None except KeyError: return "/" @page(name="My Balance", url_segment="balance", guard=my_balance_page_guard) class MyBalancePage(Component): current_balance: str = "-" last_20_transactions: list[Transaction] = [] bank_revealer_open: bool = False paypal_revealer_open: bool = False payment_qr_image: bytes = bytes() paypal_charge_amount: float = 0.00 paypal_charge_in_progress: bool = False @on_populate async def async_init(self) -> None: self.current_balance = self.session[AccountingService].make_euro_string_from_decimal( await self.session[AccountingService].get_balance(self.session[UserSession].user_name) ) self.last_20_transactions = (await self.session[AccountingService].get_transaction_history(self.session[UserSession].user_name))[:20] self.payment_qr_image = self.session[AccountingService].make_payment_qr_image( "Einfach Zocken Gaming Gesellschaft", "GENODE51BIK", "DE47517624340019856607", f"AUFLADUNG - {self.session[UserSession].user_name}") async def check_if_paypal_process_done(self) -> None: await sleep(2) if await self.session[AccountingService].has_user_open_orders(self.session[UserSession].user_name): self.session.create_task(self.check_if_paypal_process_done()) else: self.paypal_charge_in_progress = False self.paypal_charge_amount = 0.00 self.current_balance = self.session[AccountingService].make_euro_string_from_decimal( await self.session[AccountingService].get_balance(self.session[UserSession].user_name) ) self.last_20_transactions = (await self.session[AccountingService].get_transaction_history(self.session[UserSession].user_name))[:20] self.paypal_revealer_open = False async def pay_with_paypal(self) -> None: self.paypal_charge_in_progress = True logger.info("Starting PayPal transaction over %s for user %s", f"{self.paypal_charge_amount} €", self.session[UserSession].user_name) amount = Decimal(self.paypal_charge_amount) try: approval_url = await self.session[AccountingService].start_paypal_process(self.session[UserSession].user_name, amount) except Exception as e: logger.error(e) return self.session.open_url_in_browser(approval_url) self.session.create_task(self.check_if_paypal_process_done()) async def toggle_bank_revealer(self) -> None: self.bank_revealer_open = not self.bank_revealer_open async def toggle_paypal_revealer(self) -> None: self.paypal_revealer_open = not self.paypal_revealer_open def build(self) -> Component: col_row = Column if self.session.is_mobile() else Row transaction_rows = [] for transaction in sorted(self.last_20_transactions, key=lambda t: t.transaction_date, reverse=True): transaction_rows.append( TransactionRow( transaction_time=transaction.transaction_date, transaction_title=transaction.title, transaction_amount=transaction.value, is_debit=transaction.is_debit ) ) return col_row( Column( Rectangle( content=Column( Rectangle( content=Rectangle( content=Text("Guthaben", margin=0.5, selectable=False, overflow="wrap"), fill=self.session.theme.header_box_background_color, margin=0.4 ), stroke_width=0.1, stroke_color=self.session.theme.box_border_color, ), Column( Text( text=self.current_balance, justify="center", font_size=2, grow_x=True, grow_y=True, margin_top=2, margin_bottom=1 ), Spacer(), margin=1, spacing=1 ) ), fill=self.session.theme.box_color, stroke_width=0.1, stroke_color=self.session.theme.box_border_color ), Rectangle( content=Column( Rectangle( content=Rectangle( content=Text("Guthaben aufladen", margin=0.5, selectable=False, overflow="wrap"), fill=self.session.theme.header_box_background_color, margin=0.4 ), stroke_width=0.1, stroke_color=self.session.theme.box_border_color, ), Column( ElmButton(text="Banküberweißung", style="small" if self.session.is_mobile() else "normal", on_press=self.toggle_bank_revealer), Revealer(header=None, is_open=self.bank_revealer_open, content=Column( Text("QR Code", justify="center"), Image(self.payment_qr_image, min_width=14, min_height=14, margin_bottom=1), Text("Bankverbindung", justify="center"), Text("Empfänger: Einfach Zocken Gaming Gesellschaft", justify="left", overflow="wrap", font_size=0.7), Text("IBAN: DE47517624340019856607", justify="left", overflow="wrap", font_size=0.7), Text("BIC: GENODE51BIK", justify="left", overflow="wrap", font_size=0.7), Text(f"Verwendungszweck: AUFLADUNG - {self.session[UserSession].user_name}", justify="left", overflow="wrap", font_size=0.7), spacing=1 )), ElmButton(text="Paypal", style="small" if self.session.is_mobile() else "normal", on_press=self.toggle_paypal_revealer), Revealer(header=None, is_open=self.paypal_revealer_open, content=Column( NumberInput(label="Summe", decimals=2, value=self.bind().paypal_charge_amount, suffix_text="€"), ElmButton(text="Jetzt aufladen", style="small" if self.session.is_mobile() else "normal", on_press=self.pay_with_paypal, is_loading=self.paypal_charge_in_progress), spacing=1 )), margin=1, spacing=1 ) ), fill=self.session.theme.box_color, stroke_width=0.1, stroke_color=self.session.theme.box_border_color ), Spacer(), spacing=1 ), Column( Rectangle( content=Column( Rectangle( content=Rectangle( content=Text("Letzte Transaktionen", margin=0.5, selectable=False, overflow="wrap"), fill=self.session.theme.header_box_background_color, margin=0.4 ), stroke_width=0.1, stroke_color=self.session.theme.box_border_color, ), Column( Rectangle( content=Row( Text("Datum / Titel", justify="left", font_size=0.8, margin_left=0.5), Text("Betrag", justify="right", font_size=0.8, margin_right=0.5), margin_bottom=0.5, margin_top=0.5 ) ), *transaction_rows, spacing=0.5, margin=1 ) ), fill=self.session.theme.box_color, stroke_width=0.1, stroke_color=self.session.theme.box_border_color ), Spacer(), spacing=1, grow_x=True ), spacing=1, margin=1, margin_right=2 )