2
0
mirror of https://github.com/frappe/erpnext.git synced 2024-06-11 22:52:41 +00:00
erpnext/erpnext/loan_management/doctype/loan_application/loan_application.py
2022-03-28 18:52:46 +05:30

258 lines
7.7 KiB
Python

# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
import math
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
from frappe.utils import cint, flt, rounded
from erpnext.loan_management.doctype.loan.loan import (
get_monthly_repayment_amount,
get_sanctioned_amount_limit,
get_total_loan_amount,
validate_repayment_method,
)
from erpnext.loan_management.doctype.loan_security_price.loan_security_price import (
get_loan_security_price,
)
class LoanApplication(Document):
def validate(self):
self.set_pledge_amount()
self.set_loan_amount()
self.validate_loan_amount()
if self.is_term_loan:
validate_repayment_method(
self.repayment_method,
self.loan_amount,
self.repayment_amount,
self.repayment_periods,
self.is_term_loan,
)
self.validate_loan_type()
self.get_repayment_details()
self.check_sanctioned_amount_limit()
def validate_loan_type(self):
company = frappe.get_value("Loan Type", self.loan_type, "company")
if company != self.company:
frappe.throw(_("Please select Loan Type for company {0}").format(frappe.bold(self.company)))
def validate_loan_amount(self):
if not self.loan_amount:
frappe.throw(_("Loan Amount is mandatory"))
maximum_loan_limit = frappe.db.get_value("Loan Type", self.loan_type, "maximum_loan_amount")
if maximum_loan_limit and self.loan_amount > maximum_loan_limit:
frappe.throw(
_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit)
)
if self.maximum_loan_amount and self.loan_amount > self.maximum_loan_amount:
frappe.throw(
_("Loan Amount exceeds maximum loan amount of {0} as per proposed securities").format(
self.maximum_loan_amount
)
)
def check_sanctioned_amount_limit(self):
sanctioned_amount_limit = get_sanctioned_amount_limit(
self.applicant_type, self.applicant, self.company
)
if sanctioned_amount_limit:
total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company)
if sanctioned_amount_limit and flt(self.loan_amount) + flt(total_loan_amount) > flt(
sanctioned_amount_limit
):
frappe.throw(
_("Sanctioned Amount limit crossed for {0} {1}").format(
self.applicant_type, frappe.bold(self.applicant)
)
)
def set_pledge_amount(self):
for proposed_pledge in self.proposed_pledges:
if not proposed_pledge.qty and not proposed_pledge.amount:
frappe.throw(_("Qty or Amount is mandatroy for loan security"))
proposed_pledge.loan_security_price = get_loan_security_price(proposed_pledge.loan_security)
if not proposed_pledge.qty:
proposed_pledge.qty = cint(proposed_pledge.amount / proposed_pledge.loan_security_price)
proposed_pledge.amount = proposed_pledge.qty * proposed_pledge.loan_security_price
proposed_pledge.post_haircut_amount = cint(
proposed_pledge.amount - (proposed_pledge.amount * proposed_pledge.haircut / 100)
)
def get_repayment_details(self):
if self.is_term_loan:
if self.repayment_method == "Repay Over Number of Periods":
self.repayment_amount = get_monthly_repayment_amount(
self.loan_amount, self.rate_of_interest, self.repayment_periods
)
if self.repayment_method == "Repay Fixed Amount per Period":
monthly_interest_rate = flt(self.rate_of_interest) / (12 * 100)
if monthly_interest_rate:
min_repayment_amount = self.loan_amount * monthly_interest_rate
if self.repayment_amount - min_repayment_amount <= 0:
frappe.throw(_("Repayment Amount must be greater than " + str(flt(min_repayment_amount, 2))))
self.repayment_periods = math.ceil(
(math.log(self.repayment_amount) - math.log(self.repayment_amount - min_repayment_amount))
/ (math.log(1 + monthly_interest_rate))
)
else:
self.repayment_periods = self.loan_amount / self.repayment_amount
self.calculate_payable_amount()
else:
self.total_payable_amount = self.loan_amount
def calculate_payable_amount(self):
balance_amount = self.loan_amount
self.total_payable_amount = 0
self.total_payable_interest = 0
while balance_amount > 0:
interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12 * 100))
balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount)
self.total_payable_interest += interest_amount
self.total_payable_amount = self.loan_amount + self.total_payable_interest
def set_loan_amount(self):
if self.is_secured_loan and not self.proposed_pledges:
frappe.throw(_("Proposed Pledges are mandatory for secured Loans"))
if self.is_secured_loan and self.proposed_pledges:
self.maximum_loan_amount = 0
for security in self.proposed_pledges:
self.maximum_loan_amount += flt(security.post_haircut_amount)
if not self.loan_amount and self.is_secured_loan and self.proposed_pledges:
self.loan_amount = self.maximum_loan_amount
@frappe.whitelist()
def create_loan(source_name, target_doc=None, submit=0):
def update_accounts(source_doc, target_doc, source_parent):
account_details = frappe.get_all(
"Loan Type",
fields=[
"mode_of_payment",
"payment_account",
"loan_account",
"interest_income_account",
"penalty_income_account",
],
filters={"name": source_doc.loan_type},
)[0]
if source_doc.is_secured_loan:
target_doc.maximum_loan_amount = 0
target_doc.mode_of_payment = account_details.mode_of_payment
target_doc.payment_account = account_details.payment_account
target_doc.loan_account = account_details.loan_account
target_doc.interest_income_account = account_details.interest_income_account
target_doc.penalty_income_account = account_details.penalty_income_account
target_doc.loan_application = source_name
doclist = get_mapped_doc(
"Loan Application",
source_name,
{
"Loan Application": {
"doctype": "Loan",
"validation": {"docstatus": ["=", 1]},
"postprocess": update_accounts,
}
},
target_doc,
)
if submit:
doclist.submit()
return doclist
@frappe.whitelist()
def create_pledge(loan_application, loan=None):
loan_application_doc = frappe.get_doc("Loan Application", loan_application)
lsp = frappe.new_doc("Loan Security Pledge")
lsp.applicant_type = loan_application_doc.applicant_type
lsp.applicant = loan_application_doc.applicant
lsp.loan_application = loan_application_doc.name
lsp.company = loan_application_doc.company
if loan:
lsp.loan = loan
for pledge in loan_application_doc.proposed_pledges:
lsp.append(
"securities",
{
"loan_security": pledge.loan_security,
"qty": pledge.qty,
"loan_security_price": pledge.loan_security_price,
"haircut": pledge.haircut,
},
)
lsp.save()
lsp.submit()
message = _("Loan Security Pledge Created : {0}").format(lsp.name)
frappe.msgprint(message)
return lsp.name
# This is a sandbox method to get the proposed pledges
@frappe.whitelist()
def get_proposed_pledge(securities):
if isinstance(securities, str):
securities = json.loads(securities)
proposed_pledges = {"securities": []}
maximum_loan_amount = 0
for security in securities:
security = frappe._dict(security)
if not security.qty and not security.amount:
frappe.throw(_("Qty or Amount is mandatroy for loan security"))
security.loan_security_price = get_loan_security_price(security.loan_security)
if not security.qty:
security.qty = cint(security.amount / security.loan_security_price)
security.amount = security.qty * security.loan_security_price
security.post_haircut_amount = cint(security.amount - (security.amount * security.haircut / 100))
maximum_loan_amount += security.post_haircut_amount
proposed_pledges["securities"].append(security)
proposed_pledges["maximum_loan_amount"] = maximum_loan_amount
return proposed_pledges