201 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 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
 |