From 9fff01db328a2ba98acd2345aa1c8a83f40f147b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 12:43:14 +0000 Subject: [PATCH] fix(accounts): round journal entry totals to field precision (backport #58629) (#58875) Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com> --- .../doctype/journal_entry/journal_entry.js | 4 +- .../doctype/journal_entry/journal_entry.py | 12 +++-- .../journal_entry/test_journal_entry.py | 53 +++++++++++++++++++ 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index f5b59b0bae6..2c506f6ce84 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -472,8 +472,8 @@ cur_frm.cscript.update_totals = function (doc) { tc += flt(accounts[i].credit, precision("credit", accounts[i])); } doc = locals[doc.doctype][doc.name]; - doc.total_debit = td; - doc.total_credit = tc; + doc.total_debit = flt(td, precision("total_debit")); + doc.total_credit = flt(tc, precision("total_credit")); doc.difference = flt(td - tc, precision("difference")); refresh_many(["total_debit", "total_credit", "difference"]); }; diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 8cba593f7d4..b57d92d19f1 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -960,12 +960,14 @@ class JournalEntry(AccountsController): if d.debit and d.credit: frappe.throw(_("You cannot credit and debit same account at the same time")) - self.total_debit = flt(self.total_debit) + flt(d.debit, d.precision("debit")) - self.total_credit = flt(self.total_credit) + flt(d.credit, d.precision("credit")) + self.total_debit = flt( + self.total_debit + flt(d.debit, d.precision("debit")), self.precision("total_debit") + ) + self.total_credit = flt( + self.total_credit + flt(d.credit, d.precision("credit")), self.precision("total_credit") + ) - self.difference = flt(self.total_debit, self.precision("total_debit")) - flt( - self.total_credit, self.precision("total_credit") - ) + self.difference = flt(self.total_debit - self.total_credit, self.precision("difference")) def validate_multi_currency(self): alternate_currency = [] diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 7b9efbd7f55..4c1b70aff78 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -409,6 +409,59 @@ class TestJournalEntry(ERPNextTestSuite): self.check_gl_entries() + def make_jv_with_fractional_totals(self): + """0.10 + 0.20 sums to 0.30000000000000004, the residue this guards against.""" + jv = frappe.new_doc("Journal Entry") + jv.posting_date = nowdate() + jv.company = "_Test Company" + jv.voucher_type = "Journal Entry" + jv.remark = "test" + for amount in (0.10, 0.20): + jv.append( + "accounts", + { + "account": "_Test Cash - _TC", + "cost_center": "_Test Cost Center - _TC", + "debit_in_account_currency": amount, + }, + ) + jv.append( + "accounts", + { + "account": "_Test Bank - _TC", + "cost_center": "_Test Cost Center - _TC", + "credit_in_account_currency": 0.30, + }, + ) + jv.insert() + return jv + + def test_totals_are_rounded_to_precision(self): + jv = self.make_jv_with_fractional_totals() + jv.submit() + + stored = frappe.db.get_value( + "Journal Entry", jv.name, ["total_debit", "total_credit", "difference"], as_dict=True + ) + self.assertEqual(jv.total_debit, flt(jv.total_debit, jv.precision("total_debit"))) + self.assertEqual(jv.total_credit, flt(jv.total_credit, jv.precision("total_credit"))) + self.assertEqual(jv.total_debit, stored.total_debit) + self.assertEqual(jv.total_credit, stored.total_credit) + self.assertEqual(jv.difference, stored.difference) + + def test_update_after_submit_with_fractional_totals(self): + """An unrounded total is stored rounded, so updating a submitted entry used to throw.""" + jv = self.make_jv_with_fractional_totals() + jv.submit() + + jv.pay_to_recd_from = "_Test Supplier" + jv.save() + + self.assertEqual(jv.docstatus, 1) + self.assertEqual( + jv.pay_to_recd_from, frappe.db.get_value("Journal Entry", jv.name, "pay_to_recd_from") + ) + def test_jv_account_and_party_balance_with_cost_centre(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.utils import get_balance_on