diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index 10169c554fb..fd30ddf6884 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -305,7 +305,9 @@ def get_po_entries(filters): & (parent.name == child.parent) & (parent.status.notin(("Closed", "Completed", "Cancelled"))) ) - .groupby(parent.name, child.material_request_item) + # This is one row per PO item; the selected child.* columns are only functionally dependent + # on the child PK, which postgres requires in the GROUP BY (MariaDB allows omitting it). + .groupby(parent.name, child.material_request_item, child.name) ) query = apply_filters_on_query(filters, parent, child, query) diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index 1577bf2cbf6..f1781c15e05 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -6,4 +6,16 @@ from erpnext.tests.utils import ERPNextTestSuite class TestProcurementTracker(ERPNextTestSuite): - pass + def test_report_executes_and_lists_po(self): + # get_po_entries groups by (Purchase Order, material_request_item, Purchase Order Item) + # while selecting other child columns; this exercises that GROUP BY so the report stays + # valid on Postgres (which rejects selecting non-grouped columns). + from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order + from erpnext.buying.report.procurement_tracker.procurement_tracker import execute + + po = create_purchase_order(company="_Test Company") + + columns, data = execute({"company": "_Test Company"}) + + self.assertTrue(columns) + self.assertIn(po.name, {row.get("purchase_order") for row in data}) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index b6bf1d9f8da..5522aac1044 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -71,7 +71,9 @@ def get_data(filters): po_item.name, ) .where((po_item.parent == po.name) & (po.status.notin(("Stopped", "On Hold"))) & (po.docstatus == 1)) - .groupby(po_item.name) + # the selected po.* columns need the Purchase Order PK grouped on postgres; po.name is 1:1 + # with the grouped po_item.name, so groups are unchanged. + .groupby(po_item.name, po.name) .orderby(po.transaction_date) ) diff --git a/erpnext/buying/report/purchase_order_analysis/test_purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/test_purchase_order_analysis.py new file mode 100644 index 00000000000..7b7eda56a05 --- /dev/null +++ b/erpnext/buying/report/purchase_order_analysis/test_purchase_order_analysis.py @@ -0,0 +1,28 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from frappe.utils import add_days, nowdate + +from erpnext.tests.utils import ERPNextTestSuite + + +class TestPurchaseOrderAnalysis(ERPNextTestSuite): + def test_report_executes_and_lists_po(self): + # get_data groups by (Purchase Order Item, Purchase Order) while selecting other parent + # columns; this exercises that GROUP BY so the report stays valid on Postgres (which rejects + # selecting non-grouped columns). + from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order + from erpnext.buying.report.purchase_order_analysis.purchase_order_analysis import execute + + po = create_purchase_order(company="_Test Company") + + filters = { + "company": "_Test Company", + "from_date": add_days(nowdate(), -1), + "to_date": add_days(nowdate(), 1), + } + result = execute(filters) + columns, data = result[0], result[1] + + self.assertTrue(columns) + self.assertIn(po.name, {row.get("purchase_order") for row in data})