Merge pull request #21699 from nextchamp-saqib/pur-inv-status-fix

fix: purchase inv shows overdue for fraction of outstanding
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index cf4e158..aa1d5b5 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -1020,6 +1020,40 @@
 
 		# calculate totals again after applying TDS
 		self.calculate_taxes_and_totals()
+	
+	def set_status(self, update=False, status=None, update_modified=True):
+		if self.is_new():
+			if self.get('amended_from'):
+				self.status = 'Draft'
+			return
+
+		precision = self.precision("outstanding_amount")
+		outstanding_amount = flt(self.outstanding_amount, precision)
+		due_date = getdate(self.due_date)
+		nowdate = getdate()
+
+		if not status:
+			if self.docstatus == 2:
+				status = "Cancelled"
+			elif self.docstatus == 1:
+				if outstanding_amount > 0 and due_date < nowdate:
+					self.status = "Overdue"
+				elif outstanding_amount > 0 and due_date >= nowdate:
+					self.status = "Unpaid"
+				#Check if outstanding amount is 0 due to debit note issued against invoice
+				elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
+					self.status = "Debit Note Issued"
+				elif self.is_return == 1:
+					self.status = "Return"
+				elif outstanding_amount<=0:
+					self.status = "Paid"
+				else:
+					self.status = "Submitted"
+			else:
+				self.status = "Draft"
+
+		if update:
+			self.db_set('status', self.status, update_modified = update_modified)
 
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index e41ad42..6170005 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -86,6 +86,8 @@
 		pe.submit()
 
 		pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
+		pi_doc.load_from_db()
+		self.assertTrue(pi_doc.status, "Paid")
 
 		self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
 		unlink_payment_on_cancel_of_invoice()
@@ -203,7 +205,9 @@
 
 		pi.insert()
 		pi.submit()
+		pi.load_from_db()
 
+		self.assertTrue(pi.status, "Unpaid")
 		self.check_gle_for_pi(pi.name)
 
 	def check_gle_for_pi(self, pi):
@@ -234,6 +238,9 @@
 
 		pi = frappe.copy_doc(test_records[0])
 		pi.insert()
+		pi.load_from_db()
+
+		self.assertTrue(pi.status, "Draft")
 		pi.naming_series = 'TEST-'
 
 		self.assertRaises(frappe.CannotChangeConstantError, pi.save)
@@ -248,6 +255,8 @@
 		pi.get("taxes").pop(1)
 		pi.insert()
 		pi.submit()
+		pi.load_from_db()
+		self.assertTrue(pi.status, "Unpaid")
 
 		gl_entries = frappe.db.sql("""select account, debit, credit
 			from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
@@ -599,6 +608,11 @@
 		# return entry
 		pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
 
+		pi.load_from_db()
+		self.assertTrue(pi.status, "Debit Note Issued")
+		pi1.load_from_db()
+		self.assertTrue(pi1.status, "Return")
+
 		actual_qty_2 = get_qty_after_transaction()
 		self.assertEqual(actual_qty_1 - 2, actual_qty_2)
 
@@ -771,6 +785,8 @@
 		from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
 
 		pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
+		pi.load_from_db()
+		self.assertTrue(pi.status, "Return")
 
 		outstanding_amount = get_outstanding_amount(pi.doctype,
 			pi.name, "Creditors - _TC", pi.supplier, "Supplier")
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index de76e45..b465a10 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -69,17 +69,6 @@
 		["Cancelled", "eval:self.docstatus==2"],
 		["Closed", "eval:self.status=='Closed'"],
 	],
-	"Purchase Invoice": [
-		["Draft", None],
-		["Submitted", "eval:self.docstatus==1"],
-		["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
-		["Return", "eval:self.is_return==1 and self.docstatus==1"],
-		["Debit Note Issued",
-			"eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
-		["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
-		["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
-		["Cancelled", "eval:self.docstatus==2"],
-	],
 	"Material Request": [
 		["Draft", None],
 		["Stopped", "eval:self.status == 'Stopped'"],