You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
padelclub_backend/tournaments/services/currency_service.py

189 lines
5.1 KiB

"""
Currency service for handling multi-currency support in tournaments.
"""
class CurrencyService:
"""Service for handling currency formatting, symbols, and conversions."""
# Currency symbols mapping
CURRENCY_SYMBOLS = {
'EUR': '',
'USD': '$',
'GBP': '£',
'CHF': 'CHF',
'CAD': 'C$',
'AUD': 'A$',
'JPY': '¥',
'CNY': '¥',
'SEK': 'kr',
'NOK': 'kr',
'DKK': 'kr',
'PLN': '',
'CZK': '',
'HUF': 'Ft',
'RON': 'lei',
'BGN': 'лв',
'HRK': 'kn',
'RSD': 'RSD',
'BAM': 'KM',
'MKD': 'ден',
'ALL': 'L',
'MDL': 'L',
'UAH': '',
'RUB': '',
'BYN': 'Br',
'LTL': 'Lt',
'LVL': 'Ls',
'EEK': 'kr',
'ISK': 'kr',
'TRY': '',
'ILS': '',
'AED': 'د.إ',
'SAR': 'ر.س',
'QAR': 'ر.ق',
'KWD': 'د.ك',
'BHD': '.د.ب',
'OMR': 'ر.ع.',
'JOD': 'د.أ',
'LBP': 'ل.ل',
'EGP': 'ج.م',
'MAD': 'د.م.',
'TND': 'د.ت',
'DZD': 'د.ج',
'LYD': 'ل.د',
'ZAR': 'R',
'NGN': '',
'GHS': '',
'KES': 'KSh',
'UGX': 'USh',
'TZS': 'TSh',
'ETB': 'Br',
'XOF': 'CFA',
'XAF': 'FCFA',
'MZN': 'MT',
'BWP': 'P',
'ZMW': 'ZK',
'INR': '',
'PKR': '',
'BDT': '',
'LKR': '',
'MVR': 'Rf',
'NPR': '',
'BTN': 'Nu.',
'THB': '฿',
'MYR': 'RM',
'SGD': 'S$',
'IDR': 'Rp',
'PHP': '',
'VND': '',
'KHR': '',
'LAK': '',
'MMK': 'K',
'KRW': '',
'TWD': 'NT$',
'HKD': 'HK$',
'MOP': 'MOP$',
'BND': 'B$',
'FJD': 'FJ$',
'PGK': 'K',
'WST': 'WS$',
'TOP': 'T$',
'VUV': 'VT',
'SBD': 'SI$',
'MXN': '$',
'GTQ': 'Q',
'BZD': 'BZ$',
'SVC': '$',
'HNL': 'L',
'NIO': 'C$',
'CRC': '',
'PAB': 'B/.',
'COP': '$',
'VES': 'Bs.S',
'GYD': 'GY$',
'SRD': 'Sr$',
'UYU': '$U',
'PYG': '',
'BOB': 'Bs.',
'BRL': 'R$',
'PEN': 'S/',
'ECU': '$',
'CLP': '$',
'ARS': '$',
'FKP': '£',
'XPF': ''
}
# Zero-decimal currencies (amounts are in their base unit, not subdivided)
ZERO_DECIMAL_CURRENCIES = {
'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF',
'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'
}
@classmethod
def get_currency_symbol(cls, currency_code):
"""Get the currency symbol for a given currency code."""
if not currency_code:
return '' # Default to Euro symbol
return cls.CURRENCY_SYMBOLS.get(currency_code.upper(), currency_code.upper())
@classmethod
def format_amount(cls, amount, currency_code, show_symbol=True):
"""Format amount with proper currency symbol and decimal places."""
if amount is None:
return "0"
currency_code = currency_code.upper() if currency_code else 'EUR'
# Handle zero-decimal currencies
if currency_code in cls.ZERO_DECIMAL_CURRENCIES:
formatted_amount = f"{int(amount)}"
else:
# Format with 2 decimal places, removing unnecessary zeros
if amount == int(amount):
formatted_amount = f"{int(amount)}"
else:
formatted_amount = f"{amount:.2f}"
if show_symbol:
symbol = cls.get_currency_symbol(currency_code)
return f"{formatted_amount} {symbol}"
return formatted_amount
@classmethod
def convert_to_stripe_amount(cls, amount, currency_code):
"""Convert amount to Stripe's expected format (minor units)."""
if not amount:
return 0
currency_code = currency_code.upper() if currency_code else 'EUR'
# Zero-decimal currencies don't need conversion
if currency_code in cls.ZERO_DECIMAL_CURRENCIES:
return int(amount)
# Two-decimal currencies need to be multiplied by 100
return int(amount * 100)
@classmethod
def convert_from_stripe_amount(cls, stripe_amount, currency_code):
"""Convert Stripe amount (minor units) back to standard decimal format."""
if not stripe_amount:
return 0.0
currency_code = currency_code.upper() if currency_code else 'EUR'
# Zero-decimal currencies are already in the correct format
if currency_code in cls.ZERO_DECIMAL_CURRENCIES:
return float(stripe_amount)
# Two-decimal currencies need to be divided by 100
return float(stripe_amount) / 100
@classmethod
def validate_currency_code(cls, currency_code):
"""Validate if a currency code is supported."""
if not currency_code:
return False
return currency_code.upper() in cls.CURRENCY_SYMBOLS