mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-14 15:11:52 +00:00
Merge pull request #56197 from mihir-kandoi/pg-stock-entry-groupby-fix
fix(stock): Postgres GROUP-BY validity for Job Card secondary-item & disassembly queries
This commit is contained in:
@@ -2,7 +2,7 @@ from collections import defaultdict
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.query_builder.functions import Max, Min, NullIf, Sum
|
||||
from frappe.utils import flt
|
||||
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
@@ -348,12 +348,40 @@ class DisassembleStockEntry(BaseStockEntry):
|
||||
.run(as_dict=True)
|
||||
)
|
||||
|
||||
# Aggregating across all Manufacture entries of the work order, one row per item_code.
|
||||
# The non-grouped columns are constant per item_code in practice (an item plays one role with
|
||||
# one uom/warehouse across the WO's manufacture entries); Max() keeps the GROUP BY valid on
|
||||
# postgres while returning the value MySQL picked arbitrarily, preserving the one-row-per-item
|
||||
# shape the disassembly expects.
|
||||
return (
|
||||
query.select(Sum(SED.qty).as_("qty"), Sum(SED.transfer_qty).as_("transfer_qty"), *common_fields)
|
||||
query.select(
|
||||
Sum(SED.qty).as_("qty"),
|
||||
Sum(SED.transfer_qty).as_("transfer_qty"),
|
||||
SED.item_code,
|
||||
Max(SED.item_name).as_("item_name"),
|
||||
Max(SED.description).as_("description"),
|
||||
Max(SED.stock_uom).as_("stock_uom"),
|
||||
Max(SED.uom).as_("uom"),
|
||||
# qty-weighted average so consolidating an item across manufacture entries at different
|
||||
# valuation rates values the summed qty correctly (Max would bias the rate high).
|
||||
# Manufacture rows always carry positive qty, so NullIf only guards a theoretical /0.
|
||||
(Sum(SED.basic_rate * SED.qty) / NullIf(Sum(SED.qty), 0)).as_("basic_rate"),
|
||||
Max(SED.conversion_factor).as_("conversion_factor"),
|
||||
Max(SED.is_finished_item).as_("is_finished_item"),
|
||||
Max(SED.secondary_item_type).as_("secondary_item_type"),
|
||||
Max(SED.is_legacy_scrap_item).as_("is_legacy_scrap_item"),
|
||||
Max(SED.bom_secondary_item).as_("bom_secondary_item"),
|
||||
Max(SED.batch_no).as_("batch_no"),
|
||||
Max(SED.serial_no).as_("serial_no"),
|
||||
Max(SED.use_serial_batch_fields).as_("use_serial_batch_fields"),
|
||||
Max(SED.s_warehouse).as_("s_warehouse"),
|
||||
Max(SED.t_warehouse).as_("t_warehouse"),
|
||||
Max(SED.bom_no).as_("bom_no"),
|
||||
)
|
||||
.where(SE.purpose == "Manufacture")
|
||||
.where(SE.work_order == self.doc.work_order)
|
||||
.groupby(SED.item_code)
|
||||
.orderby(SED.idx)
|
||||
.orderby(Min(SED.idx))
|
||||
.run(as_dict=True)
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from collections import defaultdict
|
||||
|
||||
import frappe
|
||||
from frappe import _, bold
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.query_builder.functions import Max, Min, Sum
|
||||
from frappe.utils import ceil, cint, flt, get_link_to_form
|
||||
|
||||
from erpnext.manufacturing.doctype.bom.bom import add_additional_cost
|
||||
@@ -1005,11 +1005,14 @@ def get_secondary_items_from_job_card(work_order, jc_name=None):
|
||||
.select(
|
||||
Sum(job_card_secondary_item.stock_qty).as_("stock_qty"),
|
||||
job_card_secondary_item.item_code,
|
||||
job_card_secondary_item.item_name,
|
||||
job_card_secondary_item.description,
|
||||
job_card_secondary_item.stock_uom,
|
||||
# non-grouped columns are item attributes / the secondary-item BOM link, constant per
|
||||
# grouped (item_code, secondary_item_type) -> Max() keeps the GROUP BY valid on postgres
|
||||
# while returning the value MySQL picked arbitrarily.
|
||||
Max(job_card_secondary_item.item_name).as_("item_name"),
|
||||
Max(job_card_secondary_item.description).as_("description"),
|
||||
Max(job_card_secondary_item.stock_uom).as_("stock_uom"),
|
||||
job_card_secondary_item.secondary_item_type,
|
||||
job_card_secondary_item.bom_secondary_item,
|
||||
Max(job_card_secondary_item.bom_secondary_item).as_("bom_secondary_item"),
|
||||
)
|
||||
.join(job_card_secondary_item)
|
||||
.on(job_card_secondary_item.parent == job_card.name)
|
||||
@@ -1019,7 +1022,7 @@ def get_secondary_items_from_job_card(work_order, jc_name=None):
|
||||
& (job_card.docstatus == 1)
|
||||
)
|
||||
.groupby(job_card_secondary_item.item_code, job_card_secondary_item.secondary_item_type)
|
||||
.orderby(job_card_secondary_item.idx)
|
||||
.orderby(Min(job_card_secondary_item.idx))
|
||||
)
|
||||
|
||||
if jc_name:
|
||||
|
||||
Reference in New Issue
Block a user