diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 47ba392802e..7c396c94eed 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -65,7 +65,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 8acd5cf8587..bccf718f4a6 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -7,6 +7,7 @@ import json import frappe from frappe import _, msgprint, scrub from frappe.core.doctype.submission_queue.submission_queue import queue_submission +from frappe.model.document import Document from frappe.utils import comma_and, cstr, flt, fmt_money, formatdate, get_link_to_form, getdate, nowdate import erpnext @@ -1892,7 +1893,21 @@ def make_inter_company_journal_entry(name, voucher_type, company): @frappe.whitelist() -def make_reverse_journal_entry(source_name, target_doc=None): +def make_reverse_journal_entry(source_name: str, target_doc: str | dict | Document | None = None) -> Document: + # `get_mapped_doc` checks this as well, but the guard below discloses which entry + # reverses which, so read access has to be settled before it runs + 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), + ) + ) + from frappe.model.mapper import get_mapped_doc def post_process(source, target):