From 60235f4b2bd148ce4f063831e87dc5b3da9c7754 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 17 Jun 2026 13:19:03 +0530 Subject: [PATCH] fix(postgres): satisfy strict GROUP BY in Payment Terms Status For Sales Order 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) --- .../payment_terms_status_for_sales_order.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py index eb2b7cc21d8..1f9c2ceb442 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py @@ -3,7 +3,8 @@ import frappe from frappe import _, qb, query_builder -from frappe.query_builder import Criterion, functions +from frappe.query_builder import Criterion +from frappe.query_builder.functions import Max from frappe.utils.dateutils import getdate @@ -185,9 +186,6 @@ def get_so_with_invoices(filters): conditions = get_conditions(filters) filter_criterions = build_filter_criterions(filters) - datediff = query_builder.CustomFunction("DATEDIFF", ["cur_date", "due_date"]) - ifelse = query_builder.CustomFunction("IF", ["condition", "then", "else"]) - query_so = ( qb.from_(so) .join(soi) @@ -199,7 +197,8 @@ def get_so_with_invoices(filters): .select( so.customer, so.transaction_date.as_("submitted"), - ifelse(datediff(ps.due_date, functions.CurDate()) < 0, "Overdue", "Unpaid").as_("status"), + # CASE + a Python date is portable; MySQL's IF()/DATEDIFF()/CURDATE() don't exist on postgres + query_builder.Case().when(ps.due_date < getdate(), "Overdue").else_("Unpaid").as_("status"), ps.payment_term, ps.description, ps.due_date, @@ -230,7 +229,13 @@ def get_so_with_invoices(filters): .on(si.name == sii.parent) .inner_join(soi) .on(soi.name == sii.so_detail) - .select(sii.sales_order, sii.parent.as_("invoice"), si.base_grand_total.as_("invoice_amount")) + .select( + # grouped by the invoice (sii.parent); sales_order is arbitrary per invoice on MySQL and + # base_grand_total is constant per invoice -> Max() keeps the GROUP BY postgres-valid. + Max(sii.sales_order).as_("sales_order"), + sii.parent.as_("invoice"), + Max(si.base_grand_total).as_("invoice_amount"), + ) .where((sii.sales_order.isin([x.name for x in sorders])) & (si.docstatus == 1)) .groupby(sii.parent) )