Merge pull request #21305 from deepeshgarg007/partial_loan_repayment_fixes
fix: Loan Repayment code clean up and other fixes
This commit is contained in:
commit
3c6795a52d
@ -776,22 +776,16 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
for payment in self.get('loans'):
|
||||
amounts = calculate_amounts(payment.loan, self.posting_date, "Regular Payment")
|
||||
total_amount = amounts['interest_amount'] + amounts['payable_principal_amount']
|
||||
if payment.total_payment > total_amount:
|
||||
frappe.throw(_("""Row {0}: Paid amount {1} is greater than pending accrued amount {2}
|
||||
against loan {3}""").format(payment.idx, frappe.bold(payment.total_payment),
|
||||
frappe.bold(total_amount), frappe.bold(payment.loan)))
|
||||
|
||||
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
|
||||
self.total_loan_repayment += payment.total_payment
|
||||
|
||||
def get_loan_details(self):
|
||||
|
||||
|
@ -149,13 +149,19 @@ class TestLoan(unittest.TestCase):
|
||||
|
||||
repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 10), "Regular Payment", 111118.68)
|
||||
repayment_entry.save()
|
||||
repayment_entry.submit()
|
||||
|
||||
penalty_amount = (accrued_interest_amount * 5 * 25) / (100 * days_in_year(get_datetime(first_date).year))
|
||||
|
||||
self.assertEquals(flt(repayment_entry.interest_payable, 2), flt(accrued_interest_amount, 2))
|
||||
self.assertEquals(flt(repayment_entry.penalty_amount, 2), flt(penalty_amount, 2))
|
||||
|
||||
repayment_entry.submit()
|
||||
amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
|
||||
'paid_principal_amount'])
|
||||
|
||||
loan.load_from_db()
|
||||
|
||||
self.assertEquals(amounts[0], repayment_entry.interest_payable)
|
||||
self.assertEquals(flt(loan.total_principal_paid, 2), flt(repayment_entry.amount_paid -
|
||||
penalty_amount - amounts[0], 2))
|
||||
|
||||
def test_loan_closure_repayment(self):
|
||||
pledges = []
|
||||
@ -189,15 +195,19 @@ class TestLoan(unittest.TestCase):
|
||||
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)
|
||||
repayment_entry.save()
|
||||
"Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
|
||||
repayment_entry.submit()
|
||||
|
||||
repayment_entry.amount_paid = repayment_entry.payable_amount
|
||||
amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
|
||||
'paid_principal_amount'])
|
||||
|
||||
self.assertEquals(flt(repayment_entry.interest_payable, 3), flt(accrued_interest_amount, 3))
|
||||
unaccrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * 6) \
|
||||
/ (days_in_year(get_datetime(first_date).year) * 100)
|
||||
|
||||
self.assertEquals(flt(amounts[0] + unaccrued_interest_amount, 3),
|
||||
flt(accrued_interest_amount, 3))
|
||||
self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0)
|
||||
|
||||
repayment_entry.submit()
|
||||
loan.load_from_db()
|
||||
self.assertEquals(loan.status, "Loan Closure Requested")
|
||||
|
||||
@ -227,57 +237,15 @@ class TestLoan(unittest.TestCase):
|
||||
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)
|
||||
"Regular Payment", 89768.75)
|
||||
|
||||
repayment_entry.save()
|
||||
repayment_entry.submit()
|
||||
|
||||
repayment_entry.load_from_db()
|
||||
amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
|
||||
'paid_principal_amount'])
|
||||
|
||||
self.assertEquals(repayment_entry.interest_payable, 11250.00)
|
||||
self.assertEquals(repayment_entry.payable_principal_amount, 78303.00)
|
||||
|
||||
def test_partial_loan_repayment(self):
|
||||
pledges = []
|
||||
pledges.append({
|
||||
"loan_security": "Test Security 1",
|
||||
"qty": 4000.00,
|
||||
"haircut": 50
|
||||
})
|
||||
|
||||
loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges)
|
||||
|
||||
loan = create_demand_loan(self.applicant2, "Demand Loan", loan_security_pledge.name,
|
||||
posting_date=get_first_day(nowdate()))
|
||||
|
||||
loan.submit()
|
||||
|
||||
self.assertEquals(loan.loan_amount, 1000000)
|
||||
|
||||
first_date = '2019-10-01'
|
||||
last_date = '2019-10-30'
|
||||
|
||||
no_of_days = date_diff(last_date, first_date) + 1
|
||||
|
||||
accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
|
||||
/ (days_in_year(get_datetime().year) * 100)
|
||||
|
||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
|
||||
|
||||
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()
|
||||
repayment_entry.submit()
|
||||
|
||||
penalty_amount = (accrued_interest_amount * 4 * 25) / (100 * days_in_year(get_datetime(first_date).year))
|
||||
|
||||
lia1 = frappe.get_value("Loan Interest Accrual", {"loan": loan.name, "is_paid": 1}, 'name')
|
||||
lia2 = frappe.get_value("Loan Interest Accrual", {"loan": loan.name, "is_paid": 0}, 'name')
|
||||
|
||||
self.assertTrue(lia1)
|
||||
self.assertTrue(lia2)
|
||||
self.assertEquals(amounts[0], 11250.00)
|
||||
self.assertEquals(amounts[1], 78303.00)
|
||||
|
||||
def test_security_shortfall(self):
|
||||
pledges = []
|
||||
@ -294,7 +262,7 @@ class TestLoan(unittest.TestCase):
|
||||
|
||||
make_loan_disbursement_entry(loan.name, loan.loan_amount)
|
||||
|
||||
frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 100
|
||||
frappe.db.sql("""UPDATE `tabLoan Security Price` SET loan_security_price = 100
|
||||
where loan_security='Test Security 2'""")
|
||||
|
||||
create_process_loan_security_shortfall()
|
||||
|
@ -15,12 +15,13 @@
|
||||
"company",
|
||||
"posting_date",
|
||||
"is_term_loan",
|
||||
"is_paid",
|
||||
"section_break_7",
|
||||
"pending_principal_amount",
|
||||
"payable_principal_amount",
|
||||
"paid_principal_amount",
|
||||
"column_break_14",
|
||||
"interest_amount",
|
||||
"paid_interest_amount",
|
||||
"section_break_15",
|
||||
"process_loan_interest_accrual",
|
||||
"repayment_schedule_name",
|
||||
@ -101,13 +102,6 @@
|
||||
"label": "Company",
|
||||
"options": "Company"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_paid",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Paid",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "loan.is_term_loan",
|
||||
@ -143,12 +137,24 @@
|
||||
"hidden": 1,
|
||||
"label": "Repayment Schedule Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "paid_principal_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Paid Principal Amount",
|
||||
"options": "Company:company:default_currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "paid_interest_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Paid Interest Amount",
|
||||
"options": "Company:company:default_currency"
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-10 18:31:02.369857",
|
||||
"modified": "2020-04-16 11:24:23.258404",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Loan Management",
|
||||
"name": "Loan Interest Accrual",
|
||||
|
@ -30,9 +30,8 @@
|
||||
"reference_number",
|
||||
"column_break_21",
|
||||
"reference_date",
|
||||
"paid_accrual_entries",
|
||||
"partial_paid_entry",
|
||||
"principal_amount_paid",
|
||||
"repayment_details",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
@ -155,13 +154,6 @@
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "paid_accrual_entries",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"label": "Paid Accrual Entries",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "against_loan.is_term_loan",
|
||||
@ -197,13 +189,6 @@
|
||||
"fieldname": "column_break_21",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "partial_paid_entry",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"label": "Partial Paid Entry",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0.0",
|
||||
"fieldname": "principal_amount_paid",
|
||||
@ -225,11 +210,18 @@
|
||||
"fieldtype": "Date",
|
||||
"label": "Due Date",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "repayment_details",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 1,
|
||||
"label": "Repayment Details",
|
||||
"options": "Loan Repayment Detail"
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-02-26 06:18:54.934538",
|
||||
"modified": "2020-04-16 18:14:45.166754",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Loan Management",
|
||||
"name": "Loan Repayment",
|
||||
@ -264,7 +256,6 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
|
@ -19,11 +19,11 @@ class LoanRepayment(AccountsController):
|
||||
def validate(self):
|
||||
amounts = calculate_amounts(self.against_loan, self.posting_date, self.payment_type)
|
||||
self.set_missing_values(amounts)
|
||||
|
||||
def before_submit(self):
|
||||
self.mark_as_paid()
|
||||
self.validate_amount()
|
||||
self.allocate_amounts(amounts['pending_accrual_entries'])
|
||||
|
||||
def on_submit(self):
|
||||
self.update_paid_amount()
|
||||
self.make_gl_entries()
|
||||
|
||||
def on_cancel(self):
|
||||
@ -38,32 +38,25 @@ class LoanRepayment(AccountsController):
|
||||
self.cost_center = erpnext.get_default_cost_center(self.company)
|
||||
|
||||
if not self.interest_payable:
|
||||
self.interest_payable = amounts['interest_amount']
|
||||
self.interest_payable = flt(amounts['interest_amount'], 2)
|
||||
|
||||
if not self.penalty_amount:
|
||||
self.penalty_amount = amounts['penalty_amount']
|
||||
self.penalty_amount = flt(amounts['penalty_amount'], 2)
|
||||
|
||||
if not self.pending_principal_amount:
|
||||
self.pending_principal_amount = amounts['pending_principal_amount']
|
||||
self.pending_principal_amount = flt(amounts['pending_principal_amount'], 2)
|
||||
|
||||
if not self.payable_principal_amount and self.is_term_loan:
|
||||
self.payable_principal_amount = amounts['payable_principal_amount']
|
||||
self.payable_principal_amount = flt(amounts['payable_principal_amount'], 2)
|
||||
|
||||
if not self.payable_amount:
|
||||
self.payable_amount = amounts['payable_amount']
|
||||
|
||||
if amounts.get('paid_accrual_entries'):
|
||||
self.paid_accrual_entries = frappe.as_json(amounts.get('paid_accrual_entries'))
|
||||
self.payable_amount = flt(amounts['payable_amount'], 2)
|
||||
|
||||
if amounts.get('due_date'):
|
||||
self.due_date = amounts.get('due_date')
|
||||
|
||||
def mark_as_paid(self):
|
||||
paid_entries = []
|
||||
paid_amount = self.amount_paid
|
||||
interest_paid = paid_amount
|
||||
|
||||
if not paid_amount:
|
||||
def validate_amount(self):
|
||||
if not self.amount_paid:
|
||||
frappe.throw(_("Amount paid cannot be zero"))
|
||||
|
||||
if self.amount_paid < self.penalty_amount:
|
||||
@ -74,37 +67,15 @@ class LoanRepayment(AccountsController):
|
||||
msg = _("Amount of {0} is required for Loan closure").format(self.payable_amount)
|
||||
frappe.throw(msg)
|
||||
|
||||
def update_paid_amount(self):
|
||||
loan = frappe.get_doc("Loan", self.against_loan)
|
||||
|
||||
if self.paid_accrual_entries:
|
||||
paid_accrual_entries = json.loads(self.paid_accrual_entries)
|
||||
|
||||
if paid_amount - self.penalty_amount > 0 and self.paid_accrual_entries:
|
||||
|
||||
interest_paid = paid_amount - self.penalty_amount
|
||||
|
||||
for lia, interest_amount in iteritems(paid_accrual_entries):
|
||||
if interest_amount <= interest_paid:
|
||||
paid_entries.append(lia)
|
||||
interest_paid -= interest_amount
|
||||
elif interest_paid:
|
||||
self.partial_paid_entry = frappe.as_json({"name": lia, "interest_amount": interest_amount})
|
||||
frappe.db.set_value("Loan Interest Accrual", lia, "interest_amount",
|
||||
interest_amount - interest_paid)
|
||||
interest_paid = 0
|
||||
|
||||
if paid_entries:
|
||||
self.paid_accrual_entries = frappe.as_json(paid_entries)
|
||||
else:
|
||||
self.paid_accrual_entries = ""
|
||||
|
||||
if interest_paid:
|
||||
self.principal_amount_paid = interest_paid
|
||||
|
||||
if paid_entries:
|
||||
frappe.db.sql("""UPDATE `tabLoan Interest Accrual`
|
||||
SET is_paid = 1 where name in (%s)""" #nosec
|
||||
% ", ".join(['%s']*len(paid_entries)), tuple(paid_entries))
|
||||
for payment in self.repayment_details:
|
||||
frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
|
||||
SET paid_principal_amount = `paid_principal_amount` + %s,
|
||||
paid_interest_amount = `paid_interest_amount` + %s
|
||||
WHERE name = %s""",
|
||||
(flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual))
|
||||
|
||||
if flt(loan.total_principal_paid + self.principal_amount_paid, 2) >= flt(loan.total_payment, 2):
|
||||
frappe.db.set_value("Loan", self.against_loan, "status", "Loan Closure Requested")
|
||||
@ -116,21 +87,14 @@ class LoanRepayment(AccountsController):
|
||||
update_shortfall_status(self.against_loan, self.principal_amount_paid)
|
||||
|
||||
def mark_as_unpaid(self):
|
||||
|
||||
loan = frappe.get_doc("Loan", self.against_loan)
|
||||
|
||||
if self.paid_accrual_entries:
|
||||
paid_accrual_entries = json.loads(self.paid_accrual_entries)
|
||||
|
||||
if self.paid_accrual_entries:
|
||||
frappe.db.sql("""UPDATE `tabLoan Interest Accrual`
|
||||
SET is_paid = 0 where name in (%s)""" #nosec
|
||||
% ", ".join(['%s']*len(paid_accrual_entries)), tuple(paid_accrual_entries))
|
||||
|
||||
if self.partial_paid_entry:
|
||||
partial_paid_entry = json.loads(self.partial_paid_entry)
|
||||
frappe.db.set_value("Loan Interest Accrual", partial_paid_entry["name"], "interest_amount",
|
||||
partial_paid_entry["interest_amount"])
|
||||
for payment in self.repayment_details:
|
||||
frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
|
||||
SET paid_principal_amount = `paid_principal_amount` - %s,
|
||||
paid_interest_amount = `paid_interest_amount` - %s
|
||||
WHERE name = %s""",
|
||||
(payment.paid_principal_amount, payment.paid_interest_amount, payment.loan_interest_accrual))
|
||||
|
||||
frappe.db.sql(""" UPDATE `tabLoan` SET total_amount_paid = %s, total_principal_paid = %s
|
||||
WHERE name = %s """, (loan.total_amount_paid - self.amount_paid,
|
||||
@ -139,6 +103,38 @@ class LoanRepayment(AccountsController):
|
||||
if loan.status == "Loan Closure Requested":
|
||||
frappe.db.set_value("Loan", self.against_loan, "status", "Disbursed")
|
||||
|
||||
def allocate_amounts(self, paid_entries):
|
||||
self.set('repayment_details', [])
|
||||
self.principal_amount_paid = 0
|
||||
|
||||
if self.amount_paid - self.penalty_amount > 0 and paid_entries:
|
||||
interest_paid = self.amount_paid - self.penalty_amount
|
||||
for lia, amounts in iteritems(paid_entries):
|
||||
if amounts['interest_amount'] + amounts['payable_principal_amount'] <= interest_paid:
|
||||
interest_amount = amounts['interest_amount']
|
||||
paid_principal = amounts['payable_principal_amount']
|
||||
self.principal_amount_paid += paid_principal
|
||||
interest_paid -= (interest_amount + paid_principal)
|
||||
elif interest_paid:
|
||||
if interest_paid >= amounts['interest_amount']:
|
||||
interest_amount = amounts['interest_amount']
|
||||
paid_principal = interest_paid - interest_amount
|
||||
self.principal_amount_paid += paid_principal
|
||||
interest_paid = 0
|
||||
else:
|
||||
interest_amount = interest_paid
|
||||
interest_paid = 0
|
||||
paid_principal=0
|
||||
|
||||
self.append('repayment_details', {
|
||||
'loan_interest_accrual': lia,
|
||||
'paid_interest_amount': interest_amount,
|
||||
'paid_principal_amount': paid_principal
|
||||
})
|
||||
|
||||
if interest_paid:
|
||||
self.principal_amount_paid += interest_paid
|
||||
|
||||
def make_gl_entries(self, cancel=0, adv_adj=0):
|
||||
gle_map = []
|
||||
loan_details = frappe.get_doc("Loan", self.against_loan)
|
||||
@ -223,7 +219,7 @@ def create_repayment_entry(loan, applicant, company, posting_date, loan_type,
|
||||
"posting_date": posting_date,
|
||||
"applicant": applicant,
|
||||
"penalty_amount": penalty_amount,
|
||||
"interst_payable": interest_payable,
|
||||
"interest_payable": interest_payable,
|
||||
"payable_principal_amount": payable_principal_amount,
|
||||
"amount_paid": amount_paid,
|
||||
"loan_type": loan_type
|
||||
@ -232,15 +228,22 @@ def create_repayment_entry(loan, applicant, company, posting_date, loan_type,
|
||||
return lr
|
||||
|
||||
def get_accrued_interest_entries(against_loan):
|
||||
accrued_interest_entries = frappe.get_all("Loan Interest Accrual",
|
||||
fields=["name", "interest_amount", "posting_date", "payable_principal_amount"],
|
||||
filters = {
|
||||
"loan": against_loan,
|
||||
"is_paid": 0,
|
||||
"docstatus": 1
|
||||
}, order_by="posting_date")
|
||||
|
||||
return accrued_interest_entries
|
||||
unpaid_accrued_entries = frappe.db.sql(
|
||||
"""
|
||||
SELECT name, posting_date, interest_amount - paid_interest_amount as interest_amount,
|
||||
payable_principal_amount - paid_principal_amount as payable_principal_amount
|
||||
FROM
|
||||
`tabLoan Interest Accrual`
|
||||
WHERE
|
||||
loan = %s
|
||||
AND (interest_amount - paid_interest_amount > 0 OR
|
||||
payable_principal_amount - paid_principal_amount > 0)
|
||||
AND
|
||||
docstatus = 1
|
||||
""", (against_loan), as_dict=1)
|
||||
|
||||
return unpaid_accrued_entries
|
||||
|
||||
# This function returns the amounts that are payable at the time of loan repayment based on posting date
|
||||
# So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable
|
||||
@ -273,8 +276,10 @@ 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,
|
||||
flt(entry.interest_amount) + flt(entry.payable_principal_amount))
|
||||
pending_accrual_entries.setdefault(entry.name, {
|
||||
'interest_amount': flt(entry.interest_amount),
|
||||
'payable_principal_amount': flt(entry.payable_principal_amount)
|
||||
})
|
||||
|
||||
final_due_date = due_date
|
||||
|
||||
@ -291,7 +296,7 @@ def get_amounts(amounts, against_loan, posting_date, payment_type):
|
||||
amounts["interest_amount"] = total_pending_interest
|
||||
amounts["penalty_amount"] = penalty_amount
|
||||
amounts["payable_amount"] = payable_principal_amount + total_pending_interest + penalty_amount
|
||||
amounts["paid_accrual_entries"] = pending_accrual_entries
|
||||
amounts["pending_accrual_entries"] = pending_accrual_entries
|
||||
|
||||
if final_due_date:
|
||||
amounts["due_date"] = final_due_date
|
||||
|
@ -0,0 +1,43 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-04-15 18:31:54.026923",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"loan_interest_accrual",
|
||||
"paid_principal_amount",
|
||||
"paid_interest_amount"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "loan_interest_accrual",
|
||||
"fieldtype": "Link",
|
||||
"label": "Loan Interest Accrual",
|
||||
"options": "Loan Interest Accrual"
|
||||
},
|
||||
{
|
||||
"fieldname": "paid_principal_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Paid Principal Amount",
|
||||
"options": "Company:company:default_currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "paid_interest_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Paid Interest Amount",
|
||||
"options": "Company:company:default_currency"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-15 21:50:03.837019",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Loan Management",
|
||||
"name": "Loan Repayment Detail",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class LoanRepaymentDetail(Document):
|
||||
pass
|
@ -28,7 +28,6 @@
|
||||
{
|
||||
"fieldname": "loan_account",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Loan Account",
|
||||
"options": "Account",
|
||||
"read_only": 1
|
||||
@ -50,21 +49,23 @@
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Principal Amount",
|
||||
"options": "Company:company:default_currency"
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "interest_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Interest Amount",
|
||||
"options": "Company:company:default_currency"
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "total_payment",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Total Payment",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
"options": "Company:company:default_currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "loan_repayment_entry",
|
||||
@ -84,7 +85,7 @@
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-09 20:01:53.546364",
|
||||
"modified": "2020-04-16 13:17:04.798335",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Loan Management",
|
||||
"name": "Salary Slip Loan",
|
||||
|
@ -287,7 +287,7 @@ erpnext.pos.PointOfSale = class PointOfSale {
|
||||
if (in_list(['serial_no', 'batch_no'], field)) {
|
||||
args[field] = value;
|
||||
}
|
||||
|
||||
|
||||
// add to cur_frm
|
||||
const item = this.frm.add_child('items', args);
|
||||
frappe.flags.hide_serial_batch_dialog = true;
|
||||
|
Loading…
Reference in New Issue
Block a user