diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index ee6e3b9c61..3126326636 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -40,6 +40,8 @@ class CustomField(Document): frappe.throw(_("A field with the name '{}' already exists in doctype {}.").format(self.fieldname, self.dt)) def validate(self): + from frappe.custom.doctype.customize_form.customize_form import CustomizeForm + meta = frappe.get_meta(self.dt, cached=False) fieldnames = [df.fieldname for df in meta.get("fields")] @@ -49,7 +51,11 @@ class CustomField(Document): if self.insert_after and self.insert_after in fieldnames: self.idx = fieldnames.index(self.insert_after) + 1 - self._old_fieldtype = self.db_get('fieldtype') + old_fieldtype = self.db_get('fieldtype') + is_fieldtype_changed = (not self.is_new()) and (old_fieldtype != self.fieldtype) + + if is_fieldtype_changed and not CustomizeForm.allow_fieldtype_change(old_fieldtype, self.fieldtype): + frappe.throw(_("Fieldtype cannot be changed from {0} to {1}").format(old_fieldtype, self.fieldtype)) if not self.fieldname: frappe.throw(_("Fieldname not set for Custom Field")) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index c79c965aae..9f6996a660 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -401,22 +401,18 @@ class CustomizeForm(Document): return property_value def validate_fieldtype_change(self, df, old_value, new_value): - allowed = False - self.check_length_for_fieldtypes = [] - for allowed_changes in ALLOWED_FIELDTYPE_CHANGE: - if (old_value in allowed_changes and new_value in allowed_changes): - allowed = True - old_value_length = cint(frappe.db.type_map.get(old_value)[1]) - new_value_length = cint(frappe.db.type_map.get(new_value)[1]) - - # Ignore fieldtype check validation if new field type has unspecified maxlength - # Changes like DATA to TEXT, where new_value_lenth equals 0 will not be validated - if new_value_length and (old_value_length > new_value_length): - self.check_length_for_fieldtypes.append({'df': df, 'old_value': old_value}) - self.validate_fieldtype_length() - else: - self.flags.update_db = True - break + allowed = self.allow_fieldtype_change(old_value, new_value) + if allowed: + old_value_length = cint(frappe.db.type_map.get(old_value)[1]) + new_value_length = cint(frappe.db.type_map.get(new_value)[1]) + + # Ignore fieldtype check validation if new field type has unspecified maxlength + # Changes like DATA to TEXT, where new_value_lenth equals 0 will not be validated + if new_value_length and (old_value_length > new_value_length): + self.check_length_for_fieldtypes.append({'df': df, 'old_value': old_value}) + self.validate_fieldtype_length() + else: + self.flags.update_db = True if not allowed: frappe.throw(_("Fieldtype cannot be changed from {0} to {1} in row {2}").format(old_value, new_value, df.idx)) @@ -458,6 +454,14 @@ class CustomizeForm(Document): reset_customization(self.doc_type) self.fetch_to_customize() + @classmethod + def allow_fieldtype_change(self, old_type: str, new_type: str) -> bool: + """ allow type change, if both old_type and new_type are in same field group. + field groups are defined in ALLOWED_FIELDTYPE_CHANGE variables. + """ + in_field_group = lambda group: (old_type in group) and (new_type in group) + return any(map(in_field_group, ALLOWED_FIELDTYPE_CHANGE)) + def reset_customization(doctype): setters = frappe.get_all("Property Setter", filters={ 'doc_type': doctype,