mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-25 07:54:46 +00:00
refactor: split_invoices_based_on_payment_terms
- Invoices were in the wrong order due to the logic. The invoices with payment terms were added first and the rest after. - Overly long function with unnecessary loops (reduced to one main loop) and complexity - The split row as per payment terms was not ordered. So the second installment was allocated first
This commit is contained in:
@@ -1698,13 +1698,42 @@ def get_outstanding_reference_documents(args, validate=False):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def split_invoices_based_on_payment_terms(outstanding_invoices, company):
|
def split_invoices_based_on_payment_terms(outstanding_invoices, company) -> list:
|
||||||
invoice_ref_based_on_payment_terms = {}
|
"""Split a list of invoices based on their payment terms."""
|
||||||
|
exc_rates = get_currency_data(outstanding_invoices, company)
|
||||||
|
|
||||||
|
outstanding_invoices_after_split = []
|
||||||
|
for entry in outstanding_invoices:
|
||||||
|
if entry.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
|
if payment_term_template := frappe.db.get_value(
|
||||||
|
entry.voucher_type, entry.voucher_no, "payment_terms_template"
|
||||||
|
):
|
||||||
|
split_rows = get_split_invoice_rows(entry, payment_term_template, exc_rates)
|
||||||
|
if not split_rows:
|
||||||
|
continue
|
||||||
|
|
||||||
|
frappe.msgprint(
|
||||||
|
_("Spliting {} {} into {} row(s) as per Payment Terms").format(
|
||||||
|
split_rows[0]["voucher_type"], split_rows[0]["voucher_no"], len(split_rows)
|
||||||
|
),
|
||||||
|
alert=True,
|
||||||
|
)
|
||||||
|
outstanding_invoices_after_split += split_rows
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If not an invoice or no payment terms template, add as it is
|
||||||
|
outstanding_invoices_after_split.append(entry)
|
||||||
|
|
||||||
|
return outstanding_invoices_after_split
|
||||||
|
|
||||||
|
|
||||||
|
def get_currency_data(outstanding_invoices: list, company: str = None) -> dict:
|
||||||
|
"""Get currency and conversion data for a list of invoices."""
|
||||||
|
exc_rates = frappe._dict()
|
||||||
company_currency = (
|
company_currency = (
|
||||||
frappe.db.get_value("Company", company, "default_currency") if company else None
|
frappe.db.get_value("Company", company, "default_currency") if company else None
|
||||||
)
|
)
|
||||||
exc_rates = frappe._dict()
|
|
||||||
for doctype in ["Sales Invoice", "Purchase Invoice"]:
|
for doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype]
|
invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype]
|
||||||
for x in frappe.db.get_all(
|
for x in frappe.db.get_all(
|
||||||
@@ -1719,72 +1748,54 @@ def split_invoices_based_on_payment_terms(outstanding_invoices, company):
|
|||||||
company_currency=company_currency,
|
company_currency=company_currency,
|
||||||
)
|
)
|
||||||
|
|
||||||
for idx, d in enumerate(outstanding_invoices):
|
return exc_rates
|
||||||
if d.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
|
|
||||||
payment_term_template = frappe.db.get_value(
|
|
||||||
d.voucher_type, d.voucher_no, "payment_terms_template"
|
def get_split_invoice_rows(invoice: dict, payment_term_template: str, exc_rates: dict) -> list:
|
||||||
|
"""Split invoice based on its payment schedule table."""
|
||||||
|
split_rows = []
|
||||||
|
allocate_payment_based_on_payment_terms = frappe.db.get_value(
|
||||||
|
"Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not allocate_payment_based_on_payment_terms:
|
||||||
|
return [invoice]
|
||||||
|
|
||||||
|
payment_schedule = frappe.get_all(
|
||||||
|
"Payment Schedule", filters={"parent": invoice.voucher_no}, fields=["*"], order_by="due_date"
|
||||||
|
)
|
||||||
|
for payment_term in payment_schedule:
|
||||||
|
if not payment_term.outstanding > 0.1:
|
||||||
|
continue
|
||||||
|
|
||||||
|
doc_details = exc_rates.get(payment_term.parent, None)
|
||||||
|
is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and (
|
||||||
|
doc_details.party_account_currency != doc_details.company_currency
|
||||||
|
)
|
||||||
|
payment_term_outstanding = flt(payment_term.outstanding)
|
||||||
|
if not is_multi_currency_acc:
|
||||||
|
payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding)
|
||||||
|
|
||||||
|
split_rows.append(
|
||||||
|
frappe._dict(
|
||||||
|
{
|
||||||
|
"due_date": invoice.due_date,
|
||||||
|
"currency": invoice.currency,
|
||||||
|
"voucher_no": invoice.voucher_no,
|
||||||
|
"voucher_type": invoice.voucher_type,
|
||||||
|
"posting_date": invoice.posting_date,
|
||||||
|
"invoice_amount": flt(invoice.invoice_amount),
|
||||||
|
"outstanding_amount": payment_term_outstanding
|
||||||
|
if payment_term_outstanding
|
||||||
|
else invoice.outstanding_amount,
|
||||||
|
"payment_term_outstanding": payment_term_outstanding,
|
||||||
|
"payment_amount": payment_term.payment_amount,
|
||||||
|
"payment_term": payment_term.payment_term,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
if payment_term_template:
|
)
|
||||||
allocate_payment_based_on_payment_terms = frappe.get_cached_value(
|
|
||||||
"Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms"
|
|
||||||
)
|
|
||||||
if allocate_payment_based_on_payment_terms:
|
|
||||||
payment_schedule = frappe.get_all(
|
|
||||||
"Payment Schedule", filters={"parent": d.voucher_no}, fields=["*"]
|
|
||||||
)
|
|
||||||
|
|
||||||
for payment_term in payment_schedule:
|
return split_rows
|
||||||
if payment_term.outstanding > 0.1:
|
|
||||||
doc_details = exc_rates.get(payment_term.parent, None)
|
|
||||||
is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and (
|
|
||||||
doc_details.party_account_currency != doc_details.company_currency
|
|
||||||
)
|
|
||||||
payment_term_outstanding = flt(payment_term.outstanding)
|
|
||||||
if not is_multi_currency_acc:
|
|
||||||
payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding)
|
|
||||||
|
|
||||||
invoice_ref_based_on_payment_terms.setdefault(idx, [])
|
|
||||||
invoice_ref_based_on_payment_terms[idx].append(
|
|
||||||
frappe._dict(
|
|
||||||
{
|
|
||||||
"due_date": d.due_date,
|
|
||||||
"currency": d.currency,
|
|
||||||
"voucher_no": d.voucher_no,
|
|
||||||
"voucher_type": d.voucher_type,
|
|
||||||
"posting_date": d.posting_date,
|
|
||||||
"invoice_amount": flt(d.invoice_amount),
|
|
||||||
"outstanding_amount": payment_term_outstanding
|
|
||||||
if payment_term_outstanding
|
|
||||||
else d.outstanding_amount,
|
|
||||||
"payment_term_outstanding": payment_term_outstanding,
|
|
||||||
"payment_amount": payment_term.payment_amount,
|
|
||||||
"payment_term": payment_term.payment_term,
|
|
||||||
"account": d.account,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
outstanding_invoices_after_split = []
|
|
||||||
if invoice_ref_based_on_payment_terms:
|
|
||||||
for idx, ref in invoice_ref_based_on_payment_terms.items():
|
|
||||||
voucher_no = ref[0]["voucher_no"]
|
|
||||||
voucher_type = ref[0]["voucher_type"]
|
|
||||||
|
|
||||||
frappe.msgprint(
|
|
||||||
_("Spliting {} {} into {} row(s) as per Payment Terms").format(
|
|
||||||
voucher_type, voucher_no, len(ref)
|
|
||||||
),
|
|
||||||
alert=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
outstanding_invoices_after_split += invoice_ref_based_on_payment_terms[idx]
|
|
||||||
|
|
||||||
existing_row = list(filter(lambda x: x.get("voucher_no") == voucher_no, outstanding_invoices))
|
|
||||||
index = outstanding_invoices.index(existing_row[0])
|
|
||||||
outstanding_invoices.pop(index)
|
|
||||||
|
|
||||||
outstanding_invoices_after_split += outstanding_invoices
|
|
||||||
return outstanding_invoices_after_split
|
|
||||||
|
|
||||||
|
|
||||||
def get_orders_to_be_billed(
|
def get_orders_to_be_billed(
|
||||||
|
|||||||
Reference in New Issue
Block a user