Fix Decimal precision issue Fix Decimal precision issue Fix Decimal precision issue Fix old prices for tickets Fix Decimal precision issue
94 lines
3.6 KiB
Python
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)
|