fix: Validation for invalid serial nos at POS invoice level (#29447)

diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 134bccf..f66abdc 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -158,6 +158,20 @@
 			frappe.throw(_("Row #{}: Serial No. {} has already been transacted into another Sales Invoice. Please select valid serial no.")
 						.format(item.idx, bold_delivered_serial_nos), title=_("Item Unavailable"))
 
+	def validate_invalid_serial_nos(self, item):
+		serial_nos = get_serial_nos(item.serial_no)
+		error_msg = []
+		invalid_serials, msg = "", ""
+		for serial_no in serial_nos:
+			if not frappe.db.exists('Serial No', serial_no):
+				invalid_serials = invalid_serials + (", " if invalid_serials else "") + serial_no
+		msg = (_("Row #{}: Following Serial numbers for item {} are <b>Invalid</b>: {}").format(item.idx, frappe.bold(item.get("item_code")), frappe.bold(invalid_serials)))
+		if invalid_serials:
+			error_msg.append(msg)
+
+		if error_msg:
+			frappe.throw(error_msg, title=_("Invalid Item"), as_list=True)
+
 	def validate_stock_availablility(self):
 		if self.is_return or self.docstatus != 1:
 			return
@@ -167,6 +181,7 @@
 			if d.serial_no:
 				self.validate_pos_reserved_serial_nos(d)
 				self.validate_delivered_serial_nos(d)
+				self.validate_invalid_serial_nos(d)
 			elif d.batch_no:
 				self.validate_pos_reserved_batch_qty(d)
 			else:
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 56479a0..ba751c0 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -354,6 +354,24 @@
 		pos2.insert()
 		self.assertRaises(frappe.ValidationError, pos2.submit)
 
+	def test_invalid_serial_no_validation(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
+
+		se = make_serialized_item(company='_Test Company',
+			target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
+		serial_nos = se.get("items")[0].serial_no + 'wrong'
+
+		pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
+			account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
+			expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
+			item=se.get("items")[0].item_code, rate=1000, qty=2, do_not_save=1)
+
+		pos.get('items')[0].has_serial_no = 1
+		pos.get('items')[0].serial_no = serial_nos
+		pos.insert()
+
+		self.assertRaises(frappe.ValidationError, pos.submit)
+
 	def test_loyalty_points(self):
 		from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
 			get_loyalty_program_details_with_points,