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" + )