Merge pull request #24169 from Anurag810/payroll-entry-fixes

fix: allow addition and removal of employee in payroll Entry
This commit is contained in:
rohitwaghchaure 2020-12-30 16:50:02 +05:30 committed by GitHub
commit bb0fd33ac5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 115 additions and 50 deletions

View File

@ -17,8 +17,7 @@
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Employee", "label": "Employee",
"options": "Employee", "options": "Employee"
"read_only": 1
}, },
{ {
"fetch_from": "employee.employee_name", "fetch_from": "employee.employee_name",
@ -52,7 +51,7 @@
], ],
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-09-30 12:40:07.999878", "modified": "2020-12-17 15:43:29.542977",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Payroll", "module": "Payroll",
"name": "Payroll Employee Detail", "name": "Payroll Employee Detail",

View File

@ -10,15 +10,22 @@ frappe.ui.form.on('Payroll Entry', {
} }
frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet); frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet);
frm.set_query("department", function() { frm.events.department_filters(frm);
frm.events.payroll_payable_account_filters(frm);
},
department_filters: function (frm) {
frm.set_query("department", function () {
return { return {
"filters": { "filters": {
"company": frm.doc.company, "company": frm.doc.company,
} }
}; };
}); });
},
frm.set_query("payroll_payable_account", function() { payroll_payable_account_filters: function (frm) {
frm.set_query("payroll_payable_account", function () {
return { return {
filters: { filters: {
"company": frm.doc.company, "company": frm.doc.company,
@ -29,12 +36,12 @@ frappe.ui.form.on('Payroll Entry', {
}); });
}, },
refresh: function(frm) { refresh: function (frm) {
if (frm.doc.docstatus == 0) { if (frm.doc.docstatus == 0) {
if(!frm.is_new()) { if (!frm.is_new()) {
frm.page.clear_primary_action(); frm.page.clear_primary_action();
frm.add_custom_button(__("Get Employees"), frm.add_custom_button(__("Get Employees"),
function() { function () {
frm.events.get_employee_details(frm); frm.events.get_employee_details(frm);
} }
).toggleClass('btn-primary', !(frm.doc.employees || []).length); ).toggleClass('btn-primary', !(frm.doc.employees || []).length);
@ -42,7 +49,7 @@ frappe.ui.form.on('Payroll Entry', {
if ((frm.doc.employees || []).length) { if ((frm.doc.employees || []).length) {
frm.page.clear_primary_action(); frm.page.clear_primary_action();
frm.page.set_primary_action(__('Create Salary Slips'), () => { frm.page.set_primary_action(__('Create Salary Slips'), () => {
frm.save('Submit').then(()=>{ frm.save('Submit').then(() => {
frm.page.clear_primary_action(); frm.page.clear_primary_action();
frm.refresh(); frm.refresh();
frm.events.refresh(frm); frm.events.refresh(frm);
@ -61,48 +68,48 @@ frappe.ui.form.on('Payroll Entry', {
doc: frm.doc, doc: frm.doc,
method: 'fill_employee_details', method: 'fill_employee_details',
}).then(r => { }).then(r => {
if (r.docs && r.docs[0].employees){ if (r.docs && r.docs[0].employees) {
frm.employees = r.docs[0].employees; frm.employees = r.docs[0].employees;
frm.dirty(); frm.dirty();
frm.save(); frm.save();
frm.refresh(); frm.refresh();
if(r.docs[0].validate_attendance){ if (r.docs[0].validate_attendance) {
render_employee_attendance(frm, r.message); render_employee_attendance(frm, r.message);
} }
} }
}) });
}, },
create_salary_slips: function(frm) { create_salary_slips: function (frm) {
frm.call({ frm.call({
doc: frm.doc, doc: frm.doc,
method: "create_salary_slips", method: "create_salary_slips",
callback: function(r) { callback: function () {
frm.refresh(); frm.refresh();
frm.toolbar.refresh(); frm.toolbar.refresh();
} }
}) });
}, },
add_context_buttons: function(frm) { add_context_buttons: function (frm) {
if(frm.doc.salary_slips_submitted || (frm.doc.__onload && frm.doc.__onload.submitted_ss)) { if (frm.doc.salary_slips_submitted || (frm.doc.__onload && frm.doc.__onload.submitted_ss)) {
frm.events.add_bank_entry_button(frm); frm.events.add_bank_entry_button(frm);
} else if(frm.doc.salary_slips_created) { } else if (frm.doc.salary_slips_created) {
frm.add_custom_button(__("Submit Salary Slip"), function() { frm.add_custom_button(__("Submit Salary Slip"), function () {
submit_salary_slip(frm); submit_salary_slip(frm);
}).addClass("btn-primary"); }).addClass("btn-primary");
} }
}, },
add_bank_entry_button: function(frm) { add_bank_entry_button: function (frm) {
frappe.call({ frappe.call({
method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.payroll_entry_has_bank_entries', method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.payroll_entry_has_bank_entries',
args: { args: {
'name': frm.doc.name 'name': frm.doc.name
}, },
callback: function(r) { callback: function (r) {
if (r.message && !r.message.submitted) { if (r.message && !r.message.submitted) {
frm.add_custom_button("Make Bank Entry", function() { frm.add_custom_button("Make Bank Entry", function () {
make_bank_entry(frm); make_bank_entry(frm);
}).addClass("btn-primary"); }).addClass("btn-primary");
} }
@ -141,8 +148,37 @@ frappe.ui.form.on('Payroll Entry', {
}, },
payroll_frequency: function (frm) { payroll_frequency: function (frm) {
frm.trigger("set_start_end_dates"); frm.trigger("set_start_end_dates").then( ()=> {
frm.events.clear_employee_table(frm); frm.events.clear_employee_table(frm);
frm.events.get_employee_with_salary_slip_and_set_query(frm);
});
},
employee_filters: function (frm, emp_list) {
frm.set_query('employee', 'employees', () => {
return {
filters: {
name: ["not in", emp_list]
}
};
});
},
get_employee_with_salary_slip_and_set_query: function (frm) {
frappe.db.get_list('Salary Slip', {
filters: {
start_date: frm.doc.start_date,
end_date: frm.doc.end_date,
docstatus: 1,
},
fields: ['employee']
}).then((emp) => {
var emp_list = [];
emp.forEach((employee_data) => {
emp_list.push(Object.values(employee_data)[0]);
});
frm.events.employee_filters(frm, emp_list);
});
}, },
company: function (frm) { company: function (frm) {
@ -164,17 +200,17 @@ frappe.ui.form.on('Payroll Entry', {
from_currency: frm.doc.currency, from_currency: frm.doc.currency,
to_currency: company_currency, to_currency: company_currency,
}, },
callback: function(r) { callback: function (r) {
frm.set_value("exchange_rate", flt(r.message)); frm.set_value("exchange_rate", flt(r.message));
frm.set_df_property('exchange_rate', 'hidden', 0); frm.set_df_property('exchange_rate', 'hidden', 0);
frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency +
+ " = [?] " + company_currency); " = [?] " + company_currency);
} }
}); });
} else { } else {
frm.set_value("exchange_rate", 1.0); frm.set_value("exchange_rate", 1.0);
frm.set_df_property('exchange_rate', 'hidden', 1); frm.set_df_property('exchange_rate', 'hidden', 1);
frm.set_df_property("exchange_rate", "description", "" ); frm.set_df_property("exchange_rate", "description", "");
} }
} }
}, },
@ -192,9 +228,9 @@ frappe.ui.form.on('Payroll Entry', {
}, },
start_date: function (frm) { start_date: function (frm) {
if(!in_progress && frm.doc.start_date){ if (!in_progress && frm.doc.start_date) {
frm.trigger("set_end_date"); frm.trigger("set_end_date");
}else{ } else {
// reset flag // reset flag
in_progress = false; in_progress = false;
} }
@ -228,7 +264,7 @@ frappe.ui.form.on('Payroll Entry', {
} }
}, },
set_end_date: function(frm){ set_end_date: function (frm) {
frappe.call({ frappe.call({
method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date', method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date',
args: { args: {
@ -243,19 +279,19 @@ frappe.ui.form.on('Payroll Entry', {
}); });
}, },
validate_attendance: function(frm){ validate_attendance: function (frm) {
if(frm.doc.validate_attendance && frm.doc.employees){ if (frm.doc.validate_attendance && frm.doc.employees) {
frappe.call({ frappe.call({
method: 'validate_employee_attendance', method: 'validate_employee_attendance',
args: {}, args: {},
callback: function(r) { callback: function (r) {
render_employee_attendance(frm, r.message); render_employee_attendance(frm, r.message);
}, },
doc: frm.doc, doc: frm.doc,
freeze: true, freeze: true,
freeze_message: __('Validating Employee Attendance...') freeze_message: __('Validating Employee Attendance...')
}); });
}else{ } else {
frm.fields_dict.attendance_detail_html.html(""); frm.fields_dict.attendance_detail_html.html("");
} }
}, },
@ -270,18 +306,20 @@ frappe.ui.form.on('Payroll Entry', {
const submit_salary_slip = function (frm) { const submit_salary_slip = function (frm) {
frappe.confirm(__('This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?'), frappe.confirm(__('This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?'),
function() { function () {
frappe.call({ frappe.call({
method: 'submit_salary_slips', method: 'submit_salary_slips',
args: {}, args: {},
callback: function() {frm.events.refresh(frm);}, callback: function () {
frm.events.refresh(frm);
},
doc: frm.doc, doc: frm.doc,
freeze: true, freeze: true,
freeze_message: __('Submitting Salary Slips and creating Journal Entry...') freeze_message: __('Submitting Salary Slips and creating Journal Entry...')
}); });
}, },
function() { function () {
if(frappe.dom.freeze_count) { if (frappe.dom.freeze_count) {
frappe.dom.unfreeze(); frappe.dom.unfreeze();
frm.events.refresh(frm); frm.events.refresh(frm);
} }
@ -295,9 +333,11 @@ let make_bank_entry = function (frm) {
return frappe.call({ return frappe.call({
doc: cur_frm.doc, doc: cur_frm.doc,
method: "make_payment_entry", method: "make_payment_entry",
callback: function() { callback: function () {
frappe.set_route( frappe.set_route(
'List', 'Journal Entry', {"Journal Entry Account.reference_name": frm.doc.name} 'List', 'Journal Entry', {
"Journal Entry Account.reference_name": frm.doc.name
}
); );
}, },
freeze: true, freeze: true,
@ -309,11 +349,19 @@ let make_bank_entry = function (frm) {
} }
}; };
let render_employee_attendance = function (frm, data) {
let render_employee_attendance = function(frm, data) {
frm.fields_dict.attendance_detail_html.html( frm.fields_dict.attendance_detail_html.html(
frappe.render_template('employees_to_mark_attendance', { frappe.render_template('employees_to_mark_attendance', {
data: data data: data
}) })
); );
} };
frappe.ui.form.on('Payroll Employee Detail', {
employee: function(frm) {
frm.events.clear_employee_table(frm);
if (!frm.doc.payroll_frequency) {
frappe.throw(__("Please set a Payroll Frequency"));
}
}
});

View File

@ -129,8 +129,7 @@
"fieldname": "employees", "fieldname": "employees",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Employee Details", "label": "Employee Details",
"options": "Payroll Employee Detail", "options": "Payroll Employee Detail"
"read_only": 1
}, },
{ {
"fieldname": "section_break_13", "fieldname": "section_break_13",
@ -290,7 +289,7 @@
"icon": "fa fa-cog", "icon": "fa fa-cog",
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-10-23 13:00:33.753228", "modified": "2020-12-17 15:13:17.766210",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Payroll", "module": "Payroll",
"name": "Payroll Entry", "name": "Payroll Entry",

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
from frappe.model.document import Document from frappe.model.document import Document
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from frappe.utils import cint, flt, nowdate, add_days, getdate, fmt_money, add_to_date, DATE_FORMAT, date_diff from frappe.utils import cint, flt, add_days, getdate, add_to_date, DATE_FORMAT, date_diff, comma_and
from frappe import _ from frappe import _
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
@ -19,16 +19,26 @@ class PayrollEntry(Document):
# check if salary slips were manually submitted # check if salary slips were manually submitted
entries = frappe.db.count("Salary Slip", {'payroll_entry': self.name, 'docstatus': 1}, ['name']) entries = frappe.db.count("Salary Slip", {'payroll_entry': self.name, 'docstatus': 1}, ['name'])
if cint(entries) == len(self.employees): if cint(entries) == len(self.employees):
self.set_onload("submitted_ss", True) self.set_onload("submitted_ss", True)
def on_submit(self): def on_submit(self):
self.create_salary_slips() self.create_salary_slips()
def before_submit(self): def before_submit(self):
self.validate_employee_details()
if self.validate_attendance: if self.validate_attendance:
if self.validate_employee_attendance(): if self.validate_employee_attendance():
frappe.throw(_("Cannot Submit, Employees left to mark attendance")) frappe.throw(_("Cannot Submit, Employees left to mark attendance"))
def validate_employee_details(self):
emp_with_sal_slip = []
for employee_details in self.employees:
if frappe.db.exists("Salary Slip", {"employee": employee_details.employee, "start_date": self.start_date, "end_date": self.end_date, "docstatus": 1}):
emp_with_sal_slip.append(employee_details.employee)
if len(emp_with_sal_slip):
frappe.throw(_("Salary Slip already exists for {0} ").format(comma_and(emp_with_sal_slip)))
def on_cancel(self): def on_cancel(self):
frappe.delete_doc("Salary Slip", frappe.db.sql_list("""select name from `tabSalary Slip` frappe.delete_doc("Salary Slip", frappe.db.sql_list("""select name from `tabSalary Slip`
where payroll_entry=%s """, (self.name))) where payroll_entry=%s """, (self.name)))
@ -71,8 +81,17 @@ class PayrollEntry(Document):
and t2.docstatus = 1 and t2.docstatus = 1
%s order by t2.from_date desc %s order by t2.from_date desc
""" % cond, {"sal_struct": tuple(sal_struct), "from_date": self.end_date, "payroll_payable_account": self.payroll_payable_account}, as_dict=True) """ % cond, {"sal_struct": tuple(sal_struct), "from_date": self.end_date, "payroll_payable_account": self.payroll_payable_account}, as_dict=True)
emp_list = self.remove_payrolled_employees(emp_list)
return emp_list return emp_list
def remove_payrolled_employees(self, emp_list):
for employee_details in emp_list:
if frappe.db.exists("Salary Slip", {"employee": employee_details.employee, "start_date": self.start_date, "end_date": self.end_date, "docstatus": 1}):
emp_list.remove(employee_details)
return emp_list
def fill_employee_details(self): def fill_employee_details(self):
self.set('employees', []) self.set('employees', [])
employees = self.get_emp_list() employees = self.get_emp_list()