From 70628c06c99d279b59acc5619ab0e77d4077349a Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Tue, 9 Jun 2026 11:58:30 +0530 Subject: [PATCH 1/6] feat(currency exchange settings): frankfurter v2 support (cherry picked from commit 56bfe6b6a695b2d93ceedf8d50082aa732789b2a) # Conflicts: # erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json --- .../currency_exchange_settings.js | 18 ++++++++++++------ .../currency_exchange_settings.json | 7 ++++++- .../currency_exchange_settings.py | 19 +++++++++++++++++-- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js index 40f0938ee1c..950092a2382 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js @@ -11,22 +11,28 @@ frappe.ui.form.on("Currency Exchange Settings", { }, callback: function (r) { if (r && r.message) { + let result = [], + params = {}; if (frm.doc.service_provider == "exchangerate.host") { - let result = ["result"]; - let params = { + result = ["result"]; + params = { date: "{transaction_date}", from: "{from_currency}", to: "{to_currency}", }; - add_param(frm, r.message, params, result); } else if (["frankfurter.app", "frankfurter.dev"].includes(frm.doc.service_provider)) { - let result = ["rates", "{to_currency}"]; - let params = { + result = ["rates", "{to_currency}"]; + params = { base: "{from_currency}", symbols: "{to_currency}", }; - add_param(frm, r.message, params, result); + } else if (frm.doc.service_provider == "frankfurter.dev - v2") { + result = ["rate"]; + params = { + date: "{transaction_date}", + }; } + add_param(frm, r.message, params, result); } }, }); diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json index 614f4e6d3e5..d8c22fe2b97 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_bulk_edit": 1, "creation": "2022-01-10 13:03:26.237081", "doctype": "DocType", "editable_grid": 1, @@ -78,7 +79,7 @@ "fieldname": "service_provider", "fieldtype": "Select", "label": "Service Provider", - "options": "frankfurter.dev\nexchangerate.host\nCustom", + "options": "frankfurter.dev\nexchangerate.host\nfrankfurter.dev - v2\nCustom", "reqd": 1 }, { @@ -104,7 +105,11 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], +<<<<<<< HEAD "modified": "2025-11-25 13:03:41.896424", +======= + "modified": "2026-06-09 11:34:10.432378", +>>>>>>> 56bfe6b6a6 (feat(currency exchange settings): frankfurter v2 support) "modified_by": "Administrator", "module": "Accounts", "name": "Currency Exchange Settings", diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py index 28e4158cc8d..e57805ccea8 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.py @@ -29,7 +29,7 @@ class CurrencyExchangeSettings(Document): disabled: DF.Check req_params: DF.Table[CurrencyExchangeSettingsDetails] result_key: DF.Table[CurrencyExchangeSettingsResult] - service_provider: DF.Literal["frankfurter.dev", "exchangerate.host", "Custom"] + service_provider: DF.Literal["frankfurter.dev", "exchangerate.host", "frankfurter.dev - v2", "Custom"] url: DF.Data | None use_http: DF.Check # end: auto-generated types @@ -70,6 +70,14 @@ class CurrencyExchangeSettings(Document): self.append("req_params", {"key": "base", "value": "{from_currency}"}) self.append("req_params", {"key": "symbols", "value": "{to_currency}"}) + elif self.service_provider == "frankfurter.dev - v2": + self.set("result_key", []) + self.set("req_params", []) + + self.api_endpoint = get_api_endpoint(self.service_provider, self.use_http) + self.append("result_key", {"key": "rate"}) + self.append("req_params", {"key": "date", "value": "{transaction_date}"}) + def validate_parameters(self): params = {} for row in self.req_params: @@ -105,13 +113,20 @@ class CurrencyExchangeSettings(Document): @frappe.whitelist() def get_api_endpoint(service_provider: str | None = None, use_http: bool = False): - if service_provider and service_provider in ["exchangerate.host", "frankfurter.dev", "frankfurter.app"]: + if service_provider and service_provider in [ + "exchangerate.host", + "frankfurter.dev", + "frankfurter.app", + "frankfurter.dev - v2", + ]: if service_provider == "exchangerate.host": api = "api.exchangerate.host/convert" elif service_provider == "frankfurter.app": api = "api.frankfurter.app/{transaction_date}" elif service_provider == "frankfurter.dev": api = "api.frankfurter.dev/v1/{transaction_date}" + elif service_provider == "frankfurter.dev - v2": + api = "api.frankfurter.dev/v2/rate/{from_currency}/{to_currency}" protocol = "https://" if use_http: From d62985d9a76ca474b138a71b97cd9a98d342b7e1 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Tue, 9 Jun 2026 12:17:18 +0530 Subject: [PATCH 2/6] fix: use frankfurter v2 by default for new install (cherry picked from commit 479f9f63c9b3e26410e67190b1e8cf4e47832b19) --- erpnext/setup/install.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 8c288b783c1..03fc31b253e 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -90,14 +90,7 @@ def set_single_defaults(): def setup_currency_exchange(): ces = frappe.get_single("Currency Exchange Settings") try: - ces.set("result_key", []) - ces.set("req_params", []) - - ces.api_endpoint = "https://api.frankfurter.dev/v1/{transaction_date}" - ces.append("result_key", {"key": "rates"}) - ces.append("result_key", {"key": "{to_currency}"}) - ces.append("req_params", {"key": "base", "value": "{from_currency}"}) - ces.append("req_params", {"key": "symbols", "value": "{to_currency}"}) + ces.service_provider = "frankfurter.dev - v2" ces.save() except frappe.ValidationError: pass From 64fc3ac309c0144f1c51c9e9f438fd6104728222 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Tue, 9 Jun 2026 23:00:41 +0530 Subject: [PATCH 3/6] test: fixed currency exchange test for frankfurter v2 api (cherry picked from commit 138f683a68acb6c698beb26821299264cc1e4e73) --- .../doctype/currency_exchange/test_currency_exchange.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py index 86fae223b45..6cece137366 100644 --- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py @@ -68,13 +68,16 @@ def patched_requests_get(*args, **kwargs): if kwargs["params"].get("date") and kwargs["params"].get("from") and kwargs["params"].get("to"): if test_exchange_values.get(kwargs["params"]["date"]): return PatchResponse({"result": test_exchange_values[kwargs["params"]["date"]]}, 200) - elif args[0].startswith("https://api.frankfurter.dev") and kwargs.get("params"): + elif args[0].startswith("https://api.frankfurter.dev/v1") and kwargs.get("params"): if kwargs["params"].get("base") and kwargs["params"].get("symbols"): date = args[0].replace("https://api.frankfurter.dev/v1/", "") if test_exchange_values.get(date): return PatchResponse( {"rates": {kwargs["params"].get("symbols"): test_exchange_values.get(date)}}, 200 ) + elif args[0].startswith("https://api.frankfurter.dev/v2") and kwargs.get("params"): + if kwargs["params"].get("date") and test_exchange_values.get(kwargs["params"]["date"]): + return PatchResponse({"rate": test_exchange_values.get(kwargs["params"]["date"])}, 200) return PatchResponse({"rates": None}, 404) From 7b6520664c90c510cb94e98af5172c6d19c77a1e Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Mon, 15 Jun 2026 11:27:26 +0530 Subject: [PATCH 4/6] fix: restricting currency_exchange_settings write permission only to system manager (cherry picked from commit 0c2d5488a64325724afe4b3f925a9be92e000a4f) # Conflicts: # erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json --- .../currency_exchange_settings.json | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json index d8c22fe2b97..388546dcf56 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_bulk_edit": 1, "creation": "2022-01-10 13:03:26.237081", "doctype": "DocType", "editable_grid": 1, @@ -105,11 +104,15 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], +<<<<<<< HEAD <<<<<<< HEAD "modified": "2025-11-25 13:03:41.896424", ======= "modified": "2026-06-09 11:34:10.432378", >>>>>>> 56bfe6b6a6 (feat(currency exchange settings): frankfurter v2 support) +======= + "modified": "2026-06-15 11:25:55.873110", +>>>>>>> 0c2d5488a6 (fix: restricting currency_exchange_settings write permission only to system manager) "modified_by": "Administrator", "module": "Accounts", "name": "Currency Exchange Settings", @@ -126,24 +129,11 @@ "write": 1 }, { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "Accounts Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, "email": 1, "print": 1, "read": 1, "role": "Accounts User", - "share": 1, - "write": 1 + "share": 1 } ], "row_format": "Dynamic", From 9405b49e93b9ce1c04bc3cca78b848325fe7a6dc Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Mon, 15 Jun 2026 13:50:38 +0530 Subject: [PATCH 5/6] fix(get_exchange_rate): using get_single_value to fetch `disabled` value from `currency_exchange_settings` (cherry picked from commit abb579e2db7ac7705b859f8fb037fac7a0de96be) --- erpnext/setup/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index b7436a140f8..7de420ceea9 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -130,7 +130,7 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No if entries: return flt(entries[0].exchange_rate) - if frappe.get_cached_value("Currency Exchange Settings", "Currency Exchange Settings", "disabled"): + if frappe.get_single_value("Currency Exchange Settings", "disabled"): return 0.00 pegged_currencies = {} From 80741ceb679ba7b3f612f4f4abb54433a2c324c5 Mon Sep 17 00:00:00 2001 From: Diptanil Saha Date: Mon, 15 Jun 2026 14:39:29 +0530 Subject: [PATCH 6/6] chore: resolve conflicts --- .../currency_exchange_settings.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json index 388546dcf56..a3aea6016b9 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json @@ -104,15 +104,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], -<<<<<<< HEAD -<<<<<<< HEAD - "modified": "2025-11-25 13:03:41.896424", -======= - "modified": "2026-06-09 11:34:10.432378", ->>>>>>> 56bfe6b6a6 (feat(currency exchange settings): frankfurter v2 support) -======= "modified": "2026-06-15 11:25:55.873110", ->>>>>>> 0c2d5488a6 (fix: restricting currency_exchange_settings write permission only to system manager) "modified_by": "Administrator", "module": "Accounts", "name": "Currency Exchange Settings",