import frappe from frappe import _ from frappe.model.naming import make_autoname from frappe.utils import flt, nowdate from erpnext.accounts.doctype.account.test_account import create_account from erpnext.loan_management.doctype.loan_repayment.loan_repayment import ( get_accrued_interest_entries, ) from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import ( process_loan_interest_accrual_for_term_loans, ) def execute(): # Create a penalty account for loan types frappe.reload_doc("loan_management", "doctype", "loan_type") frappe.reload_doc("loan_management", "doctype", "loan") frappe.reload_doc("loan_management", "doctype", "repayment_schedule") frappe.reload_doc("loan_management", "doctype", "process_loan_interest_accrual") frappe.reload_doc("loan_management", "doctype", "loan_repayment") frappe.reload_doc("loan_management", "doctype", "loan_repayment_detail") frappe.reload_doc("loan_management", "doctype", "loan_interest_accrual") frappe.reload_doc("accounts", "doctype", "gl_entry") frappe.reload_doc("accounts", "doctype", "journal_entry_account") updated_loan_types = [] loans_to_close = [] # Update old loan status as closed if frappe.db.has_column("Repayment Schedule", "paid"): loans_list = frappe.db.sql( """SELECT distinct parent from `tabRepayment Schedule` where paid = 0 and docstatus = 1""", as_dict=1, ) loans_to_close = [d.parent for d in loans_list] if loans_to_close: frappe.db.sql( "UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (", ".join(["%s"] * len(loans_to_close))), tuple(loans_to_close), ) loans = frappe.get_all( "Loan", fields=[ "name", "loan_type", "company", "status", "mode_of_payment", "applicant_type", "applicant", "loan_account", "payment_account", "interest_income_account", ], filters={"docstatus": 1, "status": ("!=", "Closed")}, ) for loan in loans: # Update details in Loan Types and Loan loan_type_company = frappe.db.get_value("Loan Type", loan.loan_type, "company") loan_type = loan.loan_type group_income_account = frappe.get_value( "Account", { "company": loan.company, "is_group": 1, "root_type": "Income", "account_name": _("Indirect Income"), }, ) if not group_income_account: group_income_account = frappe.get_value( "Account", {"company": loan.company, "is_group": 1, "root_type": "Income"} ) penalty_account = create_account( company=loan.company, account_type="Income Account", account_name="Penalty Account", parent_account=group_income_account, ) # Same loan type used for multiple companies if loan_type_company and loan_type_company != loan.company: # get loan type for appropriate company loan_type_name = frappe.get_value( "Loan Type", { "company": loan.company, "mode_of_payment": loan.mode_of_payment, "loan_account": loan.loan_account, "payment_account": loan.payment_account, "disbursement_account": loan.payment_account, "interest_income_account": loan.interest_income_account, "penalty_income_account": loan.penalty_income_account, }, "name", ) if not loan_type_name: loan_type_name = create_loan_type(loan, loan_type_name, penalty_account) # update loan type in loan frappe.db.sql( "UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name, loan.name) ) loan_type = loan_type_name if loan_type_name not in updated_loan_types: updated_loan_types.append(loan_type_name) elif not loan_type_company: loan_type_doc = frappe.get_doc("Loan Type", loan.loan_type) loan_type_doc.is_term_loan = 1 loan_type_doc.company = loan.company loan_type_doc.mode_of_payment = loan.mode_of_payment loan_type_doc.payment_account = loan.payment_account loan_type_doc.loan_account = loan.loan_account loan_type_doc.interest_income_account = loan.interest_income_account loan_type_doc.penalty_income_account = penalty_account loan_type_doc.submit() updated_loan_types.append(loan.loan_type) loan_type = loan.loan_type if loan_type in updated_loan_types: if loan.status == "Fully Disbursed": status = "Disbursed" elif loan.status == "Repaid/Closed": status = "Closed" else: status = loan.status frappe.db.set_value( "Loan", loan.name, {"is_term_loan": 1, "penalty_income_account": penalty_account, "status": status}, ) process_loan_interest_accrual_for_term_loans( posting_date=nowdate(), loan_type=loan_type, loan=loan.name ) if frappe.db.has_column("Repayment Schedule", "paid"): total_principal, total_interest = frappe.db.get_value( "Repayment Schedule", {"paid": 1, "parent": loan.name}, ["sum(principal_amount) as total_principal", "sum(interest_amount) as total_interest"], ) accrued_entries = get_accrued_interest_entries(loan.name) for entry in accrued_entries: interest_paid = 0 principal_paid = 0 if flt(total_interest) > flt(entry.interest_amount): interest_paid = flt(entry.interest_amount) else: interest_paid = flt(total_interest) if flt(total_principal) > flt(entry.payable_principal_amount): principal_paid = flt(entry.payable_principal_amount) else: principal_paid = flt(total_principal) frappe.db.sql( """ UPDATE `tabLoan Interest Accrual` SET paid_principal_amount = `paid_principal_amount` + %s, paid_interest_amount = `paid_interest_amount` + %s WHERE name = %s""", (principal_paid, interest_paid, entry.name), ) total_principal = flt(total_principal) - principal_paid total_interest = flt(total_interest) - interest_paid def create_loan_type(loan, loan_type_name, penalty_account): loan_type_doc = frappe.new_doc("Loan Type") loan_type_doc.loan_name = make_autoname("Loan Type-.####") loan_type_doc.is_term_loan = 1 loan_type_doc.company = loan.company loan_type_doc.mode_of_payment = loan.mode_of_payment loan_type_doc.payment_account = loan.payment_account loan_type_doc.disbursement_account = loan.payment_account loan_type_doc.loan_account = loan.loan_account loan_type_doc.interest_income_account = loan.interest_income_account loan_type_doc.penalty_income_account = penalty_account loan_type_doc.submit() return loan_type_doc.name