Introduce the ability to specify in a Salary Structure that a component is statistical. This allows components to be used in calculations without being added/deducted from earnings deductions.
This commit is contained in:
parent
4782e8b751
commit
64f29f819a
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
|
"allow_guest_to_view": 0,
|
||||||
"allow_import": 0,
|
"allow_import": 0,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
"beta": 0,
|
"beta": 0,
|
||||||
@ -21,7 +22,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"label": "Component",
|
"label": "Component",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -31,6 +34,7 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -49,7 +53,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"label": "Abbr",
|
"label": "Abbr",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -59,6 +65,65 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"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_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ",
|
||||||
|
"fieldname": "statistical_component",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"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": "Statistical Component",
|
||||||
|
"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,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -76,7 +141,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -84,6 +151,7 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -102,7 +170,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"label": "Condition",
|
"label": "Condition",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -111,6 +181,7 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -130,7 +201,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"label": "Amount based on formula",
|
"label": "Amount based on formula",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -140,6 +213,7 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -160,7 +234,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"label": "Formula",
|
"label": "Formula",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -169,6 +245,7 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -187,7 +264,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -197,6 +276,7 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -215,7 +295,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"label": "Depends on Leave Without Pay",
|
"label": "Depends on Leave Without Pay",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -224,6 +306,7 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -242,7 +325,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"label": "Default Amount",
|
"label": "Default Amount",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -252,6 +337,7 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -270,7 +356,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -278,6 +366,7 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -296,7 +385,9 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
"label": "Condition and Formula Help",
|
"label": "Condition and Formula Help",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -306,6 +397,7 @@
|
|||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
@ -313,18 +405,18 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"has_web_view": 0,
|
||||||
"hide_heading": 0,
|
"hide_heading": 0,
|
||||||
"hide_toolbar": 0,
|
"hide_toolbar": 0,
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"image_view": 0,
|
"image_view": 0,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"in_dialog": 0,
|
|
||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-09-20 05:29:26.373992",
|
"modified": "2017-04-12 22:47:33.980646",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "chude.osiegbu@manqala.com",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Detail",
|
"name": "Salary Detail",
|
||||||
"name_case": "",
|
"name_case": "",
|
||||||
@ -333,7 +425,9 @@
|
|||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"read_only_onload": 0,
|
"read_only_onload": 0,
|
||||||
|
"show_name_in_global_search": 0,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 0,
|
||||||
"track_seen": 0
|
"track_seen": 0
|
||||||
}
|
}
|
@ -39,7 +39,7 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.trigger("toggle_fields")
|
frm.trigger("toggle_fields")
|
||||||
frm.trigger("toggle_reqd_fields")
|
frm.trigger("toggle_reqd_fields")
|
||||||
salary_detail_fields = ['formula', 'abbr']
|
salary_detail_fields = ['formula', 'abbr', 'statistical_component']
|
||||||
cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false);
|
cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false);
|
||||||
cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false);
|
cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false);
|
||||||
},
|
},
|
||||||
@ -129,16 +129,15 @@ var calculate_earning_total = function(doc, dt, dn, reset_amount) {
|
|||||||
var tbl = doc.earnings || [];
|
var tbl = doc.earnings || [];
|
||||||
var total_earn = 0;
|
var total_earn = 0;
|
||||||
for(var i = 0; i < tbl.length; i++){
|
for(var i = 0; i < tbl.length; i++){
|
||||||
if(cint(tbl[i].depends_on_lwp) == 1) {
|
if(cint(tbl[i].depends_on_lwp) == 1) {
|
||||||
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) /
|
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) /
|
||||||
cint(doc.total_working_days)*100)/100;
|
cint(doc.total_working_days)*100)/100;
|
||||||
refresh_field('amount', tbl[i].name, 'earnings');
|
refresh_field('amount', tbl[i].name, 'earnings');
|
||||||
} else if(reset_amount) {
|
} else if(reset_amount) {
|
||||||
tbl[i].amount = tbl[i].default_amount;
|
tbl[i].amount = tbl[i].default_amount;
|
||||||
refresh_field('amount', tbl[i].name, 'earnings');
|
refresh_field('amount', tbl[i].name, 'earnings');
|
||||||
}
|
}
|
||||||
total_earn += flt(tbl[i].amount);
|
total_earn += flt(tbl[i].amount);
|
||||||
|
|
||||||
}
|
}
|
||||||
doc.gross_pay = total_earn;
|
doc.gross_pay = total_earn;
|
||||||
refresh_many(['amount','gross_pay']);
|
refresh_many(['amount','gross_pay']);
|
||||||
@ -150,14 +149,14 @@ var calculate_ded_total = function(doc, dt, dn, reset_amount) {
|
|||||||
var tbl = doc.deductions || [];
|
var tbl = doc.deductions || [];
|
||||||
var total_ded = 0;
|
var total_ded = 0;
|
||||||
for(var i = 0; i < tbl.length; i++){
|
for(var i = 0; i < tbl.length; i++){
|
||||||
if(cint(tbl[i].depends_on_lwp) == 1) {
|
if(cint(tbl[i].depends_on_lwp) == 1) {
|
||||||
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_working_days)*100)/100;
|
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_working_days)*100)/100;
|
||||||
refresh_field('amount', tbl[i].name, 'deductions');
|
refresh_field('amount', tbl[i].name, 'deductions');
|
||||||
} else if(reset_amount) {
|
} else if(reset_amount) {
|
||||||
tbl[i].amount = tbl[i].default_amount;
|
tbl[i].amount = tbl[i].default_amount;
|
||||||
refresh_field('amount', tbl[i].name, 'deductions');
|
refresh_field('amount', tbl[i].name, 'deductions');
|
||||||
}
|
}
|
||||||
total_ded += flt(tbl[i].amount);
|
total_ded += flt(tbl[i].amount);
|
||||||
}
|
}
|
||||||
doc.total_deduction = total_ded;
|
doc.total_deduction = total_ded;
|
||||||
refresh_field('total_deduction');
|
refresh_field('total_deduction');
|
||||||
|
@ -13,409 +13,409 @@ from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
|||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
|
|
||||||
class SalarySlip(TransactionBase):
|
class SalarySlip(TransactionBase):
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
|
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.status = self.get_status()
|
self.status = self.get_status()
|
||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
self.check_existing()
|
self.check_existing()
|
||||||
if not self.salary_slip_based_on_timesheet:
|
if not self.salary_slip_based_on_timesheet:
|
||||||
self.get_date_details()
|
self.get_date_details()
|
||||||
|
|
||||||
if not (len(self.get("earnings")) or len(self.get("deductions"))):
|
if not (len(self.get("earnings")) or len(self.get("deductions"))):
|
||||||
# get details from salary structure
|
# get details from salary structure
|
||||||
self.get_emp_and_leave_details()
|
self.get_emp_and_leave_details()
|
||||||
else:
|
else:
|
||||||
self.get_leave_details(lwp = self.leave_without_pay)
|
self.get_leave_details(lwp = self.leave_without_pay)
|
||||||
|
|
||||||
# if self.salary_slip_based_on_timesheet or not self.net_pay:
|
# if self.salary_slip_based_on_timesheet or not self.net_pay:
|
||||||
self.calculate_net_pay()
|
self.calculate_net_pay()
|
||||||
|
|
||||||
company_currency = erpnext.get_company_currency(self.company)
|
company_currency = erpnext.get_company_currency(self.company)
|
||||||
self.total_in_words = money_in_words(self.rounded_total, company_currency)
|
self.total_in_words = money_in_words(self.rounded_total, company_currency)
|
||||||
|
|
||||||
if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"):
|
if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"):
|
||||||
max_working_hours = frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet")
|
max_working_hours = frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet")
|
||||||
if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)):
|
if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)):
|
||||||
frappe.msgprint(_("Total working hours should not be greater than max working hours {0}").
|
frappe.msgprint(_("Total working hours should not be greater than max working hours {0}").
|
||||||
format(max_working_hours), alert=True)
|
format(max_working_hours), alert=True)
|
||||||
|
|
||||||
def validate_dates(self):
|
def validate_dates(self):
|
||||||
if date_diff(self.end_date, self.start_date) < 0:
|
if date_diff(self.end_date, self.start_date) < 0:
|
||||||
frappe.throw(_("To date cannot be before From date"))
|
frappe.throw(_("To date cannot be before From date"))
|
||||||
|
|
||||||
def calculate_component_amounts(self):
|
def calculate_component_amounts(self):
|
||||||
if not getattr(self, '_salary_structure_doc', None):
|
if not getattr(self, '_salary_structure_doc', None):
|
||||||
self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
|
self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
|
||||||
|
|
||||||
data = self.get_data_for_eval()
|
data = self.get_data_for_eval()
|
||||||
|
|
||||||
for key in ('earnings', 'deductions'):
|
for key in ('earnings', 'deductions'):
|
||||||
for struct_row in self._salary_structure_doc.get(key):
|
for struct_row in self._salary_structure_doc.get(key):
|
||||||
amount = self.eval_condition_and_formula(struct_row, data)
|
amount = self.eval_condition_and_formula(struct_row, data)
|
||||||
if amount:
|
if amount and struct_row.statistical_component == 0:
|
||||||
self.update_component_row(struct_row, amount, key)
|
self.update_component_row(struct_row, amount, key)
|
||||||
|
|
||||||
def update_component_row(self, struct_row, amount, key):
|
def update_component_row(self, struct_row, amount, key):
|
||||||
component_row = None
|
component_row = None
|
||||||
for d in self.get(key):
|
for d in self.get(key):
|
||||||
if d.salary_component == struct_row.salary_component:
|
if d.salary_component == struct_row.salary_component:
|
||||||
component_row = d
|
component_row = d
|
||||||
|
|
||||||
if not component_row:
|
if not component_row:
|
||||||
self.append(key, {
|
self.append(key, {
|
||||||
'amount': amount,
|
'amount': amount,
|
||||||
'default_amount': amount,
|
'default_amount': amount,
|
||||||
'depends_on_lwp' : struct_row.depends_on_lwp,
|
'depends_on_lwp' : struct_row.depends_on_lwp,
|
||||||
'salary_component' : struct_row.salary_component
|
'salary_component' : struct_row.salary_component
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
component_row.amount = amount
|
component_row.amount = amount
|
||||||
|
|
||||||
def eval_condition_and_formula(self, d, data):
|
def eval_condition_and_formula(self, d, data):
|
||||||
try:
|
try:
|
||||||
if d.condition:
|
if d.condition:
|
||||||
if not frappe.safe_eval(d.condition, None, data):
|
if not frappe.safe_eval(d.condition, None, data):
|
||||||
return None
|
return None
|
||||||
amount = d.amount
|
amount = d.amount
|
||||||
if d.amount_based_on_formula:
|
if d.amount_based_on_formula:
|
||||||
if d.formula:
|
if d.formula:
|
||||||
amount = frappe.safe_eval(d.formula, None, data)
|
amount = frappe.safe_eval(d.formula, None, data)
|
||||||
if amount:
|
if amount:
|
||||||
data[d.abbr] = amount
|
data[d.abbr] = amount
|
||||||
|
|
||||||
return amount
|
return amount
|
||||||
|
|
||||||
except NameError as err:
|
except NameError as err:
|
||||||
frappe.throw(_("Name error: {0}".format(err)))
|
frappe.throw(_("Name error: {0}".format(err)))
|
||||||
except SyntaxError as err:
|
except SyntaxError as err:
|
||||||
frappe.throw(_("Syntax error in formula or condition: {0}".format(err)))
|
frappe.throw(_("Syntax error in formula or condition: {0}".format(err)))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
frappe.throw(_("Error in formula or condition: {0}".format(e)))
|
frappe.throw(_("Error in formula or condition: {0}".format(e)))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def get_data_for_eval(self):
|
def get_data_for_eval(self):
|
||||||
'''Returns data for evaluating formula'''
|
'''Returns data for evaluating formula'''
|
||||||
data = frappe._dict()
|
data = frappe._dict()
|
||||||
|
|
||||||
data.update(frappe.get_doc("Salary Structure Employee", {"employee": self.employee}).as_dict())
|
data.update(frappe.get_doc("Salary Structure Employee", {"employee": self.employee}).as_dict())
|
||||||
|
|
||||||
data.update(frappe.get_doc("Employee", self.employee).as_dict())
|
data.update(frappe.get_doc("Employee", self.employee).as_dict())
|
||||||
data.update(self.as_dict())
|
data.update(self.as_dict())
|
||||||
|
|
||||||
# set values for components
|
# set values for components
|
||||||
salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"])
|
salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"])
|
||||||
for sc in salary_components:
|
for sc in salary_components:
|
||||||
data.setdefault(sc.salary_component_abbr, 0)
|
data.setdefault(sc.salary_component_abbr, 0)
|
||||||
|
|
||||||
for key in ('earnings', 'deductions'):
|
for key in ('earnings', 'deductions'):
|
||||||
for d in self.get(key):
|
for d in self.get(key):
|
||||||
data[d.abbr] = d.amount
|
data[d.abbr] = d.amount
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_emp_and_leave_details(self):
|
def get_emp_and_leave_details(self):
|
||||||
'''First time, load all the components from salary structure'''
|
'''First time, load all the components from salary structure'''
|
||||||
if self.employee:
|
if self.employee:
|
||||||
self.set("earnings", [])
|
self.set("earnings", [])
|
||||||
self.set("deductions", [])
|
self.set("deductions", [])
|
||||||
|
|
||||||
if not self.salary_slip_based_on_timesheet:
|
if not self.salary_slip_based_on_timesheet:
|
||||||
self.get_date_details()
|
self.get_date_details()
|
||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
||||||
["date_of_joining", "relieving_date"])
|
["date_of_joining", "relieving_date"])
|
||||||
|
|
||||||
self.get_leave_details(joining_date, relieving_date)
|
self.get_leave_details(joining_date, relieving_date)
|
||||||
struct = self.check_sal_struct(joining_date, relieving_date)
|
struct = self.check_sal_struct(joining_date, relieving_date)
|
||||||
|
|
||||||
if struct:
|
if struct:
|
||||||
self._salary_structure_doc = frappe.get_doc('Salary Structure', struct)
|
self._salary_structure_doc = frappe.get_doc('Salary Structure', struct)
|
||||||
self.salary_slip_based_on_timesheet = self._salary_structure_doc.salary_slip_based_on_timesheet or 0
|
self.salary_slip_based_on_timesheet = self._salary_structure_doc.salary_slip_based_on_timesheet or 0
|
||||||
self.set_time_sheet()
|
self.set_time_sheet()
|
||||||
self.pull_sal_struct()
|
self.pull_sal_struct()
|
||||||
|
|
||||||
def set_time_sheet(self):
|
def set_time_sheet(self):
|
||||||
if self.salary_slip_based_on_timesheet:
|
if self.salary_slip_based_on_timesheet:
|
||||||
self.set("timesheets", [])
|
self.set("timesheets", [])
|
||||||
timesheets = frappe.db.sql(""" select * from `tabTimesheet` where employee = %(employee)s and start_date BETWEEN %(start_date)s AND %(end_date)s and (status = 'Submitted' or
|
timesheets = frappe.db.sql(""" select * from `tabTimesheet` where employee = %(employee)s and start_date BETWEEN %(start_date)s AND %(end_date)s and (status = 'Submitted' or
|
||||||
status = 'Billed')""", {'employee': self.employee, 'start_date': self.start_date, 'end_date': self.end_date}, as_dict=1)
|
status = 'Billed')""", {'employee': self.employee, 'start_date': self.start_date, 'end_date': self.end_date}, as_dict=1)
|
||||||
|
|
||||||
for data in timesheets:
|
for data in timesheets:
|
||||||
self.append('timesheets', {
|
self.append('timesheets', {
|
||||||
'time_sheet': data.name,
|
'time_sheet': data.name,
|
||||||
'working_hours': data.total_hours
|
'working_hours': data.total_hours
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_date_details(self):
|
def get_date_details(self):
|
||||||
date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date)
|
date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date)
|
||||||
self.start_date = date_details.start_date
|
self.start_date = date_details.start_date
|
||||||
self.end_date = date_details.end_date
|
self.end_date = date_details.end_date
|
||||||
|
|
||||||
def check_sal_struct(self, joining_date, relieving_date):
|
def check_sal_struct(self, joining_date, relieving_date):
|
||||||
cond = ''
|
cond = ''
|
||||||
if self.payroll_frequency:
|
if self.payroll_frequency:
|
||||||
cond = """and payroll_frequency = '%(payroll_frequency)s'""" % {"payroll_frequency": self.payroll_frequency}
|
cond = """and payroll_frequency = '%(payroll_frequency)s'""" % {"payroll_frequency": self.payroll_frequency}
|
||||||
|
|
||||||
st_name = frappe.db.sql("""select parent from `tabSalary Structure Employee`
|
st_name = frappe.db.sql("""select parent from `tabSalary Structure Employee`
|
||||||
where employee=%s and (from_date <= %s or from_date <= %s)
|
where employee=%s and (from_date <= %s or from_date <= %s)
|
||||||
and (to_date is null or to_date >= %s or to_date >= %s)
|
and (to_date is null or to_date >= %s or to_date >= %s)
|
||||||
and parent in (select name from `tabSalary Structure`
|
and parent in (select name from `tabSalary Structure`
|
||||||
where is_active = 'Yes'%s)
|
where is_active = 'Yes'%s)
|
||||||
"""% ('%s', '%s', '%s','%s','%s', cond),(self.employee, self.start_date, joining_date, self.end_date, relieving_date))
|
"""% ('%s', '%s', '%s','%s','%s', cond),(self.employee, self.start_date, joining_date, self.end_date, relieving_date))
|
||||||
|
|
||||||
if st_name:
|
if st_name:
|
||||||
if len(st_name) > 1:
|
if len(st_name) > 1:
|
||||||
frappe.msgprint(_("Multiple active Salary Structures found for employee {0} for the given dates")
|
frappe.msgprint(_("Multiple active Salary Structures found for employee {0} for the given dates")
|
||||||
.format(self.employee), title=_('Warning'))
|
.format(self.employee), title=_('Warning'))
|
||||||
return st_name and st_name[0][0] or ''
|
return st_name and st_name[0][0] or ''
|
||||||
else:
|
else:
|
||||||
self.salary_structure = None
|
self.salary_structure = None
|
||||||
frappe.msgprint(_("No active or default Salary Structure found for employee {0} for the given dates")
|
frappe.msgprint(_("No active or default Salary Structure found for employee {0} for the given dates")
|
||||||
.format(self.employee), title=_('Salary Structure Missing'))
|
.format(self.employee), title=_('Salary Structure Missing'))
|
||||||
|
|
||||||
def pull_sal_struct(self):
|
def pull_sal_struct(self):
|
||||||
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
||||||
|
|
||||||
if self.salary_slip_based_on_timesheet:
|
if self.salary_slip_based_on_timesheet:
|
||||||
self.salary_structure = self._salary_structure_doc.name
|
self.salary_structure = self._salary_structure_doc.name
|
||||||
self.hour_rate = self._salary_structure_doc.hour_rate
|
self.hour_rate = self._salary_structure_doc.hour_rate
|
||||||
self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0
|
self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0
|
||||||
wages_amount = self.hour_rate * self.total_working_hours
|
wages_amount = self.hour_rate * self.total_working_hours
|
||||||
|
|
||||||
self.add_earning_for_hourly_wages(self, self._salary_structure_doc.salary_component, wages_amount)
|
self.add_earning_for_hourly_wages(self, self._salary_structure_doc.salary_component, wages_amount)
|
||||||
|
|
||||||
make_salary_slip(self._salary_structure_doc.name, self)
|
make_salary_slip(self._salary_structure_doc.name, self)
|
||||||
|
|
||||||
def process_salary_structure(self):
|
def process_salary_structure(self):
|
||||||
'''Calculate salary after salary structure details have been updated'''
|
'''Calculate salary after salary structure details have been updated'''
|
||||||
if not self.salary_slip_based_on_timesheet:
|
if not self.salary_slip_based_on_timesheet:
|
||||||
self.get_date_details()
|
self.get_date_details()
|
||||||
self.pull_emp_details()
|
self.pull_emp_details()
|
||||||
self.get_leave_details()
|
self.get_leave_details()
|
||||||
self.calculate_net_pay()
|
self.calculate_net_pay()
|
||||||
|
|
||||||
def add_earning_for_hourly_wages(self, doc, salary_component, amount):
|
def add_earning_for_hourly_wages(self, doc, salary_component, amount):
|
||||||
row_exists = False
|
row_exists = False
|
||||||
for row in doc.earnings:
|
for row in doc.earnings:
|
||||||
if row.salary_component == salary_component:
|
if row.salary_component == salary_component:
|
||||||
row.amount = amount
|
row.amount = amount
|
||||||
row_exists = True
|
row_exists = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if not row_exists:
|
if not row_exists:
|
||||||
wages_row = {
|
wages_row = {
|
||||||
"salary_component": salary_component,
|
"salary_component": salary_component,
|
||||||
"abbr": frappe.db.get_value("Salary Component", salary_component, "salary_component_abbr"),
|
"abbr": frappe.db.get_value("Salary Component", salary_component, "salary_component_abbr"),
|
||||||
"amount": self.hour_rate * self.total_working_hours
|
"amount": self.hour_rate * self.total_working_hours
|
||||||
}
|
}
|
||||||
doc.append('earnings', wages_row)
|
doc.append('earnings', wages_row)
|
||||||
|
|
||||||
def pull_emp_details(self):
|
def pull_emp_details(self):
|
||||||
emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no"], as_dict=1)
|
emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no"], as_dict=1)
|
||||||
if emp:
|
if emp:
|
||||||
self.bank_name = emp.bank_name
|
self.bank_name = emp.bank_name
|
||||||
self.bank_account_no = emp.bank_ac_no
|
self.bank_account_no = emp.bank_ac_no
|
||||||
|
|
||||||
|
|
||||||
def get_leave_details(self, joining_date=None, relieving_date=None, lwp=None):
|
def get_leave_details(self, joining_date=None, relieving_date=None, lwp=None):
|
||||||
if not joining_date:
|
if not joining_date:
|
||||||
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
||||||
["date_of_joining", "relieving_date"])
|
["date_of_joining", "relieving_date"])
|
||||||
|
|
||||||
holidays = self.get_holidays_for_employee(self.start_date, self.end_date)
|
holidays = self.get_holidays_for_employee(self.start_date, self.end_date)
|
||||||
working_days = date_diff(self.end_date, self.start_date) + 1
|
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")):
|
if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")):
|
||||||
working_days -= len(holidays)
|
working_days -= len(holidays)
|
||||||
if working_days < 0:
|
if working_days < 0:
|
||||||
frappe.throw(_("There are more holidays than working days this month."))
|
frappe.throw(_("There are more holidays than working days this month."))
|
||||||
|
|
||||||
actual_lwp = self.calculate_lwp(holidays, working_days)
|
actual_lwp = self.calculate_lwp(holidays, working_days)
|
||||||
if not lwp:
|
if not lwp:
|
||||||
lwp = actual_lwp
|
lwp = actual_lwp
|
||||||
elif lwp != actual_lwp:
|
elif lwp != actual_lwp:
|
||||||
frappe.msgprint(_("Leave Without Pay does not match with approved Leave Application records"))
|
frappe.msgprint(_("Leave Without Pay does not match with approved Leave Application records"))
|
||||||
|
|
||||||
self.total_working_days = working_days
|
self.total_working_days = working_days
|
||||||
self.leave_without_pay = lwp
|
self.leave_without_pay = lwp
|
||||||
|
|
||||||
payment_days = flt(self.get_payment_days(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
|
self.payment_days = payment_days > 0 and payment_days or 0
|
||||||
|
|
||||||
def get_payment_days(self, joining_date, relieving_date):
|
def get_payment_days(self, joining_date, relieving_date):
|
||||||
start_date = getdate(self.start_date)
|
start_date = getdate(self.start_date)
|
||||||
if joining_date:
|
if joining_date:
|
||||||
if getdate(self.start_date) <= joining_date <= getdate(self.end_date):
|
if getdate(self.start_date) <= joining_date <= getdate(self.end_date):
|
||||||
start_date = joining_date
|
start_date = joining_date
|
||||||
elif joining_date > getdate(self.end_date):
|
elif joining_date > getdate(self.end_date):
|
||||||
return
|
return
|
||||||
|
|
||||||
end_date = getdate(self.end_date)
|
end_date = getdate(self.end_date)
|
||||||
if relieving_date:
|
if relieving_date:
|
||||||
if getdate(self.start_date) <= relieving_date <= getdate(self.end_date):
|
if getdate(self.start_date) <= relieving_date <= getdate(self.end_date):
|
||||||
end_date = relieving_date
|
end_date = relieving_date
|
||||||
elif relieving_date < getdate(self.start_date):
|
elif relieving_date < getdate(self.start_date):
|
||||||
frappe.throw(_("Employee relieved on {0} must be set as 'Left'")
|
frappe.throw(_("Employee relieved on {0} must be set as 'Left'")
|
||||||
.format(relieving_date))
|
.format(relieving_date))
|
||||||
|
|
||||||
payment_days = date_diff(end_date, start_date) + 1
|
payment_days = date_diff(end_date, start_date) + 1
|
||||||
|
|
||||||
if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")):
|
if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")):
|
||||||
holidays = self.get_holidays_for_employee(start_date, end_date)
|
holidays = self.get_holidays_for_employee(start_date, end_date)
|
||||||
payment_days -= len(holidays)
|
payment_days -= len(holidays)
|
||||||
return payment_days
|
return payment_days
|
||||||
|
|
||||||
def get_holidays_for_employee(self, start_date, end_date):
|
def get_holidays_for_employee(self, start_date, end_date):
|
||||||
holiday_list = get_holiday_list_for_employee(self.employee)
|
holiday_list = get_holiday_list_for_employee(self.employee)
|
||||||
holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
|
holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
|
||||||
where
|
where
|
||||||
parent=%(holiday_list)s
|
parent=%(holiday_list)s
|
||||||
and holiday_date >= %(start_date)s
|
and holiday_date >= %(start_date)s
|
||||||
and holiday_date <= %(end_date)s''', {
|
and holiday_date <= %(end_date)s''', {
|
||||||
"holiday_list": holiday_list,
|
"holiday_list": holiday_list,
|
||||||
"start_date": start_date,
|
"start_date": start_date,
|
||||||
"end_date": end_date
|
"end_date": end_date
|
||||||
})
|
})
|
||||||
|
|
||||||
holidays = [cstr(i) for i in holidays]
|
holidays = [cstr(i) for i in holidays]
|
||||||
|
|
||||||
return holidays
|
return holidays
|
||||||
|
|
||||||
def calculate_lwp(self, holidays, working_days):
|
def calculate_lwp(self, holidays, working_days):
|
||||||
lwp = 0
|
lwp = 0
|
||||||
holidays = "','".join(holidays)
|
holidays = "','".join(holidays)
|
||||||
for d in range(working_days):
|
for d in range(working_days):
|
||||||
dt = add_days(cstr(getdate(self.start_date)), d)
|
dt = add_days(cstr(getdate(self.start_date)), d)
|
||||||
leave = frappe.db.sql("""
|
leave = frappe.db.sql("""
|
||||||
select t1.name, t1.half_day
|
select t1.name, t1.half_day
|
||||||
from `tabLeave Application` t1, `tabLeave Type` t2
|
from `tabLeave Application` t1, `tabLeave Type` t2
|
||||||
where t2.name = t1.leave_type
|
where t2.name = t1.leave_type
|
||||||
and t2.is_lwp = 1
|
and t2.is_lwp = 1
|
||||||
and t1.docstatus = 1
|
and t1.docstatus = 1
|
||||||
and t1.status = 'Approved'
|
and t1.status = 'Approved'
|
||||||
and t1.employee = %(employee)s
|
and t1.employee = %(employee)s
|
||||||
and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date
|
and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date
|
||||||
WHEN t2.include_holiday THEN %(dt)s between from_date and to_date
|
WHEN t2.include_holiday THEN %(dt)s between from_date and to_date
|
||||||
END
|
END
|
||||||
""".format(holidays), {"employee": self.employee, "dt": dt})
|
""".format(holidays), {"employee": self.employee, "dt": 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):
|
||||||
if not self.salary_slip_based_on_timesheet:
|
if not self.salary_slip_based_on_timesheet:
|
||||||
ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
|
ret_exist = frappe.db.sql("""select name from `tabSalary Slip`
|
||||||
where start_date = %s and end_date = %s and docstatus != 2
|
where start_date = %s and end_date = %s and docstatus != 2
|
||||||
and employee = %s and name != %s""",
|
and employee = %s and name != %s""",
|
||||||
(self.start_date, self.end_date, self.employee, self.name))
|
(self.start_date, self.end_date, self.employee, self.name))
|
||||||
if ret_exist:
|
if ret_exist:
|
||||||
self.employee = ''
|
self.employee = ''
|
||||||
frappe.throw(_("Salary Slip of employee {0} already created for this period").format(self.employee))
|
frappe.throw(_("Salary Slip of employee {0} already created for this period").format(self.employee))
|
||||||
else:
|
else:
|
||||||
for data in self.timesheets:
|
for data in self.timesheets:
|
||||||
if frappe.db.get_value('Timesheet', data.time_sheet, 'status') == 'Payrolled':
|
if frappe.db.get_value('Timesheet', data.time_sheet, 'status') == 'Payrolled':
|
||||||
frappe.throw(_("Salary Slip of employee {0} already created for time sheet {1}").format(self.employee, data.time_sheet))
|
frappe.throw(_("Salary Slip of employee {0} already created for time sheet {1}").format(self.employee, data.time_sheet))
|
||||||
|
|
||||||
def sum_components(self, component_type, total_field):
|
def sum_components(self, component_type, total_field):
|
||||||
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
|
||||||
["date_of_joining", "relieving_date"])
|
["date_of_joining", "relieving_date"])
|
||||||
if not relieving_date:
|
if not relieving_date:
|
||||||
relieving_date = getdate(self.end_date)
|
relieving_date = getdate(self.end_date)
|
||||||
|
|
||||||
for d in self.get(component_type):
|
for d in self.get(component_type):
|
||||||
if ((cint(d.depends_on_lwp) == 1 and not self.salary_slip_based_on_timesheet) or\
|
if ((cint(d.depends_on_lwp) == 1 and not self.salary_slip_based_on_timesheet) or\
|
||||||
getdate(self.start_date) < joining_date or getdate(self.end_date) > relieving_date):
|
getdate(self.start_date) < joining_date or getdate(self.end_date) > relieving_date):
|
||||||
|
|
||||||
d.amount = rounded((flt(d.default_amount) * flt(self.payment_days)
|
d.amount = rounded((flt(d.default_amount) * flt(self.payment_days)
|
||||||
/ cint(self.total_working_days)), self.precision("amount", component_type))
|
/ cint(self.total_working_days)), self.precision("amount", component_type))
|
||||||
elif not self.payment_days and not self.salary_slip_based_on_timesheet:
|
elif not self.payment_days and not self.salary_slip_based_on_timesheet:
|
||||||
d.amount = 0
|
d.amount = 0
|
||||||
elif not d.amount:
|
elif not d.amount:
|
||||||
d.amount = d.default_amount
|
d.amount = d.default_amount
|
||||||
self.set(total_field, self.get(total_field) + flt(d.amount))
|
self.set(total_field, self.get(total_field) + flt(d.amount))
|
||||||
|
|
||||||
def calculate_net_pay(self):
|
def calculate_net_pay(self):
|
||||||
if self.salary_structure:
|
if self.salary_structure:
|
||||||
self.calculate_component_amounts()
|
self.calculate_component_amounts()
|
||||||
|
|
||||||
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
|
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
|
||||||
|
|
||||||
self.total_deduction = 0
|
self.total_deduction = 0
|
||||||
self.gross_pay = 0
|
self.gross_pay = 0
|
||||||
|
|
||||||
self.sum_components('earnings', 'gross_pay')
|
self.sum_components('earnings', 'gross_pay')
|
||||||
self.sum_components('deductions', 'total_deduction')
|
self.sum_components('deductions', 'total_deduction')
|
||||||
|
|
||||||
self.set_loan_repayment()
|
self.set_loan_repayment()
|
||||||
|
|
||||||
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
|
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
|
||||||
self.rounded_total = rounded(self.net_pay,
|
self.rounded_total = rounded(self.net_pay,
|
||||||
self.precision("net_pay") if disable_rounded_total else 0)
|
self.precision("net_pay") if disable_rounded_total else 0)
|
||||||
|
|
||||||
def set_loan_repayment(self):
|
def set_loan_repayment(self):
|
||||||
employee_loan = frappe.db.sql("""select sum(principal_amount) as principal_amount, sum(interest_amount) as interest_amount,
|
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`
|
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 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)""",
|
where employee = %s and repay_from_salary = 1 and docstatus = 1)""",
|
||||||
(self.start_date, self.end_date, self.employee), as_dict=True)
|
(self.start_date, self.end_date, self.employee), as_dict=True)
|
||||||
if employee_loan:
|
if employee_loan:
|
||||||
self.principal_amount = employee_loan[0].principal_amount
|
self.principal_amount = employee_loan[0].principal_amount
|
||||||
self.interest_amount = employee_loan[0].interest_amount
|
self.interest_amount = employee_loan[0].interest_amount
|
||||||
self.total_loan_repayment = employee_loan[0].total_loan_repayment
|
self.total_loan_repayment = employee_loan[0].total_loan_repayment
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
if self.net_pay < 0:
|
if self.net_pay < 0:
|
||||||
frappe.throw(_("Net Pay cannot be less than 0"))
|
frappe.throw(_("Net Pay cannot be less than 0"))
|
||||||
else:
|
else:
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.update_status(self.name)
|
self.update_status(self.name)
|
||||||
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")):
|
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")):
|
||||||
self.email_salary_slip()
|
self.email_salary_slip()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.update_status()
|
self.update_status()
|
||||||
|
|
||||||
def email_salary_slip(self):
|
def email_salary_slip(self):
|
||||||
receiver = frappe.db.get_value("Employee", self.employee, "prefered_email")
|
receiver = frappe.db.get_value("Employee", self.employee, "prefered_email")
|
||||||
|
|
||||||
if receiver:
|
if receiver:
|
||||||
subj = 'Salary Slip - from {0} to {1}'.format(self.start_date, self.end_date)
|
subj = 'Salary Slip - from {0} to {1}'.format(self.start_date, self.end_date)
|
||||||
frappe.sendmail([receiver], subject=subj, message = _("Please see attachment"),
|
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)
|
attachments=[frappe.attach_print(self.doctype, self.name, file_name=self.name)], reference_doctype= self.doctype, reference_name= self.name)
|
||||||
else:
|
else:
|
||||||
msgprint(_("{0}: Employee email not found, hence email not sent").format(self.employee_name))
|
msgprint(_("{0}: Employee email not found, hence email not sent").format(self.employee_name))
|
||||||
|
|
||||||
def update_status(self, salary_slip=None):
|
def update_status(self, salary_slip=None):
|
||||||
for data in self.timesheets:
|
for data in self.timesheets:
|
||||||
if data.time_sheet:
|
if data.time_sheet:
|
||||||
timesheet = frappe.get_doc('Timesheet', data.time_sheet)
|
timesheet = frappe.get_doc('Timesheet', data.time_sheet)
|
||||||
timesheet.salary_slip = salary_slip
|
timesheet.salary_slip = salary_slip
|
||||||
timesheet.flags.ignore_validate_update_after_submit = True
|
timesheet.flags.ignore_validate_update_after_submit = True
|
||||||
timesheet.set_status()
|
timesheet.set_status()
|
||||||
timesheet.save()
|
timesheet.save()
|
||||||
|
|
||||||
def set_status(self, status=None):
|
def set_status(self, status=None):
|
||||||
'''Get and update status'''
|
'''Get and update status'''
|
||||||
if not status:
|
if not status:
|
||||||
status = self.get_status()
|
status = self.get_status()
|
||||||
self.db_set("status", status)
|
self.db_set("status", status)
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
if self.docstatus == 0:
|
if self.docstatus == 0:
|
||||||
status = "Draft"
|
status = "Draft"
|
||||||
elif self.docstatus == 1:
|
elif self.docstatus == 1:
|
||||||
status = "Submitted"
|
status = "Submitted"
|
||||||
elif self.docstatus == 2:
|
elif self.docstatus == 2:
|
||||||
status = "Cancelled"
|
status = "Cancelled"
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def unlink_ref_doc_from_salary_slip(ref_no):
|
def unlink_ref_doc_from_salary_slip(ref_no):
|
||||||
linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip`
|
linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip`
|
||||||
where journal_entry=%s and docstatus < 2""", (ref_no))
|
where journal_entry=%s and docstatus < 2""", (ref_no))
|
||||||
if linked_ss:
|
if linked_ss:
|
||||||
for ss in linked_ss:
|
for ss in linked_ss:
|
||||||
ss_doc = frappe.get_doc("Salary Slip", ss)
|
ss_doc = frappe.get_doc("Salary Slip", ss)
|
||||||
frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "")
|
frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user