96 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			96 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import frappe
 | |
| from frappe.utils import flt, getdate
 | |
| 
 | |
| from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
 | |
| 	get_total_in_party_account_currency,
 | |
| 	is_overdue,
 | |
| )
 | |
| 
 | |
| TODAY = getdate()
 | |
| 
 | |
| 
 | |
| def execute():
 | |
| 	# This fix is not related to Party Specific Item,
 | |
| 	# but it is needed for code introduced after Party Specific Item was
 | |
| 	# If your DB doesn't have this doctype yet, you should be fine
 | |
| 	if not frappe.db.exists("DocType", "Party Specific Item"):
 | |
| 		return
 | |
| 
 | |
| 	for doctype in ("Purchase Invoice", "Sales Invoice"):
 | |
| 		fields = [
 | |
| 			"name",
 | |
| 			"status",
 | |
| 			"due_date",
 | |
| 			"outstanding_amount",
 | |
| 			"grand_total",
 | |
| 			"base_grand_total",
 | |
| 			"rounded_total",
 | |
| 			"base_rounded_total",
 | |
| 			"disable_rounded_total",
 | |
| 		]
 | |
| 		if doctype == "Sales Invoice":
 | |
| 			fields.append("is_pos")
 | |
| 
 | |
| 		invoices_to_update = frappe.get_all(
 | |
| 			doctype,
 | |
| 			fields=fields,
 | |
| 			filters={
 | |
| 				"docstatus": 1,
 | |
| 				"status": (
 | |
| 					"in",
 | |
| 					("Overdue", "Overdue and Discounted", "Partly Paid", "Partly Paid and Discounted"),
 | |
| 				),
 | |
| 				"outstanding_amount": (">", 0),
 | |
| 				"modified": (">", "2021-01-01")
 | |
| 				# an assumption is being made that only invoices modified
 | |
| 				# after 2021 got affected as incorrectly overdue.
 | |
| 				# required for performance reasons.
 | |
| 			},
 | |
| 		)
 | |
| 
 | |
| 		invoices_to_update = {invoice.name: invoice for invoice in invoices_to_update}
 | |
| 
 | |
| 		payment_schedule_items = frappe.get_all(
 | |
| 			"Payment Schedule",
 | |
| 			fields=("due_date", "payment_amount", "base_payment_amount", "parent"),
 | |
| 			filters={"parent": ("in", invoices_to_update)},
 | |
| 		)
 | |
| 
 | |
| 		for item in payment_schedule_items:
 | |
| 			invoices_to_update[item.parent].setdefault("payment_schedule", []).append(item)
 | |
| 
 | |
| 		status_map = {}
 | |
| 
 | |
| 		for invoice in invoices_to_update.values():
 | |
| 			invoice.doctype = doctype
 | |
| 			doc = frappe.get_doc(invoice)
 | |
| 			correct_status = get_correct_status(doc)
 | |
| 			if not correct_status or doc.status == correct_status:
 | |
| 				continue
 | |
| 
 | |
| 			status_map.setdefault(correct_status, []).append(doc.name)
 | |
| 
 | |
| 		for status, docs in status_map.items():
 | |
| 			frappe.db.set_value(doctype, {"name": ("in", docs)}, "status", status, update_modified=False)
 | |
| 
 | |
| 
 | |
| def get_correct_status(doc):
 | |
| 	outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
 | |
| 	total = get_total_in_party_account_currency(doc)
 | |
| 
 | |
| 	status = ""
 | |
| 	if is_overdue(doc, total):
 | |
| 		status = "Overdue"
 | |
| 	elif 0 < outstanding_amount < total:
 | |
| 		status = "Partly Paid"
 | |
| 	elif outstanding_amount > 0 and getdate(doc.due_date) >= TODAY:
 | |
| 		status = "Unpaid"
 | |
| 
 | |
| 	if not status:
 | |
| 		return
 | |
| 
 | |
| 	if doc.status.endswith(" and Discounted"):
 | |
| 		status += " and Discounted"
 | |
| 
 | |
| 	return status
 |