fix(buying): take the requested-item UOM pair off one line (#59132)

This commit is contained in:
Mihir Kandoi
2026-09-17 13:56:34 +05:30
committed by GitHub
parent 2aab7f4f72
commit 0f66c41819
2 changed files with 51 additions and 8 deletions

View File

@@ -51,7 +51,6 @@ def get_data(filters):
mr_item.item_code.as_("item_code"),
Sum(Coalesce(mr_item.qty, 0)).as_("qty"),
Sum(Coalesce(mr_item.stock_qty, 0)).as_("stock_qty"),
Max(Coalesce(mr_item.stock_uom, "")).as_("stock_uom"),
Sum(Coalesce(mr_item.ordered_qty, 0)).as_("ordered_qty"),
Sum(Coalesce(mr_item.received_qty, 0)).as_("received_qty"),
(Sum(Coalesce(mr_item.stock_qty, 0)) - Sum(Coalesce(mr_item.received_qty, 0))).as_(
@@ -78,19 +77,14 @@ def get_data(filters):
def apply_representative_lines(rows):
"""Fill item_name/description/uom from one real Material Request Item line per group.
All three are editable per line, so a request listing the same item twice holds several values
per group. Aggregating them sorts text, and MariaDB folds case while PostgreSQL orders by byte
value, so the engines pick differently. Take the first line by idx.
"""
"""Fill the line-level columns from one real Material Request Item line: the first by idx."""
material_requests = list({row.material_request for row in rows})
representative = {}
if material_requests:
for line in frappe.get_all(
"Material Request Item",
filters={"parent": ("in", material_requests), "docstatus": 1},
fields=["parent", "item_code", "item_name", "description", "uom"],
fields=["parent", "item_code", "item_name", "description", "uom", "stock_uom"],
order_by="idx",
):
representative.setdefault((line.parent, line.item_code), line)
@@ -100,6 +94,7 @@ def apply_representative_lines(rows):
row.item_name = line.item_name if line else None
row.description = line.description if line else None
row.uom = line.uom if line else ""
row.stock_uom = line.stock_uom if line else ""
def get_conditions(filters, query, mr, mr_item):

View File

@@ -74,6 +74,54 @@ class TestRequestedItemsToOrderAndReceive(ERPNextTestSuite):
self.assertEqual(len(data), 1)
self.assertEqual(getdate(data[0].required_date), getdate(add_days(today(), 1)))
def test_uom_pair_comes_from_one_line(self):
"""uom and stock_uom describe a line, so the reported pair must be one that was posted.
A request can list the same item twice in different units. Sourcing each column separately
can report one line's uom beside another's stock_uom -- a pair belonging to neither.
"""
create_item("Test MR Report Uom Item")
mr = frappe.copy_doc(self.globalTestRecords["Material Request"][0])
mr.transaction_date = today()
mr.schedule_date = add_days(today(), 5)
mr.set("items", mr.items[:1])
row = mr.items[0]
row.item_code = "Test MR Report Uom Item"
row.item_name = "Test MR Report Uom Item"
row.description = "Test MR Report Uom Item"
row.uom = "Nos"
row.schedule_date = mr.schedule_date
mr.append(
"items",
{
"item_code": "Test MR Report Uom Item",
"item_name": "Test MR Report Uom Item",
"description": "Test MR Report Uom Item",
"uom": "Nos",
"qty": row.qty,
"warehouse": row.warehouse,
"schedule_date": mr.schedule_date,
},
)
mr.submit()
# cross the two picks: the line holding the higher uom holds the lower stock_uom, so an
# independently aggregated pair cannot belong to either line
for line, uom, stock_uom in ((mr.items[0], "Nos", "Box"), (mr.items[1], "Box", "Nos")):
frappe.db.set_value(
"Material Request Item",
line.name,
{"uom": uom, "stock_uom": stock_uom},
update_modified=False,
)
posted = {("Nos", "Box"), ("Box", "Nos")}
data = get_data(self.filters.update({"item_code": "Test MR Report Uom Item"}))
self.assertEqual(len(data), 1)
self.assertIn((data[0].uom, data[0].stock_uom), posted)
self.assertEqual((data[0].uom, data[0].stock_uom), ("Nos", "Box"), "must be the first line by idx")
def setup_material_request(self, order=False, receive=False, days=0):
po = None
mr = frappe.copy_doc(self.globalTestRecords["Material Request"][0])