diff --git a/frappe/core/doctype/permission_type/permission_type.json b/frappe/core/doctype/permission_type/permission_type.json index aa46b71202..0179614df8 100644 --- a/frappe/core/doctype/permission_type/permission_type.json +++ b/frappe/core/doctype/permission_type/permission_type.json @@ -1,27 +1,26 @@ { "actions": [], - "autoname": "prompt", "creation": "2025-07-28 13:12:03.573433", "doctype": "DocType", "engine": "InnoDB", "field_order": [ - "module", - "doc_types" + "perm_type", + "doc_type" ], "fields": [ { - "fieldname": "doc_types", - "fieldtype": "Table MultiSelect", - "label": "Applies To", - "options": "Permission Type DocType", + "fieldname": "perm_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Permission Type", "reqd": 1 }, { - "fieldname": "module", + "fieldname": "doc_type", "fieldtype": "Link", "in_list_view": 1, - "label": "Module", - "options": "Module Def", + "label": "Applies To (DocType)", + "options": "DocType", "reqd": 1 } ], @@ -29,11 +28,10 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2025-11-10 12:48:16.863229", + "modified": "2025-11-13 16:17:58.536849", "modified_by": "Administrator", "module": "Core", "name": "Permission Type", - "naming_rule": "Set by user", "owner": "Administrator", "permissions": [ { diff --git a/frappe/core/doctype/permission_type/permission_type.py b/frappe/core/doctype/permission_type/permission_type.py index 094c7af7cd..33c6c61840 100644 --- a/frappe/core/doctype/permission_type/permission_type.py +++ b/frappe/core/doctype/permission_type/permission_type.py @@ -20,34 +20,26 @@ class PermissionType(Document): from typing import TYPE_CHECKING if TYPE_CHECKING: - from frappe.core.doctype.permission_type_doctype.permission_type_doctype import PermissionTypeDocType from frappe.types import DF - doc_types: DF.TableMultiSelect[PermissionTypeDocType] - module: DF.Link + doc_type: DF.Link + perm_type: DF.Data # end: auto-generated types + def autoname(self): + self.name = f"{frappe.scrub(self.doc_type)}_{frappe.scrub(self.perm_type)}" + def before_insert(self): - self.name = frappe.scrub(self.name) + self.perm_type = frappe.scrub(self.perm_type) def validate(self): from frappe.permissions import std_rights - if self.name in std_rights: + if self.perm_type in std_rights: frappe.throw( - _("Permission Type '{0}' is reserved. Please choose another name.").format(self.name) + _("Permission Type '{0}' is reserved. Please choose another name.").format(self.perm_type) ) - # remove duplicate doc types - seen = set() - unique_doc_types = [] - for dt in self.doc_types: - if dt.doc_type not in seen: - seen.add(dt.doc_type) - unique_doc_types.append(dt) - - self.doc_types = unique_doc_types - def can_write(self): return ( frappe.conf.developer_mode @@ -56,30 +48,51 @@ class PermissionType(Document): or frappe.flags.in_test ) + def should_export(self): + return ( + frappe.conf.developer_mode + and not frappe.flags.in_migrate + and not frappe.flags.in_install + and not frappe.flags.in_test + ) + + def get_folder_path(self): + app = frappe.get_doctype_app(self.doc_type) + folder = frappe.get_app_source_path(app, app, "permission_types") + return folder + def on_update(self): if not self.can_write(): frappe.throw(_("Creation of this document is only permitted in developer mode.")) - from frappe.modules.export_file import export_to_files - - export_to_files(record_list=[["Permission Type", self.name]], record_module=self.module) - for target in CUSTOM_FIELD_TARGET: self.create_custom_field(target) + if self.should_export(): + from frappe.modules.export_file import export_to_files + + module = frappe.db.get_value("DocType", self.doc_type, "module") + export_to_files(record_list=[["Permission Type", self.name]], record_module=module) + + def before_export(self, export_doc): + del export_doc["idx"] + del export_doc["docstatus"] + for key in list(export_doc.keys()): + if key.startswith("_"): + del export_doc[key] + def create_custom_field(self, target): from frappe.custom.doctype.custom_field.custom_field import create_custom_field if not self.custom_field_exists(target): field = "share_doctype" if target == "DocShare" else "parent" - doc_types = [dt.doc_type for dt in self.doc_types if dt.doc_type] - depends_on = f"eval:{frappe.as_json(doc_types)}.includes(doc.{field})" + depends_on = f"eval:doc.{field} == '{self.doc_type}'" create_custom_field( target, { - "fieldname": self.name, - "label": self.name.replace("_", " ").title(), + "fieldname": self.perm_type, + "label": frappe.unscrub(self.perm_type), "fieldtype": "Check", "insert_after": "append", "depends_on": depends_on, @@ -93,7 +106,9 @@ class PermissionType(Document): for target in CUSTOM_FIELD_TARGET: self.delete_custom_field(target) - delete_folder(self.module, "Permission Type", self.name) + if self.should_export(): + module = frappe.db.get_value("DocType", self.doc_type, "module") + delete_folder(module, "Permission Type", self.name) def delete_custom_field(self, target): if name := self.custom_field_exists(target): @@ -103,7 +118,7 @@ class PermissionType(Document): return frappe.db.exists( "Custom Field", { - "fieldname": self.name, + "fieldname": self.perm_type, "dt": target, }, ) @@ -111,19 +126,10 @@ class PermissionType(Document): @site_cache def get_doctype_ptype_map(): - ptypes = frappe.qb.get_query( - "Permission Type", - fields=[ - "name", - {"doc_types": ["doc_type"]}, - ], - order_by="name", - ) - ptypes = ptypes.run(as_dict=True) + ptypes = frappe.get_all("Permission Type", fields=["perm_type", "doc_type"], order_by="perm_type") doctype_ptype_map = defaultdict(list) for pt in ptypes: - for dt in pt.doc_types: - if pt.name not in doctype_ptype_map[dt.doc_type]: - doctype_ptype_map[dt.doc_type].append(pt.name) + if pt.perm_type not in doctype_ptype_map[pt.doc_type]: + doctype_ptype_map[pt.doc_type].append(pt.perm_type) return dict(doctype_ptype_map) diff --git a/frappe/core/doctype/permission_type/test_permission_type.py b/frappe/core/doctype/permission_type/test_permission_type.py index ba33859454..54bf11f895 100644 --- a/frappe/core/doctype/permission_type/test_permission_type.py +++ b/frappe/core/doctype/permission_type/test_permission_type.py @@ -52,9 +52,9 @@ class IntegrationTestPermissionType(IntegrationTestCase): doc = frappe.get_doc( { "doctype": "Permission Type", - "name": "read", + "perm_type": "read", + "doc_type": "ToDo", "module": "Core", - "doc_types": [{"doc_type": "ToDo"}], } ) @@ -76,9 +76,9 @@ class IntegrationTestPermissionType(IntegrationTestCase): ptype_doc = frappe.get_doc( { "doctype": "Permission Type", - "name": name, + "perm_type": name, + "doc_type": doc_type, "module": "Core", - "doc_types": [{"doc_type": doc_type}], } ) ptype_doc.insert(ignore_if_duplicate=True) @@ -88,9 +88,9 @@ class IntegrationTestPermissionType(IntegrationTestCase): def _verify_custom_fields_created(self, ptype_doc, doc_type): """Verify that custom fields are created for the permission type.""" for target in ["Custom DocPerm", "DocPerm", "DocShare"]: - custom_field = frappe.get_doc("Custom Field", {"dt": target, "fieldname": ptype_doc.name}) + custom_field = frappe.get_doc("Custom Field", {"dt": target, "fieldname": ptype_doc.perm_type}) self.assertEqual(custom_field.dt, target) - self.assertEqual(custom_field.fieldname, ptype_doc.name) + self.assertEqual(custom_field.fieldname, ptype_doc.perm_type) self.assertEqual(custom_field.fieldtype, "Check") self.assertIn(doc_type, custom_field.depends_on) diff --git a/frappe/core/doctype/permission_type_doctype/__init__.py b/frappe/core/doctype/permission_type_doctype/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frappe/core/doctype/permission_type_doctype/permission_type_doctype.json b/frappe/core/doctype/permission_type_doctype/permission_type_doctype.json deleted file mode 100644 index ff5daf4394..0000000000 --- a/frappe/core/doctype/permission_type_doctype/permission_type_doctype.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "creation": "2025-11-03 15:51:52.422122", - "doctype": "DocType", - "engine": "InnoDB", - "field_order": [ - "doc_type" - ], - "fields": [ - { - "fieldname": "doc_type", - "fieldtype": "Link", - "in_list_view": 1, - "label": "DocType", - "options": "DocType", - "reqd": 1 - } - ], - "grid_page_length": 50, - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2025-11-03 15:52:16.161580", - "modified_by": "Administrator", - "module": "Core", - "name": "Permission Type DocType", - "owner": "Administrator", - "permissions": [], - "row_format": "Dynamic", - "rows_threshold_for_grid_search": 20, - "sort_field": "creation", - "sort_order": "DESC", - "states": [] -} diff --git a/frappe/core/doctype/permission_type_doctype/permission_type_doctype.py b/frappe/core/doctype/permission_type_doctype/permission_type_doctype.py deleted file mode 100644 index 3b3922c24c..0000000000 --- a/frappe/core/doctype/permission_type_doctype/permission_type_doctype.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2025, Frappe Technologies and contributors -# For license information, please see license.txt - -# import frappe -from frappe.model.document import Document - - -class PermissionTypeDocType(Document): - # begin: auto-generated types - # This code is auto-generated. Do not modify anything in this block. - - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from frappe.types import DF - - doc_type: DF.Link - parent: DF.Data - parentfield: DF.Data - parenttype: DF.Data - # end: auto-generated types - - pass diff --git a/frappe/core/permission_type/impersonate/impersonate.json b/frappe/core/permission_type/impersonate/impersonate.json deleted file mode 100644 index 0b8f74493e..0000000000 --- a/frappe/core/permission_type/impersonate/impersonate.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "creation": "2025-10-30 19:17:39.833189", - "doc_types": [ - { - "doc_type": "User" - } - ], - "docstatus": 0, - "doctype": "Permission Type", - "idx": 0, - "modified": "2025-11-03 16:58:56.819971", - "modified_by": "Administrator", - "module": "Core", - "name": "impersonate", - "owner": "Administrator" -} diff --git a/frappe/core/permission_type/user_impersonate/user_impersonate.json b/frappe/core/permission_type/user_impersonate/user_impersonate.json new file mode 100644 index 0000000000..0e00271f96 --- /dev/null +++ b/frappe/core/permission_type/user_impersonate/user_impersonate.json @@ -0,0 +1,10 @@ +{ + "creation": "2025-11-13 16:34:50.584738", + "doc_type": "User", + "doctype": "Permission Type", + "modified": "2025-11-14 14:21:45.603237", + "modified_by": "Administrator", + "name": "user_impersonate", + "owner": "Administrator", + "perm_type": "impersonate" +} diff --git a/frappe/model/sync.py b/frappe/model/sync.py index 6623e2d514..4feb2807d7 100644 --- a/frappe/model/sync.py +++ b/frappe/model/sync.py @@ -66,7 +66,6 @@ def sync_for(app_name, force=0, reset_permissions=False): "role", "has_role", "doctype", - "permission_type_doctype", "permission_type", ]: files.append(os.path.join(FRAPPE_PATH, "core", "doctype", core_module, f"{core_module}.json"))