From 95ee7654a1ed53860e7494e76f3983de9471099a Mon Sep 17 00:00:00 2001 From: Zarrar Date: Wed, 29 Nov 2017 18:44:02 +0530 Subject: [PATCH] Employee tree (#11667) * nested set implemented * treeview for employee * patch added to assign lft rgt, update nsm_model on trash * call on_trash method of super class --- erpnext/hr/doctype/employee/employee.json | 92 ++++++++++++++++++- erpnext/hr/doctype/employee/employee.py | 38 ++++++-- erpnext/hr/doctype/employee/employee_tree.js | 36 ++++++++ erpnext/patches.txt | 3 +- .../v10_0/update_lft_rgt_for_employee.py | 8 ++ 5 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 erpnext/hr/doctype/employee/employee_tree.js create mode 100644 erpnext/patches/v10_0/update_lft_rgt_for_employee.py diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 227302c16c..2779b1d84c 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -2418,6 +2418,96 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "lft", + "fieldtype": "Int", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "lft", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "rgt", + "fieldtype": "Int", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "rgt", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "old_parent", + "fieldtype": "Data", + "hidden": 1, + "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Old Parent", + "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 } ], "has_web_view": 0, @@ -2432,7 +2522,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-10-04 11:42:02.495731", + "modified": "2017-11-19 01:27:48.222343", "modified_by": "Administrator", "module": "HR", "name": "Employee", diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 03626cdc5c..7c9c11295e 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -10,13 +10,14 @@ from frappe import throw, _, scrub import frappe.permissions from frappe.model.document import Document from erpnext.utilities.transaction_base import delete_events - +from frappe.utils.nestedset import NestedSet class EmployeeUserDisabledError(frappe.ValidationError): pass +class Employee(NestedSet): + nsm_parent_field = 'reports_to' -class Employee(Document): def autoname(self): naming_method = frappe.db.get_value("HR Settings", None, "emp_created_by") if not naming_method: @@ -52,7 +53,11 @@ class Employee(Document): frappe.permissions.remove_user_permission( "Employee", self.name, existing_user_id) + def update_nsm_model(self): + frappe.utils.nestedset.update_nsm(self) + def on_update(self): + self.update_nsm_model() if self.user_id: self.update_user() self.update_user_permissions() @@ -154,13 +159,13 @@ class Employee(Document): throw(_("Employee cannot report to himself.")) def on_trash(self): + super(Employee, self).on_trash() delete_events(self.doctype, self.name) def validate_prefered_email(self): if self.prefered_contact_email and not self.get(scrub(self.prefered_contact_email)): frappe.msgprint(_("Please enter " + self.prefered_contact_email)) - def get_timeline_data(doctype, name): '''Return timeline for attendance''' return dict(frappe.db.sql('''select unix_timestamp(attendance_date), count(*) @@ -183,7 +188,6 @@ def get_retirement_date(date_of_birth=None): return ret - def validate_employee_role(doc, method): # called via User hook if "Employee" in [d.role for d in doc.get("roles")]: @@ -241,7 +245,6 @@ def get_holiday_list_for_employee(employee, raise_exception=True): def is_holiday(employee, date=None): '''Returns True if given Employee has an holiday on the given date - :param employee: Employee `name` :param date: Date to check. Will check for today if None''' @@ -300,4 +303,27 @@ def get_employee_emails(employee_list): if user or email: employee_emails.append(user or email) - return employee_emails \ No newline at end of file + return employee_emails + +@frappe.whitelist() +def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False): + condition = '' + + if is_root: + parent = "" + if parent and company and parent!=company: + condition = ' and reports_to = "{0}"'.format(frappe.db.escape(parent)) + else: + condition = ' and ifnull(reports_to, "")=""' + + employee = frappe.db.sql(""" + select + name as value, employee_name as title, + exists(select name from `tabEmployee` where reports_to=emp.name) as expandable + from + `tabEmployee` emp + where company='{company}' {condition} order by name""" + .format(company=company, condition=condition), as_dict=1) + + # return employee + return employee \ No newline at end of file diff --git a/erpnext/hr/doctype/employee/employee_tree.js b/erpnext/hr/doctype/employee/employee_tree.js new file mode 100644 index 0000000000..5d3ec42fb1 --- /dev/null +++ b/erpnext/hr/doctype/employee/employee_tree.js @@ -0,0 +1,36 @@ +frappe.treeview_settings['Employee'] = { + get_tree_nodes: "erpnext.hr.doctype.employee.employee.get_children", + filters: [ + { + fieldname: "company", + fieldtype:"Select", + options: $.map(locals[':Company'], function(c) { return c.name; }).sort(), + label: __("Company"), + default: frappe.defaults.get_default('company') ? frappe.defaults.get_default('company') : "" + } + ], + breadcrumb: "Hr", + disable_add_node: true, + get_tree_root: false, + toolbar: [ + { toggle_btn: true }, + { + label:__("Edit"), + condition: function(node) { + return !node.is_root; + }, + click: function(node) { + frappe.set_route("Form", "Employee", node.data.value); + } + } + ], + menu_items: [ + { + label: __("New Employee"), + action: function() { + frappe.new_doc("Employee", true); + }, + condition: 'frappe.boot.user.can_create.indexOf("Employee") !== -1' + } + ], +}; \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 7725615b0d..a2efa82647 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -472,4 +472,5 @@ erpnext.patches.v9_0.update_multi_uom_fields_in_material_request erpnext.patches.v10_0.rename_schools_to_education erpnext.patches.v9_2.repost_reserved_qty_for_production erpnext.patches.v9_2.remove_company_from_patient -erpnext.patches.v9_2.set_item_name_in_production_order \ No newline at end of file +erpnext.patches.v9_2.set_item_name_in_production_order +erpnext.patches.v10_0.update_lft_rgt_for_employee diff --git a/erpnext/patches/v10_0/update_lft_rgt_for_employee.py b/erpnext/patches/v10_0/update_lft_rgt_for_employee.py new file mode 100644 index 0000000000..82fbeaaeaf --- /dev/null +++ b/erpnext/patches/v10_0/update_lft_rgt_for_employee.py @@ -0,0 +1,8 @@ +import frappe +from frappe.utils.nestedset import rebuild_tree + +def execute(): + """ assign lft and rgt appropriately """ + frappe.reload_doc("hr", "doctype", "employee") + + rebuild_tree("Employee", "reports_to") \ No newline at end of file