From 00a646ac25a65c1d0ce8ed077ad90ca8b3cac8bc 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 dd585041d71..9f344a6c576 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); } @@ -454,8 +462,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 bcb194bd23c..e50f8bc4390 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -288,7 +288,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: "not_supported", // doesn't make sense for picklist without a separate field. };