diff --git a/src/main/java/com/gcc/commissionerreview/service/AttendanceService.java b/src/main/java/com/gcc/commissionerreview/service/AttendanceService.java index 75b0ef2..c5fffc8 100644 --- a/src/main/java/com/gcc/commissionerreview/service/AttendanceService.java +++ b/src/main/java/com/gcc/commissionerreview/service/AttendanceService.java @@ -107,52 +107,118 @@ public class AttendanceService { String dateColumn; String filterDateColumn; - switch (category == null ? "present" : category.toLowerCase()) { - case "od": - dateColumn = "att.oddatetime"; - filterDateColumn = "att.oddatetime"; - break; - case "leave": - dateColumn = "att.leavedatetime"; - filterDateColumn = "att.leavedatetime"; - break; - default: - dateColumn = "att.indatetime"; - filterDateColumn = "att.indatetime"; - } - String sql = "SELECT et.enrollment_id, et.name, et.designation, et.department, et.incharge_name, et.incharge_phoneno, et.emp_id, et.park_name, " - + "ifnull(att.appversion,'') as appversion, ifnull(att.apptype,'')as apptype, ifnull(att.location,'') as location, " - + "DATE_FORMAT(att.indatetime, '%d-%m-%Y %h:%i %p') as intime, att.inphoto as inphoto, att.inby, " - + "DATE_FORMAT(att.outdatetime, '%d-%m-%Y %h:%i %p') as outtime, att.outphoto as outphoto, att.outby, " - + "DATE_FORMAT(att.oddatetime, '%d-%m-%Y %h:%i %p') as odtime, att.odby, et.zone, " - + "DATE_FORMAT(att.leavedatetime, '%d-%m-%Y %h:%i %p') as leavedatetime, att.leaveby " - + "FROM enrollment_table et " - + "inner join attendance att on et.enrollment_id = att.enrollment_id " - + " and " + filterDateColumn + " >= ? and " + filterDateColumn + " <= ? " - + "where et.isactive =1 and et.isdelete = 0 and et.emp_type = ? and et.zone !=0 " - + " and " + filterDateColumn + " is not null "; - - java.util.List params = new java.util.ArrayList<>(); - params.add(fromDateTime); - params.add(toDateTime); - params.add(empType); - - if (zone != null) { - sql += " and et.zone = ? "; - params.add(zone); - } + if (category != null && category.equalsIgnoreCase("notmarked")) { + dateColumn = "att.indatetime"; + filterDateColumn = "att.indatetime"; + + String sql = "SELECT et.enrollment_id, et.name, et.designation, et.department, et.incharge_name, et.incharge_phoneno, et.park_name, emp.image as image " + + " FROM enrollment_table et " + + " left join gccfacialdb.employees emp on et.enrollment_id = emp.employeeid " + + " where et.isactive =1 and et.isdelete = 0 and et.emp_type = ? and et.zone !=0 " + + " and et.enrollment_id not in (select enrollment_id from attendance where date(indatetime) between ? and ?) "; + java.util.List params = new java.util.ArrayList<>(); + params.add(empType); + params.add(fromDate); + params.add(toDate); + + if (zone != null) { + sql += " and et.zone = ? "; + params.add(zone); + } + + if (parkName != null && !parkName.isEmpty() && !parkName.equals("-")) { + sql += " and et.park_name = ? "; + params.add(parkName); + } + + if (parkName != null && !parkName.isEmpty() && parkName.equals("-")) { + sql += " and et.park_name = '' "; + } + + // sql += " order by " + dateColumn + " desc "; + + System.out.println("EmpType: " + empType + ", Category: " + category + ", From: " + fromDateTime + ", To: " + + toDateTime + ", Zone: " + zone + ", ParkName: " + parkName); + return jdbcTemplate.queryForList(sql, params.toArray()); + } else if (category != null && category.equals("notregistered")) { + + String sql = "SELECT et.enrollment_id, et.name, et.designation, et.department, et.incharge_name, et.incharge_phoneno, et.park_name, emp.image as image " + + " FROM enrollment_table et " + + " left join gccfacialdb.employees emp on et.enrollment_id = emp.employeeid " + + " where et.isactive =1 and et.isdelete = 0 and et.emp_type = ? and et.zone !=0 and facial_attendance = 0 "; + java.util.List params = new java.util.ArrayList<>(); + params.add(empType); + + if (zone != null) { + sql += " and et.zone = ? "; + params.add(zone); + } + + if (parkName != null && !parkName.isEmpty() && !parkName.equals("-")) { + sql += " and et.park_name = ? "; + params.add(parkName); + } + + if (parkName != null && !parkName.isEmpty() && parkName.equals("-")) { + sql += " and et.park_name = '' "; + } + + // sql += " order by " + dateColumn + " desc "; + + System.out.println("EmpType: " + empType + ", Category: " + category + ", From: " + fromDateTime + ", To: " + + toDateTime + ", Zone: " + zone + ", ParkName: " + parkName); + return jdbcTemplate.queryForList(sql, params.toArray()); - if (parkName != null && !parkName.isEmpty()) { - sql += " and et.park_name = ? "; - params.add(parkName); - } - - sql += " order by " + dateColumn + " desc "; + } else { - System.out.println("EmpType: " + empType + ", Category: " + category + ", From: " + fromDateTime + ", To: " - + toDateTime + ", Zone: " + zone + ", ParkName: " + parkName); - return jdbcTemplate.queryForList(sql, params.toArray()); + switch (category == null ? "present" : category.toLowerCase()) { + case "od": + dateColumn = "att.oddatetime"; + filterDateColumn = "att.oddatetime"; + break; + case "leave": + dateColumn = "att.leavedatetime"; + filterDateColumn = "att.leavedatetime"; + break; + default: + dateColumn = "att.indatetime"; + filterDateColumn = "att.indatetime"; + } + + String sql = "SELECT et.enrollment_id, et.name, et.designation, et.department, et.incharge_name, et.incharge_phoneno, et.emp_id, et.park_name, " + + "ifnull(att.appversion,'') as appversion, ifnull(att.apptype,'')as apptype, ifnull(att.location,'') as location, " + + "DATE_FORMAT(att.indatetime, '%d-%m-%Y %h:%i %p') as intime, att.inphoto as inphoto, att.inby, " + + "DATE_FORMAT(att.outdatetime, '%d-%m-%Y %h:%i %p') as outtime, att.outphoto as outphoto, att.outby, " + + "DATE_FORMAT(att.oddatetime, '%d-%m-%Y %h:%i %p') as odtime, att.odby, et.zone, " + + "DATE_FORMAT(att.leavedatetime, '%d-%m-%Y %h:%i %p') as leavedatetime, att.leaveby " + + "FROM enrollment_table et " + + "inner join attendance att on et.enrollment_id = att.enrollment_id " + + " and " + filterDateColumn + " >= ? and " + filterDateColumn + " <= ? " + + "where et.isactive =1 and et.isdelete = 0 and et.emp_type = ? and et.zone !=0 " + + " and " + filterDateColumn + " is not null "; + + java.util.List params = new java.util.ArrayList<>(); + params.add(fromDateTime); + params.add(toDateTime); + params.add(empType); + + if (zone != null) { + sql += " and et.zone = ? "; + params.add(zone); + } + + if (parkName != null && !parkName.isEmpty()) { + sql += " and et.park_name = ? "; + params.add(parkName); + } + + sql += " order by " + dateColumn + " desc "; + + System.out.println("EmpType: " + empType + ", Category: " + category + ", From: " + fromDateTime + ", To: " + + toDateTime + ", Zone: " + zone + ", ParkName: " + parkName); + return jdbcTemplate.queryForList(sql, params.toArray()); + } } public List> getParkWiseSummary(String fromDateStr, String toDateStr, Integer zone) { @@ -181,10 +247,10 @@ public class AttendanceService { + "SUM(CASE WHEN facial_attendance = 1 THEN 1 ELSE 0 END) AS facialRegistered, " + "SUM(CASE WHEN facial_attendance = 0 THEN 1 ELSE 0 END) AS facialNotRegistered " + "FROM enrollment_table " - + "WHERE isactive = 1 AND isdelete = 0 AND emp_type = 'Park' AND zone = ? AND park_name != '' " + + "WHERE isactive = 1 AND isdelete = 0 AND emp_type = 'Park' AND zone = ? " + "GROUP BY park_name " + ") " - + "SELECT et.park_name, COUNT(DISTINCT et.enrollment_id) AS TotalCount, " + + "SELECT et.park_name, COUNT(DISTINCT et.enrollment_id) AS TotalCount, et.incharge_name, et.incharge_phoneno, " + "COALESCE(fd.facialRegistered, 0) AS facialRegistered, COALESCE(fd.facialNotRegistered, 0) AS facialNotRegistered, " + "COUNT(DISTINCT CASE WHEN att.indatetime >= ? AND att.indatetime <= ? THEN att.enrollment_id END) AS AttendanceMarked, " + "COUNT(DISTINCT CASE WHEN att.oddatetime >= ? AND att.oddatetime <= ? THEN att.enrollment_id END) AS ODCount, " @@ -192,8 +258,8 @@ public class AttendanceService { + "FROM enrollment_table et " + "LEFT JOIN attendance att ON et.enrollment_id = att.enrollment_id " + "LEFT JOIN facial_data fd ON et.park_name = fd.park_name " - + "WHERE et.isactive = 1 AND et.isdelete = 0 AND et.emp_type = 'Park' AND et.zone != 0 AND et.zone = ? AND et.park_name != '' " - + "GROUP BY et.park_name, fd.facialRegistered, fd.facialNotRegistered " + + "WHERE et.isactive = 1 AND et.isdelete = 0 AND et.emp_type = 'Park' AND et.zone != 0 AND et.zone = ? " + + "GROUP BY et.park_name, fd.facialRegistered, fd.facialNotRegistered, et.incharge_name, et.incharge_phoneno " + "ORDER BY et.park_name"; return jdbcTemplate.queryForList(sql, new Object[] { zone, fromDateTime, toDateTime, fromDateTime, toDateTime, diff --git a/src/main/resources/templates/modules/attendance-dashboard.html b/src/main/resources/templates/modules/attendance-dashboard.html index f370217..aac6e7a 100644 --- a/src/main/resources/templates/modules/attendance-dashboard.html +++ b/src/main/resources/templates/modules/attendance-dashboard.html @@ -224,7 +224,7 @@ if (!Array.isArray(data)) { target.innerText = JSON.stringify(data); return; } if (data.length === 0) { target.innerHTML = '
No records
'; return; } const rows = data.slice().sort((a, b) => String(a.zone || '').localeCompare(String(b.zone || ''), undefined, { numeric: true })); - let html = ''; + let html = '
ZoneTotalFacial RegisteredFacial Not RegisteredPresentODLeave
'; rows.forEach(r => { const zone = r.zone; const total = Number(r.TotalCount) || 0; @@ -233,11 +233,14 @@ const present = Number(r.AttendanceMarked) || 0; const od = Number(r.ODCount) || 0; const leave = Number(r.LeaveCount) || 0; + const notMarked = total - present - od - leave; html += ``; html += ``; html += ``; - html += ``; + html += ``; html += ``; @@ -247,9 +250,39 @@ html += ``; + html += ``; html += ''; }); - html += '
ZoneTotalFacial RegisteredFacial Not RegisteredPresentODLeaveNot Marked
${zone || '-'}${total}${facialRegistered}${facialNotRegistered}` + (facialNotRegistered > 0 + ? `${facialNotRegistered}` + : `${facialNotRegistered}`) + `` + (present > 0 ? `${present}` : `${present}`) + `` + (leave > 0 ? `${leave}` : `${leave}`) + `` + (notMarked > 0 + ? `${notMarked}` + : `${notMarked}`) + `
'; + + // Compute grand totals + let grandTotal = 0, grandFacialReg = 0, grandFacialNotReg = 0, grandPresent = 0, grandOd = 0, grandLeave = 0, grandNotMarked = 0; + rows.forEach(r => { + const total = Number(r.TotalCount) || 0; + const facialRegistered = Number(r.facialRegistered) || 0; + const facialNotRegistered = Number(r.facialNotRegistered) || 0; + const present = Number(r.AttendanceMarked) || 0; + const od = Number(r.ODCount) || 0; + const leave = Number(r.LeaveCount) || 0; + grandTotal += total; + grandFacialReg += facialRegistered; + grandFacialNotReg += facialNotRegistered; + grandPresent += present; + grandOd += od; + grandLeave += leave; + grandNotMarked += (total - present - od - leave); + }); + html += ''; + html += 'Total'; + html += `${grandTotal}`; + html += `${grandFacialReg}`; + html += `${grandFacialNotReg}`; + html += `${grandPresent}`; + html += `${grandOd}`; + html += `${grandLeave}`; + html += `${grandNotMarked}`; + html += ''; target.innerHTML = html; } @@ -336,27 +369,63 @@ if (data.length === 0) { modalBody.innerHTML = '
No park-wise records
'; return; } let html = '
'; - html += ''; + html += ''; html += ''; data.forEach(r => { const park = r.park_name || '-'; + const inchargeName = r.incharge_name || '-'; + const inchargePhone = r.incharge_phoneno || '-'; const total = Number(r.TotalCount) || 0; const facialRegistered = Number(r.facialRegistered) || 0; const facialNotRegistered = Number(r.facialNotRegistered) || 0; const present = Number(r.AttendanceMarked) || 0; const od = Number(r.ODCount) || 0; const leave = Number(r.LeaveCount) || 0; + const notMarked = total - present - od - leave; html += ''; html += ``; + html += ``; + html += ``; html += ``; html += ``; - html += ``; + // html += ``; + html += ``; html += ``; html += ``; html += ``; + html += ``; html += ''; }); - html += '
Park NameTotalFacial RegisteredFacial Not RegisteredPresentODLeavePark NameIncharge NameMobile NumberTotalFacial RegisteredFacial Not RegisteredPresentODLeaveNot Marked
${park}${inchargeName}${inchargePhone}${total}${facialRegistered}${facialNotRegistered}${facialNotRegistered}` + (facialNotRegistered > 0 ? `${facialNotRegistered}` : `${facialNotRegistered}`) + `` + (present > 0 ? `${present}` : `${present}`) + `` + (od > 0 ? `${od}` : `${od}`) + `` + (leave > 0 ? `${leave}` : `${leave}`) + `` + (notMarked > 0 ? `${notMarked}` : `${notMarked}`) + `
'; + + // Compute grand totals for park-wise summary + let grandTotal = 0, grandFacialReg = 0, grandFacialNotReg = 0, grandPresent = 0, grandOd = 0, grandLeave = 0, grandNotMarked = 0; + data.forEach(r => { + const total = Number(r.TotalCount) || 0; + const facialRegistered = Number(r.facialRegistered) || 0; + const facialNotRegistered = Number(r.facialNotRegistered) || 0; + const present = Number(r.AttendanceMarked) || 0; + const od = Number(r.ODCount) || 0; + const leave = Number(r.LeaveCount) || 0; + grandTotal += total; + grandFacialReg += facialRegistered; + grandFacialNotReg += facialNotRegistered; + grandPresent += present; + grandOd += od; + grandLeave += leave; + grandNotMarked += (total - present - od - leave); + }); + html += ''; + html += 'Total'; + html += ''; + html += ''; + html += `${grandTotal}`; + html += `${grandFacialReg}`; + html += `${grandFacialNotReg}`; + html += `${grandPresent}`; + html += `${grandOd}`; + html += `${grandLeave}`; + html += `${grandNotMarked}`; + html += ''; modalBody.innerHTML = html; } catch (e) { modalBody.innerHTML = `
Error: ${e}
`; @@ -454,6 +523,12 @@ displayColumns = ['enrollment_id', 'name', 'designation', 'department', 'incharge_name', 'incharge_phoneno', 'App Version', 'App Type', 'Location']; if (empType === 'Park') displayColumns.push('park_name'); displayColumns.push('odtime', 'odby'); + } else if (catLower === 'notmarked') { + displayColumns = ['enrollment_id', 'name', 'designation', 'department', 'incharge_name', 'incharge_phoneno', 'image']; + // if (empType === 'Park') displayColumns.push('park_name'); + } else if (catLower === 'notregistered') { + displayColumns = ['enrollment_id', 'name', 'designation', 'department', 'incharge_name', 'incharge_phoneno']; + // if (empType === 'Park') displayColumns.push('park_name'); } else { displayColumns = Object.keys(data[0]); } @@ -490,7 +565,8 @@ incharge_phoneno: 'Incharge Contact', appversion: 'App Version', apptype: 'App Type', - location: 'Location' + location: 'Location', + image: 'Image' }; if (map[key]) return map[key]; return key.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); @@ -516,7 +592,7 @@ // html += `${val != null ? val : '-'}`; // } - if ((k === 'inphoto' || k === 'outphoto') && val) { + if ((k === 'inphoto' || k === 'outphoto' || k === 'image') && val) { html += ` photo