From e04a753d53014918bfc7fbdd8a2c77c2314ff0c7 Mon Sep 17 00:00:00 2001 From: alsum Date: Tue, 13 Nov 2018 11:43:58 +0200 Subject: [PATCH] assign-sa-to-group-of-employees --- .../salary_structure/salary_structure.js | 40 +++++++++++ .../salary_structure/salary_structure.py | 70 +++++++++++++++++++ .../salary_structure/test_salary_structure.py | 13 ++++ 3 files changed, 123 insertions(+) diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js index 4a111e7d4b..033938de36 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/salary_structure.js @@ -58,6 +58,9 @@ frappe.ui.form.on('Salary Structure', { doc.company = frm.doc.company; frappe.set_route('Form', 'Salary Structure Assignment', doc.name); }); + frm.add_custom_button(__("Assign to Employees"),function () { + frm.trigger('assign_to_employees') + }) } let fields_read_only = ["is_tax_applicable", "is_flexible_benefit", "variable_based_on_taxable_salary"]; fields_read_only.forEach(function(field) { @@ -65,6 +68,43 @@ frappe.ui.form.on('Salary Structure', { }); }, + assign_to_employees:function (frm) { + var d = new frappe.ui.Dialog({ + title: __("Assign to Employees"), + fields: [ + {fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")}, + {fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")}, + {fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')}, + {fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')}, + {fieldname:"employee", fieldtype: "Link", options: "Employee", label: __("Employee")}, + {fieldname:'base_variable', fieldtype:'Section Break'}, + {fieldname:'from_date', fieldtype:'Date', label: __('From Date'), "reqd": 1}, + {fieldname:'base_col_br', fieldtype:'Column Break'}, + {fieldname:'base', fieldtype:'Currency', label: __('Base')}, + {fieldname:'variable', fieldtype:'Currency', label: __('Variable')} + ], + primary_action: function() { + var data = d.get_values(); + + frappe.call({ + doc: frm.doc, + method: "assign_salary_structure", + args: data, + callback: function(r) { + if(!r.exc) { + d.hide(); + frm.reload_doc(); + } + } + }); + }, + primary_action_label: __('Assign') + }); + + + d.show(); + }, + salary_slip_based_on_timesheet: function(frm) { frm.trigger("toggle_fields") }, diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index a36d820f2c..7ead14030f 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -65,6 +65,76 @@ class SalaryStructure(Document): if not have_a_flexi and flt(self.max_benefits) > 0: frappe.throw(_("Salary Structure should have flexible benefit component(s) to dispense benefit amount")) + def get_employees(self, **kwargs): + conditions, values = [], [] + for field, value in kwargs.items(): + if value: + conditions.append("{0}=%s".format(field)) + values.append(value) + + condition_str = " and " + " and ".join(conditions) if conditions else "" + + employees = frappe.db.sql_list("select name from tabEmployee where status='Active' {condition}" + .format(condition=condition_str), tuple(values)) + + return employees + + @frappe.whitelist() + def assign_salary_structure(self, grade=None, department=None, designation=None,employee=None, + from_date=None, base=None,variable=None): + employees = self.get_employees(grade= grade,department= department,designation= designation,name=employee) + + if employees: + if len(employees) > 20: + frappe.enqueue(assign_salary_structure_for_employees, timeout=600, + employees=employees, salary_structure=self,from_date=from_date, base=base,variable=variable) + else: + assign_salary_structure_for_employees(employees, self,from_date=from_date, base=base,variable=variable) + else: + frappe.msgprint(_("No Employee Found")) + + + +def assign_salary_structure_for_employees(employees, salary_structure,from_date=None, base=None,variable=None): + salary_structures_assignments = [] + existing_assignments_for = get_existing_assignments(employees, salary_structure.name,from_date) + count=0 + for employee in employees: + if employee in existing_assignments_for: + continue + count +=1 + + salary_structures_assignment = create_salary_structures_assignment(employee, salary_structure, from_date, base, variable) + salary_structures_assignments.append(salary_structures_assignment) + frappe.publish_progress(count*100/len(set(employees) - set(existing_assignments_for)), title = _("Assigning Structures...")) + + if salary_structures_assignments: + frappe.msgprint(_("Structures have been assigned successfully")) + + +def create_salary_structures_assignment(employee, salary_structure, from_date, base, variable): + assignment = frappe.new_doc("Salary Structure Assignment") + assignment.employee = employee + assignment.salary_structure = salary_structure.name + assignment.from_date = from_date + assignment.base = base + assignment.variable = variable + assignment.save(ignore_permissions = True) + assignment.submit() + return assignment.name + + +def get_existing_assignments(employees, salary_structure,from_date): + salary_structures_assignments = frappe.db.sql_list(""" + select distinct employee from `tabSalary Structure Assignment` + where salary_structure=%s and employee in (%s) + and from_date=%s and docstatus=1 + """ % ('%s', ', '.join(['%s']*len(employees)),'%s'), [salary_structure] + employees+[from_date]) + if salary_structures_assignments: + frappe.msgprint(_("Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}") + .format("\n".join(salary_structures_assignments))) + return salary_structures_assignments + @frappe.whitelist() def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None): def postprocess(source, target): diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index 1a16db74a5..1a660d90ec 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -71,6 +71,19 @@ class TestSalaryStructure(unittest.TestCase): for row in salary_structure.deductions: self.assertFalse(("\n" in row.formula) or ("\n" in row.condition)) + def test_salary_structures_assignment(self): + salary_structure = make_salary_structure("Salary Structure Sample", "Monthly") + employee = "test_assign_stucture@salary.com" + employee_doc_name = make_employee(employee) + # clear the already assigned stuctures + frappe.db.sql('''delete from `tabSalary Structure Assignment` where employee=%s and salary_structure=%s ''', + ("test_assign_stucture@salary.com",salary_structure.name)) + #test structure_assignment + salary_structure.assign_salary_structure(employee=employee_doc_name,from_date='2013-01-01',base=5000,variable=200) + salary_structure_assignment = frappe.get_doc("Salary Structure Assignment",{'employee':employee_doc_name, 'from_date':'2013-01-01'}) + self.assertEqual(salary_structure_assignment.docstatus, 1) + self.assertEqual(salary_structure_assignment.base, 5000) + self.assertEqual(salary_structure_assignment.variable, 200) def make_salary_structure(salary_structure, payroll_frequency, employee=None, dont_submit=False, other_details=None, test_tax=False): if test_tax: