fix: serial no valuation rate (#40221)

diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py
index 2f1270e..ab38c15 100644
--- a/erpnext/stock/deprecated_serial_batch.py
+++ b/erpnext/stock/deprecated_serial_batch.py
@@ -8,9 +8,12 @@
 class DeprecatedSerialNoValuation:
 	@deprecated
 	def calculate_stock_value_from_deprecarated_ledgers(self):
-		serial_nos = list(
-			filter(lambda x: x not in self.serial_no_incoming_rate and x, self.get_serial_nos())
-		)
+		if not frappe.db.get_value(
+			"Stock Ledger Entry", {"serial_no": ("is", "set"), "is_cancelled": 0}, "name"
+		):
+			return
+
+		serial_nos = self.get_serial_nos()
 
 		actual_qty = flt(self.sle.actual_qty)
 
@@ -25,23 +28,12 @@
 	@deprecated
 	def get_incoming_value_for_serial_nos(self, serial_nos):
 		# get rate from serial nos within same company
-		all_serial_nos = frappe.get_all(
-			"Serial No", fields=["purchase_rate", "name", "company"], filters={"name": ("in", serial_nos)}
-		)
-
 		incoming_values = 0.0
-		for d in all_serial_nos:
-			if d.company == self.sle.company:
-				self.serial_no_incoming_rate[d.name] += flt(d.purchase_rate)
-				incoming_values += flt(d.purchase_rate)
-
-		# Get rate for serial nos which has been transferred to other company
-		invalid_serial_nos = [d.name for d in all_serial_nos if d.company != self.sle.company]
-		for serial_no in invalid_serial_nos:
+		for serial_no in serial_nos:
 			table = frappe.qb.DocType("Stock Ledger Entry")
-			incoming_rate = (
+			stock_ledgers = (
 				frappe.qb.from_(table)
-				.select(table.incoming_rate)
+				.select(table.incoming_rate, table.actual_qty, table.stock_value_difference)
 				.where(
 					(
 						(table.serial_no == serial_no)
@@ -51,15 +43,18 @@
 					)
 					& (table.company == self.sle.company)
 					& (table.serial_and_batch_bundle.isnull())
-					& (table.actual_qty > 0)
 					& (table.is_cancelled == 0)
 				)
-				.orderby(table.posting_date, order=Order.desc)
-				.limit(1)
-			).run()
+				.orderby(table.posting_datetime, order=Order.desc)
+			).run(as_dict=1)
 
-			self.serial_no_incoming_rate[serial_no] += flt(incoming_rate[0][0]) if incoming_rate else 0
-			incoming_values += self.serial_no_incoming_rate[serial_no]
+			for sle in stock_ledgers:
+				self.serial_no_incoming_rate[serial_no] += (
+					flt(sle.incoming_rate)
+					if sle.actual_qty > 0
+					else (sle.stock_value_difference / sle.actual_qty) * -1
+				)
+				incoming_values += self.serial_no_incoming_rate[serial_no]
 
 		return incoming_values
 
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 b6e4d6f..33f0dce 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
@@ -2118,7 +2118,7 @@
 
 		make_serial_no(serial_no, item_code)
 
-	if batch_no and frappe.db.exists("Batch", batch_no):
+	if batch_no and not frappe.db.exists("Batch", batch_no):
 		if type_of_transaction != "Inward":
 			frappe.throw(_("Batch No {0} does not exists").format(batch_no))
 
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
index b932c13..826ac03 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
@@ -540,6 +540,110 @@
 
 		self.assertRaises(frappe.exceptions.ValidationError, pr2.save)
 
+	def test_serial_no_valuation_for_legacy_ledgers(self):
+		sn_item = make_item(
+			"Test Serial No Valuation for Legacy Ledgers",
+			properties={"has_serial_no": 1, "serial_no_series": "SNN-TSNVL.-#####"},
+		).name
+
+		serial_nos = []
+		for serial_no in [f"{sn_item}-0001", f"{sn_item}-0002"]:
+			if not frappe.db.exists("Serial No", serial_no):
+				sn_doc = frappe.get_doc(
+					{
+						"doctype": "Serial No",
+						"serial_no": serial_no,
+						"item_code": sn_item,
+					}
+				).insert(ignore_permissions=True)
+				serial_nos.append(serial_no)
+
+		frappe.flags.ignore_serial_batch_bundle_validation = True
+
+		qty_after_transaction = 0.0
+		stock_value = 0.0
+		for row in [{"qty": 2, "rate": 100}, {"qty": -2, "rate": 100}, {"qty": 2, "rate": 200}]:
+			row = frappe._dict(row)
+			qty_after_transaction += row.qty
+			stock_value += row.rate * row.qty
+
+			doc = frappe.get_doc(
+				{
+					"doctype": "Stock Ledger Entry",
+					"posting_date": today(),
+					"posting_time": nowtime(),
+					"incoming_rate": row.rate if row.qty > 0 else 0,
+					"qty_after_transaction": qty_after_transaction,
+					"stock_value_difference": row.rate * row.qty,
+					"stock_value": stock_value,
+					"valuation_rate": row.rate,
+					"actual_qty": row.qty,
+					"item_code": sn_item,
+					"warehouse": "_Test Warehouse - _TC",
+					"serial_no": "\n".join(serial_nos),
+					"company": "_Test Company",
+				}
+			)
+			doc.flags.ignore_permissions = True
+			doc.flags.ignore_mandatory = True
+			doc.flags.ignore_links = True
+			doc.flags.ignore_validate = True
+			doc.submit()
+
+			for sn in serial_nos:
+				sn_doc = frappe.get_doc("Serial No", sn)
+				if row.qty > 0:
+					sn_doc.db_set("warehouse", "_Test Warehouse - _TC")
+				else:
+					sn_doc.db_set("warehouse", "")
+
+		frappe.flags.ignore_serial_batch_bundle_validation = False
+
+		se = make_stock_entry(
+			item_code=sn_item,
+			qty=2,
+			source="_Test Warehouse - _TC",
+			serial_no="\n".join(serial_nos),
+			use_serial_batch_fields=True,
+			do_not_submit=True,
+		)
+
+		se.save()
+		se.submit()
+
+		stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{"voucher_no": se.name, "is_cancelled": 0, "voucher_type": "Stock Entry"},
+			"stock_value_difference",
+		)
+
+		self.assertEqual(flt(stock_value_difference, 2), 400.0 * -1)
+
+		se = make_stock_entry(
+			item_code=sn_item,
+			qty=1,
+			rate=353,
+			target="_Test Warehouse - _TC",
+		)
+
+		serial_no = get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle)[0]
+
+		se = make_stock_entry(
+			item_code=sn_item,
+			qty=1,
+			source="_Test Warehouse - _TC",
+			serial_no=serial_no,
+			use_serial_batch_fields=True,
+		)
+
+		stock_value_difference = frappe.db.get_value(
+			"Stock Ledger Entry",
+			{"voucher_no": se.name, "is_cancelled": 0, "voucher_type": "Stock Entry"},
+			"stock_value_difference",
+		)
+
+		self.assertEqual(flt(stock_value_difference, 2), 353.0 * -1)
+
 
 def get_batch_from_bundle(bundle):
 	from erpnext.stock.serial_batch_bundle import get_batch_nos