Don't set batch nos automatically on saving, if already set and validate qty with batch (#8887)

diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 449a975..6e3990a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -17,6 +17,7 @@
 from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
 from erpnext.accounts.doctype.asset.depreciation \
 	import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal
+from erpnext.stock.doctype.batch.batch import set_batch_nos
 
 form_grid_templates = {
 	"items": "templates/form_grid/item_grid.html"
@@ -78,6 +79,10 @@
 
 		if not self.is_opening:
 			self.is_opening = 'No'
+			
+		if self._action != 'submit' and self.update_stock and not self.is_return:
+			set_batch_nos(self, 'warehouse', True)
+			
 
 		self.set_against_income_account()
 		self.validate_c_form()
@@ -87,7 +92,7 @@
 		self.set_billing_hours_and_amount()
 		self.update_timesheet_billing_for_project()
 		self.set_status()
-
+	
 	def before_save(self):
 		set_account_for_mode_of_payment(self)
 
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index 5749afb..909095e 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -5,6 +5,7 @@
 import frappe
 from frappe import _
 from frappe.model.document import Document
+from frappe.utils import flt
 
 class UnableToSelectBatchError(frappe.ValidationError): pass
 
@@ -96,24 +97,27 @@
 	for d in doc.items:
 		has_batch_no = frappe.db.get_value('Item', d.item_code, 'has_batch_no')
 		warehouse = d.get(warehouse_field, None)
-		if has_batch_no and not d.batch_no and warehouse:
-			d.batch_no = get_batch_no(d.item_code, warehouse, d.qty, throw)
+		if has_batch_no and warehouse and d.qty > 0:
+			if not d.batch_no:
+				d.batch_no = get_batch_no(d.item_code, warehouse, d.qty, throw)
+			else:
+				batch_qty = get_batch_qty(batch_no=d.batch_no, warehouse=warehouse)
+				if flt(batch_qty) < flt(d.qty):
+					frappe.throw(_("Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches").format(d.idx, d.batch_no, batch_qty, d.qty))
 
 def get_batch_no(item_code, warehouse, qty, throw=False):
 	'''get the smallest batch with for the given item_code, warehouse and qty'''
 	
 	batch_no = None
-	
 	batches = get_batch_qty(item_code = item_code, warehouse = warehouse)
 	if batches:
 		batches = sorted(batches, lambda a, b: 1 if a.qty > b.qty else -1)
-
 		for b in batches:
 			if b.qty >= qty:
 				batch_no = b.batch_no
 				# found!
 				break
-
+	
 	if not batch_no:
 		frappe.msgprint(_('Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement').format(frappe.bold(item_code)))
 		if throw: raise UnableToSelectBatchError
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index e7bf827..441b637 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -106,7 +106,7 @@
 		self.validate_uom_is_integer("uom", "qty")
 		self.validate_with_previous_doc()
 
-		if self._action != 'submit':
+		if self._action != 'submit' and not self.is_return:
 			set_batch_nos(self, 'warehouse', True)
 
 		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 35760fd..18db8f9 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -81,7 +81,7 @@
 		if out.has_serial_no:
 			out.serial_no = get_serial_no(out)
 
-		if out.has_batch_no:
+		if out.has_batch_no and not args.get("batch_no"):
 			out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty)