');
- $wrapper.append($doctype_select, $field_select);
- field.$input_wrapper.append($wrapper);
- $doctype_select.wrap('
');
- $field_select.wrap('
');
-
- let row = frappe.get_doc(doctype, docname);
- let curr_value = { doctype: null, fieldname: null };
- if (row.fetch_from) {
- let [doctype, fieldname] = row.fetch_from.split(".");
- curr_value.doctype = doctype;
- curr_value.fieldname = fieldname;
- }
-
- let doctypes = frm.doc.fields
- .filter((df) => df.fieldtype == "Link")
- .filter((df) => df.options && df.fieldname != row.fieldname)
- .sort((a, b) => a.options.localeCompare(b.options))
- .map((df) => ({
- label: `${df.options} (${df.fieldname})`,
- value: df.fieldname,
- }));
- $doctype_select.add_options([
- { label: __("Select DocType"), value: "", selected: true },
- ...doctypes,
- ]);
-
- $doctype_select.on("change", () => {
- row.fetch_from = "";
- frm.dirty();
- update_fieldname_options();
- });
-
- function update_fieldname_options() {
- $field_select.find("option").remove();
-
- let link_fieldname = $doctype_select.val();
- if (!link_fieldname) return;
- let link_field = frm.doc.fields.find((df) => df.fieldname === link_fieldname);
- let link_doctype = link_field.options;
- frappe.model.with_doctype(link_doctype, () => {
- let fields = frappe.meta
- .get_docfields(link_doctype, null, {
- fieldtype: ["not in", frappe.model.no_value_type],
- })
- .sort((a, b) => a.label.localeCompare(b.label))
- .map((df) => ({
- label: `${df.label} (${df.fieldtype})`,
- value: df.fieldname,
- }));
- $field_select.add_options([
- {
- label: __("Select Field"),
- value: "",
- selected: true,
- disabled: true,
- },
- ...fields,
- ]);
-
- if (curr_value.fieldname) {
- $field_select.val(curr_value.fieldname);
- }
- });
- }
-
- $field_select.on("change", () => {
- let fetch_from = `${$doctype_select.val()}.${$field_select.val()}`;
- row.fetch_from = fetch_from;
- frm.dirty();
- });
-
- if (curr_value.doctype) {
- $doctype_select.val(curr_value.doctype);
- update_fieldname_options();
- }
+ frm.trigger("setup_fetch_from_fields", doctype, docname);
},
fieldtype: function (frm) {
diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json
index 14ef2fd8fb..671a6e86e6 100644
--- a/frappe/core/doctype/doctype/doctype.json
+++ b/frappe/core/doctype/doctype/doctype.json
@@ -604,6 +604,7 @@
{
"default": "0",
"depends_on": "eval: doc.is_submittable",
+ "description": "Enabling this will submit documents in background",
"fieldname": "queue_in_background",
"fieldtype": "Check",
"label": "Queue in Background"
@@ -707,7 +708,7 @@
"link_fieldname": "reference_doctype"
}
],
- "modified": "2022-12-14 09:47:27.315351",
+ "modified": "2023-01-04 17:23:09.206018",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",
@@ -744,4 +745,4 @@
"states": [],
"track_changes": 1,
"translated_doctype": 1
-}
+}
\ No newline at end of file
diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py
index e1bb23b388..6cc0adcc87 100644
--- a/frappe/core/doctype/doctype/doctype.py
+++ b/frappe/core/doctype/doctype/doctype.py
@@ -366,8 +366,10 @@ class DocType(Document):
d.fieldname = d.fieldname + "_column"
elif d.fieldtype == "Tab Break":
d.fieldname = d.fieldname + "_tab"
- else:
+ elif d.fieldtype in ("Section Break", "Column Break", "Tab Break"):
d.fieldname = d.fieldtype.lower().replace(" ", "_") + "_" + str(random_string(4))
+ else:
+ frappe.throw(_("Row #{}: Fieldname is required").format(d.idx), title="Missing Fieldname")
else:
if d.fieldname in restricted:
frappe.throw(_("Fieldname {0} is restricted").format(d.fieldname), InvalidFieldNameError)
@@ -883,7 +885,7 @@ def validate_series(dt, autoname=None, name=None):
if not autoname and dt.get("fields", {"fieldname": "naming_series"}):
dt.autoname = "naming_series:"
elif dt.autoname and dt.autoname.startswith("naming_series:"):
- fieldname = dt.autoname.split("naming_series:")[0] or "naming_series"
+ fieldname = dt.autoname.split("naming_series:", 1)[0] or "naming_series"
if not dt.get("fields", {"fieldname": fieldname}):
frappe.throw(
_("Fieldname called {0} must exist to enable autonaming").format(frappe.bold(fieldname)),
@@ -911,7 +913,7 @@ def validate_series(dt, autoname=None, name=None):
and (not autoname.startswith("format:"))
):
- prefix = autoname.split(".")[0]
+ prefix = autoname.split(".", 1)[0]
doctype = frappe.qb.DocType("DocType")
used_in = (
frappe.qb.from_(doctype)
@@ -981,7 +983,7 @@ def change_name_column_type(doctype_name: str, type: str) -> None:
def validate_links_table_fieldnames(meta):
"""Validate fieldnames in Links table"""
- if not meta.links or frappe.flags.in_patch or frappe.flags.in_fixtures:
+ if not meta.links or frappe.flags.in_patch or frappe.flags.in_fixtures or frappe.flags.in_migrate:
return
fieldnames = tuple(field.fieldname for field in meta.fields)
@@ -1096,10 +1098,7 @@ def validate_fields(meta):
)
def check_link_table_options(docname, d):
- if frappe.flags.in_patch:
- return
-
- if frappe.flags.in_fixtures:
+ if frappe.flags.in_patch or frappe.flags.in_fixtures:
return
if d.fieldtype in ("Link",) + table_fields:
@@ -1133,7 +1132,7 @@ def validate_fields(meta):
d.options = options
def check_hidden_and_mandatory(docname, d):
- if d.hidden and d.reqd and not d.default:
+ if d.hidden and d.reqd and not d.default and not frappe.flags.in_migrate:
frappe.throw(
_("{0}: Field {1} in row {2} cannot be hidden and mandatory without default").format(
docname, d.label, d.idx
@@ -1346,7 +1345,7 @@ def validate_fields(meta):
if meta.sort_field:
sort_fields = [meta.sort_field]
if "," in meta.sort_field:
- sort_fields = [d.split()[0] for d in meta.sort_field.split(",")]
+ sort_fields = [d.split(maxsplit=1)[0] for d in meta.sort_field.split(",")]
for fieldname in sort_fields:
if fieldname not in (fieldname_list + list(default_fields) + list(child_table_fields)):
@@ -1416,10 +1415,9 @@ def validate_fields(meta):
)
df_options_str = "
- " + "
- ".join(_(x) for x in data_field_options) + "
"
- frappe.msgprint(text_str + df_options_str, title="Invalid Data Field", raise_exception=True)
+ frappe.msgprint(text_str + df_options_str, title="Invalid Data Field", alert=True)
def check_child_table_option(docfield):
-
if frappe.flags.in_fixtures:
return
if docfield.fieldtype not in ["Table MultiSelect", "Table"]:
@@ -1462,31 +1460,34 @@ def validate_fields(meta):
check_invalid_fieldnames(meta.get("name"), d.fieldname)
check_unique_fieldname(meta.get("name"), d.fieldname)
check_fieldname_length(d.fieldname)
- check_illegal_mandatory(meta.get("name"), d)
- check_link_table_options(meta.get("name"), d)
- check_dynamic_link_options(d)
check_hidden_and_mandatory(meta.get("name"), d)
- check_in_list_view(meta.get("istable"), d)
- check_in_global_search(d)
- check_illegal_default(d)
check_unique_and_text(meta.get("name"), d)
- check_illegal_depends_on_conditions(d)
- check_child_table_option(d)
check_table_multiselect_option(d)
scrub_options_in_select(d)
scrub_fetch_from(d)
validate_data_field_type(d)
- check_max_height(d)
- check_no_of_ratings(d)
- check_fold(fields)
- check_search_fields(meta, fields)
- check_title_field(meta)
- check_timeline_field(meta)
- check_is_published_field(meta)
- check_website_search_field(meta)
- check_sort_field(meta)
- check_image_field(meta)
+ if not frappe.flags.in_migrate:
+ check_link_table_options(meta.get("name"), d)
+ check_illegal_mandatory(meta.get("name"), d)
+ check_dynamic_link_options(d)
+ check_in_list_view(meta.get("istable"), d)
+ check_in_global_search(d)
+ check_illegal_depends_on_conditions(d)
+ check_illegal_default(d)
+ check_child_table_option(d)
+ check_max_height(d)
+ check_no_of_ratings(d)
+
+ if not frappe.flags.in_migrate:
+ check_fold(fields)
+ check_search_fields(meta, fields)
+ check_title_field(meta)
+ check_timeline_field(meta)
+ check_is_published_field(meta)
+ check_website_search_field(meta)
+ check_sort_field(meta)
+ check_image_field(meta)
def get_fields_not_allowed_in_list_view(meta) -> list[str]:
@@ -1603,11 +1604,6 @@ def validate_permissions(doctype, for_remove=False, alert=False):
d.set("import", 0)
d.set("export", 0)
- for ptype, label in [["set_user_permissions", _("Set User Permissions")]]:
- if d.get(ptype):
- d.set(ptype, 0)
- frappe.msgprint(_("{0} cannot be set for Single types").format(label))
-
def check_if_submittable(d):
if d.submit and not issubmittable:
frappe.throw(_("{0}: Cannot set Assign Submit if not Submittable").format(get_txt(d)))
diff --git a/frappe/core/doctype/doctype/test_doctype.py b/frappe/core/doctype/doctype/test_doctype.py
index e8226d4f9d..fead7672fe 100644
--- a/frappe/core/doctype/doctype/test_doctype.py
+++ b/frappe/core/doctype/doctype/test_doctype.py
@@ -172,32 +172,6 @@ class TestDocType(FrappeTestCase):
if condition:
self.assertFalse(re.match(pattern, condition))
- def test_data_field_options(self):
- doctype_name = "Test Data Fields"
- valid_data_field_options = frappe.model.data_field_options + ("",)
- invalid_data_field_options = ("Invalid Option 1", frappe.utils.random_string(5))
-
- for field_option in valid_data_field_options + invalid_data_field_options:
- test_doctype = frappe.get_doc(
- {
- "doctype": "DocType",
- "name": doctype_name,
- "module": "Core",
- "custom": 1,
- "fields": [
- {"fieldname": f"{field_option}_field", "fieldtype": "Data", "options": field_option}
- ],
- }
- )
-
- if field_option in invalid_data_field_options:
- # assert that only data options in frappe.model.data_field_options are valid
- self.assertRaises(frappe.ValidationError, test_doctype.insert)
- else:
- test_doctype.insert()
- self.assertEqual(test_doctype.name, doctype_name)
- test_doctype.delete()
-
def test_sync_field_order(self):
import os
@@ -552,13 +526,14 @@ class TestDocType(FrappeTestCase):
self.assertRaises(InvalidFieldNameError, validate_links_table_fieldnames, doc)
def test_create_virtual_doctype(self):
- """Test virtual DOcTYpe."""
+ """Test virtual DocType."""
virtual_doc = new_doctype("Test Virtual Doctype")
virtual_doc.is_virtual = 1
- virtual_doc.insert()
- virtual_doc.save()
+ virtual_doc.insert(ignore_if_duplicate=True)
+ virtual_doc.reload()
doc = frappe.get_doc("DocType", "Test Virtual Doctype")
+ self.assertDictEqual(doc.as_dict(), virtual_doc.as_dict())
self.assertEqual(doc.is_virtual, 1)
self.assertFalse(frappe.db.table_exists("Test Virtual Doctype"))
diff --git a/frappe/core/doctype/document_naming_settings/document_naming_settings.json b/frappe/core/doctype/document_naming_settings/document_naming_settings.json
index 4c86b2ec1d..9a12f3f77e 100644
--- a/frappe/core/doctype/document_naming_settings/document_naming_settings.json
+++ b/frappe/core/doctype/document_naming_settings/document_naming_settings.json
@@ -81,10 +81,10 @@
},
{
"depends_on": "transaction_type",
- "description": "Generate 3 preview of names generate by any valid series.",
+ "description": "Get a preview of generated names with a series.",
"fieldname": "try_naming_series",
"fieldtype": "Data",
- "label": "Try a naming Series"
+ "label": "Try a Naming Series"
},
{
"fieldname": "transaction_type",
@@ -111,7 +111,7 @@
"icon": "fa fa-sort-by-order",
"issingle": 1,
"links": [],
- "modified": "2022-05-30 23:51:36.136535",
+ "modified": "2023-02-20 13:11:56.662100",
"modified_by": "Administrator",
"module": "Core",
"name": "Document Naming Settings",
@@ -130,4 +130,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
-}
+}
\ No newline at end of file
diff --git a/frappe/core/doctype/error_snapshot/test_error_snapshot.py b/frappe/core/doctype/error_snapshot/test_error_snapshot.py
index 8ff48bc5c6..4779d56c7b 100644
--- a/frappe/core/doctype/error_snapshot/test_error_snapshot.py
+++ b/frappe/core/doctype/error_snapshot/test_error_snapshot.py
@@ -1,9 +1,11 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
from frappe.tests.utils import FrappeTestCase
+from frappe.utils.logger import sanitized_dict
# test_records = frappe.get_test_records('Error Snapshot')
class TestErrorSnapshot(FrappeTestCase):
- pass
+ def test_form_dict_sanitization(self):
+ self.assertNotEqual(sanitized_dict({"pwd": "SECRET", "usr": "WHAT"}).get("pwd"), "SECRET")
diff --git a/frappe/core/doctype/file/file.js b/frappe/core/doctype/file/file.js
index 4fd092a00b..159cf1ce39 100644
--- a/frappe/core/doctype/file/file.js
+++ b/frappe/core/doctype/file/file.js
@@ -24,6 +24,8 @@ frappe.ui.form.on("File", {
preview_file: function (frm) {
let $preview = "";
+ let file_name = frm.doc.file_name.split("?")[0];
+ let file_extension = file_name.split(".").pop()?.toLowerCase();
if (frappe.utils.is_image_file(frm.doc.file_url)) {
$preview = $(`
@@ -40,7 +42,7 @@ frappe.ui.form.on("File", {
${__("Your browser does not support the video element.")}
`);
- } else if (frm.doc.file_name.split("?")[0].endsWith(".pdf")) {
+ } else if (file_extension === "pdf") {
$preview = $(`
`);
- } else if (frm.doc.file_name.split("?")[0].endsWith(".mp3")) {
+ } else if (file_extension === "mp3") {
$preview = $(`
diff --git a/frappe/public/icons/timeless/icons.svg b/frappe/public/icons/timeless/icons.svg
index fed77e7df1..cfaf3ba1d7 100644
--- a/frappe/public/icons/timeless/icons.svg
+++ b/frappe/public/icons/timeless/icons.svg
@@ -835,6 +835,13 @@