Payroll Entry - Validate Attendance (#14738)

diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.js b/erpnext/hr/doctype/payroll_entry/payroll_entry.js
index 26008d9..2f6f4b2 100644
--- a/erpnext/hr/doctype/payroll_entry/payroll_entry.js
+++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.js
@@ -185,6 +185,23 @@
 			}
 		});
 	},
+
+	validate_attendance: function(frm){
+		if(frm.doc.validate_attendance && frm.doc.employees){
+			frappe.call({
+				method: 'validate_employee_attendance',
+				args: {},
+				callback: function(r) {
+					render_employee_attendance(frm, r.message);
+				},
+				doc: frm.doc,
+				freeze: true,
+				freeze_message: 'Validating Employee Attendance...'
+			});
+		}else{
+			frm.fields_dict.attendance_detail_html.html("");
+		}
+	}
 });
 
 // Submit salary slips
@@ -214,6 +231,9 @@
 	var callback = function (r) {
 		if (r.docs[0].employees){
 			cur_frm.refresh_field('employees');
+			if(r.docs[0].validate_attendance){
+				render_employee_attendance(cur_frm, r.message);
+			}
 		}
 	};
 	return $c('runserverobj', { 'method': 'fill_employee_details', 'docs': doc }, callback);
@@ -237,3 +257,12 @@
 		frappe.msgprint(__("Company, Payment Account, From Date and To Date is mandatory"));
 	}
 };
+
+
+let render_employee_attendance = function(frm, data) {
+	frm.fields_dict.attendance_detail_html.html(
+		frappe.render_template('employees_to_mark_attendance', {
+			data: data
+		})
+	);
+}
diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.json b/erpnext/hr/doctype/payroll_entry/payroll_entry.json
index 54f1e45..2cb6751 100644
--- a/erpnext/hr/doctype/payroll_entry/payroll_entry.json
+++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.json
@@ -15,6 +15,7 @@
  "fields": [
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -46,6 +47,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -77,6 +79,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -110,6 +113,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -142,6 +146,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -176,6 +181,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -207,6 +213,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -239,6 +246,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -271,6 +279,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -303,6 +312,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -333,6 +343,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -364,6 +375,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -396,6 +408,101 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "section_break_13", 
+   "fieldtype": "Section Break", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "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, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "validate_attendance", 
+   "fieldtype": "Check", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Validate Attendance", 
+   "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, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "attendance_detail_html", 
+   "fieldtype": "HTML", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "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, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -426,6 +533,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -459,6 +567,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -490,6 +599,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -522,6 +632,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -554,6 +665,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -584,6 +696,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -615,6 +728,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -646,6 +760,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -677,6 +792,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -709,6 +825,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -739,6 +856,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -771,6 +889,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -802,6 +921,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -833,6 +953,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 1, 
    "bold": 0, 
    "collapsible": 0, 
@@ -866,6 +987,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -896,6 +1018,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -927,6 +1050,7 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -968,7 +1092,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-04-27 12:42:45.054509", 
+ "modified": "2018-06-28 13:55:48.295327", 
  "modified_by": "Administrator", 
  "module": "HR", 
  "name": "Payroll Entry", 
diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.py b/erpnext/hr/doctype/payroll_entry/payroll_entry.py
index ed4957d..4f161ec 100644
--- a/erpnext/hr/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.py
@@ -6,16 +6,21 @@
 import frappe
 from frappe.model.document import Document
 from dateutil.relativedelta import relativedelta
-from frappe.utils import cint, flt, nowdate, add_days, getdate, fmt_money, add_to_date, DATE_FORMAT
+from frappe.utils import cint, flt, nowdate, add_days, getdate, fmt_money, add_to_date, DATE_FORMAT, date_diff
 from frappe import _
 from erpnext.accounts.utils import get_fiscal_year
-
+from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
 
 class PayrollEntry(Document):
 
 	def on_submit(self):
 		self.create_salary_slips()
 
+	def before_submit(self):
+		if self.validate_attendance:
+			if self.validate_employee_attendance():
+				frappe.throw(_("Cannot Submit, Employees left to mark attendance"))
+
 	def get_emp_list(self):
 		"""
 			Returns list of active employees based on selected criteria
@@ -61,6 +66,9 @@
 		for d in employees:
 			self.append('employees', d)
 
+		if self.validate_attendance:
+			return self.validate_employee_attendance()
+
 	def get_filter_condition(self):
 		self.check_mandatory()
 
@@ -381,6 +389,39 @@
 		self.update(get_start_end_dates(self.payroll_frequency,
 			self.start_date or self.posting_date, self.company))
 
+	def validate_employee_attendance(self):
+		employees_to_mark_attendance = []
+		days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
+		for employee_detail in self.employees:
+			days_holiday = self.get_count_holidays_of_employee(employee_detail.employee)
+			days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee)
+			days_in_payroll = date_diff(self.end_date, self.start_date) + 1
+			if days_in_payroll > days_holiday + days_attendance_marked:
+				employees_to_mark_attendance.append({
+					"employee": employee_detail.employee,
+					"employee_name": employee_detail.employee_name
+					})
+		return employees_to_mark_attendance
+
+	def get_count_holidays_of_employee(self, employee):
+		holiday_list = get_holiday_list_for_employee(employee)
+		holidays = 0
+		if holiday_list:
+			days = frappe.db.sql("""select count(*) from tabHoliday where
+				parent=%s and holiday_date between %s and %s""", (holiday_list,
+				self.start_date, self.end_date))
+			if days and days[0][0]:
+				holidays = days[0][0]
+		return holidays
+
+	def get_count_employee_attendance(self, employee):
+		marked_days = 0
+		attendances = frappe.db.sql("""select count(*) from tabAttendance where
+			employee=%s and docstatus=1 and attendance_date between %s and %s""",
+			(employee, self.start_date, self.end_date))
+		if attendances and attendances[0][0]:
+			marked_days = attendances[0][0]
+		return marked_days
 
 @frappe.whitelist()
 def get_start_end_dates(payroll_frequency, start_date=None, company=None):
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 24ccfbf..ed4ebab 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -30,6 +30,7 @@
         "public/js/payment/pos_payment.html",
         "public/js/payment/payment_details.html",
         "public/js/templates/item_selector.html",
+				"public/js/templates/employees_to_mark_attendance.html",
         "public/js/utils/item_selector.js",
         "public/js/help_links.js",
         "public/js/agriculture/ternary_plot.js",
diff --git a/erpnext/public/js/templates/employees_to_mark_attendance.html b/erpnext/public/js/templates/employees_to_mark_attendance.html
new file mode 100644
index 0000000..167c775
--- /dev/null
+++ b/erpnext/public/js/templates/employees_to_mark_attendance.html
@@ -0,0 +1,14 @@
+{% if data %}
+<div class="col-md-12 col-xs-12" align="center">
+	<div class="col-md-12 col-xs-12" style="padding-bottom:15px;">
+		<b>Employees to mark attendance</b>
+	</div>
+	{% for item in data %}
+	<div class="col-md-4 col-xs-6" style="padding-bottom:10px;">
+		{{ item.employee }} &nbsp;&nbsp; {{ item.employee_name }}
+	</div>
+	{% } %}
+</div>
+{% } else { %}
+<div></div>
+{% } %}