chore: resolve merge conflict with develop

This commit is contained in:
Kaushal Shriwas 2026-04-09 10:54:28 +05:30
commit f77e9f09f2
5 changed files with 70 additions and 32 deletions

View file

@ -8,7 +8,6 @@ import frappe
from frappe.database.utils import NestedSetHierarchy
from frappe.model.db_query import get_timespan_date_range
from frappe.query_builder import Field
from frappe.query_builder.functions import Coalesce
from frappe.utils import cstr
@ -51,7 +50,7 @@ def func_in(key: Field, value: list | tuple) -> frappe.qb:
value = ["" if v is None else v for v in value]
if "" in value:
return Coalesce(key, "").isin(value)
return key.isin(value) | key.isnull()
return key.isin(value)

View file

@ -34,7 +34,6 @@ frappe.ui.Notifications = class Notifications {
</span>`)
.on("click", (e) => {
e.stopImmediatePropagation();
console.log("what");
frappe.set_route("Form", "Notification Settings", frappe.session.user);
})
.appendTo(this.header_actions)
@ -247,15 +246,13 @@ class NotificationsView extends BaseNotificationsView {
.tooltip({ delay: { show: 600, hide: 100 }, trigger: "hover" });
this.setup_notification_listeners();
this.get_notifications_list(this.max_length).then((r) => {
if (!r.message) return;
this.dropdown_items = r.message.notification_logs;
frappe.update_user_info(r.message.user_info);
this.render_notifications_dropdown();
if (this.settings.seen == 0 && this.dropdown_items.length > 0) {
this.toggle_notification_icon(false);
}
});
this.dropdown_items = [];
this.notifications_fetched = false;
if (this.settings && this.settings.seen == 0) {
this.toggle_notification_icon(false);
}
}
update_dropdown() {
@ -370,7 +367,7 @@ class NotificationsView extends BaseNotificationsView {
<div class="full-log-btn">${__("See all Activity")}</div>
</a>`);
} else {
this.container.append(
this.container.html(
$(`<div class="notification-null-state">
<div class="text-center">
<img src="/assets/frappe/images/ui-states/notification-empty-state.svg" alt="Generic Empty State" class="null-state">
@ -432,25 +429,58 @@ class NotificationsView extends BaseNotificationsView {
frappe.realtime.on("indicator_hide", () => {
this.toggle_notification_icon(true);
});
this.parent.on("show.bs.dropdown", () => {
if (!this.notifications_fetched) {
this.container.html(`<div class="notification-null-state">
<div class="text-center">
<div class="spinner-border spinner-border-sm text-muted"></div>
</div>
</div>`);
this.get_notifications_list(this.max_length).then((r) => {
if (r.message && r.message.notification_logs) {
this.dropdown_items = r.message.notification_logs;
frappe.update_user_info(r.message.user_info);
} else {
this.dropdown_items = [];
}
this.render_notifications_dropdown();
this.notifications_fetched = true;
});
}
});
}
}
class EventsView extends BaseNotificationsView {
make() {
let today = frappe.datetime.get_today();
frappe
.xcall(
"frappe.desk.doctype.event.event.get_events",
{
start: today,
end: today,
},
"GET",
{ cache: true }
)
.then((event_list) => {
this.render_events_html(event_list);
});
this.events_fetched = false;
this.parent.on("show.bs.dropdown", () => {
if (this.events_fetched) return;
this.container.html(`<div class="notification-null-state">
<div class="text-center">
<div class="spinner-border spinner-border-sm text-muted"></div>
</div>
</div>`);
let today = frappe.datetime.get_today();
frappe
.xcall(
"frappe.desk.doctype.event.event.get_events",
{
start: today,
end: today,
},
"GET",
{ cache: true }
)
.then((event_list) => {
this.render_events_html(event_list);
this.events_fetched = true;
});
});
}
render_events_html(event_list) {

View file

@ -484,7 +484,11 @@ frappe.ui.Sidebar = class Sidebar {
type: "Button",
class: "sidebar-notification hidden",
onClick: () => {
this.wrapper.find(".dropdown-notifications").toggleClass("hidden");
const $dropdown = this.wrapper.find(".dropdown-notifications");
$dropdown.toggleClass("hidden");
if (!$dropdown.hasClass("hidden")) {
$dropdown.trigger("show.bs.dropdown");
}
if (frappe.is_mobile()) {
this.wrapper.removeClass("expanded");
}

View file

@ -525,15 +525,17 @@ class TestOperatorIn(IntegrationTestCase):
query = func_in(note.name, [None, "user1"])
sql_str = str(query).lower()
self.assertIn("coalesce", sql_str)
self.assertNotIn("coalesce", sql_str)
self.assertIn("is null", sql_str)
self.assertIn("''", sql_str)
def test_func_in_with_empty_string_uses_coalesce(self):
def test_func_in_with_empty_string_uses_or_is_null(self):
note = frappe.qb.DocType("Note")
query = func_in(note.name, ["", "user1"])
sql_str = str(query).lower()
self.assertIn("coalesce", sql_str)
self.assertNotIn("coalesce", sql_str)
self.assertIn("is null", sql_str)
self.assertIn("''", sql_str)
def test_func_in_with_mixed_none_and_values(self):
@ -541,7 +543,8 @@ class TestOperatorIn(IntegrationTestCase):
query = func_in(note.name, ["val1", None, "val2"])
sql_str = str(query).lower()
self.assertIn("coalesce", sql_str)
self.assertNotIn("coalesce", sql_str)
self.assertIn("is null", sql_str)
def test_in_filter_matches_null_and_empty_columns(self):
test_doctype = new_doctype(

View file

@ -6,6 +6,8 @@ from frappe.model.document import Document
from frappe.utils.data import quoted
from frappe.www.list import get_list_context, get_list_data
no_cache = 1
def get_context(context, **dict_params):
frappe.local.form_dict.update(dict_params)