fix: replace column-literal work order filter with server-side query

The work_order link filter in Stock Entry passed the string
`tabWork Order`.produced_qty as a filter value. It was never a real
column comparison: db_query coerces string values on numeric fields
with flt(), so the condition silently degraded to qty > 0, and on
backends that don't coerce text to numeric (postgres) such filters
fail with InvalidTextRepresentation.

Move the condition into a whitelisted search query that compares the
columns properly (qty > produced_qty), mirroring pick_list's
get_pending_work_orders.
This commit is contained in:
Mihir Kandoi
2026-07-17 13:54:08 +05:30
parent d8eb029284
commit eed7c98b30
3 changed files with 49 additions and 6 deletions

View File

@@ -21,11 +21,10 @@ frappe.ui.form.on("Stock Entry", {
frm.set_query("work_order", function () {
return {
filters: [
["Work Order", "docstatus", "=", 1],
["Work Order", "qty", ">", "`tabWork Order`.produced_qty"],
["Work Order", "company", "=", frm.doc.company],
],
query: "erpnext.stock.doctype.stock_entry.stock_entry.get_pending_work_orders",
filters: {
company: frm.doc.company,
},
};
});

View File

@@ -1577,6 +1577,28 @@ def make_stock_in_entry(source_name: str, target_doc: str | Document | None = No
return doclist
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_pending_work_orders(
doctype: str, txt: str, searchfield: str, start: int, page_length: int, filters: dict
) -> list:
work_order = frappe.qb.DocType("Work Order")
query = frappe.qb.get_query(
"Work Order",
fields=["name", "production_item"],
filters={
"docstatus": 1,
"company": filters.get("company"),
"name": ("like", f"%{txt}%"),
},
order_by="name",
limit=cint(page_length),
offset=cint(start),
ignore_permissions=False,
)
return query.where(work_order.qty > work_order.produced_qty).run()
@frappe.whitelist()
def get_work_order_details(work_order: str, company: str):
work_order = frappe.get_doc("Work Order", work_order)

View File

@@ -26,7 +26,11 @@ from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle
make_serial_batch_bundle,
)
from erpnext.stock.doctype.serial_no.serial_no import *
from erpnext.stock.doctype.stock_entry.stock_entry import FinishedGoodError, make_stock_in_entry
from erpnext.stock.doctype.stock_entry.stock_entry import (
FinishedGoodError,
get_pending_work_orders,
make_stock_in_entry,
)
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
@@ -3392,6 +3396,24 @@ class TestStockEntryCoverage(ERPNextTestSuite):
for bn in list(get_batches_from_bundle(row.serial_and_batch_bundle).keys()):
self.assertIn(bn, wo1_batches)
def test_get_pending_work_orders(self):
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
wo = make_wo_order_test_record(qty=2, skip_transfer=True)
def pending_work_orders(txt=""):
return [
row[0]
for row in get_pending_work_orders("Work Order", txt, "name", 0, 0, {"company": wo.company})
]
self.assertIn(wo.name, pending_work_orders())
self.assertIn(wo.name, pending_work_orders(wo.name.lower()))
self.assertNotIn(wo.name, pending_work_orders("no-such-work-order"))
frappe.db.set_value("Work Order", wo.name, "produced_qty", wo.qty)
self.assertNotIn(wo.name, pending_work_orders())
def make_serialized_item(self, **args):
args = frappe._dict(args)