How to Set Up Multi-Currency Accounting with Exchange Rate Settings in ERPNext

Configure multi-currency accounting in ERPNext by establishing a base currency in Company settings, creating Currency Exchange Settings to connect external APIs like frankfurter.dev or exchangerate.host, and utilizing the get_exchange_rate() utility in erpnext/setup/utils.py to automatically fetch real-time rates for transactions.

ERPNext supports comprehensive multi-currency accounting that allows businesses to record transactions in foreign currencies while maintaining accounts in a base currency. This guide demonstrates how to configure exchange rate settings using the actual implementation in the frappe/erpnext repository, covering external API integration, manual rate management, and programmatic access.

Core Architecture of Multi-Currency Accounting

Key Components and Data Flow

The multi-currency system revolves around several interconnected DocTypes and utility functions:

  • Currency Exchange Settings (erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py): Stores API configuration including service provider selection (frankfurter.dev, exchangerate.host, or Custom), endpoint URLs, request parameters, and result keys for parsing JSON responses.

  • get_exchange_rate() (erpnext/setup/utils.py): The central utility function (lines 61-85) that orchestrates rate retrieval. It first checks for existing Currency Exchange records, then falls back to external APIs if configured.

  • format_ces_api(): Helper function within erpnext/setup/utils.py (lines 61-66) that interpolates placeholders like {from_currency}, {to_currency}, and {transaction_date} into API requests.

  • Currency Exchange DocType (erpnext/setup/doctype/currency_exchange/currency_exchange.py): Stores historical exchange rates for specific dates when manual overrides are needed.

  • Accounts Settings: Controls global behavior including "Allow Stale Rates" and "Allow Pegged Currencies Exchange Rates" toggles.

Exchange Rate Resolution Process

When a transaction is saved, ERPNext executes this resolution chain:

  1. The system calls get_exchange_rate(from_currency, to_currency, transaction_date).
  2. It queries the Currency Exchange table for a matching record on the specified date.
  3. If no record exists and Currency Exchange Settings is active (disabled = 0), it formats the API URL using format_ces_api() with stored parameters.
  4. The external API returns JSON; the system extracts the rate using the configured result_key path.
  5. Results are cached for six hours, and pegged currency ratios are applied if enabled.

Configuring Multi-Currency Settings

Step 1: Set Company Currency and Global Preferences

Navigate to Company > Settings to establish your base currency (e.g., USD). Then access Accounts Settings to configure:

  • Allow Stale Rates: Permits use of existing Currency Exchange records when APIs are unavailable.
  • Allow Pegged Currencies: Enables fixed-ratio conversions for currencies pegged to your base currency.

These settings reside in the Accounts Settings DocType (erpnext/accounts/doctype/accounts_settings/accounts_settings.py).

Step 2: Create Currency Exchange Settings

Go to Accounts > Currency Exchange Settings and configure:

  1. Service Provider: Select from frankfurter.dev, exchangerate.host, or Custom.
  2. API Configuration: For exchangerate.host, provide your access_key in the designated field.
  3. Request Parameters: The system auto-populates default rows:
    • amount = 1
    • date = {transaction_date}
    • from = {from_currency}
    • to = {to_currency}

The set_parameters_and_result() method in currency_exchange_settings.py handles validation and default population of these fields.

Step 3: Configure Pegged Currencies (Optional)

For currencies with fixed exchange ratios, create Pegged Currency Details records in erpnext/accounts/doctype/pegged_currency_details/pegged_currency_details.py. These ratios automatically adjust calculated rates when the pegged currency option is enabled in Accounts Settings.

Implementing Exchange Rates in Code

Fetching Rates in Custom Scripts

Use the get_exchange_rate() utility to retrieve current rates programmatically:

from erpnext.setup.utils import get_exchange_rate

invoice = frappe.get_doc("Sales Invoice", "INV-0001")
rate = get_exchange_rate("INR", "USD", invoice.posting_date)

frappe.msgprint(f"Exchange rate on {invoice.posting_date}: 1 INR = {rate:.4f} USD")

This call prioritizes cached Currency Exchange records before invoking external APIs defined in your Currency Exchange Settings.

Creating Manual Exchange Rate Records

When APIs are unavailable or you need historical overrides, insert records directly:

frappe.get_doc({
    "doctype": "Currency Exchange",
    "date": "2024-04-01",
    "from_currency": "EUR",
    "to_currency": "GBP",
    "exchange_rate": 0.85
}).insert()

Subsequent calls to get_exchange_rate("EUR", "GBP", "2024-04-01") will return 0.85 without external API calls.

Runtime Configuration Override

Temporarily switch service providers for specific transactions by modifying the settings document:

settings = frappe.get_cached_doc("Currency Exchange Settings")
settings.service_provider = "Custom"
settings.api_endpoint = "https://my.custom.api/rate?date={transaction_date}&from={from_currency}&to={to_currency}"
settings.result_key = [{"key": "rate"}]
settings.save()

rate = get_exchange_rate("USD", "JPY", "2024-05-18")

Summary

  • Multi-currency accounting in ERPNext centers on the get_exchange_rate() function in erpnext/setup/utils.py, which coordinates between manual Currency Exchange records and external APIs.
  • Currency Exchange Settings (erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py) stores provider configurations and uses format_ces_api() to parameterize requests with placeholders like {from_currency} and {transaction_date}.
  • Rates are cached for six hours, with automatic fallback to historical records when "Allow Stale Rates" is enabled in Accounts Settings.
  • Pegged currency ratios from pegged_currency_details.py apply automatically when the global setting is enabled.

Frequently Asked Questions

How does ERPNext prioritize exchange rate sources?

ERPNext first checks for existing Currency Exchange records matching the date and currency pair. If no record exists, it queries the external API configured in Currency Exchange Settings, then caches the result for six hours according to the implementation in erpnext/setup/utils.py.

What placeholders can I use in custom API endpoints?

The format_ces_api() function in erpnext/setup/utils.py supports three placeholders: {from_currency}, {to_currency}, and {transaction_date}. These are automatically interpolated when the system constructs the API request URL based on your Currency Exchange Settings configuration.

Where is the exchange rate caching logic implemented?

Caching occurs within get_exchange_rate() in erpnext/setup/utils.py. The function stores successful API responses for six hours to minimize external calls and improve transaction processing speed, falling back to manual Currency Exchange records when stale rates are permitted.

Can I use multiple exchange rate providers simultaneously?

ERPNext uses a single active Currency Exchange Settings record per instance. However, you can override the service provider programmatically at runtime by modifying the Currency Exchange Settings document before calling get_exchange_rate(), or create manual Currency Exchange records for specific currency pairs that bypass external APIs entirely.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →