fix(query): match between behaviour for datetime fields with db_query
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
This commit is contained in:
parent
da92e7cd36
commit
207ee7a367
2 changed files with 72 additions and 0 deletions
|
|
@ -79,6 +79,44 @@ def _apply_date_field_filter_conversion(value, operator: str, doctype: str, fiel
|
|||
return value
|
||||
|
||||
|
||||
def _apply_datetime_field_filter_conversion(between_values: tuple | list, doctype: str, field) -> tuple:
|
||||
"""Apply date to datetime conversion for Datetime fields with 'between' operator.
|
||||
|
||||
Args:
|
||||
between_values: Tuple/list of two values [from, to] for between filter
|
||||
doctype: DocType name
|
||||
field: Field name or pypika Field object
|
||||
|
||||
Returns:
|
||||
Tuple with dates expanded to datetime ranges for Datetime fields
|
||||
"""
|
||||
from frappe.model.db_query import _convert_type_for_between_filters
|
||||
|
||||
# Extract field name
|
||||
field_name = field
|
||||
if "." in str(field):
|
||||
field_name = field.split(".")[-1]
|
||||
|
||||
# Skip querying meta for core doctypes to avoid recursion
|
||||
if doctype in CORE_DOCTYPES:
|
||||
df = None
|
||||
else:
|
||||
meta = frappe.get_meta(doctype)
|
||||
df = meta.get_field(field_name) if meta else None
|
||||
|
||||
# Standard datetime fields or Datetime fieldtype
|
||||
if not (field_name in ("creation", "modified") or (df and df.fieldtype == "Datetime")):
|
||||
return between_values
|
||||
|
||||
from_val, to_val = between_values
|
||||
|
||||
# Convert to datetime using db_query helper (handles strings, dates, datetimes)
|
||||
from_val = _convert_type_for_between_filters(from_val, set_time=datetime.time())
|
||||
to_val = _convert_type_for_between_filters(to_val, set_time=datetime.time(23, 59, 59, 999999))
|
||||
|
||||
return (from_val, to_val)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.query_builder import DocType
|
||||
|
||||
|
|
@ -487,12 +525,22 @@ class Engine:
|
|||
frappe.throw(_("Document cannot be used as a filter value"))
|
||||
_operator = operator
|
||||
|
||||
if _operator.lower() in ("timespan", "previous", "next"):
|
||||
from frappe.model.db_query import get_date_range
|
||||
|
||||
_value = get_date_range(_operator.lower(), _value)
|
||||
_operator = "between"
|
||||
|
||||
# For Date fields with datetime values, convert to date to match db_query behavior
|
||||
if isinstance(_value, datetime.datetime) or (
|
||||
isinstance(_value, list | tuple) and any(isinstance(v, datetime.datetime) for v in _value)
|
||||
):
|
||||
_value = _apply_date_field_filter_conversion(_value, _operator, doctype or self.doctype, field)
|
||||
|
||||
# For Datetime fields with date values and 'between' operator, convert to datetime range to match db_query
|
||||
if _operator.lower() == "between" and isinstance(_value, list | tuple) and len(_value) == 2:
|
||||
_value = _apply_datetime_field_filter_conversion(_value, doctype or self.doctype, field)
|
||||
|
||||
if not _value and isinstance(_value, list | tuple | set):
|
||||
_value = ("",)
|
||||
|
||||
|
|
|
|||
|
|
@ -1902,6 +1902,30 @@ class TestQuery(IntegrationTestCase):
|
|||
# If we get here without PermissionError, the test passes
|
||||
self.assertIn(self.normalize_sql("GROUP BY `created_date`"), self.normalize_sql(sql))
|
||||
|
||||
def test_between_datetime_expansion(self):
|
||||
"""Test that date strings are expanded to datetime ranges for Datetime fields with 'between' operator"""
|
||||
# Test with creation field (standard datetime field)
|
||||
query = frappe.qb.get_query(
|
||||
"User",
|
||||
filters={"creation": ["between", ["2025-12-01", "2025-12-01"]]},
|
||||
)
|
||||
sql = query.get_sql()
|
||||
# Date strings should be expanded to datetime ranges
|
||||
self.assertIn("2025-12-01 00:00:00", sql)
|
||||
self.assertIn("2025-12-01 23:59:59", sql)
|
||||
|
||||
def test_timespan_datetime_expansion(self):
|
||||
"""Test that timespan operator expands dates to datetime ranges for Datetime fields"""
|
||||
query = frappe.qb.get_query(
|
||||
"User",
|
||||
filters={"creation": ["timespan", "last 7 days"]},
|
||||
)
|
||||
sql = query.get_sql()
|
||||
# Timespan should expand dates to datetime ranges (start of first day, end of last day)
|
||||
# Should have times like 00:00:00 and 23:59:59
|
||||
self.assertIn("00:00:00", sql)
|
||||
self.assertIn("23:59:59", sql)
|
||||
|
||||
|
||||
# This function is used as a permission query condition hook
|
||||
def test_permission_hook_condition(user):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue