From 947ed5dfe19a0082cdcc0a387ddf45834afcc894 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 9 Jul 2026 14:14:44 +0530 Subject: [PATCH] fix: match depreciation schedule rows at currency precision to avoid duplicate JEs --- .../journal_entry/services/asset_service.py | 3 +- erpnext/assets/doctype/asset/test_asset.py | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/journal_entry/services/asset_service.py b/erpnext/accounts/doctype/journal_entry/services/asset_service.py index 9e76c05f168..c0b954233af 100644 --- a/erpnext/accounts/doctype/journal_entry/services/asset_service.py +++ b/erpnext/accounts/doctype/journal_entry/services/asset_service.py @@ -94,11 +94,12 @@ class AssetService: def update_journal_entry_link_on_depr_schedule(self, asset, je_row) -> None: """Stamp this entry onto the matching (date + amount) depreciation schedule row.""" depr_schedule = get_depr_schedule(asset.name, "Active", self.doc.finance_book) + precision = je_row.precision("debit") for d in depr_schedule or []: if ( d.schedule_date == self.doc.posting_date and not d.journal_entry - and d.depreciation_amount == flt(je_row.debit) + and flt(d.depreciation_amount, precision) == flt(je_row.debit, precision) ): frappe.db.set_value("Depreciation Schedule", d.name, "journal_entry", self.doc.name) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 6e67ffba7fa..fa454c45c5f 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1478,6 +1478,46 @@ class TestDepreciationBasics(AssetSetup): self.assertFalse(depr_schedule[1].journal_entry) self.assertFalse(depr_schedule[2].journal_entry) + def test_depr_schedule_link_matches_at_currency_precision(self): + """A Depreciation Schedule row whose amount carries more decimals than the + company currency (e.g. 25701.202 vs a JE debit of 25701.20) must still be + matched and stamped with the Journal Entry. Comparing at exact float + equality left the link NULL, so the scheduler treated the row as unposted + and created a duplicate Journal Entry on every run. Regression test for + AssetService.update_journal_entry_link_on_depr_schedule().""" + from unittest.mock import MagicMock, patch + + from erpnext.accounts.doctype.journal_entry.services import asset_service as asset_service_module + from erpnext.accounts.doctype.journal_entry.services.asset_service import AssetService + + posting_date = getdate("2021-06-01") + je = frappe._dict(name="JE-DEPR-TEST", finance_book=None, posting_date=posting_date) + service = AssetService(je) + + # JE debit is stored at company currency precision (2 dp)... + je_row = MagicMock() + je_row.debit = 25701.20 + je_row.precision.return_value = 2 + + # ...while the schedule row amount carries a third decimal. + schedule_row = frappe._dict( + name="DS-ROW-1", + schedule_date=posting_date, + journal_entry=None, + depreciation_amount=25701.202, + ) + asset = frappe._dict(name="ASSET-TEST") + + with ( + patch.object(asset_service_module, "get_depr_schedule", return_value=[schedule_row]), + patch.object(frappe.db, "set_value") as mock_set_value, + ): + service.update_journal_entry_link_on_depr_schedule(asset, je_row) + + mock_set_value.assert_called_once_with( + "Depreciation Schedule", "DS-ROW-1", "journal_entry", "JE-DEPR-TEST" + ) + def test_depr_entry_posting_when_depr_expense_account_is_an_expense_account(self): """Tests if the Depreciation Expense Account gets debited and the Accumulated Depreciation Account gets credited when the former's an Expense Account."""