Merge pull request #5538 from rohitwaghchaure/time_sheet_wages

[Enhancement] Time log wages and multiple active salary structure
This commit is contained in:
Rushabh Mehta
2016-06-27 17:13:48 +05:30
committed by GitHub
97 changed files with 3112 additions and 2670 deletions

View File

@@ -9,7 +9,7 @@ links = {
},
{
'label': _('Payroll'),
'items': ['Salary Structure', 'Salary Slip']
'items': ['Salary Structure', 'Salary Slip', 'Time Sheet']
},
{
'label': _('Expense'),

View File

@@ -166,7 +166,7 @@
"bold": 0,
"collapsible": 0,
"default": "1",
"description": "Check if you want to send salary slip in mail to each employee while submitting salary slip",
"description": "",
"fieldname": "email_salary_slip_to_employee",
"fieldtype": "Check",
"hidden": 0,
@@ -200,7 +200,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2016-06-25 17:43:06.643469",
"modified": "2016-06-27 16:20:59.737869",
"modified_by": "Administrator",
"module": "HR",
"name": "HR Settings",
@@ -220,8 +220,6 @@
"print": 1,
"read": 1,
"report": 0,
"restrict": 0,
"restricted": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,

View File

@@ -58,7 +58,8 @@ cur_frm.cscript.make_jv = function(doc, dt, dn) {
});
}
frappe.ui.form.on("Process Payroll", "refresh", function(frm) {
frm.disable_save();
});
frappe.ui.form.on("Process Payroll", {
refresh: function(frm) {
frm.disable_save();
}
})

View File

@@ -2,6 +2,7 @@
"allow_copy": 1,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2012-03-27 14:35:59",
"custom": 0,
"docstatus": 0,
@@ -180,6 +181,55 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "salary_slip_based_on_timesheet",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Salary Slip Based on Timesheet",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -503,13 +553,14 @@
"hide_toolbar": 1,
"icon": "icon-cog",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2016-04-26 07:22:41.792785",
"modified": "2016-06-22 18:14:02.418857",
"modified_by": "Administrator",
"module": "HR",
"name": "Process Payroll",
@@ -536,8 +587,10 @@
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
"sort_order": "DESC",
"track_seen": 0
}

View File

@@ -15,20 +15,18 @@ class ProcessPayroll(Document):
Returns list of active employees based on selected criteria
and for which salary structure exists
"""
cond = self.get_filter_condition()
cond += self.get_joining_releiving_condition()
emp_list = frappe.db.sql("""
select t1.name
from `tabEmployee` t1, `tabSalary Structure` t2
where t1.docstatus!=2 and t2.docstatus != 2
and t1.name = t2.employee
where t1.docstatus!=2 and t2.docstatus != 2 and
ifnull(t2.salary_slip_based_on_timesheet,0) = 0 and t1.name = t2.employee
%s """% cond)
return emp_list
def get_filter_condition(self):
self.check_mandatory()
@@ -67,6 +65,7 @@ class ProcessPayroll(Document):
""", (emp[0], self.month, self.fiscal_year, self.company)):
ss = frappe.get_doc({
"doctype": "Salary Slip",
"salary_slip_based_on_timesheet": 0,
"fiscal_year": self.fiscal_year,
"employee": emp[0],
"month": self.month,

View File

@@ -2,13 +2,51 @@
// License: GNU General Public License v3. See license.txt
cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('time_sheet', 'total_hours', 'working_hours');
frappe.ui.form.on("Salary Slip", {
setup: function(frm) {
frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(){
return {
filters: {
employee: frm.doc.employee,
make_for: 'Salary Slip'
}
}
}
},
company: function(frm) {
var company = locals[':Company'][frm.doc.company];
if(!frm.doc.letter_head && company.default_letter_head) {
frm.set_value('letter_head', company.default_letter_head);
}
},
refresh: function(frm) {
frm.trigger("toggle_fields")
},
salary_slip_based_on_timesheet: function(frm) {
frm.trigger("toggle_fields")
},
toggle_fields: function(frm) {
if(frm.doc.salary_slip_based_on_timesheet) {
hide_field(['fiscal_year', 'month', 'total_days_in_month', 'leave_without_pay', 'payment_days'])
unhide_field(['start_date', 'end_date', 'hourly_wages', 'timesheets'])
}else {
unhide_field(['fiscal_year', 'month', 'total_days_in_month', 'leave_without_pay', 'payment_days'])
hide_field(['start_date', 'end_date', 'hourly_wages', 'timesheets'])
}
}
})
frappe.ui.form.on("Salary Slip Timesheet", {
time_sheet: function(frm, cdt, cdn) {
doc = frm.doc;
cur_frm.cscript.fiscal_year(doc, cdt, cdn)
}
})
@@ -38,7 +76,13 @@ cur_frm.cscript.fiscal_year = function(doc,dt,dn){
});
}
cur_frm.cscript.month = cur_frm.cscript.employee = cur_frm.cscript.fiscal_year;
cur_frm.cscript.month = cur_frm.cscript.salary_slip_based_on_timesheet = cur_frm.cscript.fiscal_year;
cur_frm.cscript.start_date = cur_frm.cscript.end_date = cur_frm.cscript.fiscal_year;
cur_frm.cscript.employee = function(doc,dt,dn){
doc.salary_structure = ''
cur_frm.cscript.fiscal_year(doc, dt, dn)
}
cur_frm.cscript.leave_without_pay = function(doc,dt,dn){
if (doc.employee && doc.fiscal_year && doc.month) {
@@ -56,7 +100,7 @@ var calculate_all = function(doc, dt, dn) {
calculate_net_pay(doc, dt, dn);
}
cur_frm.cscript.e_modified_amount = function(doc,dt,dn){
cur_frm.cscript.earning_amount = function(doc,dt,dn){
calculate_earning_total(doc, dt, dn);
calculate_net_pay(doc, dt, dn);
}
@@ -67,7 +111,7 @@ cur_frm.cscript.e_depends_on_lwp = function(doc,dt,dn){
}
// Trigger on earning modified amount and depends on lwp
// ------------------------------------------------------------------------
cur_frm.cscript.d_modified_amount = function(doc,dt,dn){
cur_frm.cscript.deduction_amount = function(doc,dt,dn){
calculate_ded_total(doc, dt, dn);
calculate_net_pay(doc, dt, dn);
}
@@ -85,17 +129,17 @@ var calculate_earning_total = function(doc, dt, dn, reset_amount) {
var total_earn = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].e_depends_on_lwp) == 1) {
tbl[i].e_modified_amount = Math.round(tbl[i].e_amount)*(flt(doc.payment_days) /
tbl[i].earning_amount = Math.round(tbl[i].e_amount)*(flt(doc.payment_days) /
cint(doc.total_days_in_month)*100)/100;
refresh_field('e_modified_amount', tbl[i].name, 'earnings');
refresh_field('earning_amount', tbl[i].name, 'earnings');
} else if(reset_amount) {
tbl[i].e_modified_amount = tbl[i].e_amount;
refresh_field('e_modified_amount', tbl[i].name, 'earnings');
tbl[i].earning_amount = tbl[i].e_amount;
refresh_field('earning_amount', tbl[i].name, 'earnings');
}
total_earn += flt(tbl[i].e_modified_amount);
total_earn += flt(tbl[i].earning_amount);
}
doc.gross_pay = total_earn + flt(doc.arrear_amount) + flt(doc.leave_encashment_amount);
refresh_many(['e_modified_amount', 'gross_pay']);
refresh_many(['earning_amount', 'gross_pay']);
}
// Calculate deduction total
@@ -106,13 +150,13 @@ var calculate_ded_total = function(doc, dt, dn, reset_amount) {
var total_ded = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].d_depends_on_lwp) == 1) {
tbl[i].d_modified_amount = Math.round(tbl[i].d_amount)*(flt(doc.payment_days)/cint(doc.total_days_in_month)*100)/100;
refresh_field('d_modified_amount', tbl[i].name, 'deductions');
tbl[i].deduction_amount = Math.round(tbl[i].d_amount)*(flt(doc.payment_days)/cint(doc.total_days_in_month)*100)/100;
refresh_field('deduction_amount', tbl[i].name, 'deductions');
} else if(reset_amount) {
tbl[i].d_modified_amount = tbl[i].d_amount;
refresh_field('d_modified_amount', tbl[i].name, 'earnings');
tbl[i].deduction_amount = tbl[i].d_amount;
refresh_field('deduction_amount', tbl[i].name, 'earnings');
}
total_ded += flt(tbl[i].d_modified_amount);
total_ded += flt(tbl[i].deduction_amount);
}
doc.total_deduction = total_ded;
refresh_field('total_deduction');

View File

@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-01-10 16:34:15",
"custom": 0,
"docstatus": 0,
@@ -65,7 +66,7 @@
"bold": 0,
"collapsible": 0,
"fieldname": "employee_name",
"fieldtype": "Data",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -76,10 +77,11 @@
"no_copy": 0,
"oldfieldname": "employee_name",
"oldfieldtype": "Data",
"options": "employee.employee_name",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -167,6 +169,31 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"oldfieldtype": "Column Break",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "50%"
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -221,8 +248,8 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"fieldname": "section_break_10",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -230,8 +257,8 @@
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"oldfieldtype": "Column Break",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -239,41 +266,40 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "50%"
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "month",
"fieldtype": "Select",
"depends_on": "",
"fieldname": "salary_slip_based_on_timesheet",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 1,
"label": "Month",
"in_filter": 0,
"in_list_view": 0,
"label": "Salary Slip Based on Timesheet",
"length": 0,
"no_copy": 0,
"oldfieldname": "month",
"oldfieldtype": "Select",
"options": "\n01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12",
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "37%"
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "fiscal_year",
"fieldtype": "Link",
"hidden": 0,
@@ -301,6 +327,139 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "month",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 1,
"label": "Month",
"length": 0,
"no_copy": 0,
"oldfieldname": "month",
"oldfieldtype": "Select",
"options": "\n01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0,
"width": "37%"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "start_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Start Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "Today",
"depends_on": "",
"fieldname": "end_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "End Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_15",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "salary_structure",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Salary Structure",
"length": 0,
"no_copy": 0,
"options": "Salary Structure",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "total_days_in_month",
"fieldtype": "Float",
"hidden": 0,
@@ -327,6 +486,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "leave_without_pay",
"fieldtype": "Float",
"hidden": 0,
@@ -353,6 +513,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "payment_days",
"fieldtype": "Float",
"hidden": 0,
@@ -375,6 +536,159 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "hourly_wages",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "timesheets",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Salary Slip Timesheet",
"length": 0,
"no_copy": 0,
"options": "Salary Slip Timesheet",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_20",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "total_working_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Total Working Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "hour_rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Hour Rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "2",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "section_break_26",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -427,6 +741,30 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_01",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -610,52 +948,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_25",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_26",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -737,6 +1029,29 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_25",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -849,13 +1164,14 @@
"hide_toolbar": 0,
"icon": "icon-file-text",
"idx": 9,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-04-26 06:02:06.940543",
"max_attachments": 0,
"modified": "2016-06-27 16:22:46.063078",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",
@@ -923,6 +1239,7 @@
"write": 0
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",

View File

@@ -20,14 +20,16 @@ class SalarySlip(TransactionBase):
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
def validate(self):
self.validate_dates()
self.check_existing()
self.set_month_dates()
if not (len(self.get("earnings")) or len(self.get("deductions"))):
self.get_emp_and_leave_details()
else:
self.get_leave_details(lwp = self.leave_without_pay)
if not self.net_pay:
if self.salary_slip_based_on_timesheet or not self.net_pay:
self.calculate_net_pay()
company_currency = get_company_currency(self.company)
@@ -35,38 +37,85 @@ class SalarySlip(TransactionBase):
set_employee_name(self)
def validate_dates(self):
if date_diff(self.end_date, self.start_date) < 0:
frappe.throw(_("To date cannot be before From date"))
def get_emp_and_leave_details(self):
if self.employee:
self.set("earnings", [])
self.set("deductions", [])
self.set_month_dates()
self.validate_dates()
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
["date_of_joining", "relieving_date"])
self.get_leave_details(joining_date, relieving_date)
struct = self.check_sal_struct(joining_date, relieving_date)
if struct:
self.set("earnings", [])
self.set("deduction", [])
self.pull_sal_struct(struct)
ss_doc = frappe.get_doc('Salary Structure', struct)
self.salary_slip_based_on_timesheet = ss_doc.salary_slip_based_on_timesheet or 0
self.set_time_sheet()
self.pull_sal_struct(ss_doc)
def set_time_sheet(self):
if self.salary_slip_based_on_timesheet and not self.get('timesheets'):
self.set("timesheets", [])
timesheets = frappe.db.sql(""" select * from `tabTime Sheet` where employee = %(employee)s and (status = 'Submitted' or
status = 'Billed')""", {'employee': self.employee}, as_dict=1)
for data in timesheets:
self.append('timesheets', {
'time_sheet': data.name,
'working_hours': data.total_hours
})
def set_month_dates(self):
if self.month and not self.salary_slip_based_on_timesheet:
m = get_month_details(self.fiscal_year, self.month)
self.start_date = m['month_start_date']
self.end_date = m['month_end_date']
def check_sal_struct(self, joining_date, relieving_date):
m = get_month_details(self.fiscal_year, self.month)
timesheet = 1 if self.salary_slip_based_on_timesheet else 0
struct = frappe.db.sql("""select name from `tabSalary Structure`
where employee=%s and is_active = 'Yes'
and (from_date <= %s or from_date <= %s)
and (to_date is null or to_date >= %s or to_date >= %s)""",
(self.employee, m.month_start_date, joining_date, m.month_end_date, relieving_date))
and (to_date is null or to_date >= %s or to_date >= %s) and salary_slip_based_on_timesheet=%s""",
(self.employee, self.start_date, joining_date, self.end_date, relieving_date, timesheet))
if not struct:
msgprint(_("No active Salary Structure found for employee {0} and the month")
self.salary_structure = None
msgprint(_("No active or default Salary Structure found for employee {0} and the month")
.format(self.employee))
self.employee = None
return struct and struct[0][0] or ''
def pull_sal_struct(self, struct):
def pull_sal_struct(self, ss_doc):
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
make_salary_slip(struct, self)
make_salary_slip(ss_doc.name, self)
if self.salary_slip_based_on_timesheet:
self.salary_structure = ss_doc.name
self.hour_rate = ss_doc.hour_rate
self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0
self.add_earning_for_hourly_wages(ss_doc.earning_type)
def add_earning_for_hourly_wages(self, earning_type):
default_type = False
for data in self.earnings:
if data.earning_type == earning_type:
data.earning_amount = self.hour_rate * self.total_working_hours
default_type = True
break
if not default_type:
earnings = self.append('earnings', {})
earnings.earning_type = earning_type
earnings.earning_amount = self.hour_rate * self.total_working_hours
def pull_emp_details(self):
emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no"], as_dict=1)
@@ -81,40 +130,41 @@ class SalarySlip(TransactionBase):
if not self.month:
self.month = "%02d" % getdate(nowdate()).month
self.set_month_dates()
if not joining_date:
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
["date_of_joining", "relieving_date"])
m = get_month_details(self.fiscal_year, self.month)
holidays = self.get_holidays_for_employee(m['month_start_date'], m['month_end_date'])
holidays = self.get_holidays_for_employee(self.start_date, self.end_date)
working_days = m["month_days"]
working_days = date_diff(self.end_date, self.start_date) + 1
if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")):
working_days -= len(holidays)
if working_days < 0:
frappe.throw(_("There are more holidays than working days this month."))
if not lwp:
lwp = self.calculate_lwp(holidays, m)
lwp = self.calculate_lwp(holidays, working_days)
self.total_days_in_month = working_days
self.leave_without_pay = lwp
payment_days = flt(self.get_payment_days(m, joining_date, relieving_date)) - flt(lwp)
payment_days = flt(self.get_payment_days(joining_date, relieving_date)) - flt(lwp)
self.payment_days = payment_days > 0 and payment_days or 0
def get_payment_days(self, month, joining_date, relieving_date):
start_date = month['month_start_date']
def get_payment_days(self, joining_date, relieving_date):
start_date = getdate(self.start_date)
if joining_date:
if joining_date > month['month_start_date']:
if joining_date > getdate(self.start_date):
start_date = joining_date
elif joining_date > month['month_end_date']:
elif joining_date > getdate(self.end_date):
return
end_date = month['month_end_date']
end_date = getdate(self.end_date)
if relieving_date:
if relieving_date > start_date and relieving_date < month['month_end_date']:
if relieving_date > start_date and relieving_date < getdate(self.end_date):
end_date = relieving_date
elif relieving_date < month['month_start_date']:
elif relieving_date < getdate(self.start_date):
frappe.throw(_("Employee relieved on {0} must be set as 'Left'")
.format(relieving_date))
@@ -142,10 +192,10 @@ class SalarySlip(TransactionBase):
return holidays
def calculate_lwp(self, holidays, m):
def calculate_lwp(self, holidays, working_days):
lwp = 0
for d in range(m['month_days']):
dt = add_days(cstr(m['month_start_date']), d)
for d in range(working_days):
dt = add_days(cstr(self.start_date), d)
if dt not in holidays:
leave = frappe.db.sql("""
select t1.name, t1.half_day
@@ -161,38 +211,43 @@ class SalarySlip(TransactionBase):
return lwp
def check_existing(self):
ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
where month = %s and fiscal_year = %s and docstatus != 2
and employee = %s and name != %s""",
(self.month, self.fiscal_year, self.employee, self.name))
if ret_exist:
self.employee = ''
frappe.throw(_("Salary Slip of employee {0} already created for this month").format(self.employee))
if not self.salary_slip_based_on_timesheet:
ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
where month = %s and fiscal_year = %s and docstatus != 2
and employee = %s and name != %s""",
(self.month, self.fiscal_year, self.employee, self.name))
if ret_exist:
self.employee = ''
frappe.throw(_("Salary Slip of employee {0} already created for this period").format(self.employee))
else:
for data in self.timesheets:
if frappe.db.get_value('Time Sheet', data.time_sheet, 'status') == 'Payrolled':
frappe.throw(_("Salary Slip of employee {0} already created for time sheet {1}").format(self.employee, data.time_sheet))
def calculate_earning_total(self):
self.gross_pay = flt(self.arrear_amount) + flt(self.leave_encashment_amount)
for d in self.get("earnings"):
if cint(d.e_depends_on_lwp) == 1:
d.e_modified_amount = rounded((flt(d.e_amount) * flt(self.payment_days)
/ cint(self.total_days_in_month)), self.precision("e_modified_amount", "earnings"))
d.earning_amount = rounded((flt(d.e_amount) * flt(self.payment_days)
/ cint(self.total_days_in_month)), self.precision("earning_amount", "earnings"))
elif not self.payment_days:
d.e_modified_amount = 0
elif not d.e_modified_amount:
d.e_modified_amount = d.e_amount
self.gross_pay += flt(d.e_modified_amount)
d.earning_amount = 0
elif not d.earning_amount:
d.earning_amount = d.e_amount
self.gross_pay += flt(d.earning_amount)
def calculate_ded_total(self):
self.total_deduction = 0
for d in self.get('deductions'):
if cint(d.d_depends_on_lwp) == 1:
d.d_modified_amount = rounded((flt(d.d_amount) * flt(self.payment_days)
/ cint(self.total_days_in_month)), self.precision("d_modified_amount", "deductions"))
d.deduction_amount = rounded((flt(d.d_amount) * flt(self.payment_days)
/ cint(self.total_days_in_month)), self.precision("deduction_amount", "deductions"))
elif not self.payment_days:
d.d_modified_amount = 0
elif not d.d_modified_amount:
d.d_modified_amount = d.d_amount
d.deduction_amount = 0
elif not d.deduction_amount:
d.deduction_amount = d.d_amount
self.total_deduction += flt(d.d_modified_amount)
self.total_deduction += flt(d.deduction_amount)
def calculate_net_pay(self):
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
@@ -204,16 +259,28 @@ class SalarySlip(TransactionBase):
self.precision("net_pay") if disable_rounded_total else 0)
def on_submit(self):
self.update_status(self.name)
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")):
self.email_salary_slip()
def on_cancel(self):
self.update_status()
def email_salary_slip(self):
receiver = frappe.db.get_value("Employee", self.employee, "company_email") or \
frappe.db.get_value("Employee", self.employee, "personal_email")
if receiver:
subj = 'Salary Slip - ' + cstr(self.month) +'/'+cstr(self.fiscal_year)
subj = 'Salary Slip - from {0} to {1}, fiscal year {2}'.format(self.start_date, self.end_date, self.fiscal_year)
frappe.sendmail([receiver], subject=subj, message = _("Please see attachment"),
attachments=[frappe.attach_print(self.doctype, self.name, file_name=self.name)], reference_doctype= self.doctype, reference_name= self.name)
else:
msgprint(_("{0}: Employee email not found, hence email not sent").format(self.employee_name))
def update_status(self, salary_slip=None):
for data in self.timesheets:
if data.time_sheet:
timesheet = frappe.get_doc('Time Sheet', data.time_sheet)
timesheet.salary_slip = salary_slip
timesheet.flags.ignore_validate_update_after_submit = True
timesheet.set_status()
timesheet.save()

View File

@@ -5,14 +5,14 @@
{
"d_amount": 100,
"d_depends_on_lwp": 0,
"d_type": "_Test Professional Tax",
"deduction_type": "_Test Professional Tax",
"doctype": "Salary Slip Deduction",
"parentfield": "deductions"
},
{
"d_amount": 50,
"d_depends_on_lwp": 1,
"d_type": "_Test TDS",
"deduction_type": "_Test TDS",
"doctype": "Salary Slip Deduction",
"parentfield": "deductions"
}
@@ -23,14 +23,14 @@
"doctype": "Salary Slip Earning",
"e_amount": 15000,
"e_depends_on_lwp": 1,
"e_type": "_Test Basic Salary",
"earning_type": "_Test Basic Salary",
"parentfield": "earnings"
},
{
"doctype": "Salary Slip Earning",
"e_amount": 500,
"e_depends_on_lwp": 0,
"e_type": "_Test Allowance",
"earning_type": "_Test Allowance",
"parentfield": "earnings"
}
],

View File

@@ -4,7 +4,7 @@ from __future__ import unicode_literals
import unittest
import frappe
from frappe.utils import today
from frappe.utils import today, now_datetime, getdate, cstr
from erpnext.hr.doctype.employee.employee import make_salary_structure
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record
@@ -35,10 +35,10 @@ class TestSalarySlip(unittest.TestCase):
self.assertEquals(ss.total_days_in_month, 31)
self.assertEquals(ss.payment_days, 30)
self.assertEquals(ss.earnings[0].e_modified_amount, 14516.13)
self.assertEquals(ss.earnings[1].e_modified_amount, 500)
self.assertEquals(ss.deductions[0].d_modified_amount, 100)
self.assertEquals(ss.deductions[1].d_modified_amount, 48.39)
self.assertEquals(ss.earnings[0].earning_amount, 14516.13)
self.assertEquals(ss.earnings[1].earning_amount, 500)
self.assertEquals(ss.deductions[0].deduction_amount, 100)
self.assertEquals(ss.deductions[1].deduction_amount, 48.39)
self.assertEquals(ss.gross_pay, 15016.13)
self.assertEquals(ss.net_pay, 14867.74)
@@ -49,10 +49,10 @@ class TestSalarySlip(unittest.TestCase):
self.assertEquals(ss.total_days_in_month, 29)
self.assertEquals(ss.payment_days, 28)
self.assertEquals(ss.earnings[0].e_modified_amount, 14482.76)
self.assertEquals(ss.earnings[1].e_modified_amount, 500)
self.assertEquals(ss.deductions[0].d_modified_amount, 100)
self.assertEquals(ss.deductions[1].d_modified_amount, 48.28)
self.assertEquals(ss.earnings[0].earning_amount, 14482.76)
self.assertEquals(ss.earnings[1].earning_amount, 500)
self.assertEquals(ss.deductions[0].deduction_amount, 100)
self.assertEquals(ss.deductions[1].deduction_amount, 48.28)
self.assertEquals(ss.gross_pay, 14982.76)
self.assertEquals(ss.net_pay, 14834.48)
@@ -153,6 +153,13 @@ class TestSalarySlip(unittest.TestCase):
return salary_slip
def make_activity_for_employee(self):
activity_type = frappe.get_doc("Activity Type", "_Test Activity Type")
activity_type.billing_rate = 50
activity_type.costing_rate = 20
activity_type.wage_rate = 25
activity_type.save()
test_dependencies = ["Leave Application", "Holiday List"]
test_records = frappe.get_test_records('Salary Slip')

View File

@@ -2,19 +2,22 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-02-22 01:27:48",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "d_type",
"fieldname": "deduction_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Type",
@@ -25,6 +28,7 @@
"options": "Deduction Type",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "200px",
"read_only": 0,
"report_hide": 0,
@@ -42,6 +46,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Default Amount",
@@ -52,6 +57,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -67,6 +73,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Depends on Leave Without Pay",
@@ -74,6 +81,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -85,10 +93,11 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "d_modified_amount",
"fieldname": "deduction_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
@@ -97,6 +106,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -108,18 +118,22 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2015-11-16 06:29:55.610195",
"modified": "2016-06-27 16:02:25.613138",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip Deduction",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0
"read_only_onload": 0,
"sort_order": "ASC",
"track_seen": 0
}

View File

@@ -2,19 +2,22 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-02-22 01:27:48",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "e_type",
"fieldname": "earning_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Type",
@@ -25,6 +28,7 @@
"options": "Earning Type",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "",
"read_only": 0,
"report_hide": 0,
@@ -42,6 +46,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Default Amount",
@@ -52,6 +57,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
@@ -67,6 +73,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Depends on Leave Without Pay",
@@ -74,6 +81,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -85,10 +93,11 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "e_modified_amount",
"fieldname": "earning_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
@@ -97,6 +106,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -108,18 +118,22 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2015-11-16 06:29:55.649418",
"modified": "2016-06-27 15:56:04.146109",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip Earning",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0
"read_only_onload": 0,
"sort_order": "ASC",
"track_seen": 0
}

View File

@@ -0,0 +1,87 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2016-06-14 19:22:29.811658",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "time_sheet",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Time Sheet",
"length": 0,
"no_copy": 0,
"options": "Time Sheet",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "working_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Working Hours",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "3",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-06-15 17:59:57.876373",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip Timesheet",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, 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 SalarySlipTimesheet(Document):
pass

View File

@@ -12,7 +12,7 @@ cur_frm.cscript.onload = function(doc, dt, dn){
}
cur_frm.cscript.refresh = function(doc, dt, dn){
if((!doc.__islocal) && (doc.is_active == 'Yes')){
if((!doc.__islocal) && (doc.is_active == 'Yes') && (doc.salary_slip_based_on_timesheet == 0)){
cur_frm.add_custom_button(__('Salary Slip'),
cur_frm.cscript['Make Salary Slip'], __("Make"));
cur_frm.page.set_inner_btn_group_as_primary(__("Make"));
@@ -52,7 +52,11 @@ var calculate_totals = function(doc, cdt, cdn) {
}
doc.total_earning = total_earn;
doc.total_deduction = total_ded;
doc.net_pay = flt(total_earn) - flt(total_ded);
doc.net_pay = 0.0
if(doc.salary_slip_based_on_timesheet == 0){
doc.net_pay = flt(total_earn) - flt(total_ded);
}
refresh_many(['total_earning', 'total_deduction', 'net_pay']);
}

View File

@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 0,
"beta": 0,
"creation": "2013-03-07 18:50:29",
"custom": 0,
"docstatus": 0,
@@ -269,6 +270,33 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "No",
"fieldname": "is_default",
"fieldtype": "Select",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Default",
"length": 0,
"no_copy": 1,
"options": "Yes\nNo",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -325,6 +353,136 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:!doc.__islocal",
"fieldname": "salary_slip_based_on_timesheet",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Salary Slip Based on Timesheet",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:doc.salary_slip_based_on_timesheet",
"fieldname": "section_break_15",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"description": "Earning type for timesheet based payroll.",
"fieldname": "earning_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Earning Type",
"length": 0,
"no_copy": 0,
"options": "Earning Type",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_17",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "hour_rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Hour Rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "2",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"description": "Salary breakup based on Earning and Deduction.",
"fieldname": "earning_deduction",
"fieldtype": "Section Break",
@@ -333,7 +491,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Monthly Earning & Deduction",
"label": "",
"length": 0,
"no_copy": 0,
"oldfieldname": "earning_deduction",
@@ -379,6 +537,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "earnings",
"fieldtype": "Table",
"hidden": 0,
@@ -397,7 +556,7 @@
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
@@ -460,7 +619,8 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break0",
"depends_on": "",
"fieldname": "net_pay_detail",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -612,13 +772,14 @@
"hide_toolbar": 0,
"icon": "icon-file-text",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-03-18 16:10:30.031811",
"modified": "2016-06-27 16:23:16.856237",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure",
@@ -649,7 +810,7 @@
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
@@ -665,10 +826,12 @@
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"timeline_field": "employee",
"title_field": "employee_name"
"title_field": "employee_name",
"track_seen": 0
}

View File

@@ -16,7 +16,7 @@ class SalaryStructure(Document):
self.name = make_autoname(self.employee + '/.SST' + '/.#####')
def validate(self):
self.check_existing()
self.check_overlap()
self.validate_amount()
self.validate_employee()
self.validate_joining_date()
@@ -48,43 +48,44 @@ class SalaryStructure(Document):
for li in list1:
child = self.append(tab_fname, {})
if(tab_fname == 'earnings'):
child.e_type = cstr(li[0])
child.earning_type = cstr(li[0])
child.modified_value = 0
elif(tab_fname == 'deductions'):
child.d_type = cstr(li[0])
child.deduction_type = cstr(li[0])
child.d_modified_amt = 0
def make_earn_ded_table(self):
self.make_table('Earning Type','earnings','Salary Structure Earning')
self.make_table('Deduction Type','deductions', 'Salary Structure Deduction')
def check_existing(self):
ret = self.get_other_active_salary_structure()
if ret and self.is_active=='Yes':
frappe.throw(_("Another Salary Structure {0} is active for employee {1}. Please make its status 'Inactive' to proceed.").format(ret, self.employee))
def get_other_active_salary_structure(self):
ret = frappe.db.sql("""select name from `tabSalary Structure` where is_active = 'Yes'
and employee = %s and name!=%s""", (self.employee,self.name))
return ret[0][0] if ret else None
def before_test_insert(self):
"""Make any existing salary structure for employee inactive."""
ret = self.get_other_active_salary_structure()
if ret:
frappe.db.set_value("Salary Structure", ret, "is_active", "No")
def check_overlap(self):
existing = frappe.db.sql("""select name from `tabSalary Structure`
where employee = %(employee)s and
(
(%(from_date)s > from_date and %(from_date)s < to_date) or
(%(to_date)s > from_date and %(to_date)s < to_date) or
(%(from_date)s <= from_date and %(to_date)s >= to_date))
and name!=%(name)s
and docstatus < 2""",
{
"employee": self.employee,
"from_date": self.from_date,
"to_date": self.to_date,
"name": self.name or "No Name"
}, as_dict=True)
if existing:
frappe.throw(_("Salary structure {0} already exist, more than one salary structure for same period is not allowed").format(existing[0].name))
def validate_amount(self):
if flt(self.net_pay) < 0:
if flt(self.net_pay) < 0 and self.salary_slip_based_on_timesheet:
frappe.throw(_("Net pay cannot be negative"))
def validate_employee(self):
old_employee = frappe.db.get_value("Salary Structure", self.name, "employee")
if old_employee and self.employee != old_employee:
frappe.throw(_("Employee can not be changed"))
def validate_joining_date(self):
joining_date = getdate(frappe.db.get_value("Employee", self.employee, "date_of_joining"))
if getdate(self.from_date) < joining_date:
@@ -93,6 +94,7 @@ class SalaryStructure(Document):
@frappe.whitelist()
def make_salary_slip(source_name, target_doc=None):
def postprocess(source, target):
target.salary_structure = source.name
target.run_method("pull_emp_details")
target.run_method("get_leave_details")
target.run_method("calculate_net_pay")
@@ -109,7 +111,7 @@ def make_salary_slip(source_name, target_doc=None):
"field_map": [
["depend_on_lwp", "d_depends_on_lwp"],
["d_modified_amt", "d_amount"],
["d_modified_amt", "d_modified_amount"]
["d_modified_amt", "deduction_amount"]
],
"add_if_empty": True
},
@@ -117,8 +119,8 @@ def make_salary_slip(source_name, target_doc=None):
"doctype": "Salary Slip Earning",
"field_map": [
["depend_on_lwp", "e_depends_on_lwp"],
["modified_value", "e_modified_amount"],
["modified_value", "e_amount"]
["modified_value", "e_amount"],
["modified_value", "earning_amount"]
],
"add_if_empty": True
}

View File

@@ -6,10 +6,10 @@
"from_date": "2014-02-01",
"earnings": [
{
"e_type": "_Test Basic Salary"
"earning_type": "_Test Basic Salary"
},
{
"e_type": "_Test Allowance"
"earning_type": "_Test Allowance"
}
]
}

View File

@@ -2,19 +2,22 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-02-22 01:27:48",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "d_type",
"fieldname": "deduction_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Type",
@@ -25,6 +28,7 @@
"options": "Deduction Type",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "200px",
"read_only": 0,
"report_hide": 0,
@@ -42,6 +46,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
@@ -52,6 +57,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -67,6 +73,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Reduce Deduction for Leave Without Pay (LWP)",
@@ -76,6 +83,7 @@
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -87,18 +95,21 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2015-11-16 06:29:55.793274",
"modified": "2016-06-27 15:36:26.491643",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure Deduction",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0
"read_only_onload": 0,
"track_seen": 0
}

View File

@@ -2,6 +2,7 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-02-22 01:27:48",
"custom": 0,
"docstatus": 0,
@@ -11,10 +12,11 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "e_type",
"fieldname": "earning_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Type",
@@ -25,6 +27,7 @@
"options": "Earning Type",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": "200px",
"read_only": 0,
"report_hide": 0,
@@ -42,6 +45,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
@@ -52,6 +56,7 @@
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -67,6 +72,7 @@
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Reduce Earning for Leave Without Pay (LWP)",
@@ -76,6 +82,7 @@
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -87,18 +94,22 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2015-11-16 06:29:55.830492",
"modified": "2016-06-27 15:28:58.884891",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure Earning",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0
"read_only_onload": 0,
"sort_order": "ASC",
"track_seen": 0
}

View File

@@ -42,12 +42,12 @@ def get_columns(salary_slips):
_("Payment Days") + ":Float:120"
]
earning_types = frappe.db.sql_list("""select distinct e_type from `tabSalary Slip Earning`
where e_modified_amount != 0 and parent in (%s)""" %
earning_types = frappe.db.sql_list("""select distinct earning_type from `tabSalary Slip Earning`
where earning_amount != 0 and parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]))
ded_types = frappe.db.sql_list("""select distinct d_type from `tabSalary Slip Deduction`
where d_modified_amount != 0 and parent in (%s)""" %
ded_types = frappe.db.sql_list("""select distinct deduction_type from `tabSalary Slip Deduction`
where deduction_amount != 0 and parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]))
columns = columns + [(e + ":Currency:120") for e in earning_types] + \
@@ -83,25 +83,25 @@ def get_conditions(filters):
return conditions, filters
def get_ss_earning_map(salary_slips):
ss_earnings = frappe.db.sql("""select parent, e_type, e_modified_amount
ss_earnings = frappe.db.sql("""select parent, earning_type, earning_amount
from `tabSalary Slip Earning` where parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1)
ss_earning_map = {}
for d in ss_earnings:
ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.e_type, [])
ss_earning_map[d.parent][d.e_type] = flt(d.e_modified_amount)
ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.earning_type, [])
ss_earning_map[d.parent][d.earning_type] = flt(d.earning_amount)
return ss_earning_map
def get_ss_ded_map(salary_slips):
ss_deductions = frappe.db.sql("""select parent, d_type, d_modified_amount
ss_deductions = frappe.db.sql("""select parent, deduction_type, deduction_amount
from `tabSalary Slip Deduction` where parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1)
ss_ded_map = {}
for d in ss_deductions:
ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.d_type, [])
ss_ded_map[d.parent][d.d_type] = flt(d.d_modified_amount)
ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.deduction_type, [])
ss_ded_map[d.parent][d.deduction_type] = flt(d.deduction_amount)
return ss_ded_map