[salary slip] total no. of working days calculation [issue] webnotes/erpnext#285

This commit is contained in:
Anand Doshi 2013-08-02 19:31:41 +05:30
parent 6cd2ed88bb
commit e79d57ca19
18 changed files with 271 additions and 46 deletions

View File

@ -141,8 +141,9 @@ class AccountsController(TransactionBase):
def calculate_taxes_and_totals(self): def calculate_taxes_and_totals(self):
# validate conversion rate # validate conversion rate
if not self.doc.currency: company_currency = get_company_currency(self.doc.company)
self.doc.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 self.doc.conversion_rate = 1.0
else: else:
validate_conversion_rate(self.doc.currency, self.doc.conversion_rate, validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,

View File

@ -0,0 +1,10 @@
test_records = [
[{
"doctype": "Deduction Type",
"deduction_name": "_Test Professional Tax"
}],
[{
"doctype": "Deduction Type",
"deduction_name": "_Test TDS"
}]
]

View File

@ -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"
}]
]

View File

@ -1,7 +1,8 @@
test_records = [[{ test_records = [[{
"doctype": "Holiday List", "doctype": "Holiday List",
"holiday_list_name": "_Test 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", "doctype": "Holiday",
"parent": "_Test Holiday List", "parent": "_Test Holiday List",

View File

View File

@ -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

View File

@ -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"
}
]

View File

@ -236,5 +236,15 @@ test_records = [
"fiscal_year": "_Test Fiscal Year 2013", "fiscal_year": "_Test Fiscal Year 2013",
"employee": "_T-Employee-0002", "employee": "_T-Employee-0002",
"company": "_Test Company" "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",
}] }]
] ]

View File

@ -2,5 +2,10 @@ test_records = [
[{ [{
"leave_type_name": "_Test Leave Type", "leave_type_name": "_Test Leave Type",
"doctype": "Leave Type" "doctype": "Leave Type"
}],
[{
"leave_type_name": "_Test Leave Type LWP",
"doctype": "Leave Type",
"is_lwp": 1
}] }]
] ]

View File

@ -112,26 +112,16 @@ class DocType:
if not sql("""select name from `tabSalary Slip` if not sql("""select name from `tabSalary Slip`
where docstatus!= 2 and employee = %s and month = %s and fiscal_year = %s and company = %s 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)): """, (emp[0], self.doc.month, self.doc.fiscal_year, self.doc.company)):
ss = Document('Salary Slip') ss = webnotes.bean({
ss.fiscal_year = self.doc.fiscal_year "doctype": "Salary Slip",
ss.employee = emp[0] "fiscal_year": self.doc.fiscal_year,
ss.month = self.doc.month "employee": emp[0],
ss.email_check = self.doc.send_email "month": self.doc.month,
ss.company = self.doc.company "email_check": self.doc.send_email,
ss.save(1) "company": self.doc.company,
})
ss_obj = get_obj('Salary Slip', ss.name, with_children=1) ss.insert()
ss_obj.get_emp_and_leave_details() ss_list.append(ss.doc.name)
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)
return self.create_log(ss_list) return self.create_log(ss_list)

View File

@ -61,7 +61,7 @@ class DocType(TransactionBase):
["bank_name", "bank_ac_no", "esic_card_no", "pf_number"], as_dict=1) ["bank_name", "bank_ac_no", "esic_card_no", "pf_number"], as_dict=1)
if emp: if emp:
self.doc.bank_name = emp.bank_name 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.esic_no = emp.esic_card_no
self.doc.pf_no = emp.pf_number self.doc.pf_no = emp.pf_number
@ -72,9 +72,17 @@ class DocType(TransactionBase):
self.doc.month = "%02d" % getdate(nowdate()).month self.doc.month = "%02d" % getdate(nowdate()).month
m = get_obj('Salary Manager').get_month_details(self.doc.fiscal_year, self.doc.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: 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.total_days_in_month = m['month_days']
self.doc.leave_without_pay = lwp self.doc.leave_without_pay = lwp
payment_days = flt(self.get_payment_days(m)) - flt(lwp) payment_days = flt(self.get_payment_days(m)) - flt(lwp)
@ -103,11 +111,8 @@ class DocType(TransactionBase):
payment_days = 0 payment_days = 0
return payment_days return payment_days
def get_holidays_for_employee(self, m):
def calculate_lwp(self, m):
holidays = sql("""select t1.holiday_date holidays = sql("""select t1.holiday_date
from `tabHoliday` t1, tabEmployee t2 from `tabHoliday` t1, tabEmployee t2
where t1.parent = t2.holiday_list and t2.name = %s where t1.parent = t2.holiday_list and t2.name = %s
@ -117,8 +122,13 @@ class DocType(TransactionBase):
holidays = sql("""select t1.holiday_date holidays = sql("""select t1.holiday_date
from `tabHoliday` t1, `tabHoliday List` t2 from `tabHoliday` t1, `tabHoliday List` t2
where t1.parent = t2.name and ifnull(t2.is_default, 0) = 1 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] holidays = [cstr(i[0]) for i in holidays]
return holidays
def calculate_lwp(self, holidays, m):
lwp = 0 lwp = 0
for d in range(m['month_days']): for d in range(m['month_days']):
dt = add_days(cstr(m['month_start_date']), d) dt = add_days(cstr(m['month_start_date']), d)
@ -133,7 +143,7 @@ class DocType(TransactionBase):
and %s between from_date and to_date and %s between from_date and to_date
""", (self.doc.employee, dt)) """, (self.doc.employee, dt))
if leave: 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 return lwp
def check_existing(self): def check_existing(self):
@ -150,17 +160,29 @@ class DocType(TransactionBase):
def validate(self): def validate(self):
from webnotes.utils import money_in_words from webnotes.utils import money_in_words
self.check_existing() 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) company_currency = get_company_currency(self.doc.company)
self.doc.total_in_words = money_in_words(self.doc.rounded_total, company_currency) self.doc.total_in_words = money_in_words(self.doc.rounded_total, company_currency)
def calculate_earning_total(self): def calculate_earning_total(self):
self.doc.gross_pay = flt(self.doc.arrear_amount) + flt(self.doc.leave_encashment_amount) 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: if cint(d.e_depends_on_lwp) == 1:
d.e_modified_amount = round(flt(d.e_amount) * flt(self.doc.payment_days) d.e_modified_amount = round(flt(d.e_amount) * flt(self.doc.payment_days)
/ cint(self.doc.total_days_in_month), 2) / cint(self.doc.total_days_in_month), 2)
elif not self.doc.payment_days: elif not self.doc.payment_days:
d.e_modified_amount = 0 d.e_modified_amount = 0
else:
d.e_modified_amount = d.e_amount
self.doc.gross_pay += flt(d.e_modified_amount) self.doc.gross_pay += flt(d.e_modified_amount)
def calculate_ded_total(self): def calculate_ded_total(self):
@ -171,6 +193,8 @@ class DocType(TransactionBase):
/ cint(self.doc.total_days_in_month), 2) / cint(self.doc.total_days_in_month), 2)
elif not self.doc.payment_days: elif not self.doc.payment_days:
d.d_modified_amount = 0 d.d_modified_amount = 0
else:
d.d_modified_amount = d.d_amount
self.doc.total_deduction += flt(d.d_modified_amount) self.doc.total_deduction += flt(d.d_modified_amount)

View File

@ -2,7 +2,7 @@
{ {
"creation": "2013-01-10 16:34:15", "creation": "2013-01-10 16:34:15",
"docstatus": 0, "docstatus": 0,
"modified": "2013-07-05 14:53:44", "modified": "2013-08-02 19:23:13",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -190,7 +190,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "total_days_in_month", "fieldname": "total_days_in_month",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Total days in month", "label": "Total Working Days In The Month",
"oldfieldname": "total_days_in_month", "oldfieldname": "total_days_in_month",
"oldfieldtype": "Int", "oldfieldtype": "Int",
"read_only": 1, "read_only": 1,
@ -208,7 +208,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "payment_days", "fieldname": "payment_days",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Payment days", "label": "Payment Days",
"oldfieldname": "payment_days", "oldfieldname": "payment_days",
"oldfieldtype": "Float", "oldfieldtype": "Float",
"read_only": 1, "read_only": 1,

View File

@ -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
},
]
]

View File

@ -161,6 +161,18 @@ wn.module_page["HR"] = [
}, },
] ]
}, },
{
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"), title: wn._("Reports"),
right: true, right: true,

View File

View File

@ -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'""")

View File

@ -264,4 +264,5 @@ patch_list = [
"patches.july_2013.p10_change_partner_user_to_website_user", "patches.july_2013.p10_change_partner_user_to_website_user",
"patches.july_2013.p11_update_price_list_currency", "patches.july_2013.p11_update_price_list_currency",
"execute:webnotes.bean('Selling Settings').save() #2013-07-29", "execute:webnotes.bean('Selling Settings').save() #2013-07-29",
"patches.august_2013.p01_hr_settings",
] ]

View File

@ -2,7 +2,7 @@
{ {
"creation": "2013-05-02 17:53:24", "creation": "2013-05-02 17:53:24",
"docstatus": 0, "docstatus": 0,
"modified": "2013-07-15 15:03:01", "modified": "2013-08-02 13:45:12",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@ -181,15 +181,6 @@
"label": "HR", "label": "HR",
"read_only": 0 "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" "doctype": "DocPerm"
} }