Merge branch 'hotfix'
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index f7761ac..a55d0e7 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -4,7 +4,7 @@
import frappe
from erpnext.hooks import regional_overrides
-__version__ = '9.2.12'
+__version__ = '9.2.13'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index f655830..e6887ba 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -200,9 +200,6 @@
if (account and account_currency != existing_gle_currency) or not account:
account = get_party_gle_account(party_type, party, company)
- if not account:
- frappe.throw(_("Party account not specified, please setup default party account in company"))
-
return account
def get_party_account_currency(party_type, party, company):
diff --git a/erpnext/healthcare/doctype/consultation/consultation.py b/erpnext/healthcare/doctype/consultation/consultation.py
index e16c221..69d7ecb 100755
--- a/erpnext/healthcare/doctype/consultation/consultation.py
+++ b/erpnext/healthcare/doctype/consultation/consultation.py
@@ -8,12 +8,12 @@
from frappe.model.document import Document
from frappe.utils import getdate
import json
-from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account
+from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
class Consultation(Document):
def on_update(self):
if(self.appointment):
- frappe.db.set_value("Patient Appointment",self.appointment,"status","Closed")
+ frappe.db.set_value("Patient Appointment", self.appointment, "status", "Closed")
update_consultation_to_medical_record(self)
def after_insert(self):
@@ -23,9 +23,10 @@
if not self.diagnosis or not self.symptoms:
frappe.throw("Diagnosis and Complaints cannot be left blank")
- physician = frappe.get_doc("Physician",self.physician)
- if(frappe.session.user != physician.user_id):
- frappe.throw(_("You don't have permission to submit"))
+ def on_cancel(self):
+ if(self.appointment):
+ frappe.db.set_value("Patient Appointment", self.appointment, "status", "Open")
+ delete_medical_record(self)
def set_sales_invoice_fields(company, patient):
sales_invoice = frappe.new_doc("Sales Invoice")
@@ -91,8 +92,8 @@
item_line.qty = 1
item_line.uom = "Nos"
item_line.conversion_factor = 1
- item_line.income_account = get_income_account(physician,company)
- op_consulting_charge = frappe.get_value("Physician",physician,"op_consulting_charge")
+ item_line.income_account = get_income_account(physician, company)
+ op_consulting_charge = frappe.get_value("Physician", physician, "op_consulting_charge")
if op_consulting_charge:
item_line.rate = op_consulting_charge
item_line.amount = op_consulting_charge
@@ -111,10 +112,13 @@
medical_record.save(ignore_permissions=True)
def update_consultation_to_medical_record(consultation):
- medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s",(consultation.name))
+ medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s", (consultation.name))
if(medical_record_id[0][0]):
subject = set_subject_field(consultation)
- frappe.db.set_value("Patient Medical Record",medical_record_id[0][0],"subject",subject)
+ frappe.db.set_value("Patient Medical Record", medical_record_id[0][0], "subject", subject)
+
+def delete_medical_record(consultation):
+ frappe.db.sql("""delete from `tabPatient Medical Record` where reference_name = %s""", (consultation.name))
def set_subject_field(consultation):
subject = "No Diagnosis "
diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js
index 75b0584..8e98fee 100644
--- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js
+++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js
@@ -9,6 +9,7 @@
filters: {
'account_type': 'Receivable',
'company': d.company,
+ 'is_group': 0
}
};
});
@@ -18,6 +19,7 @@
filters: {
'root_type': 'Income',
'company': d.company,
+ 'is_group': 0
}
};
});
diff --git a/erpnext/healthcare/doctype/patient/patient_dashboard.py b/erpnext/healthcare/doctype/patient/patient_dashboard.py
index cb98f0d..f015b83 100644
--- a/erpnext/healthcare/doctype/patient/patient_dashboard.py
+++ b/erpnext/healthcare/doctype/patient/patient_dashboard.py
@@ -11,8 +11,8 @@
'items': ['Patient Appointment', 'Consultation']
},
{
- 'label': _('Lab Tests'),
- 'items': ['Lab Test']
+ 'label': _('Lab Tests and Vital Signs'),
+ 'items': ['Lab Test', 'Vital Signs']
}
]
}
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
index 1942b66..2237ff5 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
@@ -25,6 +25,14 @@
frm.add_custom_button(__('Cancel'), function() {
btn_update_status(frm, "Cancelled");
});
+
+ frm.add_custom_button(__("Consultation"),function(){
+ btn_create_consultation(frm);
+ },"Create");
+
+ frm.add_custom_button(__('Vital Signs'), function() {
+ btn_create_vital_signs(frm);
+ },"Create");
}
if(frm.doc.status == "Scheduled" && !frm.doc.__islocal){
frm.add_custom_button(__('Cancel'), function() {
diff --git a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json
index 9cbcf7b..6edc0cc 100644
--- a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json
+++ b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json
@@ -56,7 +56,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Patient",
"length": 0,
@@ -174,7 +174,7 @@
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 1,
+ "in_list_view": 0,
"in_standard_filter": 0,
"label": "Subject",
"length": 0,
@@ -236,7 +236,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Datetime",
"length": 0,
@@ -266,7 +266,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Reference DocType",
"length": 0,
@@ -297,7 +297,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Reference Name",
"length": 0,
@@ -389,7 +389,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-10-04 16:09:55.597866",
+ "modified": "2017-11-15 12:48:59.945615",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Medical Record",
@@ -425,6 +425,7 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
+ "title_field": "patient",
"track_changes": 1,
"track_seen": 1
}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/physician/physician.js b/erpnext/healthcare/doctype/physician/physician.js
index 37389fe..c607f23 100755
--- a/erpnext/healthcare/doctype/physician/physician.js
+++ b/erpnext/healthcare/doctype/physician/physician.js
@@ -9,6 +9,7 @@
filters: {
'root_type': 'Income',
'company': d.company,
+ 'is_group': 0
}
};
});
diff --git a/erpnext/healthcare/doctype/physician_schedule/physician_schedule.js b/erpnext/healthcare/doctype/physician_schedule/physician_schedule.js
index e198d35..74ba66f 100644
--- a/erpnext/healthcare/doctype/physician_schedule/physician_schedule.js
+++ b/erpnext/healthcare/doctype/physician_schedule/physician_schedule.js
@@ -33,7 +33,7 @@
while(cur_time < end_time) {
let to_time = cur_time.clone().add(values.duration, 'minutes');
- if(to_time < end_time) {
+ if(to_time <= end_time) {
// add a new timeslot
frm.add_child('time_slots', {
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()
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 6a6af3d..6790176 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -157,7 +157,7 @@
def make_route(self):
if not self.route:
return cstr(frappe.db.get_value('Item Group', self.item_group,
- 'route')) + '/' + self.scrub(self.item_name + '-' + random_string(5))
+ 'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
def validate_website_image(self):
"""Validate if the website image is a public file"""