# Currency Mapping Module # Contains canonical_currency_map dictionary and convert_currency_symbols_to_iso() function # Canonical currency map for symbol resolution (matches TypeScript version) canonical_currency_map = { # Standard currency symbols "$": "USD", "£": "GBP", "€": "EUR", "¥": "JPY", "₹": "INR", "₽": "RUB", "₩": "KRW", "₪": "ILS", "₺": "TRY", "₴": "UAH", "₼": "AZN", "₸": "KZT", "₾": "GEL", "֏": "AMD", "₲": "PYG", "₡": "CRC", "₣": "XPF", "₦": "NGN", "₵": "GHS", "₨": "PKR", "₱": "PHP", "₫": "VND", "₭": "LAK", "៛": "KHR", "৳": "BDT", "؋": "AFN", "﷼": "IRR", # Custom symbols "‡": "CUSTOM_DAGGER", "†": "CUSTOM_DAGGER_SINGLE", # Letter-based currencies "R": "ZAR", # South African Rand "C$": "CAD", # Canadian Dollar "A$": "AUD", # Australian Dollar "R$": "BRL", # Brazilian Real "S/": "PEN", # Peruvian Sol "Bs": "BOB", # Bolivian Boliviano "Q": "GTQ", # Guatemalan Quetzal "L": "HNL", # Honduran Lempira "ƒ": "AWG", # Aruban Florin "Vt": "VUV", # Vanuatu Vatu "T$": "TOP", # Tongan Pa'anga "T": "WST", # Samoan Tala "лв": "BGN", # Bulgarian Lev "Kč": "CZK", # Czech Koruna "kr": "DKK", # Danish Krone "Ft": "HUF", # Hungarian Forint "zł": "PLN", # Polish Zloty "lei": "RON", # Romanian Leu "CHF": "CHF", # Swiss Franc "KM": "BAM", # Bosnian Marka "ден": "MKD", # North Macedonian Denar "дин": "RSD", # Serbian Dinar "so'm": "UZS", # Uzbekistani Som "NT$": "TWD", # Taiwan Dollar "HK$": "HKD", # Hong Kong Dollar "S$": "SGD", # Singapore Dollar "RM": "MYR", # Malaysian Ringgit "฿": "THB", # Thai Baht "Rp": "IDR", # Indonesian Rupiah "K": "MMK", # Myanmar Kyat "Rs": "LKR", # Sri Lankan Rupee "Nu": "BTN", # Bhutanese Ngultrum "MVR": "MVR", # Maldivian Rufiyaa # Middle Eastern currencies "ع.د": "IQD", # Iraqi Dinar "ل.ل": "LBP", # Lebanese Pound "د.ا": "JOD", # Jordanian Dinar "ر.س": "SAR", # Saudi Riyal "د.إ": "AED", # UAE Dirham "ر.ق": "QAR", # Qatari Riyal "د.ك": "KWD", # Kuwaiti Dinar "د.ب": "BHD", # Bahraini Dinar "ر.ع.": "OMR", # Omani Rial "ر.ي": "YER", # Yemeni Rial "ج.م": "EGP", # Egyptian Pound "ل.د": "LYD", # Libyan Dinar "د.ت": "TND", # Tunisian Dinar "د.ج": "DZD", # Algerian Dinar "د.م.": "MAD", # Moroccan Dirham "ج.س.": "SDG", # Sudanese Pound "SSP": "SSP", # South Sudanese Pound # African currencies "KSh": "KES", # Kenyan Shilling "USh": "UGX", # Ugandan Shilling "TSh": "TZS", # Tanzanian Shilling "FRw": "RWF", # Rwandan Franc "FBu": "BIF", # Burundian Franc "MT": "MZN", # Mozambican Metical "P": "BWP", # Botswanan Pula "N$": "NAD", # Namibian Dollar "Ar": "MGA", # Malagasy Ariary "CF": "KMF", # Comorian Franc "MK": "MWK", # Malawian Kwacha "ZK": "ZMW", # Zambian Kwacha "Kz": "AOA", # Angolan Kwanza "FC": "CDF", # Congolese Franc "FCFA": "XAF", # Central African Franc "Db": "STD", # Sao Tomean Dobra "D": "GMD", # Gambian Dalasi "FG": "GNF", # Guinean Franc "CFA": "XOF", # West African Franc "Le": "SLL", # Sierra Leonean Leone "L$": "LRD", # Liberian Dollar "Br": "ETB", # Ethiopian Birr "Nfk": "ERN", # Eritrean Nakfa "Fdj": "DJF", # Djiboutian Franc "S": "SOS", # Somali Shilling # Caribbean & Central American currencies "J$": "JMD", # Jamaican Dollar "BZ$": "BZD", # Belize Dollar "TT$": "TTD", # Trinidad and Tobago Dollar "Bds$": "BBD", # Barbadian Dollar "EC$": "XCD", # East Caribbean Dollar "GY$": "GYD", # Guyanese Dollar "SRD": "SRD", # Surinamese Dollar "RD$": "DOP", # Dominican Peso # Pacific Island currencies "FJ$": "FJD", # Fijian Dollar "PGK": "PGK", # Papua New Guinean Kina "SI$": "SBD", # Solomon Islands Dollar "WS$": "WST", # Samoan Tala "VT": "VUV", # Vanuatu Vatu # European currencies "ISK": "ISK", # Icelandic Krona "Lt": "LTL", # Lithuanian Litas "Ls": "LVL", # Latvian Lats "EEK": "EEK", # Estonian Kroon "NOK": "NOK", # Norwegian Krone "SEK": "SEK", # Swedish Krona "ALL": "ALL", # Albanian Lek # South American currencies "Gs": "PYG", # Paraguayan Guarani "Bs.S": "VES", # Venezuelan Bolívar Soberano "Bs": "VEF", # Venezuelan Bolívar "$U": "UYU", # Uruguayan Peso "$a": "ARS", # Argentine Peso "$b": "BOB", # Bolivian Boliviano "CLP": "CLP", # Chilean Peso "COP": "COP", # Colombian Peso "MXN": "MXN", # Mexican Peso # Small Island Nations "BMD": "BMD", # Bermudian Dollar "BSD": "BSD", # Bahamian Dollar "KYD": "KYD", # Cayman Islands Dollar "TVD": "TVD", # Tuvaluan Dollar "ZWL": "ZWL", # Zimbabwean Dollar # Additional African currencies "LSL": "LSL", # Lesotho Loti "SZL": "SZL", # Swazi Lilangeni "CVE": "CVE", # Cape Verdean Escudo "MRO": "MRO", # Mauritanian Ouguiya "MRU": "MRU", # Mauritanian Ouguiya "SCR": "SCR", # Seychellois Rupee "SHP": "SHP", # Saint Helena Pound # Additional Asian currencies "BND": "BND", # Brunei Dollar "MOP": "MOP", # Macanese Pataca "NPR": "NPR", # Nepalese Rupee # Additional Pacific currencies "NZD": "NZD", # New Zealand Dollar # Alternative $ symbols for countries (context-dependent) "$BMD": "BMD", # Bermudian Dollar "$BSD": "BSD", # Bahamian Dollar "$BZD": "BZD", # Belize Dollar "$KYD": "KYD", # Cayman Islands Dollar "$FJD": "FJD", # Fijian Dollar "$GYD": "GYD", # Guyanese Dollar "$JMD": "JMD", # Jamaican Dollar "$LRD": "LRD", # Liberian Dollar "$NAD": "NAD", # Namibian Dollar "$SBD": "SBD", # Solomon Islands Dollar "$SRD": "SRD", # Surinamese Dollar "$TTD": "TTD", # Trinidad and Tobago Dollar "$TVD": "TVD", # Tuvaluan Dollar "$VUV": "VUV", # Vanuatu Vatu "$WST": "WST", # Samoan Tala "$XCD": "XCD", # East Caribbean Dollar "$ZWL": "ZWL", # Zimbabwean Dollar "$BWP": "BWP", # Botswanan Pula "$LSL": "LSL", # Lesotho Loti "$SZL": "SZL", # Swazi Lilangeni "$ZAR": "ZAR", # South African Rand "$HKD": "HKD", # Hong Kong Dollar "$SGD": "SGD", # Singapore Dollar "$TWD": "TWD", # Taiwan Dollar "$CHF": "CHF", # Swiss Franc "$DKK": "DKK", # Danish Krone "$NOK": "NOK", # Norwegian Krone "$SEK": "SEK", # Swedish Krona "$ARS": "ARS", # Argentine Peso "$BOB": "BOB", # Bolivian Boliviano "$CLP": "CLP", # Chilean Peso "$COP": "COP", # Colombian Peso "$MXN": "MXN", # Mexican Peso "$PEN": "PEN", # Peruvian Sol "$UYU": "UYU", # Uruguayan Peso "$VES": "VES", # Venezuelan Bolívar Soberano # Cryptocurrencies (for modern business plans) "₿": "BTC", # Bitcoin "Ξ": "ETH", # Ethereum "Ł": "LTC", # Litecoin # Special Drawing Rights "XDR": "XDR", # Special Drawing Rights # Precious Metals "XAU": "XAU", # Gold (troy ounce) "XAG": "XAG", # Silver (troy ounce) "XPT": "XPT", # Platinum (troy ounce) "XPD": "XPD", # Palladium (troy ounce) # Other "XTS": "XTS", # Testing Currency Code "XXX": "XXX", # No Currency # Missing currencies from canonical_currency_map.py "ADP": "ADP", # Andorran Peseta "AFA": "AFA", # Afghan Afghani (1927–2002) "ALK": "ALK", # Albanian Lek (1946–1965) "ANG": "ANG", # Netherlands Antillean Guilder "AOK": "AOK", # Angolan Kwanza (1977–1991) "AON": "AON", # Angolan New Kwanza (1990–2000) "AOR": "AOR", # Angolan Readjusted Kwanza (1995–1999) "ARA": "ARA", # Argentine Austral "ARL": "ARL", # Argentine Peso Ley (1970–1983) "ARM": "ARM", # Argentine Peso (1881–1970) "ARP": "ARP", # Argentine Peso (1983–1985) "ATS": "ATS", # Austrian Schilling "AZM": "AZM", # Azerbaijani Manat (1993–2006) "BAD": "BAD", # Bosnia-Herzegovina Dinar (1992–1994) "BAN": "BAN", # Bosnia-Herzegovina New Dinar (1994–1997) "BEC": "BEC", # Belgian Franc (convertible) "BEF": "BEF", # Belgian Franc "BEL": "BEL", # Belgian Franc (financial) "BGL": "BGL", # Bulgarian Hard Lev "BGM": "BGM", # Bulgarian Socialist Lev "BGO": "BGO", # Bulgarian Lev (1879–1952) "BOL": "BOL", # Bolivian Boliviano (1863–1963) "BOP": "BOP", # Bolivian Peso "BOV": "BOV", # Bolivian Mvdol "BRB": "BRB", # Brazilian New Cruzeiro (1967–1986) "BRC": "BRC", # Brazilian Cruzado (1986–1989) "BRE": "BRE", # Brazilian Cruzeiro (1990–1993) "BRN": "BRN", # Brazilian New Cruzado (1989–1990) "BRR": "BRR", # Brazilian Cruzeiro (1993–1994) "BRZ": "BRZ", # Brazilian Cruzeiro (1942–1967) "BUK": "BUK", # Burmese Kyat "BYB": "BYB", # Belarusian Ruble (1994–1999) "BYN": "BYN", # Belarusian Ruble "BYR": "BYR", # Belarusian Ruble (2000–2016) "CA$": "CAD", # Canadian Dollar "CHE": "CHE", # WIR Euro "CHW": "CHW", # WIR Franc "CLE": "CLE", # Chilean Escudo "CLF": "CLF", # Chilean Unit of Account (UF) "CNH": "CNH", # Chinese Yuan (offshore) "CNX": "CNX", # Chinese People's Bank Dollar "CN¥": "CNY", # Chinese Yuan "COU": "COU", # Colombian Real Value Unit "CSD": "CSD", # Serbian Dinar (2002–2006) "CSK": "CSK", # Czechoslovak Hard Koruna "CUC": "CUC", # Cuban Convertible Peso "CUP": "CUP", # Cuban Peso "CYP": "CYP", # Cypriot Pound "DDM": "DDM", # East German Mark "DEM": "DEM", # German Mark "ECS": "ECS", # Ecuadorian Sucre "ECV": "ECV", # Ecuadorian Unit of Constant Value "ESA": "ESA", # Spanish Peseta (A account) "ESB": "ESB", # Spanish Peseta (convertible account) "ESP": "ESP", # Spanish Peseta "FIM": "FIM", # Finnish Markka "FKP": "FKP", # Falkland Islands Pound "FRF": "FRF", # French Franc "GEK": "GEK", # Georgian Kupon Larit "GHC": "GHC", # Ghanaian Cedi (1979–2007) "GIP": "GIP", # Gibraltar Pound "GNS": "GNS", # Guinean Syli "GQE": "GQE", # Equatorial Guinean Ekwele "GRD": "GRD", # Greek Drachma "GWE": "GWE", # Portuguese Guinea Escudo "GWP": "GWP", # Guinea-Bissau Peso "HRD": "HRD", # Croatian Dinar "HRK": "HRK", # Croatian Kuna "HTG": "HTG", # Haitian Gourde "IEP": "IEP", # Irish Pound "ILP": "ILP", # Israeli Pound "ILR": "ILR", # Israeli Shekel (1980–1985) "ISJ": "ISJ", # Icelandic Króna (1918–1981) "ITL": "ITL", # Italian Lira "KGS": "KGS", # Kyrgystani Som "KPW": "KPW", # North Korean Won "KRH": "KRH", # South Korean Hwan (1953–1962) "KRO": "KRO", # South Korean Won (1945–1953) "LTT": "LTT", # Lithuanian Talonas "LUC": "LUC", # Luxembourgian Convertible Franc "LUF": "LUF", # Luxembourgian Franc "LUL": "LUL", # Luxembourg Financial Franc "LVR": "LVR", # Latvian Ruble "MAF": "MAF", # Moroccan Franc "MCF": "MCF", # Monegasque Franc "MDC": "MDC", # Moldovan Cupon "MDL": "MDL", # Moldovan Leu "MGF": "MGF", # Malagasy Franc "MKN": "MKN", # Macedonian Denar (1992–1993) "MLF": "MLF", # Malian Franc "MNT": "MNT", # Mongolian Tugrik "MTL": "MTL", # Maltese Lira "MTP": "MTP", # Maltese Pound "MUR": "MUR", # Mauritian Rupee "MVP": "MVP", # Maldivian Rupee (1947–1981) "MX$": "MXN", # Mexican Peso "MXP": "MXP", # Mexican Silver Peso (1861–1992) "MXV": "MXV", # Mexican Investment Unit "MZE": "MZE", # Mozambican Escudo "MZM": "MZM", # Mozambican Metical (1980–2006) "NIC": "NIC", # Nicaraguan Córdoba (1988–1991) "NIO": "NIO", # Nicaraguan Córdoba "NLG": "NLG", # Dutch Guilder "NZ$": "NZD", # New Zealand Dollar "PAB": "PAB", # Panamanian Balboa "PEI": "PEI", # Peruvian Inti "PES": "PES", # Peruvian Sol (1863–1965) "PLZ": "PLZ", # Polish Zloty (1950–1995) "PTE": "PTE", # Portuguese Escudo "RHD": "RHD", # Rhodesian Dollar "ROL": "ROL", # Romanian Leu (1952–2006) "RUR": "RUR", # Russian Ruble (1991–1998) "SDD": "SDD", # Sudanese Dinar (1992–2007) "SDP": "SDP", # Sudanese Pound (1957–1998) "SIT": "SIT", # Slovenian Tolar "SKK": "SKK", # Slovak Koruna "SLE": "SLE", # Sierra Leonean Leone "SRG": "SRG", # Surinamese Guilder "STN": "STN", # São Tomé & Príncipe Dobra "SUR": "SUR", # Soviet Rouble "SVC": "SVC", # Salvadoran Colón "SYP": "SYP", # Syrian Pound "TJR": "TJR", # Tajikistani Ruble "TJS": "TJS", # Tajikistani Somoni "TMM": "TMM", # Turkmenistani Manat (1993–2009) "TMT": "TMT", # Turkmenistani Manat "TPE": "TPE", # Timorese Escudo "TRL": "TRL", # Turkish Lira (1922–2005) "UAK": "UAK", # Ukrainian Karbovanets "UGS": "UGS", # Ugandan Shilling (1966–1987) "USN": "USN", # US Dollar (Next day) "USS": "USS", # US Dollar (Same day) "UYI": "UYI", # Uruguayan Peso (Indexed Units) "UYP": "UYP", # Uruguayan Peso (1975–1993) "UYW": "UYW", # Uruguayan Nominal Wage Index Unit "VEB": "VEB", # Venezuelan Bolívar (1871–2008) "VED": "VED", # Bolívar Soberano "VNN": "VNN", # Vietnamese Dong (1978–1985) "XBA": "XBA", # European Composite Unit "XBB": "XBB", # European Monetary Unit "XBC": "XBC", # European Unit of Account (XBC) "XBD": "XBD", # European Unit of Account (XBD) "Cg.": "XCG", # Caribbean guilder "XEU": "XEU", # European Currency Unit "XFO": "XFO", # French Gold Franc "XFU": "XFU", # French UIC-Franc "F CFA": "XOF", # West African CFA Franc "XRE": "XRE", # RINET Funds "XSU": "XSU", # Sucre "XUA": "XUA", # ADB Unit of Account "¤": "XXX", # Unknown Currency "YDD": "YDD", # Yemeni Dinar "YUD": "YUD", # Yugoslavian Hard Dinar (1966–1990) "YUM": "YUM", # Yugoslavian New Dinar (1994–2002) "YUN": "YUN", # Yugoslavian Convertible Dinar (1990–1992) "YUR": "YUR", # Yugoslavian Reformed Dinar (1992–1993) "ZAL": "ZAL", # South African Rand (financial) "ZMK": "ZMK", # Zambian Kwacha (1968–2012) "ZRN": "ZRN", # Zairean New Zaire (1993–1998) "ZRZ": "ZRZ", # Zairean Zaire (1971–1993) "ZWD": "ZWD", # Zimbabwean Dollar (1980–2008) "ZWG": "ZWG", # Zimbabwean Gold "ZWR": "ZWR", # Zimbabwean Dollar (2008) "US$": "USD", # US Dollar "C$": "NIO", # Nicaraguan Córdoba "TT$": "TTD", # Trinidad & Tobago Dollar "BD$": "BBD", # Barbadian Dollar "B$": "BND", # Brunei Dollar "SI$": "SBD", # Solomon Islands Dollar "G$": "GYD", # Guyanaese Dollar "SR$": "SRD", # Surinamese Dollar "CLP$": "CLP", # Chilean Peso "Bs.": "VES", # Venezuelan Bolívar "Rf": "MVR", # Maldivian Rufiyaa "SSh": "SOS", # Somali Shilling "GH₵": "GHS", # Ghanaian Cedi "Br": "BYN", # Belarusian Ruble "CFA F": "XOF", # West African CFA Franc "CFAF": "XAF", # Central African CFA Franc "JP¥": "JPY", # Japanese Yen "元": "TWD", # New Taiwan Dollar "TL": "TRY", # Turkish Lira "лв.": "BGN", # Bulgarian Lev "₮": "MNT", # Mongolian Tugrik "﷼": "SAR", # Saudi Riyal ".د.ب": "BHD", # Bahraini Dinar "ل.س": "SYP", # Syrian Pound "﷼‎": "IRR", # Iranian Rial "ש״ח": "ILS", # Israeli New Shekel "Fr": "CHF", # Swiss Franc "CFP": "XPF", # CFP Franc "E": "SZL", # Swazi Lilangeni "Rf.": "MVR", # Maldivian Rufiyaa "QR": "QAR", # Qatari Riyal "SR": "SAR", # Saudi Riyal "BD": "BHD", # Bahraini Dinar "KD": "KWD", # Kuwaiti Dinar "₯": "GRD", # Greek Drachma "ЅМ": "TJS", # Tajikistani Somoni "Br (BYN)": "BYN", # Belarusian Ruble "B/.": "PAB", # Panamanian Balboa "Gs.": "PYG", # Paraguayan Guarani "Afl.": "AWG", # Aruban Florin "NAƒ": "ANG", # Netherlands Antillean Guilder "lei (MDL)": "MDL", # Moldovan Leu "KM (BAM)": "BAM", # Bosnia-Herzegovina Convertible Mark "ден (MKD)": "MKD", # Macedonian Denar "kn": "HRK", # Croatian Kuna "Z$": "ZWL", # Zimbabwean Dollar (2009–2024) "RTGS$": "ZWL", # Zimbabwean Dollar (2009–2024) "Skr": "SEK", # Swedish Krona "Nkr": "NOK", # Norwegian Krone "Dkr": "DKK", # Danish Krone "Íkr": "ISK", # Icelandic Króna "AU$": "AUD", # Australian Dollar "SG$": "SGD", # Singapore Dollar "BB$": "BBD", # Barbadian Dollar "BS$": "BSD", # Bahamian Dollar "KY$": "KYD", # Cayman Islands Dollar "BM$": "BMD", # Bermudan Dollar "JM$": "JMD", # Jamaican Dollar } def convert_currency_symbols_to_iso(text: str) -> str: """ Convert currency symbols to ISO codes for PDF compatibility Matches the TypeScript canonicalCurrencyMap logic """ import re # Import at function level to avoid scope issues processed_text = text # Track all conversions for detailed logging conversions_made = [] total_conversions = 0 # Process each currency symbol in the canonical map for symbol, iso_code in canonical_currency_map.items(): if symbol in processed_text: # Check if it's a single character is_single_character = len(symbol) == 1 is_unicode_currency = is_single_character and ord(symbol) > 127 # Non-ASCII is_latin_single_letter = is_single_character and symbol.isalpha() and ord(symbol) < 128 # Handle multi-character symbols (like "Le", "KSh", etc.) is_multi_char_symbol = len(symbol) > 1 # Skip plain Latin single letters (to avoid converting words like "A", "R", etc.) # but DO convert single-character Unicode currency symbols like €, £, ¥, ؋, etc. if not is_unicode_currency and is_latin_single_letter: # Only convert if it's followed by a number (currency context) # Pattern: R500, R 500, R500,000 etc. pattern = rf'\b{re.escape(symbol)}\s*(\d{{1,3}}(?:,?\d{{3}})*(?:\.\d{{2}})?)\b' matches = re.findall(pattern, processed_text) if matches: old_text = processed_text processed_text = re.sub(pattern, rf'{iso_code} \1', processed_text) if old_text != processed_text: conversions_made.append(f"{symbol} → {iso_code} (context: {matches})") total_conversions += len(matches) continue # For multi-character symbols like "Ar", "Le", etc., be very careful # Only convert if they appear as standalone words or in currency contexts if is_multi_char_symbol and len(symbol) == 2 and symbol.isalpha(): # Only convert if it's a standalone word or followed by a number # Pattern: \bAr\b or \bAr\s*\d or \bLe\b or \bLe\s*\d standalone_pattern = rf'\b{re.escape(symbol)}\b' currency_pattern = rf'\b{re.escape(symbol)}\s*(\d{{1,3}}(?:,?\d{{3}})*(?:\.\d{{2}})?)' # Check for standalone occurrences first standalone_matches = re.findall(standalone_pattern, processed_text) currency_matches = re.findall(currency_pattern, processed_text) if standalone_matches or currency_matches: old_text = processed_text # Replace standalone occurrences processed_text = re.sub(standalone_pattern, iso_code, processed_text) # Replace currency context occurrences processed_text = re.sub(currency_pattern, rf'{iso_code} \1', processed_text) if old_text != processed_text: conversions_made.append(f"{symbol} → {iso_code} (standalone/currency context)") total_conversions += len(standalone_matches) + len(currency_matches) continue # For all other symbols (Unicode currency symbols, multi-character symbols) # Count occurrences before replacement old_count = processed_text.count(symbol) # Replace all occurrences old_text = processed_text processed_text = processed_text.replace(symbol, iso_code) # Check if replacement actually happened if old_text != processed_text: conversions_made.append(f"{symbol} → {iso_code} ({old_count} instances)") total_conversions += old_count return processed_text