From 74063026c811fef6a96f49a7a7616fd280afc447 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 28 Jul 2025 12:12:34 +0530 Subject: [PATCH 01/16] fix: get latest exchange rate (cherry picked from commit 5bfcaf48097e5b097a76bc4056b4d2f7e2efecc2) --- crm/fcrm/doctype/crm_deal/crm_deal.py | 2 +- crm/fcrm/doctype/crm_organization/crm_organization.py | 2 +- crm/utils/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crm/fcrm/doctype/crm_deal/crm_deal.py b/crm/fcrm/doctype/crm_deal/crm_deal.py index bd4198a3..4b9bc95d 100644 --- a/crm/fcrm/doctype/crm_deal/crm_deal.py +++ b/crm/fcrm/doctype/crm_deal/crm_deal.py @@ -177,7 +177,7 @@ class CRMDeal(Document): system_currency = frappe.db.get_single_value("FCRM Settings", "currency") or "USD" exchange_rate = 1 if self.currency and self.currency != system_currency: - exchange_rate = get_exchange_rate(self.currency, system_currency, frappe.utils.nowdate()) + exchange_rate = get_exchange_rate(self.currency, system_currency) self.db_set("exchange_rate", exchange_rate) diff --git a/crm/fcrm/doctype/crm_organization/crm_organization.py b/crm/fcrm/doctype/crm_organization/crm_organization.py index 1cdf186c..e528435c 100644 --- a/crm/fcrm/doctype/crm_organization/crm_organization.py +++ b/crm/fcrm/doctype/crm_organization/crm_organization.py @@ -16,7 +16,7 @@ class CRMOrganization(Document): system_currency = frappe.db.get_single_value("FCRM Settings", "currency") or "USD" exchange_rate = 1 if self.currency and self.currency != system_currency: - exchange_rate = get_exchange_rate(self.currency, system_currency, frappe.utils.nowdate()) + exchange_rate = get_exchange_rate(self.currency, system_currency) self.db_set("exchange_rate", exchange_rate) diff --git a/crm/utils/__init__.py b/crm/utils/__init__.py index eaf076d5..0e4582df 100644 --- a/crm/utils/__init__.py +++ b/crm/utils/__init__.py @@ -284,7 +284,7 @@ def get_exchange_rate(from_currency, to_currency, date=None): return rate frappe.log_error( - f"Failed to fetch exchange rate from {from_currency} to {to_currency} on {date}", title="Exchange Rate Fetch Error", + message=f"Failed to fetch exchange rate from {from_currency} to {to_currency} on {date}", ) return 1.0 # Default exchange rate if API call fails or no rate found From 707268656595c80aefc6aeb7c3c07888a94a4398 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 28 Jul 2025 12:59:34 +0530 Subject: [PATCH 02/16] fix: added currency exchange rate settings (cherry picked from commit 45826e0a887be6d95918ea90ef3e144cda59f4fe) --- .../__init__.py | 0 .../crm_currency_exchange_settings.js | 8 +++ .../crm_currency_exchange_settings.json | 54 +++++++++++++++++++ .../crm_currency_exchange_settings.py | 10 ++++ .../test_crm_currency_exchange_settings.py | 22 ++++++++ 5 files changed, 94 insertions(+) create mode 100644 crm/fcrm/doctype/crm_currency_exchange_settings/__init__.py create mode 100644 crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.js create mode 100644 crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.json create mode 100644 crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py create mode 100644 crm/fcrm/doctype/crm_currency_exchange_settings/test_crm_currency_exchange_settings.py diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/__init__.py b/crm/fcrm/doctype/crm_currency_exchange_settings/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.js b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.js new file mode 100644 index 00000000..bda41c07 --- /dev/null +++ b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("CRM Currency Exchange Settings", { +// refresh(frm) { + +// }, +// }); diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.json b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.json new file mode 100644 index 00000000..2faeb78b --- /dev/null +++ b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.json @@ -0,0 +1,54 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-07-28 12:29:02.935286", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "service_provider", + "access_key" + ], + "fields": [ + { + "default": "frankfurter.app", + "fieldname": "service_provider", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Service Provider", + "options": "frankfurter.app\nexchangerate.host", + "reqd": 1 + }, + { + "depends_on": "eval:doc.service_provider == 'exchangerate.host';", + "fieldname": "access_key", + "fieldtype": "Data", + "label": "Access Key", + "mandatory_depends_on": "eval:doc.service_provider == 'exchangerate.host';" + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2025-07-28 12:30:08.080590", + "modified_by": "Administrator", + "module": "FCRM", + "name": "CRM Currency Exchange Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "row_format": "Dynamic", + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py new file mode 100644 index 00000000..aafc54e2 --- /dev/null +++ b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py @@ -0,0 +1,10 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class CRMCurrencyExchangeSettings(Document): + pass + diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/test_crm_currency_exchange_settings.py b/crm/fcrm/doctype/crm_currency_exchange_settings/test_crm_currency_exchange_settings.py new file mode 100644 index 00000000..ec05d679 --- /dev/null +++ b/crm/fcrm/doctype/crm_currency_exchange_settings/test_crm_currency_exchange_settings.py @@ -0,0 +1,22 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests import IntegrationTestCase + + +# On IntegrationTestCase, the doctype test records and all +# link-field test record dependencies are recursively loaded +# Use these module variables to add/remove to/from that list +EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] +IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] + + + +class IntegrationTestCRMCurrencyExchangeSettings(IntegrationTestCase): + """ + Integration tests for CRMCurrencyExchangeSettings. + Use this class for testing interactions between multiple components. + """ + + pass From bdcd7c5487576e66ef2a970c1bcf501d5f661dce Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 28 Jul 2025 13:00:31 +0530 Subject: [PATCH 03/16] fix: moved get_exchange_rate api to exchange rate settings (cherry picked from commit 7e9d9a5fed5852f72f69ec76160c3d757fb52372) --- .../crm_currency_exchange_settings.py | 47 ++++++++++++++++++- crm/fcrm/doctype/crm_deal/crm_deal.py | 2 +- .../crm_organization/crm_organization.py | 2 +- crm/utils/__init__.py | 21 --------- 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py index aafc54e2..da4aadd6 100644 --- a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py +++ b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py @@ -1,10 +1,55 @@ # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe +import frappe +import requests +from frappe import _ from frappe.model.document import Document class CRMCurrencyExchangeSettings(Document): pass + +def get_exchange_rate(from_currency, to_currency, date=None): + if not date: + date = "latest" + + api_endpoint = f"https://api.frankfurter.app/{date}?from={from_currency}&to={to_currency}" + res = requests.get(api_endpoint, timeout=5) + if res.ok: + data = res.json() + return data["rates"][to_currency] + + # Fallback to exchangerate.host if Frankfurter API fails + ces = frappe.get_single("CRM Currency Exchange Settings") + if ces and ces.service_provider == "exchangerate.host": + if not ces.access_key: + frappe.throw( + _("Access Key is required for Service Provider: {0}").format( + frappe.bold(ces.service_provider) + ) + ) + + params = { + "access_key": ces.access_key, + "from": from_currency, + "to": to_currency, + "amount": 1, + } + + if date != "latest": + params["date"] = date + + api_endpoint = "https://api.exchangerate.host/convert" + + res = requests.get(api_endpoint, params=params, timeout=5) + if res.ok: + data = res.json() + return data["result"] + + frappe.log_error( + title="Exchange Rate Fetch Error", + message=f"Failed to fetch exchange rate from {from_currency} to {to_currency} on {date}", + ) + return 1.0 # Default exchange rate if API call fails or no rate found diff --git a/crm/fcrm/doctype/crm_deal/crm_deal.py b/crm/fcrm/doctype/crm_deal/crm_deal.py index 4b9bc95d..a682d4ba 100644 --- a/crm/fcrm/doctype/crm_deal/crm_deal.py +++ b/crm/fcrm/doctype/crm_deal/crm_deal.py @@ -6,11 +6,11 @@ from frappe import _ from frappe.desk.form.assign_to import add as assign from frappe.model.document import Document +from crm.fcrm.doctype.crm_currency_exchange_settings.crm_currency_exchange_settings import get_exchange_rate from crm.fcrm.doctype.crm_service_level_agreement.utils import get_sla from crm.fcrm.doctype.crm_status_change_log.crm_status_change_log import ( add_status_change_log, ) -from crm.utils import get_exchange_rate class CRMDeal(Document): diff --git a/crm/fcrm/doctype/crm_organization/crm_organization.py b/crm/fcrm/doctype/crm_organization/crm_organization.py index e528435c..6c736eee 100644 --- a/crm/fcrm/doctype/crm_organization/crm_organization.py +++ b/crm/fcrm/doctype/crm_organization/crm_organization.py @@ -4,7 +4,7 @@ import frappe from frappe.model.document import Document -from crm.utils import get_exchange_rate +from crm.fcrm.doctype.crm_currency_exchange_settings.crm_currency_exchange_settings import get_exchange_rate class CRMOrganization(Document): diff --git a/crm/utils/__init__.py b/crm/utils/__init__.py index 0e4582df..92ee37d7 100644 --- a/crm/utils/__init__.py +++ b/crm/utils/__init__.py @@ -267,24 +267,3 @@ def sales_user_only(fn): return fn(*args, **kwargs) return wrapper - - -def get_exchange_rate(from_currency, to_currency, date=None): - if not date: - date = "latest" - - url = f"https://api.frankfurter.app/{date}?from={from_currency}&to={to_currency}" - - for _i in range(3): - response = requests.get(url) - if response.status_code == 200: - data = response.json() - rate = data["rates"].get(to_currency) - if rate: - return rate - - frappe.log_error( - title="Exchange Rate Fetch Error", - message=f"Failed to fetch exchange rate from {from_currency} to {to_currency} on {date}", - ) - return 1.0 # Default exchange rate if API call fails or no rate found From c9b4bf801dc469f54c35595248a40b9cb747625a Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 28 Jul 2025 16:19:49 +0530 Subject: [PATCH 04/16] feat: enhance document handling with error triggering and settings helpers via app (cherry picked from commit 23a823f2bbca6c21ef32ec55d10339b86c5a1f57) --- frontend/src/data/document.js | 24 ++++++++++++++++++++++++ frontend/src/data/script.js | 6 +----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/frontend/src/data/document.js b/frontend/src/data/document.js index 44af0a3d..b1d68006 100644 --- a/frontend/src/data/document.js +++ b/frontend/src/data/document.js @@ -1,4 +1,6 @@ import { getScript } from '@/data/script' +import { globalStore } from '@/stores/global' +import { showSettings, activeSettingsPage } from '@/composables/settings' import { runSequentially, parseAssignees } from '@/utils' import { createDocumentResource, createResource, toast } from 'frappe-ui' import { reactive } from 'vue' @@ -24,6 +26,7 @@ export function useDocument(doctype, docname) { toast.success(__('Document updated successfully')) }, onError: (err) => { + triggerOnError(err) let errorMessage = __('Error updating document') if (err.exc_type == 'MandatoryError') { const fieldName = err.messages @@ -76,8 +79,21 @@ export function useDocument(doctype, docname) { controllersCache[doctype][docname || ''] = {} + const { makeCall } = globalStore() + + let helpers = {} + + helpers.crm = { + makePhoneCall: makeCall, + openSettings: (page) => { + showSettings.value = true + activeSettingsPage.value = page + }, + } + const controllersArray = await setupScript( documentsCache[doctype][docname || ''], + helpers, ) if (!controllersArray || controllersArray.length === 0) return @@ -133,6 +149,13 @@ export function useDocument(doctype, docname) { await trigger(handler) } + async function triggerOnError() { + const handler = async function () { + await (this.onError?.() || this.on_error?.()) + } + await trigger(handler) + } + async function triggerOnRefresh() { const handler = async function () { await this.refresh?.() @@ -234,6 +257,7 @@ export function useDocument(doctype, docname) { triggerOnLoad, triggerOnBeforeCreate, triggerOnSave, + triggerOnError, triggerOnRefresh, triggerOnChange, triggerOnRowAdd, diff --git a/frontend/src/data/script.js b/frontend/src/data/script.js index 47185bab..95225590 100644 --- a/frontend/src/data/script.js +++ b/frontend/src/data/script.js @@ -38,7 +38,7 @@ export function getScript(doctype, view = 'Form') { let scriptDefs = doctypeScripts[doctype] if (!scriptDefs || Object.keys(scriptDefs).length === 0) return null - const { $dialog, $socket, makeCall } = globalStore() + const { $dialog, $socket } = globalStore() helpers.createDialog = $dialog helpers.toast = toast @@ -51,10 +51,6 @@ export function getScript(doctype, view = 'Form') { throw new Error(message || __('An error occurred')) } - helpers.crm = { - makePhoneCall: makeCall, - } - return setupMultipleFormControllers(scriptDefs, document, helpers) } From cab54be0abbecd5095ad35d91e1b7a9845039ece Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 28 Jul 2025 16:20:08 +0530 Subject: [PATCH 05/16] fix: update exchange rate fetching logic to include service provider context and improve error handling (cherry picked from commit b46d5a4e5ebaf565fb998262bc5391b8bb7630b6) --- .../crm_currency_exchange_settings.py | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py index da4aadd6..1224e7ac 100644 --- a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py +++ b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py @@ -15,6 +15,8 @@ def get_exchange_rate(from_currency, to_currency, date=None): if not date: date = "latest" + api_used = "frankfurter" + api_endpoint = f"https://api.frankfurter.app/{date}?from={from_currency}&to={to_currency}" res = requests.get(api_endpoint, timeout=5) if res.ok: @@ -24,6 +26,7 @@ def get_exchange_rate(from_currency, to_currency, date=None): # Fallback to exchangerate.host if Frankfurter API fails ces = frappe.get_single("CRM Currency Exchange Settings") if ces and ces.service_provider == "exchangerate.host": + api_used = "exchangerate.host" if not ces.access_key: frappe.throw( _("Access Key is required for Service Provider: {0}").format( @@ -50,6 +53,32 @@ def get_exchange_rate(from_currency, to_currency, date=None): frappe.log_error( title="Exchange Rate Fetch Error", - message=f"Failed to fetch exchange rate from {from_currency} to {to_currency} on {date}", + message=f"Failed to fetch exchange rate from {from_currency} to {to_currency} using {api_used} API.", + ) + + if api_used == "frankfurter": + user = frappe.session.user + is_manager = ( + "System Manager" in frappe.get_roles(user) + or "Sales Manager" in frappe.get_roles(user) + or user == "Administrator" + ) + + if not is_manager: + frappe.throw( + _( + "Ask your manager to set up the Currency Exchange Provider, as default provider does not support currency conversion for {0} to {1}." + ).format(from_currency, to_currency) + ) + else: + frappe.throw( + _( + "Setup the Currency Exchange Provider as 'exchangerate.host' in settings, as default provider does not support currency conversion for {0} to {1}." + ).format(from_currency, to_currency) + ) + + frappe.throw( + _( + "Failed to fetch exchange rate from {0} to {1} on {2}. Please check your internet connection or try again later." + ).format(from_currency, to_currency, date) ) - return 1.0 # Default exchange rate if API call fails or no rate found From 1f51a97f04a066c6a94d1231c2e2ef1ff7d7f3b2 Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Sat, 26 Jul 2025 12:04:08 +0530 Subject: [PATCH 06/16] chore: Serbian (Cyrillic) translations (cherry picked from commit 3b9ff8d58f6610b9a012fc70093d5a06591e0623) # Conflicts: # crm/locale/sr.po --- crm/locale/sr.po | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crm/locale/sr.po b/crm/locale/sr.po index 18a8f51c..7ba89b24 100644 --- a/crm/locale/sr.po +++ b/crm/locale/sr.po @@ -3,7 +3,11 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: shariq@frappe.io\n" "POT-Creation-Date: 2025-07-20 09:37+0000\n" +<<<<<<< HEAD "PO-Revision-Date: 2025-07-28 07:01\n" +======= +"PO-Revision-Date: 2025-07-26 06:34\n" +>>>>>>> 3b9ff8d5 (chore: Serbian (Cyrillic) translations) "Last-Translator: shariq@frappe.io\n" "Language-Team: Serbian (Cyrillic)\n" "MIME-Version: 1.0\n" From 65366f3430a4155796633de1b6f3a52465d07953 Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Sat, 26 Jul 2025 12:04:14 +0530 Subject: [PATCH 07/16] chore: Serbian (Latin) translations (cherry picked from commit ee843fae26a1ad197972c2802b477921cac4f5c4) # Conflicts: # crm/locale/sr_CS.po --- crm/locale/sr_CS.po | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crm/locale/sr_CS.po b/crm/locale/sr_CS.po index 4df27de8..ba97cc91 100644 --- a/crm/locale/sr_CS.po +++ b/crm/locale/sr_CS.po @@ -3,7 +3,11 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: shariq@frappe.io\n" "POT-Creation-Date: 2025-07-20 09:37+0000\n" +<<<<<<< HEAD "PO-Revision-Date: 2025-07-28 07:01\n" +======= +"PO-Revision-Date: 2025-07-26 06:34\n" +>>>>>>> ee843fae (chore: Serbian (Latin) translations) "Last-Translator: shariq@frappe.io\n" "Language-Team: Serbian (Latin)\n" "MIME-Version: 1.0\n" @@ -4385,37 +4389,65 @@ msgstr "Podešavanje" #: frontend/src/components/Settings/emailConfig.js:145 msgid "Setting up Frappe Mail requires you to have an API key and API Secret of your email account. Read more " +<<<<<<< HEAD msgstr "Podešavanje Frappe Mail zahteva da imate API ključ i API tajnu za svoj imejl nalog. Saznajte više " +======= +msgstr "Podešavanje Frappe Mail zahteva da imate API ključ i API tajnu svoj imejl naloga. Saznajte više " +>>>>>>> ee843fae (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:97 msgid "Setting up GMail requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more" +<<<<<<< HEAD msgstr "Podešavanje Gmail-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više" +======= +msgstr "Podešavanje Gmail-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više" +>>>>>>> ee843fae (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:105 msgid "Setting up Outlook requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more" +<<<<<<< HEAD msgstr "Podešavanje Outlook-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više" +======= +msgstr "Podešavanje Outlook-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više" +>>>>>>> ee843fae (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:113 msgid "Setting up Sendgrid requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more " +<<<<<<< HEAD msgstr "Podešavanje Sendgrid-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +======= +msgstr "Podešavanje Sendgrid-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +>>>>>>> ee843fae (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:121 msgid "Setting up SparkPost requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more " +<<<<<<< HEAD msgstr "Podešavanje SparkPost-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +======= +msgstr "Podešavanje SparkPost-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +>>>>>>> ee843fae (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:129 msgid "Setting up Yahoo requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more " +<<<<<<< HEAD msgstr "Podešavanje Yahoo-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +======= +msgstr "Podešavanje Yahoo-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +>>>>>>> ee843fae (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:137 msgid "Setting up Yandex requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more " +<<<<<<< HEAD msgstr "Podešavanje Yandex-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +======= +msgstr "Podešavanje Yandex-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +>>>>>>> ee843fae (chore: Serbian (Latin) translations) #. Label of the defaults_tab (Tab Break) field in DocType 'FCRM Settings' #: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json From 5e1ae83b72541df5f4e1d090a7a738f3a03892ed Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Mon, 28 Jul 2025 12:31:03 +0530 Subject: [PATCH 08/16] chore: Serbian (Cyrillic) translations (cherry picked from commit 25d2b1889f6cc3a5f37ca8b4f0047723861c9189) # Conflicts: # crm/locale/sr.po --- crm/locale/sr.po | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crm/locale/sr.po b/crm/locale/sr.po index 7ba89b24..6108ee99 100644 --- a/crm/locale/sr.po +++ b/crm/locale/sr.po @@ -4,10 +4,14 @@ msgstr "" "Report-Msgid-Bugs-To: shariq@frappe.io\n" "POT-Creation-Date: 2025-07-20 09:37+0000\n" <<<<<<< HEAD +<<<<<<< HEAD "PO-Revision-Date: 2025-07-28 07:01\n" ======= "PO-Revision-Date: 2025-07-26 06:34\n" >>>>>>> 3b9ff8d5 (chore: Serbian (Cyrillic) translations) +======= +"PO-Revision-Date: 2025-07-28 07:01\n" +>>>>>>> 25d2b188 (chore: Serbian (Cyrillic) translations) "Last-Translator: shariq@frappe.io\n" "Language-Team: Serbian (Cyrillic)\n" "MIME-Version: 1.0\n" From 1c4dd8a59d3f4a1bc16542b254ad9be385cb5cbc Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Mon, 28 Jul 2025 12:31:04 +0530 Subject: [PATCH 09/16] chore: Serbian (Latin) translations (cherry picked from commit 2a6f1c402b1332a6f8cafed76e8e906486758df5) # Conflicts: # crm/locale/sr_CS.po --- crm/locale/sr_CS.po | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crm/locale/sr_CS.po b/crm/locale/sr_CS.po index ba97cc91..80b0b0ef 100644 --- a/crm/locale/sr_CS.po +++ b/crm/locale/sr_CS.po @@ -4,10 +4,14 @@ msgstr "" "Report-Msgid-Bugs-To: shariq@frappe.io\n" "POT-Creation-Date: 2025-07-20 09:37+0000\n" <<<<<<< HEAD +<<<<<<< HEAD "PO-Revision-Date: 2025-07-28 07:01\n" ======= "PO-Revision-Date: 2025-07-26 06:34\n" >>>>>>> ee843fae (chore: Serbian (Latin) translations) +======= +"PO-Revision-Date: 2025-07-28 07:01\n" +>>>>>>> 2a6f1c40 (chore: Serbian (Latin) translations) "Last-Translator: shariq@frappe.io\n" "Language-Team: Serbian (Latin)\n" "MIME-Version: 1.0\n" @@ -4390,64 +4394,92 @@ msgstr "Podešavanje" #: frontend/src/components/Settings/emailConfig.js:145 msgid "Setting up Frappe Mail requires you to have an API key and API Secret of your email account. Read more " <<<<<<< HEAD +<<<<<<< HEAD msgstr "Podešavanje Frappe Mail zahteva da imate API ključ i API tajnu za svoj imejl nalog. Saznajte više " ======= msgstr "Podešavanje Frappe Mail zahteva da imate API ključ i API tajnu svoj imejl naloga. Saznajte više " >>>>>>> ee843fae (chore: Serbian (Latin) translations) +======= +msgstr "Podešavanje Frappe Mail zahteva da imate API ključ i API tajnu za svoj imejl nalog. Saznajte više " +>>>>>>> 2a6f1c40 (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:97 msgid "Setting up GMail requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more" <<<<<<< HEAD +<<<<<<< HEAD msgstr "Podešavanje Gmail-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više" ======= msgstr "Podešavanje Gmail-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više" >>>>>>> ee843fae (chore: Serbian (Latin) translations) +======= +msgstr "Podešavanje Gmail-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više" +>>>>>>> 2a6f1c40 (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:105 msgid "Setting up Outlook requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more" <<<<<<< HEAD +<<<<<<< HEAD msgstr "Podešavanje Outlook-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više" ======= msgstr "Podešavanje Outlook-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više" >>>>>>> ee843fae (chore: Serbian (Latin) translations) +======= +msgstr "Podešavanje Outlook-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više" +>>>>>>> 2a6f1c40 (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:113 msgid "Setting up Sendgrid requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more " <<<<<<< HEAD +<<<<<<< HEAD msgstr "Podešavanje Sendgrid-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " ======= msgstr "Podešavanje Sendgrid-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više " >>>>>>> ee843fae (chore: Serbian (Latin) translations) +======= +msgstr "Podešavanje Sendgrid-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +>>>>>>> 2a6f1c40 (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:121 msgid "Setting up SparkPost requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more " <<<<<<< HEAD +<<<<<<< HEAD msgstr "Podešavanje SparkPost-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " ======= msgstr "Podešavanje SparkPost-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više " >>>>>>> ee843fae (chore: Serbian (Latin) translations) +======= +msgstr "Podešavanje SparkPost-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +>>>>>>> 2a6f1c40 (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:129 msgid "Setting up Yahoo requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more " <<<<<<< HEAD +<<<<<<< HEAD msgstr "Podešavanje Yahoo-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " ======= msgstr "Podešavanje Yahoo-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više " >>>>>>> ee843fae (chore: Serbian (Latin) translations) +======= +msgstr "Podešavanje Yahoo-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +>>>>>>> 2a6f1c40 (chore: Serbian (Latin) translations) #: frontend/src/components/Settings/emailConfig.js:137 msgid "Setting up Yandex requires you to enable two factor authentication\n" "\t\t and app specific passwords. Read more " <<<<<<< HEAD +<<<<<<< HEAD msgstr "Podešavanje Yandex-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " ======= msgstr "Podešavanje Yandex-a zahteva uključivanje dvofakturske autentifikacije i lozinke za specifične aplikacije. Saznajte više " >>>>>>> ee843fae (chore: Serbian (Latin) translations) +======= +msgstr "Podešavanje Yandex-a zahteva uključivanje dvofaktorske autentifikacije i lozinke za specifične aplikacije. Saznajte više " +>>>>>>> 2a6f1c40 (chore: Serbian (Latin) translations) #. Label of the defaults_tab (Tab Break) field in DocType 'FCRM Settings' #: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json From 53781cbf1e0b0b06a1829b929d132c2013aac8d0 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 28 Jul 2025 16:43:46 +0530 Subject: [PATCH 10/16] fix: Improve error handling in document update process (cherry picked from commit e578513eaf9a470271d11a69f151b29459fc4ff8) --- frontend/src/data/document.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/src/data/document.js b/frontend/src/data/document.js index b1d68006..80d95255 100644 --- a/frontend/src/data/document.js +++ b/frontend/src/data/document.js @@ -27,7 +27,7 @@ export function useDocument(doctype, docname) { }, onError: (err) => { triggerOnError(err) - let errorMessage = __('Error updating document') + if (err.exc_type == 'MandatoryError') { const fieldName = err.messages .map((msg) => { @@ -35,9 +35,18 @@ export function useDocument(doctype, docname) { return arr[arr.length - 1].trim() }) .join(', ') - errorMessage = __('Mandatory field error: {0}', [fieldName]) + toast.error(__('Mandatory field error: {0}', [fieldName])) + return } - toast.error(errorMessage) + + err.messages?.forEach((msg) => { + toast.error(msg) + }) + + if (err.messages?.length === 0) { + toast.error(__('An error occurred while updating the document')) + } + console.error(err) }, }, From c4df4d32538637ac3b11c98f05a2a3b7943bec62 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 28 Jul 2025 17:40:22 +0530 Subject: [PATCH 11/16] fix: removed currency exchange rate settings and moved it to crm settings (cherry picked from commit 5bee7022b22395b1da2e0de90746cff12e298748) --- .../__init__.py | 0 .../crm_currency_exchange_settings.js | 8 -- .../crm_currency_exchange_settings.json | 54 ------------ .../crm_currency_exchange_settings.py | 84 ------------------- .../test_crm_currency_exchange_settings.py | 22 ----- crm/fcrm/doctype/crm_deal/crm_deal.py | 6 +- .../crm_organization/crm_organization.py | 2 +- .../doctype/fcrm_settings/fcrm_settings.json | 38 ++++++++- .../doctype/fcrm_settings/fcrm_settings.py | 74 ++++++++++++++++ 9 files changed, 113 insertions(+), 175 deletions(-) delete mode 100644 crm/fcrm/doctype/crm_currency_exchange_settings/__init__.py delete mode 100644 crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.js delete mode 100644 crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.json delete mode 100644 crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py delete mode 100644 crm/fcrm/doctype/crm_currency_exchange_settings/test_crm_currency_exchange_settings.py diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/__init__.py b/crm/fcrm/doctype/crm_currency_exchange_settings/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.js b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.js deleted file mode 100644 index bda41c07..00000000 --- a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -// frappe.ui.form.on("CRM Currency Exchange Settings", { -// refresh(frm) { - -// }, -// }); diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.json b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.json deleted file mode 100644 index 2faeb78b..00000000 --- a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "creation": "2025-07-28 12:29:02.935286", - "doctype": "DocType", - "engine": "InnoDB", - "field_order": [ - "service_provider", - "access_key" - ], - "fields": [ - { - "default": "frankfurter.app", - "fieldname": "service_provider", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Service Provider", - "options": "frankfurter.app\nexchangerate.host", - "reqd": 1 - }, - { - "depends_on": "eval:doc.service_provider == 'exchangerate.host';", - "fieldname": "access_key", - "fieldtype": "Data", - "label": "Access Key", - "mandatory_depends_on": "eval:doc.service_provider == 'exchangerate.host';" - } - ], - "grid_page_length": 50, - "index_web_pages_for_search": 1, - "issingle": 1, - "links": [], - "modified": "2025-07-28 12:30:08.080590", - "modified_by": "Administrator", - "module": "FCRM", - "name": "CRM Currency Exchange Settings", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "row_format": "Dynamic", - "sort_field": "creation", - "sort_order": "DESC", - "states": [] -} diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py b/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py deleted file mode 100644 index 1224e7ac..00000000 --- a/crm/fcrm/doctype/crm_currency_exchange_settings/crm_currency_exchange_settings.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -import frappe -import requests -from frappe import _ -from frappe.model.document import Document - - -class CRMCurrencyExchangeSettings(Document): - pass - - -def get_exchange_rate(from_currency, to_currency, date=None): - if not date: - date = "latest" - - api_used = "frankfurter" - - api_endpoint = f"https://api.frankfurter.app/{date}?from={from_currency}&to={to_currency}" - res = requests.get(api_endpoint, timeout=5) - if res.ok: - data = res.json() - return data["rates"][to_currency] - - # Fallback to exchangerate.host if Frankfurter API fails - ces = frappe.get_single("CRM Currency Exchange Settings") - if ces and ces.service_provider == "exchangerate.host": - api_used = "exchangerate.host" - if not ces.access_key: - frappe.throw( - _("Access Key is required for Service Provider: {0}").format( - frappe.bold(ces.service_provider) - ) - ) - - params = { - "access_key": ces.access_key, - "from": from_currency, - "to": to_currency, - "amount": 1, - } - - if date != "latest": - params["date"] = date - - api_endpoint = "https://api.exchangerate.host/convert" - - res = requests.get(api_endpoint, params=params, timeout=5) - if res.ok: - data = res.json() - return data["result"] - - frappe.log_error( - title="Exchange Rate Fetch Error", - message=f"Failed to fetch exchange rate from {from_currency} to {to_currency} using {api_used} API.", - ) - - if api_used == "frankfurter": - user = frappe.session.user - is_manager = ( - "System Manager" in frappe.get_roles(user) - or "Sales Manager" in frappe.get_roles(user) - or user == "Administrator" - ) - - if not is_manager: - frappe.throw( - _( - "Ask your manager to set up the Currency Exchange Provider, as default provider does not support currency conversion for {0} to {1}." - ).format(from_currency, to_currency) - ) - else: - frappe.throw( - _( - "Setup the Currency Exchange Provider as 'exchangerate.host' in settings, as default provider does not support currency conversion for {0} to {1}." - ).format(from_currency, to_currency) - ) - - frappe.throw( - _( - "Failed to fetch exchange rate from {0} to {1} on {2}. Please check your internet connection or try again later." - ).format(from_currency, to_currency, date) - ) diff --git a/crm/fcrm/doctype/crm_currency_exchange_settings/test_crm_currency_exchange_settings.py b/crm/fcrm/doctype/crm_currency_exchange_settings/test_crm_currency_exchange_settings.py deleted file mode 100644 index ec05d679..00000000 --- a/crm/fcrm/doctype/crm_currency_exchange_settings/test_crm_currency_exchange_settings.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -# import frappe -from frappe.tests import IntegrationTestCase - - -# On IntegrationTestCase, the doctype test records and all -# link-field test record dependencies are recursively loaded -# Use these module variables to add/remove to/from that list -EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] -IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] - - - -class IntegrationTestCRMCurrencyExchangeSettings(IntegrationTestCase): - """ - Integration tests for CRMCurrencyExchangeSettings. - Use this class for testing interactions between multiple components. - """ - - pass diff --git a/crm/fcrm/doctype/crm_deal/crm_deal.py b/crm/fcrm/doctype/crm_deal/crm_deal.py index a682d4ba..6bc2e020 100644 --- a/crm/fcrm/doctype/crm_deal/crm_deal.py +++ b/crm/fcrm/doctype/crm_deal/crm_deal.py @@ -6,11 +6,9 @@ from frappe import _ from frappe.desk.form.assign_to import add as assign from frappe.model.document import Document -from crm.fcrm.doctype.crm_currency_exchange_settings.crm_currency_exchange_settings import get_exchange_rate from crm.fcrm.doctype.crm_service_level_agreement.utils import get_sla -from crm.fcrm.doctype.crm_status_change_log.crm_status_change_log import ( - add_status_change_log, -) +from crm.fcrm.doctype.crm_status_change_log.crm_status_change_log import add_status_change_log +from crm.fcrm.doctype.fcrm_settings.fcrm_settings import get_exchange_rate class CRMDeal(Document): diff --git a/crm/fcrm/doctype/crm_organization/crm_organization.py b/crm/fcrm/doctype/crm_organization/crm_organization.py index 6c736eee..a8247627 100644 --- a/crm/fcrm/doctype/crm_organization/crm_organization.py +++ b/crm/fcrm/doctype/crm_organization/crm_organization.py @@ -4,7 +4,7 @@ import frappe from frappe.model.document import Document -from crm.fcrm.doctype.crm_currency_exchange_settings.crm_currency_exchange_settings import get_exchange_rate +from crm.fcrm.doctype.fcrm_settings.fcrm_settings import get_exchange_rate class CRMOrganization(Document): diff --git a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json index 635c02d3..0e04b4d1 100644 --- a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json +++ b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json @@ -8,7 +8,12 @@ "defaults_tab", "restore_defaults", "enable_forecasting", + "currency_tab", "currency", + "exchange_rate_provider_section", + "service_provider", + "column_break_vqck", + "access_key", "branding_tab", "brand_name", "brand_logo", @@ -72,13 +77,42 @@ "in_list_view": 1, "label": "Currency", "options": "Currency" + }, + { + "fieldname": "currency_tab", + "fieldtype": "Tab Break", + "label": "Currency" + }, + { + "fieldname": "exchange_rate_provider_section", + "fieldtype": "Section Break", + "label": "Exchange Rate Provider" + }, + { + "default": "frankfurter.app", + "fieldname": "service_provider", + "fieldtype": "Select", + "label": "Service Provider", + "options": "frankfurter.app\nexchangerate.host", + "reqd": 1 + }, + { + "depends_on": "eval:doc.service_provider == 'exchangerate.host';", + "fieldname": "access_key", + "fieldtype": "Data", + "label": "Access Key", + "mandatory_depends_on": "eval:doc.service_provider == 'exchangerate.host';" + }, + { + "fieldname": "column_break_vqck", + "fieldtype": "Column Break" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2025-07-13 11:58:34.857638", - "modified_by": "Administrator", + "modified": "2025-07-28 17:04:24.585768", + "modified_by": "shariq@frappe.io", "module": "FCRM", "name": "FCRM Settings", "owner": "Administrator", diff --git a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.py b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.py index 7897bb1a..3c27695f 100644 --- a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.py +++ b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.py @@ -2,6 +2,7 @@ # For license information, please see license.txt import frappe +import requests from frappe import _ from frappe.custom.doctype.property_setter.property_setter import delete_property_setter, make_property_setter from frappe.model.document import Document @@ -132,3 +133,76 @@ def get_forecasting_script(): this.doc.probability = status.probability } }""" + + +def get_exchange_rate(from_currency, to_currency, date=None): + if not date: + date = "latest" + + api_used = "frankfurter" + + api_endpoint = f"https://api.frankfurter.app/{date}?from={from_currency}&to={to_currency}" + res = requests.get(api_endpoint, timeout=5) + if res.ok: + data = res.json() + return data["rates"][to_currency] + + # Fallback to exchangerate.host if Frankfurter API fails + settings = FCRMSettings("FCRM Settings") + if settings and settings.service_provider == "exchangerate.host": + api_used = "exchangerate.host" + if not settings.access_key: + frappe.throw( + _("Access Key is required for Service Provider: {0}").format( + frappe.bold(settings.service_provider) + ) + ) + + params = { + "access_key": settings.access_key, + "from": from_currency, + "to": to_currency, + "amount": 1, + } + + if date != "latest": + params["date"] = date + + api_endpoint = "https://api.exchangerate.host/convert" + + res = requests.get(api_endpoint, params=params, timeout=5) + if res.ok: + data = res.json() + return data["result"] + + frappe.log_error( + title="Exchange Rate Fetch Error", + message=f"Failed to fetch exchange rate from {from_currency} to {to_currency} using {api_used} API.", + ) + + if api_used == "frankfurter": + user = frappe.session.user + is_manager = ( + "System Manager" in frappe.get_roles(user) + or "Sales Manager" in frappe.get_roles(user) + or user == "Administrator" + ) + + if not is_manager: + frappe.throw( + _( + "Ask your manager to set up the Exchange Rate Provider, as default provider does not support currency conversion for {0} to {1}." + ).format(from_currency, to_currency) + ) + else: + frappe.throw( + _( + "Setup the Exchange Rate Provider as 'Exchangerate Host' in settings, as default provider does not support currency conversion for {0} to {1}." + ).format(from_currency, to_currency) + ) + + frappe.throw( + _( + "Failed to fetch exchange rate from {0} to {1} on {2}. Please check your internet connection or try again later." + ).format(from_currency, to_currency, date) + ) From 14a9571fdce5de2806e8e4b55617bf92eaf3f6b4 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 28 Jul 2025 17:48:26 +0530 Subject: [PATCH 12/16] fix: added exchange rate provider & currency together in settings modal (cherry picked from commit 36e79e49da2091dfca166f1c50452285577d7584) --- frontend/components.d.ts | 1 + .../Settings/General/CurrencySettings.vue | 197 ++++++++++++++++++ .../Settings/General/GeneralSettings.vue | 66 +----- .../Settings/General/GeneralSettingsPage.vue | 3 + 4 files changed, 207 insertions(+), 60 deletions(-) create mode 100644 frontend/src/components/Settings/General/CurrencySettings.vue diff --git a/frontend/components.d.ts b/frontend/components.d.ts index c1dd22b6..afedd3ed 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -62,6 +62,7 @@ declare module 'vue' { CountUpTimer: typeof import('./src/components/CountUpTimer.vue')['default'] CreateDocumentModal: typeof import('./src/components/Modals/CreateDocumentModal.vue')['default'] CRMLogo: typeof import('./src/components/Icons/CRMLogo.vue')['default'] + CurrencySettings: typeof import('./src/components/Settings/General/CurrencySettings.vue')['default'] CustomActions: typeof import('./src/components/CustomActions.vue')['default'] DashboardGrid: typeof import('./src/components/Dashboard/DashboardGrid.vue')['default'] DashboardIcon: typeof import('./src/components/Icons/DashboardIcon.vue')['default'] diff --git a/frontend/src/components/Settings/General/CurrencySettings.vue b/frontend/src/components/Settings/General/CurrencySettings.vue new file mode 100644 index 00000000..63699bd9 --- /dev/null +++ b/frontend/src/components/Settings/General/CurrencySettings.vue @@ -0,0 +1,197 @@ + + diff --git a/frontend/src/components/Settings/General/GeneralSettings.vue b/frontend/src/components/Settings/General/GeneralSettings.vue index 29523d5c..e533d9a3 100644 --- a/frontend/src/components/Settings/General/GeneralSettings.vue +++ b/frontend/src/components/Settings/General/GeneralSettings.vue @@ -35,37 +35,6 @@
-
-
-
- {{ __('Currency') }} -
-
- {{ - __( - 'CRM currency for all monetary values. Once set, cannot be edited.', - ) - }} -
-
-
-
- {{ settings.doc.currency }} -
- -
-
-