diff --git a/.github/helper/documentation.py b/.github/helper/documentation.py
index f8ee3fa10b..aece5f543b 100644
--- a/.github/helper/documentation.py
+++ b/.github/helper/documentation.py
@@ -24,6 +24,8 @@ def docs_link_exists(body):
parts = parsed_url.path.split('/')
if len(parts) == 5 and parts[1] == "frappe" and parts[2] in docs_repos:
return True
+ if parsed_url.netloc in ["docs.erpnext.com", "frappeframework.com"]:
+ return True
if __name__ == "__main__":
diff --git a/.github/helper/install.sh b/.github/helper/install.sh
index 454cc89694..19a7c68e19 100644
--- a/.github/helper/install.sh
+++ b/.github/helper/install.sh
@@ -17,7 +17,7 @@ if [ "$TYPE" == "server" ]; then
fi
if [ "$DB" == "mariadb" ];then
- sudo apt install mariadb-client-10.3
+ sudo apt update && sudo apt install mariadb-client-10.3
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'";
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'";
diff --git a/.gitignore b/.gitignore
index c9dd8f38f3..7e3d178630 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ dist/
frappe/docs/current
frappe/public/dist
.vscode
+.vs
node_modules
.kdev4/
*.kdev4
diff --git a/codecov.yml b/codecov.yml
index a9f6df0296..ed8b36c45c 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -11,6 +11,13 @@ coverage:
threshold: 0.5%
flags:
- server
+ patch:
+ default: false
+ server:
+ target: auto
+ threshold: 85%
+ flags:
+ - server
comment:
layout: "diff, flags"
diff --git a/frappe/app.py b/frappe/app.py
index 70575fe2f1..d73dd67983 100644
--- a/frappe/app.py
+++ b/frappe/app.py
@@ -185,7 +185,9 @@ def make_form_dict(request):
if 'application/json' in (request.content_type or '') and request_data:
args = json.loads(request_data)
else:
- args = request.form or request.args
+ args = {}
+ args.update(request.args or {})
+ args.update(request.form or {})
if not isinstance(args, dict):
frappe.throw(_("Invalid request arguments"))
diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py
index cd20a5c0f3..28880e7e38 100644
--- a/frappe/core/doctype/data_import/importer.py
+++ b/frappe/core/doctype/data_import/importer.py
@@ -199,7 +199,7 @@ class Importer:
new_doc = frappe.new_doc(self.doctype)
new_doc.update(doc)
- if (meta.autoname or "").lower() != "prompt":
+ if not doc.name and (meta.autoname or "").lower() != "prompt":
# name can only be set directly if autoname is prompt
new_doc.set("name", None)
diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json
index e18edc1512..03e3b65ea1 100644
--- a/frappe/core/doctype/doctype/doctype.json
+++ b/frappe/core/doctype/doctype/doctype.json
@@ -1,686 +1,700 @@
{
- "actions": [],
- "allow_rename": 1,
- "autoname": "Prompt",
- "creation": "2013-02-18 13:36:19",
- "description": "DocType is a Table / Form in the application.",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
- "sb0",
- "module",
- "is_submittable",
- "istable",
- "issingle",
- "is_tree",
- "editable_grid",
- "quick_entry",
- "cb01",
- "track_changes",
- "track_seen",
- "track_views",
- "custom",
- "beta",
- "is_virtual",
- "fields_section_break",
- "fields",
- "sb1",
- "naming_rule",
- "autoname",
- "name_case",
- "allow_rename",
- "column_break_15",
- "description",
- "documentation",
- "form_settings_section",
- "image_field",
- "timeline_field",
- "nsm_parent_field",
- "max_attachments",
- "column_break_23",
- "hide_toolbar",
- "allow_copy",
- "allow_import",
- "allow_events_in_timeline",
- "allow_auto_repeat",
- "view_settings",
- "title_field",
- "search_fields",
- "default_print_format",
- "sort_field",
- "sort_order",
- "column_break_29",
- "document_type",
- "icon",
- "color",
- "show_preview_popup",
- "show_name_in_global_search",
- "email_settings_sb",
- "default_email_template",
- "column_break_51",
- "email_append_to",
- "sender_field",
- "subject_field",
- "sb2",
- "permissions",
- "restrict_to_domain",
- "read_only",
- "in_create",
- "actions_section",
- "actions",
- "links_section",
- "links",
- "web_view",
- "has_web_view",
- "allow_guest_to_view",
- "index_web_pages_for_search",
- "route",
- "is_published_field",
- "website_search_field",
- "advanced",
- "engine",
- "migration_hash"
- ],
- "fields": [
- {
- "fieldname": "sb0",
- "fieldtype": "Section Break",
- "oldfieldtype": "Section Break"
- },
- {
- "fieldname": "module",
- "fieldtype": "Link",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Module",
- "oldfieldname": "module",
- "oldfieldtype": "Link",
- "options": "Module Def",
- "reqd": 1,
- "search_index": 1
- },
- {
- "default": "0",
- "depends_on": "eval:!doc.istable",
- "description": "Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended.",
- "fieldname": "is_submittable",
- "fieldtype": "Check",
- "label": "Is Submittable"
- },
- {
- "default": "0",
- "description": "Child Tables are shown as a Grid in other DocTypes",
- "fieldname": "istable",
- "fieldtype": "Check",
- "in_standard_filter": 1,
- "label": "Is Child Table",
- "oldfieldname": "istable",
- "oldfieldtype": "Check"
- },
- {
- "default": "0",
- "depends_on": "eval:!doc.istable",
- "description": "Single Types have only one record no tables associated. Values are stored in tabSingles",
- "fieldname": "issingle",
- "fieldtype": "Check",
- "in_standard_filter": 1,
- "label": "Is Single",
- "oldfieldname": "issingle",
- "oldfieldtype": "Check",
- "set_only_once": 1
- },
- {
- "default": "1",
- "depends_on": "istable",
- "fieldname": "editable_grid",
- "fieldtype": "Check",
- "label": "Editable Grid"
- },
- {
- "default": "0",
- "depends_on": "eval:!doc.istable && !doc.issingle",
- "description": "Open a dialog with mandatory fields to create a new record quickly",
- "fieldname": "quick_entry",
- "fieldtype": "Check",
- "label": "Quick Entry"
- },
- {
- "fieldname": "cb01",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "depends_on": "eval:!doc.istable",
- "description": "If enabled, changes to the document are tracked and shown in timeline",
- "fieldname": "track_changes",
- "fieldtype": "Check",
- "label": "Track Changes"
- },
- {
- "default": "0",
- "depends_on": "eval:!doc.istable",
- "description": "If enabled, the document is marked as seen, the first time a user opens it",
- "fieldname": "track_seen",
- "fieldtype": "Check",
- "label": "Track Seen"
- },
- {
- "default": "0",
- "depends_on": "eval:!doc.istable",
- "description": "If enabled, document views are tracked, this can happen multiple times",
- "fieldname": "track_views",
- "fieldtype": "Check",
- "label": "Track Views"
- },
- {
- "default": "0",
- "fieldname": "custom",
- "fieldtype": "Check",
- "label": "Custom?"
- },
- {
- "default": "0",
- "fieldname": "beta",
- "fieldtype": "Check",
- "label": "Beta"
- },
- {
- "fieldname": "fields_section_break",
- "fieldtype": "Section Break",
- "label": "Fields",
- "oldfieldtype": "Section Break"
- },
- {
- "fieldname": "fields",
- "fieldtype": "Table",
- "label": "Fields",
- "oldfieldname": "fields",
- "oldfieldtype": "Table",
- "options": "DocField"
- },
- {
- "fieldname": "sb1",
- "fieldtype": "Section Break",
- "label": "Naming"
- },
- {
- "description": "Naming Options:\n
')
- .appendTo(frm.fields_dict.module_html.wrapper);
-
+ const module_area = $(frm.fields_dict.module_html.wrapper);
frm.module_editor = new frappe.ModuleEditor(frm, module_area);
}
}
if (frm.module_editor) {
- frm.module_editor.refresh();
+ frm.module_editor.show();
+ }
+ },
+
+ validate: function (frm) {
+ if (frm.module_editor) {
+ frm.module_editor.set_modules_in_table();
}
}
});
diff --git a/frappe/core/doctype/module_profile/module_profile.json b/frappe/core/doctype/module_profile/module_profile.json
index 0e4e56962e..32bc757427 100644
--- a/frappe/core/doctype/module_profile/module_profile.json
+++ b/frappe/core/doctype/module_profile/module_profile.json
@@ -34,11 +34,17 @@
}
],
"index_web_pages_for_search": 1,
- "links": [],
- "modified": "2021-01-03 15:36:52.622696",
+ "links": [
+ {
+ "link_doctype": "User",
+ "link_fieldname": "module_profile"
+ }
+ ],
+ "modified": "2021-12-03 15:47:21.296443",
"modified_by": "Administrator",
"module": "Core",
"name": "Module Profile",
+ "naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
diff --git a/frappe/core/doctype/role_profile/role_profile.json b/frappe/core/doctype/role_profile/role_profile.json
index 4b3f35aa57..7cd60a16d1 100644
--- a/frappe/core/doctype/role_profile/role_profile.json
+++ b/frappe/core/doctype/role_profile/role_profile.json
@@ -1,175 +1,80 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "role_profile",
- "beta": 0,
- "creation": "2017-08-31 04:16:38.764465",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "autoname": "role_profile",
+ "creation": "2017-08-31 04:16:38.764465",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "role_profile",
+ "roles_html",
+ "roles"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "role_profile",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Role Name",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
+ "fieldname": "role_profile",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Role Name",
+ "reqd": 1,
"unique": 1
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "roles_html",
- "fieldtype": "HTML",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Roles HTML",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "roles_html",
+ "fieldtype": "HTML",
+ "label": "Roles HTML",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "roles",
- "fieldtype": "Table",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Roles Assigned",
- "length": 0,
- "no_copy": 0,
- "options": "Has Role",
- "permlevel": 1,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
+ "fieldname": "roles",
+ "fieldtype": "Table",
+ "hidden": 1,
+ "label": "Roles Assigned",
+ "options": "Has Role",
+ "permlevel": 1,
+ "print_hide": 1,
+ "read_only": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2017-10-17 11:05:11.183066",
- "modified_by": "Administrator",
- "module": "Core",
- "name": "Role Profile",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "links": [
+ {
+ "link_doctype": "User",
+ "link_fieldname": "role_profile_name"
+ }
+ ],
+ "modified": "2021-12-03 15:45:45.270963",
+ "modified_by": "Administrator",
+ "module": "Core",
+ "name": "Role Profile",
+ "naming_rule": "Expression (old style)",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "email": 1,
+ "export": 1,
+ "permlevel": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "role_profile",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "role_profile",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/frappe/core/doctype/user/test_user.py b/frappe/core/doctype/user/test_user.py
index e47846958a..b3c85b22a1 100644
--- a/frappe/core/doctype/user/test_user.py
+++ b/frappe/core/doctype/user/test_user.py
@@ -251,7 +251,7 @@ class TestUser(unittest.TestCase):
c = FrappeClient(url)
res1 = c.session.post(url, data=data, verify=c.verify, headers=c.headers)
res2 = c.session.post(url, data=data, verify=c.verify, headers=c.headers)
- self.assertEqual(res1.status_code, 200)
+ self.assertEqual(res1.status_code, 400)
self.assertEqual(res2.status_code, 417)
def test_user_rename(self):
diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js
index 2ce7413aa7..5b3a1affd9 100644
--- a/frappe/core/doctype/user/user.js
+++ b/frappe/core/doctype/user/user.js
@@ -50,7 +50,7 @@ frappe.ui.form.on('User', {
let d = frm.add_child("block_modules");
d.module = v.module;
});
- frm.module_editor && frm.module_editor.refresh();
+ frm.module_editor && frm.module_editor.show();
}
});
}
@@ -180,7 +180,7 @@ frappe.ui.form.on('User', {
frm.roles_editor.show();
}
- frm.module_editor && frm.module_editor.refresh();
+ frm.module_editor && frm.module_editor.show();
if(frappe.session.user==doc.name) {
// update display settings
diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py
index f36553593b..6c729901e5 100644
--- a/frappe/core/doctype/user/user.py
+++ b/frappe/core/doctype/user/user.py
@@ -808,6 +808,7 @@ def reset_password(user):
return frappe.msgprint(_("Password reset instructions have been sent to your email"))
except frappe.DoesNotExistError:
+ frappe.local.response['http_status_code'] = 400
frappe.clear_messages()
return 'not found'
diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js
index 8ca6e0e54e..4862185b99 100644
--- a/frappe/custom/doctype/customize_form/customize_form.js
+++ b/frappe/custom/doctype/customize_form/customize_form.js
@@ -114,6 +114,7 @@ frappe.ui.form.on("Customize Form", {
frm.page.clear_icons();
if (frm.doc.doc_type) {
+ frm.page.set_title(__('Customize Form - {0}', [frm.doc.doc_type]));
frappe.customize_form.set_primary_action(frm);
frm.add_custom_button(
@@ -276,6 +277,21 @@ frappe.ui.form.on("DocType Action", {
}
});
+// can't delete standard states
+frappe.ui.form.on("DocType State", {
+ before_states_remove: function(frm, doctype, name) {
+ let row = frappe.get_doc(doctype, name);
+ if (!(row.custom || row.__islocal)) {
+ frappe.msgprint(__("Cannot delete standard document state."));
+ throw "cannot delete standard document state";
+ }
+ },
+ states_add: function(frm, cdt, cdn) {
+ let f = frappe.model.get_doc(cdt, cdn);
+ f.custom = 1;
+ }
+});
+
frappe.customize_form.set_primary_action = function(frm) {
frm.page.set_primary_action(__("Update"), function() {
if (frm.doc.doc_type) {
diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json
index c2940a92e3..bdf95ad351 100644
--- a/frappe/custom/doctype/customize_form/customize_form.json
+++ b/frappe/custom/doctype/customize_form/customize_form.json
@@ -41,6 +41,8 @@
"actions",
"document_links_section",
"links",
+ "document_states_section",
+ "states",
"section_break_8",
"sort_field",
"column_break_10",
@@ -280,6 +282,20 @@
"fieldname": "autoname",
"fieldtype": "Data",
"label": "Auto Name"
+ },
+ {
+ "collapsible": 1,
+ "collapsible_depends_on": "states",
+ "depends_on": "doc_type",
+ "fieldname": "document_states_section",
+ "fieldtype": "Section Break",
+ "label": "Document States"
+ },
+ {
+ "fieldname": "states",
+ "fieldtype": "Table",
+ "label": "States",
+ "options": "DocType State"
}
],
"hide_toolbar": 1,
@@ -288,10 +304,11 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-06-21 19:01:06.920663",
+ "modified": "2021-12-14 16:45:04.308690",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form",
+ "naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
@@ -308,5 +325,6 @@
"search_fields": "doc_type",
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py
index 94f25a41aa..0b17200c6f 100644
--- a/frappe/custom/doctype/customize_form/customize_form.py
+++ b/frappe/custom/doctype/customize_form/customize_form.py
@@ -72,7 +72,7 @@ class CustomizeForm(Document):
new_d[prop] = d.get(prop)
self.append("fields", new_d)
- for fieldname in ('links', 'actions'):
+ for fieldname in ('links', 'actions', 'states'):
for d in meta.get(fieldname):
self.append(fieldname, d)
@@ -258,7 +258,8 @@ class CustomizeForm(Document):
'''
for doctype, fieldname, field_map in (
('DocType Link', 'links', doctype_link_properties),
- ('DocType Action', 'actions', doctype_action_properties)
+ ('DocType Action', 'actions', doctype_action_properties),
+ ('DocType State', 'states', doctype_state_properties),
):
has_custom = False
items = []
@@ -568,6 +569,11 @@ doctype_action_properties = {
'hidden': 'Check'
}
+doctype_state_properties = {
+ 'title': 'Data',
+ 'color': 'Select'
+}
+
ALLOWED_FIELDTYPE_CHANGE = (
('Currency', 'Float', 'Percent'),
diff --git a/frappe/custom/doctype/property_setter/property_setter.json b/frappe/custom/doctype/property_setter/property_setter.json
index fcb36637fe..9707f1ee1c 100644
--- a/frappe/custom/doctype/property_setter/property_setter.json
+++ b/frappe/custom/doctype/property_setter/property_setter.json
@@ -37,7 +37,7 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Applied On",
- "options": "\nDocField\nDocType\nDocType Link\nDocType Action",
+ "options": "\nDocField\nDocType\nDocType Link\nDocType Action\nDocType State",
"read_only_depends_on": "eval:!doc.__islocal",
"reqd": 1
},
@@ -109,7 +109,7 @@
"idx": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2021-09-04 12:46:17.860769",
+ "modified": "2021-12-14 14:15:41.929071",
"modified_by": "Administrator",
"module": "Custom",
"name": "Property Setter",
@@ -141,5 +141,6 @@
"search_fields": "doc_type,property",
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/frappe/database/database.py b/frappe/database/database.py
index 6a4e781b44..0f325a746e 100644
--- a/frappe/database/database.py
+++ b/frappe/database/database.py
@@ -511,14 +511,10 @@ class Database(object):
# Get coulmn and value of the single doctype Accounts Settings
account_settings = frappe.db.get_singles_dict("Accounts Settings")
"""
- result = self.sql("""
- SELECT field, value
- FROM `tabSingles`
- WHERE doctype = %s
- """, doctype)
-
+ result = self.query.get_sql(
+ "Singles", filters={"doctype": doctype}, fields=["field", "value"]
+ ).run()
dict_ = frappe._dict(result)
-
return dict_
@staticmethod
@@ -547,8 +543,11 @@ class Database(object):
if fieldname in self.value_cache[doctype]:
return self.value_cache[doctype][fieldname]
- val = self.sql("""select `value` from
- `tabSingles` where `doctype`=%s and `field`=%s""", (doctype, fieldname))
+ val = self.query.get_sql(
+ table="Singles",
+ filters={"doctype": doctype, "field": fieldname},
+ fields="value",
+ ).run()
val = val[0][0] if val else None
df = frappe.get_meta(doctype).get_field(fieldname)
diff --git a/frappe/database/query.py b/frappe/database/query.py
index 69328cb206..6d2be5fa25 100644
--- a/frappe/database/query.py
+++ b/frappe/database/query.py
@@ -286,14 +286,13 @@ class Query:
):
criterion = self.build_conditions(table, filters, **kwargs)
if isinstance(fields, (list, tuple)):
- query = criterion.select(*kwargs.get("field_objects"))
+ query = criterion.select(*kwargs.get("field_objects", fields))
elif isinstance(fields, Criterion):
query = criterion.select(fields)
else:
- if fields=="*":
- query = criterion.select(fields)
+ query = criterion.select(fields)
return query
diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py
index 9ffe9aaf06..e9a47cecd1 100644
--- a/frappe/desk/doctype/global_search_settings/global_search_settings.py
+++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py
@@ -33,7 +33,7 @@ class GlobalSearchSettings(Document):
def get_doctypes_for_global_search():
def get_from_db():
- doctypes = frappe.get_list("Global Search DocType", fields=["document_type"], order_by="idx ASC")
+ doctypes = frappe.get_all("Global Search DocType", fields=["document_type"], order_by="idx ASC")
return [d.document_type for d in doctypes] or []
return frappe.cache().hget("global_search", "search_priorities", get_from_db)
diff --git a/frappe/desk/doctype/kanban_board_column/kanban_board_column.json b/frappe/desk/doctype/kanban_board_column/kanban_board_column.json
index 95d9294e9a..c0acde5da5 100644
--- a/frappe/desk/doctype/kanban_board_column/kanban_board_column.json
+++ b/frappe/desk/doctype/kanban_board_column/kanban_board_column.json
@@ -1,155 +1,55 @@
{
- "allow_copy": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2016-10-19 12:26:42.569185",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "creation": "2016-10-19 12:26:42.569185",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "column_name",
+ "status",
+ "indicator",
+ "order"
+ ],
"fields": [
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_name",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Column Name",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "column_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Column Name"
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Active",
- "fieldname": "status",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Status",
- "length": 0,
- "no_copy": 0,
- "options": "Active\nArchived",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "default": "Active",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "options": "Active\nArchived"
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "darkgrey",
- "fieldname": "indicator",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Indicator",
- "length": 0,
- "no_copy": 0,
- "options": "blue\norange\nred\ngreen\ndarkgrey\npurple\nyellow\nlightblue",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "default": "Gray",
+ "fieldname": "indicator",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Indicator",
+ "options": "Blue\nCyan\nGray\nGreen\nLight Blue\nOrange\nPink\nPurple\nRed\nRed\nYellow"
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "order",
- "fieldtype": "Code",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Order",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
+ "fieldname": "order",
+ "fieldtype": "Code",
+ "label": "Order"
}
- ],
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
-
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2017-01-17 15:23:43.520379",
- "modified_by": "Administrator",
- "module": "Desk",
- "name": "Kanban Board Column",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2021-12-14 13:13:38.804259",
+ "modified_by": "Administrator",
+ "module": "Desk",
+ "name": "Kanban Board Column",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py
index cbf459e8ae..14ea2712e2 100644
--- a/frappe/desk/form/linked_with.py
+++ b/frappe/desk/form/linked_with.py
@@ -410,11 +410,11 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
try:
if link.get("filters"):
- ret = frappe.get_list(doctype=dt, fields=fields, filters=link.get("filters"))
+ ret = frappe.get_all(doctype=dt, fields=fields, filters=link.get("filters"))
elif link.get("get_parent"):
if me and me.parent and me.parenttype == dt:
- ret = frappe.get_list(doctype=dt, fields=fields,
+ ret = frappe.get_all(doctype=dt, fields=fields,
filters=[[dt, "name", '=', me.parent]])
else:
ret = None
@@ -426,7 +426,7 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
if link.get("doctype_fieldname"):
filters.append([link.get('child_doctype'), link.get("doctype_fieldname"), "=", doctype])
- ret = frappe.get_list(doctype=dt, fields=fields, filters=filters, or_filters=or_filters, distinct=True)
+ ret = frappe.get_all(doctype=dt, fields=fields, filters=filters, or_filters=or_filters, distinct=True)
else:
link_fieldnames = link.get("fieldname")
@@ -437,7 +437,7 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
# dynamic link
if link.get("doctype_fieldname"):
filters.append([dt, link.get("doctype_fieldname"), "=", doctype])
- ret = frappe.get_list(doctype=dt, fields=fields, filters=filters, or_filters=or_filters)
+ ret = frappe.get_all(doctype=dt, fields=fields, filters=filters, or_filters=or_filters)
else:
ret = None
diff --git a/frappe/desk/page/user_profile/user_profile_controller.js b/frappe/desk/page/user_profile/user_profile_controller.js
index c1a89f316e..40b542d5c3 100644
--- a/frappe/desk/page/user_profile/user_profile_controller.js
+++ b/frappe/desk/page/user_profile/user_profile_controller.js
@@ -17,21 +17,15 @@ class UserProfile {
show() {
let route = frappe.get_route();
this.user_id = route[1] || frappe.session.user;
-
- //validate if user
- if (route.length > 1) {
- frappe.dom.freeze(__('Loading user profile') + '...');
- frappe.db.exists('User', this.user_id).then(exists => {
- frappe.dom.unfreeze();
- if (exists) {
- this.make_user_profile();
- } else {
- frappe.msgprint(__('User does not exist'));
- }
- });
- } else {
- frappe.set_route('user-profile', frappe.session.user);
- }
+ frappe.dom.freeze(__('Loading user profile') + '...');
+ frappe.db.exists('User', this.user_id).then(exists => {
+ frappe.dom.unfreeze();
+ if (exists) {
+ this.make_user_profile();
+ } else {
+ frappe.msgprint(__('User does not exist'));
+ }
+ });
}
make_user_profile() {
@@ -74,8 +68,7 @@ class UserProfile {
primary_action_label: __('Go'),
primary_action: ({ user }) => {
dialog.hide();
- this.user_id = user;
- this.make_user_profile();
+ frappe.set_route('user-profile', user);
}
});
dialog.show();
diff --git a/frappe/desk/page/user_profile/user_profile_sidebar.html b/frappe/desk/page/user_profile/user_profile_sidebar.html
index 4a35c6cf9c..9f8889fd03 100644
--- a/frappe/desk/page/user_profile/user_profile_sidebar.html
+++ b/frappe/desk/page/user_profile/user_profile_sidebar.html
@@ -51,10 +51,10 @@
{%=__("Edit Profile") %}
{%=__("User Settings") %}
- {%=__("Leaderboard") %}
-
\ No newline at end of file
+
diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py
index 4489a68cac..d89a3d83be 100644
--- a/frappe/email/doctype/email_queue/email_queue.py
+++ b/frappe/email/doctype/email_queue/email_queue.py
@@ -18,7 +18,7 @@ from frappe import _, safe_encode, task
from frappe.model.document import Document
from frappe.email.queue import get_unsubcribed_url, get_unsubscribe_message
from frappe.email.email_body import add_attachment, get_formatted_html, get_email
-from frappe.utils import cint, split_emails, add_days, nowdate, cstr
+from frappe.utils import cint, split_emails, add_days, nowdate, cstr, get_hook_method
from frappe.email.doctype.email_account.email_account import EmailAccount
@@ -121,9 +121,13 @@ class EmailQueue(Document):
continue
message = ctx.build_message(recipient.recipient)
- if not frappe.flags.in_test:
- ctx.smtp_session.sendmail(from_addr=self.sender, to_addrs=recipient.recipient, msg=message)
- ctx.add_to_sent_list(recipient)
+ method = get_hook_method('override_email_send')
+ if method:
+ method(self, self.sender, recipient.recipient, message)
+ else:
+ if not frappe.flags.in_test:
+ ctx.smtp_session.sendmail(from_addr=self.sender, to_addrs=recipient.recipient, msg=message)
+ ctx.add_to_sent_list(recipient)
if frappe.flags.in_test:
frappe.flags.sent_mail = message
@@ -283,9 +287,14 @@ class SendMailContext:
if attachment.get('fcontent'):
continue
- fid = attachment.get("fid")
- if fid:
- _file = frappe.get_doc("File", fid)
+ file_filters = {}
+ if attachment.get('fid'):
+ file_filters['name'] = attachment.get('fid')
+ elif attachment.get('file_url'):
+ file_filters['file_url'] = attachment.get('file_url')
+
+ if file_filters:
+ _file = frappe.get_doc("File", file_filters)
fcontent = _file.get_content()
attachment.update({
'fname': _file.file_name,
@@ -293,6 +302,7 @@ class SendMailContext:
'parent': message_obj
})
attachment.pop("fid", None)
+ attachment.pop("file_url", None)
add_attachment(**attachment)
elif attachment.get("print_format_attachment") == 1:
@@ -503,7 +513,7 @@ class QueueBuilder:
if self._attachments:
# store attachments with fid or print format details, to be attached on-demand later
for att in self._attachments:
- if att.get('fid'):
+ if att.get('fid') or att.get('file_url'):
attachments.append(att)
elif att.get("print_format_attachment") == 1:
if not att.get('lang', None):
diff --git a/frappe/email/doctype/newsletter/newsletter.js b/frappe/email/doctype/newsletter/newsletter.js
index 3277d8e9ee..55805ad485 100644
--- a/frappe/email/doctype/newsletter/newsletter.js
+++ b/frappe/email/doctype/newsletter/newsletter.js
@@ -4,69 +4,137 @@
frappe.ui.form.on('Newsletter', {
refresh(frm) {
let doc = frm.doc;
- if (!doc.__islocal && !cint(doc.email_sent) && !doc.__unsaved
- && in_list(frappe.boot.user.can_write, doc.doctype)) {
- frm.add_custom_button(__('Send Now'), function() {
- frappe.confirm(__("Do you really want to send this email newsletter?"), function() {
- frm.call('send_emails').then(() => {
- frm.refresh();
- });
+ let can_write = in_list(frappe.boot.user.can_write, doc.doctype);
+ if (!frm.is_new() && !frm.is_dirty() && !doc.email_sent && can_write) {
+ frm.add_custom_button(__('Send a test email'), () => {
+ frm.events.send_test_email(frm);
+ }, __('Preview'));
+
+ frm.add_custom_button(__('Check broken links'), () => {
+ frm.dashboard.set_headline(__('Checking broken links...'));
+ frm.call('find_broken_links').then(r => {
+ frm.dashboard.set_headline('');
+ let links = r.message;
+ if (links && links.length) {
+ let html = '