ezgg-lan-manager/src/ez_lan_manager/services/TicketingService.py
tcprod a419ee8885 Replace float with Decimal for price calculations
Fix Decimal precision issue

Fix Decimal precision issue

Fix Decimal precision issue

Fix old prices for tickets

Fix Decimal precision issue
2025-02-07 23:20:57 +01:00

94 lines
3.6 KiB
Python

import logging
from typing import Optional
from src.ez_lan_manager.services.AccountingService import AccountingService, InsufficientFundsError
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager.types.ConfigurationTypes import TicketInfo
from src.ez_lan_manager.types.Ticket import Ticket
logger = logging.getLogger(__name__.split(".")[-1])
class TicketNotAvailableError(Exception):
def __init__(self, category: str):
self.category = category
class UserAlreadyHasTicketError(Exception):
pass
class TicketingService:
def __init__(self, ticket_infos: tuple[TicketInfo, ...], db_service: DatabaseService,
accounting_service: AccountingService) -> None:
self._ticket_infos = ticket_infos
self._db_service = db_service
self._accounting_service = accounting_service
def get_ticket_info_by_category(self, category: str) -> Optional[TicketInfo]:
return next(filter(lambda t: t.category == category, self._ticket_infos), None)
def get_total_tickets(self) -> int:
return sum([t_i.total_tickets for t_i in self._ticket_infos])
async def get_available_tickets_for_category(self, category: str) -> int:
ticket_info = self.get_ticket_info_by_category(category)
if not ticket_info or ticket_info.total_tickets < 1:
return 0
result = ticket_info.total_tickets
all_tickets = await self._db_service.get_tickets()
for ticket in all_tickets:
if ticket.category == category:
result -= 1
return result
async def purchase_ticket(self, user_id: int, category: str) -> Ticket:
all_categories = [t_i.category for t_i in self._ticket_infos]
if category not in all_categories or (await self.get_available_tickets_for_category(category)) < 1:
raise TicketNotAvailableError(category)
user_balance = await self._accounting_service.get_balance(user_id)
ticket_info = self.get_ticket_info_by_category(category)
if not ticket_info:
raise TicketNotAvailableError(category)
if ticket_info.price > user_balance:
raise InsufficientFundsError
if await self.get_user_ticket(user_id):
raise UserAlreadyHasTicketError
if new_ticket := await self._db_service.generate_ticket_for_user(user_id, category):
await self._accounting_service.remove_balance(
user_id,
ticket_info.price,
f"TICKET {new_ticket.ticket_id}"
)
logger.debug(f"User {user_id} purchased ticket {new_ticket.ticket_id}")
return new_ticket
raise RuntimeError("An unknown error occurred while purchasing ticket")
async def refund_ticket(self, user_id: int) -> bool:
user_ticket = await self.get_user_ticket(user_id)
if not user_ticket:
return False
ticket_info = self.get_ticket_info_by_category(user_ticket.category)
if await self._db_service.delete_ticket(user_ticket.ticket_id):
await self._accounting_service.add_balance(user_id, ticket_info.price,
f"TICKET REFUND {user_ticket.ticket_id}")
logger.debug(f"User {user_id} refunded ticket {user_ticket.ticket_id}")
return True
return False
async def transfer_ticket(self, ticket_id: int, user_id: int) -> bool:
return await self._db_service.change_ticket_owner(ticket_id, user_id)
async def get_user_ticket(self, user_id: int) -> Optional[Ticket]:
return await self._db_service.get_ticket_for_user(user_id)