Compare commits

...

1 Commits

Author SHA1 Message Date
Sagar Vora
eed35acf81 feat: add Partly Paid status in Invoices (#27625)
(cherry picked from commit c8b9a55e96)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.json
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
#	erpnext/controllers/accounts_controller.py
2025-02-21 00:15:58 +00:00
6 changed files with 140 additions and 0 deletions

View File

@@ -1174,6 +1174,7 @@
"oldfieldtype": "Section Break",
"options": "fa fa-file-text",
"print_hide": 1
<<<<<<< HEAD
},
{
"default": "0",
@@ -1183,6 +1184,8 @@
"ignore_user_permissions": 1,
"label": "Is Internal Supplier",
"read_only": 1
=======
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
},
{
"fieldname": "credit_to",
@@ -1310,6 +1313,17 @@
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
<<<<<<< HEAD
=======
},
{
"default": "0",
"fetch_from": "supplier.is_internal_supplier",
"fieldname": "is_internal_supplier",
"fieldtype": "Check",
"label": "Is Internal Supplier",
"read_only": 1
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
},
{
"fieldname": "tax_withholding_category",
@@ -1399,6 +1413,7 @@
"hidden": 1,
"label": "Ignore Default Payment Terms Template",
"read_only": 1
<<<<<<< HEAD
},
{
"collapsible": 1,
@@ -1431,13 +1446,19 @@
"no_copy": 1,
"options": "Tax Withheld Vouchers",
"read_only": 1
=======
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
}
],
"icon": "fa fa-file-text",
"idx": 204,
"is_submittable": 1,
"links": [],
<<<<<<< HEAD
"modified": "2022-10-07 14:19:14.214157",
=======
"modified": "2021-09-21 09:27:39.967811",
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -13,7 +13,10 @@ from erpnext.accounts.deferred_revenue import validate_service_stop_date
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
check_if_return_invoice_linked_with_payment_entry,
<<<<<<< HEAD
get_total_in_party_account_currency,
=======
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
is_overdue,
unlink_inter_company_doc,
update_linked_doc,
@@ -1567,17 +1570,27 @@ class PurchaseInvoice(BuyingController):
return
outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
<<<<<<< HEAD
total = get_total_in_party_account_currency(self)
=======
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
if not status:
if self.docstatus == 2:
status = "Cancelled"
elif self.docstatus == 1:
if self.is_internal_transfer():
<<<<<<< HEAD
self.status = "Internal Transfer"
elif is_overdue(self, total):
self.status = "Overdue"
elif 0 < outstanding_amount < total:
=======
self.status = 'Internal Transfer'
elif is_overdue(self):
self.status = "Overdue"
elif 0 < outstanding_amount < flt(self.grand_total, self.precision("grand_total")):
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
self.status = "Partly Paid"
elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
self.status = "Unpaid"

View File

@@ -2047,7 +2047,11 @@
"link_fieldname": "consolidated_invoice"
}
],
<<<<<<< HEAD
"modified": "2023-01-28 19:45:47.538163",
=======
"modified": "2021-09-21 09:27:50.191854",
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -1685,21 +1685,32 @@ class SalesInvoice(SellingController):
return
outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
<<<<<<< HEAD
total = get_total_in_party_account_currency(self)
=======
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
if not status:
if self.docstatus == 2:
status = "Cancelled"
elif self.docstatus == 1:
if self.is_internal_transfer():
<<<<<<< HEAD
self.status = "Internal Transfer"
elif is_overdue(self, total):
self.status = "Overdue"
elif 0 < outstanding_amount < total:
=======
self.status = 'Internal Transfer'
elif is_overdue(self):
self.status = "Overdue"
elif 0 < outstanding_amount < flt(self.grand_total, self.precision("grand_total")):
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
self.status = "Partly Paid"
elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
self.status = "Unpaid"
# Check if outstanding amount is 0 due to credit note issued against invoice
<<<<<<< HEAD
elif (
outstanding_amount <= 0
and self.is_return == 0
@@ -1707,6 +1718,9 @@ class SalesInvoice(SellingController):
"Sales Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
)
):
=======
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
self.status = "Credit Note Issued"
elif self.is_return == 1:
self.status = "Return"
@@ -1760,6 +1774,28 @@ def is_overdue(doc, total):
return (total - outstanding_amount) < payable_amount
def is_overdue(doc):
outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
if outstanding_amount <= 0:
return
grand_total = flt(doc.grand_total, doc.precision("grand_total"))
nowdate = getdate()
if doc.payment_schedule:
# calculate payable amount till date
payable_amount = sum(
payment.payment_amount
for payment in doc.payment_schedule
if getdate(payment.due_date) < nowdate
)
if (grand_total - outstanding_amount) < payable_amount:
return True
elif getdate(doc.due_date) < nowdate:
return True
def get_discounting_status(sales_invoice):
status = None

View File

@@ -2216,6 +2216,11 @@ class TestSalesInvoice(unittest.TestCase):
def test_credit_note(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
<<<<<<< HEAD
=======
si = create_sales_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
si = create_sales_invoice(item_code="_Test Item", qty=(5 * -1), rate=500, is_return=1)
@@ -3568,6 +3573,54 @@ class TestSalesInvoice(unittest.TestCase):
self.assertTrue(return_si.docstatus == 1)
def test_payment_statuses(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
today = nowdate()
# Test Overdue
si = create_sales_invoice(do_not_submit=True)
si.payment_schedule = []
si.append("payment_schedule", {
"due_date": add_days(today, -5),
"invoice_portion": 50,
"payment_amount": si.grand_total / 2
})
si.append("payment_schedule", {
"due_date": add_days(today, 5),
"invoice_portion": 50,
"payment_amount": si.grand_total / 2
})
si.submit()
self.assertEqual(si.status, "Overdue")
# Test payment less than due amount
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
pe.reference_no = "1"
pe.reference_date = nowdate()
pe.paid_amount = 1
pe.references[0].allocated_amount = pe.paid_amount
pe.submit()
si.reload()
self.assertEqual(si.status, "Overdue")
# Test Partly Paid
pe = frappe.copy_doc(pe)
pe.paid_amount = si.grand_total / 2
pe.references[0].allocated_amount = pe.paid_amount
pe.submit()
si.reload()
self.assertEqual(si.status, "Partly Paid")
# Test Paid
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
pe.reference_no = "1"
pe.reference_date = nowdate()
pe.paid_amount = si.outstanding_amount
pe.submit()
si.reload()
self.assertEqual(si.status, "Paid")
def get_sales_invoice_for_e_invoice():
si = make_sales_invoice_for_ewaybill()
si.naming_series = "INV-2020-.#####"

View File

@@ -2209,6 +2209,7 @@ def get_advance_payment_entries(
def update_invoice_status():
"""Updates status as Overdue for applicable invoices. Runs daily."""
<<<<<<< HEAD
today = getdate()
payment_schedule = frappe.qb.DocType("Payment Schedule")
for doctype in ("Sales Invoice", "Purchase Invoice"):
@@ -2261,7 +2262,19 @@ def update_invoice_status():
)
frappe.qb.update(invoice).set("status", status).where(conditions).run()
=======
>>>>>>> c8b9a55e96 (feat: add `Partly Paid` status in Invoices (#27625))
for doctype in ("Sales Invoice", "Purchase Invoice"):
frappe.db.sql("""
update `tab{}` as dt set dt.status = 'Overdue'
where dt.docstatus = 1
and dt.status != 'Overdue'
and dt.outstanding_amount > 0
and (dt.grand_total - dt.outstanding_amount) <
(select sum(payment_amount) from `tabPayment Schedule` as ps
where ps.parent = dt.name and ps.due_date < %s)
""".format(doctype), getdate())
@frappe.whitelist()
def get_payment_terms(