This article is currently available in English only. Translation coming soon.
By the end of this recipe, your Odoo 19 instance will record sales orders, vendor bills, and bank transactions in any of 170+ currencies, automatically revalue open foreign-currency balances at month-end, and produce auditor-grade journal entries for unrealized FX gains and losses. Skill required: Odoo administrator with accounting context. Time required: 90 minutes for the basic flow, plus a 30-minute audit review at the first month-end. ECOSIRE has rolled this out for clients trading across GBP, EUR, USD, AED, INR, BRL, CNY — and the recipe below distills the gotchas we have hit in production.
The single biggest pitfall: enabling a currency without configuring its rate update source means every transaction in that currency uses the rate that was loaded at install time (often months out of date). This shows up at month-end as a phantom 5 to 10 percent gain or loss on the FX revaluation report and triggers an audit query. Configure the live rate feed BEFORE opening transactions in any new currency.
What you will need
- Odoo version: 17, 18, or 19, Community or Enterprise. The
currency_rate_livemodule ships with Community. - Modules:
account(always installed),currency_rate_live(in Apps), and optionallyaccount_accountantfor the Enterprise revaluation wizard. - User access: Settings administrator + Accounting Manager.
- Data: at least one fully working company with chart of accounts and a fiscal year defined.
- Decision before you start: which currency is your "company currency" (the base for reporting)? This is set on
res.company.currency_idand is non-trivial to change after data exists. For a UK Ltd, GBP. For a Delaware C-Corp, USD. For a Pakistan Pvt Ltd, PKR. Multi-company structures can have different base currencies per company. - Time: 90 minutes for the configuration, 30 minutes verification at first month-end.
Step-by-step
1. Activate the currencies you trade in
Go to Accounting > Configuration > Currencies. By default only the company currency is active. For each currency you transact in, click the "Active" toggle. Activate at least USD, EUR, and any other currency on your customers' or suppliers' contact records. Verification: the currency selector on a Sales Order draft now lists the activated currencies.
2. Install the live rate feed module
Go to Apps, search for "Currency Rate Live" (technical name currency_rate_live), and install. This module knows how to fetch from ECB (European Central Bank), the Bank of Canada, the Reserve Bank of Australia, and a handful of others. Verification: Accounting > Configuration > Currencies now shows a "Service" column on each currency row.
3. Configure the rate provider
Go to Settings > Accounting > Currencies > Automatic Currency Rates. Pick a provider: ECB is free, daily updated, and covers ~30 major currencies. For exotic currencies (PKR, IDR, NGN, ZAR), use xe.com (paid API key required) or the Bank of Canada (free for many currencies). Set the interval to "Daily" and the next execution date to tomorrow at 6 AM UTC.
# If you want a custom provider, add a small module that extends res.currency.rate.provider
from odoo import models, api
import requests
class CurrencyRateProviderCustom(models.Model):
_inherit = 'res.currency.rate.provider'
def _get_supported_currencies(self):
if self.service == 'custom_pkr':
return ['USD', 'PKR', 'EUR']
return super()._get_supported_currencies()
def _obtain_rates(self, base_currency, currencies, date_from, date_to):
if self.service == 'custom_pkr':
# Call your custom rate API
resp = requests.get('https://your-rate-api.example.com/rates', timeout=10)
data = resp.json()
return {ccy: {fields.Date.today(): rate} for ccy, rate in data.items() if ccy in currencies}
return super()._obtain_rates(base_currency, currencies, date_from, date_to)
Verification: open Accounting > Configuration > Currencies > USD > Rates. After the cron runs, you should see today's rate against your company currency. If your company is EUR-based, USD rate should be roughly 0.92 (varies daily).
4. Enable foreign-currency journal entries
Go to Accounting > Configuration > Journals. For each journal where you expect foreign-currency transactions, set the "Allowed Currencies" field. Bank journals must have their currency_id set to the actual currency of the bank account (a HSBC USD bank account has currency_id = USD even though the company currency is GBP). Verification: create a draft customer invoice in USD against a GBP company. The invoice form shows two amounts — the USD invoice total and the GBP equivalent at today's rate.
5. Configure the FX revaluation accounts
Go to Settings > Accounting > Default Accounts. Set:
- Realized Loss Account: usually 7990 "Realized FX Loss"
- Realized Gain Account: 4990 "Realized FX Gain"
- Unrealized Loss Account: 7991 "Unrealized FX Loss"
- Unrealized Gain Account: 4991 "Unrealized FX Gain"
Realized gains/losses are recorded automatically when an invoice is paid (the difference between invoice rate and payment rate). Unrealized gains/losses are recorded by the month-end revaluation cron on still-open balances. Verification: open the Trial Balance. There should be four FX accounts visible (with zero balance until the first revaluation).
6. Run the FX revaluation manually
Go to Accounting > Reporting > Foreign Currency Adjustments. Select the period (month-end), select the journal where the revaluation entries should post (we recommend a dedicated FX journal), and click "Adjust Balance". Odoo computes the difference between the original transaction rate and the period-end rate, then posts a reversing journal entry on the last day of the month + a backdated reversal on the first day of the next month.
-- Verify the revaluation is correct: foreign-currency open balances
SELECT
account_move_line.account_id,
account_account.code,
SUM(account_move_line.amount_currency) AS open_in_ccy,
SUM(account_move_line.balance) AS open_in_company_ccy,
res_currency.name AS currency
FROM account_move_line
JOIN account_account ON account_move_line.account_id = account_account.id
JOIN res_currency ON account_move_line.currency_id = res_currency.id
WHERE account_move_line.parent_state = 'posted'
AND account_move_line.reconciled = false
AND account_move_line.currency_id IS NOT NULL
GROUP BY 1,2,5;
Verification: the SQL output matches the foreign-currency adjustment report in Odoo. Both show the open positions in foreign currency and their company-currency equivalent.
7. Schedule the monthly revaluation cron
In Enterprise, go to Settings > Technical > Scheduled Actions. Find "Foreign Currency Revaluation" and set the next execution to the last day of the current month at 23:30. Set the interval to "1 Month".
Verification: at the next month-end, the FX revaluation entries appear in the FX journal automatically and reverse themselves on day 1 of the following month.
8. Reconcile foreign-currency invoices and payments
When a customer pays a USD invoice from a USD bank account, Odoo automatically computes the realized FX gain or loss based on the rate difference between invoice date and payment date. Go to the customer invoice, click "Register Payment", select the USD bank journal, and confirm. Verification: open the journal entry and confirm the realized FX line is posted to account 4990 or 7990, NOT to a generic suspense account.
Common mistakes
- Activating a currency but not configuring its rate provider. Every transaction uses the install-date rate, which is months stale.
- Setting the bank journal currency wrong. A USD bank account must have
currency_id = USDon the journal; otherwise every USD payment looks like a 0.92x conversion. - Forgetting the FX accounts on company setup. The revaluation wizard fails silently if any of the four accounts are not configured, leaving auditors with phantom unrealized FX hidden in clearing accounts.
- Running revaluation on a date that is not month-end. Consistent posting dates matter for comparison reports — always use the last calendar day of the period.
- Manual rate overrides without an audit note. If you override the daily rate, log the reason in the rate's "comment" field; auditors look for unexplained spikes.
Going further
Multiple rate providers per currency: ECB for EUR/USD/GBP, Banco de Mexico for MXN, custom API for PKR. Configure two res.currency.rate.provider records and Odoo picks the correct one per currency.
Hedge accounting: complex enterprise treasury operations need to record forward contracts and revalue them separately. This is typically a custom module since standard Odoo does not have a hedge accounting feature.
Inter-company FX: in a multi-company structure with different base currencies, every inter-company transaction generates FX. Set up a standardized inter-company rate (often the start-of-month rate) and a small Python script to lock that rate on inter-company invoices.
Multi-currency consolidation: Enterprise's Consolidation module translates each subsidiary's trial balance to the parent currency using a mix of period-end and historical rates. Configure the translation method per account type.
For complex multi-currency, multi-jurisdiction accounting setups, ECOSIRE's accounting services handle the implementation end to end. We have configured this for clients across UK, EU, US, Middle East, and APAC. Pair this with how to set up multi-company invoicing in Odoo 19 for the full picture.
Frequently Asked Questions
Can I change the company base currency after the fact?
In theory yes, but in practice you should not. Odoo supports the operation via Settings > Companies > Update Info but every existing journal entry is recalculated at the current rate, which destroys historical accuracy. The clean path is to start a new fiscal year in the new base currency and migrate opening balances.
How does Odoo handle currency rounding?
The smallest currency unit is set on res.currency.rounding. USD is 0.01, JPY is 1, BHD is 0.001. Odoo rounds at line-item level using the configured method (HALF-UP, HALF-EVEN). For invoices that need penny-perfect summation, use the "Round Globally" tax computation method on the company config.
What if a rate is missing for a date?
Odoo uses the most recent rate before the missing date. If you backdate an invoice to a Saturday and ECB only publishes on weekdays, Odoo uses Friday's rate. This is generally acceptable for accounting but can drift on long weekends.
How do I handle currencies that need approval?
For high-risk currencies (USD on a sanctions-restricted entity, for example), add an ir.model.access rule that restricts who can post journal entries with that currency. We have built per-currency approval flows for clients in financial services where treasury must sign off on every USD invoice over $50k.
For implementation help on complex multi-currency setups, our Odoo accounting team handles the rollout. Or read how to build a cash flow statement in Odoo for the next step in your reporting stack.
تحریر
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
ECOSIRE
Odoo ERP کے ساتھ اپنے کاروبار کو تبدیل کریں
آپ کے کاموں کو ہموار کرنے کے لیے ماہر Odoo کا نفاذ، حسب ضرورت، اور معاونت۔
متعلقہ مضامین
How to Add a Custom Button to an Odoo Form View (2026)
Add custom action buttons to Odoo 19 form views: Python action method, view inheritance, conditional visibility, confirmation dialogs. Production-tested.
How to Add a Custom Field in Odoo Without Studio (2026)
Add custom fields via custom module in Odoo 19: model inheritance, view extension, computed fields, store/non-store decisions. Code-first, version-controlled.
How to Add a Custom Report in Odoo Using External Layout
Build a branded PDF report in Odoo 19 using web.external_layout: QWeb template, paperformat, action binding. With print logo + footer overrides.