From 68ba4fd69707978dc12dfa4a8df1f546bdc9dc5e Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 28 Mar 2018 12:33:15 +0530 Subject: [PATCH] [Fix] Multi-batch selection issue in POS (#13407) --- .../js/utils/serial_no_batch_selector.js | 77 ++++++++++++------ .../page/point_of_sale/point_of_sale.js | 79 ++++++++++++------- 2 files changed, 104 insertions(+), 52 deletions(-) diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 0fb87300bb..78def269df 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -85,8 +85,6 @@ erpnext.SerialNoBatchSelector = Class.extend({ fields: fields }); - this.bind_qty(); - this.dialog.set_primary_action(__('Insert'), function() { me.values = me.dialog.get_values(); if(me.validate()) { @@ -102,17 +100,24 @@ erpnext.SerialNoBatchSelector = Class.extend({ } if (d.batch_no) { - this.dialog.fields_dict.batches.df.data.push({ - 'batch_no': d.batch_no, - 'actual_qty': d.actual_qty, - 'selected_qty': d.qty, - 'available_qty': d.actual_batch_qty + this.frm.doc.items.forEach(data => { + if(data.item_code == d.item_code) { + this.dialog.fields_dict.batches.df.data.push({ + 'batch_no': data.batch_no, + 'actual_qty': data.actual_qty, + 'selected_qty': data.qty, + 'available_qty': data.actual_batch_qty + }); + } }); - this.dialog.fields_dict.batches.grid.refresh(); } } + if (this.has_batch) { + this.update_total_qty(); + } + this.dialog.show(); }, @@ -161,18 +166,44 @@ erpnext.SerialNoBatchSelector = Class.extend({ var me = this; if(this.has_batch) { this.values.batches.map((batch, i) => { - let item_code_field = {}; - let row = (i !== 0) ? this.frm.add_child("items", this.item) : this.item; + let batch_no = batch.batch_no; + let row = ''; + + if (i !== 0 && !this.batch_exists(batch_no)) { + row = this.frm.add_child("items", { + 'item_code': this.item.item_code, + 'item_name': this.item.item_name, + 'price_list_rate': this.item.price_list_rate, + 'rate': this.item.rate, + 'qty': batch.selected_qty, + 'batch_no': batch_no, + 'actual_qty': this.item.actual_qty, + 'discount_percentage': this.item.discount_percentage + }); + } else { + row = this.frm.doc.items.find(i => i.batch_no === batch_no); + } + + if (!row) { + row = this.item; + } + this.map_row_values(row, batch, 'batch_no', 'selected_qty', this.values.warehouse); }); } else { this.map_row_values(this.item, this.values, 'serial_no', 'qty'); } + refresh_field("items"); this.callback && this.callback(this.item); }, + batch_exists: function(batch) { + const batches = this.frm.doc.items.map(data => data.batch_no); + return (batches && in_list(batches, batch)) ? true : false; + }, + map_row_values: function(row, values, number, qty_field, warehouse) { row.qty = values[qty_field]; row[number] = values[number]; @@ -185,20 +216,15 @@ erpnext.SerialNoBatchSelector = Class.extend({ } }, - bind_qty: function() { - let batches_field = this.dialog.fields_dict.batches; + update_total_qty: function() { let qty_field = this.dialog.fields_dict.qty; - if(batches_field) { - batches_field.grid.wrapper.on('change', function() { - let total_qty = 0; - batches_field.grid.wrapper.find( - 'input[data-fieldname="selected_qty"]').each(function() { + let total_qty = 0; - total_qty += Number($(this).val()); - }); - qty_field.set_input(total_qty); - }); - } + this.dialog.fields_dict.batches.df.data.forEach(data => { + total_qty += flt(data.selected_qty); + }); + + qty_field.set_input(total_qty); }, get_batch_fields: function() { @@ -230,7 +256,10 @@ erpnext.SerialNoBatchSelector = Class.extend({ if(row === this.grid_row) { return ""; } - return row.on_grid_fields_dict.batch_no.get_value(); + + if (row.on_grid_fields_dict.batch_no) { + return row.on_grid_fields_dict.batch_no.get_value(); + } }); if(selected_batches.includes(val)) { this.set_value(""); @@ -293,6 +322,8 @@ erpnext.SerialNoBatchSelector = Class.extend({ } else { this.grid.refresh(); } + + me.update_total_qty(); } }, ], diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index a027a98656..400cd9517f 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -23,7 +23,7 @@ frappe.pages['point-of-sale'].on_page_load = function(wrapper) { frappe.pages['point-of-sale'].refresh = function(wrapper) { if (wrapper.pos) { - cur_frm = wrapper.pos.frm; + wrapper.pos.make_new_invoice(); } if (frappe.flags.is_offline) { @@ -96,8 +96,8 @@ erpnext.pos.PointOfSale = class PointOfSale { wrapper: this.wrapper.find('.cart-container'), events: { on_customer_change: (customer) => this.frm.set_value('customer', customer), - on_field_change: (item_code, field, value) => { - this.update_item_in_cart(item_code, field, value); + on_field_change: (item_code, field, value, batch_no) => { + this.update_item_in_cart(item_code, field, value, batch_no); }, on_numpad: (value) => { if (value == 'Pay') { @@ -158,10 +158,12 @@ erpnext.pos.PointOfSale = class PointOfSale { }); } - update_item_in_cart(item_code, field='qty', value=1) { + update_item_in_cart(item_code, field='qty', value=1, batch_no) { frappe.dom.freeze(); - if(this.cart.exists(item_code)) { - const item = this.frm.doc.items.find(i => i.item_code === item_code); + if(this.cart.exists(item_code, batch_no)) { + const search_field = batch_no ? 'batch_no' : 'item_code'; + const search_value = batch_no || item_code; + const item = this.frm.doc.items.find(i => i[search_field] === search_value); frappe.flags.hide_serial_batch_dialog = false; if (typeof value === 'string' && !in_list(['serial_no', 'batch_no'], field)) { @@ -219,29 +221,31 @@ erpnext.pos.PointOfSale = class PointOfSale { ]); } - select_batch_and_serial_no(item) { + select_batch_and_serial_no(row) { frappe.dom.unfreeze(); - erpnext.show_serial_batch_selector(this.frm, item, () => { - this.update_item_in_frm(item, 'qty', item.qty) - .then(() => { - // update cart - frappe.run_serially([ - () => { - if (item.qty === 0) { - frappe.model.clear_doc(item.doctype, item.name); - } - }, - () => this.update_cart_data(item) - ]); - }); + erpnext.show_serial_batch_selector(this.frm, row, () => { + this.frm.doc.items.forEach(item => { + this.update_item_in_frm(item, 'qty', item.qty) + .then(() => { + // update cart + frappe.run_serially([ + () => { + if (item.qty === 0) { + frappe.model.clear_doc(item.doctype, item.name); + } + }, + () => this.update_cart_data(item) + ]); + }); + }) }, () => { - this.on_close(item); + this.on_close(row); }, true); } on_close(item) { - if (!this.cart.exists(item.item_code) && item.qty) { + if (!this.cart.exists(item.item_code, item.batch_no) && item.qty) { frappe.model.clear_doc(item.doctype, item.name); } } @@ -492,6 +496,11 @@ erpnext.pos.PointOfSale = class PointOfSale { // // }).addClass('visible-xs'); + this.page.add_menu_item(__("Form View"), function () { + frappe.model.sync(me.frm.doc); + frappe.set_route("Form", me.frm.doc.doctype, me.frm.doc.name); + }); + this.page.add_menu_item(__("POS Profile"), function () { frappe.set_route('List', 'POS Profile'); }); @@ -602,11 +611,15 @@ class POSCart { this.customer_field.set_value(""); this.frm.msgbox = ""; + let total_item_qty = 0.0; + this.frm.set_value("pos_total_qty",total_item_qty); + this.$discount_amount.find('input:text').val(''); this.wrapper.find('.grand-total-value').text( format_currency(this.frm.doc.grand_total, this.frm.currency)); this.wrapper.find('.rounded-total-value').text( format_currency(this.frm.doc.rounded_total, this.frm.currency)); + this.$qty_total.find(".quantity-total").text(total_item_qty); const customer = this.frm.doc.customer; this.customer_field.set_value(customer); @@ -721,7 +734,7 @@ class POSCart { total_item_qty += d.qty; } }); - this.$qty_total.find('.quantity-total').text(total_item_qty) + this.$qty_total.find('.quantity-total').text(total_item_qty); this.frm.set_value("pos_total_qty",total_item_qty); } @@ -804,10 +817,11 @@ class POSCart { this.numpad.reset_value(); } else { const item_code = this.selected_item.attr('data-item-code'); + const batch_no = this.selected_item.attr('data-batch-no'); const field = this.selected_item.active_field; const value = this.numpad.get_value(); - this.events.on_field_change(item_code, field, value); + this.events.on_field_change(item_code, field, value, batch_no); } } @@ -835,7 +849,7 @@ class POSCart { add_item(item) { this.$empty_state.hide(); - if (this.exists(item.item_code)) { + if (this.exists(item.item_code, item.batch_no)) { // update quantity this.update_item(item); } else if (flt(item.qty) > 0.0) { @@ -848,7 +862,10 @@ class POSCart { } update_item(item) { - const $item = this.$cart_items.find(`[data-item-code="${item.item_code}"]`); + const item_selector = item.batch_no ? + `[data-batch-no="${item.batch_no}"]` : `[data-item-code="${item.item_code}"]`; + + const $item = this.$cart_items.find(item_selector); if(item.qty > 0) { const is_stock_item = this.get_item_details(item.item_code).is_stock_item; @@ -870,7 +887,8 @@ class POSCart { const rate = format_currency(item.rate, this.frm.doc.currency); const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red'; return ` -
+
${item.item_name}
@@ -911,8 +929,11 @@ class POSCart { return this.item_data[item_code]; } - exists(item_code) { - let $item = this.$cart_items.find(`[data-item-code="${item_code}"]`); + exists(item_code, batch_no) { + const is_exists = batch_no ? + `[data-batch-no="${batch_no}"]` : `[data-item-code="${item_code}"]`; + + let $item = this.$cart_items.find(is_exists); return $item.length > 0; }