From e0606c9aa03362026ab4ddb23e127f3e15324e67 Mon Sep 17 00:00:00 2001 From: Shankarv19bcr Date: Mon, 26 Jan 2026 15:57:05 +0530 Subject: [PATCH 01/54] feat(file): add DocShare support to file permission checks --- frappe/core/doctype/file/file.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 3abf1d0c87..4135d9c369 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -885,6 +885,16 @@ def has_permission(doc, ptype=None, user=None, debug=False): if user != "Guest" and doc.owner == user: return True + if ( + user != "Guest" + and ptype in ("read", "write", "submit", "share") + and frappe.db.get_all( + "DocShare", + filters={"share_doctype": "File", "share_name": doc.name, ptype: 1}, + or_filters={"user": user, "everyone": 1}, + ) + ): + return True if doc.attached_to_doctype and doc.attached_to_name: attached_to_doctype = doc.attached_to_doctype From ab790b51bcb30322530a005d83869c1d8a093227 Mon Sep 17 00:00:00 2001 From: UmakanthKaspa Date: Sun, 15 Feb 2026 13:26:27 +0530 Subject: [PATCH 02/54] fix: pre-populate existing values in dynamic filters dialog --- .../desk/doctype/dashboard_chart/dashboard_chart.js | 8 +++++++- frappe/desk/doctype/number_card/number_card.js | 8 +++++++- frappe/desk/doctype/todo/todo.json | 11 +++++++++-- frappe/desk/doctype/todo/todo.py | 1 + 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js index e11d496d69..1c3f0e8f1f 100644 --- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js +++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js @@ -488,7 +488,13 @@ frappe.ui.form.on("Dashboard Chart", { }); dialog.show(); - dialog.set_values(frm.dynamic_filters); + if (frm.dynamic_filters) { + let filter_values = {}; + frm.dynamic_filters.forEach((f) => { + filter_values[f[0] + ":" + f[1]] = f[3]; + }); + dialog.set_values(filter_values); + } }); }, diff --git a/frappe/desk/doctype/number_card/number_card.js b/frappe/desk/doctype/number_card/number_card.js index c5621fe6a4..8f53d39700 100644 --- a/frappe/desk/doctype/number_card/number_card.js +++ b/frappe/desk/doctype/number_card/number_card.js @@ -405,7 +405,13 @@ frappe.ui.form.on("Number Card", { }); dialog.show(); - dialog.set_values(frm.dynamic_filters); + if (frm.dynamic_filters) { + let filter_values = {}; + frm.dynamic_filters.forEach((f) => { + filter_values[f[0] + ":" + f[1]] = f[3]; + }); + dialog.set_values(filter_values); + } }); }, diff --git a/frappe/desk/doctype/todo/todo.json b/frappe/desk/doctype/todo/todo.json index 3186a0a846..f897bbd1b2 100644 --- a/frappe/desk/doctype/todo/todo.json +++ b/frappe/desk/doctype/todo/todo.json @@ -10,6 +10,7 @@ "description_and_status", "status", "priority", + "test", "column_break_2", "color", "date", @@ -156,12 +157,17 @@ "in_standard_filter": 1, "label": "Allocated To", "options": "User" + }, + { + "fieldname": "test", + "fieldtype": "Data", + "label": "Test" } ], "icon": "fa fa-check", "idx": 2, "links": [], - "modified": "2024-03-23 16:03:58.758787", + "modified": "2026-02-15 13:06:18.389084", "modified_by": "Administrator", "module": "Desk", "name": "ToDo", @@ -191,6 +197,7 @@ } ], "quick_entry": 1, + "row_format": "Dynamic", "search_fields": "description, reference_type, reference_name", "sender_field": "sender", "sort_field": "creation", @@ -200,4 +207,4 @@ "title_field": "description", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index 77cdfb90db..6c6f578f37 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -33,6 +33,7 @@ class ToDo(Document): role: DF.Link | None sender: DF.Data | None status: DF.Literal["Open", "Closed", "Cancelled"] + test: DF.Data | None # end: auto-generated types DocType = "ToDo" From b9f659ef7481407860df57f19d379fcad0239ecb Mon Sep 17 00:00:00 2001 From: Akash Tom Date: Wed, 18 Feb 2026 15:08:39 +0530 Subject: [PATCH 03/54] fix: apply margin for header and footer for ids header-html and footer-html --- frappe/utils/pdf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index e93349f4db..9dad6463e6 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -369,6 +369,12 @@ def prepare_header_footer(soup: BeautifulSoup): # {"header-html": "/tmp/frappe-pdf-random.html"} options[html_id] = fname + + if html_id == "header-html": + options["margin-top"] = "25mm" + elif html_id == "footer-html": + options["margin-bottom"] = "25mm" + else: if html_id == "header-html": options["margin-top"] = "15mm" From 2293491aba79a3696af7b66dc14758d46a768846 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 20 Feb 2026 14:48:43 +0530 Subject: [PATCH 04/54] fix: default readonly values for datetime and time --- frappe/public/js/frappe/ui/field_group.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/public/js/frappe/ui/field_group.js b/frappe/public/js/frappe/ui/field_group.js index 98ce569ab5..caa70877d4 100644 --- a/frappe/public/js/frappe/ui/field_group.js +++ b/frappe/public/js/frappe/ui/field_group.js @@ -35,6 +35,10 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout { if (def_value == "Today" && field.df["fieldtype"] == "Date") { def_value = frappe.datetime.get_today(); + } else if (def_value == "Now" && field.df["fieldtype"] == "Datetime") { + def_value = frappe.datetime.now_datetime(); + } else if (def_value == "Now" && field.df["fieldtype"] == "Time") { + def_value = frappe.datetime.now_time(); } field.set_input(def_value); From e9730499d636591f0758a696696aafb1f4b017b6 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 20 Feb 2026 17:59:07 +0530 Subject: [PATCH 05/54] refactor: cleanup date default keyword util --- frappe/public/js/frappe/ui/field_group.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/ui/field_group.js b/frappe/public/js/frappe/ui/field_group.js index caa70877d4..a379da66b1 100644 --- a/frappe/public/js/frappe/ui/field_group.js +++ b/frappe/public/js/frappe/ui/field_group.js @@ -18,6 +18,17 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout { } } + resolve_date_default_keywords(def_value, fieldtype) { + if (def_value == "Today" && fieldtype == "Date") { + return frappe.datetime.get_today(); + } else if (def_value == "Now" && fieldtype == "Datetime") { + return frappe.datetime.now_datetime(); + } else if (def_value == "Now" && fieldtype == "Time") { + return frappe.datetime.now_time(); + } + return def_value; + } + make() { let me = this; if (this.fields) { @@ -33,13 +44,7 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout { ) return; - if (def_value == "Today" && field.df["fieldtype"] == "Date") { - def_value = frappe.datetime.get_today(); - } else if (def_value == "Now" && field.df["fieldtype"] == "Datetime") { - def_value = frappe.datetime.now_datetime(); - } else if (def_value == "Now" && field.df["fieldtype"] == "Time") { - def_value = frappe.datetime.now_time(); - } + def_value = me.resolve_date_default_keywords(def_value, field.df.fieldtype); field.set_input(def_value); // if default and has depends_on, render its fields. From 9972937b072afd1b575bd6efc2e5bb19877039bd Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 20 Feb 2026 18:11:48 +0530 Subject: [PATCH 06/54] fix: default string check ignore case --- frappe/public/js/frappe/ui/field_group.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/ui/field_group.js b/frappe/public/js/frappe/ui/field_group.js index a379da66b1..8c09a96858 100644 --- a/frappe/public/js/frappe/ui/field_group.js +++ b/frappe/public/js/frappe/ui/field_group.js @@ -19,13 +19,23 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout { } resolve_date_default_keywords(def_value, fieldtype) { - if (def_value == "Today" && fieldtype == "Date") { + if (!def_value) return def_value; + + def_value = def_value.toLowerCase(); + + if (def_value == "today" && fieldtype == "Date") { return frappe.datetime.get_today(); - } else if (def_value == "Now" && fieldtype == "Datetime") { - return frappe.datetime.now_datetime(); - } else if (def_value == "Now" && fieldtype == "Time") { - return frappe.datetime.now_time(); } + + if (def_value == "now") { + if (fieldtype == "Datetime") { + return frappe.datetime.now_datetime(); + } + if (fieldtype == "Time") { + return frappe.datetime.now_time(); + } + } + return def_value; } From 6d79d703a0a0f317e902d90ad72dcd38234127c2 Mon Sep 17 00:00:00 2001 From: Safwan Samsudeen Date: Mon, 23 Feb 2026 11:18:51 +0530 Subject: [PATCH 07/54] fix: use standard method --- frappe/core/doctype/file/file.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 4135d9c369..dadacb4b5b 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -887,12 +887,8 @@ def has_permission(doc, ptype=None, user=None, debug=False): return True if ( user != "Guest" - and ptype in ("read", "write", "submit", "share") - and frappe.db.get_all( - "DocShare", - filters={"share_doctype": "File", "share_name": doc.name, ptype: 1}, - or_filters={"user": user, "everyone": 1}, - ) + and ptype + and frappe.share.get_shared("File", filters=[["share_name", "=", doc.name]], rights=[ptype]) ): return True From a2fdff9006ca426316ba488042b06131f2e3f43f Mon Sep 17 00:00:00 2001 From: Safwan <62411302+safwansamsudeen@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:34:37 +0530 Subject: [PATCH 08/54] fix: pass on user param Co-authored-by: Akhil Narang --- frappe/core/doctype/file/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index dadacb4b5b..b98c4e3286 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -888,7 +888,7 @@ def has_permission(doc, ptype=None, user=None, debug=False): if ( user != "Guest" and ptype - and frappe.share.get_shared("File", filters=[["share_name", "=", doc.name]], rights=[ptype]) + and frappe.share.get_shared("File", filters=[["share_name", "=", doc.name]], rights=[ptype], user=user) ): return True From e15d4c837afdc6fcc6520435defd526e62ec7a0d Mon Sep 17 00:00:00 2001 From: Safwan Samsudeen Date: Mon, 23 Feb 2026 14:45:22 +0530 Subject: [PATCH 09/54] fix: validate ptype in file has_permission --- frappe/core/doctype/file/file.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index b98c4e3286..c0c5445957 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -887,8 +887,10 @@ def has_permission(doc, ptype=None, user=None, debug=False): return True if ( user != "Guest" - and ptype - and frappe.share.get_shared("File", filters=[["share_name", "=", doc.name]], rights=[ptype], user=user) + and ptype in ["read", "write", "share", "submit"] + and frappe.share.get_shared( + "File", filters=[["share_name", "=", doc.name]], rights=[ptype], user=user + ) ): return True From 0cdeee51ab682bf13f80113232de835294b533ca Mon Sep 17 00:00:00 2001 From: Aditya Patil Date: Mon, 23 Feb 2026 18:58:12 +0530 Subject: [PATCH 10/54] refactor: linked documents retrieval and UX --- frappe/desk/form/linked_with.py | 89 ++++++++++++--------- frappe/public/js/frappe/form/linked_with.js | 47 ++++++++--- frappe/public/scss/desk/list.scss | 21 +++++ 3 files changed, 105 insertions(+), 52 deletions(-) diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py index 4c54d1be4a..ccb681804f 100644 --- a/frappe/desk/form/linked_with.py +++ b/frappe/desk/form/linked_with.py @@ -437,37 +437,19 @@ def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> di is_target_doctype_table = frappe.get_meta(doctype).istable for linked_doctype, link_context in linkinfo.items(): - # Don't try to fetch linked documents if the user can't read the doctype - if not frappe.has_permission(linked_doctype): - continue - linked_doctype_meta = frappe.get_meta(linked_doctype) if linked_doctype_meta.issingle: continue + has_permission = frappe.has_permission(linked_doctype) filters = [] + or_filters = [] ret = None parent_info = None - fields = [ - d.fieldname - for d in linked_doctype_meta.get( - "fields", - { - "in_list_view": 1, - "fieldtype": ["not in", ("Image", "HTML", "Button", *frappe.model.table_fields)], - }, - ) - ] + ["name", "modified", "docstatus"] - - if add_fields := link_context.get("add_fields"): - fields += add_fields - - fields = [sf.strip() for sf in fields if sf] - if filters_ctx := link_context.get("filters"): - ret = frappe.get_list(doctype=linked_doctype, fields=fields, filters=filters_ctx, order_by=None) + filters = filters_ctx elif link_context.get("get_parent"): # check for child table @@ -478,13 +460,10 @@ def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> di doctype, name, ["parenttype", "parent"], as_dict=True, order_by=None ) - if parent_info and parent_info.parenttype == linked_doctype: - ret = frappe.get_list( - doctype=linked_doctype, - fields=fields, - filters=[[linked_doctype, "name", "=", parent_info.parent]], - order_by=None, - ) + if not (parent_info and parent_info.parenttype == linked_doctype): + continue + + filters = [[linked_doctype, "name", "=", parent_info.parent]] elif child_doctype := link_context.get("child_doctype"): or_filters = [ @@ -495,15 +474,6 @@ def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> di if doctype_fieldname := link_context.get("doctype_fieldname"): filters.append([child_doctype, doctype_fieldname, "=", doctype]) - ret = frappe.get_list( - doctype=linked_doctype, - fields=fields, - filters=filters, - or_filters=or_filters, - distinct=True, - order_by=None, - ) - elif link_fieldnames := link_context.get("fieldname"): if isinstance(link_fieldnames, str): link_fieldnames = [link_fieldnames] @@ -518,12 +488,51 @@ def get_linked_docs(doctype: str, name: str, linkinfo: dict | None = None) -> di or frappe.db.exists(linked_doctype, {"parenttype": doctype, "parent": name}) ): continue + + total_count = len( + frappe.get_all( + linked_doctype, + filters=filters, + or_filters=or_filters, + fields=["name"], + order_by=None, + ) + ) + + if not total_count: + continue + + if has_permission: + fields = [ + d.fieldname + for d in linked_doctype_meta.get( + "fields", + { + "in_list_view": 1, + "fieldtype": ["not in", ("Image", "HTML", "Button", *frappe.model.table_fields)], + }, + ) + ] + ["name", "modified", "docstatus"] + + if add_fields := link_context.get("add_fields"): + fields += add_fields + + fields = [sf.strip() for sf in fields if sf] + ret = frappe.get_list( - doctype=linked_doctype, fields=fields, filters=filters, or_filters=or_filters, order_by=None + doctype=linked_doctype, + fields=fields, + filters=filters, + or_filters=or_filters, + distinct=True, + order_by=None, ) - if ret: - results[linked_doctype] = ret + permitted_count = len(ret or []) + results[linked_doctype] = { + "docs": ret or [], + "hidden_count": total_count - permitted_count, + } return results diff --git a/frappe/public/js/frappe/form/linked_with.js b/frappe/public/js/frappe/form/linked_with.js index 12c5dc4f6b..851976f29e 100644 --- a/frappe/public/js/frappe/form/linked_with.js +++ b/frappe/public/js/frappe/form/linked_with.js @@ -20,7 +20,8 @@ frappe.ui.form.LinkedWith = class LinkedWith { make_dialog() { this.dialog = new frappe.ui.Dialog({ - title: __("Linked With"), + title: __("Links"), + minimizable: true, }); this.dialog.on_page_show = () => { @@ -39,22 +40,34 @@ frappe.ui.form.LinkedWith = class LinkedWith { make_html() { let html = ""; const linked_docs = this.frm.__linked_docs; - const linked_doctypes = Object.keys(linked_docs); + const linked_doctypes = Object.keys(linked_docs).filter((dt) => { + const entry = linked_docs[dt]; + return (entry.docs && entry.docs.length) || entry.hidden_count > 0; + }); if (linked_doctypes.length === 0) { html = __("Not Linked to any record"); } else { - html = linked_doctypes - .map((doctype) => { - const docs = linked_docs[doctype]; - return ` -
- ${this.make_doc_head(doctype)} - ${docs.map((doc) => this.make_doc_row(doc, doctype)).join("")} + html = ` +
+ ${__("Following documents are linked to {0}", [frappe.utils.get_form_link(this.frm.doctype, this.frm.docname, true).bold()])}
- `; - }) - .join(""); + ${linked_doctypes + .map((doctype) => { + const { docs, hidden_count } = linked_docs[doctype]; + let rows = (docs || []).map((doc) => this.make_doc_row(doc, doctype)).join(""); + if (hidden_count > 0) { + rows += this.make_hidden_count_row(hidden_count); + } + return ` +
+ ${this.make_doc_head(doctype)} + ${rows} +
+ `; + }) + .join("")} + `; } $(this.dialog.body).html(html); @@ -68,6 +81,16 @@ frappe.ui.form.LinkedWith = class LinkedWith { `; } + make_hidden_count_row(count) { + return `
+
+
+ ${count == 1 ? __("{0} restricted document", [count]) : __("{0} restricted documents", [count])} +
+
+
`; + } + make_doc_row(doc, doctype) { return `
diff --git a/frappe/public/scss/desk/list.scss b/frappe/public/scss/desk/list.scss index 6ac02526f5..65aa1c5b94 100644 --- a/frappe/public/scss/desk/list.scss +++ b/frappe/public/scss/desk/list.scss @@ -371,6 +371,27 @@ input.list-header-checkbox { .list-item-table { border: 1px solid $border-color; border-radius: 3px; + + .list-row-head { + border-radius: unset; + } + + .list-row-container { + border-bottom: 1px solid $border-color; + border-radius: unset; + + &:last-child { + border-bottom: none; + } + } + + .list-row-container:hover { + border-radius: unset; + } + + .list-row-container .list-row { + border-bottom: none; + } } .list-item { From 2357c2c923beab9dc8a773d62bef9582f7923a77 Mon Sep 17 00:00:00 2001 From: Aditya Patil Date: Mon, 23 Feb 2026 19:14:55 +0530 Subject: [PATCH 11/54] fix: wording ('linked with' instead of 'linked to') --- frappe/public/js/frappe/form/linked_with.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/linked_with.js b/frappe/public/js/frappe/form/linked_with.js index 851976f29e..034202da1e 100644 --- a/frappe/public/js/frappe/form/linked_with.js +++ b/frappe/public/js/frappe/form/linked_with.js @@ -50,7 +50,7 @@ frappe.ui.form.LinkedWith = class LinkedWith { } else { html = `
- ${__("Following documents are linked to {0}", [frappe.utils.get_form_link(this.frm.doctype, this.frm.docname, true).bold()])} + ${__("Following documents are linked with {0}", [frappe.utils.get_form_link(this.frm.doctype, this.frm.docname, true).bold()])}
${linked_doctypes .map((doctype) => { From 8e97332029aa13d51405992700403c4b307e7145 Mon Sep 17 00:00:00 2001 From: Luis Mendoza Date: Fri, 6 Feb 2026 16:01:22 +0000 Subject: [PATCH 12/54] fix: resolve currency precision for child table rows in unsaved documents --- frappe/model/meta.py | 6 ++++++ frappe/public/js/frappe/model/meta.js | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 9e5624252d..5d430d2c5a 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -884,6 +884,12 @@ def get_field_currency(df, doc=None): if frappe.get_meta(doc.parenttype).has_field(df.get("options")): # only get_value if parent has currency field currency = frappe.db.get_value(doc.parenttype, doc.parent, df.get("options")) + if not currency: + # Parent may not be in DB yet (new document being saved). + # Use the in-memory parent document reference if available. + parent = getattr(doc, "parent_doc", None) + if parent: + currency = parent.get(df.get("options")) if currency: frappe.local.field_currency.setdefault((doc.doctype, ref_docname), frappe._dict()).setdefault( diff --git a/frappe/public/js/frappe/model/meta.js b/frappe/public/js/frappe/model/meta.js index d393f33cb7..7692dcfdfc 100644 --- a/frappe/public/js/frappe/model/meta.js +++ b/frappe/public/js/frappe/model/meta.js @@ -332,7 +332,8 @@ $.extend(frappe.meta, { } else if (df && df.fieldtype === "Currency") { precision = cint(frappe.defaults.get_default("currency_precision")); if (!precision) { - var number_format = get_number_format(); + var currency = frappe.meta.get_field_currency(df, doc); + var number_format = get_number_format(currency); var number_format_info = get_number_format_info(number_format); precision = number_format_info.precision; } From 72237e20b6680b865793080cebc994bf3e01807a Mon Sep 17 00:00:00 2001 From: Abdul Mannan Shaikh Date: Tue, 24 Feb 2026 07:09:51 +0000 Subject: [PATCH 13/54] fix(file): relink Attach fields in child tables during document rename --- frappe/core/doctype/file/utils.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/frappe/core/doctype/file/utils.py b/frappe/core/doctype/file/utils.py index 67abd82879..9d2a797ae8 100644 --- a/frappe/core/doctype/file/utils.py +++ b/frappe/core/doctype/file/utils.py @@ -427,6 +427,29 @@ def relink_mismatched_files(doc: "Document") -> None: for df in attach_fields: if doc.get(df.fieldname): relink_files(doc, df.fieldname, doc.__temporary_name) + + # Relink files in child table Attach fields + table_fields = doc.meta.get("fields", {"fieldtype": "Table"}) + for table_df in table_fields: + child_rows = doc.get(table_df.fieldname) or [] + if not child_rows: + continue + + child_meta = frappe.get_meta(table_df.options) + child_attach_fields = child_meta.get("fields", {"fieldtype": ["in", ["Attach", "Attach Image"]]}) + + if not child_attach_fields: + continue + + for child_row in child_rows: + for child_df in child_attach_fields: + file_url = child_row.get(child_df.fieldname) + if file_url: + frappe.db.set_value( + "File", + {"file_url": file_url, "attached_to_name": doc.__temporary_name}, + {"attached_to_name": doc.name}, + ) # delete temporary name after relinking is done doc.delete_key("__temporary_name") From 0580bb80feb61946e9c719906cc0e88fce866a0f Mon Sep 17 00:00:00 2001 From: Aditya Patil Date: Tue, 24 Feb 2026 12:44:52 +0530 Subject: [PATCH 14/54] fix: pre-commit --- frappe/public/js/frappe/form/linked_with.js | 26 +++++++++++++-------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/frappe/public/js/frappe/form/linked_with.js b/frappe/public/js/frappe/form/linked_with.js index 034202da1e..2aaa3dcb95 100644 --- a/frappe/public/js/frappe/form/linked_with.js +++ b/frappe/public/js/frappe/form/linked_with.js @@ -50,23 +50,29 @@ frappe.ui.form.LinkedWith = class LinkedWith { } else { html = `
- ${__("Following documents are linked with {0}", [frappe.utils.get_form_link(this.frm.doctype, this.frm.docname, true).bold()])} + ${__("Following documents are linked with {0}", [ + frappe.utils + .get_form_link(this.frm.doctype, this.frm.docname, true) + .bold(), + ])}
${linked_doctypes - .map((doctype) => { - const { docs, hidden_count } = linked_docs[doctype]; - let rows = (docs || []).map((doc) => this.make_doc_row(doc, doctype)).join(""); - if (hidden_count > 0) { - rows += this.make_hidden_count_row(hidden_count); - } - return ` + .map((doctype) => { + const { docs, hidden_count } = linked_docs[doctype]; + let rows = (docs || []) + .map((doc) => this.make_doc_row(doc, doctype)) + .join(""); + if (hidden_count > 0) { + rows += this.make_hidden_count_row(hidden_count); + } + return `
${this.make_doc_head(doctype)} ${rows}
`; - }) - .join("")} + }) + .join("")} `; } From a0b201d9a0de052f721d3def20f0b6b9280e6cb7 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Tue, 24 Feb 2026 15:19:50 +0530 Subject: [PATCH 15/54] fix: only check default keywords for valid fields --- frappe/public/js/frappe/ui/field_group.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/ui/field_group.js b/frappe/public/js/frappe/ui/field_group.js index 8c09a96858..e482e32159 100644 --- a/frappe/public/js/frappe/ui/field_group.js +++ b/frappe/public/js/frappe/ui/field_group.js @@ -19,7 +19,7 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout { } resolve_date_default_keywords(def_value, fieldtype) { - if (!def_value) return def_value; + if (!def_value || typeof def_value !== "string") return def_value; def_value = def_value.toLowerCase(); @@ -54,7 +54,9 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout { ) return; - def_value = me.resolve_date_default_keywords(def_value, field.df.fieldtype); + if (["Date", "Datetime", "Time"].includes(field.df.fieldtype)) { + def_value = me.resolve_date_default_keywords(def_value, field.df.fieldtype); + } field.set_input(def_value); // if default and has depends_on, render its fields. From 621ba403661e6148d0d6bdad75772c5e41125ece Mon Sep 17 00:00:00 2001 From: Aditya Patil Date: Tue, 24 Feb 2026 17:56:52 +0530 Subject: [PATCH 16/54] feat: loading state and spinner for dialog primary button --- .../js/frappe/file_uploader/FileUploader.vue | 19 ++---------- .../file_uploader/file_uploader.bundle.js | 1 + .../js/frappe/form/sidebar/assign_to.js | 7 +---- frappe/public/js/frappe/list/list_filter.js | 4 +-- frappe/public/js/frappe/ui/dialog.js | 31 ++++++++++++++++++- .../public/js/frappe/utils/dashboard_utils.js | 5 ++- .../js/frappe/views/reports/query_report.js | 2 +- frappe/public/scss/common/modal.scss | 5 +++ 8 files changed, 44 insertions(+), 30 deletions(-) diff --git a/frappe/public/js/frappe/file_uploader/FileUploader.vue b/frappe/public/js/frappe/file_uploader/FileUploader.vue index 10012c82ad..f21d925efe 100644 --- a/frappe/public/js/frappe/file_uploader/FileUploader.vue +++ b/frappe/public/js/frappe/file_uploader/FileUploader.vue @@ -514,22 +514,7 @@ function check_restrictions(file) { return is_correct_type && valid_file_size; } -function set_loading_state(dialog, loading) { - let $btn = dialog?.get_primary_btn(); - if (loading) { - $btn?.css("width", $btn.outerWidth()); - $btn?.html(``); - $btn?.prop("disabled", true); - dialog?.get_secondary_btn().prop("disabled", true); - } else { - $btn?.css("width", ""); - $btn?.html(__("Upload")); - $btn?.prop("disabled", false); - dialog?.get_secondary_btn().prop("disabled", false); - } -} -function upload_files(dialog) { - set_loading_state(dialog, true); +function upload_files() { if (show_file_browser.value) { promise = upload_via_file_browser(); } else if (show_web_link.value) { @@ -542,7 +527,7 @@ function upload_files(dialog) { } else { promise = frappe.run_serially(files.value.map((file, i) => () => upload_file(file, i))); } - return promise.finally(() => set_loading_state(dialog, false)); + return promise; } function upload_via_file_browser() { let selected_file = file_browser.value.selected_node; diff --git a/frappe/public/js/frappe/file_uploader/file_uploader.bundle.js b/frappe/public/js/frappe/file_uploader/file_uploader.bundle.js index 44e9d9add6..999cc675ea 100644 --- a/frappe/public/js/frappe/file_uploader/file_uploader.bundle.js +++ b/frappe/public/js/frappe/file_uploader/file_uploader.bundle.js @@ -151,6 +151,7 @@ class FileUploader { const dialog_opts = { title: title || __("Upload"), primary_action_label: __("Upload"), + primary_action_loading_label: __("Uploading"), primary_action: () => this.upload_files(), on_page_show: () => { this.uploader.wrapper_ready = true; diff --git a/frappe/public/js/frappe/form/sidebar/assign_to.js b/frappe/public/js/frappe/form/sidebar/assign_to.js index ce4e0ca995..1e9b1ee195 100644 --- a/frappe/public/js/frappe/form/sidebar/assign_to.js +++ b/frappe/public/js/frappe/form/sidebar/assign_to.js @@ -114,9 +114,7 @@ frappe.ui.form.AssignToDialog = class AssignToDialog { let args = me.dialog.get_values(); if (args && args.assign_to) { - me.dialog.set_message("Assigning..."); - - frappe.call({ + return frappe.call({ method: me.method, args: $.extend(args, { doctype: me.doctype, @@ -125,15 +123,12 @@ frappe.ui.form.AssignToDialog = class AssignToDialog { bulk_assign: me.bulk_assign || false, re_assign: me.re_assign || false, }), - btn: me.dialog.get_primary_btn(), callback: function (r) { if (!r.exc) { if (me.callback) { me.callback(r); } me.dialog && me.dialog.hide(); - } else { - me.dialog.clear_message(); } }, }); diff --git a/frappe/public/js/frappe/list/list_filter.js b/frappe/public/js/frappe/list/list_filter.js index e46c59eca9..f3ff15587b 100644 --- a/frappe/public/js/frappe/list/list_filter.js +++ b/frappe/public/js/frappe/list/list_filter.js @@ -120,7 +120,7 @@ export default class ListFilter { fields: fields, primary_action_label: __("Create"), primary_action: (values) => { - this.bind_save_filter(dialog, values.filter_name, values?.is_global); + return this.bind_save_filter(dialog, values.filter_name, values?.is_global); }, }); dialog.show(); @@ -138,7 +138,7 @@ export default class ListFilter { dialog.fields_dict.filter_name.set_description(__("Duplicate Filter Name")); return; } - this.save_filter(value, is_global).then(() => { + return this.save_filter(value, is_global).then(() => { this.refresh_list_filter(); dialog.hide(); }); diff --git a/frappe/public/js/frappe/ui/dialog.js b/frappe/public/js/frappe/ui/dialog.js index 714edade37..8d2e978cf6 100644 --- a/frappe/public/js/frappe/ui/dialog.js +++ b/frappe/public/js/frappe/ui/dialog.js @@ -199,6 +199,7 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { this.has_primary_action = true; var me = this; const primary_btn = this.get_primary_btn().removeClass("hide").html(label); + const spinner = ``; if (typeof click == "function") { primary_btn.off("click").on("click", function () { me.primary_action_fulfilled = true; @@ -207,7 +208,35 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { // if no values then return var values = me.get_values(); if (!values) return; - click && click.apply(me, [values]); + const action = click.apply(me, [values]); + if (action && typeof action.then === "function") { + const loading_label = me.primary_action_loading_label; + primary_btn + .css({ + "min-width": primary_btn.outerWidth(), + "min-height": primary_btn.outerHeight(), + }) + .prop("disabled", true) + .addClass("btn-primary-dark") + .html( + `
+ ${spinner} + ${ + loading_label + ? `${loading_label}` + : "" + } +
` + ); + + Promise.resolve(action).finally(() => { + primary_btn + .css({ "min-width": "", "min-height": "" }) + .prop("disabled", false) + .removeClass("btn-primary-dark") + .html(label); + }); + } }); } return primary_btn; diff --git a/frappe/public/js/frappe/utils/dashboard_utils.js b/frappe/public/js/frappe/utils/dashboard_utils.js index 46059db89e..991b9da65b 100644 --- a/frappe/public/js/frappe/utils/dashboard_utils.js +++ b/frappe/public/js/frappe/utils/dashboard_utils.js @@ -264,7 +264,7 @@ frappe.dashboard_utils = { primary_action: (values) => { values.name = docname; values.set_standard = frappe.boot.developer_mode; - frappe.xcall(method, { args: values }).then(() => { + return frappe.xcall(method, { args: values }).then(() => { let dashboard_route_html = `${values.dashboard}`; let message = __("{0} {1} added to Dashboard {2}", [ doctype, @@ -273,9 +273,8 @@ frappe.dashboard_utils = { ]); frappe.msgprint(message); + dialog.hide(); }); - - dialog.hide(); }, }); diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 8693fce7fb..f790aea680 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -2109,7 +2109,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { }, ], primary_action: (values) => { - frappe.call({ + return frappe.call({ method: "frappe.desk.query_report.save_report", args: { reference_report: this.report_name, diff --git a/frappe/public/scss/common/modal.scss b/frappe/public/scss/common/modal.scss index 3cb388a3fd..214688b4a7 100644 --- a/frappe/public/scss/common/modal.scss +++ b/frappe/public/scss/common/modal.scss @@ -103,6 +103,11 @@ body.modal-open[style^="padding-right"] { button:not(:last-child) { margin-right: var(--margin-xs); } + + .btn-primary-dark { + min-width: 80px; + max-width: 200px; + } } & > * { From 332cad5b1815f6cd0236c2f3170c3428ad11b967 Mon Sep 17 00:00:00 2001 From: Aditya Patil Date: Wed, 25 Feb 2026 14:01:26 +0530 Subject: [PATCH 17/54] refactor: added `return` to pass the thenable back to primary_action in all the dialogs with async calls --- .../doctype/communication/communication.js | 4 ++-- frappe/core/doctype/doctype/doctype_list.js | 2 +- frappe/core/doctype/user/user.js | 19 ++++++++++--------- .../database_storage_usage_by_tables.js | 4 ++-- frappe/public/js/frappe/form/reminders.js | 5 ++--- .../public/js/frappe/list/bulk_operations.js | 4 +--- .../autocomplete_dialog.js | 6 ++---- frappe/public/js/frappe/ui/messages.js | 2 +- .../frappe/views/dashboard/dashboard_view.js | 5 +++-- frappe/public/js/frappe/views/interaction.js | 2 +- .../website_slideshow/website_slideshow.js | 2 +- 11 files changed, 26 insertions(+), 29 deletions(-) diff --git a/frappe/core/doctype/communication/communication.js b/frappe/core/doctype/communication/communication.js index 86ef59f994..d6103636e8 100644 --- a/frappe/core/doctype/communication/communication.js +++ b/frappe/core/doctype/communication/communication.js @@ -211,8 +211,7 @@ frappe.ui.form.on("Communication", { ], primary_action_label: __("Move"), primary_action(values) { - d.hide(); - frappe.call({ + return frappe.call({ method: "frappe.email.inbox.move_email", args: { communication: frm.doc.name, @@ -220,6 +219,7 @@ frappe.ui.form.on("Communication", { }, freeze: true, callback: function () { + d.hide(); window.history.back(); }, }); diff --git a/frappe/core/doctype/doctype/doctype_list.js b/frappe/core/doctype/doctype/doctype_list.js index 46b5e5b99d..3bb353c266 100644 --- a/frappe/core/doctype/doctype/doctype_list.js +++ b/frappe/core/doctype/doctype/doctype_list.js @@ -103,7 +103,7 @@ frappe.listview_settings["DocType"] = { primary_action_label: __("Create & Continue"), primary_action(values) { if (!values.istable) values.editable_grid = 0; - frappe.db + return frappe.db .insert({ doctype: "DocType", ...values, diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js index 4be1cfadec..3e9ff32d5e 100644 --- a/frappe/core/doctype/user/user.js +++ b/frappe/core/doctype/user/user.js @@ -201,18 +201,19 @@ frappe.ui.form.on("User", { }, ], primary_action: (values) => { - d.hide(); if (values.new_password !== values.confirm_password) { frappe.throw(__("Passwords do not match!")); } - frappe.call( - "frappe.integrations.doctype.ldap_settings.ldap_settings.reset_password", - { - user: frm.doc.email, - password: values.new_password, - logout: values.logout_sessions, - } - ); + return frappe + .call( + "frappe.integrations.doctype.ldap_settings.ldap_settings.reset_password", + { + user: frm.doc.email, + password: values.new_password, + logout: values.logout_sessions, + } + ) + .then(() => d.hide()); }, }); d.show(); diff --git a/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js b/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js index 5a1ae6f87a..093016705e 100644 --- a/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js +++ b/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js @@ -25,7 +25,7 @@ frappe.query_reports["Database Storage Usage By Tables"] = { size: "small", primary_action_label: "Optimize", primary_action(values) { - frappe.call({ + return frappe.call({ method: "frappe.core.report.database_storage_usage_by_tables.database_storage_usage_by_tables.optimize_doctype", args: { doctype_name: values.doctype_name, @@ -38,9 +38,9 @@ frappe.query_reports["Database Storage Usage By Tables"] = { ) ); } + d.hide(); }, }); - d.hide(); }, }); d.show(); diff --git a/frappe/public/js/frappe/form/reminders.js b/frappe/public/js/frappe/form/reminders.js index 5c8fa16060..667ca805d6 100644 --- a/frappe/public/js/frappe/form/reminders.js +++ b/frappe/public/js/frappe/form/reminders.js @@ -48,8 +48,7 @@ export class ReminderManager { ], primary_action_label: __("Create"), primary_action: () => { - this.create_reminder(); - this.dialog.hide(); + return this.create_reminder().then(() => this.dialog.hide()); }, secondary_action_label: __("Cancel"), secondary_action: () => { @@ -84,7 +83,7 @@ export class ReminderManager { } create_reminder() { - frappe + return frappe .xcall("frappe.automation.doctype.reminder.reminder.create_new_reminder", { remind_at: this.dialog.get_value("remind_at"), description: this.dialog.get_value("description"), diff --git a/frappe/public/js/frappe/list/bulk_operations.js b/frappe/public/js/frappe/list/bulk_operations.js index dd85c3da0d..0b794a03c3 100644 --- a/frappe/public/js/frappe/list/bulk_operations.js +++ b/frappe/public/js/frappe/list/bulk_operations.js @@ -452,9 +452,7 @@ export default class BulkOperations { primary_action: () => { let args = dialog.get_values(); if (args && args.tags) { - dialog.set_message("Adding Tags..."); - - frappe.call({ + return frappe.call({ method: "frappe.desk.doctype.tag.tag.add_tags", args: { tags: args.tags, diff --git a/frappe/public/js/frappe/ui/address_autocomplete/autocomplete_dialog.js b/frappe/public/js/frappe/ui/address_autocomplete/autocomplete_dialog.js index 53d8405ceb..3fb854c15d 100644 --- a/frappe/public/js/frappe/ui/address_autocomplete/autocomplete_dialog.js +++ b/frappe/public/js/frappe/ui/address_autocomplete/autocomplete_dialog.js @@ -48,9 +48,6 @@ frappe.ui.AddressAutocompleteDialog = class AddressAutocompleteDialog { ], primary_action_label: __("Create Address"), primary_action: () => { - // Insert the address into the database - dialog.hide(); - const address = this.parse_selected_value(); address["doctype"] = "Address"; address["links"] = [ @@ -59,7 +56,8 @@ frappe.ui.AddressAutocompleteDialog = class AddressAutocompleteDialog { link_name: this.link_name, }, ]; - frappe.db.insert(address).then((doc) => { + return frappe.db.insert(address).then((doc) => { + dialog.hide(); this.after_insert && this.after_insert(doc); }); }, diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js index 1e4661f76d..a7a7a2dd82 100644 --- a/frappe/public/js/frappe/ui/messages.js +++ b/frappe/public/js/frappe/ui/messages.js @@ -206,7 +206,7 @@ frappe.msgprint = function (msg, title, is_minimizable, re_route) { typeof data.primary_action.server_action === "string" ) { data.primary_action.action = () => { - frappe.call({ + return frappe.call({ method: data.primary_action.server_action, args: data.primary_action.args, callback() { diff --git a/frappe/public/js/frappe/views/dashboard/dashboard_view.js b/frappe/public/js/frappe/views/dashboard/dashboard_view.js index 2adaee49fb..9011902091 100644 --- a/frappe/public/js/frappe/views/dashboard/dashboard_view.js +++ b/frappe/public/js/frappe/views/dashboard/dashboard_view.js @@ -449,7 +449,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView { : chart.chart_type; chart.document_type = this.doctype; chart.filters_json = "[]"; - frappe + return frappe .xcall( "frappe.desk.doctype.dashboard_chart.dashboard_chart.create_dashboard_chart", { args: chart } @@ -460,6 +460,7 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView { name: doc.chart_name, label: chart.label, }); + dialog.hide(); }); } else { this.chart_group.new_widget.on_create({ @@ -467,8 +468,8 @@ frappe.views.DashboardView = class DashboardView extends frappe.views.ListView { label: __(chart.chart), name: chart.chart, }); + dialog.hide(); } - dialog.hide(); }, }); dialog.show(); diff --git a/frappe/public/js/frappe/views/interaction.js b/frappe/public/js/frappe/views/interaction.js index ba4ebe63d9..b6d6f80cfc 100644 --- a/frappe/public/js/frappe/views/interaction.js +++ b/frappe/public/js/frappe/views/interaction.js @@ -17,7 +17,7 @@ frappe.views.InteractionComposer = class InteractionComposer { fields: me.get_fields(), primary_action_label: __("Create"), primary_action: function () { - me.create_action(); + return me.create_action(); }, }); diff --git a/frappe/website/doctype/website_slideshow/website_slideshow.js b/frappe/website/doctype/website_slideshow/website_slideshow.js index 60a683eae8..7dea1370cb 100644 --- a/frappe/website/doctype/website_slideshow/website_slideshow.js +++ b/frappe/website/doctype/website_slideshow/website_slideshow.js @@ -31,7 +31,7 @@ frappe.ui.form.on("Website Slideshow", { ], primary_action_label: __("Add to table"), primary_action: ({ reference_doctype, reference_name }) => { - frappe.db + return frappe.db .get_list("File", { fields: ["file_url"], filters: { From 86154f0b227a699e77bb9617eb60ac0da2573323 Mon Sep 17 00:00:00 2001 From: Aditya Patil Date: Wed, 25 Feb 2026 14:26:32 +0530 Subject: [PATCH 18/54] fix: spinner styling --- frappe/public/js/frappe/ui/dialog.js | 2 +- frappe/public/scss/common/modal.scss | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/ui/dialog.js b/frappe/public/js/frappe/ui/dialog.js index 8d2e978cf6..e544b48cc4 100644 --- a/frappe/public/js/frappe/ui/dialog.js +++ b/frappe/public/js/frappe/ui/dialog.js @@ -199,7 +199,7 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { this.has_primary_action = true; var me = this; const primary_btn = this.get_primary_btn().removeClass("hide").html(label); - const spinner = ``; + const spinner = ``; if (typeof click == "function") { primary_btn.off("click").on("click", function () { me.primary_action_fulfilled = true; diff --git a/frappe/public/scss/common/modal.scss b/frappe/public/scss/common/modal.scss index 214688b4a7..19f1c8dba5 100644 --- a/frappe/public/scss/common/modal.scss +++ b/frappe/public/scss/common/modal.scss @@ -2,6 +2,12 @@ h5.modal-title { margin: 0px !important; } +@keyframes spin { + to { + transform: rotate(360deg); + } +} + // Hack to fix incorrect padding applied by Bootstrap body.modal-open[style^="padding-right"] { padding-right: 12px !important; From c0ba3d9fabd24552a3d47db65f9c146cb10a0e06 Mon Sep 17 00:00:00 2001 From: Aditya Patil Date: Wed, 25 Feb 2026 15:51:41 +0530 Subject: [PATCH 19/54] fix: spinner size and spacing to match frappe-ui --- frappe/public/js/frappe/ui/dialog.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/ui/dialog.js b/frappe/public/js/frappe/ui/dialog.js index e544b48cc4..1129ed3aca 100644 --- a/frappe/public/js/frappe/ui/dialog.js +++ b/frappe/public/js/frappe/ui/dialog.js @@ -199,7 +199,7 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { this.has_primary_action = true; var me = this; const primary_btn = this.get_primary_btn().removeClass("hide").html(label); - const spinner = ``; + const spinner = ``; if (typeof click == "function") { primary_btn.off("click").on("click", function () { me.primary_action_fulfilled = true; @@ -219,7 +219,7 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { .prop("disabled", true) .addClass("btn-primary-dark") .html( - `
+ `
${spinner} ${ loading_label From f005bf5b02a6e51f6ceefeb3d9b9f3814648e92f Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 25 Feb 2026 17:33:48 +0530 Subject: [PATCH 20/54] fix: calculate scroll position wrt correct container in jump to field --- frappe/public/js/frappe/utils/utils.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index fb888880f3..f64a4f56dc 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -328,7 +328,7 @@ Object.assign(frappe.utils, { scroll_top = typeof element == "number" ? element - cint(additional_offset) - : this.get_scroll_position(element, additional_offset); + : this.get_scroll_position(element, element_to_be_scrolled, additional_offset); } if (scroll_top < 0) { @@ -366,10 +366,26 @@ Object.assign(frappe.utils, { element_to_be_scrolled.scrollTop(scroll_top); } }, - get_scroll_position: function (element, additional_offset) { - let header_offset = - $(".navbar").height() + $(".page-head:visible").height() || $(".navbar").height(); - return $(element).offset().top - header_offset - cint(additional_offset); + get_scroll_position: function (element, element_to_be_scrolled, additional_offset) { + function getOffsetRelativeToContainer() { + let offset = 0; + + let el = element instanceof HTMLElement ? element : element[0]; + const container = element_to_be_scrolled ? element_to_be_scrolled[0] : null; + + while (el && el !== container && el.offsetParent) { + offset += el.offsetTop; + el = el.offsetParent; + } + + return offset; + } + + const navbar_height = $(".navbar").height() || 0; + const page_head_height = $(".page-head:visible").height() || 0; + const header_offset = navbar_height + page_head_height; + const element_offset_top = getOffsetRelativeToContainer(); + return element_offset_top - header_offset - cint(additional_offset); }, filter_dict: function (dict, filters) { var ret = []; From 7e344f95f2e5be3be143c7bfd9191b7a673a0a89 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 25 Feb 2026 18:24:42 +0530 Subject: [PATCH 21/54] fix: pass scrollable container from form --- frappe/public/js/frappe/form/form.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index cefd7e1527..325fcdd1e5 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -2083,7 +2083,7 @@ frappe.ui.form.Form = class FrappeForm { } // scroll to input - frappe.utils.scroll_to($el, true, 15); + frappe.utils.scroll_to($el, true, 15, $(".main-section")); // focus if text field if (focus) { From d862716267d9489fdc180eeab9d831ac9c3ddc56 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 25 Feb 2026 18:25:15 +0530 Subject: [PATCH 22/54] fix: avoid clipping highlight shadow for grid --- frappe/public/scss/common/grid.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/scss/common/grid.scss b/frappe/public/scss/common/grid.scss index fa03f4d84f..00a967a46e 100644 --- a/frappe/public/scss/common/grid.scss +++ b/frappe/public/scss/common/grid.scss @@ -779,7 +779,7 @@ .data-row.row { flex-wrap: nowrap; } -.frappe-control[data-fieldtype="Table"].form-group:has(.column-limit-reached) { +.frappe-control[data-fieldtype="Table"].form-group:has(.column-limit-reached):not(.highlight) { overflow-x: clip; } .column-limit-reached { From 8a2dca535e4cbbad502c6190959e1cd81fcfbd32 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 25 Feb 2026 18:39:04 +0530 Subject: [PATCH 23/54] fix: consider fixed tabs list height for correct vertical offset --- frappe/public/js/frappe/utils/utils.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index f64a4f56dc..c4e4d0718f 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -328,7 +328,7 @@ Object.assign(frappe.utils, { scroll_top = typeof element == "number" ? element - cint(additional_offset) - : this.get_scroll_position(element, element_to_be_scrolled, additional_offset); + : this.get_scroll_position(element, additional_offset, element_to_be_scrolled); } if (scroll_top < 0) { @@ -366,8 +366,8 @@ Object.assign(frappe.utils, { element_to_be_scrolled.scrollTop(scroll_top); } }, - get_scroll_position: function (element, element_to_be_scrolled, additional_offset) { - function getOffsetRelativeToContainer() { + get_scroll_position: function (element, additional_offset, element_to_be_scrolled) { + const get_offset_relative_to_container = () => { let offset = 0; let el = element instanceof HTMLElement ? element : element[0]; @@ -379,12 +379,19 @@ Object.assign(frappe.utils, { } return offset; - } + }; + + const get_header_offset = () => { + const navbar_height = $(".navbar").height() || 0; + const page_head_height = $(".page-head:visible").height() || 0; + const tabs_container_height = $(".form-tabs-list:visible").height() || 0; + + return navbar_height + page_head_height + tabs_container_height; + }; + + const element_offset_top = get_offset_relative_to_container(); + const header_offset = get_header_offset(); - const navbar_height = $(".navbar").height() || 0; - const page_head_height = $(".page-head:visible").height() || 0; - const header_offset = navbar_height + page_head_height; - const element_offset_top = getOffsetRelativeToContainer(); return element_offset_top - header_offset - cint(additional_offset); }, filter_dict: function (dict, filters) { From e9b1017d466f2a27409cbe9220a93419b94c5f27 Mon Sep 17 00:00:00 2001 From: Sumit Jain Date: Wed, 25 Feb 2026 20:49:06 +0530 Subject: [PATCH 24/54] fix: Add translation for labels in print format templates --- frappe/templates/print_format/macros/AttachImage.html | 2 +- frappe/templates/print_format/macros/Data.html | 2 +- frappe/templates/print_format/macros/Signature.html | 2 +- frappe/templates/print_format/macros/Table.html | 4 ++-- frappe/templates/print_format/print_format.html | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frappe/templates/print_format/macros/AttachImage.html b/frappe/templates/print_format/macros/AttachImage.html index 796662f67a..53d173a2a9 100644 --- a/frappe/templates/print_format/macros/AttachImage.html +++ b/frappe/templates/print_format/macros/AttachImage.html @@ -2,6 +2,6 @@ {%- block value -%}
- {{ df.label }} + {{ _(df.label) }}
{%- endblock -%} diff --git a/frappe/templates/print_format/macros/Data.html b/frappe/templates/print_format/macros/Data.html index 722c42ce1a..d04593f330 100644 --- a/frappe/templates/print_format/macros/Data.html +++ b/frappe/templates/print_format/macros/Data.html @@ -1,7 +1,7 @@ {% if value %}
{%- block label -%} -
{{ df.label }}
+
{{ _(df.label) }}
{%- endblock -%} {%- block value -%}
{{ doc.get_formatted(df.fieldname) }}
diff --git a/frappe/templates/print_format/macros/Signature.html b/frappe/templates/print_format/macros/Signature.html index 128ff2a927..909e49f0ab 100644 --- a/frappe/templates/print_format/macros/Signature.html +++ b/frappe/templates/print_format/macros/Signature.html @@ -2,6 +2,6 @@ {%- block value -%}
- {{ df.label }} + {{ _(df.label) }}
{%- endblock -%} diff --git a/frappe/templates/print_format/macros/Table.html b/frappe/templates/print_format/macros/Table.html index 27c0be961c..a967c923d3 100644 --- a/frappe/templates/print_format/macros/Table.html +++ b/frappe/templates/print_format/macros/Table.html @@ -1,7 +1,7 @@ {% if doc.get(df.fieldname) %}
- {{ df.label }} + {{ _(df.label) }}
{% set columns = df.table_columns %} @@ -9,7 +9,7 @@ {% for column in columns %} {% endfor %} diff --git a/frappe/templates/print_format/print_format.html b/frappe/templates/print_format/print_format.html index b9fb95a9d3..2e494b4321 100644 --- a/frappe/templates/print_format/print_format.html +++ b/frappe/templates/print_format/print_format.html @@ -21,7 +21,7 @@ {% for section in layout.sections %}
{% if section.label %} - + {% endif %}
From 15313e8747d70bcf4cdb27ff7b06910bef9bc55e Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 25 Feb 2026 15:20:03 +0530 Subject: [PATCH 25/54] fix(UI): specify px unit to image height and width labels --- frappe/printing/doctype/letter_head/letter_head.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/printing/doctype/letter_head/letter_head.json b/frappe/printing/doctype/letter_head/letter_head.json index 4ddafc47db..782f8d9484 100644 --- a/frappe/printing/doctype/letter_head/letter_head.json +++ b/frappe/printing/doctype/letter_head/letter_head.json @@ -127,12 +127,12 @@ { "fieldname": "image_height", "fieldtype": "Float", - "label": "Image Height" + "label": "Image Height (px)" }, { "fieldname": "image_width", "fieldtype": "Float", - "label": "Image Width" + "label": "Image Width (px)" }, { "depends_on": "eval:doc.footer_source==='Image' && doc.letter_head_name", @@ -148,12 +148,12 @@ { "fieldname": "footer_image_height", "fieldtype": "Float", - "label": "Image Height" + "label": "Image Height (px)" }, { "fieldname": "footer_image_width", "fieldtype": "Float", - "label": "Image Width" + "label": "Image Width (px)" }, { "fieldname": "footer_align", @@ -203,7 +203,7 @@ "links": [], "make_attachments_public": 1, "max_attachments": 3, - "modified": "2026-02-24 20:53:14.297567", + "modified": "2026-02-25 14:37:56.061516", "modified_by": "Administrator", "module": "Printing", "name": "Letter Head", From ae12ea072a31df7d4e5ca78776422e97616ac57d Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 25 Feb 2026 23:52:25 +0530 Subject: [PATCH 26/54] chore: linters check --- frappe/printing/doctype/letter_head/letter_head.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/printing/doctype/letter_head/letter_head.json b/frappe/printing/doctype/letter_head/letter_head.json index 782f8d9484..aa40d0a751 100644 --- a/frappe/printing/doctype/letter_head/letter_head.json +++ b/frappe/printing/doctype/letter_head/letter_head.json @@ -203,7 +203,7 @@ "links": [], "make_attachments_public": 1, "max_attachments": 3, - "modified": "2026-02-25 14:37:56.061516", + "modified": "2026-02-25 14:37:57.061516", "modified_by": "Administrator", "module": "Printing", "name": "Letter Head", From b58592f30cad342936c3ff68776e86f9541d3583 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 26 Feb 2026 01:15:50 +0530 Subject: [PATCH 27/54] fix: scroll to route field correctly in test --- cypress/integration/web_form.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cypress/integration/web_form.js b/cypress/integration/web_form.js index 36f65a80bc..cbcd9e5bcf 100644 --- a/cypress/integration/web_form.js +++ b/cypress/integration/web_form.js @@ -27,7 +27,9 @@ context("Web Form", () => { cy.wait("@save_form"); + cy.get('.frappe-control[data-fieldname="route"]').scrollIntoView(); cy.get_field("route").should("have.value", "note"); + cy.get(".title-area .indicator-pill") .should("contain.text", "Published") .should("have.class", "green"); From d15a6d6da0d32bd14db910d59c56d8e117d32000 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 26 Feb 2026 11:20:50 +0530 Subject: [PATCH 28/54] fix: Only show "Edit Values" button if template has something to edit Closes: https://github.com/frappe/frappe/issues/18612 --- .../website_settings/website_settings.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/frappe/website/doctype/website_settings/website_settings.js b/frappe/website/doctype/website_settings/website_settings.js index 07051588bd..a09101b062 100644 --- a/frappe/website/doctype/website_settings/website_settings.js +++ b/frappe/website/doctype/website_settings/website_settings.js @@ -16,6 +16,10 @@ frappe.ui.form.on("Website Settings", { frm.add_custom_button(__("View Website"), () => { window.open("/", "_blank"); }); + + // Check if templates have fields and show/hide edit button + frm.events.check_template_has_fields(frm, "navbar_template"); + frm.events.check_template_has_fields(frm, "footer_template"); }, set_banner_from_image: function (frm) { @@ -100,11 +104,36 @@ frappe.ui.form.on("Website Settings", { frappe.show_alert(__("Please select {0}", [frm.get_docfield(template_field).label])); return; } + let values = JSON.parse(frm.doc[values_field] || "{}"); open_web_template_values_editor(template, values).then((new_values) => { frm.set_value(values_field, JSON.stringify(new_values)); }); }, + + check_template_has_fields(frm, template_field) { + let template = frm.doc[template_field]; + let button_field = "edit_" + template_field + "_values"; + + if (!template || template === "Standard Navbar" || template === "Standard Footer") { + frm.toggle_display(button_field, false); + return; + } + + frappe.model.with_doc("Web Template", template, () => { + let doc = frappe.model.get_doc("Web Template", template); + let has_fields = doc.fields && doc.fields.length > 0; + frm.toggle_display(button_field, has_fields); + }); + }, + + navbar_template(frm) { + frm.events.check_template_has_fields(frm, "navbar_template"); + }, + + footer_template(frm) { + frm.events.check_template_has_fields(frm, "footer_template"); + }, }); frappe.ui.form.on("Top Bar Item", { From 9c0cb33267fa53f443935e8c6536ec90dad48ebb Mon Sep 17 00:00:00 2001 From: sokumon Date: Thu, 26 Feb 2026 11:42:33 +0530 Subject: [PATCH 29/54] fix: show if any of the app is installed --- frappe/desk/page/desktop/desktop.js | 1 - frappe/public/js/billing.bundle.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/desk/page/desktop/desktop.js b/frappe/desk/page/desktop/desktop.js index 514af59374..ba76bfb88f 100644 --- a/frappe/desk/page/desktop/desktop.js +++ b/frappe/desk/page/desktop/desktop.js @@ -548,7 +548,6 @@ class DesktopPage { frappe.router.on("change", function () { if (frappe.get_route()[0] == "desktop" || frappe.get_route()[0] == "") { me.setup_navbar(); - me.setup_edit_button(); } else { $(".navbar").show(); frappe.desktop_utils.close_desktop_modal(); diff --git a/frappe/public/js/billing.bundle.js b/frappe/public/js/billing.bundle.js index 79e0609c58..da8b52be7a 100644 --- a/frappe/public/js/billing.bundle.js +++ b/frappe/public/js/billing.bundle.js @@ -92,7 +92,7 @@ function addChatBubble() { const all_apps = frappe.utils.get_installed_apps(); const desk_apps = ["erpnext", "hrms"]; - const apps_allowed = frappe.utils.is_sub_array(all_apps, desk_apps); + const apps_allowed = desk_apps.some((app) => all_apps.includes(app)); if (checkBusinessHours && apps_allowed) { let chat_banner = document.createElement("script"); chat_banner.innerHTML = From 372ce74ca94e3996cfae4f7f5744606d8b88fcf5 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 26 Feb 2026 12:53:24 +0530 Subject: [PATCH 30/54] fix: use scroll_to param in url strictly for fields --- frappe/public/js/frappe/form/form.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 2570864ee3..c56c10fe09 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -1574,15 +1574,10 @@ frappe.ui.form.Form = class FrappeForm { var scroll_to = frappe.route_options.scroll_to; delete frappe.route_options.scroll_to; - var selector = []; - for (var key in scroll_to) { - var value = scroll_to[key]; - selector.push(repl('[data-%(key)s="%(value)s"]', { key: key, value: value })); - } - - selector = $(selector.join(" ")); - if (selector.length) { - frappe.utils.scroll_to(selector); + if (this.scroll_to_field(scroll_to)) { + const url = new URL(window.location); + url.searchParams.delete("scroll_to"); + history.replaceState(null, null, url); } } else if (window.location.hash) { if ($(window.location.hash).length) { From 14a003eeb72633bb0c9337ef030a2fd11bf73568 Mon Sep 17 00:00:00 2001 From: sokumon Date: Thu, 26 Feb 2026 13:09:45 +0530 Subject: [PATCH 31/54] fix: disable chat popup --- frappe/public/js/billing.bundle.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/frappe/public/js/billing.bundle.js b/frappe/public/js/billing.bundle.js index da8b52be7a..c3771f4ce9 100644 --- a/frappe/public/js/billing.bundle.js +++ b/frappe/public/js/billing.bundle.js @@ -32,9 +32,6 @@ $(document).ready(function () { !!frappe.boot.setup_complete && !frappe.is_mobile() && frappe.user.has_role("System Manager"); - if (visiblity_condition && isFCUser) { - addChatBubble(); - } if (isFCUser) { $.extend(card_args, { primary_action_label: "Upgrade", From cf2ec23f2739f0e2c4c96a3696e4988276a72d94 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 26 Feb 2026 13:27:46 +0530 Subject: [PATCH 32/54] fix: cannot scroll in folders --- frappe/desk/page/desktop/desktop.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frappe/desk/page/desktop/desktop.css b/frappe/desk/page/desktop/desktop.css index ef5e4e6062..4c294eb722 100644 --- a/frappe/desk/page/desktop/desktop.css +++ b/frappe/desk/page/desktop/desktop.css @@ -87,12 +87,17 @@ } } .modal -.modal-body .icons-container,.folder-icon .icons-container { +.modal-body .icons-container, .folder-icon .icons-container { padding:0px; margin: 0px; height: 100%; + overflow: auto; +} + +.folder-icon .icons-container { overflow: hidden; } + .icons{ gap: 16px; display: grid; From 92e3d1272774d5d2f26ac07ae0fc5ed1bb717616 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 26 Feb 2026 13:53:08 +0530 Subject: [PATCH 33/54] fix: text muted for skipped steps --- frappe/public/js/frappe/ui/user_onboarding/OnboardingPanel.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/ui/user_onboarding/OnboardingPanel.vue b/frappe/public/js/frappe/ui/user_onboarding/OnboardingPanel.vue index 9ee883cf31..6104e4d7a3 100644 --- a/frappe/public/js/frappe/ui/user_onboarding/OnboardingPanel.vue +++ b/frappe/public/js/frappe/ui/user_onboarding/OnboardingPanel.vue @@ -306,7 +306,7 @@ function markReset(step) {
{{ __(step.action_label) }} From e737289c24d2b0b02de760cc15429fc42dbb0cfd Mon Sep 17 00:00:00 2001 From: Akash Tom Date: Thu, 26 Feb 2026 14:35:17 +0530 Subject: [PATCH 34/54] fix: prevent focus on empty mandatory field on save --- frappe/public/js/frappe/form/save.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index e3c2cd1e4e..4a182d47e2 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -231,7 +231,7 @@ frappe.ui.form.check_mandatory = function (frm) { } function scroll_to(fieldname) { - if (frm.scroll_to_field(fieldname)) { + if (frm.scroll_to_field(fieldname, false)) { frm.scroll_set = true; } } From 2526dbd99aef194b74877536e0fd467b5786d75f Mon Sep 17 00:00:00 2001 From: sokumon Date: Thu, 26 Feb 2026 14:40:01 +0530 Subject: [PATCH 35/54] fix: show chat popup only in the desktop page --- frappe/public/js/billing.bundle.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/billing.bundle.js b/frappe/public/js/billing.bundle.js index c3771f4ce9..2c11bc75af 100644 --- a/frappe/public/js/billing.bundle.js +++ b/frappe/public/js/billing.bundle.js @@ -32,6 +32,16 @@ $(document).ready(function () { !!frappe.boot.setup_complete && !frappe.is_mobile() && frappe.user.has_role("System Manager"); + if (visiblity_condition && isFCUser) { + frappe.router.on("change", function () { + if (frappe.get_route()[0] == "") { + addChatBubble(); + toggleChatBubble(true); + } else { + toggleChatBubble(false); + } + }); + } if (isFCUser) { $.extend(card_args, { primary_action_label: "Upgrade", @@ -89,9 +99,10 @@ function addChatBubble() { const all_apps = frappe.utils.get_installed_apps(); const desk_apps = ["erpnext", "hrms"]; - const apps_allowed = desk_apps.some((app) => all_apps.includes(app)); + const apps_allowed = frappe.utils.is_sub_array(all_apps, desk_apps); if (checkBusinessHours && apps_allowed) { let chat_banner = document.createElement("script"); + chat_banner.setAttribute("id", "chat_widget_trigger"); chat_banner.innerHTML = '(function(d,t){var BASE_URL="https://chat.frappe.cloud";var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src=BASE_URL+"/packs/js/sdk.js";g.async=true;s.parentNode.insertBefore(g,s);g.onload=function(){window.chatwootSDK.run({websiteToken:"LdmfJzftdJGEcFjoTqk8CrSq",baseUrl:BASE_URL})}})(document,"script");'; document.body.append(chat_banner); @@ -106,3 +117,13 @@ function checkBusinessHours() { return istTime.getHours() >= 11 && istTime.getHours() < 18; } + +function toggleChatBubble(toggle) { + if (toggle) { + $(".woot-widget-holder").show(); + $("#cw-bubble-holder").show(); + } else { + $(".woot-widget-holder").hide(); + $("#cw-bubble-holder").hide(); + } +} From bc569c97b170f54887abaaaf5739afcc4f245117 Mon Sep 17 00:00:00 2001 From: Vibhuti Garachh Date: Thu, 26 Feb 2026 14:30:42 +0530 Subject: [PATCH 36/54] fix: improve grid row layout using flex for proper alignment --- frappe/public/js/frappe/form/grid_row.js | 6 ++++-- frappe/public/scss/common/grid.scss | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 348bd13840..616c794936 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -1033,7 +1033,7 @@ export default class GridRow { let is_focused = false; var $col = $( - `
` + `
` ) .attr("data-fieldname", df.fieldname) .attr("data-fieldtype", df.fieldtype) @@ -1095,7 +1095,9 @@ export default class GridRow { return out; }); - $col.field_area = $('
').appendTo($col).toggle(false); + $col.field_area = $('
') + .appendTo($col) + .toggle(false); $col.static_area = $('
').appendTo($col).html(txt); // set title attribute to see full label for columns in the heading row diff --git a/frappe/public/scss/common/grid.scss b/frappe/public/scss/common/grid.scss index fa03f4d84f..9c6e17a44b 100644 --- a/frappe/public/scss/common/grid.scss +++ b/frappe/public/scss/common/grid.scss @@ -309,8 +309,8 @@ border-radius: 0px; border: 0px; padding-top: 10px; - padding-bottom: calc(var(--padding-md) - 3px); - height: auto; + padding-bottom: 10px; + height: 100%; } .link-btn { @@ -430,6 +430,7 @@ .frappe-control { margin-bottom: 0px !important; position: relative; + flex-grow: 1; } .col-sm-6 { From 6a7343e54327a0faff4564efb48f9f8e84bd7f6e Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 26 Feb 2026 15:27:41 +0530 Subject: [PATCH 37/54] fix: empty space in report view --- frappe/public/scss/desk/report.scss | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/frappe/public/scss/desk/report.scss b/frappe/public/scss/desk/report.scss index c476bbe5c0..42f922eeae 100644 --- a/frappe/public/scss/desk/report.scss +++ b/frappe/public/scss/desk/report.scss @@ -93,6 +93,41 @@ border-radius: var(--border-radius); } } + +.report-view { + .layout-main-section { + height: calc(100vh - var(--page-head-height)); + display: flex; + flex-direction: column; + overflow: hidden; + + .page-form { + flex-shrink: 0; + } + + .frappe-list { + flex-grow: 1; + display: flex; + flex-direction: column; + overflow: hidden; + + .result, + .no-result { + flex-grow: 1; + overflow: auto; + } + + .comparison-message { + margin: 4px 0px; + } + + .no-result + .comparison-message { + display: none; + } + } + } +} + @include media-breakpoint-up(sm) { .report-view { width: calc(100% - 220px); From 3fbbe235825635410b685635b3546df6c298b52b Mon Sep 17 00:00:00 2001 From: Akash Tom Date: Thu, 26 Feb 2026 15:29:08 +0530 Subject: [PATCH 38/54] fix(Number Card): remove restriction on Currency based aggregation --- frappe/desk/doctype/number_card/number_card.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frappe/desk/doctype/number_card/number_card.js b/frappe/desk/doctype/number_card/number_card.js index c5621fe6a4..37ddd727bc 100644 --- a/frappe/desk/doctype/number_card/number_card.js +++ b/frappe/desk/doctype/number_card/number_card.js @@ -124,11 +124,6 @@ frappe.ui.form.on("Number Card", { frappe.model.with_doctype(doctype, () => { frappe.get_meta(doctype).fields.map((df) => { if (frappe.model.numeric_fieldtypes.includes(df.fieldtype)) { - if (df.fieldtype == "Currency") { - if (!df.options || df.options !== "Company:company:default_currency") { - return; - } - } aggregate_based_on_fields.push({ label: df.label, value: df.fieldname }); } }); From a2eca18bd6aee9c16405d2e0318139969c0d6716 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 26 Feb 2026 15:53:55 +0530 Subject: [PATCH 39/54] fix: hide unnecessary comparison message --- .../js/frappe/views/reports/report_view.js | 35 +++++++++++++------ frappe/public/scss/desk/report.scss | 34 +++++++++++++++--- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index 68d523b0fa..9bedbf0e11 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -99,16 +99,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { }); } - setup_paging_area() { - super.setup_paging_area(); - const message = __( - "For comparison, use >5, <10 or =324. For ranges, use 5:10 (for values between 5 & 10)." - ); - this.$paging_area.before( - `${message}` - ); - } - setup_sort_selector() { this.sort_selector = new frappe.ui.SortSelector({ parent: this.filter_area.$filter_list_wrapper, @@ -430,6 +420,8 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } setup_inline_filter_observer() { + this.setup_inline_filter_help_icons(); + this.$datatable_wrapper.on( "keyup", ".dt-filter", @@ -439,6 +431,29 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { ); } + setup_inline_filter_help_icons() { + const message = __( + "For comparison, use >5, <10 or =324.\nFor ranges, use 5:10 (for values between 5 & 10)." + ); + + this.$datatable_wrapper.find(".dt-filter").each((_, input) => { + const $input = $(input); + + if ($input.siblings(".comparison-help-icon").length) { + return; + } + + const $icon = $( + `${frappe.utils.icon( + "info", + "xs" + )}` + ); + + $input.after($icon); + }); + } + update_count_for_inline_filter() { if (!this.datatable) return; diff --git a/frappe/public/scss/desk/report.scss b/frappe/public/scss/desk/report.scss index 42f922eeae..a84b4719c3 100644 --- a/frappe/public/scss/desk/report.scss +++ b/frappe/public/scss/desk/report.scss @@ -118,10 +118,6 @@ } .comparison-message { - margin: 4px 0px; - } - - .no-result + .comparison-message { display: none; } } @@ -164,6 +160,36 @@ @include get_textstyle("base", "regular"); } +.report-view { + .datatable .dt-row-filter .dt-cell__content { + position: relative; + } + + .datatable .dt-row-filter .dt-filter.dt-input { + padding-inline-end: 1.5rem; + } + + .datatable .dt-row-filter .comparison-help-icon { + position: absolute; + top: 50%; + right: 10px; + transform: translateY(-50%); + display: inline-flex; + opacity: 0; + pointer-events: none; + transition: opacity 0.15s ease; + + .icon { + stroke: currentColor; + } + } + + .datatable .dt-row-filter .dt-filter.dt-input:focus + .comparison-help-icon { + opacity: 1; + pointer-events: auto; + } +} + .list-count { margin-right: var(--margin-sm); @include get_textstyle("base", "regular"); From c4442a3a5d617ae8fc68c9cfccf13b9b4767f283 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 26 Feb 2026 16:28:39 +0530 Subject: [PATCH 40/54] fix: don't replace # in web link fragments for attachment links --- frappe/public/js/frappe/form/sidebar/attachments.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/sidebar/attachments.js b/frappe/public/js/frappe/form/sidebar/attachments.js index bb36a2f65b..894856dcec 100644 --- a/frappe/public/js/frappe/form/sidebar/attachments.js +++ b/frappe/public/js/frappe/form/sidebar/attachments.js @@ -180,8 +180,18 @@ frappe.ui.form.Attachments = class Attachments { file_url = "/files/" + attachment.file_name; } } + + const is_web_url = /^(https?:)?\/\//i.test(file_url); + + file_url = encodeURI(file_url); + // hash is not escaped, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI - return encodeURI(file_url).replace(/#/g, "%23"); + // only encode hash if it's a local file path, not a web URL + if (!is_web_url) { + file_url = file_url.replace(/#/g, "%23"); + } + + return file_url; } get_file_id_from_file_url(file_url) { var fid; From 46c04d03e846241032d43de57993beb715303031 Mon Sep 17 00:00:00 2001 From: Safwan <62411302+safwansamsudeen@users.noreply.github.com> Date: Thu, 26 Feb 2026 16:38:48 +0530 Subject: [PATCH 41/54] fix: save issue with paginated grid (#37588) --- frappe/public/js/frappe/form/layout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 893d8abe3e..d1dc39a231 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -745,7 +745,7 @@ frappe.ui.form.Layout = class Layout { if (f.df.fieldtype === "Table") { for (const row of f.grid?.grid_rows || []) { - row.refresh_dependency(); + row?.refresh_dependency(); } } } From c0f772e71a588f70382090051c687c48d8000882 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 26 Feb 2026 17:11:34 +0530 Subject: [PATCH 42/54] fix: Limit storage usage report to current schema (#37595) For deployments where permissions somehow allow reading other schemas. --- .../database_storage_usage_by_tables.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py b/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py index df0a0c9470..df8dff27ce 100644 --- a/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py +++ b/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py @@ -22,6 +22,7 @@ def execute(filters=None): round((data_length / 1024 / 1024), 2) as data_size, round((index_length / 1024 / 1024), 2) as index_size FROM information_schema.TABLES + WHERE table_schema = DATABASE() ORDER BY (data_length + index_length) DESC; """, "postgres": """ From d5067ce684b07045011c53f8426e51f88c262330 Mon Sep 17 00:00:00 2001 From: Akash Tom Date: Thu, 26 Feb 2026 17:46:39 +0530 Subject: [PATCH 43/54] fix(Chart Widget): recreate chart on applying filter to render legend properly --- frappe/public/js/frappe/widgets/chart_widget.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frappe/public/js/frappe/widgets/chart_widget.js b/frappe/public/js/frappe/widgets/chart_widget.js index f9c1d049cc..eda2048a67 100644 --- a/frappe/public/js/frappe/widgets/chart_widget.js +++ b/frappe/public/js/frappe/widgets/chart_widget.js @@ -570,8 +570,14 @@ export default class ChartWidget extends Widget { let setup_dashboard_chart = () => { const chart_args = this.get_chart_args(); + const is_circular_chart = ["Pie", "Donut", "Percentage"].includes(this.chart_doc.type); + if (!this.dashboard_chart) { this.dashboard_chart = frappe.utils.make_chart(this.chart_wrapper[0], chart_args); + } else if (is_circular_chart) { + this.chart_wrapper.empty(); + delete this.dashboard_chart; + this.dashboard_chart = frappe.utils.make_chart(this.chart_wrapper[0], chart_args); } else { this.dashboard_chart.update(this.data); } @@ -619,6 +625,7 @@ export default class ChartWidget extends Widget { colors: colors, height: this.height, maxSlices: this.chart_doc.number_of_groups || max_slices, + truncateLegends: 0, axisOptions: { xIsSeries: this.chart_doc.timeseries, shortenYAxisNumbers: 1, From d4e6fea1ce03e9f00f9a2c530518c4bf82a91ccc Mon Sep 17 00:00:00 2001 From: KerollesFathy Date: Thu, 26 Feb 2026 14:12:27 +0000 Subject: [PATCH 44/54] fix: hide print format builder after route change --- .../printing/page/print_format_builder/print_format_builder.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/printing/page/print_format_builder/print_format_builder.js b/frappe/printing/page/print_format_builder/print_format_builder.js index bae82ba040..a40101ba87 100644 --- a/frappe/printing/page/print_format_builder/print_format_builder.js +++ b/frappe/printing/page/print_format_builder/print_format_builder.js @@ -35,6 +35,7 @@ frappe.PrintFormatBuilder = class PrintFormatBuilder { this.show_start(); } else { this.page.set_title(this.print_format.name); + this.page.sidebar.toggle(true); this.setup_print_format(); } } @@ -65,6 +66,7 @@ frappe.PrintFormatBuilder = class PrintFormatBuilder { this.page.main.html(frappe.render_template("print_format_builder_start", {})); this.page.clear_actions(); this.page.set_title(__("Print Format Builder")); + this.page.sidebar.toggle(false); this.start_edit_print_format(); this.start_new_print_format(); } From eeceba66366bda9dcfd0e26b7fb1a8eda8a3114c Mon Sep 17 00:00:00 2001 From: UmakanthKaspa Date: Thu, 26 Feb 2026 21:09:49 +0530 Subject: [PATCH 45/54] revert: undo changes to todo.json and todo.py --- frappe/desk/doctype/todo/todo.json | 11 ++--------- frappe/desk/doctype/todo/todo.py | 1 - 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/frappe/desk/doctype/todo/todo.json b/frappe/desk/doctype/todo/todo.json index f897bbd1b2..3186a0a846 100644 --- a/frappe/desk/doctype/todo/todo.json +++ b/frappe/desk/doctype/todo/todo.json @@ -10,7 +10,6 @@ "description_and_status", "status", "priority", - "test", "column_break_2", "color", "date", @@ -157,17 +156,12 @@ "in_standard_filter": 1, "label": "Allocated To", "options": "User" - }, - { - "fieldname": "test", - "fieldtype": "Data", - "label": "Test" } ], "icon": "fa fa-check", "idx": 2, "links": [], - "modified": "2026-02-15 13:06:18.389084", + "modified": "2024-03-23 16:03:58.758787", "modified_by": "Administrator", "module": "Desk", "name": "ToDo", @@ -197,7 +191,6 @@ } ], "quick_entry": 1, - "row_format": "Dynamic", "search_fields": "description, reference_type, reference_name", "sender_field": "sender", "sort_field": "creation", @@ -207,4 +200,4 @@ "title_field": "description", "track_changes": 1, "track_seen": 1 -} +} \ No newline at end of file diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index 7b4711095c..ddcbc3eb7e 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -33,7 +33,6 @@ class ToDo(Document): role: DF.Link | None sender: DF.Data | None status: DF.Literal["Open", "Closed", "Cancelled"] - test: DF.Data | None # end: auto-generated types DocType = "ToDo" From a7dbc6f0fdefd723f9b4ea4ad92abae419be276a Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Thu, 26 Feb 2026 22:14:14 +0530 Subject: [PATCH 46/54] fix: default field values not set on first render (#37384) --- frappe/public/js/frappe/ui/field_group.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/ui/field_group.js b/frappe/public/js/frappe/ui/field_group.js index e482e32159..bf6021fc3c 100644 --- a/frappe/public/js/frappe/ui/field_group.js +++ b/frappe/public/js/frappe/ui/field_group.js @@ -44,7 +44,9 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout { if (this.fields) { super.make(); this.refresh(); - // set default + + let defaults = {}; + $.each(this.fields_list, function (i, field) { let def_value = field.df["default"]; // loose equality check matches undefined also @@ -58,8 +60,10 @@ frappe.ui.FieldGroup = class FieldGroup extends frappe.ui.form.Layout { def_value = me.resolve_date_default_keywords(def_value, field.df.fieldtype); } - field.set_input(def_value); - // if default and has depends_on, render its fields. + defaults[field.df.fieldname] = def_value; + }); + + this.set_values(defaults).then(() => { me.refresh_dependency(); }); From 8e3392d3c7fb3db98b3639eb3dd5fff768822e48 Mon Sep 17 00:00:00 2001 From: Shrihari Mahabal Date: Thu, 26 Feb 2026 23:06:11 +0530 Subject: [PATCH 47/54] fix: make display_depends_on work for Column Break --- frappe/public/js/frappe/form/column.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/form/column.js b/frappe/public/js/frappe/form/column.js index 92ec3d8917..e46cd49ef6 100644 --- a/frappe/public/js/frappe/form/column.js +++ b/frappe/public/js/frappe/form/column.js @@ -37,24 +37,35 @@ export default class Column { } resize_all_columns() { - // distribute all columns equally - let columns = this.section.wrapper.find(".form-column").length; + // distribute visible columns equally + let all_columns = this.section.wrapper.find(".form-column"); + let visible_columns = all_columns.filter(":not(.hide-control)"); + let columns = visible_columns.length || all_columns.length; let colspan = cint(12 / columns); if (columns == 5) { colspan = 20; } - this.section.wrapper - .find(".form-column") - .removeClass() - .addClass("form-column") - .addClass("col-sm-" + colspan); + all_columns.each(function () { + const $col = $(this); + const is_hidden = $col.hasClass("hide-control"); + $col.removeClass() + .addClass("form-column") + .addClass("col-sm-" + colspan); + if (is_hidden) { + $col.addClass("hide-control"); + } + }); } add_field() {} refresh() { + if (!this.df) return; + const hide = this.df.hidden || this.df.hidden_due_to_dependency; + this.wrapper.toggleClass("hide-control", !!hide); + this.resize_all_columns(); this.section.refresh(); } } From f20780fe8f6432f7a43dba2213755a130bca501a Mon Sep 17 00:00:00 2001 From: KerollesFathy Date: Thu, 26 Feb 2026 21:03:00 +0000 Subject: [PATCH 48/54] fix: add translate placeholder --- frappe/public/js/frappe/form/controls/color.js | 2 +- frappe/public/js/frappe/form/controls/data.js | 2 +- frappe/public/js/frappe/form/controls/icon.js | 2 +- frappe/public/js/frappe/form/controls/select.js | 2 +- frappe/public/js/frappe/form/controls/text_editor.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/color.js b/frappe/public/js/frappe/form/controls/color.js index e9f88faec5..65c9f1b1d3 100644 --- a/frappe/public/js/frappe/form/controls/color.js +++ b/frappe/public/js/frappe/form/controls/color.js @@ -2,7 +2,7 @@ import Picker from "../../color_picker/color_picker"; frappe.ui.form.ControlColor = class ControlColor extends frappe.ui.form.ControlData { make_input() { - this.df.placeholder = this.df.placeholder || __("Choose a color"); + this.df.placeholder = __(this.df.placeholder) || __("Choose a color"); super.make_input(); this.make_color_input(); } diff --git a/frappe/public/js/frappe/form/controls/data.js b/frappe/public/js/frappe/form/controls/data.js index 3cd3446d2f..69f2b5d519 100644 --- a/frappe/public/js/frappe/form/controls/data.js +++ b/frappe/public/js/frappe/form/controls/data.js @@ -241,7 +241,7 @@ frappe.ui.form.ControlData = class ControlData extends frappe.ui.form.ControlInp this.$input .attr("data-fieldtype", this.df.fieldtype) .attr("data-fieldname", this.df.fieldname) - .attr("placeholder", this.df.placeholder || ""); + .attr("placeholder", __(this.df.placeholder || "")); if (this.doctype) { this.$input.attr("data-doctype", this.doctype); } diff --git a/frappe/public/js/frappe/form/controls/icon.js b/frappe/public/js/frappe/form/controls/icon.js index a964153d2b..6f86665c2b 100644 --- a/frappe/public/js/frappe/form/controls/icon.js +++ b/frappe/public/js/frappe/form/controls/icon.js @@ -2,7 +2,7 @@ import Picker from "../../icon_picker/icon_picker"; frappe.ui.form.ControlIcon = class ControlIcon extends frappe.ui.form.ControlData { make_input() { - this.df.placeholder = this.df.placeholder || __("Choose an icon"); + this.df.placeholder = __(this.df.placeholder) || __("Choose an icon"); super.make_input(); this.get_all_icons(); this.make_icon_input(); diff --git a/frappe/public/js/frappe/form/controls/select.js b/frappe/public/js/frappe/form/controls/select.js index eee88cb149..c6802b5984 100644 --- a/frappe/public/js/frappe/form/controls/select.js +++ b/frappe/public/js/frappe/form/controls/select.js @@ -28,7 +28,7 @@ frappe.ui.form.ControlSelect = class ControlSelect extends frappe.ui.form.Contro const placeholder_html = `
- ${this.df.placeholder} + ${__(this.df.placeholder)}
`; if (this.only_input) { this.$wrapper.append(placeholder_html); diff --git a/frappe/public/js/frappe/form/controls/text_editor.js b/frappe/public/js/frappe/form/controls/text_editor.js index 6bbb9c1aa0..0264f0ae3b 100644 --- a/frappe/public/js/frappe/form/controls/text_editor.js +++ b/frappe/public/js/frappe/form/controls/text_editor.js @@ -235,7 +235,7 @@ frappe.ui.form.ControlTextEditor = class ControlTextEditor extends frappe.ui.for theme: this.df.theme || "snow", readOnly: this.disabled || this.df.read_only, bounds: this.quill_container[0], - placeholder: this.df.placeholder || "", + placeholder: __(this.df.placeholder || ""), }; // In a grid row where space is constrained, hide the toolbar. From 28b72250319c2bc8fbab71b29cc20c0719ee549a Mon Sep 17 00:00:00 2001 From: MochaMind Date: Fri, 27 Feb 2026 04:57:04 +0530 Subject: [PATCH 49/54] fix: Serbian (Cyrillic) translations --- frappe/locale/sr.po | 76 ++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/frappe/locale/sr.po b/frappe/locale/sr.po index 97e1c2ff25..59a68f9ff8 100644 --- a/frappe/locale/sr.po +++ b/frappe/locale/sr.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" "POT-Creation-Date: 2026-02-22 09:42+0000\n" -"PO-Revision-Date: 2026-02-23 22:07\n" +"PO-Revision-Date: 2026-02-26 23:27\n" "Last-Translator: developers@frappe.io\n" "Language-Team: Serbian (Cyrillic)\n" "MIME-Version: 1.0\n" @@ -1357,7 +1357,7 @@ msgstr "Додај параметре упита" #. Label of the add_reply_to_header (Check) field in DocType 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "Add Reply-To header" -msgstr "" +msgstr "Додај заглавље адресе за одговор" #: frappe/core/doctype/user/user.py:860 msgid "Add Roles" @@ -1523,7 +1523,7 @@ msgstr "Додај на контролну таблу" #: frappe/desk/doctype/workspace/workspace.js:49 msgid "Add to Desktop" -msgstr "" +msgstr "Додај на радну површину" #: frappe/public/js/frappe/form/sidebar/assign_to.js:110 msgid "Add to ToDo" @@ -1646,7 +1646,7 @@ msgstr "Адресе и контакти" #. Account' #: frappe/email/doctype/email_account/email_account.json msgid "Addresses added here will be used as the Reply-To header for outgoing emails sent from this account." -msgstr "" +msgstr "Адресе додате овде користиће се као адреса за одговор за излазне имејлове послате са овог налога." #. Description of a DocType #: frappe/custom/doctype/client_script/client_script.json @@ -3082,7 +3082,7 @@ msgstr "Историја измена" #. Label of a Workspace Sidebar Item #: frappe/workspace_sidebar/users.json msgid "Audits" -msgstr "" +msgstr "Ревизије" #. Label of the auth_url_data (Code) field in DocType 'Social Login Key' #: frappe/integrations/doctype/social_login_key/social_login_key.json @@ -3516,7 +3516,7 @@ msgstr "Слика позадине" #. Label of a Workspace Sidebar Item #: frappe/workspace_sidebar/system.json msgid "Background Job" -msgstr "" +msgstr "Позадински задатак" #. Label of a Link in the Build Workspace #. Label of the background_jobs_section (Section Break) field in DocType @@ -4934,7 +4934,7 @@ msgstr "Кликните да поставите филтере" #: frappe/desk/page/desktop/desktop.js:1261 msgid "Click to edit" -msgstr "" +msgstr "Кликните за уређивање" #: frappe/public/js/frappe/list/list_view.js:754 msgid "Click to sort by {0}" @@ -6537,7 +6537,7 @@ msgstr "Цијан" #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "DELAY" -msgstr "" +msgstr "ОДЛАГАЊЕ" #. Option for the 'Method' (Select) field in DocType 'Recorder' #. Option for the 'Request Method' (Select) field in DocType 'Webhook' @@ -7365,7 +7365,7 @@ msgstr "Статус" #. Label of the dsn_notify_type (Select) field in DocType 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "Delivery Status Notification Type" -msgstr "" +msgstr "Врста обавештења о статусу испоруке" #. Option for the 'Sign ups' (Select) field in DocType 'Social Login Key' #: frappe/integrations/doctype/social_login_key/social_login_key.json @@ -9430,7 +9430,7 @@ msgstr "Планер омогућен" #. Label of the enabled (Check) field in DocType 'Notification Settings' #: frappe/desk/doctype/notification_settings/notification_settings.json msgid "Enabled System Notification" -msgstr "" +msgstr "Омогућено системско обавештење" #: frappe/email/doctype/email_account/email_account.py:1101 msgid "Enabled email inbox for user {0}" @@ -10128,7 +10128,7 @@ msgstr "Додатни параметри" #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "FAILURE" -msgstr "" +msgstr "НЕУСПЕХ" #. Option for the 'Social Login Provider' (Select) field in DocType 'Social #. Login Key' @@ -10272,7 +10272,7 @@ msgstr "Неуспешан покушај пријаве на Frappe Cloud" #: frappe/email/doctype/email_account/email_account.py:232 msgid "Failed to retrieve the list of IMAP folders from the server. Please ensure the mailbox is accessible and the account has permission to list folders." -msgstr "" +msgstr "Неуспешно преузимање листе IMAP директоријума са сервера. Проверите да ли је поштанско сандуче доступно и да ли налог има дозволу за приказ директоријума." #: frappe/email/doctype/email_queue/email_queue.py:311 msgid "Failed to send email with subject:" @@ -11339,7 +11339,7 @@ msgstr "Јединица фракције" #. Label of a Desktop Icon #: frappe/desktop_icon/framework.json msgid "Framework" -msgstr "" +msgstr "Framework" #. Option for the 'Social Login Provider' (Select) field in DocType 'Social #. Login Key' @@ -12235,7 +12235,7 @@ msgstr "Наслов" #. Label of a Workspace Sidebar Item #: frappe/workspace_sidebar/system.json msgid "Health Report" -msgstr "" +msgstr "Извештај о стању система" #. Option for the 'Type' (Select) field in DocType 'Dashboard Chart' #: frappe/desk/doctype/dashboard_chart/dashboard_chart.json @@ -12654,7 +12654,7 @@ msgstr "IMAP датотека" #: frappe/email/doctype/email_account/email_account.py:235 #: frappe/email/doctype/email_account/email_account.py:263 msgid "IMAP Folder Not Found" -msgstr "" +msgstr "IMAP директоријум није пронађен" #. Label of the ip_address (Data) field in DocType 'Activity Log' #. Label of the ip_address (Data) field in DocType 'Comment' @@ -13414,7 +13414,7 @@ msgstr "Погрешан верификациони код" #: frappe/public/js/frappe/views/gantt/gantt_view.js:88 msgid "Incorrect configuration" -msgstr "" +msgstr "Неисправна конфигурација" #: frappe/model/document.py:1733 msgid "Incorrect value in row {0}:" @@ -16979,7 +16979,7 @@ msgstr "MyISAM" #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "NEVER" -msgstr "" +msgstr "НИКАДА" #: frappe/workflow/doctype/workflow/workflow.js:19 msgid "NOTE: If you add states or transitions in the table, it will be reflected in the Workflow Builder but you will have to position them manually. Also Workflow Builder is currently in BETA." @@ -17750,7 +17750,7 @@ msgstr "Нема података за извоз" #: frappe/public/js/frappe/views/reports/query_report.js:1543 msgid "No data to perform this action" -msgstr "" +msgstr "Нема података за извршавање ове радње" #: frappe/contacts/doctype/address/address.py:247 msgid "No default Address Template found. Please create a new one from Setup > Printing and Branding > Address Template." @@ -17795,7 +17795,7 @@ msgstr "Нема додатних записа" #: frappe/public/js/frappe/views/reports/report_view.js:337 msgid "No matching entries in the current results" -msgstr "" +msgstr "Нема подударних записа у тренутним резултатима" #: frappe/templates/includes/search_template.html:49 msgid "No matching records. Search something new" @@ -18429,7 +18429,7 @@ msgstr "OAuth грешка" #. Label of a Workspace Sidebar Item #: frappe/workspace_sidebar/integrations.json msgid "OAuth Provider" -msgstr "" +msgstr "OAuth провајдер" #. Name of a DocType #. Label of a Link in the Integrations Workspace @@ -18698,7 +18698,7 @@ msgstr "Дозволи уређивање само за" #: frappe/core/doctype/module_def/module_def.py:95 msgid "Only Custom Modules can be renamed." -msgstr "" +msgstr "Искључиво прилагођени модули могу бити преименовани." #: frappe/core/doctype/doctype/doctype.py:1652 msgid "Only Options allowed for Data field are:" @@ -19969,7 +19969,7 @@ msgstr "Молимо Вас да додате валидан коментар." #: frappe/public/js/frappe/views/reports/query_report.js:1544 msgid "Please adjust filters to include some data" -msgstr "" +msgstr "Прилагодите филтере како бисте укључили неке податке" #: frappe/core/doctype/user/user.py:1122 msgid "Please ask your administrator to verify your sign-up" @@ -20029,7 +20029,7 @@ msgstr "Молимо Вас да кликнете на следећи линк #: frappe/public/js/frappe/views/gantt/gantt_view.js:89 msgid "Please configure the start field for this Doctype in the controller file." -msgstr "" +msgstr "Молимо Вас да конфигуришете почетно поље за овај DocType у датотеци контролера." #: frappe/www/confirm_workflow_action.html:4 msgid "Please confirm your action to {0} this document." @@ -21132,7 +21132,7 @@ msgstr "Љубичасто" #. Label of a Workspace Sidebar Item #: frappe/workspace_sidebar/integrations.json msgid "Push Notification" -msgstr "" +msgstr "Push обавештење" #. Name of a DocType #. Label of a Link in the Integrations Workspace @@ -22225,16 +22225,16 @@ msgstr "Одговори свима" #. Name of a DocType #: frappe/email/doctype/reply_to_address/reply_to_address.json msgid "Reply To Address" -msgstr "" +msgstr "Адреса за одговор" #: frappe/email/doctype/email_account/email_account.py:278 msgid "Reply To email is required" -msgstr "" +msgstr "Адреса за одговор је обавезна" #. Label of the reply_to_addresses (Table) field in DocType 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "Reply-To Addresses" -msgstr "" +msgstr "Адреса за одговор" #. Label of the report (Check) field in DocType 'Custom DocPerm' #. Label of the report (Link) field in DocType 'Custom Role' @@ -23276,19 +23276,19 @@ msgstr "SSL/TLS режим" #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "SUCCESS" -msgstr "" +msgstr "УСПЕХ" #. Option for the 'Delivery Status Notification Type' (Select) field in DocType #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "SUCCESS,FAILURE" -msgstr "" +msgstr "УСПЕХ, НЕУСПЕХ" #. Option for the 'Delivery Status Notification Type' (Select) field in DocType #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "SUCCESS,FAILURE,DELAY" -msgstr "" +msgstr "УСПЕХ, НЕУСПЕХ, ОДЛАГАЊЕ" #: frappe/public/js/frappe/color_picker/color_picker.js:20 msgid "SWATCHES" @@ -24115,7 +24115,7 @@ msgstr "Изабери две верзије за приказ разлика." #. DocType 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "Select which delivery events should trigger a delivery status notification (DSN) from the SMTP server." -msgstr "" +msgstr "Изаберите који догађаји испоруке треба да покрену обавештење о статусу испоруке (DNS) са SMTP сервера." #: frappe/public/js/frappe/form/link_selector.js:24 #: frappe/public/js/frappe/form/multi_select_dialog.js:80 @@ -27098,7 +27098,7 @@ msgstr "Коментар не може бити празан" #: frappe/email/doctype/email_account/email_account.py:290 msgid "The configured SMTP server does not support DSN (Delivery Status Notification)." -msgstr "" +msgstr "Конфигурисани SMTP сервер не подржава DNS (обавештење о статусу испоруке)." #: frappe/templates/emails/workflow_action.html:9 msgid "The contents of this email are strictly confidential. Please do not forward this email to anyone." @@ -27160,7 +27160,7 @@ msgstr "Следећа скрипта заглавља ће додати тре #: frappe/email/doctype/email_account/email_account.py:257 msgid "The following configured IMAP folder(s) were not found on the server:
    {0}
Please verify the folder names exactly as they appear on the server (folder names are case-sensitive)." -msgstr "" +msgstr "Следећи конфигурисани IMAP директоријуми нису пронађени на серверу:
    {0}
Молимо Вас да проверите називе директоријума тачно онако како су приказани на серверу (велика и мала слова су битна за називе датотека)." #: frappe/core/doctype/data_import/importer.py:1092 msgid "The following values are invalid: {0}. Values must be one of {1}" @@ -27252,7 +27252,7 @@ msgstr "Изабрани документ {0} није {1}." #: frappe/email/doctype/email_account/email_account.py:247 msgid "The server did not return any IMAP folders for this account." -msgstr "" +msgstr "Сервер није вратио ниједан IMAP директоријум за овај налог." #: frappe/utils/response.py:343 msgid "The system is being updated. Please refresh again after a few moments." @@ -29089,7 +29089,7 @@ msgstr "Отпреми" #: frappe/public/js/frappe/file_uploader/FileUploader.vue:663 msgid "Upload Failed" -msgstr "" +msgstr "Отпремање је неуспешно" #: frappe/public/js/print_format_builder/LetterHeadEditor.vue:93 msgid "Upload Image" @@ -30783,7 +30783,7 @@ msgstr "Ставка бочне траке радног простора" #: frappe/desk/doctype/workspace/workspace.js:58 msgid "Workspace added to desktop" -msgstr "" +msgstr "Радни простор је додат на радну површину" #: frappe/public/js/frappe/views/workspace/workspace.js:558 msgid "Workspace {0} created" @@ -31698,7 +31698,7 @@ msgstr "нпр. \"Подршка\", \"Продаја\", \"Петар Петро #: frappe/public/js/frappe/ui/toolbar/awesome_bar.js:230 msgid "e.g. (55 + 434) / 4" -msgstr "" +msgstr "на пример (55 + 434) / 4" #. Description of the 'Incoming Server' (Data) field in DocType 'Email Account' #. Description of the 'Incoming Server' (Data) field in DocType 'Email Domain' @@ -32860,7 +32860,7 @@ msgstr "{0} од {1} ({2} редова са зависним подацима)" #: frappe/public/js/frappe/views/reports/report_view.js:456 msgid "{0} of {1} records match (filtered on visible rows only)" -msgstr "" +msgstr "{0} од {1} записа одговара критеријуму (филтрирано само по видљивим редовима)" #: frappe/utils/data.py:1571 msgctxt "Money in words" From 1bec9370bf0f1e3acbe54078a767271f7edcd5b8 Mon Sep 17 00:00:00 2001 From: MochaMind Date: Fri, 27 Feb 2026 04:57:07 +0530 Subject: [PATCH 50/54] fix: Serbian (Latin) translations --- frappe/locale/sr_CS.po | 76 +++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/frappe/locale/sr_CS.po b/frappe/locale/sr_CS.po index 615d2f479f..48c4216803 100644 --- a/frappe/locale/sr_CS.po +++ b/frappe/locale/sr_CS.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: frappe\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" "POT-Creation-Date: 2026-02-22 09:42+0000\n" -"PO-Revision-Date: 2026-02-23 22:07\n" +"PO-Revision-Date: 2026-02-26 23:27\n" "Last-Translator: developers@frappe.io\n" "Language-Team: Serbian (Latin)\n" "MIME-Version: 1.0\n" @@ -1358,7 +1358,7 @@ msgstr "Dodaj parametre upita" #. Label of the add_reply_to_header (Check) field in DocType 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "Add Reply-To header" -msgstr "" +msgstr "Dodaj zaglavlje adrese za odgovor" #: frappe/core/doctype/user/user.py:860 msgid "Add Roles" @@ -1524,7 +1524,7 @@ msgstr "Dodaj na kontrolnu tablu" #: frappe/desk/doctype/workspace/workspace.js:49 msgid "Add to Desktop" -msgstr "" +msgstr "Dodaj na radnu površinu" #: frappe/public/js/frappe/form/sidebar/assign_to.js:110 msgid "Add to ToDo" @@ -1647,7 +1647,7 @@ msgstr "Adrese i kontakti" #. Account' #: frappe/email/doctype/email_account/email_account.json msgid "Addresses added here will be used as the Reply-To header for outgoing emails sent from this account." -msgstr "" +msgstr "Adrese dodate ovde koristiće se kao adresa za odgovor za izlazne imejlove poslate sa ovog naloga." #. Description of a DocType #: frappe/custom/doctype/client_script/client_script.json @@ -3083,7 +3083,7 @@ msgstr "Istorija izmena" #. Label of a Workspace Sidebar Item #: frappe/workspace_sidebar/users.json msgid "Audits" -msgstr "" +msgstr "Revizije" #. Label of the auth_url_data (Code) field in DocType 'Social Login Key' #: frappe/integrations/doctype/social_login_key/social_login_key.json @@ -3517,7 +3517,7 @@ msgstr "Slika pozadine" #. Label of a Workspace Sidebar Item #: frappe/workspace_sidebar/system.json msgid "Background Job" -msgstr "" +msgstr "Pozadinski zadatak" #. Label of a Link in the Build Workspace #. Label of the background_jobs_section (Section Break) field in DocType @@ -4935,7 +4935,7 @@ msgstr "Kliknite da postavite filtere" #: frappe/desk/page/desktop/desktop.js:1261 msgid "Click to edit" -msgstr "" +msgstr "Kliknite za uređivanje" #: frappe/public/js/frappe/list/list_view.js:754 msgid "Click to sort by {0}" @@ -6538,7 +6538,7 @@ msgstr "Cijan" #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "DELAY" -msgstr "" +msgstr "ODLAGANJE" #. Option for the 'Method' (Select) field in DocType 'Recorder' #. Option for the 'Request Method' (Select) field in DocType 'Webhook' @@ -7366,7 +7366,7 @@ msgstr "Status" #. Label of the dsn_notify_type (Select) field in DocType 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "Delivery Status Notification Type" -msgstr "" +msgstr "Vrsta obaveštenja o statusu isporuke" #. Option for the 'Sign ups' (Select) field in DocType 'Social Login Key' #: frappe/integrations/doctype/social_login_key/social_login_key.json @@ -9431,7 +9431,7 @@ msgstr "Planer omogućen" #. Label of the enabled (Check) field in DocType 'Notification Settings' #: frappe/desk/doctype/notification_settings/notification_settings.json msgid "Enabled System Notification" -msgstr "" +msgstr "Omogućeno sistemsko obaveštenje" #: frappe/email/doctype/email_account/email_account.py:1101 msgid "Enabled email inbox for user {0}" @@ -10129,7 +10129,7 @@ msgstr "Dodatni parametri" #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "FAILURE" -msgstr "" +msgstr "NEUSPEH" #. Option for the 'Social Login Provider' (Select) field in DocType 'Social #. Login Key' @@ -10273,7 +10273,7 @@ msgstr "Neuspešan pokušaj prijave na Frappe Cloud" #: frappe/email/doctype/email_account/email_account.py:232 msgid "Failed to retrieve the list of IMAP folders from the server. Please ensure the mailbox is accessible and the account has permission to list folders." -msgstr "" +msgstr "Neuspešno preuzimanje liste IMAP direktorijuma sa servera. Proverite da li je poštansko sanduče dostupno i da li nalog ima dozvolu za prikaz direktorijuma." #: frappe/email/doctype/email_queue/email_queue.py:311 msgid "Failed to send email with subject:" @@ -11340,7 +11340,7 @@ msgstr "Jedinica frakcije" #. Label of a Desktop Icon #: frappe/desktop_icon/framework.json msgid "Framework" -msgstr "" +msgstr "Framework" #. Option for the 'Social Login Provider' (Select) field in DocType 'Social #. Login Key' @@ -12236,7 +12236,7 @@ msgstr "Naslov" #. Label of a Workspace Sidebar Item #: frappe/workspace_sidebar/system.json msgid "Health Report" -msgstr "" +msgstr "Izveštaj o stanju sistema" #. Option for the 'Type' (Select) field in DocType 'Dashboard Chart' #: frappe/desk/doctype/dashboard_chart/dashboard_chart.json @@ -12655,7 +12655,7 @@ msgstr "IMAP datoteka" #: frappe/email/doctype/email_account/email_account.py:235 #: frappe/email/doctype/email_account/email_account.py:263 msgid "IMAP Folder Not Found" -msgstr "" +msgstr "IMAP direktorijum nije pronađen" #. Label of the ip_address (Data) field in DocType 'Activity Log' #. Label of the ip_address (Data) field in DocType 'Comment' @@ -13415,7 +13415,7 @@ msgstr "Pogrešan verifikacioni kod" #: frappe/public/js/frappe/views/gantt/gantt_view.js:88 msgid "Incorrect configuration" -msgstr "" +msgstr "Neispravna konfiguracija" #: frappe/model/document.py:1733 msgid "Incorrect value in row {0}:" @@ -16980,7 +16980,7 @@ msgstr "MyISAM" #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "NEVER" -msgstr "" +msgstr "NIKADA" #: frappe/workflow/doctype/workflow/workflow.js:19 msgid "NOTE: If you add states or transitions in the table, it will be reflected in the Workflow Builder but you will have to position them manually. Also Workflow Builder is currently in BETA." @@ -17751,7 +17751,7 @@ msgstr "Nema podataka za izvoz" #: frappe/public/js/frappe/views/reports/query_report.js:1543 msgid "No data to perform this action" -msgstr "" +msgstr "Nema podataka za izvršavanje ove radnje" #: frappe/contacts/doctype/address/address.py:247 msgid "No default Address Template found. Please create a new one from Setup > Printing and Branding > Address Template." @@ -17796,7 +17796,7 @@ msgstr "Nema dodatnih zapisa" #: frappe/public/js/frappe/views/reports/report_view.js:337 msgid "No matching entries in the current results" -msgstr "" +msgstr "Nema podudarnih zapisa u trenutnim rezultatima" #: frappe/templates/includes/search_template.html:49 msgid "No matching records. Search something new" @@ -18430,7 +18430,7 @@ msgstr "OAuth greška" #. Label of a Workspace Sidebar Item #: frappe/workspace_sidebar/integrations.json msgid "OAuth Provider" -msgstr "" +msgstr "OAuth provajder" #. Name of a DocType #. Label of a Link in the Integrations Workspace @@ -18699,7 +18699,7 @@ msgstr "Dozvoli uređivanje samo za" #: frappe/core/doctype/module_def/module_def.py:95 msgid "Only Custom Modules can be renamed." -msgstr "" +msgstr "Isključivo prilagođeni moduli mogu biti preimenovani." #: frappe/core/doctype/doctype/doctype.py:1652 msgid "Only Options allowed for Data field are:" @@ -19970,7 +19970,7 @@ msgstr "Molimo Vas da dodate validan komentar." #: frappe/public/js/frappe/views/reports/query_report.js:1544 msgid "Please adjust filters to include some data" -msgstr "" +msgstr "Prilagodite filtere kako biste uključili neke podatke" #: frappe/core/doctype/user/user.py:1122 msgid "Please ask your administrator to verify your sign-up" @@ -20030,7 +20030,7 @@ msgstr "Molimo Vas da kliknete na sledeći link da biste postavili novu lozinku" #: frappe/public/js/frappe/views/gantt/gantt_view.js:89 msgid "Please configure the start field for this Doctype in the controller file." -msgstr "" +msgstr "Molimo Vas da konfigurišete početno polje za ovaj DocType u datoteci kontrolera." #: frappe/www/confirm_workflow_action.html:4 msgid "Please confirm your action to {0} this document." @@ -21133,7 +21133,7 @@ msgstr "Ljubičasto" #. Label of a Workspace Sidebar Item #: frappe/workspace_sidebar/integrations.json msgid "Push Notification" -msgstr "" +msgstr "Push obaveštenje" #. Name of a DocType #. Label of a Link in the Integrations Workspace @@ -22226,16 +22226,16 @@ msgstr "Odgovori svima" #. Name of a DocType #: frappe/email/doctype/reply_to_address/reply_to_address.json msgid "Reply To Address" -msgstr "" +msgstr "Adresa za odgovor" #: frappe/email/doctype/email_account/email_account.py:278 msgid "Reply To email is required" -msgstr "" +msgstr "Adresa za odgovor je obavezna" #. Label of the reply_to_addresses (Table) field in DocType 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "Reply-To Addresses" -msgstr "" +msgstr "Adrese za odgovor" #. Label of the report (Check) field in DocType 'Custom DocPerm' #. Label of the report (Link) field in DocType 'Custom Role' @@ -23277,19 +23277,19 @@ msgstr "SSL/TLS režim" #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "SUCCESS" -msgstr "" +msgstr "USPEH" #. Option for the 'Delivery Status Notification Type' (Select) field in DocType #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "SUCCESS,FAILURE" -msgstr "" +msgstr "USPEH, NEUSPEH" #. Option for the 'Delivery Status Notification Type' (Select) field in DocType #. 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "SUCCESS,FAILURE,DELAY" -msgstr "" +msgstr "USPEH, NEUSPEH, ODLAGANJE" #: frappe/public/js/frappe/color_picker/color_picker.js:20 msgid "SWATCHES" @@ -24116,7 +24116,7 @@ msgstr "Izaberi dve verzije za prikaz razlika." #. DocType 'Email Account' #: frappe/email/doctype/email_account/email_account.json msgid "Select which delivery events should trigger a delivery status notification (DSN) from the SMTP server." -msgstr "" +msgstr "Izaberite koji događaji isporuke treba da pokrenu obaveštenje o statusu isporuke (DNS) sa SMTP servera." #: frappe/public/js/frappe/form/link_selector.js:24 #: frappe/public/js/frappe/form/multi_select_dialog.js:80 @@ -27099,7 +27099,7 @@ msgstr "Komentar ne može biti prazan" #: frappe/email/doctype/email_account/email_account.py:290 msgid "The configured SMTP server does not support DSN (Delivery Status Notification)." -msgstr "" +msgstr "Konfigurisani SMTP server ne podržava DSN (obaveštenje o statusu isporuke)." #: frappe/templates/emails/workflow_action.html:9 msgid "The contents of this email are strictly confidential. Please do not forward this email to anyone." @@ -27161,7 +27161,7 @@ msgstr "Sledeća skripta zaglavlja će dodati trenutni datum u element klase 'he #: frappe/email/doctype/email_account/email_account.py:257 msgid "The following configured IMAP folder(s) were not found on the server:
    {0}
Please verify the folder names exactly as they appear on the server (folder names are case-sensitive)." -msgstr "" +msgstr "Sledeći konfigurisani IMAP direktorijumi nisu pronađeni na serveru:
    {0}
Molimo Vas da proverite nazive direktorijuma tačno onako kako su prikazani na serveru (velika i mala slova su bitna za nazive datoteka)." #: frappe/core/doctype/data_import/importer.py:1092 msgid "The following values are invalid: {0}. Values must be one of {1}" @@ -27253,7 +27253,7 @@ msgstr "Izabrani dokument {0} nije {1}." #: frappe/email/doctype/email_account/email_account.py:247 msgid "The server did not return any IMAP folders for this account." -msgstr "" +msgstr "Server nije vratio nijedan IMAP direktorijum za ovaj nalog." #: frappe/utils/response.py:343 msgid "The system is being updated. Please refresh again after a few moments." @@ -29089,7 +29089,7 @@ msgstr "Otpremi" #: frappe/public/js/frappe/file_uploader/FileUploader.vue:663 msgid "Upload Failed" -msgstr "" +msgstr "Otpremanje je neuspešno" #: frappe/public/js/print_format_builder/LetterHeadEditor.vue:93 msgid "Upload Image" @@ -30783,7 +30783,7 @@ msgstr "Stavka bočne trake radnog prostora" #: frappe/desk/doctype/workspace/workspace.js:58 msgid "Workspace added to desktop" -msgstr "" +msgstr "Radni prostor je dodat na radnu površinu" #: frappe/public/js/frappe/views/workspace/workspace.js:558 msgid "Workspace {0} created" @@ -31698,7 +31698,7 @@ msgstr "npr. \"Podrška\", \"Prodaja\", \"Petar Petrović\"" #: frappe/public/js/frappe/ui/toolbar/awesome_bar.js:230 msgid "e.g. (55 + 434) / 4" -msgstr "" +msgstr "na primer (55 + 434) / 4" #. Description of the 'Incoming Server' (Data) field in DocType 'Email Account' #. Description of the 'Incoming Server' (Data) field in DocType 'Email Domain' @@ -32860,7 +32860,7 @@ msgstr "{0} od {1} ({2} redova sa zavisnim podacima)" #: frappe/public/js/frappe/views/reports/report_view.js:456 msgid "{0} of {1} records match (filtered on visible rows only)" -msgstr "" +msgstr "{0} od {1} zapisa odgovara kriterijumu (filtrirano samo po vidljivim redovima)" #: frappe/utils/data.py:1571 msgctxt "Money in words" From 734e83b89e912c4d3c6f6e070ee19fa8413835a7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 27 Feb 2026 08:25:33 +0530 Subject: [PATCH 51/54] chore: update pypdf --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 306aab4d3a..5d097cb7af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ # We depend on internal attributes, # do NOT add loose requirements on PyMySQL versions. "PyMySQL==1.1.2", - "pypdf==6.7.2", + "pypdf==6.7.3", "PyPika @ git+https://github.com/frappe/pypika@2c50e6142b2d61d2d243e466fdd5dc03b3d918f2", "mysqlclient==2.2.7", "PyQRCode~=1.2.1", From b9b9687f558b7cbedfb9a0dc26699660e7b2e899 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 03:06:48 +0000 Subject: [PATCH 52/54] chore(deps): bump minimatch from 3.1.3 to 3.1.5 Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.1.3 to 3.1.5. - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.3...v3.1.5) --- updated-dependencies: - dependency-name: minimatch dependency-version: 3.1.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 689fa38fba..44040b1a72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2055,9 +2055,9 @@ mime@^1.4.1: integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== minimatch@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.3.tgz#6a5cba9b31f503887018f579c89f81f61162e624" - integrity sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA== + version "3.1.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== dependencies: brace-expansion "^1.1.7" From a91d65a67902abb00a051236321503376ea6a26d Mon Sep 17 00:00:00 2001 From: sokumon Date: Fri, 27 Feb 2026 13:09:26 +0530 Subject: [PATCH 53/54] fix: don't show chat widget on weekends --- frappe/public/js/billing.bundle.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/billing.bundle.js b/frappe/public/js/billing.bundle.js index 2c11bc75af..be249d877b 100644 --- a/frappe/public/js/billing.bundle.js +++ b/frappe/public/js/billing.bundle.js @@ -99,8 +99,8 @@ function addChatBubble() { const all_apps = frappe.utils.get_installed_apps(); const desk_apps = ["erpnext", "hrms"]; - const apps_allowed = frappe.utils.is_sub_array(all_apps, desk_apps); - if (checkBusinessHours && apps_allowed) { + const apps_allowed = desk_apps.some((app) => all_apps.includes(app)); + if (checkBusinessHours() && apps_allowed) { let chat_banner = document.createElement("script"); chat_banner.setAttribute("id", "chat_widget_trigger"); chat_banner.innerHTML = @@ -112,10 +112,16 @@ function addChatBubble() { } function checkBusinessHours() { - let currentTime = new Date(); - const istTime = new Date(currentTime.toLocaleString("en-US", { timeZone: "Asia/Kolkata" })); + let current_time = new Date(); + const ist_time = new Date(current_time.toLocaleString("en-US", { timeZone: "Asia/Kolkata" })); - return istTime.getHours() >= 11 && istTime.getHours() < 18; + const hours = ist_time.getHours(); + const day = ist_time.getDay(); + + const is_weekend = day === 0 || day === 6; + const is_business_hour = hours >= 11 && hours < 18; + + return !is_weekend && is_business_hour; } function toggleChatBubble(toggle) { From 48c096adc84b6a99c9aee6e29a9a3e4b54e8c129 Mon Sep 17 00:00:00 2001 From: Ejaaz Khan Date: Fri, 27 Feb 2026 14:32:17 +0530 Subject: [PATCH 54/54] fix(grid): add background color to actions button --- frappe/public/scss/common/grid.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/scss/common/grid.scss b/frappe/public/scss/common/grid.scss index 5b91d77d2b..16f7d6b4d3 100644 --- a/frappe/public/scss/common/grid.scss +++ b/frappe/public/scss/common/grid.scss @@ -270,6 +270,7 @@ .col:last-child { border: none; + background-color: var(--fg-color); } .btn-open-row {
- {{ column.label }} + {{ _(column.label) }}