From 07364f5ece4764fc58df16c43d9d208896cead79 Mon Sep 17 00:00:00 2001 From: Valmik Jangla Date: Wed, 27 Jan 2016 17:43:57 +0530 Subject: [PATCH] Feature - Employee Attendance Tool --- erpnext/config/hr.py | 14 +- .../employee_attendance_tool/__init__.py | 0 .../employee_attendance_tool.css | 21 ++ .../employee_attendance_tool.js | 233 +++++++++++++++ .../employee_attendance_tool.json | 275 ++++++++++++++++++ .../employee_attendance_tool.py | 52 ++++ 6 files changed, 592 insertions(+), 3 deletions(-) create mode 100644 erpnext/hr/doctype/employee_attendance_tool/__init__.py create mode 100644 erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css create mode 100644 erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js create mode 100644 erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.json create mode 100644 erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py index 4501be72d96..e1471124c9c 100644 --- a/erpnext/config/hr.py +++ b/erpnext/config/hr.py @@ -60,9 +60,9 @@ def get_data(): "items": [ { "type": "doctype", - "name": "Process Payroll", - "label": _("Process Payroll"), - "description":_("Generate Salary Slips"), + "name": "Employee Attendance Tool", + "label": _("Employee Attendance Tool"), + "description":_("Mark Employee Attendance in Bulk"), "hide_count": True }, { @@ -71,6 +71,14 @@ def get_data(): "description":_("Upload attendance from a .csv file"), "hide_count": True }, + { + "type": "doctype", + "name": "Process Payroll", + "label": _("Process Payroll"), + "description":_("Generate Salary Slips"), + "hide_count": True + }, + { "type": "doctype", "name": "Leave Control Panel", diff --git a/erpnext/hr/doctype/employee_attendance_tool/__init__.py b/erpnext/hr/doctype/employee_attendance_tool/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css new file mode 100644 index 00000000000..d25fb2247ed --- /dev/null +++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css @@ -0,0 +1,21 @@ +.top-toolbar{ + padding-bottom: 30px; + margin-left: -17px; +} + +.bottom-toolbar{ + margin-left: -17px; + margin-top: 20px; +} + +.btn{ + margin-right: 5px; +} + +.marked-employee-label{ + font-weight: normal; +} + +.checkbox{ + margin-top: -3px; +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js new file mode 100644 index 00000000000..9467d39116e --- /dev/null +++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js @@ -0,0 +1,233 @@ +frappe.ui.form.on("Employee Attendance Tool", { + refresh:function(frm){ + frm.disable_save(); + }, + + onload:function(frm){ + erpnext.employee_attendance_tool.load_employees(frm); + }, + + date:function(frm){ + erpnext.employee_attendance_tool.load_employees(frm); + }, + + department:function(frm){ + erpnext.employee_attendance_tool.load_employees(frm); + }, + + branch:function(frm){ + erpnext.employee_attendance_tool.load_employees(frm); + }, + + company:function(frm){ + erpnext.employee_attendance_tool.load_employees(frm); + } + + +}); + + +erpnext.employee_attendance_tool = { + load_employees: function(frm){ + if(frm.doc.date){ + frappe.call({ + method:"erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.get_employees", + args:{ + date:frm.doc.date, + department:frm.doc.department, + branch:frm.doc.branch, + company:frm.doc.company + }, + callback:function(r){ + if(r.message['unmarked'].length > 0){ + unhide_field('unmarked_attendance_section') + if(!frm.employee_area){ + frm.employee_area = $('
') + .appendTo(frm.fields_dict.employees_html.wrapper); + } + frm.EmployeeSelector = new erpnext.EmployeeSelector(frm, frm.employee_area, r.message['unmarked']) + } + else{ + hide_field('unmarked_attendance_section') + } + + if(r.message['marked'].length > 0){ + unhide_field('marked_attendance_section') + if(!frm.marked_employee_area){ + + frm.marked_employee_area = $('
') + .appendTo(frm.fields_dict.marked_attendance_html.wrapper); + } + frm.MarkedEmployee = new erpnext.MarkedEmployee(frm, frm.marked_employee_area, r.message['marked']) + } + else{ + hide_field('marked_attendance_section') + } + } + }); + } + } +} + +erpnext.MarkedEmployee = Class.extend({ + init: function(frm, wrapper, employee) { + this.wrapper = wrapper; + this.frm = frm; + this.make(frm, employee); + }, + make: function(frm, employee) { + var me = this; + $(this.wrapper).empty(); + + $.each(employee, function(i, m) { + var attendance_icon = "'icon-check'" + if(m.status == "Absent") { + attendance_icon="'icon-check-empty'" + } + else if(m.status == "Half Day"){ + attendance_icon = "'icon-check-minus'" + } + $(repl('
\ + \ +
', {employee: m.employee_name, icon: attendance_icon})).appendTo(me.wrapper); + }); + } +}); + + +erpnext.EmployeeSelector = Class.extend({ + init: function(frm, wrapper, employee) { + this.wrapper = wrapper; + this.frm = frm; + this.make(frm, employee); + }, + make: function(frm, employee) { + var me = this; + + $(this.wrapper).empty(); + var employee_toolbar = $('
\ + \ + \ +
').appendTo($(this.wrapper)); + + var mark_employee_toolbar = $('
\ + \ + \ +
') + + employee_toolbar.find(".btn-add") + .html(__('Check all')) + .on("click", function() { + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if(!$(check).is(":checked")) { + check.checked = true; + } + }); + }); + + employee_toolbar.find(".btn-remove") + .html(__('Uncheck all')) + .on("click", function() { + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if($(check).is(":checked")) { + check.checked = false; + } + }); + }); + + mark_employee_toolbar.find(".btn-mark-present") + .html(__('Mark Present')) + .on("click", function() { + var employee_present = []; + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if($(check).is(":checked")) { + employee_present.push(employee[i]); + } + }); + frappe.call({ + method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", + args:{ + "employee_list":employee_present, + "status":"Present", + "date":frm.doc.date, + "company":frm.doc.company + }, + + callback: function(r) { + erpnext.employee_attendance_tool.load_employees(frm); + + } + }); + }); + + mark_employee_toolbar.find(".btn-mark-absent") + .html(__('Mark Absent')) + .on("click", function() { + var employee_absent = []; + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if($(check).is(":checked")) { + employee_absent.push(employee[i]); + } + }); + frappe.call({ + method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", + args:{ + "employee_list":employee_absent, + "status":"Absent", + "date":frm.doc.date, + "company":frm.doc.company + }, + + callback: function(r) { + erpnext.employee_attendance_tool.load_employees(frm); + + } + }); + }); + + + mark_employee_toolbar.find(".btn-mark-half-day") + .html(__('Mark Half Day')) + .on("click", function() { + var employee_half_day = []; + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if($(check).is(":checked")) { + employee_half_day.push(employee[i]); + } + }); + frappe.call({ + method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", + args:{ + "employee_list":employee_half_day, + "status":"Half Day", + "date":frm.doc.date, + "company":frm.doc.company + }, + + callback: function(r) { + erpnext.employee_attendance_tool.load_employees(frm); + + } + }); + }); + + + $.each(employee, function(i, m) { + $(repl('
\ +
\ + \ +
', {employee: m.employee_name})).appendTo(me.wrapper); + + + + }); + + mark_employee_toolbar.appendTo($(this.wrapper)); + + + } +}); + + diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.json b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.json new file mode 100644 index 00000000000..f31bcf8e990 --- /dev/null +++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.json @@ -0,0 +1,275 @@ +{ + "allow_copy": 1, + "allow_import": 0, + "allow_rename": 0, + "creation": "2016-01-27 14:59:47.849379", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "default": "Today", + "fieldname": "date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "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": "department", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "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_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 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, + "fieldname": "branch", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Branch", + "length": 0, + "no_copy": 0, + "options": "Branch", + "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": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "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": "date", + "fieldname": "unmarked_attendance_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Unmarked Attendance", + "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": "employees_html", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Employees HTML", + "length": 0, + "no_copy": 0, + "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": "date", + "fieldname": "marked_attendance_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Marked Attendance", + "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": "marked_attendance_html", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Marked Attendance HTML", + "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 + } + ], + "hide_heading": 1, + "hide_toolbar": 1, + "idx": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2016-01-29 02:14:36.034952", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Attendance Tool", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 1 + } + ], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py new file mode 100644 index 00000000000..622b0d95dba --- /dev/null +++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py @@ -0,0 +1,52 @@ +# -*- 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 +import copy +import json +from frappe.model.document import Document + + +class EmployeeAttendanceTool(Document): + pass + + +@frappe.whitelist() +def get_employees(date, department=None, branch=None, company=None): + attendance_not_marked = [] + attendance_marked = [] + employee_list = frappe.get_list("Employee", fields=["employee", "employee_name"], filters={ + "status": "Active", "department": department, "branch": branch, "company": company}, order_by="employee_name") + marked_employee = {} + for emp in frappe.get_list("Attendance", fields=["employee", "status"], + filters={"att_date": date}): + marked_employee[emp['employee']] = emp['status'] + + for employee in employee_list: + employee['status'] = marked_employee.get(employee['employee']) + if employee['employee'] not in marked_employee: + attendance_not_marked.append(employee) + else: + attendance_marked.append(employee) + return { + "marked": attendance_marked, + "unmarked": attendance_not_marked + } + + +@frappe.whitelist() +def mark_employee_attendance(employee_list, status, date, company=None): + employee_list = json.loads(employee_list) + for employee in employee_list: + attendance = frappe.new_doc("Attendance") + attendance.employee = employee['employee'] + attendance.employee_name = employee['employee_name'] + attendance.att_date = date + attendance.status = status + if company: + attendance.company = company + else: + attendance.company = frappe.db.get_value("Employee", employee['employee'], "Company") + attendance.submit()