diff --git a/lending/loan_management/doctype/loan/loan.js b/lending/loan_management/doctype/loan/loan.js index 16904526..42afb2a8 100644 --- a/lending/loan_management/doctype/loan/loan.js +++ b/lending/loan_management/doctype/loan/loan.js @@ -106,6 +106,11 @@ frappe.ui.form.on('Loan', { frm.trigger("toggle_fields"); }, + is_term_loan: function(frm) { + frm.doc.repayment_method = frm.doc.repayment_schedule_type = ""; + frm.doc.monthly_repayment_amount = frm.doc.repayment_periods = ""; + }, + repayment_schedule_type: function(frm) { if (frm.doc.repayment_schedule_type == "Pro-rated calendar months") { frm.set_df_property("repayment_start_date", "label", "Interest Calculation Start Date"); @@ -114,13 +119,6 @@ frappe.ui.form.on('Loan', { } }, - loan_type: function(frm) { - frm.toggle_reqd("repayment_method", frm.doc.is_term_loan); - frm.toggle_display("repayment_method", frm.doc.is_term_loan); - frm.toggle_display("repayment_periods", frm.doc.is_term_loan); - }, - - make_loan_disbursement: function (frm) { frappe.call({ args: { @@ -244,7 +242,7 @@ frappe.ui.form.on('Loan', { callback: function (r) { if (!r.exc && r.message) { - let loan_fields = ["loan_type", "loan_amount", "repayment_method", + let loan_fields = ["loan_type", "loan_amount", "repayment_method", "repayment_round_up", "monthly_repayment_amount", "repayment_periods", "rate_of_interest", "is_secured_loan"] loan_fields.forEach(field => { @@ -268,13 +266,4 @@ frappe.ui.form.on('Loan', { }); } }, - - repayment_method: function (frm) { - frm.trigger("toggle_fields") - }, - - toggle_fields: function (frm) { - frm.toggle_enable("monthly_repayment_amount", frm.doc.repayment_method == "Repay Fixed Amount per Period") - frm.toggle_enable("repayment_periods", frm.doc.repayment_method == "Repay Over Number of Periods") - } -}); \ No newline at end of file +}); diff --git a/lending/loan_management/doctype/loan/loan.json b/lending/loan_management/doctype/loan/loan.json index 8391d1de..4b501771 100644 --- a/lending/loan_management/doctype/loan/loan.json +++ b/lending/loan_management/doctype/loan/loan.json @@ -30,6 +30,7 @@ "maximum_loan_amount", "repayment_method", "repayment_periods", + "repayment_round_up", "monthly_repayment_amount", "repayment_start_date", "is_term_loan", @@ -178,13 +179,23 @@ "fieldname": "repayment_method", "fieldtype": "Select", "label": "Repayment Method", - "options": "\nRepay Fixed Amount per Period\nRepay Over Number of Periods" + "mandatory_depends_on": "is_term_loan", + "options": "Repay Fixed Amount per Period\nRepay Over Number of Periods" }, { "depends_on": "is_term_loan", "fieldname": "repayment_periods", "fieldtype": "Int", - "label": "Repayment Period in Months" + "label": "Repayment Period in Months", + "mandatory_depends_on": "eval: doc.repayment_method == 'Repay Over Number of Periods'", + "read_only_depends_on": "eval: doc.repayment_method != 'Repay Over Number of Periods'" + }, + { + "default": "0", + "depends_on": "eval: doc.repayment_method == 'Repay Over Number of Periods'", + "fieldname": "repayment_round_up", + "fieldtype": "Check", + "label": "Round Up" }, { "depends_on": "is_term_loan", @@ -193,7 +204,9 @@ "fieldname": "monthly_repayment_amount", "fieldtype": "Currency", "label": "Monthly Repayment Amount", - "options": "Company:company:default_currency" + "mandatory_depends_on": "eval: doc.repayment_method == 'Repay Fixed Amount per Period'", + "options": "Company:company:default_currency", + "read_only_depends_on": "eval: doc.repayment_method != 'Repay Fixed Amount per Period'" }, { "collapsible": 1, diff --git a/lending/loan_management/doctype/loan/loan.py b/lending/loan_management/doctype/loan/loan.py index a704d1ca..11ffb5c1 100644 --- a/lending/loan_management/doctype/loan/loan.py +++ b/lending/loan_management/doctype/loan/loan.py @@ -20,9 +20,11 @@ class Loan(AccountsController): def validate(self): - self.set_loan_amount() + if self.docstatus.is_draft(): + self.set_missing_fields() + self.set_loan_amount() + self.validate_loan_amount() - self.set_missing_fields() self.validate_cost_center() self.validate_accounts() self.check_sanctioned_amount_limit() @@ -31,8 +33,11 @@ def validate(self): if self.is_term_loan and not self.is_new(): self.update_draft_schedule() - if not self.is_term_loan or (self.is_term_loan and not self.is_new()): - self.calculate_totals() + if self.docstatus.is_draft(): + if self.is_term_loan and not self.is_new(): + self.update_draft_schedule() + if not self.is_term_loan or (self.is_term_loan and not self.is_new()): + self.calculate_totals() def after_insert(self): if self.is_term_loan: @@ -129,6 +134,7 @@ def make_draft_schedule(self): "repayment_method": self.repayment_method, "repayment_start_date": self.repayment_start_date, "repayment_periods": self.repayment_periods, + "repayment_round_up": self.repayment_round_up, "loan_amount": self.loan_amount, "monthly_repayment_amount": self.monthly_repayment_amount, "loan_type": self.loan_type, @@ -150,6 +156,7 @@ def update_draft_schedule(self): "repayment_periods": self.repayment_periods, "repayment_method": self.repayment_method, "repayment_start_date": self.repayment_start_date, + "repayment_round_up": self.repayment_round_up, "posting_date": self.posting_date, "loan_amount": self.loan_amount, "monthly_repayment_amount": self.monthly_repayment_amount, diff --git a/lending/loan_management/doctype/loan_application/loan_application.js b/lending/loan_management/doctype/loan_application/loan_application.js index ffef3e7a..4adedf72 100644 --- a/lending/loan_management/doctype/loan_application/loan_application.js +++ b/lending/loan_management/doctype/loan_application/loan_application.js @@ -12,7 +12,6 @@ frappe.ui.form.on('Loan Application', { } }, refresh: function(frm) { - frm.trigger("toggle_fields"); frm.trigger("add_toolbar_buttons"); frm.set_query('loan_type', () => { return { @@ -22,18 +21,12 @@ frappe.ui.form.on('Loan Application', { }; }); }, - repayment_method: function(frm) { + is_term_loan: function(frm) { + frm.doc.repayment_method = ""; frm.doc.repayment_amount = frm.doc.repayment_periods = ""; - frm.trigger("toggle_fields"); - frm.trigger("toggle_required"); }, - toggle_fields: function(frm) { - frm.toggle_enable("repayment_amount", frm.doc.repayment_method=="Repay Fixed Amount per Period") - frm.toggle_enable("repayment_periods", frm.doc.repayment_method=="Repay Over Number of Periods") - }, - toggle_required: function(frm){ - frm.toggle_reqd("repayment_amount", cint(frm.doc.repayment_method=='Repay Fixed Amount per Period')) - frm.toggle_reqd("repayment_periods", cint(frm.doc.repayment_method=='Repay Over Number of Periods')) + repayment_method: function(frm) { + frm.doc.repayment_amount = frm.doc.repayment_periods = ""; }, add_toolbar_buttons: function(frm) { if (frm.doc.status == "Approved") { @@ -85,10 +78,6 @@ frappe.ui.form.on('Loan Application', { } }) }, - is_term_loan: function(frm) { - frm.set_df_property('repayment_method', 'hidden', 1 - frm.doc.is_term_loan); - frm.set_df_property('repayment_method', 'reqd', frm.doc.is_term_loan); - }, is_secured_loan: function(frm) { frm.set_df_property('proposed_pledges', 'reqd', frm.doc.is_secured_loan); }, diff --git a/lending/loan_management/doctype/loan_application/loan_application.json b/lending/loan_management/doctype/loan_application/loan_application.json index f91fa072..a801795a 100644 --- a/lending/loan_management/doctype/loan_application/loan_application.json +++ b/lending/loan_management/doctype/loan_application/loan_application.json @@ -1,7 +1,7 @@ { "actions": [], "autoname": "ACC-LOAP-.YYYY.-.#####", - "creation": "2019-08-29 17:46:49.201740", + "creation": "2023-09-18 18:58:25.087137", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", @@ -26,10 +26,11 @@ "maximum_loan_amount", "repayment_info", "repayment_method", - "total_payable_amount", - "column_break_11", "repayment_periods", + "repayment_round_up", "repayment_amount", + "column_break_11", + "total_payable_amount", "total_payable_interest", "amended_from" ], @@ -127,7 +128,8 @@ "fieldname": "repayment_method", "fieldtype": "Select", "label": "Repayment Method", - "options": "\nRepay Fixed Amount per Period\nRepay Over Number of Periods" + "mandatory_depends_on": "eval: doc.is_term_loan == 1", + "options": "Repay Fixed Amount per Period\nRepay Over Number of Periods" }, { "fetch_from": "loan_type.rate_of_interest", @@ -149,17 +151,26 @@ "fieldtype": "Column Break" }, { - "depends_on": "repayment_method", "fieldname": "repayment_amount", "fieldtype": "Currency", "label": "Monthly Repayment Amount", - "options": "Company:company:default_currency" + "mandatory_depends_on": "eval: doc.repayment_method == 'Repay Fixed Amount per Period'", + "options": "Company:company:default_currency", + "read_only_depends_on": "eval: doc.repayment_method != 'Repay Fixed Amount per Period'" + }, + { + "default": "0", + "depends_on": "eval: doc.repayment_method == 'Repay Over Number of Periods'", + "fieldname": "repayment_round_up", + "fieldtype": "Check", + "label": "Round Up" }, { - "depends_on": "repayment_method", "fieldname": "repayment_periods", "fieldtype": "Int", - "label": "Repayment Period in Months" + "label": "Repayment Period in Months", + "mandatory_depends_on": "eval: doc.repayment_method == 'Repay Over Number of Periods'", + "read_only_depends_on": "eval: doc.repayment_method != 'Repay Over Number of Periods'" }, { "fieldname": "total_payable_amount", @@ -215,7 +226,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-04-19 18:24:40.119647", + "modified": "2023-09-18 19:35:16.307804", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Application", @@ -276,6 +287,7 @@ "search_fields": "applicant_type, applicant, loan_type, loan_amount", "sort_field": "modified", "sort_order": "DESC", + "states": [], "timeline_field": "applicant", "title_field": "applicant", "track_changes": 1 diff --git a/lending/loan_management/doctype/loan_application/loan_application.py b/lending/loan_management/doctype/loan_application/loan_application.py index f09be506..3ddc5418 100644 --- a/lending/loan_management/doctype/loan_application/loan_application.py +++ b/lending/loan_management/doctype/loan_application/loan_application.py @@ -25,25 +25,23 @@ class LoanApplication(Document): def validate(self): - self.set_pledge_amount() - self.set_loan_amount() + if self.docstatus.is_draft(): + self.set_pledge_amount() + self.set_loan_amount() + + self.validate_loan_type() self.validate_loan_amount() if self.is_term_loan: self.validate_repayment_method() - self.validate_loan_type() + if self.docstatus.is_draft(): + self.get_repayment_details() - self.get_repayment_details() self.check_sanctioned_amount_limit() def validate_repayment_method(self): - if self.repayment_method == "Repay Over Number of Periods" and not self.repayment_periods: - frappe.throw(_("Please enter Repayment Periods")) - if self.repayment_method == "Repay Fixed Amount per Period": - if not self.repayment_amount: - frappe.throw(_("Please enter repayment Amount")) if self.repayment_amount > self.loan_amount: frappe.throw(_("Monthly Repayment Amount cannot be greater than Loan Amount")) @@ -106,9 +104,10 @@ def get_repayment_details(self): if self.is_term_loan: if self.repayment_method == "Repay Over Number of Periods": - self.repayment_amount = get_monthly_repayment_amount( + amount = get_monthly_repayment_amount( self.loan_amount, self.rate_of_interest, self.repayment_periods ) + self.repayment_amount = math.ceil(amount) if self.repayment_round_up else amount if self.repayment_method == "Repay Fixed Amount per Period": monthly_interest_rate = flt(self.rate_of_interest) / (12 * 100) @@ -133,8 +132,8 @@ def calculate_payable_amount(self): self.total_payable_interest = 0 while balance_amount > 0: - interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12 * 100)) - balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount) + interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12 * 100), 2) + balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount, 2) self.total_payable_interest += interest_amount diff --git a/lending/loan_management/doctype/loan_repayment_schedule/loan_repayment_schedule.json b/lending/loan_management/doctype/loan_repayment_schedule/loan_repayment_schedule.json index dd4e856e..2d33e9e2 100644 --- a/lending/loan_management/doctype/loan_repayment_schedule/loan_repayment_schedule.json +++ b/lending/loan_management/doctype/loan_repayment_schedule/loan_repayment_schedule.json @@ -18,7 +18,7 @@ "loan_type", "repayment_schedule_type", "repayment_method", - "repayment_periods", + "repayment_round_up", "monthly_repayment_amount", "repayment_start_date", "section_break_6rpg", @@ -72,6 +72,13 @@ "label": "Repayment Method", "options": "\nRepay Fixed Amount per Period\nRepay Over Number of Periods" }, + { + "default": "0", + "depends_on": "eval: doc.repayment_method == 'Repay Over Number of Periods'", + "fieldname": "repayment_round_up", + "fieldtype": "Check", + "label": "Round Up" + }, { "fieldname": "monthly_repayment_amount", "fieldtype": "Currency", @@ -184,4 +191,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/lending/loan_management/doctype/loan_repayment_schedule/loan_repayment_schedule.py b/lending/loan_management/doctype/loan_repayment_schedule/loan_repayment_schedule.py index 13e4f727..87b54578 100644 --- a/lending/loan_management/doctype/loan_repayment_schedule/loan_repayment_schedule.py +++ b/lending/loan_management/doctype/loan_repayment_schedule/loan_repayment_schedule.py @@ -17,9 +17,10 @@ def validate(self): def set_missing_fields(self): if self.repayment_method == "Repay Over Number of Periods": - self.monthly_repayment_amount = get_monthly_repayment_amount( + amount = get_monthly_repayment_amount( self.loan_amount, self.rate_of_interest, self.repayment_periods ) + self.monthly_repayment_amount = math.ceil(amount) if self.repayment_round_up else amount def set_repayment_period(self): if self.repayment_method == "Repay Fixed Amount per Period": @@ -167,13 +168,12 @@ def add_single_month(date): return add_months(date, 1) -def get_monthly_repayment_amount(loan_amount, rate_of_interest, repayment_periods): - if rate_of_interest: - monthly_interest_rate = flt(rate_of_interest) / (12 * 100) - monthly_repayment_amount = math.ceil( - (loan_amount * monthly_interest_rate * (1 + monthly_interest_rate) ** repayment_periods) - / ((1 + monthly_interest_rate) ** repayment_periods - 1) +def get_monthly_repayment_amount(loan_amount, yearly_intrate, periods): + if yearly_intrate: + monthly_intrate = yearly_intrate / (12 * 100) + annuity_factor = (monthly_intrate * (1 + monthly_intrate) ** periods) / ( + (1 + monthly_intrate) ** periods - 1 ) + return loan_amount * annuity_factor else: - monthly_repayment_amount = math.ceil(flt(loan_amount) / repayment_periods) - return monthly_repayment_amount + return loan_amount / periods