Merge branch 'develop' of https://github.com/frappe/erpnext into develop-ritvik-POS-runtime-effect
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 4b2fcec..89a9611 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -542,6 +542,7 @@
 		is_stock_item = True
 		bin_qty = get_bin_qty(item_code, warehouse)
 		pos_sales_qty = get_pos_reserved_qty(item_code, warehouse)
+
 		return bin_qty - pos_sales_qty, is_stock_item
 	else:
 		is_stock_item = True
@@ -595,7 +596,6 @@
 		.where(
 			(p_inv.name == p_item.parent)
 			& (IfNull(p_inv.consolidated_invoice, "") == "")
-			& (p_inv.is_return == 0)
 			& (p_item.docstatus == 1)
 			& (p_item.item_code == item_code)
 			& (p_item.warehouse == warehouse)
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index d8cbcc1..4f46aa1 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -95,7 +95,6 @@
 			sales_invoice = self.process_merging_into_sales_invoice(sales)
 
 		self.save()  # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
-
 		self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
 
 	def on_cancel(self):
@@ -108,7 +107,6 @@
 
 	def process_merging_into_sales_invoice(self, data):
 		sales_invoice = self.get_new_sales_invoice()
-
 		sales_invoice = self.merge_pos_invoice_into(sales_invoice, data)
 
 		sales_invoice.is_consolidated = 1
@@ -165,8 +163,7 @@
 				for i in items:
 					if (
 						i.item_code == item.item_code
-						and not i.serial_no
-						and not i.batch_no
+						and not i.serial_and_batch_bundle
 						and i.uom == item.uom
 						and i.net_rate == item.net_rate
 						and i.warehouse == item.warehouse
@@ -276,6 +273,21 @@
 			si.flags.ignore_validate = True
 			si.cancel()
 
+	def get_batched_invoices(self, pos_invoice_docs):
+		grouped_batch = []
+		current_batch = []
+		for item in pos_invoice_docs:
+			if not current_batch:
+				current_batch.append(item)
+			elif current_batch[-1].get("is_return") != item.get("is_return"):
+				grouped_batch.append(current_batch)
+				current_batch = [item]
+			else:
+				current_batch.append(item)
+
+		grouped_batch.append(current_batch)
+		return grouped_batch
+
 
 def update_item_wise_tax_detail(consolidate_tax_row, tax_row):
 	consolidated_tax_detail = json.loads(consolidate_tax_row.item_wise_tax_detail)
@@ -385,13 +397,14 @@
 		for d in invoices
 		if d.is_return and d.return_against
 	]
+
 	for pos_invoice in pos_return_docs:
 		for item in pos_invoice.items:
 			if not item.serial_no and not item.serial_and_batch_bundle:
 				continue
 
 			return_against_is_added = any(
-				d for d in _invoices if d.pos_invoice == pos_invoice.return_against
+				d for d in invoices if d.pos_invoice == pos_invoice.return_against
 			)
 			if return_against_is_added:
 				break
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 43bd7ac..1f90c5b 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -3,7 +3,7 @@
 
 import collections
 import csv
-from collections import defaultdict
+from collections import Counter, defaultdict
 from typing import Dict, List
 
 import frappe
@@ -1197,6 +1197,7 @@
 		filters=[
 			["POS Invoice", "consolidated_invoice", "is", "not set"],
 			["POS Invoice", "docstatus", "=", 1],
+			["POS Invoice", "is_return", "=", 0],
 			["POS Invoice Item", "item_code", "=", kwargs.item_code],
 			["POS Invoice", "name", "!=", kwargs.ignore_voucher_no],
 		],
@@ -1214,7 +1215,6 @@
 	for d in get_serial_batch_ledgers(kwargs.item_code, docstatus=1, name=ids):
 		ignore_serial_nos.append(d.serial_no)
 
-	# Will be deprecated in v16
 	returned_serial_nos = []
 	for pos_invoice in pos_invoices:
 		if pos_invoice.serial_no:
@@ -1242,8 +1242,13 @@
 				child_doc, parent_doc, ignore_voucher_detail_no=kwargs.get("ignore_voucher_detail_no")
 			)
 		)
+	# Counter is used to create a hashmap of serial nos, which contains count of each serial no
+	# so we subtract returned serial nos from ignore serial nos after creating a counter of each to get the items which we need 	to ignore(which are sold)
 
-	return list(set(ignore_serial_nos) - set(returned_serial_nos))
+	ignore_serial_nos_counter = Counter(ignore_serial_nos)
+	returned_serial_nos_counter = Counter(returned_serial_nos)
+
+	return list(ignore_serial_nos_counter - returned_serial_nos_counter)
 
 
 def get_reserved_batches_for_pos(kwargs):