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",