[salary slip] total no. of working days calculation [issue] webnotes/erpnext#285
diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py
index e3e5d5e..0a2a20b 100644
--- a/controllers/accounts_controller.py
+++ b/controllers/accounts_controller.py
@@ -141,8 +141,9 @@
def calculate_taxes_and_totals(self):
# validate conversion rate
- if not self.doc.currency:
- self.doc.currency = get_company_currency(self.doc.company)
+ company_currency = get_company_currency(self.doc.company)
+ if not self.doc.currency or self.doc.currency == company_currency:
+ self.doc.currency = company_currency
self.doc.conversion_rate = 1.0
else:
validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
diff --git a/hr/doctype/deduction_type/test_deduction_type.py b/hr/doctype/deduction_type/test_deduction_type.py
new file mode 100644
index 0000000..5960aac
--- /dev/null
+++ b/hr/doctype/deduction_type/test_deduction_type.py
@@ -0,0 +1,10 @@
+test_records = [
+ [{
+ "doctype": "Deduction Type",
+ "deduction_name": "_Test Professional Tax"
+ }],
+ [{
+ "doctype": "Deduction Type",
+ "deduction_name": "_Test TDS"
+ }]
+]
\ No newline at end of file
diff --git a/hr/doctype/earning_type/test_earning_type.py b/hr/doctype/earning_type/test_earning_type.py
new file mode 100644
index 0000000..ea245a2
--- /dev/null
+++ b/hr/doctype/earning_type/test_earning_type.py
@@ -0,0 +1,12 @@
+test_records = [
+ [{
+ "doctype": "Earning Type",
+ "earning_name": "_Test Basic Salary",
+ "taxable": "Yes"
+ }],
+ [{
+ "doctype": "Earning Type",
+ "earning_name": "_Test Allowance",
+ "taxable": "Yes"
+ }]
+]
\ No newline at end of file
diff --git a/hr/doctype/holiday_list/test_holiday_list.py b/hr/doctype/holiday_list/test_holiday_list.py
index e3a2d82..07b7fe5 100644
--- a/hr/doctype/holiday_list/test_holiday_list.py
+++ b/hr/doctype/holiday_list/test_holiday_list.py
@@ -1,7 +1,8 @@
test_records = [[{
"doctype": "Holiday List",
"holiday_list_name": "_Test Holiday List",
- "fiscal_year": "_Test Fiscal Year 2013"
+ "fiscal_year": "_Test Fiscal Year 2013",
+ "is_default": 1
}, {
"doctype": "Holiday",
"parent": "_Test Holiday List",
diff --git a/hr/doctype/hr_settings/__init__.py b/hr/doctype/hr_settings/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hr/doctype/hr_settings/__init__.py
diff --git a/hr/doctype/hr_settings/hr_settings.py b/hr/doctype/hr_settings/hr_settings.py
new file mode 100644
index 0000000..928aa9f
--- /dev/null
+++ b/hr/doctype/hr_settings/hr_settings.py
@@ -0,0 +1,8 @@
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+class DocType:
+ def __init__(self, d, dl):
+ self.doc, self.doclist = d, dl
\ No newline at end of file
diff --git a/hr/doctype/hr_settings/hr_settings.txt b/hr/doctype/hr_settings/hr_settings.txt
new file mode 100644
index 0000000..e3694d0
--- /dev/null
+++ b/hr/doctype/hr_settings/hr_settings.txt
@@ -0,0 +1,59 @@
+[
+ {
+ "creation": "2013-08-02 13:45:23",
+ "docstatus": 0,
+ "modified": "2013-08-02 14:22:26",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "doctype": "DocType",
+ "document_type": "Other",
+ "icon": "icon-cog",
+ "issingle": 1,
+ "module": "HR",
+ "name": "__common__"
+ },
+ {
+ "doctype": "DocField",
+ "name": "__common__",
+ "parent": "HR Settings",
+ "parentfield": "fields",
+ "parenttype": "DocType",
+ "permlevel": 0
+ },
+ {
+ "create": 1,
+ "doctype": "DocPerm",
+ "name": "__common__",
+ "parent": "HR Settings",
+ "parentfield": "permissions",
+ "parenttype": "DocType",
+ "permlevel": 0,
+ "read": 1,
+ "role": "System Manager",
+ "write": 1
+ },
+ {
+ "doctype": "DocType",
+ "name": "HR Settings"
+ },
+ {
+ "description": "Employee record is created using selected field. ",
+ "doctype": "DocField",
+ "fieldname": "emp_created_by",
+ "fieldtype": "Select",
+ "label": "Employee Records to be created by",
+ "options": "Naming Series\nEmployee Number"
+ },
+ {
+ "description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day",
+ "doctype": "DocField",
+ "fieldname": "include_holidays_in_total_working_days",
+ "fieldtype": "Check",
+ "label": "Include holidays in Total no. of Working Days"
+ },
+ {
+ "doctype": "DocPerm"
+ }
+]
\ No newline at end of file
diff --git a/hr/doctype/leave_application/test_leave_application.py b/hr/doctype/leave_application/test_leave_application.py
index 338225c..4acabda 100644
--- a/hr/doctype/leave_application/test_leave_application.py
+++ b/hr/doctype/leave_application/test_leave_application.py
@@ -236,5 +236,15 @@
"fiscal_year": "_Test Fiscal Year 2013",
"employee": "_T-Employee-0002",
"company": "_Test Company"
+ }],
+ [{
+ "doctype": "Leave Application",
+ "leave_type": "_Test Leave Type LWP",
+ "from_date": "2013-01-02",
+ "to_date": "2013-01-02",
+ "posting_date": "2013-01-02",
+ "fiscal_year": "_Test Fiscal Year 2013",
+ "employee": "_T-Employee-0001",
+ "company": "_Test Company",
}]
]
diff --git a/hr/doctype/leave_type/test_leave_type.py b/hr/doctype/leave_type/test_leave_type.py
index 4d78975..9b3b240 100644
--- a/hr/doctype/leave_type/test_leave_type.py
+++ b/hr/doctype/leave_type/test_leave_type.py
@@ -2,5 +2,10 @@
[{
"leave_type_name": "_Test Leave Type",
"doctype": "Leave Type"
+ }],
+ [{
+ "leave_type_name": "_Test Leave Type LWP",
+ "doctype": "Leave Type",
+ "is_lwp": 1
}]
]
\ No newline at end of file
diff --git a/hr/doctype/salary_manager/salary_manager.py b/hr/doctype/salary_manager/salary_manager.py
index e9b3dd7..7a8f1da 100644
--- a/hr/doctype/salary_manager/salary_manager.py
+++ b/hr/doctype/salary_manager/salary_manager.py
@@ -112,26 +112,16 @@
if not sql("""select name from `tabSalary Slip`
where docstatus!= 2 and employee = %s and month = %s and fiscal_year = %s and company = %s
""", (emp[0], self.doc.month, self.doc.fiscal_year, self.doc.company)):
- ss = Document('Salary Slip')
- ss.fiscal_year = self.doc.fiscal_year
- ss.employee = emp[0]
- ss.month = self.doc.month
- ss.email_check = self.doc.send_email
- ss.company = self.doc.company
- ss.save(1)
-
- ss_obj = get_obj('Salary Slip', ss.name, with_children=1)
- ss_obj.get_emp_and_leave_details()
- ss_obj.calculate_net_pay()
- ss_obj.validate()
- ss_obj.doc.save()
-
- for d in getlist(ss_obj.doclist, 'earning_details'):
- d.save()
- for d in getlist(ss_obj.doclist, 'deduction_details'):
- d.save()
-
- ss_list.append(ss.name)
+ ss = webnotes.bean({
+ "doctype": "Salary Slip",
+ "fiscal_year": self.doc.fiscal_year,
+ "employee": emp[0],
+ "month": self.doc.month,
+ "email_check": self.doc.send_email,
+ "company": self.doc.company,
+ })
+ ss.insert()
+ ss_list.append(ss.doc.name)
return self.create_log(ss_list)
diff --git a/hr/doctype/salary_slip/salary_slip.py b/hr/doctype/salary_slip/salary_slip.py
index 09bd602..3e943bc 100644
--- a/hr/doctype/salary_slip/salary_slip.py
+++ b/hr/doctype/salary_slip/salary_slip.py
@@ -61,7 +61,7 @@
["bank_name", "bank_ac_no", "esic_card_no", "pf_number"], as_dict=1)
if emp:
self.doc.bank_name = emp.bank_name
- self.doc.bank_ac_no = emp.bank_ac_no
+ self.doc.bank_account_no = emp.bank_ac_no
self.doc.esic_no = emp.esic_card_no
self.doc.pf_no = emp.pf_number
@@ -72,9 +72,17 @@
self.doc.month = "%02d" % getdate(nowdate()).month
m = get_obj('Salary Manager').get_month_details(self.doc.fiscal_year, self.doc.month)
+ holidays = self.get_holidays_for_employee(m)
+ if not cint(webnotes.conn.get_value("HR Settings", "HR Settings",
+ "include_holidays_in_total_working_days")):
+ m["month_days"] -= len(holidays)
+ if m["month_days"] < 0:
+ msgprint(_("Bummer! There are more holidays than working days this month."),
+ raise_exception=True)
+
if not lwp:
- lwp = self.calculate_lwp(m)
+ lwp = self.calculate_lwp(holidays, m)
self.doc.total_days_in_month = m['month_days']
self.doc.leave_without_pay = lwp
payment_days = flt(self.get_payment_days(m)) - flt(lwp)
@@ -103,11 +111,8 @@
payment_days = 0
return payment_days
-
-
-
-
- def calculate_lwp(self, m):
+
+ def get_holidays_for_employee(self, m):
holidays = sql("""select t1.holiday_date
from `tabHoliday` t1, tabEmployee t2
where t1.parent = t2.holiday_list and t2.name = %s
@@ -117,8 +122,13 @@
holidays = sql("""select t1.holiday_date
from `tabHoliday` t1, `tabHoliday List` t2
where t1.parent = t2.name and ifnull(t2.is_default, 0) = 1
- and t2.fiscal_year = %s""", self.doc.fiscal_year)
+ and t2.fiscal_year = %s
+ and t1.holiday_date between %s and %s""", (self.doc.fiscal_year,
+ m['month_start_date'], m['month_end_date']))
holidays = [cstr(i[0]) for i in holidays]
+ return holidays
+
+ def calculate_lwp(self, holidays, m):
lwp = 0
for d in range(m['month_days']):
dt = add_days(cstr(m['month_start_date']), d)
@@ -133,7 +143,7 @@
and %s between from_date and to_date
""", (self.doc.employee, dt))
if leave:
- lwp = cint(leave[0][1]) and lwp + 0.5 or lwp + 1
+ lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
return lwp
def check_existing(self):
@@ -150,17 +160,29 @@
def validate(self):
from webnotes.utils import money_in_words
self.check_existing()
+
+ if not (len(self.doclist.get({"parentfield": "earning_details"})) or
+ len(self.doclist.get({"parentfield": "deduction_details"}))):
+ self.get_emp_and_leave_details()
+ else:
+ self.get_leave_details(self.doc.leave_without_pay)
+
+ if not self.doc.net_pay:
+ self.calculate_net_pay()
+
company_currency = get_company_currency(self.doc.company)
self.doc.total_in_words = money_in_words(self.doc.rounded_total, company_currency)
def calculate_earning_total(self):
self.doc.gross_pay = flt(self.doc.arrear_amount) + flt(self.doc.leave_encashment_amount)
- for d in getlist(self.doclist, 'earning_details'):
+ for d in self.doclist.get({"parentfield": "earning_details"}):
if cint(d.e_depends_on_lwp) == 1:
d.e_modified_amount = round(flt(d.e_amount) * flt(self.doc.payment_days)
/ cint(self.doc.total_days_in_month), 2)
elif not self.doc.payment_days:
d.e_modified_amount = 0
+ else:
+ d.e_modified_amount = d.e_amount
self.doc.gross_pay += flt(d.e_modified_amount)
def calculate_ded_total(self):
@@ -171,6 +193,8 @@
/ cint(self.doc.total_days_in_month), 2)
elif not self.doc.payment_days:
d.d_modified_amount = 0
+ else:
+ d.d_modified_amount = d.d_amount
self.doc.total_deduction += flt(d.d_modified_amount)
diff --git a/hr/doctype/salary_slip/salary_slip.txt b/hr/doctype/salary_slip/salary_slip.txt
index 871c6b8..641adae 100644
--- a/hr/doctype/salary_slip/salary_slip.txt
+++ b/hr/doctype/salary_slip/salary_slip.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-01-10 16:34:15",
"docstatus": 0,
- "modified": "2013-07-05 14:53:44",
+ "modified": "2013-08-02 19:23:13",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -190,7 +190,7 @@
"doctype": "DocField",
"fieldname": "total_days_in_month",
"fieldtype": "Data",
- "label": "Total days in month",
+ "label": "Total Working Days In The Month",
"oldfieldname": "total_days_in_month",
"oldfieldtype": "Int",
"read_only": 1,
@@ -208,7 +208,7 @@
"doctype": "DocField",
"fieldname": "payment_days",
"fieldtype": "Float",
- "label": "Payment days",
+ "label": "Payment Days",
"oldfieldname": "payment_days",
"oldfieldtype": "Float",
"read_only": 1,
diff --git a/hr/doctype/salary_slip/test_salary_slip.py b/hr/doctype/salary_slip/test_salary_slip.py
new file mode 100644
index 0000000..e48bf62
--- /dev/null
+++ b/hr/doctype/salary_slip/test_salary_slip.py
@@ -0,0 +1,85 @@
+import webnotes
+import unittest
+
+class TestSalarySlip(unittest.TestCase):
+ def setUp(self):
+ webnotes.conn.sql("""delete from `tabLeave Application`""")
+ webnotes.conn.sql("""delete from `tabSalary Slip`""")
+ from hr.doctype.leave_application.test_leave_application import test_records as leave_applications
+ la = webnotes.bean(copy=leave_applications[4])
+ la.insert()
+ la.doc.status = "Approved"
+ la.submit()
+
+ def tearDown(self):
+ webnotes.conn.set_value("HR Settings", "HR Settings", "include_holidays_in_total_working_days", 0)
+
+ def test_salary_slip_with_holidays_included(self):
+ webnotes.conn.set_value("HR Settings", "HR Settings", "include_holidays_in_total_working_days", 1)
+ ss = webnotes.bean(copy=test_records[0])
+ ss.insert()
+ self.assertEquals(ss.doc.total_days_in_month, 31)
+ self.assertEquals(ss.doc.payment_days, 30)
+ self.assertEquals(ss.doclist[1].e_modified_amount, 14516.13)
+ self.assertEquals(ss.doclist[2].e_modified_amount, 500)
+ self.assertEquals(ss.doclist[3].d_modified_amount, 100)
+ self.assertEquals(ss.doclist[4].d_modified_amount, 48.39)
+ self.assertEquals(ss.doc.gross_pay, 15016.13)
+ self.assertEquals(ss.doc.net_pay, 14867.74)
+
+ def test_salary_slip_with_holidays_excluded(self):
+ ss = webnotes.bean(copy=test_records[0])
+ ss.insert()
+ self.assertEquals(ss.doc.total_days_in_month, 30)
+ self.assertEquals(ss.doc.payment_days, 29)
+ self.assertEquals(ss.doclist[1].e_modified_amount, 14500)
+ self.assertEquals(ss.doclist[2].e_modified_amount, 500)
+ self.assertEquals(ss.doclist[3].d_modified_amount, 100)
+ self.assertEquals(ss.doclist[4].d_modified_amount, 48.33)
+ self.assertEquals(ss.doc.gross_pay, 15000)
+ self.assertEquals(ss.doc.net_pay, 14851.67)
+
+test_dependencies = ["Leave Application"]
+
+test_records = [
+ [
+ {
+ "doctype": "Salary Slip",
+ "employee": "_T-Employee-0001",
+ "employee_name": "_Test Employee",
+ "company": "_Test Company",
+ "fiscal_year": "_Test Fiscal Year 2013",
+ "month": "01",
+ "total_days_in_month": 31,
+ "payment_days": 31
+ },
+ {
+ "doctype": "Salary Slip Earning",
+ "parentfield": "earning_details",
+ "e_type": "_Test Basic Salary",
+ "e_amount": 15000,
+ "e_depends_on_lwp": 1
+ },
+ {
+ "doctype": "Salary Slip Earning",
+ "parentfield": "earning_details",
+ "e_type": "_Test Allowance",
+ "e_amount": 500,
+ "e_depends_on_lwp": 0
+ },
+ {
+ "doctype": "Salary Slip Deduction",
+ "parentfield": "deduction_details",
+ "d_type": "_Test Professional Tax",
+ "d_amount": 100,
+ "d_depends_on_lwp": 0
+ },
+ {
+ "doctype": "Salary Slip Deduction",
+ "parentfield": "deduction_details",
+ "d_type": "_Test TDS",
+ "d_amount": 50,
+ "d_depends_on_lwp": 1
+ },
+ ]
+]
\ No newline at end of file
diff --git a/hr/page/hr_home/hr_home.js b/hr/page/hr_home/hr_home.js
index 21ec1e1..54c3d66 100644
--- a/hr/page/hr_home/hr_home.js
+++ b/hr/page/hr_home/hr_home.js
@@ -162,6 +162,18 @@
]
},
{
+ title: wn._("Setup"),
+ icon: "icon-cog",
+ items: [
+ {
+ "label": wn._("HR Settings"),
+ "route": "Form/HR Settings",
+ "doctype":"HR Settings",
+ "description": "Settings for HR Module"
+ }
+ ]
+ },
+ {
title: wn._("Reports"),
right: true,
icon: "icon-list",
diff --git a/patches/august_2013/__init__.py b/patches/august_2013/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/patches/august_2013/__init__.py
diff --git a/patches/august_2013/p01_hr_settings.py b/patches/august_2013/p01_hr_settings.py
new file mode 100644
index 0000000..408f1e4
--- /dev/null
+++ b/patches/august_2013/p01_hr_settings.py
@@ -0,0 +1,16 @@
+import webnotes
+
+def execute():
+ webnotes.reload_doc("hr", "doctype", "hr_settings")
+ webnotes.reload_doc("setup", "doctype", "global_defaults")
+
+ hr = webnotes.bean("HR Settings", "HR Settings")
+ hr.doc.emp_created_by = webnotes.conn.get_value("Global Defaults", "Global Defaults", "emp_created_by")
+
+ if webnotes.conn.sql("""select name from `tabSalary Slip` where docstatus=1 limit 1"""):
+ hr.doc.include_holidays_in_total_working_days = 1
+
+ hr.save()
+
+ webnotes.conn.sql("""delete from `tabSingles` where doctype = 'Global Defaults'
+ and field = 'emp_created_by'""")
\ No newline at end of file
diff --git a/patches/patch_list.py b/patches/patch_list.py
index 1426539..c4c4a83 100644
--- a/patches/patch_list.py
+++ b/patches/patch_list.py
@@ -264,4 +264,5 @@
"patches.july_2013.p10_change_partner_user_to_website_user",
"patches.july_2013.p11_update_price_list_currency",
"execute:webnotes.bean('Selling Settings').save() #2013-07-29",
+ "patches.august_2013.p01_hr_settings",
]
\ No newline at end of file
diff --git a/setup/doctype/global_defaults/global_defaults.txt b/setup/doctype/global_defaults/global_defaults.txt
index b59ca94..8f5b940 100644
--- a/setup/doctype/global_defaults/global_defaults.txt
+++ b/setup/doctype/global_defaults/global_defaults.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-02 17:53:24",
"docstatus": 0,
- "modified": "2013-07-15 15:03:01",
+ "modified": "2013-08-02 13:45:12",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -182,15 +182,6 @@
"read_only": 0
},
{
- "description": "Employee record is created using selected field. ",
- "doctype": "DocField",
- "fieldname": "emp_created_by",
- "fieldtype": "Select",
- "label": "Employee Records to be created by ",
- "options": "Naming Series\nEmployee Number",
- "read_only": 0
- },
- {
"doctype": "DocPerm"
}
]
\ No newline at end of file