diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 3effdf7996848e1fe42f0f28a13cd1b4f6854812..5100f7082a030ec54a5507d38a532674efa5dc2c 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -1119,4 +1119,39 @@ $.extend(erpnext.stock.utils, { const barcode_scanner = new erpnext.utils.BarcodeScanner({ frm: frm }); barcode_scanner.scan_api_call(child_row.barcode, callback); }, + + get_serial_range(range_string, separator) { + /* Return an array of serial numbers generated from a range string. + + Examples (using separator "::"): + - "1::5" => ["1", "2", "3", "4", "5"] + - "SN0009::12" => ["SN0009", "SN0010", "SN0011", "SN0012"] + - "ABC//05::8" => ["ABC//05", "ABC//06", "ABC//07", "ABC//08"] + */ + if (!range_string) { + return; + } + + const [start_str, end_str] = range_string.trim().split(separator); + + if (!start_str || !end_str) { + return; + } + + const end_int = parseInt(end_str); + const length_difference = start_str.length - end_str.length; + const start_int = parseInt(start_str.substring(length_difference)); + + if (isNaN(start_int) || isNaN(end_int)) { + return; + } + + const serial_numbers = Array(end_int - start_int + 1) + .fill(1) + .map((x, y) => x + y) + .map((x) => x + start_int - 1); + return serial_numbers.map((val) => { + return start_str.substring(0, length_difference) + val.toString().padStart(end_str.length, "0"); + }); + }, }); diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 017f162fb0bd20513ea514b999d7c4fcaefb3109..1838ffa768bbf2c5d33f9ee728d283e7d32ff065 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -208,11 +208,21 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { depends_on: 'eval:doc.import_using_csv_file === 0', }, { - fieldtype: 'Small Text', - label: __('Enter Serial Nos'), - fieldname: 'upload_serial_nos', - depends_on: 'eval:doc.import_using_csv_file === 0', - description: __('Enter each serial no in a new line'), + fieldtype: "Data", + label: __("Enter Serial No Range"), + fieldname: "serial_no_range", + depends_on: "eval:doc.import_using_csv_file === 0", + description: __('Enter "ABC-001::100" for serial nos "ABC-001" to "ABC-100".'), + onchange: () => { + this.set_serial_nos_from_range(); + }, + }, + { + fieldtype: "Small Text", + label: __("Enter Serial Nos"), + fieldname: "upload_serial_nos", + depends_on: "eval:doc.import_using_csv_file === 0", + description: __("Enter each serial no in a new line"), }, { fieldtype: 'Column Break', @@ -255,6 +265,20 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { return fields; } + set_serial_nos_from_range() { + const serial_no_range = this.dialog.get_value("serial_no_range"); + + if (!serial_no_range) { + return; + } + + const serial_nos = erpnext.stock.utils.get_serial_range(serial_no_range, "::"); + + if (serial_nos) { + this.dialog.set_value("upload_serial_nos", serial_nos.join("\n")); + } + } + create_serial_nos() { let {upload_serial_nos} = this.dialog.get_values();