From 24a66d10e7f38e909a61e6dc5810901a82e5c210 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 17 Jun 2026 13:18:56 +0530 Subject: [PATCH] fix(postgres): satisfy strict GROUP BY in Deferred Revenue And Expense report Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict GROUP BY. No behaviour change on MariaDB. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../deferred_revenue_and_expense.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py index 22fabeabca6..1b7ea5063ee 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py @@ -3,7 +3,8 @@ import frappe from frappe import _, qb -from frappe.query_builder import Column, functions +from frappe.query_builder import functions +from frappe.query_builder.custom import ConstantColumn from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, getdate, rounded from erpnext.accounts.report.financial_statements import get_period_list @@ -300,8 +301,10 @@ class Deferred_Revenue_and_Expense_Report: Get all sales and purchase invoices which has deferred revenue/expense items """ gle = qb.DocType("GL Entry") - # column doesn't have an alias option - posted = Column("posted") + # a literal marker: real GL rows are "posted" (dummy/simulated future entries use "not"). + # ConstantColumn renders a single-quoted string literal, valid on both backends -- a plain + # Column rendered as "posted", which MySQL reads as the string but postgres as an identifier. + posted = ConstantColumn("posted").as_("posted") if self.filters.type == "Revenue": inv = qb.DocType("Sales Invoice") @@ -327,13 +330,15 @@ class Deferred_Revenue_and_Expense_Report: ) .select( inv.name.as_("doc"), - inv.posting_date, + # non-grouped columns are constant per grouped invoice / invoice item -> Max() keeps the + # GROUP BY valid on postgres while returning the same value MySQL picked. + functions.Max(inv.posting_date).as_("posting_date"), inv_item.name.as_("item"), - inv_item.item_name, - inv_item.service_start_date, - inv_item.service_end_date, - inv_item.base_net_amount, - deferred_account_field, + functions.Max(inv_item.item_name).as_("item_name"), + functions.Max(inv_item.service_start_date).as_("service_start_date"), + functions.Max(inv_item.service_end_date).as_("service_end_date"), + functions.Max(inv_item.base_net_amount).as_("base_net_amount"), + functions.Max(deferred_account_field).as_(deferred_account_field.name), gle.posting_date.as_("gle_posting_date"), functions.Sum(gle.debit).as_("debit"), functions.Sum(gle.credit).as_("credit"),