From f7e216e285dd01f77f8536182b159fc6ddc26866 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 10 Apr 2020 20:03:11 +0530 Subject: [PATCH] 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 b2dc96178d8..f1881d97dfc 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 e73d41a28ed..097d3a096b0 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 8b220171e8a..9cd8b2e90a9 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 696410b7bb5..eb5f127aa93 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 759b0d8e094..34b801b3bef 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 57050d86c61..aba5f4260c4 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 72a4ddcc8fe..2d9c45d1225 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 fa7db2d565a..2918486ebdd 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 968e377fcc5..01290d400a3 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 33f496fc3ed..a26112011ca 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 b8e6dabba7a..698b42409ba 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 e681ae42c34..dd4ca367c58 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 7f79cb1fd93..0ef098f278f 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 0f33da918d2..cd3cf7ec96b 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 ce020fff07d..f7e211656e7 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",