diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js index e44af3a9167..51e1f7a91dc 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js @@ -4,6 +4,12 @@ frappe.ui.form.on('Accounts Settings', { refresh: function(frm) { + }, + validate_access_key(frm) { + frappe.call({ + doc: frm.doc, + method: "validate_access_key" + }); } }); diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 9a85b94e9a9..41805b1bd50 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -47,6 +47,9 @@ "allow_stale", "stale_days", "service_provider", + "column_break_eiyok", + "access_key", + "validate_access_key", "report_settings_sb", "use_custom_cash_flow" ], @@ -303,10 +306,28 @@ "label": "Show Taxes as Table in Print" }, { + "default": "frankfurter.app", "fieldname": "service_provider", "fieldtype": "Select", "label": "Service Provider", "options": "frankfurter.app\nexchangerate.host" + }, + { + "depends_on": "eval:doc.service_provider == \"exchangerate.host\"", + "description": "Access Key is mandatory for exchangerate.host", + "fieldname": "access_key", + "fieldtype": "Data", + "label": "Access Key" + }, + { + "depends_on": "eval:doc.service_provider == \"exchangerate.host\"", + "fieldname": "validate_access_key", + "fieldtype": "Button", + "label": "Validate Access Key" + }, + { + "fieldname": "column_break_eiyok", + "fieldtype": "Column Break" } ], "icon": "icon-cog", @@ -314,7 +335,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-10-06 15:05:51.999404", + "modified": "2023-10-07 14:20:01.779208", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 835498176c7..7eafbd70e3a 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -8,12 +8,43 @@ import frappe from frappe import _ from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.model.document import Document -from frappe.utils import cint +from frappe.utils import cint, nowdate from erpnext.stock.utils import check_pending_reposting class AccountsSettings(Document): + @frappe.whitelist() + def validate_access_key(self): + if self.service_provider == "exchangerate.host": + if not self.access_key: + frappe.throw(_("Access Key is required for exchangerate.host")) + else: + import requests + + # Validate access key + api_url = "https://api.exchangerate.host/convert" + response = requests.get( + api_url, + params={ + "access_key": self.access_key, + "transaction_date": nowdate(), + "amount": 1, + "from": "USD", + "to": "INR", + }, + ) + # exchangerate.host return 200 for all requests. Can't rely on it to raise exception + if not response.json()["success"]: + frappe.throw( + title=_("Service Provider Error"), + msg=_("Currency exchange rate serivce provider: {0} returned Error. {1}").format( + frappe.bold(self.service_provider), response.json() + ), + exc=frappe.ValidationError, + ) + frappe.msgprint(msg=_("Success"), title=_("Access Key Validation")) + def on_update(self): frappe.clear_cache() diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 5c7a5632374..647c4015419 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -113,12 +113,30 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No if not value: import requests - api_url = f"https://api.frankfurter.app/{transaction_date}" - response = requests.get(api_url, params={"from": from_currency, "to": to_currency}) + if currency_settings.service_provider == "exchangerate.host": + api_url = "https://api.exchangerate.host/convert" + response = requests.get( + api_url, + params={ + "access_key": currency_settings.access_key, + "transaction_date": transaction_date, + "amount": 1, + "from": from_currency, + "to": to_currency, + }, + ) + # exchangerate.host return 200 for all requests. Can't rely on it to raise exception + value = response.json()["result"] + if not response.json()["success"]: + raise frappe.ValidationError + + else: + api_url = f"https://api.frankfurter.app/{transaction_date}" + response = requests.get(api_url, params={"from": from_currency, "to": to_currency}) + value = response.json()["rates"][to_currency] # expire in 6 hours response.raise_for_status() - value = response.json()["rates"][to_currency] cache.setex(name=key, time=21600, value=flt(value)) return flt(value) except Exception: