mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-15 07:28:39 +00:00
fix(postgres): satisfy strict GROUP BY in Bom Stock Analysis 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) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.query_builder.functions import Floor, IfNull, Sum
|
||||
from frappe.query_builder.functions import Floor, IfNull, Max, Min, Sum
|
||||
from frappe.utils import flt
|
||||
from frappe.utils.data import comma_and
|
||||
from pypika.terms import ExistsCriterion
|
||||
@@ -202,14 +202,15 @@ def get_bom_data(filters):
|
||||
.on(bom_item.item_code == bin.item_code)
|
||||
.select(
|
||||
bom_item.item_code,
|
||||
bom_item.description,
|
||||
bom_item.parent.as_("from_bom_no"),
|
||||
# non-grouped columns are constant per grouped item_code -> Max() keeps the GROUP BY valid
|
||||
Max(bom_item.description).as_("description"),
|
||||
Max(bom_item.parent).as_("from_bom_no"),
|
||||
Sum(bom_item.qty_consumed_per_unit).as_("qty_per_unit"),
|
||||
IfNull(Sum(bin.actual_qty), 0).as_("actual_qty"),
|
||||
)
|
||||
.where((bom_item.parent == filters.get("bom")) & (bom_item.parenttype == "BOM"))
|
||||
.groupby(bom_item.item_code)
|
||||
.orderby(bom_item.idx)
|
||||
.orderby(Min(bom_item.idx))
|
||||
)
|
||||
|
||||
if filters.get("warehouse"):
|
||||
@@ -233,7 +234,9 @@ def get_bom_data(filters):
|
||||
query = query.where(bin.warehouse == filters.get("warehouse"))
|
||||
|
||||
if bom_item_table == "BOM Item":
|
||||
query = query.select(bom_item.bom_no, bom_item.is_phantom_item)
|
||||
query = query.select(
|
||||
Max(bom_item.bom_no).as_("bom_no"), Max(bom_item.is_phantom_item).as_("is_phantom_item")
|
||||
)
|
||||
|
||||
data = query.run(as_dict=True)
|
||||
return explode_phantom_boms(data, filters) if bom_item_table == "BOM Item" else data
|
||||
@@ -312,15 +315,17 @@ def get_producible_fg_items(filters):
|
||||
.on(BOM_ITEM.item_code == bin_subquery.item_code)
|
||||
.select(
|
||||
BOM_ITEM.item_code,
|
||||
BOM_ITEM.description,
|
||||
BOM_ITEM.parent.as_("from_bom_no"),
|
||||
(BOM_ITEM.stock_qty / BOM.quantity).as_("qty_per_unit"),
|
||||
IfNull(bin_subquery.actual_qty, 0).as_("available_qty"),
|
||||
Floor(bin_subquery.actual_qty / ((Sum(BOM_ITEM.stock_qty)) / BOM.quantity)),
|
||||
# Sum() below makes this an aggregate query; the other columns are constant per grouped
|
||||
# item_code -> Max() keeps them valid on postgres with the same value MySQL picked.
|
||||
Max(BOM_ITEM.description).as_("description"),
|
||||
Max(BOM_ITEM.parent).as_("from_bom_no"),
|
||||
Max(BOM_ITEM.stock_qty / BOM.quantity).as_("qty_per_unit"),
|
||||
Max(IfNull(bin_subquery.actual_qty, 0)).as_("available_qty"),
|
||||
Floor(Max(bin_subquery.actual_qty) / ((Sum(BOM_ITEM.stock_qty)) / Max(BOM.quantity))),
|
||||
)
|
||||
.where((BOM_ITEM.parent == filters.get("bom")) & (BOM_ITEM.parenttype == "BOM"))
|
||||
.groupby(BOM_ITEM.item_code)
|
||||
.orderby(BOM_ITEM.idx)
|
||||
.orderby(Min(BOM_ITEM.idx))
|
||||
)
|
||||
|
||||
return query.run(as_list=True)
|
||||
|
||||
Reference in New Issue
Block a user