diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index a4f096af595..3e5a3071c8f 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -95,7 +95,7 @@ frappe.ui.form.on("Journal Entry", { ); } - if (frm.doc.docstatus == 1) { + if (frm.doc.docstatus == 1 && !frm.doc.reversal_of) { frm.add_custom_button( __("Reverse Journal Entry"), function () { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 331ed50dea3..90baa886ef5 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -1777,6 +1777,20 @@ def make_inter_company_journal_entry(name, voucher_type, company): @frappe.whitelist() def make_reverse_journal_entry(source_name, target_doc=None): + # `get_mapped_doc` checks this as well, but the guards below disclose which entry + # reverses which, so read access has to be settled before they run + if not frappe.has_permission("Journal Entry", doc=source_name): + frappe.throw(_("Not permitted"), frappe.PermissionError) + + reversal_of = frappe.db.get_value("Journal Entry", source_name, "reversal_of") + if reversal_of: + frappe.throw( + _("{0} is already a Reverse Journal Entry of {1}. Cancel it instead of reversing it.").format( + get_link_to_form("Journal Entry", source_name), + get_link_to_form("Journal Entry", reversal_of), + ) + ) + existing_reverse = frappe.db.exists("Journal Entry", {"reversal_of": source_name, "docstatus": 1}) if existing_reverse: frappe.throw( diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index a34b440a228..7b9efbd7f55 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -249,7 +249,7 @@ class TestJournalEntry(ERPNextTestSuite): self.check_gl_entries() def test_disallow_reversal_of_a_reversal_journal_entry(self): - from erpnext.accounts.doctype.journal_entry.mapper import make_reverse_journal_entry + from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry jv = make_journal_entry("_Test Bank - _TC", "Sales - _TC", 100, submit=True)