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. };