Feature - Employee Attendance Tool
diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py
index 4501be7..e147112 100644
--- a/erpnext/config/hr.py
+++ b/erpnext/config/hr.py
@@ -60,9 +60,9 @@
 			"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
 				},
 				{
@@ -73,6 +73,14 @@
 				},
 				{
 					"type": "doctype",
+					"name": "Process Payroll",
+					"label": _("Process Payroll"),
+					"description":_("Generate Salary Slips"),
+					"hide_count": True
+				},
+			
+				{
+					"type": "doctype",
 					"name": "Leave Control Panel",
 					"label": _("Leave Allocation Tool"),
 					"description":_("Allocate leaves for the year."),
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 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/employee_attendance_tool/__init__.py
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 0000000..d25fb22
--- /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 0000000..9467d39
--- /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 = $('<div>')
+							.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 = $('<div>')
+							.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('<div class="col-sm-3">\
+				<label class="marked-employee-label"><span class=%(icon)s></span>\
+				%(employee)s</label>\
+				</div>', {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 = $('<div class="col-sm-12 top-toolbar">\
+			<button class="btn btn-default btn-add btn-xs"></button>\
+			<button class="btn btn-xs btn-default btn-remove"></button>\
+			</div>').appendTo($(this.wrapper));
+
+		var mark_employee_toolbar = $('<div class="col-sm-12 bottom-toolbar">\
+			<button class="btn btn-default btn-primary btn-mark-present btn-xs"></button>\
+			<button class="btn btn-default btn-primary btn-mark-absent btn-xs"></button>\
+			<button class="btn btn-default btn-primary btn-mark-half-day btn-xs"></button></div>')
+
+		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('<div class="col-sm-3 unmarked-employee-checkbox">\
+				<div class="checkbox">\
+				<label><input type="checkbox" class="employee-check" employee="%(employee)s"/>\
+				%(employee)s</label>\
+				</div></div>', {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 0000000..f31bcf8
--- /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 0000000..622b0d9
--- /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()