DEV Community

viraj geeth
viraj geeth

Posted on Originally published at allratestoday.com

Loading Official Central Bank Exchange Rates into Odoo (res.currency.rate, the Right Direction)

Odoo's Automatic Currency Rates setting (module currency_rate_live) pulls from a fixed list of providers: the ECB, the Swiss FTA, the Bank of Canada, Banxico and a few more. If the central bank your statutory accounts must follow is on that list, use it and stop reading.

If it is not, or you run subsidiaries under several central banks and want one feed, you write the rows yourself. The good news is that res.currency.rate is one of the most stable models in Odoo and the loader is short. The full version with notes is in the Odoo guide.

The rate Odoo stores

res.currency.rate.rate is units of the foreign currency per 1 unit of the company currency. For a EUR company and USD that is USD per EUR, which is the ECB's own direction, so nothing to invert.

For an INR company following the Reserve Bank of India, which publishes INR per USD, you need the inverse. Do not divide in your own code. Ask the API for the pair in Odoo's direction and let it invert, flagging what it did:

GET https://allratestoday.com/api/v1/central-bank/rbi/latest?source=INR&target=USD

{ "bank": "rbi", "rate_date": "2026-10-09", "source": "INR", "target": "USD",
  "rate": 0.01035, "derived": true, "method": "inverse" }
Enter fullscreen mode Exit fullscreen mode

derived: true with method: "inverse" is the line you want in your audit file. When the bank publishes the pair directly you get derived: false, method: "published".

The loader

import xmlrpc.client, requests

URL, DB, USER, PWD = "https://erp.example.com", "prod", "bot@example.com", "api-key"
BANK = "ecb"
ART = {"Authorization": "Bearer art_live_your_key"}

common = xmlrpc.client.ServerProxy(f"{URL}/xmlrpc/2/common")
uid = common.authenticate(DB, USER, PWD, {})
models = xmlrpc.client.ServerProxy(f"{URL}/xmlrpc/2/object")
call = lambda model, method, *args, **kw: models.execute_kw(DB, uid, PWD, model, method, list(args), kw)

company = call("res.company", "read", [1], fields=["currency_id"])[0]
home = call("res.currency", "read", [company["currency_id"][0]], fields=["name"])[0]["name"]
active = {c["name"]: c["id"] for c in call("res.currency", "search_read", [["active", "=", True]], fields=["name"])}

for code, currency_id in active.items():
    if code == home:
        continue
    r = requests.get(f"https://allratestoday.com/api/v1/central-bank/{BANK}/latest",
                     params={"source": home, "target": code}, headers=ART, timeout=10)
    if r.status_code == 404:
        continue                                   # bank does not publish this currency
    body = r.json()
    vals = {"currency_id": currency_id, "name": body["rate_date"], "rate": body["rate"], "company_id": 1}
    existing = call("res.currency.rate", "search",
                    [["currency_id", "=", currency_id], ["name", "=", body["rate_date"]], ["company_id", "=", 1]])
    if existing:
        call("res.currency.rate", "write", existing, vals)
    else:
        call("res.currency.rate", "create", vals)
    print(code, body["rate_date"], body["rate"], body["method"])
Enter fullscreen mode Exit fullscreen mode

Three details that bite people:

  • name is the effective date, not the run date. Always use the bank's rate_date. If you run the loader on a Monday for a bank that publishes on business days, you get Friday's table dated Friday, which is correct.
  • Multi-company: run the loop per company with that company's home currency, and per bank code if the subsidiaries sit under different central banks.
  • Month-end sources such as HMRC's monthly table or a monthly-average publisher load the same way on a monthly cron.

Run it inside Odoo

Put the loop in a small custom module and register an ir.cron that runs daily after the bank's publication time. Inside Odoo the same logic uses self.env['res.currency.rate'] and requests, with the API key in an ir.config_parameter rather than in the code. An inverse_company_rate field also exists from Odoo 14; writing either field is fine, but be consistent so reruns update rather than fight.

Try it without a key

https://allratestoday.com/api/open/central-bank/ecb returns the latest ECB table keyless, so you can prototype the loader before signing up.

Links

Rates are redistributed from each institution's public publication. They are reference rates for accounting, tax and compliance, not dealing quotes.

Top comments (0)