Merge pull request #22142 from deepeshgarg007/loan_amount_precision

fix: Precision in loan amount calculation
This commit is contained in:
Deepesh Garg 2020-06-07 21:28:23 +05:30 committed by GitHub
commit 63e8391381
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 51 additions and 42 deletions

View File

@ -23,7 +23,7 @@
{
"hidden": 0,
"label": "Reports",
"links": "[\n {\n \"dependencies\": [\n \"Loan Repayment\"\n ],\n \"doctype\": \"Loan Repayment\",\n \"incomplete_dependencies\": [\n \"Loan Repayment\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Repayment and Closure\",\n \"name\": \"Loan Repayment and Closure\",\n \"route\": \"#query-report/Loan Repayment and Closure\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"doctype\": \"Loan Security Pledge\",\n \"incomplete_dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Security Status\",\n \"name\": \"Loan Security Status\",\n \"route\": \"#query-report/Loan Security Status\",\n \"type\": \"report\"\n }\n]"
"links": "[\n {\n \"doctype\": \"Loan Repayment\",\n \"is_query_report\": true,\n \"label\": \"Loan Repayment and Closure\",\n \"name\": \"Loan Repayment and Closure\",\n \"route\": \"#query-report/Loan Repayment and Closure\",\n \"type\": \"report\"\n },\n {\n \"doctype\": \"Loan Security Pledge\",\n \"is_query_report\": true,\n \"label\": \"Loan Security Status\",\n \"name\": \"Loan Security Status\",\n \"route\": \"#query-report/Loan Security Status\",\n \"type\": \"report\"\n }\n]"
}
],
"category": "Modules",
@ -34,11 +34,10 @@
"docstatus": 0,
"doctype": "Desk Page",
"extends_another_page": 0,
"hide_custom": 0,
"idx": 0,
"is_standard": 1,
"label": "Loan",
"modified": "2020-05-28 13:37:42.017709",
"modified": "2020-06-07 19:42:14.947902",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan",

View File

@ -27,6 +27,7 @@ class LoanDisbursement(AccountsController):
def on_cancel(self):
self.make_gl_entries(cancel=1)
self.ignore_linked_doctypes = ['GL Entry']
def set_missing_values(self):
if not self.disbursement_date:

View File

@ -31,6 +31,7 @@ class LoanInterestAccrual(AccountsController):
self.update_is_accrued()
self.make_gl_entries(cancel=1)
self.ignore_linked_doctypes = ['GL Entry']
def update_is_accrued(self):
frappe.db.set_value('Repayment Schedule', self.repayment_schedule_name, 'is_accrued', 0)
@ -176,21 +177,23 @@ def get_term_loans(date, term_loan=None, loan_type=None):
return term_loans
def make_loan_interest_accrual_entry(args):
loan_interest_accrual = frappe.new_doc("Loan Interest Accrual")
loan_interest_accrual.loan = args.loan
loan_interest_accrual.applicant_type = args.applicant_type
loan_interest_accrual.applicant = args.applicant
loan_interest_accrual.interest_income_account = args.interest_income_account
loan_interest_accrual.loan_account = args.loan_account
loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, 2)
loan_interest_accrual.interest_amount = flt(args.interest_amount, 2)
loan_interest_accrual.posting_date = args.posting_date or nowdate()
loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest
loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name
loan_interest_accrual.payable_principal_amount = args.payable_principal
precision = cint(frappe.db.get_default("currency_precision")) or 2
loan_interest_accrual.save()
loan_interest_accrual.submit()
loan_interest_accrual = frappe.new_doc("Loan Interest Accrual")
loan_interest_accrual.loan = args.loan
loan_interest_accrual.applicant_type = args.applicant_type
loan_interest_accrual.applicant = args.applicant
loan_interest_accrual.interest_income_account = args.interest_income_account
loan_interest_accrual.loan_account = args.loan_account
loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, precision)
loan_interest_accrual.interest_amount = flt(args.interest_amount, precision)
loan_interest_accrual.posting_date = args.posting_date or nowdate()
loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest
loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name
loan_interest_accrual.payable_principal_amount = args.payable_principal
loan_interest_accrual.save()
loan_interest_accrual.submit()
def get_no_of_days_for_interest_accural(loan, posting_date):

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe, erpnext
import json
from frappe import _
from frappe.utils import flt, getdate
from frappe.utils import flt, getdate, cint
from six import iteritems
from frappe.model.document import Document
from frappe.utils import date_diff, add_days, getdate, add_months, get_first_day, get_datetime
@ -29,8 +29,11 @@ class LoanRepayment(AccountsController):
def on_cancel(self):
self.mark_as_unpaid()
self.make_gl_entries(cancel=1)
self.ignore_linked_doctypes = ['GL Entry']
def set_missing_values(self, amounts):
precision = cint(frappe.db.get_default("currency_precision")) or 2
if not self.posting_date:
self.posting_date = get_datetime()
@ -38,24 +41,26 @@ class LoanRepayment(AccountsController):
self.cost_center = erpnext.get_default_cost_center(self.company)
if not self.interest_payable:
self.interest_payable = flt(amounts['interest_amount'], 2)
self.interest_payable = flt(amounts['interest_amount'], precision)
if not self.penalty_amount:
self.penalty_amount = flt(amounts['penalty_amount'], 2)
self.penalty_amount = flt(amounts['penalty_amount'], precision)
if not self.pending_principal_amount:
self.pending_principal_amount = flt(amounts['pending_principal_amount'], 2)
self.pending_principal_amount = flt(amounts['pending_principal_amount'], precision)
if not self.payable_principal_amount and self.is_term_loan:
self.payable_principal_amount = flt(amounts['payable_principal_amount'], 2)
self.payable_principal_amount = flt(amounts['payable_principal_amount'], precision)
if not self.payable_amount:
self.payable_amount = flt(amounts['payable_amount'], 2)
self.payable_amount = flt(amounts['payable_amount'], precision)
if amounts.get('due_date'):
self.due_date = amounts.get('due_date')
def validate_amount(self):
precision = cint(frappe.db.get_default("currency_precision")) or 2
if not self.amount_paid:
frappe.throw(_("Amount paid cannot be zero"))
@ -63,11 +68,13 @@ class LoanRepayment(AccountsController):
msg = _("Paid amount cannot be less than {0}").format(self.penalty_amount)
frappe.throw(msg)
if self.payment_type == "Loan Closure" and flt(self.amount_paid, 2) < flt(self.payable_amount, 2):
if self.payment_type == "Loan Closure" and flt(self.amount_paid, precision) < flt(self.payable_amount, precision):
msg = _("Amount of {0} is required for Loan closure").format(self.payable_amount)
frappe.throw(msg)
def update_paid_amount(self):
precision = cint(frappe.db.get_default("currency_precision")) or 2
loan = frappe.get_doc("Loan", self.against_loan)
for payment in self.repayment_details:
@ -75,9 +82,9 @@ class LoanRepayment(AccountsController):
SET paid_principal_amount = `paid_principal_amount` + %s,
paid_interest_amount = `paid_interest_amount` + %s
WHERE name = %s""",
(flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual))
(flt(payment.paid_principal_amount, precision), flt(payment.paid_interest_amount, precision), payment.loan_interest_accrual))
if flt(loan.total_principal_paid + self.principal_amount_paid, 2) >= flt(loan.total_payment, 2):
if flt(loan.total_principal_paid + self.principal_amount_paid, precision) >= flt(loan.total_payment, precision):
if loan.is_secured_loan:
frappe.db.set_value("Loan", self.against_loan, "status", "Loan Closure Requested")
else:
@ -253,6 +260,7 @@ def get_accrued_interest_entries(against_loan):
# So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
def get_amounts(amounts, against_loan, posting_date, payment_type):
precision = cint(frappe.db.get_default("currency_precision")) or 2
against_loan_doc = frappe.get_doc("Loan", against_loan)
loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type)
@ -282,8 +290,8 @@ def get_amounts(amounts, against_loan, posting_date, payment_type):
payable_principal_amount += entry.payable_principal_amount
pending_accrual_entries.setdefault(entry.name, {
'interest_amount': flt(entry.interest_amount),
'payable_principal_amount': flt(entry.payable_principal_amount)
'interest_amount': flt(entry.interest_amount, precision),
'payable_principal_amount': flt(entry.payable_principal_amount, precision)
})
if not final_due_date:
@ -301,11 +309,11 @@ def get_amounts(amounts, against_loan, posting_date, payment_type):
per_day_interest = (payable_principal_amount * (loan_type_details.rate_of_interest / 100))/365
total_pending_interest += (pending_days * per_day_interest)
amounts["pending_principal_amount"] = pending_principal_amount
amounts["payable_principal_amount"] = payable_principal_amount
amounts["interest_amount"] = total_pending_interest
amounts["penalty_amount"] = penalty_amount
amounts["payable_amount"] = payable_principal_amount + total_pending_interest + penalty_amount
amounts["pending_principal_amount"] = flt(pending_principal_amount, precision)
amounts["payable_principal_amount"] = flt(payable_principal_amount, precision)
amounts["interest_amount"] = flt(total_pending_interest, precision)
amounts["penalty_amount"] = flt(penalty_amount, precision)
amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision)
amounts["pending_accrual_entries"] = pending_accrual_entries
if final_due_date:

View File

@ -41,6 +41,7 @@
"options": "Company:company:default_currency"
},
{
"default": "0",
"fieldname": "rate_of_interest",
"fieldtype": "Percent",
"label": "Rate of Interest (%) Yearly",
@ -143,7 +144,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2020-04-15 00:24:43.259963",
"modified": "2020-06-07 18:55:59.346292",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Type",

View File

@ -76,7 +76,8 @@ def get_columns(filters):
"fieldtype": "Link",
"fieldname": "currency",
"options": "Currency",
"width": 50
"width": 50,
"hidden": 1
}
]
@ -84,17 +85,13 @@ def get_columns(filters):
def get_data(filters):
loan_security_price_map = frappe._dict(frappe.get_all("Loan Security",
fields=["name", "loan_security_price"], as_list=1
))
data = []
conditions = get_conditions(filters)
loan_security_pledges = frappe.db.sql("""
SELECT
p.name, p.applicant, p.loan, p.status, p.pledge_time,
c.loan_security, c.qty
c.loan_security, c.qty, c.loan_security_price, c.amount
FROM
`tabLoan Security Pledge` p, `tabPledge` c
WHERE
@ -115,8 +112,8 @@ def get_data(filters):
row["pledge_time"] = pledge.pledge_time
row["loan_security"] = pledge.loan_security
row["qty"] = pledge.qty
row["loan_security_price"] = loan_security_price_map.get(pledge.loan_security)
row["loan_security_value"] = row["loan_security_price"] * pledge.qty
row["loan_security_price"] = pledge.loan_security_price
row["loan_security_value"] = pledge.amount
row["currency"] = default_currency
data.append(row)