Merge pull request #56144 from nabinhait/refactor-si-status

refactor(sales_invoice): simplify StatusService.set_status, cover set_indicator
This commit is contained in:
Nabin Hait
2026-06-19 16:17:11 +05:30
committed by GitHub
2 changed files with 57 additions and 29 deletions

View File

@@ -21,45 +21,52 @@ class StatusService:
doc.status = "Draft"
return
outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
total = get_total_in_party_account_currency(doc)
if not status:
if doc.docstatus == 2:
status = "Cancelled"
elif doc.docstatus == 1:
if doc.is_internal_transfer():
doc.status = "Internal Transfer"
elif is_overdue(doc, total):
doc.status = "Overdue"
elif 0 < outstanding_amount < total:
doc.status = "Partly Paid"
elif outstanding_amount > 0 and getdate(doc.due_date) >= getdate():
doc.status = "Unpaid"
elif doc.is_return == 0 and frappe.db.get_value(
"Sales Invoice", {"is_return": 1, "return_against": doc.name, "docstatus": 1}
):
doc.status = "Credit Note Issued"
elif doc.is_return == 1:
doc.status = "Return"
elif outstanding_amount <= 0:
doc.status = "Paid"
else:
doc.status = "Submitted"
if (
doc.status in ("Unpaid", "Partly Paid", "Overdue")
and doc.is_discounted
and get_discounting_status(doc.name) == "Disbursed"
):
doc.status += " and Discounted"
doc.status = self._get_submitted_status()
else:
doc.status = "Draft"
if update:
doc.db_set("status", doc.status, update_modified=update_modified)
def _get_submitted_status(self) -> str:
"""Status of a submitted invoice, with the invoice-discounting suffix applied."""
doc = self.doc
outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
total = get_total_in_party_account_currency(doc)
status = self._get_payment_status(outstanding_amount, total)
if (
status in ("Unpaid", "Partly Paid", "Overdue")
and doc.is_discounted
and get_discounting_status(doc.name) == "Disbursed"
):
status += " and Discounted"
return status
def _get_payment_status(self, outstanding_amount: float, total: float) -> str:
doc = self.doc
if doc.is_internal_transfer():
return "Internal Transfer"
if is_overdue(doc, total):
return "Overdue"
if 0 < outstanding_amount < total:
return "Partly Paid"
if outstanding_amount > 0 and getdate(doc.due_date) >= getdate():
return "Unpaid"
if doc.is_return == 0 and frappe.db.get_value(
"Sales Invoice", {"is_return": 1, "return_against": doc.name, "docstatus": 1}
):
return "Credit Note Issued"
if doc.is_return == 1:
return "Return"
if outstanding_amount <= 0:
return "Paid"
return "Submitted"
def set_indicator(self) -> None:
doc = self.doc
if doc.outstanding_amount < 0:

View File

@@ -3880,6 +3880,27 @@ class TestSalesInvoice(ERPNextTestSuite):
party_link.delete()
def test_status_indicator(self):
from erpnext.accounts.doctype.sales_invoice.services.status import StatusService
si = create_sales_invoice(do_not_save=True)
cases = [
# outstanding, due_date, is_return -> indicator color, title
(-50, nowdate(), 0, "gray", "Credit Note Issued"),
(100, add_days(nowdate(), 5), 0, "orange", "Unpaid"),
(100, add_days(nowdate(), -5), 0, "red", "Overdue"),
(0, nowdate(), 1, "gray", "Return"),
(0, nowdate(), 0, "green", "Paid"),
]
for outstanding, due_date, is_return, color, title in cases:
with self.subTest(title=title):
si.outstanding_amount = outstanding
si.due_date = due_date
si.is_return = is_return
StatusService(si).set_indicator()
self.assertEqual(si.indicator_color, color)
self.assertEqual(si.indicator_title, title)
def test_payment_statuses(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry