refactor: dunning

This commit is contained in:
barredterra 2021-09-30 17:37:35 +02:00 committed by marination
parent 603117eb6b
commit 16a23d9f0f
3 changed files with 65 additions and 86 deletions

View File

@ -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");
}
});

View File

@ -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(

View File

@ -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",