mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-18 17:08:42 +00:00
fix(manufacturing): keep Work Order Stock report one row per item (MariaDB parity)
#56196's Postgres GROUP BY fix added bom.quantity, bom_item.stock_qty and bin.actual_qty to the GROUP BY. bom.quantity and bin.actual_qty are pinned to a single value by the WHERE/join, but a BOM may list the same item_code on multiple lines with different stock_qty (validate_materials does not dedupe), so grouping by stock_qty SPLITS the row and changes req_items/instock on MariaDB for such BOMs. Aggregate build_qty with Max() and group by item_code only: one row per item_code (identical to the pre-#56196 single-line result; deterministic for duplicate lines), and Postgres-valid. MariaDB output is unchanged for the common single-line case and its row count is restored for the duplicate-line case.
This commit is contained in:
@@ -8,9 +8,9 @@ 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.
|
||||
# get_item_list aggregates build_qty with Max() and groups by item_code, so it returns one
|
||||
# row per item_code and runs on Postgres (which rejects non-grouped selected columns).
|
||||
# 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
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.query_builder.functions import IfNull
|
||||
from frappe.query_builder.functions import IfNull, Max, Sum
|
||||
from frappe.utils import cint
|
||||
|
||||
|
||||
@@ -40,17 +40,23 @@ def get_item_list(wo_list, filters):
|
||||
)
|
||||
.select(
|
||||
bom_item.item_code.as_("item_code"),
|
||||
IfNull(bin.actual_qty * bom.quantity / bom_item.stock_qty, 0).as_("build_qty"),
|
||||
# Aggregate so the query stays one row per item_code and is Postgres
|
||||
# GROUP-BY-valid. A BOM may list the same item on several lines; adding the
|
||||
# qty columns to GROUP BY would split the row and change the row count on
|
||||
# MariaDB. actual_qty (single warehouse) and bom.quantity (single BOM) are
|
||||
# pinned to one value, so Max() returns that value; the per-unit requirement
|
||||
# is the TOTAL of this item across its lines, so stock_qty is summed. For the
|
||||
# common single-line item this equals the prior expression exactly.
|
||||
IfNull(Max(bin.actual_qty) * Max(bom.quantity) / Sum(bom_item.stock_qty), 0).as_(
|
||||
"build_qty"
|
||||
),
|
||||
)
|
||||
.where(
|
||||
(bom.name == bom_item.parent)
|
||||
& (bom_item.item_code == wo_item_details.item_code)
|
||||
& (bom.name == wo_details.bom_no)
|
||||
)
|
||||
# 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)
|
||||
.groupby(bom_item.item_code)
|
||||
).run(as_dict=1)
|
||||
|
||||
stock_qty = 0
|
||||
|
||||
Reference in New Issue
Block a user