brotherton-erpnext/erpnext/patches/v13_0/fix_invoice_statuses.py
2022-03-28 18:52:46 +05:30

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