From e825bb2f74455586a7878d2db251ab1e2d201dd4 Mon Sep 17 00:00:00 2001 From: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:26:04 +0530 Subject: [PATCH] fix(accounts): round journal entry totals to field precision (#58629) --- .../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 02176131cca..28e00ec5347 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -624,8 +624,8 @@ Object.assign(erpnext.journal_entry, { total_credit += flt(row.credit, precision("credit", row)); }); - frm.doc.total_debit = total_debit; - frm.doc.total_credit = total_credit; + frm.doc.total_debit = flt(total_debit, precision("total_debit")); + frm.doc.total_credit = flt(total_credit, precision("total_credit")); frm.doc.difference = flt(total_debit - total_credit, precision("difference")); ["total_debit", "total_credit", "difference"].forEach((field) => frm.refresh_field(field)); }, diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 4272a9c315e..f78c78697a3 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -674,12 +674,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 8c58868aff2..51af6e3c34c 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -461,6 +461,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