POS batch fixes (#12005)

diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 66c0c44..e86435c 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1225,7 +1225,7 @@
 	}
 });
 
-erpnext.show_serial_batch_selector = function(frm, d, callback, show_dialog) {
+erpnext.show_serial_batch_selector = function(frm, d, callback, on_close, show_dialog) {
 	frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() {
 		new erpnext.SerialNoBatchSelector({
 			frm: frm,
@@ -1234,7 +1234,8 @@
 				type: "Warehouse",
 				name: d.warehouse
 			},
-			callback: callback
+			callback: callback,
+			on_close: on_close
 		}, show_dialog);
 	});
 }
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index f348746..e21caad 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -20,6 +20,7 @@
 		this.item_code = this.item.item_code;
 		this.qty = this.item.qty;
 		this.make_dialog();
+		this.on_close_dialog();
 	},
 
 	make_dialog: function() {
@@ -115,6 +116,12 @@
 		this.dialog.show();
 	},
 
+	on_close_dialog: function() {
+		this.dialog.get_close_btn().on('click', () => {
+			this.on_close && this.on_close(this.item);
+		});
+	},
+
 	validate: function() {
 		let values = this.values;
 		if(!values.warehouse) {
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 f3599d5..2e690ec 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -170,8 +170,10 @@
 
 			// if actual_batch_qty and actual_qty if there is only one batch. In such
 			// a case, no point showing the dialog
-			if(field === 'qty' && (item.serial_no || item.batch_no)
-			    && (item.actual_batch_qty != item.actual_qty)) {
+			const show_dialog = item.has_serial_no || item.has_batch_no;
+
+			if (show_dialog && field == 'qty' && ((!item.batch_no && item.has_batch_no) ||
+				(item.has_serial_no) || (item.actual_batch_qty != item.actual_qty)) ) {
 				this.select_batch_and_serial_no(item);
 			} else {
 				this.update_item_in_frm(item, field, value)
@@ -198,7 +200,8 @@
 
 				// if actual_batch_qty and actual_qty if then there is only one batch. In such
 				// a case, no point showing the dialog
-				if (show_dialog && field == 'qty' && (item.actual_batch_qty != item.actual_qty)) {
+				if (show_dialog && field == 'qty' && ((!item.batch_no && item.has_batch_no) ||
+					(item.has_serial_no) || (item.actual_batch_qty != item.actual_qty)) ) {
 					// check has serial no/batch no and update cart
 					this.select_batch_and_serial_no(item);
 				} else {
@@ -209,18 +212,32 @@
 	}
 
 	select_batch_and_serial_no(item) {
+		frappe.dom.unfreeze();
+
 		erpnext.show_serial_batch_selector(this.frm, item, () => {
 			this.update_item_in_frm(item, 'qty', item.qty)
 				.then(() => {
 					// update cart
-					if (item.qty === 0) {
-						frappe.model.clear_doc(item.doctype, item.name);
-					}
-					this.update_cart_data(item);
+					frappe.run_serially([
+						() => {
+							if (item.qty === 0) {
+								frappe.model.clear_doc(item.doctype, item.name);
+							}
+						},
+						() => this.update_cart_data(item)
+					]);
 				});
+		}, () => {
+			this.on_close(item);
 		}, true);
 	}
 
+	on_close(item) {
+		if (!this.cart.exists(item.item_code) && item.qty) {
+			frappe.model.clear_doc(item.doctype, item.name);
+		}
+	}
+
 	update_cart_data(item) {
 		this.cart.add_item(item);
 		this.cart.update_taxes_and_totals();
@@ -735,7 +752,7 @@
 		if (this.exists(item.item_code)) {
 			// update quantity
 			this.update_item(item);
-		} else {
+		} else if (flt(item.qty) > 0.0) {
 			// add to cart
 			const $item = $(this.get_item_html(item));
 			$item.appendTo(this.$cart_items);