From d959ca1694d2639caa71c90c433e96868e680c7b Mon Sep 17 00:00:00 2001 From: Sagar Vora <16315650+sagarvora@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:21:32 +0530 Subject: [PATCH] fix: handle dunning status change on all changes to outstanding amount --- erpnext/accounts/doctype/dunning/dunning.py | 44 +++++---------------- erpnext/accounts/utils.py | 4 ++ erpnext/hooks.py | 4 -- 3 files changed, 13 insertions(+), 39 deletions(-) diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py index 9580bbfbf39..f64e957400b 100644 --- a/erpnext/accounts/doctype/dunning/dunning.py +++ b/erpnext/accounts/doctype/dunning/dunning.py @@ -164,43 +164,17 @@ class Dunning(AccountsController): ] -def resolve_dunnings(doc, method=None): - """ - Resolve / unresolve Dunning based on whether all payments have been made. - Called when a Payment Entry / Credit Note is submitted / cancelled. - """ - - match doc.doctype: - case "Payment Entry": - return resolve_dunnings_from_payment_entry(doc) - case "Sales Invoice": - return resolve_dunnings_from_credit_note(doc) - - -def resolve_dunnings_from_payment_entry(doc): - is_submitted = doc.docstatus == 1 - - for reference in doc.references: - if reference.reference_doctype != "Sales Invoice" or not reference.allocated_amount: - continue - - _update_linked_dunnings(reference.reference_name, to_resolve=is_submitted) - - -def resolve_dunnings_from_credit_note(doc): - """ - Check if dunning should be resolved when a credit note is issued against a Sales Invoice. - Only process if update_outstanding_for_self is False (credit note is being applied against the original invoice). - """ - if not doc.is_return or doc.update_outstanding_for_self or not doc.return_against: +def update_linked_dunnings(doc, previous_outstanding_amount): + if ( + doc.doctype != "Sales Invoice" + or doc.is_return + or previous_outstanding_amount == doc.outstanding_amount + ): return - _update_linked_dunnings(doc.return_against, to_resolve=doc.docstatus == 1) - - -def _update_linked_dunnings(sales_invoice: str, to_resolve: bool = True): + to_resolve = doc.outstanding_amount < previous_outstanding_amount state = "Unresolved" if to_resolve else "Resolved" - dunnings = get_linked_dunnings_as_per_state(sales_invoice, state) + dunnings = get_linked_dunnings_as_per_state(doc.name, state) if not dunnings: return @@ -245,7 +219,7 @@ def _update_linked_dunnings(sales_invoice: str, to_resolve: bool = True): if has_outstanding: break - new_status = "Resolved" if (not has_outstanding and to_resolve) else "Unresolved" + new_status = "Resolved" if not has_outstanding else "Unresolved" if dunning.status != new_status: dunning.status = new_status diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9e855229f8b..ab0283b44bd 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1940,6 +1940,8 @@ def create_payment_ledger_entry( def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, party): + from erpnext.accounts.doctype.dunning.dunning import update_linked_dunnings + if not voucher_type or not voucher_no: return @@ -1969,6 +1971,7 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa outstanding = voucher_outstanding[0] ref_doc = frappe.get_lazy_doc(voucher_type, voucher_no) + previous_outstanding_amount = ref_doc.outstanding_amount outstanding_amount = flt( outstanding["outstanding_in_account_currency"], ref_doc.precision("outstanding_amount") ) @@ -1982,6 +1985,7 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa outstanding_amount, ) + update_linked_dunnings(ref_doc, previous_outstanding_amount) ref_doc.set_status(update=True) ref_doc.notify_update() diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b99f685aaeb..2bbd0f2c7da 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -359,11 +359,9 @@ doc_events = { "on_submit": [ "erpnext.regional.create_transaction_log", "erpnext.regional.italy.utils.sales_invoice_on_submit", - "erpnext.accounts.doctype.dunning.dunning.resolve_dunnings", ], "on_cancel": [ "erpnext.regional.italy.utils.sales_invoice_on_cancel", - "erpnext.accounts.doctype.dunning.dunning.resolve_dunnings", ], "on_trash": "erpnext.regional.check_deletion_permission", }, @@ -376,9 +374,7 @@ doc_events = { "Payment Entry": { "on_submit": [ "erpnext.regional.create_transaction_log", - "erpnext.accounts.doctype.dunning.dunning.resolve_dunnings", ], - "on_cancel": ["erpnext.accounts.doctype.dunning.dunning.resolve_dunnings"], "on_trash": "erpnext.regional.check_deletion_permission", }, "Address": {