diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index fb4f35081c2..6860d6a2384 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -58,43 +58,9 @@ $.extend(erpnext, { .css({"margin-bottom": "10px", "margin-top": "10px"}) .appendTo(grid_row.grid_form.fields_dict.serial_no.$wrapper)); + var me = this; $btn.on("click", function() { - var d = new frappe.ui.Dialog({ - title: __("Add Serial No"), - fields: [ - { - "fieldtype": "Link", - "fieldname": "serial_no", - "options": "Serial No", - "label": __("Serial No"), - "get_query": function () { - return { - filters: { - item_code:grid_row.doc.item_code, - warehouse:cur_frm.doc.is_return ? null : grid_row.doc.warehouse - } - } - } - }, - { - "fieldtype": "Button", - "fieldname": "add", - "label": __("Add") - } - ] - }); - - d.get_input("add").on("click", function() { - var serial_no = d.get_value("serial_no"); - if(serial_no) { - var val = (grid_row.doc.serial_no || "").split("\n").concat([serial_no]).join("\n"); - grid_row.grid_form.fields_dict.serial_no.set_model_value(val.trim()); - } - d.hide(); - return false; - }); - - d.show(); + me.show_serial_batch_selector(grid_row.frm, grid_row.doc); }); } }); diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index b94cdd8c4c3..b22d5ca4c4e 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -5,12 +5,11 @@ erpnext.SerialNoBatchSelector = Class.extend({ this.show_dialog = show_dialog; // frm, item, warehouse_details, has_batch, oldest let d = this.item; - - // Don't show dialog if batch no or serial no already set - if(d && d.has_batch_no && (!d.batch_no || this.show_dialog)) { + if (d && d.has_batch_no && (!d.batch_no || this.show_dialog)) { this.has_batch = 1; this.setup(); - } else if(d && d.has_serial_no && (!d.serial_no || this.show_dialog)) { + // !(this.show_dialog == false) ensures that show_dialog is implictly true, even when undefined + } else if(d && d.has_serial_no && !(this.show_dialog == false)) { this.has_batch = 0; this.setup(); } @@ -68,13 +67,41 @@ erpnext.SerialNoBatchSelector = Class.extend({ { fieldname: 'qty', fieldtype:'Float', - read_only: 1, + read_only: me.has_batch, label: __(me.has_batch ? 'Total Qty' : 'Qty'), default: 0 }, + { + fieldname: 'auto_fetch_button', + fieldtype:'Button', + hidden: me.has_batch, + label: __('Fetch based on FIFO'), + click: () => { + let qty = this.dialog.fields_dict.qty.get_value(); + let numbers = frappe.call({ + method: "erpnext.stock.doctype.serial_no.serial_no.auto_fetch_serial_number", + args: { + qty: qty, + item_code: me.item_code, + warehouse: me.warehouse_details.name + } + }); + + numbers.then((data) => { + let auto_fetched_serial_numbers = data.message; + let records_length = auto_fetched_serial_numbers.length; + if (records_length < qty) { + frappe.msgprint(`Fetched only ${records_length} serial numbers.`); + } + let serial_no_list_field = this.dialog.fields_dict.serial_no; + numbers = auto_fetched_serial_numbers.join('\n'); + serial_no_list_field.set_value(numbers); + }); + } + } ]; - if(this.has_batch) { + if (this.has_batch) { title = __("Select Batch Numbers"); fields = fields.concat(this.get_batch_fields()); } else { @@ -87,6 +114,10 @@ erpnext.SerialNoBatchSelector = Class.extend({ fields: fields }); + if (this.item.serial_no) { + this.dialog.fields_dict.serial_no.set_value(this.item.serial_no); + } + this.dialog.set_primary_action(__('Insert'), function() { me.values = me.dialog.get_values(); if(me.validate()) { @@ -234,7 +265,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ var me = this; return [ {fieldtype:'Section Break', label: __('Batches')}, - {fieldname: 'batches', fieldtype: 'Table', + {fieldname: 'batches', fieldtype: 'Table', label: __('Batch Entries'), fields: [ { fieldtype:'Link', @@ -343,10 +374,10 @@ erpnext.SerialNoBatchSelector = Class.extend({ var me = this; this.serial_list = []; return [ - {fieldtype: 'Section Break', label: __('Serial No')}, + {fieldtype: 'Section Break', label: __('Serial Numbers')}, { fieldtype: 'Link', fieldname: 'serial_no_select', options: 'Serial No', - label: __('Select'), + label: __('Select to add Serial Number.'), get_query: function() { return { filters: {item_code: me.item_code, warehouse: me.warehouse_details.name}}; }, @@ -383,6 +414,7 @@ erpnext.SerialNoBatchSelector = Class.extend({ { fieldname: 'serial_no', fieldtype: 'Small Text', + label: __(me.has_batch ? 'Selected Batch Numbers' : 'Selected Serial Numbers'), onchange: function() { me.serial_list = this.get_value() .replace(/\n/g, ' ').match(/\S+/g) || []; diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index cb1d1532447..c1aef95216d 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -459,3 +459,13 @@ def get_delivery_note_serial_no(item_code, qty, delivery_note): serial_nos = '\n'.join(dn_serial_nos) return serial_nos + +@frappe.whitelist() +def auto_fetch_serial_number(qty, item_code, warehouse): + serial_numbers = frappe.get_list("Serial No", filters={ + "item_code": item_code, + "warehouse": warehouse, + "delivery_document_no": "", + "sales_invoice": "" + }, limit=qty, order_by="creation") + return [item['name'] for item in serial_numbers]