fix: not able to issue expired batches
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index e27718a..f9fc5f6 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -36,6 +36,10 @@
 	pass
 
 
+class BatchExpiredError(frappe.ValidationError):
+	pass
+
+
 class StockController(AccountsController):
 	def validate(self):
 		super(StockController, self).validate()
@@ -77,6 +81,10 @@
 	def validate_serialized_batch(self):
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
+		is_material_issue = False
+		if self.doctype == "Stock Entry" and self.purpose == "Material Issue":
+			is_material_issue = True
+
 		for d in self.get("items"):
 			if hasattr(d, "serial_no") and hasattr(d, "batch_no") and d.serial_no and d.batch_no:
 				serial_nos = frappe.get_all(
@@ -93,6 +101,9 @@
 							)
 						)
 
+			if is_material_issue:
+				continue
+
 			if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
 				expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
 
@@ -100,7 +111,8 @@
 					frappe.throw(
 						_("Row #{0}: The batch {1} has already expired.").format(
 							d.idx, get_link_to_form("Batch", d.get("batch_no"))
-						)
+						),
+						BatchExpiredError,
 					)
 
 	def clean_serial_nos(self):
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 3e470d4..271e2e0 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -473,7 +473,13 @@
 				"doctype": "Batch",
 				"batch_id": args.batch_id,
 				"item": args.item_code,
+				"expiry_date": args.expiry_date,
 			}
-		).insert()
+		)
+
+		if args.expiry_date:
+			batch.expiry_date = args.expiry_date
+
+		batch.insert()
 
 	return batch
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index a2f9978..b574b71 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe.permissions import add_user_permission, remove_user_permission
 from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import add_days, flt, nowdate, nowtime
+from frappe.utils import add_days, flt, nowdate, nowtime, today
 
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
 from erpnext.stock.doctype.item.test_item import (
@@ -1589,6 +1589,31 @@
 			self.assertEqual(obj.items[index].basic_rate, 200)
 			self.assertEqual(obj.items[index].basic_amount, 2000)
 
+	def test_batch_expiry(self):
+		from erpnext.controllers.stock_controller import BatchExpiredError
+		from erpnext.stock.doctype.batch.test_batch import make_new_batch
+
+		item_code = "Test Batch Expiry Test Item - 001"
+		item_doc = create_item(item_code=item_code, is_stock_item=1, valuation_rate=10)
+
+		item_doc.has_batch_no = 1
+		item_doc.save()
+
+		batch = make_new_batch(
+			batch_id=frappe.generate_hash("", 5), item_code=item_doc.name, expiry_date=add_days(today(), -1)
+		)
+
+		se = make_stock_entry(
+			item_code=item_code,
+			purpose="Material Receipt",
+			qty=4,
+			to_warehouse="_Test Warehouse - _TC",
+			batch_no=batch.name,
+			do_not_save=True,
+		)
+
+		self.assertRaises(BatchExpiredError, se.save)
+
 
 def make_serialized_item(**args):
 	args = frappe._dict(args)