Merge branch 'multi_company' of https://github.com/ESS-LLP/erpnext-healthcare into ess_pr_21253
diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js
index 42b35c2..b2b95b2 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.js
+++ b/erpnext/accounts/report/purchase_register/purchase_register.js
@@ -34,6 +34,33 @@
"label": __("Mode of Payment"),
"fieldtype": "Link",
"options": "Mode of Payment"
+ },
+ {
+ "fieldname":"cost_center",
+ "label": __("Cost Center"),
+ "fieldtype": "Link",
+ "options": "Cost Center"
+ },
+ {
+ "fieldname":"warehouse",
+ "label": __("Warehouse"),
+ "fieldtype": "Link",
+ "options": "Warehouse"
+ },
+ {
+ "fieldname":"item_group",
+ "label": __("Item Group"),
+ "fieldtype": "Link",
+ "options": "Item Group"
}
]
}
+
+erpnext.dimension_filters.forEach((dimension) => {
+ frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{
+ "fieldname": dimension["fieldname"],
+ "label": __(dimension["label"]),
+ "fieldtype": "Link",
+ "options": dimension["document_type"]
+ });
+});
\ No newline at end of file
diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py
index 3f8abb7..9399e70 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.py
+++ b/erpnext/accounts/report/purchase_register/purchase_register.py
@@ -5,6 +5,7 @@
import frappe
from frappe.utils import flt
from frappe import msgprint, _
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
def execute(filters=None):
return _execute(filters)
@@ -66,7 +67,7 @@
total_tax += tax_amount
row.append(tax_amount)
- # total tax, grand total, rounded total & outstanding amount
+ # total tax, grand total, rounded total & outstanding amount
row += [total_tax, inv.base_grand_total, flt(inv.base_grand_total, 0), inv.outstanding_amount]
data.append(row)
@@ -134,6 +135,38 @@
if filters.get("mode_of_payment"): conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s"
+ if filters.get("cost_center"):
+ conditions += """ and exists(select name from `tabPurchase Invoice Item`
+ where parent=`tabPurchase Invoice`.name
+ and ifnull(`tabPurchase Invoice Item`.cost_center, '') = %(cost_center)s)"""
+
+ if filters.get("warehouse"):
+ conditions += """ and exists(select name from `tabPurchase Invoice Item`
+ where parent=`tabPurchase Invoice`.name
+ and ifnull(`tabPurchase Invoice Item`.warehouse, '') = %(warehouse)s)"""
+
+ if filters.get("item_group"):
+ conditions += """ and exists(select name from `tabPurchase Invoice Item`
+ where parent=`tabPurchase Invoice`.name
+ and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s)"""
+
+ accounting_dimensions = get_accounting_dimensions(as_list=False)
+
+ if accounting_dimensions:
+ common_condition = """
+ and exists(select name from `tabPurchase Invoice Item`
+ where parent=`tabPurchase Invoice`.name
+ """
+ for dimension in accounting_dimensions:
+ if filters.get(dimension.fieldname):
+ if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
+ filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
+ filters.get(dimension.fieldname))
+
+ conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname)
+ else:
+ conditions += common_condition + "and ifnull(`tabPurchase Invoice Item`.{0}, '') in (%({0})s))".format(dimension.fieldname)
+
return conditions
def get_invoices(filters, additional_query_columns):
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 4d0520a..de76e45 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -69,16 +69,16 @@
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
],
- "Purchase Invoice": [
- ["Draft", None],
- ["Submitted", "eval:self.docstatus==1"],
- ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
- ["Return", "eval:self.is_return==1 and self.docstatus==1"],
- ["Debit Note Issued",
- "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
- ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
- ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
- ["Cancelled", "eval:self.docstatus==2"],
+ "Purchase Invoice": [
+ ["Draft", None],
+ ["Submitted", "eval:self.docstatus==1"],
+ ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
+ ["Return", "eval:self.is_return==1 and self.docstatus==1"],
+ ["Debit Note Issued",
+ "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
+ ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
+ ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
+ ["Cancelled", "eval:self.docstatus==2"],
],
"Material Request": [
["Draft", None],
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 84be280..2f07e15 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -313,14 +313,14 @@
"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_interest_accrual.loan_interest_accrual.make_accrual_interest_entry_for_term_loans"
+ "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": [
"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/payroll_entry/test_payroll_entry.py b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py
index 35f5a57..49671d5 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 @@
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 @@
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.json b/erpnext/hr/doctype/salary_slip/salary_slip.json
index e73d41a..097d3a0 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/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index c637215..b3c803b 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -755,30 +755,43 @@
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 9acfd1f..ecccac7 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 @@
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 @@
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/loan.js b/erpnext/loan_management/doctype/loan/loan.js
index 8b22017..9cd8b2e 100644
--- a/erpnext/loan_management/doctype/loan/loan.js
+++ b/erpnext/loan_management/doctype/loan/loan.js
@@ -97,6 +97,8 @@
"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 @@
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.json b/erpnext/loan_management/doctype/loan/loan.json
index 2834e5b..b04e822 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 696410b..c7a2fba 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -19,6 +19,7 @@
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 @@
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:
@@ -198,7 +203,7 @@
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 +211,7 @@
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 759b0d8..108672b 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -10,10 +10,10 @@
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.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.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 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):
@@ -145,7 +145,7 @@
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 +186,7 @@
/ (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 +224,7 @@
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 +264,8 @@
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()
@@ -297,7 +297,7 @@
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_application/loan_application.js b/erpnext/loan_management/doctype/loan_application/loan_application.js
index 57050d8..aba5f42 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 @@
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 @@
});
},
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 72a4ddc..2d9c45d 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",
@@ -84,13 +84,6 @@
"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 fa7db2d..2918486 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 @@
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 @@
)[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 968e377..189b2f5 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
@@ -7,8 +7,8 @@
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.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year)
+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_year
from erpnext.selling.doctype.customer.test_customer import get_customer_dict
class TestLoanDisbursement(unittest.TestCase):
@@ -56,7 +56,7 @@
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 33f496f..a261120 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 b8e6dab..094b9c6 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 @@
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 @@
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_account,
+ '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 @@
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_account,
+ '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 e681ae4..2afed08 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,8 +7,8 @@
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.loan_interest_accrual.loan_interest_accrual import (make_accrual_interest_entry_for_term_loans, days_in_year)
+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_year
from erpnext.selling.doctype.customer.test_customer import get_customer_dict
class TestLoanInterestAccrual(unittest.TestCase):
@@ -54,7 +54,7 @@
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/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index a70e312..2d2ca4c 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 @@
)
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 @@
"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 @@
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 @@
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 b7be84f..ab040f1 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 @@
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 14b18ab..208cb19 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 @@
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'))
+
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 7f79cb1..0ef098f 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 0f33da9..cd3cf7e 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 @@
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 @@
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/process_loan_security_shortfall/process_loan_security_shortfall.py b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py
index 417e367..b4aad25 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 @@
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})
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 ce020ff..f7e2116 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",
diff --git a/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py b/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py
index 3fccbfa..85202bf 100644
--- a/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py
+++ b/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py
@@ -1,10 +1,11 @@
import frappe
+
def execute():
- frappe.reload_doc('selling', 'doctype', frappe.scrub('Sales Order Item'))
- frappe.reload_doc('buying', 'doctype', frappe.scrub('Purchase Order Item'))
+ frappe.reload_doc('selling', 'doctype', 'sales_order_item', force=True)
+ frappe.reload_doc('buying', 'doctype', 'purchase_order_item', force=True)
- for doctype in ['Sales Order Item', 'Purchase Order Item']:
+ for doctype in ('Sales Order Item', 'Purchase Order Item'):
frappe.db.sql("""
UPDATE `tab{0}`
SET against_blanket_order = 1
diff --git a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
index 5f249e4..02378e0 100644
--- a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
+++ b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
@@ -67,7 +67,8 @@
practitioners = frappe.db.sql("select name from `tabHealthcare Practitioner` where 'active'= 0", as_dict=1)
practitioners_lst = [p.name for p in practitioners]
frappe.reload_doc('healthcare', 'doctype', 'healthcare_practitioner')
- frappe.db.sql("update `tabHealthcare Practitioner` set status = 'Disabled' where name IN %(practitioners)s""", {"practitioners": practitioners_lst})
+ if practitioners_lst:
+ frappe.db.sql("update `tabHealthcare Practitioner` set status = 'Disabled' where name IN %(practitioners)s""", {"practitioners": practitioners_lst})
# set Clinical Procedure status
if frappe.db.exists('DocType', 'Clinical Procedure'):
diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
index a2b32fe..9e7a023 100644
--- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
+++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py
@@ -277,7 +277,7 @@
def get_itc_details(self, reverse_charge='N'):
itc_amount = frappe.db.sql("""
- select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge
+ select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge
from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
@@ -312,7 +312,7 @@
and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders')
group by s.gst_category, s.place_of_supply""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
- inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount) as tax_amount, s.place_of_supply, s.gst_category
+ inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount_after_discount_amount) as tax_amount, s.place_of_supply, s.gst_category
from `tabSales Invoice` s, `tabSales Taxes and Charges` t
where t.parent = s.name and s.docstatus = 1 and month(s.posting_date) = %s and year(s.posting_date) = %s
and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders')
@@ -385,7 +385,7 @@
tax_template = 'Purchase Taxes and Charges'
tax_amounts = frappe.db.sql("""
- select s.gst_category, sum(t.tax_amount) as tax_amount, t.account_head
+ select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head
from `tab{doctype}` s , `tab{template}` t
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json
index fb28b5c..731a730 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.json
+++ b/erpnext/stock/doctype/serial_no/serial_no.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:serial_no",
@@ -41,7 +42,6 @@
"delivery_document_no",
"delivery_date",
"delivery_time",
- "is_cancelled",
"column_break5",
"customer",
"customer_name",
@@ -56,7 +56,8 @@
"warranty_period",
"more_info",
"serial_no_details",
- "company"
+ "company",
+ "status"
],
"fields": [
{
@@ -307,16 +308,6 @@
"read_only": 1
},
{
- "fieldname": "is_cancelled",
- "fieldtype": "Select",
- "hidden": 1,
- "label": "Is Cancelled",
- "oldfieldname": "is_cancelled",
- "oldfieldtype": "Select",
- "options": "\nYes\nNo",
- "report_hide": 1
- },
- {
"fieldname": "column_break5",
"fieldtype": "Column Break",
"width": "50%"
@@ -423,11 +414,20 @@
"remember_last_selected_value": 1,
"reqd": 1,
"search_index": 1
+ },
+ {
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_standard_filter": 1,
+ "label": "Status",
+ "options": "\nActive\nDelivered\nExpired",
+ "read_only": 1
}
],
"icon": "fa fa-barcode",
"idx": 1,
- "modified": "2020-02-28 19:31:09.357323",
+ "links": [],
+ "modified": "2020-04-08 13:29:58.517772",
"modified_by": "Administrator",
"module": "Stock",
"name": "Serial No",
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 772ac58..b32c709 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -35,6 +35,15 @@
self.set_maintenance_status()
self.validate_warehouse()
self.validate_item()
+ self.set_status()
+
+ def set_status(self):
+ if self.delivery_document_type:
+ self.status = "Delivered"
+ elif self.warranty_expiry_date and getdate(self.warranty_expiry_date) <= getdate(nowdate()):
+ self.status = "Expired"
+ else:
+ self.status = "Active"
def set_maintenance_status(self):
if not self.warranty_expiry_date and not self.amc_expiry_date:
@@ -197,6 +206,7 @@
self.set_purchase_details(last_sle.get("purchase_sle"))
self.set_sales_details(last_sle.get("delivery_sle"))
self.set_maintenance_status()
+ self.set_status()
def process_serial_no(sle):
item_det = get_item_details(sle.item_code)
diff --git a/erpnext/stock/doctype/serial_no/serial_no_list.js b/erpnext/stock/doctype/serial_no/serial_no_list.js
index 5b1e312..651f790 100644
--- a/erpnext/stock/doctype/serial_no/serial_no_list.js
+++ b/erpnext/stock/doctype/serial_no/serial_no_list.js
@@ -1,14 +1,12 @@
frappe.listview_settings['Serial No'] = {
- add_fields: ["is_cancelled", "item_code", "warehouse", "warranty_expiry_date", "delivery_document_type"],
+ add_fields: ["item_code", "warehouse", "warranty_expiry_date", "delivery_document_type"],
get_indicator: (doc) => {
- if (doc.is_cancelled) {
- return [__("Cancelled"), "red", "is_cancelled,=,Yes"];
- } else if (doc.delivery_document_type) {
- return [__("Delivered"), "green", "delivery_document_type,is,set|is_cancelled,=,No"];
+ if (doc.delivery_document_type) {
+ return [__("Delivered"), "green", "delivery_document_type,is,set"];
} else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) {
- return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set|is_cancelled,=,No"];
+ return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set"];
} else {
- return [__("Active"), "green", "delivery_document_type,is,not set|is_cancelled,=,No"];
+ return [__("Active"), "green", "delivery_document_type,is,not set"];
}
}
};
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 45ed498..dab5a7b 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -45,7 +45,7 @@
def calculate_batch_qty(self):
if self.batch_no:
- batch_qty = frappe.db.get_value("Stock Ledger Entry",
+ batch_qty = frappe.db.get_value("Stock Ledger Entry",
{"docstatus": 1, "batch_no": self.batch_no, "is_cancelled": "No"},
"sum(actual_qty)") or 0
frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty)
@@ -72,7 +72,7 @@
frappe.throw(_("Actual Qty is mandatory"))
def validate_item(self):
- item_det = frappe.db.sql("""select name, has_batch_no, docstatus,
+ item_det = frappe.db.sql("""select name, item_name, has_batch_no, docstatus,
is_stock_item, has_variants, stock_uom, create_new_batch
from tabItem where name=%s""", self.item_code, as_dict=True)
@@ -87,10 +87,11 @@
# check if batch number is required
if self.voucher_type != 'Stock Reconciliation':
if item_det.has_batch_no ==1:
+ batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" + item_det.item_name
if not self.batch_no:
- frappe.throw(_("Batch number is mandatory for Item {0}").format(self.item_code))
+ frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item))
elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}):
- frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, self.item_code))
+ frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item))
elif item_det.has_batch_no ==0 and self.batch_no and self.is_cancelled == "No":
frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code))