chore: Tests for Stock Reconciliation

(cherry picked from commit 5bc5af1066e0bb4bead98598ed4bf9911d340734)
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 60b0f38..09f6b7b 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -654,14 +654,14 @@
 
 	def test_serial_batch_item_stock_entry(self):
 		"""
-			Behaviour: 1) Submit Stock Entry (Receipt) with Serial & Batched Item
-				2) Cancel same Stock Entry
-			Expected Result: 1) Batch is created with Reference in Serial No
-				2) Batch is deleted and Serial No is Inactive
+		Behaviour: 1) Submit Stock Entry (Receipt) with Serial & Batched Item
+		        2) Cancel same Stock Entry
+		Expected Result: 1) Batch is created with Reference in Serial No
+		        2) Batch is deleted and Serial No is Inactive
 		"""
 		from erpnext.stock.doctype.batch.batch import get_batch_qty
 
-		item = frappe.db.exists("Item", {'item_name': 'Batched and Serialised Item'})
+		item = frappe.db.exists("Item", {"item_name": "Batched and Serialised Item"})
 		if not item:
 			item = create_item("Batched and Serialised Item")
 			item.has_batch_no = 1
@@ -671,9 +671,11 @@
 			item.serial_no_series = "S-.####"
 			item.save()
 		else:
-			item = frappe.get_doc("Item", {'item_name': 'Batched and Serialised Item'})
+			item = frappe.get_doc("Item", {"item_name": "Batched and Serialised Item"})
 
-		se = make_stock_entry(item_code=item.item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100)
+		se = make_stock_entry(
+			item_code=item.item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100
+		)
 		batch_no = se.items[0].batch_no
 		serial_no = get_serial_nos(se.items[0].serial_no)[0]
 		batch_qty = get_batch_qty(batch_no, "_Test Warehouse - _TC", item.item_code)
@@ -693,15 +695,15 @@
 
 	def test_serial_batch_item_qty_deduction(self):
 		"""
-			Behaviour: Create 2 Stock Entries, both adding Serial Nos to same batch
-			Expected Result: 1) Cancelling first Stock Entry (origin transaction of created batch)
-				should throw a Link Exists Error
-				2) Cancelling second Stock Entry should make Serial Nos that are, linked to mentioned batch
-				and in that transaction only, Inactive.
+		Behaviour: Create 2 Stock Entries, both adding Serial Nos to same batch
+		Expected Result: 1) Cancelling first Stock Entry (origin transaction of created batch)
+		        should throw a LinkExistsError
+		        2) Cancelling second Stock Entry should make Serial Nos that are, linked to mentioned batch
+		        and in that transaction only, Inactive.
 		"""
 		from erpnext.stock.doctype.batch.batch import get_batch_qty
 
-		item = frappe.db.exists("Item", {'item_name': 'Batched and Serialised Item'})
+		item = frappe.db.exists("Item", {"item_name": "Batched and Serialised Item"})
 		if not item:
 			item = create_item("Batched and Serialised Item")
 			item.has_batch_no = 1
@@ -711,24 +713,31 @@
 			item.serial_no_series = "S-.####"
 			item.save()
 		else:
-			item = frappe.get_doc("Item", {'item_name': 'Batched and Serialised Item'})
+			item = frappe.get_doc("Item", {"item_name": "Batched and Serialised Item"})
 
-		se1 = make_stock_entry(item_code=item.item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100)
+		se1 = make_stock_entry(
+			item_code=item.item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100
+		)
 		batch_no = se1.items[0].batch_no
 		serial_no1 = get_serial_nos(se1.items[0].serial_no)[0]
 
 		# Check Source (Origin) Document of Batch
 		self.assertEqual(frappe.db.get_value("Batch", batch_no, "reference_name"), se1.name)
 
-		se2 = make_stock_entry(item_code=item.item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100,
-			batch_no=batch_no)
+		se2 = make_stock_entry(
+			item_code=item.item_code,
+			target="_Test Warehouse - _TC",
+			qty=1,
+			basic_rate=100,
+			batch_no=batch_no,
+		)
 		serial_no2 = get_serial_nos(se2.items[0].serial_no)[0]
 
 		batch_qty = get_batch_qty(batch_no, "_Test Warehouse - _TC", item.item_code)
 		self.assertEqual(batch_qty, 2)
 		frappe.db.commit()
 
-		# Cancelling Origin Document
+		# Cancelling Origin Document of Batch
 		self.assertRaises(frappe.LinkExistsError, se1.cancel)
 		frappe.db.rollback()
 
@@ -742,7 +751,7 @@
 		self.assertEqual(frappe.db.get_value("Serial No", serial_no1, "batch_no"), batch_no)
 		self.assertEqual(frappe.db.get_value("Serial No", serial_no1, "status"), "Active")
 
-		# Check id Serial No from Stock Entry 2 is Unlinked and Inactive
+		# Check if Serial No from Stock Entry 2 is Unlinked and Inactive
 		self.assertEqual(frappe.db.get_value("Serial No", serial_no2, "batch_no"), None)
 		self.assertEqual(frappe.db.get_value("Serial No", serial_no2, "status"), "Inactive")
 
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 1e59aae..02acdb3 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -250,7 +250,7 @@
 		warehouse = "_Test Warehouse for Stock Reco2 - _TC"
 
 		sr = create_stock_reconciliation(
-			item_code=item_code, warehouse=warehouse, qty=5, rate=200, do_not_submit=1
+			item_code=item_code, warehouse=warehouse, qty=5, rate=200, do_not_save=1
 		)
 		sr.save()
 		sr.submit()
@@ -288,6 +288,107 @@
 			stock_doc = frappe.get_doc("Stock Reconciliation", d)
 			stock_doc.cancel()
 
+	def test_stock_reco_for_serial_and_batch_item(self):
+		item = frappe.db.exists("Item", {"item_name": "Batched and Serialised Item"})
+		if not item:
+			item = create_item("Batched and Serialised Item")
+			item.has_batch_no = 1
+			item.create_new_batch = 1
+			item.has_serial_no = 1
+			item.batch_number_series = "B-BATCH-.##"
+			item.serial_no_series = "S-.####"
+			item.save()
+		else:
+			item = frappe.get_doc("Item", {"item_name": "Batched and Serialised Item"})
+
+		warehouse = "_Test Warehouse for Stock Reco2 - _TC"
+
+		sr = create_stock_reconciliation(item_code=item.item_code, warehouse=warehouse, qty=1, rate=100)
+
+		batch_no = sr.items[0].batch_no
+
+		serial_nos = get_serial_nos(sr.items[0].serial_no)
+		self.assertEqual(len(serial_nos), 1)
+		self.assertEqual(frappe.db.get_value("Serial No", serial_nos[0], "batch_no"), batch_no)
+
+		sr.cancel()
+
+		self.assertEqual(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Inactive")
+		self.assertEqual(frappe.db.exists("Batch", batch_no), None)
+
+		if frappe.db.exists("Serial No", serial_nos[0]):
+			frappe.delete_doc("Serial No", serial_nos[0])
+
+	def test_stock_reco_for_serial_and_batch_item_with_future_dependent_entry(self):
+		"""
+		Behaviour: 1) Create Stock Reconciliation, which will be the origin document
+		        of a new batch having a serial no
+		        2) Create a Stock Entry that adds a serial no to the same batch following this
+		                Stock Reconciliation
+		        3) Cancel Stock Reconciliation
+		        4) Cancel Stock Entry
+		Expected Result: 3) Cancelling the Stock Reco throws a LinkExistsError since
+		        Stock Entry is dependent on the batch involved
+		        4) Serial No only in the Stock Entry is Inactive and Batch qty decreases
+		"""
+		from erpnext.stock.doctype.batch.batch import get_batch_qty
+		from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+
+		item = frappe.db.exists("Item", {"item_name": "Batched and Serialised Item"})
+		if not item:
+			item = create_item("Batched and Serialised Item")
+			item.has_batch_no = 1
+			item.create_new_batch = 1
+			item.has_serial_no = 1
+			item.batch_number_series = "B-BATCH-.##"
+			item.serial_no_series = "S-.####"
+			item.save()
+		else:
+			item = frappe.get_doc("Item", {"item_name": "Batched and Serialised Item"})
+
+		warehouse = "_Test Warehouse for Stock Reco2 - _TC"
+
+		stock_reco = create_stock_reconciliation(
+			item_code=item.item_code, warehouse=warehouse, qty=1, rate=100
+		)
+		batch_no = stock_reco.items[0].batch_no
+		serial_no = get_serial_nos(stock_reco.items[0].serial_no)[0]
+
+		stock_entry = make_stock_entry(
+			item_code=item.item_code, target=warehouse, qty=1, basic_rate=100, batch_no=batch_no
+		)
+		serial_no_2 = get_serial_nos(stock_entry.items[0].serial_no)[0]
+
+		# Check Batch qty after 2 transactions
+		batch_qty = get_batch_qty(batch_no, warehouse, item.item_code)
+		self.assertEqual(batch_qty, 2)
+		frappe.db.commit()
+
+		# Cancelling Origin Document of Batch
+		self.assertRaises(frappe.LinkExistsError, stock_reco.cancel)
+		frappe.db.rollback()
+
+		stock_entry.cancel()
+
+		# Check Batch qty after cancellation
+		batch_qty = get_batch_qty(batch_no, warehouse, item.item_code)
+		self.assertEqual(batch_qty, 1)
+
+		# Check if Serial No from Stock Reconcilation is intact
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no, "batch_no"), batch_no)
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Active")
+
+		# Check if Serial No from Stock Entry is Unlinked and Inactive
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no_2, "batch_no"), None)
+		self.assertEqual(frappe.db.get_value("Serial No", serial_no_2, "status"), "Inactive")
+
+		stock_reco.load_from_db()
+		stock_reco.cancel()
+
+		for sn in (serial_no, serial_no_2):
+			if frappe.db.exists("Serial No", sn):
+				frappe.delete_doc("Serial No", sn)
+
 	def test_customer_provided_items(self):
 		item_code = "Stock-Reco-customer-Item-100"
 		create_item(
@@ -684,11 +785,13 @@
 		},
 	)
 
-	try:
-		if not args.do_not_submit:
-			sr.submit()
-	except EmptyStockReconciliationItemsError:
-		pass
+	if not args.do_not_save:
+		sr.insert()
+		try:
+			if not args.do_not_submit:
+				sr.submit()
+		except EmptyStockReconciliationItemsError:
+			pass
 	return sr