fix(pos): cannot close the pos if sr. no. is sold & returned
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 d4513c6..d8f893c 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
@@ -53,7 +53,7 @@
 					frappe.throw(msg)
 
 	def on_submit(self):
-		pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
+		pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
 
 		returns = [d for d in pos_invoice_docs if d.get('is_return') == 1]
 		sales = [d for d in pos_invoice_docs if d.get('is_return') == 0]
@@ -70,7 +70,7 @@
 		self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
 
 	def on_cancel(self):
-		pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
+		pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
 
 		self.update_pos_invoices(pos_invoice_docs)
 		self.cancel_linked_invoices()
@@ -254,7 +254,7 @@
 		'docstatus': 1
 	}
 	pos_invoices = frappe.db.get_all('POS Invoice', filters=filters,
-		fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer'])
+		fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer', 'is_return', 'return_against'])
 
 	return pos_invoices
 
@@ -294,17 +294,59 @@
 	else:
 		cancel_merge_logs(merge_logs, closing_entry)
 
+def split_invoices(invoices):
+	'''
+	Splits invoices into multiple groups
+	Use-case:
+	If a serial no is sold and later it is returned
+	then split the invoices such that the selling entry is merged first and then the return entry
+	'''
+	# Input
+	# invoices = [
+	# 	{'pos_invoice': 'Invoice with SR#1 & SR#2', 'is_return': 0},
+	# 	{'pos_invoice': 'Invoice with SR#1', 'is_return': 1},
+	# 	{'pos_invoice': 'Invoice with SR#2', 'is_return': 0}
+	# ]
+	# Output
+	# _invoices = [
+	# 	[{'pos_invoice': 'Invoice with SR#1 & SR#2', 'is_return': 0}],
+	# 	[{'pos_invoice': 'Invoice with SR#1', 'is_return': 1}, {'pos_invoice': 'Invoice with SR#2', 'is_return': 0}],
+	# ]
+
+	_invoices = []
+	special_invoices = []
+	pos_return_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) 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: continue
+
+			return_against_is_added = any(d for d in _invoices if d.pos_invoice == pos_invoice.return_against)
+			if return_against_is_added: break
+
+			return_against_is_consolidated = frappe.db.get_value('POS Invoice', pos_invoice.return_against, 'status', cache=True) == 'Consolidated'
+			if return_against_is_consolidated: break
+
+			pos_invoice_row = [d for d in invoices if d.pos_invoice == pos_invoice.return_against]
+			_invoices.append(pos_invoice_row)
+			special_invoices.append(pos_invoice.return_against)
+			break
+
+	_invoices.append([d for d in invoices if d.pos_invoice not in special_invoices])
+
+	return _invoices
+
 def create_merge_logs(invoice_by_customer, closing_entry=None):
 	try:
 		for customer, invoices in invoice_by_customer.items():
-			merge_log = frappe.new_doc('POS Invoice Merge Log')
-			merge_log.posting_date = getdate(closing_entry.get('posting_date')) if closing_entry else nowdate()
-			merge_log.customer = customer
-			merge_log.pos_closing_entry = closing_entry.get('name') if closing_entry else None
+			for _invoices in split_invoices(invoices):
+				merge_log = frappe.new_doc('POS Invoice Merge Log')
+				merge_log.posting_date = getdate(closing_entry.get('posting_date')) if closing_entry else nowdate()
+				merge_log.customer = customer
+				merge_log.pos_closing_entry = closing_entry.get('name') if closing_entry else None
 
-			merge_log.set('pos_invoices', invoices)
-			merge_log.save(ignore_permissions=True)
-			merge_log.submit()
+				merge_log.set('pos_invoices', _invoices)
+				merge_log.save(ignore_permissions=True)
+				merge_log.submit()
 
 		if closing_entry:
 			closing_entry.set_status(update=True, status='Submitted')
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
index 8909da9..655d726 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
@@ -385,4 +385,66 @@
 		finally:
 			frappe.set_user("Administrator")
 			frappe.db.sql("delete from `tabPOS Profile`")
+			frappe.db.sql("delete from `tabPOS Invoice`")
+
+	def test_serial_no_case_1(self):
+		'''
+		Create a POS Invoice with serial no
+		Create a Return Invoice with serial no
+		Create a POS Invoice with serial no again
+		Consolidate the invoices
+
+		The first POS Invoice should be consolidated with a separate single Merge Log
+		The second and third POS Invoice should be consolidated with a single Merge Log
+		'''
+
+		from erpnext.stock.doctype.serial_no.test_serial_no import get_serial_nos
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+
+		frappe.db.sql("delete from `tabPOS Invoice`")
+
+		try:
+			se = make_serialized_item()
+			serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
+
+			init_user_and_profile()
+
+			pos_inv = create_pos_invoice(
+					item_code="_Test Serialized Item With Series",
+					serial_no=serial_no,
+					qty=1,
+					rate=100,
+					do_not_submit=1
+			)
+			pos_inv.append('payments', {
+				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 100
+			})
+			pos_inv.submit()
+
+			pos_inv_cn = make_sales_return(pos_inv.name)
+			pos_inv_cn.paid_amount = -100
+			pos_inv_cn.submit()
+
+			pos_inv2 = create_pos_invoice(
+					item_code="_Test Serialized Item With Series",
+					serial_no=serial_no,
+					qty=1,
+					rate=100,
+					do_not_submit=1
+			)
+			pos_inv2.append('payments', {
+				'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 100
+			})
+			pos_inv.submit()
+
+			consolidate_pos_invoices()
+
+			pos_inv.load_from_db()
+			pos_inv2.load_from_db()
+
+			self.assertNotEqual(pos_inv.consolidated_invoice, pos_inv2.consolidated_invoice)
+
+		finally:
+			frappe.set_user("Administrator")
+			frappe.db.sql("delete from `tabPOS Profile`")
 			frappe.db.sql("delete from `tabPOS Invoice`")
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json b/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json
index 205c4ed..387c4b0 100644
--- a/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json
+++ b/erpnext/accounts/doctype/pos_invoice_reference/pos_invoice_reference.json
@@ -9,7 +9,9 @@
   "posting_date",
   "column_break_3",
   "customer",
-  "grand_total"
+  "grand_total",
+  "is_return",
+  "return_against"
  ],
  "fields": [
   {
@@ -48,11 +50,27 @@
    "in_list_view": 1,
    "label": "Amount",
    "reqd": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "pos_invoice.is_return",
+   "fieldname": "is_return",
+   "fieldtype": "Check",
+   "label": "Is Return",
+   "read_only": 1
+  },
+  {
+   "fetch_from": "pos_invoice.return_against",
+   "fieldname": "return_against",
+   "fieldtype": "Link",
+   "label": "Return Against",
+   "options": "POS Invoice",
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-05-29 15:08:42.194979",
+ "modified": "2022-03-24 13:32:02.366257",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice Reference",
@@ -61,5 +79,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file