From 725fd8ca973e0dc0b6f2b824edbc834378321385 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 17 Jun 2026 14:05:54 +0530 Subject: [PATCH] fix(postgres): satisfy strict GROUP BY in production plan sub-assembly queries Co-Authored-By: Claude Opus 4.8 (1M context) --- .../services/sub_assembly_queries.py | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/services/sub_assembly_queries.py b/erpnext/manufacturing/doctype/production_plan/services/sub_assembly_queries.py index c4e25cffae5..a86c24521ab 100644 --- a/erpnext/manufacturing/doctype/production_plan/services/sub_assembly_queries.py +++ b/erpnext/manufacturing/doctype/production_plan/services/sub_assembly_queries.py @@ -4,7 +4,7 @@ """Sub-assembly resolution helpers for Production Plan.""" import frappe -from frappe.query_builder.functions import IfNull, Sum +from frappe.query_builder.functions import IfNull, Max, Sum from frappe.utils import flt from erpnext.manufacturing.doctype.bom.bom import get_children as get_bom_children @@ -184,24 +184,27 @@ def _sub_assembly_rm_query(company, bom_no, include_non_stock_items, planned_qty def _sub_assembly_rm_columns(bei, bom, item, item_default, item_uom, planned_qty): + # only item_code/stock_uom are grouped; every other column is functionally dependent on the + # grouped item (item attributes) or arbitrary per BOM Item on MySQL -> Max() keeps the GROUP BY + # valid on postgres while returning the same value MySQL picked. return [ (IfNull(Sum(bei.stock_qty / IfNull(bom.quantity, 1)), 0) * planned_qty).as_("qty"), - item.item_name, - item.name.as_("item_code"), - bei.description, + Max(item.item_name).as_("item_name"), + Max(item.name).as_("item_code"), + Max(bei.description).as_("description"), bei.stock_uom, - bei.is_phantom_item, - bei.bom_no, - item.min_order_qty, - bei.source_warehouse, - item.default_material_request_type, - item.min_order_qty, - item_default.default_warehouse, - item.purchase_uom, - item_uom.conversion_factor, - item.safety_stock, - bom.item.as_("main_bom_item"), - bom.name.as_("main_bom"), + Max(bei.is_phantom_item).as_("is_phantom_item"), + Max(bei.bom_no).as_("bom_no"), + Max(item.min_order_qty).as_("min_order_qty"), + Max(bei.source_warehouse).as_("source_warehouse"), + Max(item.default_material_request_type).as_("default_material_request_type"), + Max(item.min_order_qty).as_("min_order_qty"), + Max(item_default.default_warehouse).as_("default_warehouse"), + Max(item.purchase_uom).as_("purchase_uom"), + Max(item_uom.conversion_factor).as_("conversion_factor"), + Max(item.safety_stock).as_("safety_stock"), + Max(bom.item).as_("main_bom_item"), + Max(bom.name).as_("main_bom"), ]