2
0
mirror of https://github.com/frappe/erpnext.git synced 2024-06-02 18:31:06 +00:00
erpnext/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py

258 lines
7.2 KiB
Python

# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.utils import add_days, flt, get_datetime, nowdate
import erpnext
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import (
get_pledged_security_qty,
)
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (
process_loan_interest_accrual_for_demand_loans,
)
class LoanDisbursement(AccountsController):
def validate(self):
self.set_missing_values()
self.validate_disbursal_amount()
def on_submit(self):
self.set_status_and_amounts()
self.make_gl_entries()
def on_cancel(self):
self.set_status_and_amounts(cancel=1)
self.make_gl_entries(cancel=1)
self.ignore_linked_doctypes = ["GL Entry", "Payment Ledger Entry"]
def set_missing_values(self):
if not self.disbursement_date:
self.disbursement_date = nowdate()
if not self.cost_center:
self.cost_center = erpnext.get_default_cost_center(self.company)
if not self.posting_date:
self.posting_date = self.disbursement_date or nowdate()
def validate_disbursal_amount(self):
possible_disbursal_amount = get_disbursal_amount(self.against_loan)
if self.disbursed_amount > possible_disbursal_amount:
frappe.throw(_("Disbursed Amount cannot be greater than {0}").format(possible_disbursal_amount))
def set_status_and_amounts(self, cancel=0):
loan_details = frappe.get_all(
"Loan",
fields=[
"loan_amount",
"disbursed_amount",
"total_payment",
"total_principal_paid",
"total_interest_payable",
"status",
"is_term_loan",
"is_secured_loan",
],
filters={"name": self.against_loan},
)[0]
if cancel:
disbursed_amount, status, total_payment = self.get_values_on_cancel(loan_details)
else:
disbursed_amount, status, total_payment = self.get_values_on_submit(loan_details)
frappe.db.set_value(
"Loan",
self.against_loan,
{
"disbursement_date": self.disbursement_date,
"disbursed_amount": disbursed_amount,
"status": status,
"total_payment": total_payment,
},
)
def get_values_on_cancel(self, loan_details):
disbursed_amount = loan_details.disbursed_amount - self.disbursed_amount
total_payment = loan_details.total_payment
if loan_details.disbursed_amount > loan_details.loan_amount:
topup_amount = loan_details.disbursed_amount - loan_details.loan_amount
if topup_amount > self.disbursed_amount:
topup_amount = self.disbursed_amount
total_payment = total_payment - topup_amount
if disbursed_amount == 0:
status = "Sanctioned"
elif disbursed_amount >= loan_details.loan_amount:
status = "Disbursed"
else:
status = "Partially Disbursed"
return disbursed_amount, status, total_payment
def get_values_on_submit(self, loan_details):
disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount
total_payment = loan_details.total_payment
if loan_details.status in ("Disbursed", "Partially Disbursed") and not loan_details.is_term_loan:
process_loan_interest_accrual_for_demand_loans(
posting_date=add_days(self.disbursement_date, -1),
loan=self.against_loan,
accrual_type="Disbursement",
)
if disbursed_amount > loan_details.loan_amount:
topup_amount = disbursed_amount - loan_details.loan_amount
if topup_amount < 0:
topup_amount = 0
if topup_amount > self.disbursed_amount:
topup_amount = self.disbursed_amount
total_payment = total_payment + topup_amount
if flt(disbursed_amount) >= loan_details.loan_amount:
status = "Disbursed"
else:
status = "Partially Disbursed"
return disbursed_amount, status, total_payment
def make_gl_entries(self, cancel=0, adv_adj=0):
gle_map = []
gle_map.append(
self.get_gl_dict(
{
"account": self.loan_account,
"against": self.disbursement_account,
"debit": self.disbursed_amount,
"debit_in_account_currency": self.disbursed_amount,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
"remarks": _("Disbursement against loan:") + self.against_loan,
"cost_center": self.cost_center,
"party_type": self.applicant_type,
"party": self.applicant,
"posting_date": self.disbursement_date,
}
)
)
gle_map.append(
self.get_gl_dict(
{
"account": self.disbursement_account,
"against": self.loan_account,
"credit": self.disbursed_amount,
"credit_in_account_currency": self.disbursed_amount,
"against_voucher_type": "Loan",
"against_voucher": self.against_loan,
"remarks": _("Disbursement against loan:") + self.against_loan,
"cost_center": self.cost_center,
"posting_date": self.disbursement_date,
}
)
)
if gle_map:
make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj)
def get_total_pledged_security_value(loan):
update_time = get_datetime()
loan_security_price_map = frappe._dict(
frappe.get_all(
"Loan Security Price",
fields=["loan_security", "loan_security_price"],
filters={"valid_from": ("<=", update_time), "valid_upto": (">=", update_time)},
as_list=1,
)
)
hair_cut_map = frappe._dict(
frappe.get_all("Loan Security", fields=["name", "haircut"], as_list=1)
)
security_value = 0.0
pledged_securities = get_pledged_security_qty(loan)
for security, qty in pledged_securities.items():
after_haircut_percentage = 100 - hair_cut_map.get(security)
security_value += (
loan_security_price_map.get(security, 0) * qty * after_haircut_percentage
) / 100
return security_value
@frappe.whitelist()
def get_disbursal_amount(loan, on_current_security_price=0):
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import (
get_pending_principal_amount,
)
loan_details = frappe.get_value(
"Loan",
loan,
[
"loan_amount",
"disbursed_amount",
"total_payment",
"debit_adjustment_amount",
"credit_adjustment_amount",
"refund_amount",
"total_principal_paid",
"total_interest_payable",
"status",
"is_term_loan",
"is_secured_loan",
"maximum_loan_amount",
"written_off_amount",
],
as_dict=1,
)
if loan_details.is_secured_loan and frappe.get_all(
"Loan Security Shortfall", filters={"loan": loan, "status": "Pending"}
):
return 0
pending_principal_amount = get_pending_principal_amount(loan_details)
security_value = 0.0
if loan_details.is_secured_loan and on_current_security_price:
security_value = get_total_pledged_security_value(loan)
if loan_details.is_secured_loan and not on_current_security_price:
security_value = get_maximum_amount_as_per_pledged_security(loan)
if not security_value and not loan_details.is_secured_loan:
security_value = flt(loan_details.loan_amount)
disbursal_amount = flt(security_value) - flt(pending_principal_amount)
if (
loan_details.is_term_loan
and (disbursal_amount + loan_details.loan_amount) > loan_details.loan_amount
):
disbursal_amount = loan_details.loan_amount - loan_details.disbursed_amount
return disbursal_amount
def get_maximum_amount_as_per_pledged_security(loan):
return flt(frappe.db.get_value("Loan Security Pledge", {"loan": loan}, "sum(maximum_loan_value)"))