From e9c391608c6f3f4b3c278572fe07595cc2b3d3d2 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Sun, 21 Jun 2026 04:44:02 +0530 Subject: [PATCH] fix(manufacturing): make Work Order Stock report GROUP BY Postgres-valid get_item_list() computes build_qty as IfNull(bin.actual_qty * bom.quantity / bom_item.stock_qty, 0) while grouping only by bom_item.item_code. The three operand columns are neither grouped nor aggregated, so MariaDB arbitrary-picks them but Postgres rejects the query with "column ... must appear in the GROUP BY clause". Add bom.quantity, bom_item.stock_qty and bin.actual_qty to the GROUP BY. The WHERE pins bom/item and the join pins warehouse to single rows (Bin is unique per item+warehouse), so the result stays one row per item and MariaDB behaviour is unchanged. Adds a test (no test file existed) that runs the report against a Work Order and asserts it is listed, exercising the query on both engines. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../test_work_order_stock_report.py | 24 +++++++++++++++++++ .../work_order_stock_report.py | 5 +++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 erpnext/manufacturing/report/work_order_stock_report/test_work_order_stock_report.py diff --git a/erpnext/manufacturing/report/work_order_stock_report/test_work_order_stock_report.py b/erpnext/manufacturing/report/work_order_stock_report/test_work_order_stock_report.py new file mode 100644 index 00000000000..583a89e9664 --- /dev/null +++ b/erpnext/manufacturing/report/work_order_stock_report/test_work_order_stock_report.py @@ -0,0 +1,24 @@ +# Copyright (c) 2017, Velometro Mobility Inc and contributors +# For license information, please see license.txt + +import frappe + +from erpnext.tests.utils import ERPNextTestSuite + + +class TestWorkOrderStockReport(ERPNextTestSuite): + def test_report_executes_and_lists_work_order(self): + # get_item_list computes build_qty by multiplying bin/bom/bom_item columns that are not + # functionally dependent on the grouped item_code; they must be in the GROUP BY for the + # report to run on Postgres. This exercises that query on both engines. + from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record + from erpnext.manufacturing.report.work_order_stock_report.work_order_stock_report import execute + + wo = make_wo_order_test_record( + production_item="_Test FG Item", qty=1, source_warehouse="_Test Warehouse - _TC" + ) + + columns, data = execute(frappe._dict(warehouse="_Test Warehouse - _TC")) + + self.assertTrue(columns) + self.assertIn(wo.name, {row["work_order"] for row in data}) diff --git a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py index 998b0e4bccd..0637b14ccc7 100644 --- a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py +++ b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py @@ -47,7 +47,10 @@ def get_item_list(wo_list, filters): & (bom_item.item_code == wo_item_details.item_code) & (bom.name == wo_details.bom_no) ) - .groupby(bom_item.item_code) + # build_qty multiplies columns from bin/bom/bom_item that aren't functionally + # dependent on the grouped item_code, so postgres requires them in the GROUP BY. + # The WHERE pins bom, item and warehouse to single rows, so this stays one row. + .groupby(bom_item.item_code, bom.quantity, bom_item.stock_qty, bin.actual_qty) ).run(as_dict=1) stock_qty = 0