From 41badb3d740cd9ef5192a9756022afe6d0e72750 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Wed, 1 Jul 2026 02:12:21 +0530 Subject: [PATCH 1/4] fix(crm_settings): skip allowed users check when frappe crm is installed locally --- .../crm/doctype/crm_settings/crm_settings.js | 33 +++++++++++++++++-- .../doctype/crm_settings/crm_settings.json | 4 +-- .../crm/doctype/crm_settings/crm_settings.py | 7 +++- erpnext/crm/frappe_crm_api.py | 12 ++++++- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.js b/erpnext/crm/doctype/crm_settings/crm_settings.js index 0fb695a3da4..ef71437be49 100644 --- a/erpnext/crm/doctype/crm_settings/crm_settings.js +++ b/erpnext/crm/doctype/crm_settings/crm_settings.js @@ -2,6 +2,35 @@ // For license information, please see license.txt frappe.ui.form.on("CRM Settings", { - // refresh: function(frm) { - // } + refresh: function (frm) { + const flag = frm.events.calculate_visiblity_flag(frm); + + frm.set_df_property("allowed_users", "hidden", !flag); + frm.set_df_property("allowed_users", "reqd", flag); + }, + + enable_frappe_crm_data_synchronization: function (frm) { + const flag = frm.events.calculate_visiblity_flag(frm); + + if (flag) { + frappe.show_alert( + __("Allowed Users is required for data synchronization from remote Frappe CRM site.") + ); + } + + /* + make allowed_users field visible and mandatory if enable_frappe_crm_data_synchronization + is set and crm app is not installed. + */ + + frm.set_df_property("allowed_users", "hidden", !flag); + frm.set_df_property("allowed_users", "reqd", flag); + }, + + calculate_visiblity_flag: function (frm) { + const crm_sync_enabled = frm.doc.enable_frappe_crm_data_synchronization; + const is_crm_installed = cint(frappe.utils.get_installed_apps().includes("crm")); + + return crm_sync_enabled && !is_crm_installed; + }, }); diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.json b/erpnext/crm/doctype/crm_settings/crm_settings.json index 236a2d8ef76..3fbd1ea208c 100644 --- a/erpnext/crm/doctype/crm_settings/crm_settings.json +++ b/erpnext/crm/doctype/crm_settings/crm_settings.json @@ -120,9 +120,9 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:doc.enable_frappe_crm_data_synchronization === 1;", "fieldname": "allowed_users", "fieldtype": "Table MultiSelect", + "hidden": 1, "label": "Allowed Users", "options": "Frappe CRM Allowed User", "permlevel": 1 @@ -140,7 +140,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2026-06-22 01:26:13.474915", + "modified": "2026-07-01 01:09:16.461470", "modified_by": "Administrator", "module": "CRM", "name": "CRM Settings", diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.py b/erpnext/crm/doctype/crm_settings/crm_settings.py index 5779d2d8e9e..81d0072c4bd 100644 --- a/erpnext/crm/doctype/crm_settings/crm_settings.py +++ b/erpnext/crm/doctype/crm_settings/crm_settings.py @@ -6,6 +6,8 @@ from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.model.document import Document +from erpnext.crm.frappe_crm_api import is_crm_installed + class CRMSettings(Document): # begin: auto-generated types @@ -46,13 +48,16 @@ class CRMSettings(Document): ) def validate_allowed_users(self): - if self.enable_frappe_crm_data_synchronization and not self.allowed_users: + if self.enable_frappe_crm_data_synchronization and not (is_crm_installed() or self.allowed_users): frappe.throw( _( "Please add at least one user on Allowed Users to allow Data Synchronization from Frappe CRM site." ) ) + if self.enable_frappe_crm_data_synchronization and is_crm_installed() and self.allowed_users: + frappe.throw(_("Allowed Users is not required as Frappe CRM is already installed on the site.")) + def before_save(self): self.clear_allowed_users() diff --git a/erpnext/crm/frappe_crm_api.py b/erpnext/crm/frappe_crm_api.py index bbb0b8e5215..2c170ee0233 100644 --- a/erpnext/crm/frappe_crm_api.py +++ b/erpnext/crm/frappe_crm_api.py @@ -152,7 +152,9 @@ def create_customer(customer_data: dict | None = None): for field in CUSTOMER_ALLOWED_FIELDS: if customer_data.get(field) is not None: customer.set(field, customer_data.get(field)) - customer.insert(ignore_permissions=True) + + # If CRM is installed on the site, User Permission cannot be ignored while saving Customer Records. + customer.insert(ignore_permissions=not is_crm_installed()) customer_name = customer.name except Exception: frappe.db.rollback() @@ -183,6 +185,10 @@ def validate_frappe_crm_sync(): _("Frappe CRM data synchronization is not enabled on ERPNext. Contact System Manager of ERPNext.") ) + # Skip allowed_users validation if CRM is installed on the site. + if is_crm_installed(): + return + allowed_users = [d.user for d in CRMSettings.allowed_users] if frappe.session.user not in allowed_users: @@ -192,3 +198,7 @@ def validate_frappe_crm_sync(): ), exc=frappe.PermissionError, ) + + +def is_crm_installed(): + return "crm" in frappe.get_installed_apps() From c86aa2d6fe96b7087bb78d201131f2933201e7d4 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Wed, 1 Jul 2026 03:52:08 +0530 Subject: [PATCH 2/4] feat(crm_settings): auto-update crm sync settings on frappe crm install and uninstall --- erpnext/crm/frappe_crm_api.py | 23 +++++++++++++++++++++++ erpnext/hooks.py | 3 +++ erpnext/setup/install.py | 16 ++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/erpnext/crm/frappe_crm_api.py b/erpnext/crm/frappe_crm_api.py index 2c170ee0233..11495fd0cfc 100644 --- a/erpnext/crm/frappe_crm_api.py +++ b/erpnext/crm/frappe_crm_api.py @@ -1,5 +1,6 @@ import json +import click import frappe from frappe import _ @@ -202,3 +203,25 @@ def validate_frappe_crm_sync(): def is_crm_installed(): return "crm" in frappe.get_installed_apps() + + +def remove_allowed_users_on_crm_install(): + CRMSettings = frappe.get_single("CRM Settings") + + if not CRMSettings.enable_frappe_crm_data_synchronization: + return + + CRMSettings.allowed_users = [] + CRMSettings.save() + click.secho("Removed Allowed Users from CRM Settings.") + + +def disable_frappe_crm_data_synchronization_on_crm_uninstall(): + CRMSettings = frappe.get_single("CRM Settings") + + if not CRMSettings.enable_frappe_crm_data_synchronization: + return + + CRMSettings.enable_frappe_crm_data_synchronization = 0 + CRMSettings.save() + click.secho("Enable Frappe CRM Data Synchronization on CRM Settings has been disabled.") diff --git a/erpnext/hooks.py b/erpnext/hooks.py index cc808075fe4..e783d9e0fc4 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -65,6 +65,9 @@ setup_wizard_stages = "erpnext.setup.setup_wizard.setup_wizard.get_setup_stages" after_install = "erpnext.setup.install.after_install" +after_app_install = "erpnext.setup.install.after_app_install" +after_app_uninstall = "erpnext.setup.install.after_app_uninstall" + boot_session = "erpnext.startup.boot.boot_session" notification_config = "erpnext.startup.notifications.get_notification_config" get_help_messages = "erpnext.utilities.activation.get_help_messages" diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 48dea538517..346b1834032 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -417,3 +417,19 @@ DEFAULT_ROLE_PROFILES = { "Purchase Manager", ], } + + +def after_app_install(app_name=None): + if app_name == "crm": + from erpnext.crm.frappe_crm_api import remove_allowed_users_on_crm_install + + remove_allowed_users_on_crm_install() + + +def after_app_uninstall(app_name=None): + if app_name == "crm": + from erpnext.crm.frappe_crm_api import disable_frappe_crm_data_synchronization_on_crm_uninstall + + disable_frappe_crm_data_synchronization_on_crm_uninstall() + + frappe.db.commit() # nosemgrep From 2de423e225e0331eed2a60a4ad2f34b4de1283e8 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Thu, 9 Jul 2026 12:23:19 +0530 Subject: [PATCH 3/4] fix(`frappe_crm_api`): handle failure for `after_app_install` and `after_app_uninstall` --- erpnext/crm/frappe_crm_api.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/erpnext/crm/frappe_crm_api.py b/erpnext/crm/frappe_crm_api.py index 11495fd0cfc..db837025783 100644 --- a/erpnext/crm/frappe_crm_api.py +++ b/erpnext/crm/frappe_crm_api.py @@ -206,22 +206,28 @@ def is_crm_installed(): def remove_allowed_users_on_crm_install(): - CRMSettings = frappe.get_single("CRM Settings") + try: + CRMSettings = frappe.get_single("CRM Settings") - if not CRMSettings.enable_frappe_crm_data_synchronization: - return + if not CRMSettings.enable_frappe_crm_data_synchronization: + return - CRMSettings.allowed_users = [] - CRMSettings.save() - click.secho("Removed Allowed Users from CRM Settings.") + CRMSettings.allowed_users = [] + CRMSettings.save() + click.secho("Removed 'Allowed Users' from CRM Settings.") + except Exception: + click.secho("'Allowed Users' from CRM Settings couldn't be cleared.") def disable_frappe_crm_data_synchronization_on_crm_uninstall(): - CRMSettings = frappe.get_single("CRM Settings") + try: + CRMSettings = frappe.get_single("CRM Settings") - if not CRMSettings.enable_frappe_crm_data_synchronization: - return + if not CRMSettings.enable_frappe_crm_data_synchronization: + return - CRMSettings.enable_frappe_crm_data_synchronization = 0 - CRMSettings.save() - click.secho("Enable Frappe CRM Data Synchronization on CRM Settings has been disabled.") + CRMSettings.enable_frappe_crm_data_synchronization = 0 + CRMSettings.save() + click.secho("'Enable Frappe CRM Data Synchronization' on CRM Settings has been disabled.") + except Exception: + click.secho("'Enable Frappe CRM Data Synchronization' on CRM Settings could not be disabled.") From 0f987d7135979f8671bd9490921b766a332ef333 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Thu, 9 Jul 2026 12:24:40 +0530 Subject: [PATCH 4/4] chore: patch to clear out allowed users on `crm_settings` if frappe crm is installed on the site --- erpnext/patches.txt | 1 + ...crm_settings_handle_allowed_users_for_frappe_crm.py | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 erpnext/patches/v16_0/crm_settings_handle_allowed_users_for_frappe_crm.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 80cf243d055..e3d0ea32b62 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -496,3 +496,4 @@ erpnext.patches.v16_0.backfill_pick_list_transferred_qty erpnext.patches.v16_0.create_shop_floor_roles erpnext.patches.v15_0.backfill_sla_link_filters_on_custom_field erpnext.patches.v15_0.backfill_sla_link_filters_on_docfield +erpnext.patches.v16_0.crm_settings_handle_allowed_users_for_frappe_crm \ No newline at end of file diff --git a/erpnext/patches/v16_0/crm_settings_handle_allowed_users_for_frappe_crm.py b/erpnext/patches/v16_0/crm_settings_handle_allowed_users_for_frappe_crm.py new file mode 100644 index 00000000000..166cd5c66f8 --- /dev/null +++ b/erpnext/patches/v16_0/crm_settings_handle_allowed_users_for_frappe_crm.py @@ -0,0 +1,10 @@ +import frappe + + +def execute(): + from erpnext.crm.frappe_crm_api import is_crm_installed, remove_allowed_users_on_crm_install + + if not is_crm_installed(): + return + + remove_allowed_users_on_crm_install()