Merge pull request #23567 from nextchamp-saqib/restrict-future-posting

chore: do not allow future transactions
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index c8e3330..298e111 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -5,7 +5,7 @@
 import frappe
 import frappe.share
 from frappe import _
-from frappe.utils import cstr, now_datetime, cint, flt, get_time, get_datetime, get_link_to_form
+from frappe.utils import cstr, now_datetime, cint, flt, get_time, get_datetime, get_link_to_form, date_diff, nowdate
 from erpnext.controllers.status_updater import StatusUpdater
 from erpnext.accounts.utils import get_fiscal_year
 
@@ -29,7 +29,27 @@
 			except ValueError:
 				frappe.throw(_('Invalid Posting Time'))
 
+		self.validate_future_posting()
 		self.validate_with_last_transaction_posting_time()
+	
+	def is_stock_transaction(self):
+		if self.doctype not in ["Sales Invoice", "Purchase Invoice", "Stock Entry", "Stock Reconciliation",
+			"Delivery Note", "Purchase Receipt", "Fees"]:
+				return False
+
+		if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
+			if not (self.get("update_stock") or self.get("is_pos")):
+				return False
+
+		return True
+	
+	def validate_future_posting(self):
+		if not self.is_stock_transaction():
+			return
+
+		if getattr(self, 'set_posting_time', None) and date_diff(self.posting_date, nowdate()) > 0:
+			msg = _("Posting future transactions are not allowed due to Immutable Ledger")
+			frappe.throw(msg, title=_("Future Posting Not Allowed"))
 
 	def add_calendar_event(self, opts, force=False):
 		if cstr(self.contact_by) != cstr(self._prev.contact_by) or \
@@ -162,13 +182,8 @@
 
 	def validate_with_last_transaction_posting_time(self):
 
-		if self.doctype not in ["Sales Invoice", "Purchase Invoice", "Stock Entry", "Stock Reconciliation",
-			"Delivery Note", "Purchase Receipt", "Fees"]:
-				return
-
-		if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
-			if not (self.get("update_stock") or self.get("is_pos")):
-				return
+		if not self.is_stock_transaction():
+			return
 
 		for item in self.get('items'):
 			last_transaction_time = frappe.db.sql("""