mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-12 14:11:46 +00:00
Merge pull request #56883 from nabinhait/sherlock/fix-dunning-outstanding-currency
fix: use transaction-currency outstanding on Dunning for foreign-currency invoices
This commit is contained in:
@@ -12,6 +12,7 @@ from erpnext.accounts.doctype.sales_invoice.mapper import (
|
||||
create_dunning as create_dunning_from_sales_invoice,
|
||||
)
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
|
||||
create_sales_invoice,
|
||||
create_sales_invoice_against_cost_center,
|
||||
)
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
@@ -152,6 +153,37 @@ class TestDunning(ERPNextTestSuite):
|
||||
dunning.reload()
|
||||
self.assertEqual(dunning.status, "Unresolved")
|
||||
|
||||
@ERPNextTestSuite.change_settings(
|
||||
"Accounts Settings", {"allow_multi_currency_invoices_against_single_party_account": 1}
|
||||
)
|
||||
def test_dunning_outstanding_uses_transaction_currency(self):
|
||||
"""
|
||||
Regression for #56006: dunning outstanding must be in the invoice transaction
|
||||
currency, not in the party account currency.
|
||||
|
||||
A USD invoice posted against an INR receivable account stores
|
||||
outstanding_amount in INR (party account currency). The overdue payment
|
||||
row on the resulting Dunning must carry the USD amount, not the INR amount.
|
||||
"""
|
||||
si = create_sales_invoice(
|
||||
posting_date=add_days(today(), -10),
|
||||
currency="USD",
|
||||
conversion_rate=50,
|
||||
rate=100,
|
||||
debit_to="Debtors - _TC",
|
||||
)
|
||||
|
||||
# Sanity-check the invoice state before creating the dunning
|
||||
self.assertEqual(si.currency, "USD")
|
||||
self.assertEqual(si.outstanding_amount, 5000.0) # INR (party account currency)
|
||||
self.assertEqual(si.payment_schedule[0].outstanding, 100.0) # USD (transaction currency)
|
||||
|
||||
dunning = create_dunning_from_sales_invoice(si.name)
|
||||
|
||||
self.assertEqual(len(dunning.overdue_payments), 1)
|
||||
# Must reflect 100 USD, not 5000 INR mislabelled as USD
|
||||
self.assertEqual(dunning.overdue_payments[0].outstanding, 100.0)
|
||||
|
||||
def test_dunning_not_affected_by_standalone_credit_note(self):
|
||||
"""
|
||||
Test that dunning is NOT resolved when a credit note has update_outstanding_for_self checked.
|
||||
|
||||
@@ -594,7 +594,15 @@ def create_dunning(
|
||||
if source.payment_schedule and len(source.payment_schedule) == 1:
|
||||
for row in target.overdue_payments:
|
||||
if row.payment_schedule == source.payment_schedule[0].name:
|
||||
row.outstanding = source.get("outstanding_amount")
|
||||
# outstanding_amount is in the party account currency, but the Overdue Payment
|
||||
# row is in the invoice's transaction currency. When they differ, use the
|
||||
# payment schedule's own outstanding — it is kept in transaction currency and
|
||||
# updated as payments are allocated, so it stays correct even when the invoice
|
||||
# and its payments post at different exchange rates (#56006).
|
||||
if source.party_account_currency and source.party_account_currency != source.currency:
|
||||
row.outstanding = source.payment_schedule[0].outstanding
|
||||
else:
|
||||
row.outstanding = source.get("outstanding_amount")
|
||||
|
||||
target.validate()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user