diff --git a/frappe/custom/doctype/custom_field/custom_field.js b/frappe/custom/doctype/custom_field/custom_field.js
index fba19ca45e..be416cb49a 100644
--- a/frappe/custom/doctype/custom_field/custom_field.js
+++ b/frappe/custom/doctype/custom_field/custom_field.js
@@ -25,6 +25,27 @@ frappe.ui.form.on("Custom Field", {
frm.toggle_enable("dt", frm.doc.__islocal);
frm.trigger("dt");
frm.toggle_reqd("label", !frm.doc.fieldname);
+
+ if (frm.doc.is_system_generated) {
+ frm.dashboard.add_comment(
+ __(
+ "Warning: This field is system generated and may be overwritten by a future update. Modify it using {0} instead.",
+ [
+ frappe.utils.get_form_link(
+ "Customize Form",
+ "Customize Form",
+ true,
+ __("Customize Form"),
+ {
+ doc_type: frm.doc.dt,
+ }
+ ),
+ ]
+ ),
+ "yellow",
+ true
+ );
+ }
},
dt: function (frm) {
if (!frm.doc.dt) {
diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js
index 4ab693b415..dbcc9b17ce 100644
--- a/frappe/custom/doctype/customize_form/customize_form.js
+++ b/frappe/custom/doctype/customize_form/customize_form.js
@@ -251,10 +251,23 @@ frappe.ui.form.on("Customize Form", {
// can't delete standard fields
frappe.ui.form.on("Customize Form Field", {
before_fields_remove: function (frm, doctype, name) {
- var row = frappe.get_doc(doctype, name);
+ let row = frappe.get_doc(doctype, name);
+
+ if (row.is_system_generated) {
+ frappe.throw(
+ __(
+ "Cannot delete system generated field {0}. You can hide it instead.",
+ [__(row.label) || row.fieldname]
+ )
+ );
+ }
+
if (!(row.is_custom_field || row.__islocal)) {
- frappe.msgprint(__("Cannot delete standard field. You can hide it if you want"));
- throw "cannot delete standard field";
+ frappe.throw(
+ __("Cannot delete standard field {0}. You can hide it instead.", [
+ __(row.label) || row.fieldname,
+ ])
+ );
}
},
fields_add: function (frm, cdt, cdn) {
diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py
index bdd18cddfa..42cbf33f4f 100644
--- a/frappe/custom/doctype/customize_form/customize_form.py
+++ b/frappe/custom/doctype/customize_form/customize_form.py
@@ -193,8 +193,9 @@ class CustomizeForm(Document):
# docfield
for df in self.get("fields"):
meta_df = meta.get("fields", {"fieldname": df.fieldname})
- if not meta_df or meta_df[0].get("is_custom_field"):
+ if not meta_df or not is_standard_or_system_generated_field(meta_df[0]):
continue
+
self.set_property_setters_for_docfield(meta, df, meta_df)
# action and links
@@ -350,12 +351,14 @@ class CustomizeForm(Document):
def update_custom_fields(self):
for i, df in enumerate(self.get("fields")):
- if df.get("is_custom_field"):
- if not frappe.db.exists("Custom Field", {"dt": self.doc_type, "fieldname": df.fieldname}):
- self.add_custom_field(df, i)
- self.flags.update_db = True
- else:
- self.update_in_custom_field(df, i)
+ if is_standard_or_system_generated_field(df):
+ continue
+
+ if not frappe.db.exists("Custom Field", {"dt": self.doc_type, "fieldname": df.fieldname}):
+ self.add_custom_field(df, i)
+ self.flags.update_db = True
+ else:
+ self.update_in_custom_field(df, i)
self.delete_custom_fields()
@@ -380,7 +383,7 @@ class CustomizeForm(Document):
def update_in_custom_field(self, df, i):
meta = frappe.get_meta(self.doc_type)
meta_df = meta.get("fields", {"fieldname": df.fieldname})
- if not (meta_df and meta_df[0].get("is_custom_field")):
+ if not meta_df or is_standard_or_system_generated_field(meta_df[0]):
# not a custom field
return
@@ -416,7 +419,7 @@ class CustomizeForm(Document):
}
for fieldname in fields_to_remove:
df = meta.get("fields", {"fieldname": fieldname})[0]
- if df.get("is_custom_field"):
+ if not is_standard_or_system_generated_field(df):
frappe.delete_doc("Custom Field", df.name)
def make_property_setter(
@@ -561,6 +564,10 @@ def reset_customization(doctype):
frappe.clear_cache(doctype=doctype)
+def is_standard_or_system_generated_field(df):
+ return not df.get("is_custom_field") or df.get("is_system_generated")
+
+
doctype_properties = {
"search_fields": "Data",
"title_field": "Data",
diff --git a/frappe/custom/doctype/customize_form/test_customize_form.py b/frappe/custom/doctype/customize_form/test_customize_form.py
index 661652c74c..8d98dc4149 100644
--- a/frappe/custom/doctype/customize_form/test_customize_form.py
+++ b/frappe/custom/doctype/customize_form/test_customize_form.py
@@ -403,3 +403,25 @@ class TestCustomizeForm(FrappeTestCase):
with self.assertRaises(frappe.ValidationError):
d.run_method("save_customization")
+
+ def test_system_generated_fields(self):
+ doctype = "Event"
+ custom_field_name = "test_custom_field"
+
+ custom_field = frappe.get_doc("Custom Field", {"dt": doctype, "fieldname": custom_field_name})
+ custom_field.is_system_generated = 1
+ custom_field.save()
+
+ d = self.get_customize_form(doctype)
+ custom_field = d.getone("fields", {"fieldname": custom_field_name})
+ custom_field.description = "Test Description"
+ d.run_method("save_customization")
+
+ property_setter_filters = {
+ "doc_type": doctype,
+ "field_name": custom_field_name,
+ "property": "description",
+ }
+ self.assertEqual(
+ frappe.db.get_value("Property Setter", property_setter_filters, "value"), "Test Description"
+ )