Employee loan fixes (#11495)
* Employee loan fixes
* Update update_employee_loan_details.py
diff --git a/erpnext/hr/doctype/employee_loan/test_employee_loan.py b/erpnext/hr/doctype/employee_loan/test_employee_loan.py
index 8671baa..c32e85e 100644
--- a/erpnext/hr/doctype/employee_loan/test_employee_loan.py
+++ b/erpnext/hr/doctype/employee_loan/test_employee_loan.py
@@ -38,9 +38,8 @@
self.assertEquals(employee_loan.total_interest_payable, 22712)
self.assertEquals(employee_loan.total_payment, 302712)
-
def create_loan_type(loan_name, maximum_loan_amount, rate_of_interest):
- if not frappe.db.get_value("Loan Type", loan_name):
+ if not frappe.db.exists("Loan Type", loan_name):
frappe.get_doc({
"doctype": "Loan Type",
"loan_name": loan_name,
@@ -49,6 +48,7 @@
}).insert()
def create_employee_loan(employee, loan_type, loan_amount, repayment_method, repayment_periods):
+ create_loan_type(loan_type, 500000, 8.4)
if not frappe.db.get_value("Employee Loan", {"employee":employee}):
employee_loan = frappe.new_doc("Employee Loan")
employee_loan.update({
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.py b/erpnext/hr/doctype/process_payroll/process_payroll.py
index 0e329a7..f8ac044 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.py
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.py
@@ -190,23 +190,28 @@
def format_as_links(self, salary_slip):
return ['<a href="#Form/Salary Slip/{0}">{0}</a>'.format(salary_slip)]
- def get_total_salary_and_loan_amounts(self):
+ def get_loan_details(self):
"""
- Get total loan principal, loan interest and salary amount from submitted salary slip based on selected criteria
+ Get loan details from submitted salary slip based on selected criteria
"""
cond = self.get_filter_condition()
- totals = frappe.db.sql("""
- select sum(principal_amount) as total_principal_amount, sum(interest_amount) as total_interest_amount,
- sum(total_loan_repayment) as total_loan_repayment, sum(rounded_total) as rounded_total from `tabSalary Slip` t1
+ return frappe.db.sql(""" select eld.employee_loan_account,
+ eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment
+ from
+ `tabSalary Slip` t1, `tabSalary Slip Loan` eld
+ where
+ t1.docstatus = 1 and t1.name = eld.parent and start_date >= %s and end_date <= %s %s
+ """ % ('%s', '%s', cond), (self.start_date, self.end_date), as_dict=True) or []
+
+ def get_total_salary_amount(self):
+ """
+ Get total salary amount from submitted salary slip based on selected criteria
+ """
+ cond = self.get_filter_condition()
+ totals = frappe.db.sql(""" select sum(rounded_total) as rounded_total from `tabSalary Slip` t1
where t1.docstatus = 1 and start_date >= %s and end_date <= %s %s
""" % ('%s', '%s', cond), (self.start_date, self.end_date), as_dict=True)
- return totals[0]
-
- def get_loan_accounts(self):
- loan_accounts = frappe.get_all("Employee Loan", fields=["employee_loan_account", "interest_income_account"],
- filters = {"company": self.company, "docstatus":1})
- if loan_accounts:
- return loan_accounts[0]
+ return totals and totals[0] or None
def get_salary_component_account(self, salary_component):
account = frappe.db.get_value("Salary Component Account",
@@ -257,8 +262,7 @@
earnings = self.get_salary_component_total(component_type = "earnings") or {}
deductions = self.get_salary_component_total(component_type = "deductions") or {}
default_payroll_payable_account = self.get_default_payroll_payable_account()
- loan_amounts = self.get_total_salary_and_loan_amounts()
- loan_accounts = self.get_loan_accounts()
+ loan_details = self.get_loan_details()
jv_name = ""
precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
@@ -294,18 +298,18 @@
})
# Employee loan
- if loan_amounts.total_loan_repayment:
+ for data in loan_details:
accounts.append({
- "account": loan_accounts.employee_loan_account,
- "credit_in_account_currency": loan_amounts.total_principal_amount
+ "account": data.employee_loan_account,
+ "credit_in_account_currency": data.principal_amount
})
accounts.append({
- "account": loan_accounts.interest_income_account,
- "credit_in_account_currency": loan_amounts.total_interest_amount,
+ "account": data.interest_income_account,
+ "credit_in_account_currency": data.interest_amount,
"cost_center": self.cost_center,
"project": self.project
})
- payable_amount -= flt(loan_amounts.total_loan_repayment, precision)
+ payable_amount -= flt(data.total_payment, precision)
# Payable amount
accounts.append({
@@ -327,11 +331,11 @@
def make_payment_entry(self):
self.check_permission('write')
- total_salary_amount = self.get_total_salary_and_loan_amounts()
+ total_salary_amount = self.get_total_salary_amount()
default_payroll_payable_account = self.get_default_payroll_payable_account()
precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
- if total_salary_amount.rounded_total:
+ if total_salary_amount and total_salary_amount.rounded_total:
journal_entry = frappe.new_doc('Journal Entry')
journal_entry.voucher_type = 'Bank Entry'
journal_entry.user_remark = _('Payment of salary from {0} to {1}')\
diff --git a/erpnext/hr/doctype/process_payroll/test_process_payroll.py b/erpnext/hr/doctype/process_payroll/test_process_payroll.py
index cac43c4..91b60b4 100644
--- a/erpnext/hr/doctype/process_payroll/test_process_payroll.py
+++ b/erpnext/hr/doctype/process_payroll/test_process_payroll.py
@@ -6,9 +6,9 @@
import erpnext
import frappe
-from frappe.utils import nowdate
-from erpnext.hr.doctype.process_payroll.process_payroll import get_end_date
-
+from dateutil.relativedelta import relativedelta
+from erpnext.accounts.utils import get_fiscal_year, getdate, nowdate
+from erpnext.hr.doctype.process_payroll.process_payroll import get_start_end_dates, get_end_date
class TestProcessPayroll(unittest.TestCase):
def test_process_payroll(self):
@@ -19,22 +19,9 @@
if not frappe.db.get_value('Salary Component Account',
{'parent': data.name, 'company': erpnext.get_default_company()}, 'name'):
get_salary_component_account(data.name)
-
- payment_account = frappe.get_value('Account',
- {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
if not frappe.db.get_value("Salary Slip", {"start_date": "2016-11-01", "end_date": "2016-11-30"}):
- process_payroll = frappe.get_doc("Process Payroll", "Process Payroll")
- process_payroll.company = erpnext.get_default_company()
- process_payroll.start_date = "2016-11-01"
- process_payroll.end_date = "2016-11-30"
- process_payroll.payment_account = payment_account
- process_payroll.posting_date = nowdate()
- process_payroll.payroll_frequency = "Monthly"
- process_payroll.create_salary_slips()
- process_payroll.submit_salary_slips()
- if process_payroll.get_sal_slip_list(ss_status = 1):
- r = process_payroll.make_payment_entry()
+ make_process_payroll()
def test_get_end_date(self):
self.assertEqual(get_end_date('2017-01-01', 'monthly'), {'end_date': '2017-01-31'})
@@ -45,7 +32,99 @@
self.assertEqual(get_end_date('2020-02-15', 'bimonthly'), {'end_date': ''})
self.assertEqual(get_end_date('2017-02-15', 'monthly'), {'end_date': '2017-03-14'})
self.assertEqual(get_end_date('2017-02-15', 'daily'), {'end_date': '2017-02-15'})
-
+
+ def test_employee_loan(self):
+ from erpnext.hr.doctype.salary_structure.test_salary_structure import (make_employee,
+ make_salary_structure)
+ from erpnext.hr.doctype.employee_loan.test_employee_loan import create_employee_loan
+
+ branch = "Test Employee Branch"
+ employee = make_employee("test_employee@loan.com")
+ company = erpnext.get_default_company()
+ holiday_list = make_holiday("test holiday for loan")
+
+ if not frappe.db.exists('Salary Component', 'Basic Salary'):
+ frappe.get_doc({
+ 'doctype': 'Salary Component',
+ 'salary_component': 'Basic Salary',
+ 'salary_component_abbr': 'BS',
+ 'type': 'Earning',
+ 'accounts': [{
+ 'company': company,
+ 'default_account': frappe.db.get_value('Account',
+ {'company': company, 'root_type': 'Expense', 'account_type': ''}, 'name')
+ }]
+ }).insert()
+
+ if not frappe.db.get_value('Salary Component Account',
+ {'parent': 'Basic Salary', 'company': company}):
+ salary_component = frappe.get_doc('Salary Component', 'Basic Salary')
+ salary_component.append('accounts', {
+ 'company': company,
+ 'default_account': 'Salary - WP'
+ })
+
+ company_doc = frappe.get_doc('Company', company)
+ if not company_doc.default_payroll_payable_account:
+ company_doc.default_payroll_payable_account = frappe.db.get_value('Account',
+ {'company': company, 'root_type': 'Liability', 'account_type': ''}, 'name')
+ company_doc.save()
+
+ if not frappe.db.exists('Branch', branch):
+ frappe.get_doc({
+ 'doctype': 'Branch',
+ 'branch': branch
+ }).insert()
+
+ employee_doc = frappe.get_doc('Employee', employee)
+ employee_doc.branch = branch
+ employee_doc.holiday_list = holiday_list
+ employee_doc.save()
+
+ employee_loan = create_employee_loan(employee,
+ "Personal Loan", 280000, "Repay Over Number of Periods", 20)
+ employee_loan.repay_from_salary = 1
+ employee_loan.submit()
+
+ salary_strcture = "Test Salary Structure for Loan"
+ if not frappe.db.exists('Salary Structure', salary_strcture):
+ salary_strcture = make_salary_structure(salary_strcture, [{
+ 'employee': employee,
+ 'from_date': '2017-01-01',
+ 'base': 30000
+ }])
+
+ salary_strcture = frappe.get_doc('Salary Structure', salary_strcture)
+ salary_strcture.set('earnings', [{
+ 'salary_component': 'Basic Salary',
+ 'abbr': 'BS',
+ 'amount_based_on_formula':1,
+ 'formula': 'base*.5'
+ }])
+ salary_strcture.save()
+
+ dates = get_start_end_dates('Monthly', nowdate())
+ make_process_payroll(start_date=dates.start_date,
+ end_date=dates.end_date, branch=branch)
+
+ name = frappe.db.get_value('Salary Slip',
+ {'posting_date': nowdate(), 'employee': employee}, 'name')
+
+ salary_slip = frappe.get_doc('Salary Slip', name)
+ for row in salary_slip.loans:
+ if row.employee_loan == employee_loan.name:
+ interest_amount = (280000 * 8.4)/(12*100)
+ principal_amount = employee_loan.monthly_repayment_amount - interest_amount
+ self.assertEqual(row.interest_amount, interest_amount)
+ self.assertEqual(row.principal_amount, principal_amount)
+ self.assertEqual(row.total_payment,
+ interest_amount + principal_amount)
+
+ if salary_slip.docstatus == 0:
+ frappe.delete_doc('Salary Slip', name)
+
+ employee_loan.cancel()
+ frappe.delete_doc('Employee Loan', employee_loan.name)
def get_salary_component_account(sal_comp):
company = erpnext.get_default_company()
@@ -63,4 +142,54 @@
"parent_account": "Indirect Expenses - " + frappe.db.get_value('Company', company, 'abbr'),
"company": company
}).insert()
- return salary_account
\ No newline at end of file
+ return salary_account
+
+def make_process_payroll(**args):
+ args = frappe._dict(args)
+
+ process_payroll = frappe.get_doc("Process Payroll", "Process Payroll")
+ process_payroll.company = erpnext.get_default_company()
+ process_payroll.start_date = args.start_date or "2016-11-01"
+ process_payroll.end_date = args.end_date or "2016-11-30"
+ process_payroll.payment_account = get_payment_account()
+ process_payroll.posting_date = nowdate()
+ process_payroll.payroll_frequency = "Monthly"
+ process_payroll.branch = args.branch or None
+ process_payroll.create_salary_slips()
+ process_payroll.submit_salary_slips()
+ if process_payroll.get_sal_slip_list(ss_status = 1):
+ r = process_payroll.make_payment_entry()
+
+ return process_payroll
+
+def get_payment_account():
+ return frappe.get_value('Account',
+ {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
+
+def make_holiday(holiday_list_name):
+ if not frappe.db.exists('Holiday List', holiday_list_name):
+ current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True)
+ dt = getdate(nowdate())
+
+ new_year = dt + relativedelta(month=01, day=01, year=dt.year)
+ republic_day = dt + relativedelta(month=01, day=26, year=dt.year)
+ test_holiday = dt + relativedelta(month=02, day=02, year=dt.year)
+
+ frappe.get_doc({
+ 'doctype': 'Holiday List',
+ 'from_date': current_fiscal_year.year_start_date,
+ 'to_date': current_fiscal_year.year_end_date,
+ 'holiday_list_name': holiday_list_name,
+ 'holidays': [{
+ 'holiday_date': new_year,
+ 'description': 'New Year'
+ }, {
+ 'holiday_date': republic_day,
+ 'description': 'Republic Day'
+ }, {
+ 'holiday_date': test_holiday,
+ 'description': 'Test Holiday'
+ }]
+ }).insert()
+
+ return holiday_list_name
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json
index 6a52f2d..6cc62eb 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.json
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.json
@@ -1293,7 +1293,68 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "principal_amount",
+ "fieldname": "loans",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employee Loan",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Salary Slip Loan",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_43",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "fieldname": "total_principal_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -1302,7 +1363,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Principal Amount",
+ "label": "Total Principal Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
@@ -1324,7 +1385,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "interest_amount",
+ "default": "0",
+ "fieldname": "total_interest_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -1333,7 +1395,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Interest Amount",
+ "label": "Total Interest Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
@@ -1355,7 +1417,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "column_break_48",
+ "fieldname": "column_break_45",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -1384,6 +1446,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "default": "0",
"fieldname": "total_loan_repayment",
"fieldtype": "Currency",
"hidden": 0,
@@ -1604,7 +1667,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-11-10 18:40:33.817074",
+ "modified": "2017-11-13 23:55:37.504856",
"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 1f9c192..ea5f35b 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -375,15 +375,34 @@
self.precision("net_pay") if disable_rounded_total else 0)
def set_loan_repayment(self):
- employee_loan = frappe.db.sql("""select sum(principal_amount) as principal_amount, sum(interest_amount) as interest_amount,
- sum(total_payment) as total_loan_repayment from `tabRepayment Schedule`
- where payment_date between %s and %s and parent in (select name from `tabEmployee Loan`
- where employee = %s and repay_from_salary = 1 and docstatus = 1)""",
- (self.start_date, self.end_date, self.employee), as_dict=True)
- if employee_loan:
- self.principal_amount = employee_loan[0].principal_amount
- self.interest_amount = employee_loan[0].interest_amount
- self.total_loan_repayment = employee_loan[0].total_loan_repayment
+ self.set('loans', [])
+ self.total_loan_repayment = 0
+ self.total_interest_amount = 0
+ self.total_principal_amount = 0
+
+ for loan in self.get_employee_loan_details():
+ self.append('loans', {
+ 'employee_loan': loan.name,
+ 'total_payment': loan.total_payment,
+ 'interest_amount': loan.interest_amount,
+ 'principal_amount': loan.principal_amount,
+ 'employee_loan_account': loan.employee_loan_account,
+ 'interest_income_account': loan.interest_income_account
+ })
+
+ self.total_loan_repayment += loan.total_payment
+ self.total_interest_amount += loan.interest_amount
+ self.total_principal_amount += loan.principal_amount
+
+ def get_employee_loan_details(self):
+ return frappe.db.sql("""select rps.principal_amount, rps.interest_amount, el.name,
+ rps.total_payment, el.employee_loan_account, el.interest_income_account
+ from
+ `tabRepayment Schedule` as rps, `tabEmployee Loan` as el
+ where
+ el.name = rps.parent and rps.payment_date between %s and %s and
+ el.repay_from_salary = 1 and el.docstatus = 1 and el.employee = %s""",
+ (self.start_date, self.end_date, self.employee), as_dict=True) or []
def on_submit(self):
if self.net_pay < 0:
diff --git a/erpnext/hr/doctype/salary_slip_loan/__init__.py b/erpnext/hr/doctype/salary_slip_loan/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/salary_slip_loan/__init__.py
diff --git a/erpnext/hr/doctype/salary_slip_loan/salary_slip_loan.json b/erpnext/hr/doctype/salary_slip_loan/salary_slip_loan.json
new file mode 100644
index 0000000..445c2f4
--- /dev/null
+++ b/erpnext/hr/doctype/salary_slip_loan/salary_slip_loan.json
@@ -0,0 +1,256 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2017-11-08 12:51:12.834479",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "employee_loan",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Employee Loan",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Employee Loan",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "employee_loan_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Employee Loan Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "interest_income_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Interest Income Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "principal_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Principal Amount",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "interest_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Interest Amount",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "total_payment",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Total Payment",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-11-13 23:59:47.237689",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Salary Slip Loan",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_slip_loan/salary_slip_loan.py b/erpnext/hr/doctype/salary_slip_loan/salary_slip_loan.py
new file mode 100644
index 0000000..83908ce
--- /dev/null
+++ b/erpnext/hr/doctype/salary_slip_loan/salary_slip_loan.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, 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 SalarySlipLoan(Document):
+ pass
diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py
index 6b1404c..2382a81 100644
--- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py
@@ -17,11 +17,9 @@
def setUp(self):
self.make_holiday_list()
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Structure Test Holiday List")
- make_earning_salary_component(["Basic Salary", "Special Allowance", "HRA"])
- make_deduction_salary_component(["Professional Tax", "TDS"])
make_employee("test_employee@salary.com")
make_employee("test_employee_2@salary.com")
-
+
def make_holiday_list(self):
if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"):
holiday_list = frappe.get_doc({
@@ -33,7 +31,7 @@
}).insert()
holiday_list.get_weekly_off_dates()
holiday_list.save()
-
+
def test_amount_totals(self):
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee@salary.com"})
if not sal_slip:
@@ -64,7 +62,7 @@
for row in salary_structure.deductions:
self.assertFalse(("\n" in row.formula) or ("\n" in row.condition))
-
+
def make_employee(user):
if not frappe.db.get_value("User", user):
frappe.get_doc({
@@ -74,7 +72,6 @@
"new_password": "password",
"roles": [{"doctype": "Has Role", "role": "Employee"}]
}).insert()
-
if not frappe.db.get_value("Employee", {"user_id": user}):
emp = frappe.get_doc({
@@ -95,7 +92,7 @@
return emp.name
else:
return frappe.get_value("Employee", {"employee_name":user}, "name")
-
+
def make_salary_slip_from_salary_structure(employee):
sal_struct = make_salary_structure('Salary Structure Sample')
sal_slip = make_salary_slip(sal_struct, employee = employee)
@@ -106,22 +103,21 @@
sal_slip.insert()
sal_slip.submit()
return sal_slip
-
-def make_salary_structure(sal_struct):
+
+def make_salary_structure(sal_struct, employees=None):
if not frappe.db.exists('Salary Structure', sal_struct):
frappe.get_doc({
"doctype": "Salary Structure",
"name": sal_struct,
"company": erpnext.get_default_company(),
- "employees": get_employee_details(),
+ "employees": employees or get_employee_details(),
"earnings": get_earnings_component(),
"deductions": get_deductions_component(),
"payroll_frequency": "Monthly",
"payment_account": frappe.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
}).insert()
- return sal_struct
-
-
+ return sal_struct
+
def get_employee_details():
return [{"employee": frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"),
"base": 25000,
@@ -136,8 +132,11 @@
"idx": 2
}
]
-
-def get_earnings_component():
+
+def get_earnings_component():
+ make_earning_salary_component(["Basic Salary", "Special Allowance", "HRA"])
+ make_deduction_salary_component(["Professional Tax", "TDS"])
+
return [
{
"salary_component": 'Basic Salary',
@@ -167,7 +166,7 @@
"idx": 4
},
]
-
+
def get_deductions_component():
return [
{
@@ -191,5 +190,4 @@
"formula": 'base*.1',
"idx": 3
}
- ]
-
\ No newline at end of file
+ ]
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index ca8b0ec..1ae99a3 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -458,4 +458,5 @@
erpnext.patches.v9_0.set_pos_profile_name
erpnext.patches.v9_0.remove_non_existing_warehouse_from_stock_settings
execute:frappe.delete_doc_if_exists("DocType", "Program Fee")
-erpnext.patches.v9_2.delete_healthcare_domain_default_items
\ No newline at end of file
+erpnext.patches.v9_0.update_employee_loan_details
+erpnext.patches.v9_2.delete_healthcare_domain_default_items
diff --git a/erpnext/patches/v9_0/update_employee_loan_details.py b/erpnext/patches/v9_0/update_employee_loan_details.py
new file mode 100644
index 0000000..86690fc
--- /dev/null
+++ b/erpnext/patches/v9_0/update_employee_loan_details.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc('hr', 'doctype', 'salary_slip_loan')
+ frappe.reload_doc('hr', 'doctype', 'salary_slip')
+
+ for data in frappe.db.sql(""" select name,
+ start_date, end_date, total_loan_repayment
+ from
+ `tabSalary Slip`
+ where
+ docstatus < 2 and ifnull(total_loan_repayment, 0) > 0""", as_dict=1):
+ salary_slip = frappe.get_doc('Salary Slip', data.name)
+ salary_slip.set_loan_repayment()
+
+ if salary_slip.total_loan_repayment == data.total_loan_repayment:
+ for row in salary_slip.loans:
+ row.db_update()
+
+ salary_slip.db_update()