From ab35ec8f48078303e609fcddef9d2794a051a0f1 Mon Sep 17 00:00:00 2001 From: AMS Fauzi <33210916+AMSFauzi7869@users.noreply.github.com> Date: Thu, 10 Nov 2022 15:35:10 +0800 Subject: [PATCH 1/6] fix: reportview permlevel bug (#18822) --- frappe/desk/reportview.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 679b052baf..9fb7e026c5 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -3,6 +3,7 @@ """build query for doclistview and return results""" +import copy import json from io import StringIO @@ -89,9 +90,10 @@ def validate_args(data): def validate_fields(data): + data_fields = copy.copy(data.fields) wildcard = update_wildcard_field_param(data) - for field in data.fields or []: + for field in data_fields or []: fieldname = extract_fieldname(field) if is_standard(fieldname): continue From faab012022f5ebf1ce9274b9aadf873539c5fe02 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 10 Nov 2022 13:57:26 +0530 Subject: [PATCH 2/6] fix: page has an empty menu button --- frappe/public/js/frappe/ui/page.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/ui/page.js b/frappe/public/js/frappe/ui/page.js index 4e67272f88..e381f332a9 100644 --- a/frappe/public/js/frappe/ui/page.js +++ b/frappe/public/js/frappe/ui/page.js @@ -327,13 +327,14 @@ frappe.ui.Page = class Page { //--- Menu --// - add_menu_item(label, click, standard, shortcut) { + add_menu_item(label, click, standard, shortcut, show_parent) { return this.add_dropdown_item({ label, click, standard, parent: this.menu, shortcut, + show_parent, }); } @@ -424,7 +425,7 @@ frappe.ui.Page = class Page { icon = null, }) { if (show_parent) { - parent.parent().removeClass("hide"); + parent.parent().removeClass("hide hidden-xl"); } let $link = this.is_in_group_button_dropdown(parent, "li > a.grey-link > span", label); @@ -602,8 +603,11 @@ frappe.ui.Page = class Page { }; // Add actions as menu item in Mobile View let menu_item_label = group ? `${group} > ${label}` : label; - let menu_item = this.add_menu_item(menu_item_label, _action, false); + let menu_item = this.add_menu_item(menu_item_label, _action, false, false, false); menu_item.parent().addClass("hidden-xl"); + if (this.menu_btn_group.hasClass("hide")) { + this.menu_btn_group.removeClass("hide").addClass("hidden-xl"); + } if (group) { var $group = this.get_or_add_inner_group_button(group); From 52ee21270ed26e3712a5ba86ed02ceda864be23f Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 10 Nov 2022 13:57:06 +0530 Subject: [PATCH 3/6] refactor: validation of permlevel --- frappe/desk/reportview.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 9fb7e026c5..b24ab21455 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -3,7 +3,6 @@ """build query for doclistview and return results""" -import copy import json from io import StringIO @@ -90,10 +89,9 @@ def validate_args(data): def validate_fields(data): - data_fields = copy.copy(data.fields) wildcard = update_wildcard_field_param(data) - for field in data_fields or []: + for field in list(data.fields or []): fieldname = extract_fieldname(field) if is_standard(fieldname): continue From bc0abd9cfd7f2cc94f849de54cab89b2e9871928 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Thu, 10 Nov 2022 15:18:49 +0530 Subject: [PATCH 4/6] feat(util): add is_last_day_of_the_month (#18835) * feat: add is_last_day_of_the_month --- frappe/tests/test_utils.py | 4 ++++ frappe/utils/data.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/frappe/tests/test_utils.py b/frappe/tests/test_utils.py index 9f3d8d00f8..44966691a0 100644 --- a/frappe/tests/test_utils.py +++ b/frappe/tests/test_utils.py @@ -493,6 +493,10 @@ class TestDateUtils(FrappeTestCase): frappe.utils.get_last_day_of_week("2020-12-28"), frappe.utils.getdate("2021-01-02") ) + def test_is_last_day_of_the_month(self): + self.assertEqual(frappe.utils.is_last_day_of_the_month("2020-12-24"), False) + self.assertEqual(frappe.utils.is_last_day_of_the_month("2020-12-31"), True) + def test_get_time(self): datetime_input = now_datetime() timedelta_input = get_timedelta() diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 8f0065b04c..00572c1ae5 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -471,6 +471,12 @@ def get_last_day(dt): return get_first_day(dt, 0, 1) + datetime.timedelta(-1) +def is_last_day_of_the_month(dt): + last_day_of_the_month = get_last_day(dt) + + return getdate(dt) == getdate(last_day_of_the_month) + + def get_quarter_ending(date): date = getdate(date) From a093b7b34ceada0c92e53557745a3568651e4456 Mon Sep 17 00:00:00 2001 From: gavin Date: Thu, 10 Nov 2022 16:22:28 +0530 Subject: [PATCH 5/6] perf(workflow): get_transitions (#18834) * Load from db only if doc passed is not of type Document. This was added earlier to load in case of dicts. ref: https://github.com/frappe/frappe/pull/11883 * Move blocks to evaluate only if required * Add typing hints --- frappe/model/workflow.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/frappe/model/workflow.py b/frappe/model/workflow.py index 923fbc1b3b..8338157996 100644 --- a/frappe/model/workflow.py +++ b/frappe/model/workflow.py @@ -1,12 +1,17 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE import json +from typing import TYPE_CHECKING, Union import frappe from frappe import _ from frappe.model.docstatus import DocStatus from frappe.utils import cint +if TYPE_CHECKING: + from frappe.model.document import Document + from frappe.workflow.doctype.workflow.workflow import Workflow + class WorkflowStateError(frappe.ValidationError): pass @@ -32,20 +37,22 @@ def get_workflow_name(doctype): @frappe.whitelist() -def get_transitions(doc, workflow=None, raise_exception=False): +def get_transitions( + doc: Union["Document", str, dict], workflow: "Workflow" = None, raise_exception: bool = False +) -> list[dict]: """Return list of possible transitions for the given doc""" - doc = frappe.get_doc(frappe.parse_json(doc)) + from frappe.model.document import Document + + if not isinstance(doc, Document): + doc = frappe.get_doc(frappe.parse_json(doc)) + doc.load_from_db() if doc.is_new(): return [] - doc.load_from_db() + doc.check_permission("read") - frappe.has_permission(doc, "read", throw=True) - roles = frappe.get_roles() - - if not workflow: - workflow = get_workflow(doc.doctype) + workflow = workflow or get_workflow(doc.doctype) current_state = doc.get(workflow.workflow_state_field) if not current_state: @@ -55,11 +62,14 @@ def get_transitions(doc, workflow=None, raise_exception=False): frappe.throw(_("Workflow State not set"), WorkflowStateError) transitions = [] + roles = frappe.get_roles() + for transition in workflow.transitions: if transition.state == current_state and transition.allowed in roles: if not is_transition_condition_satisfied(transition, doc): continue transitions.append(transition.as_dict()) + return transitions @@ -79,7 +89,7 @@ def get_workflow_safe_globals(): ) -def is_transition_condition_satisfied(transition, doc): +def is_transition_condition_satisfied(transition, doc) -> bool: if not transition.condition: return True else: @@ -198,7 +208,7 @@ def validate_workflow(doc): ) -def get_workflow(doctype): +def get_workflow(doctype) -> "Workflow": return frappe.get_doc("Workflow", get_workflow_name(doctype)) From 9ad053852157b1965ffa0c8f88666175315590f7 Mon Sep 17 00:00:00 2001 From: gavin Date: Thu, 10 Nov 2022 16:53:28 +0530 Subject: [PATCH 6/6] perf(doc): skip order_by when name is set in load_from_db (#18837) Setting ORDER BY clause in the SQL nudges MariaDB to not use index (even) for primary keys. --- frappe/model/document.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frappe/model/document.py b/frappe/model/document.py index 942c7005a2..d438544e70 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -142,9 +142,14 @@ class Document(BaseDocument): self._fix_numeric_types() else: + get_value_kwargs = {"for_update": self.flags.for_update, "as_dict": True} + if not isinstance(self.name, (dict, list)): + get_value_kwargs["order_by"] = None + d = frappe.db.get_value( - self.doctype, self.name, "*", as_dict=1, for_update=self.flags.for_update + doctype=self.doctype, filters=self.name, fieldname="*", **get_value_kwargs ) + if not d: frappe.throw( _("{0} {1} not found").format(_(self.doctype), self.name), frappe.DoesNotExistError