From eea4756ce9109a48da8e31b2e6e0d8852c913fa5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 2 Jul 2026 04:57:11 +0000 Subject: [PATCH] fix: validate reverse GL entries on current date under immutable ledger (backport #56709) (#56752) fix: validate reverse GL entries on current date under immutable ledger (#56709) * fix: validate reverse GL entries on current date under immutable ledger When Immutable Ledger is enabled, the reverse GL entry is posted on the current date, but the closed-period checks in make_reverse_gl_entries still validate against the original (backdated) posting date. This blocks cancelling a backdated voucher, such as a suspense Journal Entry for a migrated NPA loan, with a books-closed error even though the reverse entry lands in an open period. Validate both check_freezing_date and validate_against_pcv against the current date when Immutable Ledger is enabled. When it is disabled, behaviour is unchanged. Follow-up to #55268. * test: reset frozen till date after reverse entry test The freeze date set on the company was not reset, so it leaked into the next test which posts entries in that period. Reset it in a finally block. * fix: prefer explicit posting_date under immutable ledger Prefer the posting_date argument before frappe.form_dict and getdate, at both the validation and the GL entry site, so an explicit date passed by the caller is honoured and validation still matches the posted date. (cherry picked from commit cab1b129c064bdbef8e01763f0f1201495d0d233) Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com> --- .../test_period_closing_voucher.py | 15 +++++++++------ erpnext/accounts/general_ledger.py | 12 +++++++----- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index cf226d4e5c7..8ab96447544 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -352,12 +352,15 @@ class TestPeriodClosingVoucher(ERPNextTestSuite): self.make_period_closing_voucher(posting_date="2021-03-31") - # Passed posting_date is after PCV end date, so cancellation should not fail. - make_reverse_gl_entries( - voucher_type="Journal Entry", - voucher_no=jv.name, - posting_date="2022-01-01", - ) + frappe.db.set_value("Company", "Test PCV Company", "accounts_frozen_till_date", "2021-12-31") + + try: + make_reverse_gl_entries( + voucher_type="Journal Entry", + voucher_no=jv.name, + ) + finally: + frappe.db.set_value("Company", "Test PCV Company", "accounts_frozen_till_date", None) totals_after_cancel = frappe.db.sql( """ diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 1619f93d456..a4018f50f61 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -716,13 +716,15 @@ def make_reverse_gl_entries( partial_cancel=partial_cancel, ) validate_accounting_period(gl_entries) - check_freezing_date(gl_entries[0]["posting_date"], gl_entries[0]["company"], adv_adj) is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries) - # For reverse entries, use the posting_date parameter if provided and valid - # Otherwise fall back to original posting_date - validation_date = posting_date if posting_date else gl_entries[0]["posting_date"] + if immutable_ledger_enabled: + validation_date = posting_date or frappe.form_dict.get("posting_date") or getdate() + else: + validation_date = posting_date if posting_date else gl_entries[0]["posting_date"] + + check_freezing_date(validation_date, gl_entries[0]["company"], adv_adj) validate_against_pcv(is_opening, validation_date, gl_entries[0]["company"]) if partial_cancel: @@ -789,7 +791,7 @@ def make_reverse_gl_entries( if immutable_ledger_enabled: new_gle["is_cancelled"] = 0 - new_gle["posting_date"] = frappe.form_dict.get("posting_date") or getdate() + new_gle["posting_date"] = posting_date or frappe.form_dict.get("posting_date") or getdate() elif posting_date: new_gle["posting_date"] = posting_date