From 16a23d9f0f69ce532ea406dee2b8421b9803c456 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 30 Sep 2021 17:37:35 +0200 Subject: [PATCH] refactor: dunning --- erpnext/accounts/doctype/dunning/dunning.js | 35 ++++---- erpnext/accounts/doctype/dunning/dunning.py | 80 +++++++++---------- .../doctype/payment_entry/payment_entry.py | 36 +++------ 3 files changed, 65 insertions(+), 86 deletions(-) diff --git a/erpnext/accounts/doctype/dunning/dunning.js b/erpnext/accounts/doctype/dunning/dunning.js index 98462b89db..5cee711950 100644 --- a/erpnext/accounts/doctype/dunning/dunning.js +++ b/erpnext/accounts/doctype/dunning/dunning.js @@ -56,19 +56,6 @@ frappe.ui.form.on("Dunning", { frm.page.set_inner_btn_group_as_primary(__("Create")); } - if (frm.doc.docstatus > 0) { - frm.add_custom_button(__("Ledger"), function () { - frappe.route_options = { - "voucher_no": frm.doc.name, - "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, - "company": frm.doc.company, - "show_cancelled_entries": frm.doc.docstatus === 2 - }; - frappe.set_route("query-report", "General Ledger"); - }, __("View")); - } - if (frm.doc.docstatus === 0) { frm.add_custom_button(__("Fetch Overdue Payments"), function () { erpnext.utils.map_current_doc({ @@ -248,22 +235,29 @@ frappe.ui.form.on("Dunning", { calculate_interest: function (frm) { frm.doc.overdue_payments.forEach((row) => { const interest_per_day = frm.doc.rate_of_interest / 100 / 365; - const interest = flt((interest_per_day * row.outstanding * cint(row.overdue_days)) / 365 || 0, precision("interest")); + const interest = flt((interest_per_day * row.overdue_days * row.outstanding), precision("interest")); frappe.model.set_value(row.doctype, row.name, "interest", interest); }); }, calculate_totals: function (frm) { + debugger; const total_interest = frm.doc.overdue_payments .reduce((prev, cur) => prev + cur.interest, 0); const total_outstanding = frm.doc.overdue_payments .reduce((prev, cur) => prev + cur.outstanding, 0); - const dunning_amount = flt(total_interest + frm.doc.dunning_fee, precision("dunning_amount")); - const grand_total = flt(total_outstanding + dunning_amount, precision("grand_total")); + const dunning_amount = total_interest + frm.doc.dunning_fee; + const base_dunning_amount = dunning_amount * frm.doc.conversion_rate; + const grand_total = total_outstanding + dunning_amount; - frm.set_value("total_outstanding", total_outstanding); - frm.set_value("total_interest", total_interest); - frm.set_value("dunning_amount", dunning_amount); - frm.set_value("grand_total", grand_total); + function setWithPrecison(field, value) { + frm.set_value(field, flt(value, precision(field))); + } + + setWithPrecison("total_outstanding", total_outstanding); + setWithPrecison("total_interest", total_interest); + setWithPrecison("dunning_amount", dunning_amount); + setWithPrecison("base_dunning_amount", base_dunning_amount); + setWithPrecison("grand_total", grand_total); }, make_payment_entry: function (frm) { return frappe.call({ @@ -283,6 +277,7 @@ frappe.ui.form.on("Dunning", { frappe.ui.form.on("Overdue Payment", { interest: function (frm, cdt, cdn) { + debugger; frm.trigger("calculate_totals"); } }); \ No newline at end of file diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py index 5194090743..ec116f3061 100644 --- a/erpnext/accounts/doctype/dunning/dunning.py +++ b/erpnext/accounts/doctype/dunning/dunning.py @@ -1,24 +1,44 @@ # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +""" +# Accounting +1. Payment of outstanding invoices with dunning amount + + - Debit full amount to bank + - Credit invoiced amount to receivables + - Credit dunning amount to interest and similar revenue + + -> Resolves dunning automatically +""" +from __future__ import unicode_literals import json import frappe + +from frappe import _ from frappe.utils import getdate -from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries from erpnext.controllers.accounts_controller import AccountsController class Dunning(AccountsController): def validate(self): + self.validate_same_currency() self.validate_overdue_payments() self.validate_totals() + self.set_dunning_level() - if not self.income_account: - self.income_account = frappe.db.get_value("Company", self.company, "default_income_account") + def validate_same_currency(self): + """ + Throw an error if invoice currency differs from dunning currency. + """ + for row in self.overdue_payments: + invoice_currency = frappe.get_value("Sales Invoice", row.sales_invoice, "currency") + if invoice_currency != self.currency: + frappe.throw(_("The currency of invoice {} ({}) is different from the currency of this dunning ({}).").format(row.sales_invoice, invoice_currency, self.currency)) def validate_overdue_payments(self): daily_interest = self.rate_of_interest / 100 / 365 @@ -31,51 +51,25 @@ class Dunning(AccountsController): self.total_outstanding = sum(row.outstanding for row in self.overdue_payments) self.total_interest = sum(row.interest for row in self.overdue_payments) self.dunning_amount = self.total_interest + self.dunning_fee + self.base_dunning_amount = self.dunning_amount * self.conversion_rate self.grand_total = self.total_outstanding + self.dunning_amount - def on_submit(self): - self.make_gl_entries() - - def on_cancel(self): - if self.dunning_amount: - self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry") - make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) - - def make_gl_entries(self): - if not self.dunning_amount: - return - - cost_center = self.cost_center or frappe.get_cached_value("Company", self.company, "cost_center") - - make_gl_entries( - [ - self.get_gl_dict({ - "account": self.debit_to, - "party_type": "Customer", - "party": self.customer, - "due_date": self.due_date, - "against": self.income_account, - "debit": self.dunning_amount, - "debit_in_account_currency": self.dunning_amount, - "against_voucher": self.name, - "against_voucher_type": "Dunning", - "cost_center": cost_center - }), - self.get_gl_dict({ - "account": self.income_account, - "against": self.customer, - "credit": self.dunning_amount, - "cost_center": cost_center, - "credit_in_account_currency": self.dunning_amount - }) - ], - cancel=(self.docstatus == 2), - update_outstanding="No", - merge_entries=False - ) + def set_dunning_level(self): + for row in self.overdue_payments: + past_dunnings = frappe.get_all("Overdue Payment", + filters={ + "payment_schedule": row.payment_schedule, + "parent": ("!=", row.parent), + "docstatus": 1 + } + ) + row.dunning_level = len(past_dunnings) + 1 def resolve_dunning(doc, state): + """ + Todo: refactor + """ for reference in doc.references: if reference.reference_doctype == "Sales Invoice" and reference.outstanding_amount <= 0: dunnings = frappe.get_list( diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index b6d3e5a30e..397e998f0b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1849,30 +1849,20 @@ def get_payment_entry( pe.append("references", reference) else: if dt == "Dunning": - pe.append( - "references", - { + for overdue_payment in doc.overdue_payments: + pe.append("references", { "reference_doctype": "Sales Invoice", - "reference_name": doc.get("sales_invoice"), - "bill_no": doc.get("bill_no"), - "due_date": doc.get("due_date"), - "total_amount": doc.get("outstanding_amount"), - "outstanding_amount": doc.get("outstanding_amount"), - "allocated_amount": doc.get("outstanding_amount"), - }, - ) - pe.append( - "references", - { - "reference_doctype": dt, - "reference_name": dn, - "bill_no": doc.get("bill_no"), - "due_date": doc.get("due_date"), - "total_amount": doc.get("dunning_amount"), - "outstanding_amount": doc.get("dunning_amount"), - "allocated_amount": doc.get("dunning_amount"), - }, - ) + "reference_name": overdue_payment.sales_invoice, + "payment_term": overdue_payment.payment_term, + "due_date": overdue_payment.due_date, + "total_amount": overdue_payment.outstanding, + "outstanding_amount": overdue_payment.outstanding, + "allocated_amount": overdue_payment.outstanding + }) + + pe.append("deductions", { + "amount": doc.dunning_amount + }) else: pe.append( "references",