We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
DocType Layout Fields table does not populate
## Description of the issue
When selecting a Document Type in a DocType Layout document the Fields table does not populate. The document must be saved twice in order to populate the fields, because the first save resulting in an error message. Obviously this would lead people (like myself) to believe the DocType Layout feature is non-functional.

Here is an image demonstrating the problems:
![image](https://user-images.githubusercontent.com/16672299/159814715-b8642de3-97c7-4fee-b151-2ee18fef3ecd.png)

## Context information (for bug reports)

**Output of `bench version`**
```
ERPNext: v13.22.0 (version-13)
Frappe Framework: v13.22.0 (version-13)
```

## Steps to reproduce the issue

1. Add a new DocType Layout document
2. Enter a value in Document Type

### Observed result

- Fields table does not populate.

### Expected result

Something more like this
![image](https://user-images.githubusercontent.com/16672299/159814647-dccba2a6-61be-4796-8431-325c18e24abb.png)

## Additional information

From what I can tell, there are two issues at play:

1. The document_type field expects no rows to exist (but a blank row is auto-generated)
2. The method neglects to refresh the Fields table

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `frappe/custom/doctype/doctype_layout/doctype_layout.py`
Content:
```
1 # Copyright (c) 2020, Frappe Technologies and contributors
2 # License: MIT. See LICENSE
3 
4 from frappe.desk.utils import slug
5 from frappe.model.document import Document
6 
7 
8 class DocTypeLayout(Document):
9 	def validate(self):
10 		if not self.route:
11 			self.route = slug(self.name)
12
```

Path: `frappe/custom/doctype/custom_field/custom_field.py`
Content:
```
1 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
2 # License: MIT. See LICENSE
3 
4 import json
5 
6 import frappe
7 from frappe import _
8 from frappe.model import core_doctypes_list
9 from frappe.model.docfield import supports_translation
10 from frappe.model.document import Document
11 from frappe.query_builder.functions import IfNull
12 from frappe.utils import cstr
13 
14 
15 class CustomField(Document):
16 	def autoname(self):
17 		self.set_fieldname()
18 		self.name = self.dt + "-" + self.fieldname
19 
20 	def set_fieldname(self):
21 		if not self.fieldname:
22 			label = self.label
23 			if not label:
24 				if self.fieldtype in ["Section Break", "Column Break", "Tab Break"]:
25 					label = self.fieldtype + "_" + str(self.idx)
26 				else:
27 					frappe.throw(_("Label is mandatory"))
28 
29 			# remove special characters from fieldname
30 			self.fieldname = "".join(
31 				filter(lambda x: x.isdigit() or x.isalpha() or "_", cstr(label).replace(" ", "_"))
32 			)
33 
34 		# fieldnames should be lowercase
35 		self.fieldname = self.fieldname.lower()
36 
37 	def before_insert(self):
38 		self.set_fieldname()
39 
40 	def validate(self):
41 		# these imports have been added to avoid cyclical import, should fix in future
42 		from frappe.core.doctype.doctype.doctype import check_fieldname_conflicts
43 		from frappe.custom.doctype.customize_form.customize_form import CustomizeForm
44 
45 		# don't always get meta to improve performance
46 		# setting idx is just an improvement, not a requirement
47 		if self.is_new() or self.insert_after == "append":
48 			meta = frappe.get_meta(self.dt, cached=False)
49 			fieldnames = [df.fieldname for df in meta.get("fields")]
50 
51 			if self.is_new() and self.fieldname in fieldnames:
52 				frappe.throw(
53 					_("A field with the name {0} already exists in {1}").format(
54 						frappe.bold(self.fieldname), self.dt
55 					)
56 				)
57 
58 			if self.insert_after == "append":
59 				self.insert_after = fieldnames[-1]
60 
61 			if self.insert_after and self.insert_after in fieldnames:
62 				self.idx = fieldnames.index(self.insert_after) + 1
63 
64 		if (
65 			not self.is_virtual
66 			and (doc_before_save := self.get_doc_before_save())
67 			and (old_fieldtype := doc_before_save.fieldtype) != self.fieldtype
68 			and not CustomizeForm.allow_fieldtype_change(old_fieldtype, self.fieldtype)
69 		):
70 			frappe.throw(
71 				_("Fieldtype cannot be changed from {0} to {1}").format(old_fieldtype, self.fieldtype)
72 			)
73 
74 		if not self.fieldname:
75 			frappe.throw(_("Fieldname not set for Custom Field"))
76 
77 		if self.get("translatable", 0) and not supports_translation(self.fieldtype):
78 			self.translatable = 0
79 
80 		check_fieldname_conflicts(self)
81 
82 	def on_update(self):
83 		# validate field
84 		if not self.flags.ignore_validate:
85 			from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype
86 
87 			validate_fields_for_doctype(self.dt)
88 
89 		# clear cache and update the schema
90 		if not frappe.flags.in_create_custom_fields:
91 			frappe.clear_cache(doctype=self.dt)
92 			frappe.db.updatedb(self.dt)
93 
94 	def on_trash(self):
95 		# check if Admin owned field
96 		if self.owner == "Administrator" and frappe.session.user != "Administrator":
97 			frappe.throw(
98 				_(
99 					"Custom Field {0} is created by the Administrator and can only be deleted through the Administrator account."
100 				).format(frappe.bold(self.label))
101 			)
102 
103 		# delete property setter entries
104 		frappe.db.delete("Property Setter", {"doc_type": self.dt, "field_name": self.fieldname})
105 		frappe.clear_cache(doctype=self.dt)
106 
107 	def validate_insert_after(self, meta):
108 		if not meta.get_field(self.insert_after):
109 			frappe.throw(
110 				_(
111 					"Insert After field '{0}' mentioned in Custom Field '{1}', with label '{2}', does not exist"
112 				).format(self.insert_after, self.name, self.label),
113 				frappe.DoesNotExistError,
114 			)
115 
116 		if self.fieldname == self.insert_after:
117 			frappe.throw(_("Insert After cannot be set as {0}").format(meta.get_label(self.insert_after)))
118 
119 
120 @frappe.whitelist()
121 def get_fields_label(doctype=None):
122 	meta = frappe.get_meta(doctype)
123 
124 	if doctype in core_doctypes_list:
125 		return frappe.msgprint(_("Custom Fields cannot be added to core DocTypes."))
126 
127 	if meta.custom:
128 		return frappe.msgprint(_("Custom Fields can only be added to a standard DocType."))
129 
130 	return [
131 		{"value": df.fieldname or "", "label": _(df.label or "")}
132 		for df in frappe.get_meta(doctype).get("fields")
133 	]
134 
135 
136 def create_custom_field_if_values_exist(doctype, df):
137 	df = frappe._dict(df)
138 	if df.fieldname in frappe.db.get_table_columns(doctype) and frappe.db.count(
139 		dt=doctype, filters=IfNull(df.fieldname, "") != ""
140 	):
141 		create_custom_field(doctype, df)
142 
143 
144 def create_custom_field(doctype, df, ignore_validate=False, is_system_generated=True):
145 	df = frappe._dict(df)
146 	if not df.fieldname and df.label:
147 		df.fieldname = frappe.scrub(df.label)
148 	if not frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df.fieldname}):
149 		custom_field = frappe.get_doc(
150 			{
151 				"doctype": "Custom Field",
152 				"dt": doctype,
153 				"permlevel": 0,
154 				"fieldtype": "Data",
155 				"hidden": 0,
156 				"is_system_generated": is_system_generated,
157 			}
158 		)
159 		custom_field.update(df)
160 		custom_field.flags.ignore_validate = ignore_validate
161 		custom_field.insert()
162 		return custom_field
163 
164 
165 def create_custom_fields(custom_fields, ignore_validate=False, update=True):
166 	"""Add / update multiple custom fields
167 
168 	:param custom_fields: example `{'Sales Invoice': [dict(fieldname='test')]}`"""
169 
170 	try:
171 		frappe.flags.in_create_custom_fields = True
172 		doctypes_to_update = set()
173 
174 		if frappe.flags.in_setup_wizard:
175 			ignore_validate = True
176 
177 		for doctypes, fields in custom_fields.items():
178 			if isinstance(fields, dict):
179 				# only one field
180 				fields = [fields]
181 
182 			if isinstance(doctypes, str):
183 				# only one doctype
184 				doctypes = (doctypes,)
185 
186 			for doctype in doctypes:
187 				doctypes_to_update.add(doctype)
188 
189 				for df in fields:
190 					field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]})
191 					if not field:
192 						try:
193 							df = df.copy()
194 							df["owner"] = "Administrator"
195 							create_custom_field(doctype, df, ignore_validate=ignore_validate)
196 
197 						except frappe.exceptions.DuplicateEntryError:
198 							pass
199 
200 					elif update:
201 						custom_field = frappe.get_doc("Custom Field", field)
202 						custom_field.flags.ignore_validate = ignore_validate
203 						custom_field.update(df)
204 						custom_field.save()
205 
206 		for doctype in doctypes_to_update:
207 			frappe.clear_cache(doctype=doctype)
208 			frappe.db.updatedb(doctype)
209 
210 	finally:
211 		frappe.flags.in_create_custom_fields = False
212
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py
--- a/frappe/custom/doctype/custom_field/custom_field.py
+++ b/frappe/custom/doctype/custom_field/custom_field.py
@@ -102,6 +102,20 @@
 
 		# delete property setter entries
 		frappe.db.delete("Property Setter", {"doc_type": self.dt, "field_name": self.fieldname})
+
+		# update doctype layouts
+		doctype_layouts = frappe.get_all(
+			"DocType Layout", filters={"document_type": self.dt}, pluck="name"
+		)
+
+		for layout in doctype_layouts:
+			layout_doc = frappe.get_doc("DocType Layout", layout)
+			for field in layout_doc.fields:
+				if field.fieldname == self.fieldname:
+					layout_doc.remove(field)
+					layout_doc.save()
+					break
+
 		frappe.clear_cache(doctype=self.dt)
 
 	def validate_insert_after(self, meta):
diff --git a/frappe/custom/doctype/doctype_layout/doctype_layout.py b/frappe/custom/doctype/doctype_layout/doctype_layout.py
--- a/frappe/custom/doctype/doctype_layout/doctype_layout.py
+++ b/frappe/custom/doctype/doctype_layout/doctype_layout.py
@@ -1,11 +1,77 @@
 # Copyright (c) 2020, Frappe Technologies and contributors
 # License: MIT. See LICENSE
 
+from typing import TYPE_CHECKING
+
+import frappe
 from frappe.desk.utils import slug
 from frappe.model.document import Document
 
+if TYPE_CHECKING:
+	from frappe.core.doctype.docfield.docfield import DocField
+
 
 class DocTypeLayout(Document):
 	def validate(self):
 		if not self.route:
 			self.route = slug(self.name)
+
+	@frappe.whitelist()
+	def sync_fields(self):
+		doctype_fields = frappe.get_meta(self.document_type).fields
+
+		if self.is_new():
+			added_fields = [field.fieldname for field in doctype_fields]
+			removed_fields = []
+		else:
+			doctype_fieldnames = {field.fieldname for field in doctype_fields}
+			layout_fieldnames = {field.fieldname for field in self.fields}
+			added_fields = list(doctype_fieldnames - layout_fieldnames)
+			removed_fields = list(layout_fieldnames - doctype_fieldnames)
+
+		if not (added_fields or removed_fields):
+			return
+
+		added = self.add_fields(added_fields, doctype_fields)
+		removed = self.remove_fields(removed_fields)
+
+		for index, field in enumerate(self.fields):
+			field.idx = index + 1
+
+		return {"added": added, "removed": removed}
+
+	def add_fields(self, added_fields: list[str], doctype_fields: list["DocField"]) -> list[dict]:
+		added = []
+		for field in added_fields:
+			field_details = next((f for f in doctype_fields if f.fieldname == field), None)
+			if not field_details:
+				continue
+
+			# remove 'doctype' data from the DocField to allow adding it to the layout
+			row = self.append("fields", field_details.as_dict(no_default_fields=True))
+			row_data = row.as_dict()
+
+			if field_details.get("insert_after"):
+				insert_after = next(
+					(f for f in self.fields if f.fieldname == field_details.insert_after),
+					None,
+				)
+
+				# initialize new row to just after the insert_after field
+				if insert_after:
+					self.fields.insert(insert_after.idx, row)
+					self.fields.pop()
+
+					row_data = {"idx": insert_after.idx + 1, "fieldname": row.fieldname, "label": row.label}
+
+			added.append(row_data)
+		return added
+
+	def remove_fields(self, removed_fields: list[str]) -> list[dict]:
+		removed = []
+		for field in removed_fields:
+			field_details = next((f for f in self.fields if f.fieldname == field), None)
+			if field_details:
+				self.remove(field_details)
+				removed.append(field_details.as_dict())
+		return removed

{"golden_diff": "diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py\n--- a/frappe/custom/doctype/custom_field/custom_field.py\n+++ b/frappe/custom/doctype/custom_field/custom_field.py\n@@ -102,6 +102,20 @@\n \n \t\t# delete property setter entries\n \t\tfrappe.db.delete(\"Property Setter\", {\"doc_type\": self.dt, \"field_name\": self.fieldname})\n+\n+\t\t# update doctype layouts\n+\t\tdoctype_layouts = frappe.get_all(\n+\t\t\t\"DocType Layout\", filters={\"document_type\": self.dt}, pluck=\"name\"\n+\t\t)\n+\n+\t\tfor layout in doctype_layouts:\n+\t\t\tlayout_doc = frappe.get_doc(\"DocType Layout\", layout)\n+\t\t\tfor field in layout_doc.fields:\n+\t\t\t\tif field.fieldname == self.fieldname:\n+\t\t\t\t\tlayout_doc.remove(field)\n+\t\t\t\t\tlayout_doc.save()\n+\t\t\t\t\tbreak\n+\n \t\tfrappe.clear_cache(doctype=self.dt)\n \n \tdef validate_insert_after(self, meta):\ndiff --git a/frappe/custom/doctype/doctype_layout/doctype_layout.py b/frappe/custom/doctype/doctype_layout/doctype_layout.py\n--- a/frappe/custom/doctype/doctype_layout/doctype_layout.py\n+++ b/frappe/custom/doctype/doctype_layout/doctype_layout.py\n@@ -1,11 +1,77 @@\n # Copyright (c) 2020, Frappe Technologies and contributors\n # License: MIT. See LICENSE\n \n+from typing import TYPE_CHECKING\n+\n+import frappe\n from frappe.desk.utils import slug\n from frappe.model.document import Document\n \n+if TYPE_CHECKING:\n+\tfrom frappe.core.doctype.docfield.docfield import DocField\n+\n \n class DocTypeLayout(Document):\n \tdef validate(self):\n \t\tif not self.route:\n \t\t\tself.route = slug(self.name)\n+\n+\t@frappe.whitelist()\n+\tdef sync_fields(self):\n+\t\tdoctype_fields = frappe.get_meta(self.document_type).fields\n+\n+\t\tif self.is_new():\n+\t\t\tadded_fields = [field.fieldname for field in doctype_fields]\n+\t\t\tremoved_fields = []\n+\t\telse:\n+\t\t\tdoctype_fieldnames = {field.fieldname for field in doctype_fields}\n+\t\t\tlayout_fieldnames = {field.fieldname for field in self.fields}\n+\t\t\tadded_fields = list(doctype_fieldnames - layout_fieldnames)\n+\t\t\tremoved_fields = list(layout_fieldnames - doctype_fieldnames)\n+\n+\t\tif not (added_fields or removed_fields):\n+\t\t\treturn\n+\n+\t\tadded = self.add_fields(added_fields, doctype_fields)\n+\t\tremoved = self.remove_fields(removed_fields)\n+\n+\t\tfor index, field in enumerate(self.fields):\n+\t\t\tfield.idx = index + 1\n+\n+\t\treturn {\"added\": added, \"removed\": removed}\n+\n+\tdef add_fields(self, added_fields: list[str], doctype_fields: list[\"DocField\"]) -> list[dict]:\n+\t\tadded = []\n+\t\tfor field in added_fields:\n+\t\t\tfield_details = next((f for f in doctype_fields if f.fieldname == field), None)\n+\t\t\tif not field_details:\n+\t\t\t\tcontinue\n+\n+\t\t\t# remove 'doctype' data from the DocField to allow adding it to the layout\n+\t\t\trow = self.append(\"fields\", field_details.as_dict(no_default_fields=True))\n+\t\t\trow_data = row.as_dict()\n+\n+\t\t\tif field_details.get(\"insert_after\"):\n+\t\t\t\tinsert_after = next(\n+\t\t\t\t\t(f for f in self.fields if f.fieldname == field_details.insert_after),\n+\t\t\t\t\tNone,\n+\t\t\t\t)\n+\n+\t\t\t\t# initialize new row to just after the insert_after field\n+\t\t\t\tif insert_after:\n+\t\t\t\t\tself.fields.insert(insert_after.idx, row)\n+\t\t\t\t\tself.fields.pop()\n+\n+\t\t\t\t\trow_data = {\"idx\": insert_after.idx + 1, \"fieldname\": row.fieldname, \"label\": row.label}\n+\n+\t\t\tadded.append(row_data)\n+\t\treturn added\n+\n+\tdef remove_fields(self, removed_fields: list[str]) -> list[dict]:\n+\t\tremoved = []\n+\t\tfor field in removed_fields:\n+\t\t\tfield_details = next((f for f in self.fields if f.fieldname == field), None)\n+\t\t\tif field_details:\n+\t\t\t\tself.remove(field_details)\n+\t\t\t\tremoved.append(field_details.as_dict())\n+\t\treturn removed\n", "issue": "DocType Layout Fields table does not populate\n## Description of the issue\r\nWhen selecting a Document Type in a DocType Layout document the Fields table does not populate. The document must be saved twice in order to populate the fields, because the first save resulting in an error message. Obviously this would lead people (like myself) to believe the DocType Layout feature is non-functional.\r\n\r\nHere is an image demonstrating the problems:\r\n![image](https://user-images.githubusercontent.com/16672299/159814715-b8642de3-97c7-4fee-b151-2ee18fef3ecd.png)\r\n\r\n## Context information (for bug reports)\r\n\r\n**Output of `bench version`**\r\n```\r\nERPNext: v13.22.0 (version-13)\r\nFrappe Framework: v13.22.0 (version-13)\r\n```\r\n\r\n## Steps to reproduce the issue\r\n\r\n1. Add a new DocType Layout document\r\n2. Enter a value in Document Type\r\n\r\n### Observed result\r\n\r\n- Fields table does not populate.\r\n\r\n### Expected result\r\n\r\nSomething more like this\r\n![image](https://user-images.githubusercontent.com/16672299/159814647-dccba2a6-61be-4796-8431-325c18e24abb.png)\r\n\r\n## Additional information\r\n\r\nFrom what I can tell, there are two issues at play:\r\n\r\n1. The document_type field expects no rows to exist (but a blank row is auto-generated)\r\n2. The method neglects to refresh the Fields table\n", "before_files": [{"content": "# Copyright (c) 2020, Frappe Technologies and contributors\n# License: MIT. See LICENSE\n\nfrom frappe.desk.utils import slug\nfrom frappe.model.document import Document\n\n\nclass DocTypeLayout(Document):\n\tdef validate(self):\n\t\tif not self.route:\n\t\t\tself.route = slug(self.name)\n", "path": "frappe/custom/doctype/doctype_layout/doctype_layout.py"}, {"content": "# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors\n# License: MIT. See LICENSE\n\nimport json\n\nimport frappe\nfrom frappe import _\nfrom frappe.model import core_doctypes_list\nfrom frappe.model.docfield import supports_translation\nfrom frappe.model.document import Document\nfrom frappe.query_builder.functions import IfNull\nfrom frappe.utils import cstr\n\n\nclass CustomField(Document):\n\tdef autoname(self):\n\t\tself.set_fieldname()\n\t\tself.name = self.dt + \"-\" + self.fieldname\n\n\tdef set_fieldname(self):\n\t\tif not self.fieldname:\n\t\t\tlabel = self.label\n\t\t\tif not label:\n\t\t\t\tif self.fieldtype in [\"Section Break\", \"Column Break\", \"Tab Break\"]:\n\t\t\t\t\tlabel = self.fieldtype + \"_\" + str(self.idx)\n\t\t\t\telse:\n\t\t\t\t\tfrappe.throw(_(\"Label is mandatory\"))\n\n\t\t\t# remove special characters from fieldname\n\t\t\tself.fieldname = \"\".join(\n\t\t\t\tfilter(lambda x: x.isdigit() or x.isalpha() or \"_\", cstr(label).replace(\" \", \"_\"))\n\t\t\t)\n\n\t\t# fieldnames should be lowercase\n\t\tself.fieldname = self.fieldname.lower()\n\n\tdef before_insert(self):\n\t\tself.set_fieldname()\n\n\tdef validate(self):\n\t\t# these imports have been added to avoid cyclical import, should fix in future\n\t\tfrom frappe.core.doctype.doctype.doctype import check_fieldname_conflicts\n\t\tfrom frappe.custom.doctype.customize_form.customize_form import CustomizeForm\n\n\t\t# don't always get meta to improve performance\n\t\t# setting idx is just an improvement, not a requirement\n\t\tif self.is_new() or self.insert_after == \"append\":\n\t\t\tmeta = frappe.get_meta(self.dt, cached=False)\n\t\t\tfieldnames = [df.fieldname for df in meta.get(\"fields\")]\n\n\t\t\tif self.is_new() and self.fieldname in fieldnames:\n\t\t\t\tfrappe.throw(\n\t\t\t\t\t_(\"A field with the name {0} already exists in {1}\").format(\n\t\t\t\t\t\tfrappe.bold(self.fieldname), self.dt\n\t\t\t\t\t)\n\t\t\t\t)\n\n\t\t\tif self.insert_after == \"append\":\n\t\t\t\tself.insert_after = fieldnames[-1]\n\n\t\t\tif self.insert_after and self.insert_after in fieldnames:\n\t\t\t\tself.idx = fieldnames.index(self.insert_after) + 1\n\n\t\tif (\n\t\t\tnot self.is_virtual\n\t\t\tand (doc_before_save := self.get_doc_before_save())\n\t\t\tand (old_fieldtype := doc_before_save.fieldtype) != self.fieldtype\n\t\t\tand not CustomizeForm.allow_fieldtype_change(old_fieldtype, self.fieldtype)\n\t\t):\n\t\t\tfrappe.throw(\n\t\t\t\t_(\"Fieldtype cannot be changed from {0} to {1}\").format(old_fieldtype, self.fieldtype)\n\t\t\t)\n\n\t\tif not self.fieldname:\n\t\t\tfrappe.throw(_(\"Fieldname not set for Custom Field\"))\n\n\t\tif self.get(\"translatable\", 0) and not supports_translation(self.fieldtype):\n\t\t\tself.translatable = 0\n\n\t\tcheck_fieldname_conflicts(self)\n\n\tdef on_update(self):\n\t\t# validate field\n\t\tif not self.flags.ignore_validate:\n\t\t\tfrom frappe.core.doctype.doctype.doctype import validate_fields_for_doctype\n\n\t\t\tvalidate_fields_for_doctype(self.dt)\n\n\t\t# clear cache and update the schema\n\t\tif not frappe.flags.in_create_custom_fields:\n\t\t\tfrappe.clear_cache(doctype=self.dt)\n\t\t\tfrappe.db.updatedb(self.dt)\n\n\tdef on_trash(self):\n\t\t# check if Admin owned field\n\t\tif self.owner == \"Administrator\" and frappe.session.user != \"Administrator\":\n\t\t\tfrappe.throw(\n\t\t\t\t_(\n\t\t\t\t\t\"Custom Field {0} is created by the Administrator and can only be deleted through the Administrator account.\"\n\t\t\t\t).format(frappe.bold(self.label))\n\t\t\t)\n\n\t\t# delete property setter entries\n\t\tfrappe.db.delete(\"Property Setter\", {\"doc_type\": self.dt, \"field_name\": self.fieldname})\n\t\tfrappe.clear_cache(doctype=self.dt)\n\n\tdef validate_insert_after(self, meta):\n\t\tif not meta.get_field(self.insert_after):\n\t\t\tfrappe.throw(\n\t\t\t\t_(\n\t\t\t\t\t\"Insert After field '{0}' mentioned in Custom Field '{1}', with label '{2}', does not exist\"\n\t\t\t\t).format(self.insert_after, self.name, self.label),\n\t\t\t\tfrappe.DoesNotExistError,\n\t\t\t)\n\n\t\tif self.fieldname == self.insert_after:\n\t\t\tfrappe.throw(_(\"Insert After cannot be set as {0}\").format(meta.get_label(self.insert_after)))\n\n\n@frappe.whitelist()\ndef get_fields_label(doctype=None):\n\tmeta = frappe.get_meta(doctype)\n\n\tif doctype in core_doctypes_list:\n\t\treturn frappe.msgprint(_(\"Custom Fields cannot be added to core DocTypes.\"))\n\n\tif meta.custom:\n\t\treturn frappe.msgprint(_(\"Custom Fields can only be added to a standard DocType.\"))\n\n\treturn [\n\t\t{\"value\": df.fieldname or \"\", \"label\": _(df.label or \"\")}\n\t\tfor df in frappe.get_meta(doctype).get(\"fields\")\n\t]\n\n\ndef create_custom_field_if_values_exist(doctype, df):\n\tdf = frappe._dict(df)\n\tif df.fieldname in frappe.db.get_table_columns(doctype) and frappe.db.count(\n\t\tdt=doctype, filters=IfNull(df.fieldname, \"\") != \"\"\n\t):\n\t\tcreate_custom_field(doctype, df)\n\n\ndef create_custom_field(doctype, df, ignore_validate=False, is_system_generated=True):\n\tdf = frappe._dict(df)\n\tif not df.fieldname and df.label:\n\t\tdf.fieldname = frappe.scrub(df.label)\n\tif not frappe.db.get_value(\"Custom Field\", {\"dt\": doctype, \"fieldname\": df.fieldname}):\n\t\tcustom_field = frappe.get_doc(\n\t\t\t{\n\t\t\t\t\"doctype\": \"Custom Field\",\n\t\t\t\t\"dt\": doctype,\n\t\t\t\t\"permlevel\": 0,\n\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\"hidden\": 0,\n\t\t\t\t\"is_system_generated\": is_system_generated,\n\t\t\t}\n\t\t)\n\t\tcustom_field.update(df)\n\t\tcustom_field.flags.ignore_validate = ignore_validate\n\t\tcustom_field.insert()\n\t\treturn custom_field\n\n\ndef create_custom_fields(custom_fields, ignore_validate=False, update=True):\n\t\"\"\"Add / update multiple custom fields\n\n\t:param custom_fields: example `{'Sales Invoice': [dict(fieldname='test')]}`\"\"\"\n\n\ttry:\n\t\tfrappe.flags.in_create_custom_fields = True\n\t\tdoctypes_to_update = set()\n\n\t\tif frappe.flags.in_setup_wizard:\n\t\t\tignore_validate = True\n\n\t\tfor doctypes, fields in custom_fields.items():\n\t\t\tif isinstance(fields, dict):\n\t\t\t\t# only one field\n\t\t\t\tfields = [fields]\n\n\t\t\tif isinstance(doctypes, str):\n\t\t\t\t# only one doctype\n\t\t\t\tdoctypes = (doctypes,)\n\n\t\t\tfor doctype in doctypes:\n\t\t\t\tdoctypes_to_update.add(doctype)\n\n\t\t\t\tfor df in fields:\n\t\t\t\t\tfield = frappe.db.get_value(\"Custom Field\", {\"dt\": doctype, \"fieldname\": df[\"fieldname\"]})\n\t\t\t\t\tif not field:\n\t\t\t\t\t\ttry:\n\t\t\t\t\t\t\tdf = df.copy()\n\t\t\t\t\t\t\tdf[\"owner\"] = \"Administrator\"\n\t\t\t\t\t\t\tcreate_custom_field(doctype, df, ignore_validate=ignore_validate)\n\n\t\t\t\t\t\texcept frappe.exceptions.DuplicateEntryError:\n\t\t\t\t\t\t\tpass\n\n\t\t\t\t\telif update:\n\t\t\t\t\t\tcustom_field = frappe.get_doc(\"Custom Field\", field)\n\t\t\t\t\t\tcustom_field.flags.ignore_validate = ignore_validate\n\t\t\t\t\t\tcustom_field.update(df)\n\t\t\t\t\t\tcustom_field.save()\n\n\t\tfor doctype in doctypes_to_update:\n\t\t\tfrappe.clear_cache(doctype=doctype)\n\t\t\tfrappe.db.updatedb(doctype)\n\n\tfinally:\n\t\tfrappe.flags.in_create_custom_fields = False\n", "path": "frappe/custom/doctype/custom_field/custom_field.py"}], "after_files": [{"content": "# Copyright (c) 2020, Frappe Technologies and contributors\n# License: MIT. See LICENSE\n\nfrom typing import TYPE_CHECKING\n\nimport frappe\nfrom frappe.desk.utils import slug\nfrom frappe.model.document import Document\n\nif TYPE_CHECKING:\n\tfrom frappe.core.doctype.docfield.docfield import DocField\n\n\nclass DocTypeLayout(Document):\n\tdef validate(self):\n\t\tif not self.route:\n\t\t\tself.route = slug(self.name)\n\n\t@frappe.whitelist()\n\tdef sync_fields(self):\n\t\tdoctype_fields = frappe.get_meta(self.document_type).fields\n\n\t\tif self.is_new():\n\t\t\tadded_fields = [field.fieldname for field in doctype_fields]\n\t\t\tremoved_fields = []\n\t\telse:\n\t\t\tdoctype_fieldnames = {field.fieldname for field in doctype_fields}\n\t\t\tlayout_fieldnames = {field.fieldname for field in self.fields}\n\t\t\tadded_fields = list(doctype_fieldnames - layout_fieldnames)\n\t\t\tremoved_fields = list(layout_fieldnames - doctype_fieldnames)\n\n\t\tif not (added_fields or removed_fields):\n\t\t\treturn\n\n\t\tadded = self.add_fields(added_fields, doctype_fields)\n\t\tremoved = self.remove_fields(removed_fields)\n\n\t\tfor index, field in enumerate(self.fields):\n\t\t\tfield.idx = index + 1\n\n\t\treturn {\"added\": added, \"removed\": removed}\n\n\tdef add_fields(self, added_fields: list[str], doctype_fields: list[\"DocField\"]) -> list[dict]:\n\t\tadded = []\n\t\tfor field in added_fields:\n\t\t\tfield_details = next((f for f in doctype_fields if f.fieldname == field), None)\n\t\t\tif not field_details:\n\t\t\t\tcontinue\n\n\t\t\t# remove 'doctype' data from the DocField to allow adding it to the layout\n\t\t\trow = self.append(\"fields\", field_details.as_dict(no_default_fields=True))\n\t\t\trow_data = row.as_dict()\n\n\t\t\tif field_details.get(\"insert_after\"):\n\t\t\t\tinsert_after = next(\n\t\t\t\t\t(f for f in self.fields if f.fieldname == field_details.insert_after),\n\t\t\t\t\tNone,\n\t\t\t\t)\n\n\t\t\t\t# initialize new row to just after the insert_after field\n\t\t\t\tif insert_after:\n\t\t\t\t\tself.fields.insert(insert_after.idx, row)\n\t\t\t\t\tself.fields.pop()\n\n\t\t\t\t\trow_data = {\"idx\": insert_after.idx + 1, \"fieldname\": row.fieldname, \"label\": row.label}\n\n\t\t\tadded.append(row_data)\n\t\treturn added\n\n\tdef remove_fields(self, removed_fields: list[str]) -> list[dict]:\n\t\tremoved = []\n\t\tfor field in removed_fields:\n\t\t\tfield_details = next((f for f in self.fields if f.fieldname == field), None)\n\t\t\tif field_details:\n\t\t\t\tself.remove(field_details)\n\t\t\t\tremoved.append(field_details.as_dict())\n\t\treturn removed\n", "path": "frappe/custom/doctype/doctype_layout/doctype_layout.py"}, {"content": "# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors\n# License: MIT. See LICENSE\n\nimport json\n\nimport frappe\nfrom frappe import _\nfrom frappe.model import core_doctypes_list\nfrom frappe.model.docfield import supports_translation\nfrom frappe.model.document import Document\nfrom frappe.query_builder.functions import IfNull\nfrom frappe.utils import cstr\n\n\nclass CustomField(Document):\n\tdef autoname(self):\n\t\tself.set_fieldname()\n\t\tself.name = self.dt + \"-\" + self.fieldname\n\n\tdef set_fieldname(self):\n\t\tif not self.fieldname:\n\t\t\tlabel = self.label\n\t\t\tif not label:\n\t\t\t\tif self.fieldtype in [\"Section Break\", \"Column Break\", \"Tab Break\"]:\n\t\t\t\t\tlabel = self.fieldtype + \"_\" + str(self.idx)\n\t\t\t\telse:\n\t\t\t\t\tfrappe.throw(_(\"Label is mandatory\"))\n\n\t\t\t# remove special characters from fieldname\n\t\t\tself.fieldname = \"\".join(\n\t\t\t\tfilter(lambda x: x.isdigit() or x.isalpha() or \"_\", cstr(label).replace(\" \", \"_\"))\n\t\t\t)\n\n\t\t# fieldnames should be lowercase\n\t\tself.fieldname = self.fieldname.lower()\n\n\tdef before_insert(self):\n\t\tself.set_fieldname()\n\n\tdef validate(self):\n\t\t# these imports have been added to avoid cyclical import, should fix in future\n\t\tfrom frappe.core.doctype.doctype.doctype import check_fieldname_conflicts\n\t\tfrom frappe.custom.doctype.customize_form.customize_form import CustomizeForm\n\n\t\t# don't always get meta to improve performance\n\t\t# setting idx is just an improvement, not a requirement\n\t\tif self.is_new() or self.insert_after == \"append\":\n\t\t\tmeta = frappe.get_meta(self.dt, cached=False)\n\t\t\tfieldnames = [df.fieldname for df in meta.get(\"fields\")]\n\n\t\t\tif self.is_new() and self.fieldname in fieldnames:\n\t\t\t\tfrappe.throw(\n\t\t\t\t\t_(\"A field with the name {0} already exists in {1}\").format(\n\t\t\t\t\t\tfrappe.bold(self.fieldname), self.dt\n\t\t\t\t\t)\n\t\t\t\t)\n\n\t\t\tif self.insert_after == \"append\":\n\t\t\t\tself.insert_after = fieldnames[-1]\n\n\t\t\tif self.insert_after and self.insert_after in fieldnames:\n\t\t\t\tself.idx = fieldnames.index(self.insert_after) + 1\n\n\t\tif (\n\t\t\tnot self.is_virtual\n\t\t\tand (doc_before_save := self.get_doc_before_save())\n\t\t\tand (old_fieldtype := doc_before_save.fieldtype) != self.fieldtype\n\t\t\tand not CustomizeForm.allow_fieldtype_change(old_fieldtype, self.fieldtype)\n\t\t):\n\t\t\tfrappe.throw(\n\t\t\t\t_(\"Fieldtype cannot be changed from {0} to {1}\").format(old_fieldtype, self.fieldtype)\n\t\t\t)\n\n\t\tif not self.fieldname:\n\t\t\tfrappe.throw(_(\"Fieldname not set for Custom Field\"))\n\n\t\tif self.get(\"translatable\", 0) and not supports_translation(self.fieldtype):\n\t\t\tself.translatable = 0\n\n\t\tcheck_fieldname_conflicts(self)\n\n\tdef on_update(self):\n\t\t# validate field\n\t\tif not self.flags.ignore_validate:\n\t\t\tfrom frappe.core.doctype.doctype.doctype import validate_fields_for_doctype\n\n\t\t\tvalidate_fields_for_doctype(self.dt)\n\n\t\t# clear cache and update the schema\n\t\tif not frappe.flags.in_create_custom_fields:\n\t\t\tfrappe.clear_cache(doctype=self.dt)\n\t\t\tfrappe.db.updatedb(self.dt)\n\n\tdef on_trash(self):\n\t\t# check if Admin owned field\n\t\tif self.owner == \"Administrator\" and frappe.session.user != \"Administrator\":\n\t\t\tfrappe.throw(\n\t\t\t\t_(\n\t\t\t\t\t\"Custom Field {0} is created by the Administrator and can only be deleted through the Administrator account.\"\n\t\t\t\t).format(frappe.bold(self.label))\n\t\t\t)\n\n\t\t# delete property setter entries\n\t\tfrappe.db.delete(\"Property Setter\", {\"doc_type\": self.dt, \"field_name\": self.fieldname})\n\n\t\t# update doctype layouts\n\t\tdoctype_layouts = frappe.get_all(\n\t\t\t\"DocType Layout\", filters={\"document_type\": self.dt}, pluck=\"name\"\n\t\t)\n\n\t\tfor layout in doctype_layouts:\n\t\t\tlayout_doc = frappe.get_doc(\"DocType Layout\", layout)\n\t\t\tfor field in layout_doc.fields:\n\t\t\t\tif field.fieldname == self.fieldname:\n\t\t\t\t\tlayout_doc.remove(field)\n\t\t\t\t\tlayout_doc.save()\n\t\t\t\t\tbreak\n\n\t\tfrappe.clear_cache(doctype=self.dt)\n\n\tdef validate_insert_after(self, meta):\n\t\tif not meta.get_field(self.insert_after):\n\t\t\tfrappe.throw(\n\t\t\t\t_(\n\t\t\t\t\t\"Insert After field '{0}' mentioned in Custom Field '{1}', with label '{2}', does not exist\"\n\t\t\t\t).format(self.insert_after, self.name, self.label),\n\t\t\t\tfrappe.DoesNotExistError,\n\t\t\t)\n\n\t\tif self.fieldname == self.insert_after:\n\t\t\tfrappe.throw(_(\"Insert After cannot be set as {0}\").format(meta.get_label(self.insert_after)))\n\n\n@frappe.whitelist()\ndef get_fields_label(doctype=None):\n\tmeta = frappe.get_meta(doctype)\n\n\tif doctype in core_doctypes_list:\n\t\treturn frappe.msgprint(_(\"Custom Fields cannot be added to core DocTypes.\"))\n\n\tif meta.custom:\n\t\treturn frappe.msgprint(_(\"Custom Fields can only be added to a standard DocType.\"))\n\n\treturn [\n\t\t{\"value\": df.fieldname or \"\", \"label\": _(df.label or \"\")}\n\t\tfor df in frappe.get_meta(doctype).get(\"fields\")\n\t]\n\n\ndef create_custom_field_if_values_exist(doctype, df):\n\tdf = frappe._dict(df)\n\tif df.fieldname in frappe.db.get_table_columns(doctype) and frappe.db.count(\n\t\tdt=doctype, filters=IfNull(df.fieldname, \"\") != \"\"\n\t):\n\t\tcreate_custom_field(doctype, df)\n\n\ndef create_custom_field(doctype, df, ignore_validate=False, is_system_generated=True):\n\tdf = frappe._dict(df)\n\tif not df.fieldname and df.label:\n\t\tdf.fieldname = frappe.scrub(df.label)\n\tif not frappe.db.get_value(\"Custom Field\", {\"dt\": doctype, \"fieldname\": df.fieldname}):\n\t\tcustom_field = frappe.get_doc(\n\t\t\t{\n\t\t\t\t\"doctype\": \"Custom Field\",\n\t\t\t\t\"dt\": doctype,\n\t\t\t\t\"permlevel\": 0,\n\t\t\t\t\"fieldtype\": \"Data\",\n\t\t\t\t\"hidden\": 0,\n\t\t\t\t\"is_system_generated\": is_system_generated,\n\t\t\t}\n\t\t)\n\t\tcustom_field.update(df)\n\t\tcustom_field.flags.ignore_validate = ignore_validate\n\t\tcustom_field.insert()\n\t\treturn custom_field\n\n\ndef create_custom_fields(custom_fields, ignore_validate=False, update=True):\n\t\"\"\"Add / update multiple custom fields\n\n\t:param custom_fields: example `{'Sales Invoice': [dict(fieldname='test')]}`\"\"\"\n\n\ttry:\n\t\tfrappe.flags.in_create_custom_fields = True\n\t\tdoctypes_to_update = set()\n\n\t\tif frappe.flags.in_setup_wizard:\n\t\t\tignore_validate = True\n\n\t\tfor doctypes, fields in custom_fields.items():\n\t\t\tif isinstance(fields, dict):\n\t\t\t\t# only one field\n\t\t\t\tfields = [fields]\n\n\t\t\tif isinstance(doctypes, str):\n\t\t\t\t# only one doctype\n\t\t\t\tdoctypes = (doctypes,)\n\n\t\t\tfor doctype in doctypes:\n\t\t\t\tdoctypes_to_update.add(doctype)\n\n\t\t\t\tfor df in fields:\n\t\t\t\t\tfield = frappe.db.get_value(\"Custom Field\", {\"dt\": doctype, \"fieldname\": df[\"fieldname\"]})\n\t\t\t\t\tif not field:\n\t\t\t\t\t\ttry:\n\t\t\t\t\t\t\tdf = df.copy()\n\t\t\t\t\t\t\tdf[\"owner\"] = \"Administrator\"\n\t\t\t\t\t\t\tcreate_custom_field(doctype, df, ignore_validate=ignore_validate)\n\n\t\t\t\t\t\texcept frappe.exceptions.DuplicateEntryError:\n\t\t\t\t\t\t\tpass\n\n\t\t\t\t\telif update:\n\t\t\t\t\t\tcustom_field = frappe.get_doc(\"Custom Field\", field)\n\t\t\t\t\t\tcustom_field.flags.ignore_validate = ignore_validate\n\t\t\t\t\t\tcustom_field.update(df)\n\t\t\t\t\t\tcustom_field.save()\n\n\t\tfor doctype in doctypes_to_update:\n\t\t\tfrappe.clear_cache(doctype=doctype)\n\t\t\tfrappe.db.updatedb(doctype)\n\n\tfinally:\n\t\tfrappe.flags.in_create_custom_fields = False\n", "path": "frappe/custom/doctype/custom_field/custom_field.py"}]}