From 67c1d13f91d63182f481cfd7e93d811f82032409 Mon Sep 17 00:00:00 2001 From: sokumon Date: Mon, 21 Apr 2025 17:58:18 +0530 Subject: [PATCH 1/9] fix(Note): make note show up only on login --- frappe/desk/doctype/note/note.py | 49 ++++++++++++++++++-------------- frappe/hooks.py | 1 + frappe/public/js/frappe/desk.js | 15 ++++++---- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/frappe/desk/doctype/note/note.py b/frappe/desk/doctype/note/note.py index 96cf4c0972..8623beceb7 100644 --- a/frappe/desk/doctype/note/note.py +++ b/frappe/desk/doctype/note/note.py @@ -71,30 +71,37 @@ def has_permission(doc, user): def get_unseen_notes(): - from frappe.query_builder.terms import ParameterizedValueWrapper, SubQuery - - def _get_unseen_notes(): - note = frappe.qb.DocType("Note") - nsb = frappe.qb.DocType("Note Seen By").as_("nsb") - - return ( - frappe.qb.from_(note) - .select(note.name, note.title, note.content, note.notify_on_every_login) - .where( - (note.notify_on_login == 1) - & (note.expire_notification_on > frappe.utils.now()) - & ( - ParameterizedValueWrapper(frappe.session.user).notin( - SubQuery(frappe.qb.from_(nsb).select(nsb.user).where(nsb.parent == note.name)) - ) - ) - ) - ).run(as_dict=1) - return ( frappe.cache.get_value( f"{UNSEEN_NOTES_KEY}{frappe.session.user}", - generator=_get_unseen_notes, ) or [] ) + + +@frappe.whitelist() +def reset_notes(): + frappe.cache.set_value(f"{UNSEEN_NOTES_KEY}{frappe.session.user}", []) + return frappe.cache.get_value(f"{UNSEEN_NOTES_KEY}{frappe.session.user}") + + +def _get_unseen_notes(): + from frappe.query_builder.terms import ParameterizedValueWrapper, SubQuery + + note = frappe.qb.DocType("Note") + nsb = frappe.qb.DocType("Note Seen By").as_("nsb") + + results = ( + frappe.qb.from_(note) + .select(note.name, note.title, note.content, note.notify_on_every_login) + .where( + (note.notify_on_login == 1) + & (note.expire_notification_on > frappe.utils.now()) + & ( + ParameterizedValueWrapper(frappe.session.user).notin( + SubQuery(frappe.qb.from_(nsb).select(nsb.user).where(nsb.parent == note.name)) + ) + ) + ) + ).run(as_dict=1) + frappe.cache.set_value(f"{UNSEEN_NOTES_KEY}{frappe.session.user}", results) diff --git a/frappe/hooks.py b/frappe/hooks.py index e1a43cbf58..46d19cab6d 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -90,6 +90,7 @@ on_session_creation = [ "frappe.core.doctype.user.user.notify_admin_access_to_system_manager", ] +on_login = "frappe.desk.doctype.note.note._get_unseen_notes" on_logout = "frappe.core.doctype.session_default_settings.session_default_settings.clear_session_defaults" # PDF diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index b16e409397..d41a358b8c 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -470,11 +470,12 @@ frappe.Application = class Application { if (frappe.boot.notes.length) { frappe.boot.notes.forEach(function (note) { if (!note.seen || note.notify_on_every_login) { - var d = frappe.msgprint({ message: note.content, title: note.title }); + var d = new frappe.ui.Dialog({ content: note.content, title: note.title }); d.keep_open = true; - d.custom_onhide = function () { + d.msg_area = $('
').appendTo(d.body); + d.msg_area.append(note.content); + d.onhide = function () { note.seen = true; - // Mark note as read if the Notify On Every Login flag is not set if (!note.notify_on_every_login) { frappe.call({ @@ -483,11 +484,13 @@ frappe.Application = class Application { note: note.name, }, }); + } else { + frappe.call({ + method: "frappe.desk.doctype.note.note.reset_notes", + }); } - - // next note - me.show_notes(); }; + d.show(); } }); } From 932d5ccc08ca7f955b70314e872065ae563a5480 Mon Sep 17 00:00:00 2001 From: sokumon Date: Mon, 21 Apr 2025 19:12:46 +0530 Subject: [PATCH 2/9] fix(test): fetch unseen notes --- frappe/tests/test_boot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_boot.py b/frappe/tests/test_boot.py index b28cfa6ff1..92191a0283 100644 --- a/frappe/tests/test_boot.py +++ b/frappe/tests/test_boot.py @@ -1,6 +1,6 @@ import frappe from frappe.boot import get_user_pages_or_reports -from frappe.desk.doctype.note.note import get_unseen_notes, mark_as_seen +from frappe.desk.doctype.note.note import _get_unseen_notes, get_unseen_notes, mark_as_seen from frappe.tests import IntegrationTestCase @@ -20,6 +20,7 @@ class TestBootData(IntegrationTestCase): note.insert() frappe.set_user("test@example.com") + _get_unseen_notes() unseen_notes = [d.title for d in get_unseen_notes()] self.assertListEqual(unseen_notes, ["Test Note"]) From 28947b33dd8c0a090e7751d47925baa91b6e721e Mon Sep 17 00:00:00 2001 From: sokumon Date: Mon, 5 May 2025 16:45:50 +0530 Subject: [PATCH 3/9] fix: add informative guest message --- frappe/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 55813cd4cb..967b851e0e 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -666,7 +666,7 @@ def is_whitelisted(method): is_guest = session["user"] == "Guest" if method not in whitelisted or (is_guest and method not in guest_methods): - summary = _("You are not permitted to access this resource.") + summary = _("You are not permitted to access this resource. Login to access") detail = _("Function {0} is not whitelisted.").format(bold(f"{method.__module__}.{method.__name__}")) msg = f"
{summary}{detail}
" throw(msg, PermissionError, title=_("Method Not Allowed")) From ddf2587cc7791965a6ccd3eba9909a7120ff1c42 Mon Sep 17 00:00:00 2001 From: Vengadesh Date: Sun, 18 May 2025 06:15:04 +0530 Subject: [PATCH 4/9] feat: Dynamic and configurable child table search bar --- frappe/core/doctype/doctype/doctype.json | 12 +++++++++++- frappe/core/doctype/doctype/doctype.py | 1 + frappe/public/js/frappe/form/grid_row.js | 6 +++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 5c550637b6..1cc0105b5f 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -33,6 +33,7 @@ "editable_grid", "quick_entry", "grid_page_length", + "grid_search_field_length", "cb01", "track_changes", "track_seen", @@ -697,9 +698,18 @@ "fieldname": "protect_attached_files", "fieldtype": "Check", "label": "Protect Attached Files" + }, + { + "default": "0", + "depends_on": "istable", + "fieldname": "grid_search_field_length", + "fieldtype": "Int", + "label": "Grid Search Field Length", + "non_negative": 1 } ], "grid_page_length": 50, + "grid_search_field_length": 50, "icon": "fa fa-bolt", "idx": 6, "index_web_pages_for_search": 1, @@ -775,7 +785,7 @@ "link_fieldname": "document_type" } ], - "modified": "2025-03-27 18:16:53.286909", + "modified": "2025-05-18 06:10:30.044174", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 3939240bfb..b91bbed7e3 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -122,6 +122,7 @@ class DocType(Document): fields: DF.Table[DocField] force_re_route_to_default_view: DF.Check grid_page_length: DF.Int + grid_search_field_length: DF.Int has_web_view: DF.Check hide_toolbar: DF.Check icon: DF.Data | None diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index ee739ef891..dcd6df33d1 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -852,8 +852,12 @@ export default class GridRow { show_search_row() { // show or remove search columns based on grid rows + let show_length = this.grid?.meta?.grid_search_field_length + ? this.grid.meta.grid_search_field_length > 0 + : 20; this.show_search = - this.show_search && (this.grid?.data?.length >= 20 || this.grid.filter_applied); + this.show_search && + (this.grid?.data?.length >= show_length || this.grid.filter_applied); !this.show_search && this.wrapper.remove(); return this.show_search; } From a770b52f35dac73658beb721c4cbb0b7187f1fec Mon Sep 17 00:00:00 2001 From: Vengadesh Date: Mon, 19 May 2025 08:20:15 +0530 Subject: [PATCH 5/9] fix: modified the condition for fetching the search field length value --- frappe/public/js/frappe/form/grid_row.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index dcd6df33d1..d3830a2272 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -852,9 +852,10 @@ export default class GridRow { show_search_row() { // show or remove search columns based on grid rows - let show_length = this.grid?.meta?.grid_search_field_length - ? this.grid.meta.grid_search_field_length > 0 - : 20; + let show_length = + this.grid?.meta?.grid_search_field_length > 0 + ? this.grid.meta.grid_search_field_length + : 20; this.show_search = this.show_search && (this.grid?.data?.length >= show_length || this.grid.filter_applied); From fae19a802af9db08f24b76c4cc50edbf23a3716b Mon Sep 17 00:00:00 2001 From: Vengadesh Date: Tue, 20 May 2025 19:35:47 +0530 Subject: [PATCH 6/9] fix: Field label changed in doctype json --- frappe/core/doctype/doctype/doctype.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 1cc0105b5f..df96f9ac72 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -704,7 +704,7 @@ "depends_on": "istable", "fieldname": "grid_search_field_length", "fieldtype": "Int", - "label": "Grid Search Field Length", + "label": "Rows Threshold for Grid Search", "non_negative": 1 } ], From 0b14f288261136249790d425c5c5780505a2e8b0 Mon Sep 17 00:00:00 2001 From: ShreyasTheNewbie Date: Wed, 21 May 2025 05:45:42 +0000 Subject: [PATCH 7/9] feat: add background color to Number Card --- .../desk/doctype/number_card/number_card.json | 18 +++++++++++++++--- frappe/desk/doctype/number_card/number_card.py | 1 + .../js/frappe/widgets/number_card_widget.js | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/frappe/desk/doctype/number_card/number_card.json b/frappe/desk/doctype/number_card/number_card.json index 08d21ec4a4..5c897378a0 100644 --- a/frappe/desk/doctype/number_card/number_card.json +++ b/frappe/desk/doctype/number_card/number_card.json @@ -31,7 +31,9 @@ "dynamic_filters_section", "dynamic_filters_json", "section_break_16", - "color" + "color", + "column_break_xtre", + "background_color" ], "fields": [ { @@ -207,10 +209,19 @@ "fieldtype": "Link", "label": "Currency", "options": "Currency" + }, + { + "fieldname": "column_break_xtre", + "fieldtype": "Column Break" + }, + { + "fieldname": "background_color", + "fieldtype": "Color", + "label": "Background Color" } ], "links": [], - "modified": "2025-01-28 18:22:27.268239", + "modified": "2025-05-21 10:49:51.759985", "modified_by": "Administrator", "module": "Desk", "name": "Number Card", @@ -250,10 +261,11 @@ "share": 1 } ], + "row_format": "Dynamic", "search_fields": "label, document_type", "sort_field": "creation", "sort_order": "DESC", "states": [], "title_field": "label", "track_changes": 1 -} \ No newline at end of file +} diff --git a/frappe/desk/doctype/number_card/number_card.py b/frappe/desk/doctype/number_card/number_card.py index 9f78e1a69f..cf3792bb76 100644 --- a/frappe/desk/doctype/number_card/number_card.py +++ b/frappe/desk/doctype/number_card/number_card.py @@ -23,6 +23,7 @@ class NumberCard(Document): from frappe.types import DF aggregate_function_based_on: DF.Literal[None] + background_color: DF.Color | None color: DF.Color | None currency: DF.Link | None document_type: DF.Link | None diff --git a/frappe/public/js/frappe/widgets/number_card_widget.js b/frappe/public/js/frappe/widgets/number_card_widget.js index c7c38da91b..cb850d5a1c 100644 --- a/frappe/public/js/frappe/widgets/number_card_widget.js +++ b/frappe/public/js/frappe/widgets/number_card_widget.js @@ -157,6 +157,7 @@ export default class NumberCardWidget extends Widget { async render_card() { this.prepare_actions(); this.set_title(); + this.card_doc?.background_color && this.widget.css("background-color", this.card_doc.background_color); this.set_loading_state(); if (!this.card_doc.type) { From eece9106bff10e2bbb9efcc14d520ef544c6c469 Mon Sep 17 00:00:00 2001 From: ShreyasTheNewbie Date: Wed, 21 May 2025 08:28:06 +0000 Subject: [PATCH 8/9] fix: code formatting --- frappe/public/js/frappe/widgets/number_card_widget.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/widgets/number_card_widget.js b/frappe/public/js/frappe/widgets/number_card_widget.js index cb850d5a1c..9edd74265d 100644 --- a/frappe/public/js/frappe/widgets/number_card_widget.js +++ b/frappe/public/js/frappe/widgets/number_card_widget.js @@ -157,7 +157,8 @@ export default class NumberCardWidget extends Widget { async render_card() { this.prepare_actions(); this.set_title(); - this.card_doc?.background_color && this.widget.css("background-color", this.card_doc.background_color); + this.card_doc?.background_color && + this.widget.css("background-color", this.card_doc.background_color); this.set_loading_state(); if (!this.card_doc.type) { From 00e0b1420827c15627f62b63fe2b90784886f839 Mon Sep 17 00:00:00 2001 From: Vengadesh Date: Wed, 21 May 2025 22:06:22 +0530 Subject: [PATCH 9/9] fix: field name changed in doctype json --- frappe/core/doctype/doctype/doctype.json | 7 +++---- frappe/core/doctype/doctype/doctype.py | 2 +- frappe/public/js/frappe/form/grid_row.js | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index df96f9ac72..898fd81011 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -33,7 +33,7 @@ "editable_grid", "quick_entry", "grid_page_length", - "grid_search_field_length", + "rows_threshold_for_grid_search", "cb01", "track_changes", "track_seen", @@ -702,14 +702,13 @@ { "default": "0", "depends_on": "istable", - "fieldname": "grid_search_field_length", + "fieldname": "rows_threshold_for_grid_search", "fieldtype": "Int", "label": "Rows Threshold for Grid Search", "non_negative": 1 } ], "grid_page_length": 50, - "grid_search_field_length": 50, "icon": "fa fa-bolt", "idx": 6, "index_web_pages_for_search": 1, @@ -785,7 +784,7 @@ "link_fieldname": "document_type" } ], - "modified": "2025-05-18 06:10:30.044174", + "modified": "2025-05-21 21:58:59.947374", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index b91bbed7e3..77a3864860 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -122,7 +122,6 @@ class DocType(Document): fields: DF.Table[DocField] force_re_route_to_default_view: DF.Check grid_page_length: DF.Int - grid_search_field_length: DF.Int has_web_view: DF.Check hide_toolbar: DF.Check icon: DF.Data | None @@ -162,6 +161,7 @@ class DocType(Document): restrict_to_domain: DF.Link | None route: DF.Data | None row_format: DF.Literal["Dynamic", "Compressed"] + rows_threshold_for_grid_search: DF.Int search_fields: DF.Data | None sender_field: DF.Data | None sender_name_field: DF.Data | None diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index d3830a2272..11fcdaf058 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -853,8 +853,8 @@ export default class GridRow { show_search_row() { // show or remove search columns based on grid rows let show_length = - this.grid?.meta?.grid_search_field_length > 0 - ? this.grid.meta.grid_search_field_length + this.grid?.meta?.rows_threshold_for_grid_search > 0 + ? this.grid.meta.rows_threshold_for_grid_search : 20; this.show_search = this.show_search &&