From f7e216e285dd01f77f8536182b159fc6ddc26866 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 10 Apr 2020 20:03:11 +0530 Subject: [PATCH 1/4] fix: Employee loan fixes --- erpnext/hooks.py | 4 +- .../hr/doctype/salary_slip/salary_slip.json | 5 +- erpnext/loan_management/doctype/loan/loan.js | 10 +- erpnext/loan_management/doctype/loan/loan.py | 5 +- .../loan_management/doctype/loan/test_loan.py | 13 +-- .../loan_application/loan_application.js | 21 ++-- .../loan_disbursement/loan_disbursement.json | 13 +-- .../loan_disbursement/loan_disbursement.py | 49 +++++---- .../test_loan_disbursement.py | 4 +- .../loan_interest_accrual.json | 10 +- .../loan_interest_accrual.py | 102 ++++++++++++------ .../test_loan_interest_accrual.py | 4 +- .../process_loan_interest_accrual.json | 11 +- .../process_loan_interest_accrual.py | 40 ++++++- .../salary_slip_loan/salary_slip_loan.json | 13 +-- 15 files changed, 197 insertions(+), 107 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b2dc96178d..f1881d97df 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -312,13 +312,13 @@ scheduler_events = { "erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation", "erpnext.hr.utils.generate_leave_encashment", "erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall.check_for_ltv_shortfall", - "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.make_accrual_interest_entry_for_term_loans" + "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_term_loans" ], "monthly_long": [ "erpnext.accounts.deferred_revenue.convert_deferred_revenue_to_income", "erpnext.accounts.deferred_revenue.convert_deferred_expense_to_expense", "erpnext.hr.utils.allocate_earned_leaves", - "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual" + "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_demand_loans" ] } diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json index e73d41a28e..097d3a096b 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.json +++ b/erpnext/hr/doctype/salary_slip/salary_slip.json @@ -372,8 +372,7 @@ "fieldtype": "Table", "label": "Employee Loan", "options": "Salary Slip Loan", - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "fieldname": "section_break_43", @@ -464,7 +463,7 @@ "idx": 9, "is_submittable": 1, "links": [], - "modified": "2019-12-31 17:13:45.146271", + "modified": "2020-04-09 20:02:53.159827", "modified_by": "Administrator", "module": "HR", "name": "Salary Slip", diff --git a/erpnext/loan_management/doctype/loan/loan.js b/erpnext/loan_management/doctype/loan/loan.js index 8b220171e8..9cd8b2e90a 100644 --- a/erpnext/loan_management/doctype/loan/loan.js +++ b/erpnext/loan_management/doctype/loan/loan.js @@ -97,6 +97,8 @@ frappe.ui.form.on('Loan', { "company": frm.doc.company, "applicant_type": frm.doc.applicant_type, "applicant": frm.doc.applicant, + "pending_amount": frm.doc.loan_amount - frm.doc.disbursed_amount > 0 ? + frm.doc.loan_amount - frm.doc.disbursed_amount : 0, "as_dict": 1 }, method: "erpnext.loan_management.doctype.loan.loan.make_loan_disbursement", @@ -149,10 +151,10 @@ frappe.ui.form.on('Loan', { return frappe.call({ method: "erpnext.loan_management.doctype.loan.loan.get_loan_application", args: { - "loan_application": frm.doc.loan_application - }, - callback: function (r) { - if (!r.exc && r.message) { + "loan_application": frm.doc.loan_application + }, + callback: function (r) { + if (!r.exc && r.message) { let loan_fields = ["loan_type", "loan_amount", "repayment_method", "monthly_repayment_amount", "repayment_periods", "rate_of_interest", "is_secured_loan"] diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index 696410b7bb..eb5f127aa9 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -198,7 +198,7 @@ def close_loan(loan, total_amount_paid): frappe.db.set_value("Loan", loan, "status", "Closed") @frappe.whitelist() -def make_loan_disbursement(loan, company, applicant_type, applicant, disbursed_amount=0, as_dict=0): +def make_loan_disbursement(loan, company, applicant_type, applicant, pending_amount=0, as_dict=0): disbursement_entry = frappe.new_doc("Loan Disbursement") disbursement_entry.against_loan = loan disbursement_entry.applicant_type = applicant_type @@ -206,8 +206,7 @@ def make_loan_disbursement(loan, company, applicant_type, applicant, disbursed_a disbursement_entry.company = company disbursement_entry.disbursement_date = nowdate() - if disbursed_amount: - disbursement_entry.disbursed_amount = disbursed_amount + disbursement_entry.disbursed_amount = pending_amount if as_dict: return disbursement_entry.as_dict() else: diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 759b0d8e09..34b801b3be 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -10,7 +10,8 @@ from frappe.utils import (nowdate, add_days, getdate, now_datetime, add_to_date, add_months, get_first_day, get_last_day, flt, date_diff) from erpnext.selling.doctype.customer.test_customer import get_customer_dict from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee -from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (process_loan_interest_accrual_for_demand_loans, + process_loan_interest_accrual_for_term_loans) from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year) from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import check_for_ltv_shortfall @@ -145,7 +146,7 @@ class TestLoan(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) - process_loan_interest_accrual(posting_date = last_date) + process_loan_interest_accrual_for_demand_loans(posting_date = last_date) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 10), "Regular Payment", 111118.68) repayment_entry.save() @@ -186,7 +187,7 @@ class TestLoan(unittest.TestCase): / (days_in_year(get_datetime(first_date).year) * 100) make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) - process_loan_interest_accrual(posting_date = last_date) + process_loan_interest_accrual_for_demand_loans(posting_date = last_date) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), "Loan Closure", 13315.0681) @@ -224,7 +225,7 @@ class TestLoan(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1)) - make_accrual_interest_entry_for_term_loans(posting_date=nowdate()) + process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(get_last_day(nowdate()), 5), "Regular Payment", 89768.7534247) @@ -264,8 +265,8 @@ class TestLoan(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) - process_loan_interest_accrual(posting_date = add_days(first_date, 15)) - process_loan_interest_accrual(posting_date = add_days(first_date, 30)) + process_loan_interest_accrual_for_demand_loans(posting_date = add_days(first_date, 15)) + process_loan_interest_accrual_for_demand_loans(posting_date = add_days(first_date, 30)) repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 1), "Regular Payment", 6500) repayment_entry.save() diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.js b/erpnext/loan_management/doctype/loan_application/loan_application.js index 57050d86c6..aba5f4260c 100644 --- a/erpnext/loan_management/doctype/loan_application/loan_application.js +++ b/erpnext/loan_management/doctype/loan_application/loan_application.js @@ -31,13 +31,15 @@ frappe.ui.form.on('Loan Application', { add_toolbar_buttons: function(frm) { if (frm.doc.status == "Approved") { - frappe.db.get_value("Loan Security Pledge", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => { - if (!r) { - frm.add_custom_button(__('Loan Security Pledge'), function() { - frm.trigger('create_loan_security_pledge') - },__('Create')) - } - }); + if (frm.doc.is_secured) { + frappe.db.get_value("Loan Security Pledge", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => { + if (!r) { + frm.add_custom_button(__('Loan Security Pledge'), function() { + frm.trigger('create_loan_security_pledge') + },__('Create')) + } + }); + } frappe.db.get_value("Loan", {"loan_application": frm.doc.name, "docstatus": 1}, "name", (r) => { if (!r) { @@ -61,6 +63,11 @@ frappe.ui.form.on('Loan Application', { }); }, create_loan_security_pledge: function(frm) { + + if(!frm.doc.is_secured_loan) { + frappe.throw(__("Loan Security Pledge can only be created for secured loans")); + } + frappe.call({ method: "erpnext.loan_management.doctype.loan_application.loan_application.create_pledge", args: { diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json index 72a4ddcc8f..2d9c45d122 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "LM-DIS-.#####", "creation": "2019-09-07 12:44:49.125452", "doctype": "DocType", @@ -13,7 +14,6 @@ "applicant_type", "applicant", "section_break_7", - "pending_amount_for_disbursal", "disbursed_amount", "accounting_dimensions_section", "cost_center", @@ -83,13 +83,6 @@ "label": "Posting Date", "read_only": 1 }, - { - "fieldname": "pending_amount_for_disbursal", - "fieldtype": "Currency", - "label": "Pending Amount For Disbursal", - "options": "Company:company:default_currency", - "read_only": 1 - }, { "fieldname": "column_break_4", "fieldtype": "Column Break" @@ -99,6 +92,7 @@ "fieldtype": "Section Break" }, { + "collapsible": 1, "fieldname": "section_break_13", "fieldtype": "Section Break" }, @@ -123,7 +117,8 @@ } ], "is_submittable": 1, - "modified": "2019-10-24 12:32:32.230881", + "links": [], + "modified": "2020-04-09 14:44:28.527271", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Disbursement", diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index fa7db2d565..2918486ebd 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -4,21 +4,24 @@ from __future__ import unicode_literals import frappe, erpnext +from frappe import _ from frappe.model.document import Document from frappe.utils import nowdate, getdate, add_days, flt from erpnext.controllers.accounts_controller import AccountsController from erpnext.accounts.general_ledger import make_gl_entries -from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual +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.set_pending_amount_for_disbursal() def before_submit(self): self.set_status_and_amounts() + def before_cancel(self): + self.set_status_and_amounts(cancel=1) + def on_submit(self): self.make_gl_entries() @@ -38,13 +41,7 @@ class LoanDisbursement(AccountsController): if not self.bank_account and self.applicant_type == "Customer": self.bank_account = frappe.db.get_value("Customer", self.applicant, "default_bank_account") - def set_pending_amount_for_disbursal(self): - loan_amount, disbursed_amount = frappe.db.get_value('Loan', - {'name': self.against_loan}, ['loan_amount', 'disbursed_amount']) - - self.pending_amount_for_disbursal = loan_amount - disbursed_amount - - def set_status_and_amounts(self): + def set_status_and_amounts(self, cancel=0): loan_details = frappe.get_all("Loan", fields = ["loan_amount", "disbursed_amount", "total_principal_paid", "status", "is_term_loan"], @@ -52,26 +49,32 @@ class LoanDisbursement(AccountsController): )[0] if loan_details.status == "Disbursed" and not loan_details.is_term_loan: - process_loan_interest_accrual(posting_date=add_days(self.disbursement_date, -1), + process_loan_interest_accrual_for_demand_loans(posting_date=add_days(self.disbursement_date, -1), loan=self.against_loan) - disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount - - if flt(disbursed_amount) - flt(loan_details.total_principal_paid) > flt(loan_details.loan_amount): - frappe.throw(_("Disbursed Amount cannot be greater than loan amount")) - - if flt(disbursed_amount) > flt(loan_details.loan_amount): - total_principal_paid = loan_details.total_principal_paid - (disbursed_amount - loan_details.loan_amount) - frappe.db.set_value("Loan", self.against_loan, "total_principal_paid", total_principal_paid) - - if flt(loan_details.loan_amount) == flt(disbursed_amount): - frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed") + if cancel: + disbursed_amount = loan_details.disbursed_amount - self.disbursed_amount + if disbursed_amount == 0: + status = "Sanctioned" + elif disbursed_amount >= loan_details.disbursed_amount: + status = "Disbursed" + else: + status = "Partially Disbursed" else: - frappe.db.set_value("Loan", self.against_loan, "status", "Partially Disbursed") + disbursed_amount = self.disbursed_amount + loan_details.disbursed_amount + + if flt(disbursed_amount) - flt(loan_details.total_principal_paid) > flt(loan_details.loan_amount): + frappe.throw(_("Disbursed Amount cannot be greater than loan amount")) + + if flt(disbursed_amount) >= loan_details.disbursed_amount: + status = "Disbursed" + else: + status = "Partially Disbursed" frappe.db.set_value("Loan", self.against_loan, { "disbursement_date": self.disbursement_date, - "disbursed_amount": disbursed_amount + "disbursed_amount": disbursed_amount, + "status": status }) def make_gl_entries(self, cancel=0, adv_adj=0): diff --git a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py index 968e377fcc..01290d400a 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py @@ -7,7 +7,7 @@ import unittest from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date) from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_repayment_entry, make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_security_price) -from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year) from erpnext.selling.doctype.customer.test_customer import get_customer_dict @@ -56,7 +56,7 @@ class TestLoanDisbursement(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) - process_loan_interest_accrual(posting_date=add_days(last_date, 1)) + process_loan_interest_accrual_for_demand_loans(posting_date=add_days(last_date, 1)) # Paid 511095.89 amount includes 5,00,000 principal amount and 11095.89 interest amount repayment_entry = create_repayment_entry(loan.name, self.applicant, add_days(get_last_day(nowdate()), 5), diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json index 33f496fc3e..a26112011c 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.json @@ -23,6 +23,7 @@ "interest_amount", "section_break_15", "process_loan_interest_accrual", + "repayment_schedule_name", "amended_from" ], "fields": [ @@ -135,12 +136,19 @@ { "fieldname": "column_break_14", "fieldtype": "Column Break" + }, + { + "fieldname": "repayment_schedule_name", + "fieldtype": "Data", + "hidden": 1, + "label": "Repayment Schedule Name", + "read_only": 1 } ], "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2020-02-07 01:22:06.924125", + "modified": "2020-04-10 18:31:02.369857", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Interest Accrual", diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index b8e6dabba7..698b42409b 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -27,8 +27,14 @@ class LoanInterestAccrual(AccountsController): self.make_gl_entries() def on_cancel(self): + if self.repayment_schedule_name: + self.update_is_accrued() + self.make_gl_entries(cancel=1) + def update_is_accrued(self): + frappe.db.set_value('Repayment Schedule', self.repayment_schedule_name, 'is_accrued', 0) + def make_gl_entries(self, cancel=0, adv_adj=0): gle_map = [] @@ -83,9 +89,19 @@ def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_i interest_per_day = (pending_principal_amount * loan.rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100) payable_interest = interest_per_day * no_of_days - make_loan_interest_accrual_entry(loan.name, loan.applicant_type, loan.applicant,loan.interest_income_account, - loan.loan_account, pending_principal_amount, payable_interest, process_loan_interest = process_loan_interest, - posting_date=posting_date) + args = frappe._dict({ + 'loan': loan.name, + 'applicant_type': loan.applicant_type, + 'applicant': loan.applicant, + 'interest_income_account': loan.interest_income_account, + 'loan_account': loan.loan_acccount, + 'pending_principal_amount': pending_principal_amount, + 'interest_amount': payable_interest, + 'process_loan_interest': process_loan_interest, + 'posting_date': posting_date + }) + + make_loan_interest_accrual_entry(args) def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_interest, open_loans=None, loan_type=None): query_filters = { @@ -107,49 +123,71 @@ def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_inte for loan in open_loans: calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest) -def make_accrual_interest_entry_for_term_loans(posting_date=None): +def make_accrual_interest_entry_for_term_loans(posting_date, process_loan_interest, term_loan=None, loan_type=None): curr_date = posting_date or add_days(nowdate(), 1) - term_loans = frappe.db.sql("""SELECT l.name, l.total_payment, l.total_amount_paid, l.loan_account, - l.interest_income_account, l.is_term_loan, l.disbursement_date, l.applicant_type, l.applicant, - l.rate_of_interest, l.total_interest_payable, l.repayment_start_date, rs.name as payment_entry, - rs.payment_date, rs.principal_amount, rs.interest_amount, rs.is_accrued , rs.balance_loan_amount - FROM `tabLoan` l, `tabRepayment Schedule` rs - WHERE rs.parent = l.name - AND l.docstatus=1 - AND l.is_term_loan =1 - AND rs.payment_date <= %s - AND rs.is_accrued=0 - AND l.status = 'Disbursed'""", (curr_date), as_dict=1) + term_loans = get_term_loans(curr_date, term_loan, loan_type) accrued_entries = [] for loan in term_loans: accrued_entries.append(loan.payment_entry) - make_loan_interest_accrual_entry(loan.name, loan.applicant_type, loan.applicant,loan.interest_income_account, - loan.loan_account, loan.principal_amount + loan.balance_loan_amount, loan.interest_amount, - payable_principal = loan.principal_amount , posting_date=posting_date) + args = frappe._dict({ + 'loan': loan.name, + 'applicant_type': loan.applicant_type, + 'applicant': loan.applicant, + 'interest_income_account': loan.interest_income_account, + 'loan_account': loan.loan_acccount, + 'interest_amount': loan.interest_amount, + 'payable_principal': loan.principal_amount, + 'process_loan_interest': process_loan_interest, + 'repayment_schedule_name': loan.payment_entry, + 'posting_date': posting_date + }) + + make_loan_interest_accrual_entry(args) if accrued_entries: frappe.db.sql("""UPDATE `tabRepayment Schedule` SET is_accrued = 1 where name in (%s)""" #nosec % ", ".join(['%s']*len(accrued_entries)), tuple(accrued_entries)) -def make_loan_interest_accrual_entry(loan, applicant_type, applicant, interest_income_account, loan_account, - pending_principal_amount, interest_amount, payable_principal=None, process_loan_interest=None, posting_date=None): - loan_interest_accrual = frappe.new_doc("Loan Interest Accrual") - loan_interest_accrual.loan = loan - loan_interest_accrual.applicant_type = applicant_type - loan_interest_accrual.applicant = applicant - loan_interest_accrual.interest_income_account = interest_income_account - loan_interest_accrual.loan_account = loan_account - loan_interest_accrual.pending_principal_amount = flt(pending_principal_amount, 2) - loan_interest_accrual.interest_amount = flt(interest_amount, 2) - loan_interest_accrual.posting_date = posting_date or nowdate() - loan_interest_accrual.process_loan_interest_accrual = process_loan_interest +def get_term_loans(date, term_loan=None, loan_type=None): + condition = '' - if payable_principal: - loan_interest_accrual.payable_principal_amount = payable_principal + if term_loan: + condition +=' AND l.name = %s' % frappe.db.escape(term_loan) + + if loan_type: + condition += ' AND l.loan_type = %s' % frappe.db.escape(loan_type) + + term_loans = frappe.db.sql("""SELECT l.name, l.total_payment, l.total_amount_paid, l.loan_account, + l.interest_income_account, l.is_term_loan, l.disbursement_date, l.applicant_type, l.applicant, + l.rate_of_interest, l.total_interest_payable, l.repayment_start_date, rs.name as payment_entry, + rs.payment_date, rs.principal_amount, rs.interest_amount, rs.is_accrued , rs.balance_loan_amount + FROM `tabLoan` l, `tabRepayment Schedule` rs + WHERE rs.parent = l.name + AND l.docstatus=1 + AND l.is_term_loan =1 + AND rs.payment_date <= %s + AND rs.is_accrued=0 {0} + AND l.status = 'Disbursed'""".format(condition), (getdate(date)), as_dict=1) + + 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 loan_interest_accrual.save() loan_interest_accrual.submit() diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py index e681ae42c3..dd4ca367c5 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py @@ -7,7 +7,7 @@ import unittest from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date) from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_loan_security_price, make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan) -from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year) from erpnext.selling.doctype.customer.test_customer import get_customer_dict @@ -54,7 +54,7 @@ class TestLoanInterestAccrual(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date) - process_loan_interest_accrual(posting_date=last_date) + process_loan_interest_accrual_for_demand_loans(posting_date=last_date) loan_interest_accural = frappe.get_doc("Loan Interest Accrual", {'loan': loan.name}) diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json index 7f79cb1fd9..0ef098f278 100644 --- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json +++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.json @@ -9,6 +9,7 @@ "posting_date", "loan_type", "loan", + "process_type", "amended_from" ], "fields": [ @@ -39,11 +40,18 @@ "fieldtype": "Link", "label": "Loan ", "options": "Loan" + }, + { + "fieldname": "process_type", + "fieldtype": "Data", + "hidden": 1, + "label": "Process Type", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-02-01 08:14:33.978636", + "modified": "2020-04-09 22:52:53.911416", "modified_by": "Administrator", "module": "Loan Management", "name": "Process Loan Interest Accrual", @@ -74,7 +82,6 @@ "write": 1 } ], - "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py index 0f33da918d..cd3cf7ec96 100644 --- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py @@ -6,7 +6,8 @@ from __future__ import unicode_literals import frappe from frappe.utils import nowdate from frappe.model.document import Document -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import make_accrual_interest_entry_for_demand_loans +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_demand_loans, + make_accrual_interest_entry_for_term_loans) class ProcessLoanInterestAccrual(Document): def on_submit(self): @@ -14,16 +15,45 @@ class ProcessLoanInterestAccrual(Document): if self.loan: loan_doc = frappe.get_doc('Loan', self.loan) - open_loans.append(loan_doc) + if loan_doc: + open_loans.append(loan_doc) - make_accrual_interest_entry_for_demand_loans(self.posting_date, self.name, - open_loans = open_loans, loan_type = self.loan_type) + if (not self.loan or not loan_doc.is_term_loan) and self.process_type != 'Term Loans': + make_accrual_interest_entry_for_demand_loans(self.posting_date, self.name, + open_loans = open_loans, loan_type = self.loan_type) -def process_loan_interest_accrual(posting_date=None, loan_type=None, loan=None): + if (not self.loan or loan_doc.is_term_loan) and self.process_type != 'Demand Loans': + make_accrual_interest_entry_for_term_loans(self.posting_date, self.name, term_loan=self.loan, + loan_type=self.loan_type) + + +def process_loan_interest_accrual_for_demand_loans(posting_date=None, loan_type=None, loan=None): loan_process = frappe.new_doc('Process Loan Interest Accrual') loan_process.posting_date = posting_date or nowdate() loan_process.loan_type = loan_type + loan_process.process_type = 'Demand Loans' loan_process.loan = loan loan_process.submit() +def process_loan_interest_accrual_for_term_loans(posting_date=None, loan_type=None, loan=None): + + if not term_loan_accrual_pending(posting_date or nowdate()): + return + + loan_process = frappe.new_doc('Process Loan Interest Accrual') + loan_process.posting_date = posting_date or nowdate() + loan_process.loan_type = loan_type + loan_process.process_type = 'Term Loans' + loan_process.loan = loan + + loan_process.submit() + +def term_loan_accrual_pending(date): + pending_accrual = frappe.db.get_value('Repayment Schedule', { + 'payment_date': ('<=', date), + 'is_accrued': 0 + }) + + return pending_accrual + diff --git a/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json b/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json index ce020fff07..f7e211656e 100644 --- a/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json +++ b/erpnext/loan_management/doctype/salary_slip_loan/salary_slip_loan.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-08-29 18:11:36.829526", "doctype": "DocType", "editable_grid": 1, @@ -49,16 +50,14 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Principal Amount", - "options": "Company:company:default_currency", - "read_only": 1 + "options": "Company:company:default_currency" }, { "fieldname": "interest_amount", "fieldtype": "Currency", "in_list_view": 1, "label": "Interest Amount", - "options": "Company:company:default_currency", - "read_only": 1 + "options": "Company:company:default_currency" }, { "fieldname": "total_payment", @@ -79,11 +78,13 @@ "fieldname": "loan_type", "fieldtype": "Link", "label": "Loan Type", - "options": "Loan Type" + "options": "Loan Type", + "read_only": 1 } ], "istable": 1, - "modified": "2019-10-28 09:15:31.174244", + "links": [], + "modified": "2020-04-09 20:01:53.546364", "modified_by": "Administrator", "module": "Loan Management", "name": "Salary Slip Loan", From af27d61bee3d2b6ad6a29c8219549c957900c3a9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 13 Apr 2020 12:29:00 +0530 Subject: [PATCH 2/4] fix: Employee loan test case and other fixes --- erpnext/hooks.py | 2 +- .../payroll_entry/test_payroll_entry.py | 4 +- erpnext/hr/doctype/salary_slip/salary_slip.py | 45 ++++++++++++------- .../doctype/salary_slip/test_salary_slip.py | 4 +- .../loan_management/doctype/loan/test_loan.py | 7 ++- .../test_loan_disbursement.py | 2 +- .../loan_interest_accrual.py | 4 +- .../test_loan_interest_accrual.py | 2 +- .../doctype/loan_repayment/loan_repayment.py | 11 +++-- .../loan_security_shortfall.py | 9 +--- .../doctype/loan_type/loan_type.py | 3 ++ 11 files changed, 52 insertions(+), 41 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index f1881d97df..6d02a277a0 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -311,7 +311,7 @@ scheduler_events = { "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms", "erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation", "erpnext.hr.utils.generate_leave_encashment", - "erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall.check_for_ltv_shortfall", + "erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall.create_process_loan_security_shortfall", "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_term_loans" ], "monthly_long": [ diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py index 35f5a57a1c..49671d5e22 100644 --- a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py @@ -13,7 +13,7 @@ from erpnext.hr.doctype.salary_slip.test_salary_slip import get_salary_component make_earning_salary_component, make_deduction_salary_component from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import make_accrual_interest_entry_for_term_loans +from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans class TestPayrollEntry(unittest.TestCase): def setUp(self): @@ -81,7 +81,7 @@ class TestPayrollEntry(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1)) - make_accrual_interest_entry_for_term_loans(posting_date=nowdate()) + process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) dates = get_start_end_dates('Monthly', nowdate()) diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index d03a3dd9a3..4d48fdfbd2 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -755,30 +755,43 @@ class SalarySlip(TransactionBase): d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0] def set_loan_repayment(self): - self.set('loans', []) self.total_loan_repayment = 0 self.total_interest_amount = 0 self.total_principal_amount = 0 - for loan in self.get_loan_details(): + if not self.get('loans'): + for loan in self.get_loan_details(): - amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment") + amounts = calculate_amounts(loan.name, self.posting_date, "Regular Payment") - total_payment = amounts['interest_amount'] + amounts['payable_principal_amount'] + if amounts['interest_amount'] or amounts['payable_principal_amount']: + self.append('loans', { + 'loan': loan.name, + 'total_payment': amounts['interest_amount'] + amounts['payable_principal_amount'], + 'interest_amount': amounts['interest_amount'], + 'principal_amount': amounts['payable_principal_amount'], + 'loan_account': loan.loan_account, + 'interest_income_account': loan.interest_income_account + }) - if total_payment: - self.append('loans', { - 'loan': loan.name, - 'total_payment': total_payment, - 'interest_amount': amounts['interest_amount'], - 'principal_amount': amounts['payable_principal_amount'], - 'loan_account': loan.loan_account, - 'interest_income_account': loan.interest_income_account - }) + for payment in self.get('loans'): + amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment") - self.total_loan_repayment += total_payment - self.total_interest_amount += amounts['interest_amount'] - self.total_principal_amount += amounts['payable_principal_amount'] + if payment.interest_amount > amounts['interest_amount']: + frappe.throw(_("""Row {0}: Paid Interest amount {1} is greater than pending interest amount {2} + against loan {3}""").format(payment.idx, frappe.bold(payment.interest_amount), + frappe.bold(amounts['interest_amount']), frappe.bold(payment.loan))) + + if payment.principal_amount > amounts['payable_principal_amount']: + frappe.throw(_("""Row {0}: Paid Principal amount {1} is greater than pending principal amount {2} + against loan {3}""").format(payment.idx, frappe.bold(payment.principal_amount), + frappe.bold(amounts['payable_principal_amount']), frappe.bold(payment.loan))) + + payment.total_payment = payment.interest_amount + payment.principal_amount + self.total_interest_amount += payment.interest_amount + self.total_principal_amount += payment.principal_amount + + self.total_loan_repayment = self.total_interest_amount + self.total_principal_amount def get_loan_details(self): diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index 9acfd1f5c6..ecccac7d41 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -146,7 +146,7 @@ class TestSalarySlip(unittest.TestCase): def test_loan_repayment_salary_slip(self): from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan, make_loan_disbursement_entry, create_loan_accounts - from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import make_accrual_interest_entry_for_term_loans + from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans applicant = make_employee("test_loanemployee@salary.com", company="_Test Company") @@ -166,7 +166,7 @@ class TestSalarySlip(unittest.TestCase): make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1)) - make_accrual_interest_entry_for_term_loans(posting_date=nowdate()) + process_loan_interest_accrual_for_term_loans(posting_date=nowdate()) ss = make_employee_salary_slip("test_loanemployee@salary.com", "Monthly") ss.submit() diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 34b801b3be..108672b25a 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -12,9 +12,8 @@ from erpnext.selling.doctype.customer.test_customer import get_customer_dict from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (process_loan_interest_accrual_for_demand_loans, process_loan_interest_accrual_for_term_loans) -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year) - -from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import check_for_ltv_shortfall +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year +from erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall import create_process_loan_security_shortfall class TestLoan(unittest.TestCase): def setUp(self): @@ -298,7 +297,7 @@ class TestLoan(unittest.TestCase): frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 100 where loan_security='Test Security 2'""") - check_for_ltv_shortfall() + create_process_loan_security_shortfall() loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name}) self.assertTrue(loan_security_shortfall) diff --git a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py index 01290d400a..189b2f5aff 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py @@ -8,7 +8,7 @@ from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_la from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_repayment_entry, make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_security_price) from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year) +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year from erpnext.selling.doctype.customer.test_customer import get_customer_dict class TestLoanDisbursement(unittest.TestCase): diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 698b42409b..094b9c698c 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -94,7 +94,7 @@ def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_i 'applicant_type': loan.applicant_type, 'applicant': loan.applicant, 'interest_income_account': loan.interest_income_account, - 'loan_account': loan.loan_acccount, + 'loan_account': loan.loan_account, 'pending_principal_amount': pending_principal_amount, 'interest_amount': payable_interest, 'process_loan_interest': process_loan_interest, @@ -137,7 +137,7 @@ def make_accrual_interest_entry_for_term_loans(posting_date, process_loan_intere 'applicant_type': loan.applicant_type, 'applicant': loan.applicant, 'interest_income_account': loan.interest_income_account, - 'loan_account': loan.loan_acccount, + 'loan_account': loan.loan_account, 'interest_amount': loan.interest_amount, 'payable_principal': loan.principal_amount, 'process_loan_interest': process_loan_interest, diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py index dd4ca367c5..2f19ca1022 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py @@ -8,7 +8,7 @@ from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_la from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_loan_security_price, make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan) from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year) +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_years from erpnext.selling.doctype.customer.test_customer import get_customer_dict class TestLoanInterestAccrual(unittest.TestCase): diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index a70e312880..2d2ca4c2f4 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -210,7 +210,7 @@ class LoanRepayment(AccountsController): ) if gle_map: - make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj) + make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj, merge_entries=False) def create_repayment_entry(loan, applicant, company, posting_date, loan_type, payment_type, interest_payable, payable_principal_amount, amount_paid, penalty_amount=None): @@ -223,7 +223,7 @@ def create_repayment_entry(loan, applicant, company, posting_date, loan_type, "posting_date": posting_date, "applicant": applicant, "penalty_amount": penalty_amount, - "interets_payable": interest_payable, + "interst_payable": interest_payable, "payable_principal_amount": payable_principal_amount, "amount_paid": amount_paid, "loan_type": loan_type @@ -236,7 +236,8 @@ def get_accrued_interest_entries(against_loan): fields=["name", "interest_amount", "posting_date", "payable_principal_amount"], filters = { "loan": against_loan, - "is_paid": 0 + "is_paid": 0, + "docstatus": 1 }, order_by="posting_date") return accrued_interest_entries @@ -272,7 +273,9 @@ def get_amounts(amounts, against_loan, posting_date, payment_type): total_pending_interest += entry.interest_amount payable_principal_amount += entry.payable_principal_amount - pending_accrual_entries.setdefault(entry.name, entry.interest_amount) + pending_accrual_entries.setdefault(entry.name, + flt(entry.interest_amount) + flt(entry.payable_principal_amount)) + final_due_date = due_date pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid - against_loan_doc.total_interest_payable diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py index b7be84f836..ab040f1d33 100644 --- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py +++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py @@ -37,17 +37,10 @@ def add_security(loan): return loan_security_pledge.as_dict() -def check_for_ltv_shortfall(process_loan_security_shortfall=None): +def check_for_ltv_shortfall(process_loan_security_shortfall): update_time = get_datetime() - if not process_loan_security_shortfall: - process = frappe.new_doc("Process Loan Security Shortfall") - process.update_time = update_time - process.submit() - - process_loan_security_shortfall = process.name - loan_security_price_map = frappe._dict(frappe.get_all("Loan Security Price", fields=["loan_security", "loan_security_price"], filters = { diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.py b/erpnext/loan_management/doctype/loan_type/loan_type.py index 14b18ab57a..208cb19c88 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type.py +++ b/erpnext/loan_management/doctype/loan_type/loan_type.py @@ -19,3 +19,6 @@ class LoanType(Document): frappe.throw(_("Account {0} does not belong to company {1}").format(frappe.bold(self.get(fieldname)), frappe.bold(self.company))) + if self.get('loan_account') == self.get('payment_account'): + frappe.throw(_('Loan Account and Payment Account cannot be same')) + From baa7521d0252d596c126e91281f20c05eaf8bdb9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 13 Apr 2020 13:18:11 +0530 Subject: [PATCH 3/4] fix: Allow repayment from Salary only for term loans --- erpnext/loan_management/doctype/loan/loan.json | 6 ++++-- erpnext/loan_management/doctype/loan/loan.py | 5 +++++ .../process_loan_security_shortfall.py | 11 ++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json index 2834e5b655..b04e82274e 100644 --- a/erpnext/loan_management/doctype/loan/loan.json +++ b/erpnext/loan_management/doctype/loan/loan.json @@ -126,7 +126,7 @@ "depends_on": "eval:doc.applicant_type==\"Employee\"", "fieldname": "repay_from_salary", "fieldtype": "Check", - "label": "Repay from Salary" + "label": "Repay From Salary" }, { "fieldname": "section_break_8", @@ -178,6 +178,8 @@ }, { "depends_on": "is_term_loan", + "fetch_from": "loan_application.repayment_amount", + "fetch_if_empty": 1, "fieldname": "monthly_repayment_amount", "fieldtype": "Currency", "label": "Monthly Repayment Amount", @@ -350,7 +352,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-02-07 01:31:25.172173", + "modified": "2020-04-13 13:16:10.192624", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index eb5f127aa9..c7a2fba878 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -19,6 +19,7 @@ class Loan(AccountsController): self.validate_loan_security_pledge() self.validate_loan_amount() self.check_sanctioned_amount_limit() + self.validate_repay_from_salary() if self.is_term_loan: validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, @@ -77,6 +78,10 @@ class Loan(AccountsController): 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 validate_repay_from_salary(self): + if not self.is_term_loan and self.repay_from_salary: + frappe.throw(_("Repay From Salary can be selected only for term loans")) + def make_repayment_schedule(self): if not self.repayment_start_date: diff --git a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py index 417e3678c9..b4aad25ac8 100644 --- a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py +++ b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py @@ -14,4 +14,13 @@ class ProcessLoanSecurityShortfall(Document): self.set_onload('update_time', get_datetime()) def on_submit(self): - check_for_ltv_shortfall(process_loan_security_shortfall = self.name) + check_for_ltv_shortfall(self.name) + +def create_process_loan_security_shortfall(): + if check_for_secured_loans(): + process = frappe.new_doc("Process Loan Security Shortfall") + process.update_time = get_datetime() + process.submit() + +def check_for_secured_loans(): + return frappe.db.count('Loan', {'docstatus': 1, 'is_secured_loan': 1}) From 40dcfd148b99ac17884f35af4d93ebf3a9a4d304 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 13 Apr 2020 16:29:58 +0530 Subject: [PATCH 4/4] fix: Import Error --- .../doctype/loan_interest_accrual/test_loan_interest_accrual.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py index 2f19ca1022..2afed08e18 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py @@ -8,7 +8,7 @@ from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_la from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_loan_security_price, make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan) from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans -from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_years +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year from erpnext.selling.doctype.customer.test_customer import get_customer_dict class TestLoanInterestAccrual(unittest.TestCase):