""" 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