diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index ae8d3d455cb..81ff351d373 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -94,6 +94,7 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { description: __('Fetch Serial Numbers based on FIFO'), click: () => { let qty = this.dialog.fields_dict.qty.get_value(); + let already_selected_serial_nos = get_selected_serial_nos(me); let numbers = frappe.call({ method: "erpnext.stock.doctype.serial_no.serial_no.auto_fetch_serial_number", args: { @@ -101,7 +102,8 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { item_code: me.item_code, warehouse: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '', batch_nos: me.item.batch_no || null, - posting_date: me.frm.doc.posting_date || me.frm.doc.transaction_date + posting_date: me.frm.doc.posting_date || me.frm.doc.transaction_date, + exclude_sr_nos: already_selected_serial_nos } }); @@ -577,15 +579,29 @@ function get_pending_qty_fields(me) { return pending_qty_fields; } -function calc_total_selected_qty(me) { +// get all items with same item code except row for which selector is open. +function get_rows_with_same_item_code(me) { const { frm: { doc: { items }}, item: { name, item_code }} = me; - const totalSelectedQty = items - .filter( item => ( item.name !== name ) && ( item.item_code === item_code ) ) - .map( item => flt(item.qty) ) - .reduce( (i, j) => i + j, 0); + return items.filter(item => (item.name !== name) && (item.item_code === item_code)) +} + +function calc_total_selected_qty(me) { + const totalSelectedQty = get_rows_with_same_item_code(me) + .map(item => flt(item.qty)) + .reduce((i, j) => i + j, 0); return totalSelectedQty; } +function get_selected_serial_nos(me) { + const selected_serial_nos = get_rows_with_same_item_code(me) + .map(item => item.serial_no) + .filter(serial => serial) + .map(sr_no_string => sr_no_string.split('\n')) + .reduce((acc, arr) => acc.concat(arr), []) + .filter(serial => serial); + return selected_serial_nos; +}; + function check_can_calculate_pending_qty(me) { const { frm: { doc }, item } = me; const docChecks = doc.bom_no diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index bf62f50c971..3cb97558c2f 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -564,9 +564,15 @@ def get_delivery_note_serial_no(item_code, qty, delivery_note): return serial_nos @frappe.whitelist() -def auto_fetch_serial_number(qty, item_code, warehouse, posting_date=None, batch_nos=None, for_doctype=None): +def auto_fetch_serial_number(qty, item_code, warehouse, + posting_date=None, batch_nos=None, for_doctype=None, exclude_sr_nos=None): filters = { "item_code": item_code, "warehouse": warehouse } + if exclude_sr_nos is None: + exclude_sr_nos = [] + else: + exclude_sr_nos = get_serial_nos(clean_serial_no_string("\n".join(exclude_sr_nos))) + if batch_nos: try: filters["batch_no"] = json.loads(batch_nos) if (type(json.loads(batch_nos)) == list) else [json.loads(batch_nos)] @@ -578,10 +584,9 @@ def auto_fetch_serial_number(qty, item_code, warehouse, posting_date=None, batch serial_numbers = [] if for_doctype == 'POS Invoice': - reserved_sr_nos = get_pos_reserved_serial_nos(filters) - serial_numbers = fetch_serial_numbers(filters, qty, do_not_include=reserved_sr_nos) - else: - serial_numbers = fetch_serial_numbers(filters, qty) + exclude_sr_nos.extend(get_pos_reserved_serial_nos(filters)) + + serial_numbers = fetch_serial_numbers(filters, qty, do_not_include=exclude_sr_nos) return [d.get('name') for d in serial_numbers]