fix!: dont delete customizations when doctypes are deleted

If someone deletes doctype and restores it back all customization are
lost, there's no "real" reason to delete all these customization. They
are only ever active if the doctype is being used.

Explanations:

- Custom field: is used by meta when doctype meta is requested, if meta
  isn't requested then custom field is effectively inactive.
- Client script: loaded by meta when doctype is requested by desk. So
  inactive in deleted state.
- Property setter: loaded by meta, so inactive when doctype isn't
  present.
- Report: will break doctype isn't present, but user should delete them
  manually to avoid loss of "scripts" or anything special they might
  have done. Also report's doctype don't 100% indicate that it's based
  solely on that doctype.
This commit is contained in:
Ankush Menat 2022-05-27 19:17:12 +05:30
parent d193ea22d1
commit a0ecb912db
2 changed files with 52 additions and 7 deletions

View file

@ -1,10 +1,14 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
import random
import string
import unittest
from typing import Dict, List, Optional
from unittest.mock import patch
import frappe
from frappe.cache_manager import clear_doctype_cache
from frappe.core.doctype.doctype.doctype import (
CannotIndexedError,
DoctypeLinkError,
@ -15,8 +19,8 @@ from frappe.core.doctype.doctype.doctype import (
WrongOptionsDoctypeLinkError,
validate_links_table_fieldnames,
)
# test_records = frappe.get_test_records('DocType')
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.desk.form.load import getdoc
class TestDocType(unittest.TestCase):
@ -628,10 +632,55 @@ class TestDocType(unittest.TestCase):
self.assertEqual(test_json.test_json_field["hello"], "world")
@patch.dict(frappe.conf, {"developer_mode": 1})
def test_delete_doctype_with_customization(self):
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
custom_field = "customfield"
doctype = new_doctype(custom=0).insert().name
# Create property setter and custom field
field = "some_fieldname"
make_property_setter(doctype, field, "default", "DELETETHIS", "Data")
create_custom_fields({doctype: [{"fieldname": custom_field, "fieldtype": "Data"}]})
# Create 1 record
original_doc = frappe.get_doc(doctype=doctype, custom_field_name="wat").insert()
self.assertEqual(original_doc.some_fieldname, "DELETETHIS")
# delete doctype
frappe.delete_doc("DocType", doctype)
clear_doctype_cache(doctype)
# "restore" doctype by inserting doctype with same schema again
new_doctype(doctype, custom=0).insert()
# Ensure basically same doctype getting "restored"
restored_doc = frappe.get_last_doc(doctype)
verify_fields = ["doctype", field, custom_field]
for f in verify_fields:
self.assertEqual(original_doc.get(f), restored_doc.get(f))
# Check form load of restored doctype
getdoc(doctype, restored_doc.name)
# ensure meta - property setter
self.assertEqual(frappe.get_meta(doctype).get_field(field).default, "DELETETHIS")
frappe.delete_doc("DocType", doctype)
def new_doctype(
name, unique: bool = False, depends_on: str = "", fields: Optional[List[Dict]] = None, **kwargs
name: Optional[str] = None,
unique: bool = False,
depends_on: str = "",
fields: Optional[List[Dict]] = None,
**kwargs,
):
if not name:
# Test prefix is required to avoid coverage
name = "Test " + "".join(random.sample(string.ascii_lowercase, 10))
doc = frappe.get_doc(
{
"doctype": "DocType",

View file

@ -88,10 +88,6 @@ def delete_doc(
update_flags(doc, flags, ignore_permissions)
check_permission_and_not_submitted(doc)
frappe.db.delete("Custom Field", {"dt": name})
frappe.db.delete("Client Script", {"dt": name})
frappe.db.delete("Property Setter", {"doc_type": name})
frappe.db.delete("Report", {"ref_doctype": name})
frappe.db.delete("Custom DocPerm", {"parent": name})
frappe.db.delete("__global_search", {"doctype": name})