fix: validate docs in closed accounting period on save (#36157)

fix: validate docs in closed accounting period on save (#36157)

(cherry picked from commit 5985e02574e387ef92a2bf0a9d2d1b49ad57cdd3)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.js b/erpnext/accounts/doctype/accounting_period/accounting_period.js
index e3d805a..f17b6f9 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.js
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.js
@@ -20,5 +20,11 @@
 				}
 			});
 		}
+
+		frm.set_query("document_type", "closed_documents", () => {
+			return {
+				query: "erpnext.controllers.queries.get_doctypes_for_closing",
+			}
+		});
 	}
 });
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py
index 80c9715..d5f37a6 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py
@@ -11,6 +11,10 @@
 	pass
 
 
+class ClosedAccountingPeriod(frappe.ValidationError):
+	pass
+
+
 class AccountingPeriod(Document):
 	def validate(self):
 		self.validate_overlap()
@@ -65,3 +69,42 @@
 					"closed_documents",
 					{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
 				)
+
+
+def validate_accounting_period_on_doc_save(doc, method=None):
+	if doc.doctype == "Bank Clearance":
+		return
+	elif doc.doctype == "Asset":
+		if doc.is_existing_asset:
+			return
+		else:
+			date = doc.available_for_use_date
+	elif doc.doctype == "Asset Repair":
+		date = doc.completion_date
+	else:
+		date = doc.posting_date
+
+	ap = frappe.qb.DocType("Accounting Period")
+	cd = frappe.qb.DocType("Closed Document")
+
+	accounting_period = (
+		frappe.qb.from_(ap)
+		.from_(cd)
+		.select(ap.name)
+		.where(
+			(ap.name == cd.parent)
+			& (ap.company == doc.company)
+			& (cd.closed == 1)
+			& (cd.document_type == doc.doctype)
+			& (date >= ap.start_date)
+			& (date <= ap.end_date)
+		)
+	).run(as_dict=1)
+
+	if accounting_period:
+		frappe.throw(
+			_("You cannot create a {0} within the closed Accounting Period {1}").format(
+				doc.doctype, frappe.bold(accounting_period[0]["name"])
+			),
+			ClosedAccountingPeriod,
+		)
diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
index 85025d1..41d9479 100644
--- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
@@ -6,9 +6,11 @@
 import frappe
 from frappe.utils import add_months, nowdate
 
-from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError
+from erpnext.accounts.doctype.accounting_period.accounting_period import (
+	ClosedAccountingPeriod,
+	OverlapError,
+)
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.accounts.general_ledger import ClosedAccountingPeriod
 
 test_dependencies = ["Item"]
 
@@ -33,9 +35,9 @@
 		ap1.save()
 
 		doc = create_sales_invoice(
-			do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
+			do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
 		)
-		self.assertRaises(ClosedAccountingPeriod, doc.submit)
+		self.assertRaises(ClosedAccountingPeriod, doc.save)
 
 	def tearDown(self):
 		for d in frappe.get_all("Accounting Period"):
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index f1dad87..e9dc5fc 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -13,14 +13,11 @@
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_accounting_dimensions,
 )
+from erpnext.accounts.doctype.accounting_period.accounting_period import ClosedAccountingPeriod
 from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
 from erpnext.accounts.utils import create_payment_ledger_entry
 
 
-class ClosedAccountingPeriod(frappe.ValidationError):
-	pass
-
-
 def make_gl_entries(
 	gl_map,
 	cancel=False,
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 3bb1128..d1dcd6a 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -824,6 +824,15 @@
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
+def get_doctypes_for_closing(doctype, txt, searchfield, start, page_len, filters):
+	doctypes = frappe.get_hooks("period_closing_doctypes")
+	if txt:
+		doctypes = [d for d in doctypes if txt.lower() in d.lower()]
+	return [(d,) for d in set(doctypes)]
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
 def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
 
 	item_doc = frappe.get_cached_doc("Item", filters.get("item_code"))
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 316d943..b21f37c 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -285,10 +285,34 @@
 	"Customer": "erpnext.controllers.queries.customer_query",
 }
 
+period_closing_doctypes = [
+	"Sales Invoice",
+	"Purchase Invoice",
+	"Journal Entry",
+	"Bank Clearance",
+	"Stock Entry",
+	"Dunning",
+	"Invoice Discounting",
+	"Payment Entry",
+	"Period Closing Voucher",
+	"Process Deferred Accounting",
+	"Asset",
+	"Asset Capitalization",
+	"Asset Repair",
+	"Delivery Note",
+	"Landed Cost Voucher",
+	"Purchase Receipt",
+	"Stock Reconciliation",
+	"Subcontracting Receipt",
+]
+
 doc_events = {
 	"*": {
 		"validate": "erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
 	},
+	tuple(period_closing_doctypes): {
+		"validate": "erpnext.accounts.doctype.accounting_period.accounting_period.validate_accounting_period_on_doc_save",
+	},
 	"Stock Entry": {
 		"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
 		"on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty",
@@ -464,15 +488,6 @@
 
 invoice_doctypes = ["Sales Invoice", "Purchase Invoice"]
 
-period_closing_doctypes = [
-	"Sales Invoice",
-	"Purchase Invoice",
-	"Journal Entry",
-	"Bank Clearance",
-	"Asset",
-	"Stock Entry",
-]
-
 bank_reconciliation_doctypes = [
 	"Payment Entry",
 	"Journal Entry",