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.
189 lines
5.1 KiB
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': 'zł',
|
|
'CZK': 'Kč',
|
|
'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
|
|
|