From bb5be1037273c1ad656a0674f46d709d23895d3f Mon Sep 17 00:00:00 2001 From: pandiyan Date: Mon, 13 Jul 2026 13:17:40 +0530 Subject: [PATCH] fix: allow barcode scan to add and increment items in pick list - allow new rows on scan when pick manually is enabled, since only then are scanned rows not subject to being overridden by set_item_locations on save - stop capping picked qty at the default demand qty (1) for rows added by the scanner itself, so repeat scans of the same barcode keep incrementing the row instead of failing with "maximum quantity scanned" - ignore barcode uom when matching an existing row if new rows aren't allowed, since there's no alternate-uom row to fall back to (cherry picked from commit 3ece4a615d0ce7a206a144acc78221c03df7135b) --- erpnext/public/js/utils/barcode_scanner.js | 13 +++++++++++-- erpnext/stock/doctype/pick_list/pick_list.js | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js index 77246d82bc6..c1ce996792a 100644 --- a/erpnext/public/js/utils/barcode_scanner.js +++ b/erpnext/public/js/utils/barcode_scanner.js @@ -15,6 +15,11 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { this.warehouse_field = opts.warehouse_field || "warehouse"; // field name on row which defines max quantity to be scanned e.g. picklist this.max_qty_field = opts.max_qty_field; + // row fields that, if set, mean max_qty_field is a real demand qty (e.g. from a + // linked Sales Order) that scanning must not exceed. Rows with none of these set + // have no real demand qty, so max_qty_field is just an arbitrary default and + // shouldn't cap further scans. + this.demand_ref_fields = opts.demand_ref_fields || []; // scanner won't add a new row if this flag is set. this.dont_allow_new_row = opts.dont_allow_new_row; // scanner will ask user to type the quantity instead of incrementing by 1 @@ -390,6 +395,9 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } async set_barcode_uom(row, uom) { + // e.g. Pick List: picked_qty is always tracked in stock UOM, so an incidental + // barcode uom must not overwrite the row's own uom. + if (this.max_qty_field) return; if (uom && frappe.meta.has_field(row.doctype, this.uom_field)) { await frappe.model.set_value(row.doctype, row.name, this.uom_field, uom); } @@ -456,8 +464,9 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { const matching_row = (row) => { const item_match = row.item_code == item_code; const batch_match = !row[this.batch_no_field] || row[this.batch_no_field] == batch_no; - const uom_match = !uom || row[this.uom_field] == uom; - const qty_in_limit = flt(row[this.qty_field]) < flt(row[this.max_qty_field]); + const uom_match = !uom || this.max_qty_field || row[this.uom_field] == uom; + const has_demand_qty = this.demand_ref_fields.some((fieldname) => row[fieldname]); + const qty_in_limit = !has_demand_qty || flt(row[this.qty_field]) < flt(row[this.max_qty_field]); const item_scanned = row.has_item_scanned; let warehouse_match = true; diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index b5367cd846e..f89355439b3 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -297,7 +297,8 @@ frappe.ui.form.on("Pick List", { items_table_name: "locations", qty_field: "picked_qty", max_qty_field: "qty", - dont_allow_new_row: true, + demand_ref_fields: ["sales_order_item", "material_request_item", "product_bundle_item"], + dont_allow_new_row: !frm.doc.pick_manually, prompt_qty: frm.doc.prompt_qty, serial_no_field: "serial_no", };