[ADD] ATTENDANCE: Attendance dashboard
This commit is contained in:
@@ -1,4 +1,33 @@
|
||||
$(document).ready(function () {
|
||||
|
||||
// initializing the department overtime chart.
|
||||
var departmentChartData = {
|
||||
labels: [],
|
||||
datasets: [],
|
||||
};
|
||||
window["departmentOvertimeChart"] = {};
|
||||
const departmentOvertimeChart = document.getElementById("departmentOvertimeChart");
|
||||
var departmentAttendanceChart = new Chart(departmentOvertimeChart, {
|
||||
type: "pie",
|
||||
data: departmentChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
afterRender: (departmentAttendanceChart) => emptyOvertimeChart(departmentAttendanceChart),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
var today = new Date();
|
||||
month = ("0" + (today.getMonth() + 1)).slice(-2);
|
||||
year = today.getFullYear();
|
||||
var day = ("0" + today.getDate()).slice(-2);
|
||||
var formattedDate = year + "-" + month + "-" + day;
|
||||
var currentWeek = getWeekNumber(today);
|
||||
|
||||
$("#attendance_month").val(formattedDate);
|
||||
|
||||
$.ajax({
|
||||
@@ -11,6 +40,148 @@ $(document).ready(function () {
|
||||
createAttendanceChart(response.dataSet, response.labels);
|
||||
},
|
||||
});
|
||||
|
||||
// Function to update the department overtime chart according to the response fetched from backend.
|
||||
|
||||
function departmentDataUpdate(response) {
|
||||
departmentChartData.labels = response.labels;
|
||||
departmentChartData.datasets=response.dataset;
|
||||
departmentChartData.message=response.message;
|
||||
departmentChartData.emptyImageSrc=response.emptyImageSrc;
|
||||
departmentAttendanceChart.update();
|
||||
}
|
||||
|
||||
// Function to update the department overtime chart according to the dates provided.
|
||||
|
||||
function changeDepartmentMonth() {
|
||||
let type = $("#department_date_type").val();
|
||||
let date = $("#department_month").val();
|
||||
let end_date = $("#department_month2").val();
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/attendance/department-overtime-chart",
|
||||
dataType: "json",
|
||||
data: {
|
||||
date: date,
|
||||
type: type,
|
||||
end_date: end_date,
|
||||
},
|
||||
success: function (response) {
|
||||
departmentDataUpdate(response);
|
||||
},
|
||||
error: (error) => {},
|
||||
});
|
||||
}
|
||||
|
||||
// Function to update the input fields according to type select field.
|
||||
|
||||
function changeDepartmentView(element) {
|
||||
var dataType = $(element).val();
|
||||
if (dataType === "date_range") {
|
||||
$("#department_month").prop("type", "date");
|
||||
$("#department_day_input").after(
|
||||
'<input type="date" class="mb-2 float-end pointer oh-select ml-2" id="department_month2" style="width: 100px;color:#5e5c5c;"/>'
|
||||
);
|
||||
$("#department_month").val(formattedDate);
|
||||
$("#department_month2").val(formattedDate);
|
||||
changeDepartmentMonth();
|
||||
} else {
|
||||
$("#department_month2").remove();
|
||||
if (dataType === "weekly") {
|
||||
$("#department_month").prop("type", "week");
|
||||
if (currentWeek <10){
|
||||
$("#department_month").val(`${year}-W0${currentWeek}`);
|
||||
}
|
||||
else {
|
||||
$("#department_month").val(`${year}-W${currentWeek}`);
|
||||
}
|
||||
changeDepartmentMonth();
|
||||
} else if (dataType === "day") {
|
||||
$("#department_month").prop("type", "date");
|
||||
$("#department_month").val(formattedDate);
|
||||
changeDepartmentMonth();
|
||||
} else {
|
||||
$("#department_month").prop("type", "month");
|
||||
$("#department_month").val(`${year}-${month}`);
|
||||
changeDepartmentMonth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function for empty message for department overtime chart.
|
||||
|
||||
function emptyOvertimeChart(departmentAttendanceChart, args, options) {
|
||||
flag = false;
|
||||
for (let i = 0; i < departmentAttendanceChart.data.datasets.length; i++) {
|
||||
flag = flag + departmentAttendanceChart.data.datasets[i].data.some(Boolean);
|
||||
}
|
||||
if (!flag) {
|
||||
const { ctx, canvas } = departmentAttendanceChart;
|
||||
departmentAttendanceChart.clear();
|
||||
const parent = canvas.parentElement;
|
||||
|
||||
// Set canvas width/height to match
|
||||
canvas.width = parent.clientWidth;
|
||||
canvas.height = parent.clientHeight;
|
||||
// Calculate center position
|
||||
const x = canvas.width / 2;
|
||||
const y = (canvas.height - 70) / 2;
|
||||
var noDataImage = new Image();
|
||||
noDataImage.src = departmentAttendanceChart.data.emptyImageSrc
|
||||
? departmentAttendanceChart.data.emptyImageSrc
|
||||
: "/static/images/ui/joiningchart.png";
|
||||
|
||||
message = departmentAttendanceChart.data.message
|
||||
? departmentAttendanceChart.data.message
|
||||
: emptyMessages[languageCode];
|
||||
|
||||
noDataImage.onload = () => {
|
||||
// Draw image first at center
|
||||
ctx.drawImage(noDataImage, x - 35, y, 70, 70);
|
||||
|
||||
// Draw text below image
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillStyle = "hsl(0,0%,45%)";
|
||||
ctx.font = "16px Poppins";
|
||||
ctx.fillText(message, x, y + 70 + 30);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Ajax request to create department overtime chart initially.
|
||||
|
||||
$.ajax({
|
||||
url: "/attendance/department-overtime-chart",
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
},
|
||||
success: (response) => {
|
||||
|
||||
departmentDataUpdate(response);
|
||||
|
||||
},
|
||||
error: (error) => {
|
||||
console.log("Error", error);
|
||||
},
|
||||
});
|
||||
|
||||
// Functions to update department overtime chart while changing the date input field and select input field.
|
||||
|
||||
$("#departmentChartCard").on("change","#department_date_type", function (e) {
|
||||
changeDepartmentView($(this))
|
||||
})
|
||||
|
||||
$("#departmentChartCard").on("change","#department_month", function (e) {
|
||||
changeDepartmentMonth()
|
||||
})
|
||||
|
||||
$("#departmentChartCard").on("change","#department_month2", function (e) {
|
||||
changeDepartmentMonth()
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
var data;
|
||||
@@ -149,7 +320,12 @@ function changeView(element) {
|
||||
$("#attendance_month2").remove();
|
||||
if (dataType === "weekly") {
|
||||
$("#attendance_month").prop("type", "week");
|
||||
$("#attendance_month").val(`${year}-W${currentWeek}`);
|
||||
if (currentWeek <10){
|
||||
$("#attendance_month").val(`${year}-W0${currentWeek}`);
|
||||
}
|
||||
else {
|
||||
$("#attendance_month").val(`${year}-W${currentWeek}`);
|
||||
}
|
||||
changeMonth();
|
||||
} else if (dataType === "day") {
|
||||
$("#attendance_month").prop("type", "date");
|
||||
@@ -163,7 +339,7 @@ function changeView(element) {
|
||||
}
|
||||
}
|
||||
var chart = new Chart(
|
||||
document.getElementById("pendingHoursCanvas").getContext("2d"),
|
||||
document.getElementById("pendingHoursCanvas"),
|
||||
{}
|
||||
);
|
||||
window["pendingHoursCanvas"] = chart;
|
||||
@@ -174,12 +350,13 @@ function pendingHourChart(year, month) {
|
||||
data: { month: month, year: year },
|
||||
success: function (response) {
|
||||
pendingHoursCanvas.destroy();
|
||||
var ctx = document.getElementById("pendingHoursCanvas").getContext("2d");
|
||||
var ctx = document.getElementById("pendingHoursCanvas");
|
||||
pendingHoursCanvas = new Chart(ctx, {
|
||||
type: "bar", // Bar chart type
|
||||
data: response.data,
|
||||
options: {
|
||||
responsive: true,
|
||||
aspectRatio: false,
|
||||
indexAxis: "x",
|
||||
scales: {
|
||||
x: {
|
||||
@@ -187,6 +364,7 @@ function pendingHourChart(year, month) {
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
stacked: true,
|
||||
},
|
||||
},
|
||||
onClick: (e, activeEls) => {
|
||||
|
||||
@@ -1,181 +1,333 @@
|
||||
{% extends 'index.html' %}
|
||||
{% block content %}
|
||||
{% load static %}
|
||||
{% extends 'index.html' %} {% load i18n %} {% block content %} {% load static %}
|
||||
<style>
|
||||
.oh-sticky-table__right {
|
||||
position: sticky;
|
||||
right: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
<div class="oh-wrapper">
|
||||
<div class="oh-dashboard row">
|
||||
<div class="oh-dashboard__left col-12 col-sm-12 col-md-12 col-lg-9">
|
||||
<div class="oh-dashboard__cards row">
|
||||
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
|
||||
<div class="oh-card-dashboard oh-card-dashboard oh-card-dashboard--success">
|
||||
<div class="oh-card-dashboard__header">
|
||||
<span class="oh-card-dashboard__title">On Time</span>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<div class="oh-card-dashboard__counts">
|
||||
<span class="oh-card-dashboard__sign">
|
||||
{% if on_time < late_come %}
|
||||
<ion-icon name="caret-down-outline"></ion-icon>
|
||||
{% else %}
|
||||
<ion-icon name="caret-up-outline"></ion-icon>
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="oh-card-dashboard__count">{{on_time}}</span>
|
||||
<div class="oh-dashboard__left col-12 col-sm-12 col-md-12 col-lg-12 pb-5">
|
||||
<div class="oh-dashboard row">
|
||||
<div class="oh-dashboard__cards row">
|
||||
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
|
||||
<div class="oh-card-dashboard oh-card-dashboard--neutral">
|
||||
<div class="oh-card-dashboard__header">
|
||||
<span class="oh-card-dashboard__title"
|
||||
>{% trans "Today's Attendances" %}</span
|
||||
>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<div class="oh-card-dashboard__counts">
|
||||
<span class="oh-card-dashboard__count"
|
||||
>{{marked_attendances_ratio}}%</span
|
||||
>
|
||||
</div>
|
||||
<span class="oh-badge oh-card-dashboard__badge"
|
||||
>{{marked_attendances}}/{{expected_attendances}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="oh-badge oh-card-dashboard__badge"
|
||||
>{{on_time_ratio}}%</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
|
||||
<div class="oh-card-dashboard oh-card-dashboard--neutral">
|
||||
<div class="oh-card-dashboard__header">
|
||||
<span class="oh-card-dashboard__title"
|
||||
>Today's Attendances</span
|
||||
>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<div class="oh-card-dashboard__counts">
|
||||
{% comment %} <span class="oh-card-dashboard__count">100%</span> {% endcomment %}
|
||||
<span class="oh-card-dashboard__count">{{marked_attendances_ratio}}%</span>
|
||||
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard oh-card-dashboard--success"
|
||||
>
|
||||
<div class="oh-card-dashboard__header">
|
||||
<span class="oh-card-dashboard__title"
|
||||
>{% trans "On Time" %}</span
|
||||
>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<div class="oh-card-dashboard__counts">
|
||||
<span class="oh-card-dashboard__sign">
|
||||
{% if on_time < late_come %}
|
||||
<ion-icon
|
||||
name="caret-down-outline"
|
||||
></ion-icon>
|
||||
{% else %}
|
||||
<ion-icon
|
||||
name="caret-up-outline"
|
||||
></ion-icon>
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="oh-card-dashboard__count"
|
||||
>{{on_time}}</span
|
||||
>
|
||||
</div>
|
||||
<span class="oh-badge oh-card-dashboard__badge"
|
||||
>{{on_time_ratio}}%</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="oh-badge oh-card-dashboard__badge"
|
||||
>{{marked_attendances}}/{{expected_attendances}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
|
||||
<div class="oh-card-dashboard oh-card-dashboard--danger">
|
||||
<div class="oh-card-dashboard__header">
|
||||
<span class="oh-card-dashboard__title"
|
||||
>Late Come</span
|
||||
>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<div class="oh-card-dashboard__counts">
|
||||
<span class="oh-card-dashboard__sign">
|
||||
{% if late_come < on_time %}
|
||||
<ion-icon name="caret-down-outline"></ion-icon>
|
||||
{% else %}
|
||||
<ion-icon name="caret-up-outline"></ion-icon>
|
||||
{% endif %}
|
||||
|
||||
</span>
|
||||
<span class="oh-card-dashboard__count">{{late_come}}</span>
|
||||
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
|
||||
<div class="oh-card-dashboard oh-card-dashboard--danger">
|
||||
<div class="oh-card-dashboard__header">
|
||||
<span class="oh-card-dashboard__title"
|
||||
>{% trans "Late Come" %}</span
|
||||
>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<div class="oh-card-dashboard__counts">
|
||||
<span class="oh-card-dashboard__sign">
|
||||
{% if late_come < on_time %}
|
||||
<ion-icon
|
||||
name="caret-down-outline"
|
||||
></ion-icon>
|
||||
{% else %}
|
||||
<ion-icon
|
||||
name="caret-up-outline"
|
||||
></ion-icon>
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="oh-card-dashboard__count"
|
||||
>{{late_come}}</span
|
||||
>
|
||||
</div>
|
||||
<span class="oh-badge oh-card-dashboard__badge"
|
||||
>{{late_come_ratio}}%</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="oh-badge oh-card-dashboard__badge"
|
||||
>{{late_come_ratio}}%</span
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-dashboard row pt-3">
|
||||
<div class="oh-dashboard__movable-cards row mt-4">
|
||||
<div
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-4 "
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oh-dashboard__movable-cards row mt-4">
|
||||
<div class="col-12 col-sm-12 col-md-12 col-lg-6 oh-card-dashboard--moveable">
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent"
|
||||
>
|
||||
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider">
|
||||
<span class="oh-card-dashboard__title">Day Analytic</span>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<canvas id="dailyAnalytic"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-12 col-md-12 col-lg-6 oh-card-dashboard--moveable">
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent"
|
||||
>
|
||||
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider">
|
||||
<span class="oh-card-dashboard__title">Overall Conversions</span>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<canvas id="myChart2"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-dashboard__right col-12 col-sm-12 col-md-12 col-lg-3">
|
||||
<div class="oh-dashboard__events">
|
||||
<div class="oh-dashbaord__events-reel">
|
||||
<div class="oh-dashboard__event oh-dashboard__event--purple">
|
||||
<div class="oh-dasboard__event-photo">
|
||||
<img src="/static/images/upload/userphoto.png" class="oh-dashboard__event-userphoto"/>
|
||||
</div>
|
||||
<div class="oh-dasboard__event-details">
|
||||
<span class="oh-dashboard__event-title">Birthday</span>
|
||||
<span class="oh-dashboard__event-main">Katie Melua</span>
|
||||
<span class="oh-dashboard__event-date">29/03/2023</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oh-dashboard__event oh-dashboard__event--crimson">
|
||||
<div class="oh-dasboard__event-photo">
|
||||
<img src="/static/images/upload/userphoto.png" class="oh-dashboard__event-userphoto"/>
|
||||
</div>
|
||||
|
||||
<div class="oh-dasboard__event-details">
|
||||
<span class="oh-dashboard__event-title">Birthday</span>
|
||||
<span class="oh-dashboard__event-main">Katie Melua</span>
|
||||
<span class="oh-dashboard__event-date">29/03/2023</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oh-dashboard__event oh-dashboard__event--purple">
|
||||
<div class="oh-dasboard__event-photo">
|
||||
<img src="/static/images/upload/userphoto.png" class="oh-dashboard__event-userphoto"/>
|
||||
</div>
|
||||
<div class="oh-dasboard__event-details">
|
||||
<span class="oh-dashboard__event-title">Birthday</span>
|
||||
<span class="oh-dashboard__event-main">Katie Melua</span>
|
||||
<span class="oh-dashboard__event-date">29/03/2023</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="oh-dashboard__events-nav">
|
||||
<li class="oh-dashboard__events-nav-item oh-dashboard__events-nav-item--active" data-target="0"></li>
|
||||
<li class="oh-dashboard__events-nav-item" data-target="1"></li>
|
||||
<li class="oh-dashboard__events-nav-item" data-target="2"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent mt-3">
|
||||
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider">
|
||||
<span class="oh-card-dashboard__title">On Break</span>
|
||||
</div>
|
||||
|
||||
<div class="oh-card-dashboard__body">
|
||||
<ul class="oh-card-dashboard__user-list">
|
||||
{% for emp in on_break %}
|
||||
<li class="oh-card-dashboard__user-item">
|
||||
<div class="oh-profile oh-profile--md">
|
||||
<div class="oh-profile__avatar mr-1">
|
||||
<img
|
||||
src="https://ui-avatars.com/api/?name={{emp.employee_id}}&background=random"
|
||||
class="oh-profile__image"
|
||||
alt="Amy Winehouse"
|
||||
/>
|
||||
</div>
|
||||
<span class="oh-profile__name oh-text--dark"
|
||||
>{{emp.employee_id}}</span
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard__header oh-card-dashboard__header--divider" style="margin-bottom:2.25rem;"
|
||||
id="attendance_header"
|
||||
>
|
||||
<div class="oh-card-dashboard__title mb-2">
|
||||
{% trans "Attendance Analytic" %}
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<select
|
||||
id="type"
|
||||
class="oh-select"
|
||||
name="type"
|
||||
onchange="changeView(this)"
|
||||
style="
|
||||
width: 150px;
|
||||
padding: 3px;
|
||||
color: #5e5c5c;
|
||||
"
|
||||
>
|
||||
<option value="day">
|
||||
{% trans "Day" %}
|
||||
</option>
|
||||
<option value="weekly">
|
||||
{% trans "Weekly" %}
|
||||
</option>
|
||||
<option value="monthly">
|
||||
{% trans "Monthly" %}
|
||||
</option>
|
||||
<option value="date_range">
|
||||
{% trans "Date range" %}
|
||||
</option>
|
||||
</select>
|
||||
<span id="day_input">
|
||||
<input
|
||||
type="date"
|
||||
class="mb-2 float-end pointer oh-select"
|
||||
id="attendance_month"
|
||||
onchange="changeMonth()"
|
||||
style="width: 100px; color: #5e5c5c"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<canvas
|
||||
id="dailyAnalytic"
|
||||
style="cursor: pointer"
|
||||
></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-12 col-sm-12 col-md-12 col-lg-4"
|
||||
id="notInYetId"
|
||||
hx-get="{% url "not-in-yet" %}" hx-trigger="load" >
|
||||
{% include "dashboard/not_in_yet.html" %}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-4 "
|
||||
id="pendingHours"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard__header oh-card-dashboard__header--divider"
|
||||
id="pendingHoursHeader"
|
||||
>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="oh-card-dashboard__title mb-2">
|
||||
{% trans "Hours Chart" %}
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<input
|
||||
type="month"
|
||||
class="mb-2 float-end pointer oh-select"
|
||||
id="hourAccountMonth"
|
||||
onchange="dynamicMonth($(this))"
|
||||
style="width: 100px; color: #5e5c5c"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body" style="height:275px">
|
||||
<canvas
|
||||
id="pendingHoursCanvas"
|
||||
style="cursor: pointer"
|
||||
></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oh-dashboard row pt-3">
|
||||
<div
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-3 "
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard__header oh-card-dashboard__header--divider"
|
||||
>
|
||||
<span class="oh-card-dashboard__title"
|
||||
>{% trans "On Break" %}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="oh-card-dashboard__body" style="height:400px">
|
||||
{% if on_break %}
|
||||
<ul class="oh-card-dashboard__user-list">
|
||||
{% for emp in on_break %}
|
||||
<li class="oh-card-dashboard__user-item">
|
||||
<div class="oh-profile oh-profile--md">
|
||||
<div class="oh-profile__avatar mr-1">
|
||||
<img
|
||||
src="https://ui-avatars.com/api/?name={{emp.employee_id}}&background=random"
|
||||
class="oh-profile__image"
|
||||
alt="Amy Winehouse"
|
||||
/>
|
||||
</div>
|
||||
<span class="oh-profile__name oh-text--dark"
|
||||
>{{emp.employee_id}}</span
|
||||
>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
|
||||
<div style="" class="">
|
||||
<img style=" display: block;width: 70px;margin: 20px auto ;" src="/static/images/ui/coffee-break.png" class="" alt=""/>
|
||||
<h3 style="font-size:16px" class="oh-404__subtitle">{% trans "No employees on Break...." %}</h3>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-9"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard__header oh-card-dashboard__header--divider"
|
||||
>
|
||||
<span class="oh-card-dashboard__title"
|
||||
>{% trans "Overtime to validate" %}</span
|
||||
>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body" style="height:400px">
|
||||
{% include "attendance/dashboard/overtime_table.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-dashboard row pt-3">
|
||||
|
||||
<div
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-8" id="validateTableCard"
|
||||
>
|
||||
{% include "attendance/dashboard/to_validate_table.html" %}
|
||||
</div>
|
||||
<div
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-4 "
|
||||
id="departmentChartCard"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard__header oh-card-dashboard__header--divider"
|
||||
id="departmentChartHeader"
|
||||
>
|
||||
<div class="oh-card-dashboard__title mb-2">
|
||||
{% trans "Department overtime Chart" %}
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<select
|
||||
id="department_date_type"
|
||||
class="oh-select"
|
||||
name="type"
|
||||
style="
|
||||
width: 150px;
|
||||
padding: 3px;
|
||||
color: #5e5c5c;
|
||||
"
|
||||
>
|
||||
<option value="day">
|
||||
{% trans "Day" %}
|
||||
</option>
|
||||
<option value="weekly">
|
||||
{% trans "Weekly" %}
|
||||
</option>
|
||||
<option value="monthly">
|
||||
{% trans "Monthly" %}
|
||||
</option>
|
||||
<option value="date_range">
|
||||
{% trans "Date range" %}
|
||||
</option>
|
||||
</select>
|
||||
<span id="department_day_input">
|
||||
<input
|
||||
type="date"
|
||||
class="mb-2 float-end pointer oh-select"
|
||||
id="department_month"
|
||||
style="width: 100px; color: #5e5c5c"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body" style="height:400px">
|
||||
<canvas
|
||||
id="departmentOverChart"
|
||||
style="cursor: pointer"
|
||||
></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
<script src="{% static 'dashboard/attendanceChart.js' %}"></script>
|
||||
<script src="{% static 'dashboard/attendanceChart.js' %}"></script>
|
||||
|
||||
{% endblock content %}
|
||||
{% endblock content %}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
{% load i18n %}
|
||||
{% if overtime_attendances %}
|
||||
<div class="oh-sticky-table h-100">
|
||||
<div class="oh-sticky-table__table oh-table--sortable">
|
||||
<div class="oh-sticky-table__thead">
|
||||
<div class="oh-sticky-table__tr">
|
||||
<div
|
||||
class="oh-sticky-table__th"
|
||||
>
|
||||
{% trans "Employee" %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th">{% trans "Check-In" %}</div>
|
||||
<div
|
||||
class="oh-sticky-table__th"
|
||||
>
|
||||
{% trans "In Date" %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th">{% trans "Check-Out" %}</div>
|
||||
<div
|
||||
class="oh-sticky-table__th"
|
||||
>
|
||||
{% trans "Out Date" %}
|
||||
</div>
|
||||
<div
|
||||
class="oh-sticky-table__th"
|
||||
>
|
||||
{% trans "Overtime" %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% for attendance in overtime_attendances %}
|
||||
<div class="oh-sticky-table__tbody">
|
||||
<div
|
||||
class="oh-sticky-table__tr"
|
||||
draggable="false"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#detailAttendance"
|
||||
hx-target="#detailAttendanceModalBody"
|
||||
hx-get="{% url 'user-request-one-view' attendance.id %}?ot=true&instances_ids={{ot_attendances_ids}}"
|
||||
>
|
||||
<div class="oh-sticky-table__sd">
|
||||
<div class="oh-profile oh-profile--md">
|
||||
<div class="oh-profile__avatar mr-1">
|
||||
<img
|
||||
src="{{attendance.employee_id.get_avatar}}"
|
||||
class="oh-profile__image"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<span class="oh-profile__name oh-text--dark"
|
||||
>{{attendance.employee_id}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-sticky-table__td timeformat_changer">
|
||||
{{attendance.attendance_clock_in}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td dateformat_changer">
|
||||
{{attendance.attendance_clock_in_date}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td timeformat_changer">
|
||||
{{attendance.attendance_clock_out}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td dateformat_changer">
|
||||
{{attendance.attendance_clock_out_date}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td">
|
||||
{{attendance.attendance_overtime}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td">
|
||||
<a
|
||||
href="{% url 'approve-overtime' attendance.id %}"
|
||||
onclick="event.stopPropagation()"
|
||||
class="oh-btn oh-btn--info"
|
||||
>
|
||||
{% trans "Approve" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
|
||||
<div style="" class="">
|
||||
<img style="display: block;width: 70px;margin: 20px auto ;" src="/static/images/ui/overtime-icon.png" class="" alt=""/>
|
||||
<h3 style="font-size:16px" class="oh-404__subtitle">{% trans "No Overtime to Validate...." %}</h3>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
142
attendance/templates/attendance/dashboard/to_validate_table.html
Normal file
142
attendance/templates/attendance/dashboard/to_validate_table.html
Normal file
@@ -0,0 +1,142 @@
|
||||
{% load i18n %}
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard__header oh-card-dashboard__header--divider"
|
||||
>
|
||||
{% if validate_attendances.has_previous %}
|
||||
<span class="oh-card-dashboard__title" id="department-previous" style="cursor: pointer;" hx-target="#validateTableCard"
|
||||
hx-get="{% url 'attendance-validate-table' %}?{{pd}}&page={{ validate_attendances.previous_page_number }}">
|
||||
<ion-icon name="caret-back-outline" role="img" class="md hydrated" aria-label="caret back outline"></ion-icon>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
<span class="oh-card-dashboard__title">{% trans 'Attendance to validate' %} </span>
|
||||
|
||||
{% if validate_attendances.has_next %}
|
||||
<span class="oh-card-dashboard__title float-end" id="department-previous" style="cursor: pointer;" hx-target="#validateTableCard"
|
||||
hx-get="{% url 'attendance-validate-table' %}?{{pd}}&page={{ validate_attendances.next_page_number }}">
|
||||
<ion-icon name="caret-forward-outline" role="img" class="md hydrated" aria-label="caret back outline"></ion-icon>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body" style="height:450px">
|
||||
{% if validate_attendances %}
|
||||
<div class="oh-sticky-table h-100">
|
||||
<div class="oh-sticky-table__table oh-table--sortable">
|
||||
<div class="oh-sticky-table__thead">
|
||||
<div class="oh-sticky-table__tr">
|
||||
<div
|
||||
class="oh-sticky-table__th"
|
||||
>
|
||||
{% trans "Employee" %}
|
||||
</div>
|
||||
<div
|
||||
class="oh-sticky-table__th"
|
||||
>
|
||||
{% trans "Date" %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th">{% trans "Check-In" %}</div>
|
||||
<div
|
||||
class="oh-sticky-table__th"
|
||||
>
|
||||
{% trans "In Date" %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th">{% trans "Check-Out" %}</div>
|
||||
<div
|
||||
class="oh-sticky-table__th"
|
||||
>
|
||||
{% trans "Out Date" %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th">{% trans "Shift" %}</div>
|
||||
<div class="oh-sticky-table__th">{% trans "Work Type" %}</div>
|
||||
<div class="oh-sticky-table__th">{% trans "Min Hour" %}</div>
|
||||
<div
|
||||
class="oh-sticky-table__th"
|
||||
>
|
||||
{% trans "At Work" %}
|
||||
</div>
|
||||
<div
|
||||
class="oh-sticky-table__th"
|
||||
>
|
||||
{% trans "Pending Hour" %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th oh-sticky-table__right"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% for attendance in validate_attendances %}
|
||||
<div class="oh-sticky-table__tbody">
|
||||
<div
|
||||
class="oh-sticky-table__tr"
|
||||
draggable="false"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#detailAttendance"
|
||||
hx-target="#detailAttendanceModalBody"
|
||||
hx-get="{% url 'user-request-one-view' attendance.id %}?validate=true&instances_ids={{validate_attendances_ids}}"
|
||||
>
|
||||
|
||||
<div class="oh-sticky-table__sd">
|
||||
<div class="oh-profile oh-profile--md">
|
||||
<div class="oh-profile__avatar mr-1">
|
||||
<img
|
||||
src="{{attendance.employee_id.get_avatar}}"
|
||||
class="oh-profile__image"
|
||||
alt="Mary Magdalene"
|
||||
/>
|
||||
</div>
|
||||
<span class="oh-profile__name oh-text--dark"
|
||||
>{{attendance.employee_id}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-sticky-table__td dateformat_changer">{{attendance.attendance_date}}</div>
|
||||
<div class="oh-sticky-table__td timeformat_changer">
|
||||
{{attendance.attendance_clock_in}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td dateformat_changer">
|
||||
{{attendance.attendance_clock_in_date}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td timeformat_changer">
|
||||
{{attendance.attendance_clock_out}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td dateformat_changer">
|
||||
{{attendance.attendance_clock_out_date}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td">{{attendance.shift_id}}</div>
|
||||
<div class="oh-sticky-table__td">{{attendance.work_type_id}}</div>
|
||||
<div class="oh-sticky-table__td">{{attendance.minimum_hour}}</div>
|
||||
<div class="oh-sticky-table__td">
|
||||
{{attendance.attendance_worked_hour}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td">{{attendance.hours_pending}}</div>
|
||||
<div class="oh-sticky-table__td oh-sticky-table__right">
|
||||
{% if perms.attendance.change_attendance %}
|
||||
<a
|
||||
href='{% url "validate-this-attendance" attendance.id %}'
|
||||
hx-target="#updateAttendanceBody"
|
||||
data-req="/attendance/request-attendance-view/?id={{attendance.id}}"
|
||||
onclick="event.stopPropagation(); {% if attendance.is_validate_request %} event.preventDefault(); showSweetAlert($(this).data('req')); {% endif %}"
|
||||
class="oh-btn oh-btn--info"
|
||||
>
|
||||
{% trans "Validate" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
|
||||
<div style="" class="">
|
||||
<img style="display: block;width: 70px;margin: 20px auto ;" src="/static/images/ui/attendance-validate.png" class="" alt=""/>
|
||||
<h3 style="font-size:16px" class="oh-404__subtitle">{% trans "All Attendance Validated." %}</h3>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -15,11 +15,14 @@ from attendance.filters import (
|
||||
LateComeEarlyOutFilter,
|
||||
AttendanceOverTimeFilter,
|
||||
)
|
||||
from attendance.models import Attendance, AttendanceLateComeEarlyOut, AttendanceOverTime
|
||||
from attendance.models import Attendance, AttendanceLateComeEarlyOut, AttendanceOverTime, AttendanceValidationCondition
|
||||
from attendance.views.views import strtime_seconds
|
||||
from base.methods import filtersubordinates
|
||||
from base.models import Department, EmployeeShiftSchedule
|
||||
from employee.models import Employee
|
||||
from employee.not_in_out_dashboard import paginator_qry
|
||||
from horilla.decorators import login_required
|
||||
from leave.models import LeaveRequest
|
||||
|
||||
|
||||
def find_on_time(request, today, week_day, department=None):
|
||||
@@ -29,22 +32,12 @@ def find_on_time(request, today, week_day, department=None):
|
||||
|
||||
on_time = 0
|
||||
attendances = Attendance.objects.filter(attendance_date=today)
|
||||
attendances = filtersubordinates(request, attendances, "attendance.view_attendance")
|
||||
if department is not None:
|
||||
attendances = attendances.filter(
|
||||
employee_id__employee_work_info__department_id=department
|
||||
)
|
||||
excepted_attendances = 0
|
||||
for attendance in attendances:
|
||||
shift = attendance.shift_id
|
||||
schedules_today = shift.employeeshiftschedule_set.filter(day__day=week_day)
|
||||
if schedules_today.first() is not None:
|
||||
excepted_attendances = excepted_attendances + 1
|
||||
late_come_obj = attendance.late_come_early_out.filter(
|
||||
type="late_come"
|
||||
).first()
|
||||
if late_come_obj is None:
|
||||
on_time = on_time + 1
|
||||
late_come = AttendanceLateComeEarlyOut.objects.filter(attendance_id__attendance_date=today,type="late_come")
|
||||
on_time = len(attendances) - len(late_come)
|
||||
return on_time
|
||||
|
||||
|
||||
@@ -52,13 +45,9 @@ def find_expected_attendances(week_day):
|
||||
"""
|
||||
This method is used to find count of expected attendances for the week day
|
||||
"""
|
||||
schedules_today = EmployeeShiftSchedule.objects.filter(day__day=week_day)
|
||||
expected_attendances = 0
|
||||
for schedule in schedules_today:
|
||||
shift = schedule.shift_id
|
||||
expected_attendances = expected_attendances + len(
|
||||
shift.employeeworkinformation_set.all()
|
||||
)
|
||||
employees = Employee.objects.filter(is_active=True)
|
||||
on_leave = LeaveRequest.objects.filter(status = "Approved")
|
||||
expected_attendances = len(employees) - len(on_leave)
|
||||
return expected_attendances
|
||||
|
||||
|
||||
@@ -67,6 +56,8 @@ def dashboard(request):
|
||||
"""
|
||||
This method is used to render individual dashboard for attendance module
|
||||
"""
|
||||
page_number = request.GET.get("page")
|
||||
previous_data = request.GET.urlencode()
|
||||
employees = Employee.objects.filter(
|
||||
is_active=True,
|
||||
).filter(~Q(employee_work_info__shift_id=None))
|
||||
@@ -76,7 +67,8 @@ def dashboard(request):
|
||||
week_day = today.strftime("%A").lower()
|
||||
|
||||
on_time = find_on_time(request, today=today, week_day=week_day)
|
||||
late_come_obj = find_late_come(start_date=today)
|
||||
late_come = find_late_come(start_date=today)
|
||||
late_come_obj = len(late_come)
|
||||
|
||||
marked_attendances = late_come_obj + on_time
|
||||
|
||||
@@ -85,15 +77,30 @@ def dashboard(request):
|
||||
late_come_ratio = 0
|
||||
marked_attendances_ratio = 0
|
||||
if expected_attendances != 0:
|
||||
on_time_ratio = f"{(on_time / expected_attendances) * 100:.1f}"
|
||||
late_come_ratio = f"{(late_come_obj / expected_attendances) * 100:.1f}"
|
||||
on_time_ratio = f"{(on_time / expected_attendances) * 100:.2f}"
|
||||
late_come_ratio = f"{(late_come_obj / expected_attendances) * 100:.2f}"
|
||||
marked_attendances_ratio = (
|
||||
f"{(marked_attendances / expected_attendances) * 100:.1f}"
|
||||
f"{(marked_attendances / expected_attendances) * 100:.2f}"
|
||||
)
|
||||
early_outs = AttendanceLateComeEarlyOut.objects.filter(
|
||||
type="early_out", attendance_id__attendance_date=today
|
||||
)
|
||||
|
||||
condition = AttendanceValidationCondition.objects.first()
|
||||
min_ot = strtime_seconds("00:00")
|
||||
if condition is not None and condition.minimum_overtime_to_approve is not None:
|
||||
min_ot = strtime_seconds(condition.minimum_overtime_to_approve)
|
||||
ot_attendances = Attendance.objects.filter(
|
||||
overtime_second__gte=min_ot,
|
||||
attendance_validated=True,
|
||||
employee_id__is_active=True,
|
||||
attendance_overtime_approve =False,
|
||||
)
|
||||
|
||||
validate_attendances = Attendance.objects.filter(
|
||||
attendance_validated=False, employee_id__is_active=True
|
||||
)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"attendance/dashboard/dashboard.html",
|
||||
@@ -107,9 +114,30 @@ def dashboard(request):
|
||||
"marked_attendances": marked_attendances,
|
||||
"marked_attendances_ratio": marked_attendances_ratio,
|
||||
"on_break": early_outs,
|
||||
"overtime_attendances": ot_attendances,
|
||||
"validate_attendances": paginator_qry(validate_attendances, page_number),
|
||||
"pd": previous_data,
|
||||
},
|
||||
)
|
||||
|
||||
@login_required
|
||||
def validated_attendances_table(request):
|
||||
page_number = request.GET.get("page")
|
||||
previous_data = request.GET.urlencode()
|
||||
validate_attendances = Attendance.objects.filter(
|
||||
attendance_validated=False, employee_id__is_active=True
|
||||
)
|
||||
context = {
|
||||
"validate_attendances": paginator_qry(validate_attendances, page_number),
|
||||
"pd": previous_data,
|
||||
}
|
||||
|
||||
return render(
|
||||
request,
|
||||
"attendance/dashboard/to_validate_table.html",
|
||||
context
|
||||
)
|
||||
|
||||
|
||||
def total_attendance(start_date, department, end_date=None):
|
||||
"""
|
||||
@@ -138,6 +166,14 @@ def find_late_come(start_date, department=None, end_date=None):
|
||||
"attendance_date__lte": end_date,
|
||||
}
|
||||
).qs
|
||||
else:
|
||||
late_come_obj = LateComeEarlyOutFilter(
|
||||
{
|
||||
"type": "late_come",
|
||||
"attendance_date__gte": start_date,
|
||||
"attendance_date__lte": end_date,
|
||||
}
|
||||
).qs
|
||||
return late_come_obj
|
||||
|
||||
|
||||
@@ -322,3 +358,79 @@ def pending_hours(request):
|
||||
}
|
||||
|
||||
return JsonResponse({"data": data})
|
||||
|
||||
|
||||
@login_required
|
||||
def department_overtime_chart(request):
|
||||
start_date = request.GET.get("date") if request.GET.get("date") else date.today()
|
||||
chart_type = request.GET.get("type") if request.GET.get("type") else "day"
|
||||
end_date = request.GET.get("end_date") if request.GET.get("end_date") else start_date
|
||||
|
||||
if chart_type == "day":
|
||||
start_date = start_date
|
||||
end_date = start_date
|
||||
if chart_type == "weekly":
|
||||
start_date, end_date = get_week_start_end_dates(start_date)
|
||||
if chart_type == "monthly":
|
||||
start_date, end_date = get_month_start_end_dates(start_date)
|
||||
if chart_type == "date_range":
|
||||
start_date = start_date
|
||||
end_date = end_date
|
||||
|
||||
attendance = total_attendance(
|
||||
start_date=start_date, department=None, end_date=end_date
|
||||
)
|
||||
|
||||
|
||||
condition = AttendanceValidationCondition.objects.first()
|
||||
min_ot = strtime_seconds("00:00")
|
||||
if condition is not None and condition.minimum_overtime_to_approve is not None:
|
||||
min_ot = strtime_seconds(condition.minimum_overtime_to_approve)
|
||||
attendances = attendance.filter(
|
||||
overtime_second__gte=min_ot,
|
||||
attendance_validated=True,
|
||||
employee_id__is_active=True,
|
||||
attendance_overtime_approve =True,
|
||||
)
|
||||
departments = []
|
||||
department_total= []
|
||||
|
||||
for attendance in attendances:
|
||||
departments.append(
|
||||
attendance.employee_id.employee_work_info.department_id.department
|
||||
)
|
||||
departments = list(set(departments))
|
||||
|
||||
for depart in departments:
|
||||
department_total.append({"department": depart, "ot_hours": 0})
|
||||
|
||||
|
||||
for attendance in attendances:
|
||||
if attendance.employee_id.employee_work_info.department_id:
|
||||
department = attendance.employee_id.employee_work_info.department_id.department
|
||||
ot = attendance.approved_overtime_second
|
||||
ot_hrs = ot / 3600
|
||||
for depart in department_total:
|
||||
if depart["department"] == department:
|
||||
depart["ot_hours"] += ot_hrs
|
||||
|
||||
dataset = [
|
||||
{
|
||||
"label": "",
|
||||
"data": [],
|
||||
}
|
||||
]
|
||||
|
||||
for depart_total, depart in zip(department_total, departments):
|
||||
if depart == depart_total["department"]:
|
||||
dataset[0]["data"].append(depart_total["ot_hours"])
|
||||
|
||||
response = {
|
||||
"dataset": dataset,
|
||||
"labels": departments,
|
||||
"department_total": department_total,
|
||||
"message": _("No validated Overtimes were found"),
|
||||
"emptyImageSrc":"/static/images/ui/overtime-icon.png",
|
||||
}
|
||||
|
||||
return JsonResponse(response)
|
||||
@@ -15,6 +15,17 @@ from horilla.decorators import manager_can_enter, login_required
|
||||
from horilla import settings
|
||||
from employee.filters import EmployeeFilter
|
||||
from recruitment.models import RecruitmentMailTemplate
|
||||
from django.core.paginator import Paginator
|
||||
|
||||
|
||||
|
||||
def paginator_qry(qryset, page_number):
|
||||
"""
|
||||
This method is used to paginate query set
|
||||
"""
|
||||
paginator = Paginator(qryset, 20)
|
||||
qryset = paginator.get_page(page_number)
|
||||
return qryset
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -24,12 +35,15 @@ def not_in_yet(request):
|
||||
This context processor wil return the employees, if they not marked the attendance
|
||||
for the day
|
||||
"""
|
||||
page_number = request.GET.get("page")
|
||||
previous_data = request.GET.urlencode()
|
||||
emps = (
|
||||
EmployeeFilter({"not_in_yet": date.today()})
|
||||
.qs.exclude(employee_work_info__isnull=True)
|
||||
.filter(is_active=True)
|
||||
)
|
||||
return render(request, "dashboard/not_in_yet.html", {"employees": emps})
|
||||
|
||||
return render(request, "dashboard/not_in_yet.html", {"employees": paginator_qry(emps, page_number),"pd": previous_data,})
|
||||
|
||||
|
||||
@login_required
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
{% load i18n %}
|
||||
<div class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent">
|
||||
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider mb-0">
|
||||
<span class="oh-card-dashboard__title">{% trans 'Not In Yet' %}</span>
|
||||
{% if employees.has_previous %}
|
||||
|
||||
<span class="oh-card-dashboard__title" id="employee-previous" style="cursor: pointer;" hx-target="#notInYetId"
|
||||
hx-get="{% url 'not-in-yet' %}?{{pd}}&page={{ employees.previous_page_number }}">
|
||||
<ion-icon name="caret-back-outline" role="img" class="md hydrated" aria-label="caret back outline"></ion-icon>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="oh-card-dashboard__title">{% trans 'Not In Yet' %} </span>
|
||||
{% if employees.has_next %}
|
||||
|
||||
<span class="oh-card-dashboard__title float-end" id="employee-previous" style="cursor: pointer;" hx-target="#notInYetId"
|
||||
hx-get="{% url 'not-in-yet' %}?{{pd}}&page={{ employees.next_page_number }}">
|
||||
<ion-icon name="caret-forward-outline" role="img" class="md hydrated" aria-label="caret back outline"></ion-icon>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<div class="oh-sticky-table" style="max-height:320px;">
|
||||
<div class="oh-sticky-table" style="height:320px;">
|
||||
<div class="oh-sticky-table__table oh-table--sortable">
|
||||
<div class="oh-sticky-table__tbody">
|
||||
{% for emp in employees %}
|
||||
|
||||
BIN
static/images/ui/attendance-validate.png
Normal file
BIN
static/images/ui/attendance-validate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
static/images/ui/coffee-break.png
Normal file
BIN
static/images/ui/coffee-break.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
static/images/ui/comment.png
Normal file
BIN
static/images/ui/comment.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
static/images/ui/overtime-icon.png
Normal file
BIN
static/images/ui/overtime-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -51,7 +51,7 @@
|
||||
>
|
||||
</div>
|
||||
<!-- end of back button -->
|
||||
<div class="oh-dashboard row" id="dashboard">
|
||||
<div class="oh-dashboard row" id="dashboard" style="padding-bottom: 3.5rem;">
|
||||
<div class="oh-dashboard__left col-12 col-sm-12 col-md-12 col-lg-9">
|
||||
<div class="oh-dashboard__cards row">
|
||||
{% if perms.employee.view_employee %}
|
||||
@@ -144,7 +144,7 @@
|
||||
{% if perms.candidate.view_employee or request.user|is_reportingmanager %}
|
||||
<div
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-6 oh-card-dashboard--moveable"
|
||||
id="notInYetdd"
|
||||
id="notInYetId"
|
||||
hx-get="{% url "not-in-yet" %}"
|
||||
hx-trigger="load"
|
||||
>
|
||||
@@ -326,7 +326,7 @@
|
||||
style="width: 100px; color: #5e5c5c"
|
||||
/>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body">
|
||||
<div class="oh-card-dashboard__body" style="height:300px">
|
||||
<canvas
|
||||
id="pendingHoursCanvas"
|
||||
style="cursor: pointer"
|
||||
@@ -456,6 +456,57 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.base.change_shiftrequest or request.user|is_reportingmanager %}
|
||||
<div
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-6 oh-card-dashboard--moveable" id="shiftRequestApprove"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent" style="height:425px"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard__header oh-card-dashboard__header--divider"
|
||||
>
|
||||
<span class="oh-card-dashboard__title"
|
||||
>{% trans "Shift Request Approve" %}</span
|
||||
>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body"
|
||||
id="shiftRequestApproveBody"
|
||||
hx-get="{% url "dashboard-shift-request" %}"
|
||||
hx-trigger="load"
|
||||
style="height:80%"
|
||||
>
|
||||
{% include "request_and_approve/shift_request.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if perms.base.change_shiftrequest or request.user|is_reportingmanager %}
|
||||
<div
|
||||
class="col-12 col-sm-12 col-md-12 col-lg-6 oh-card-dashboard--moveable" id="WorkTypeRequestApprove"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent" style="height:425px"
|
||||
>
|
||||
<div
|
||||
class="oh-card-dashboard__header oh-card-dashboard__header--divider"
|
||||
>
|
||||
<span class="oh-card-dashboard__title"
|
||||
>{% trans "Work Type Request Approve" %}</span
|
||||
>
|
||||
</div>
|
||||
<div class="oh-card-dashboard__body"
|
||||
id="WorkTypeRequestApproveBody"
|
||||
hx-get="{% url "dashboard-work-type-request" %}"
|
||||
hx-trigger="load"
|
||||
style="height:80%"
|
||||
>
|
||||
{% include "request_and_approve/work_type_request.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -539,6 +590,32 @@
|
||||
<div class="oh-modal__dialog-body" id="mail-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="oh-modal"
|
||||
id="shiftRequestDetailModal"
|
||||
role="dialog"
|
||||
aria-labelledby="shiftRequestDetailModal"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="oh-modal__dialog oh-modal__dialog--timeoff oh-timeoff-modal">
|
||||
<div class="oh-modal__dialog-header">
|
||||
<h2
|
||||
class="oh-modal__dialog-title"
|
||||
id=""
|
||||
>
|
||||
{% trans "Details" %}
|
||||
</h2>
|
||||
<button class="oh-modal__close" aria-label="Close">
|
||||
<ion-icon name="close-outline"></ion-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="oh-modal__dialog-body oh-modal__dialog-relative"
|
||||
id="shiftRequestDetailTarget"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
|
||||
@@ -399,6 +399,11 @@
|
||||
>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="oh-sidebar__submenu-item">
|
||||
<a href="{%url 'view-reimbursement' %}" class="oh-sidebar__submenu-link"
|
||||
>{% trans "Encashments & Reimbursements" %}</a
|
||||
>
|
||||
</li>
|
||||
{% if perms.payroll.view_filingstatus %}
|
||||
<li class="oh-sidebar__submenu-item">
|
||||
<a
|
||||
@@ -440,6 +445,15 @@
|
||||
style="display: none"
|
||||
>
|
||||
<ul class="oh-sidebar__submenu-items">
|
||||
{% if perms.attendance.view_attendance or request.user|is_reportingmanager %}
|
||||
<li class="oh-sidebar__submenu-item">
|
||||
<a
|
||||
class="oh-sidebar__submenu-link"
|
||||
href="{% url 'attendance-dashboard' %}"
|
||||
>{% trans "Dashboard" %}</a
|
||||
>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.attendance.view_attendance or request.user|is_reportingmanager %}
|
||||
<li class="oh-sidebar__submenu-item">
|
||||
<a
|
||||
@@ -561,7 +575,7 @@
|
||||
>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.leave.view_leaverequest or request.user|is_reportingmanager %}
|
||||
{% if perms.leave.view_leaverequest or request.user|is_reportingmanager or request.user|is_leave_approval_manager %}
|
||||
<li class="oh-sidebar__submenu-item">
|
||||
<a
|
||||
href="{% url 'request-view' %}"
|
||||
@@ -721,7 +735,7 @@
|
||||
<li
|
||||
class="oh-sidebar__menu-item"
|
||||
x-data="{ isOpen: getOpenState('helpDeskNav') }"
|
||||
>
|
||||
>
|
||||
<a
|
||||
class="oh-sidebar__menu-link"
|
||||
data-id="helpDeskNav"
|
||||
@@ -765,6 +779,74 @@
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="oh-sidebar__menu-item"
|
||||
x-data="{ isOpen: getOpenState('configNav') }"
|
||||
>
|
||||
<a
|
||||
class="oh-sidebar__menu-link"
|
||||
data-id="configNav"
|
||||
x-on:click.prevent="isOpen = !isOpen; saveOpenState('configNav', isOpen)"
|
||||
x-bind:class="isOpen ? 'oh-sidebar__menu-link--active' : ''"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<div class="oh-sidebar__menu-icon">
|
||||
<svg
|
||||
fill="#ffffff"
|
||||
height="20"
|
||||
width="24"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 482.568 482.568"
|
||||
xml:space="preserve"
|
||||
stroke="#ffffff"
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||
<g
|
||||
id="SVGRepo_tracerCarrier"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d="M116.993,203.218c13.4-1.8,26.8,2.8,36.3,12.3l24,24l22.7-22.6l-32.8-32.7c-5.1-5.1-5.1-13.4,0-18.5s13.4-5.1,18.5,0 l32.8,32.8l22.7-22.6l-24.1-24.1c-9.5-9.5-14.1-23-12.3-36.3c4-30.4-5.7-62.2-29-85.6c-23.8-23.8-56.4-33.4-87.3-28.8 c-4.9,0.7-6.9,6.8-3.4,10.3l30.9,30.9c14.7,14.7,14.7,38.5,0,53.1l-19,19c-14.7,14.7-38.5,14.7-53.1,0l-31-30.9 c-3.5-3.5-9.5-1.5-10.3,3.4c-4.6,30.9,5,63.5,28.8,87.3C54.793,197.518,86.593,207.218,116.993,203.218z"
|
||||
></path>
|
||||
<path
|
||||
d="M309.193,243.918l-22.7,22.6l134.8,134.8c5.1,5.1,5.1,13.4,0,18.5s-13.4,5.1-18.5,0l-134.8-134.8l-22.7,22.6l138.9,138.9 c17.6,17.6,46.1,17.5,63.7-0.1s17.6-46.1,0.1-63.7L309.193,243.918z"
|
||||
></path>
|
||||
<path
|
||||
d="M361.293,153.918h59.9l59.9-119.7l-29.9-29.9l-119.8,59.8v59.9l-162.8,162.3l-29.3-29.2l-118,118 c-24.6,24.6-24.6,64.4,0,89s64.4,24.6,89,0l118-118l-29.9-29.9L361.293,153.918z"
|
||||
></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<span>{% trans "Configuration" %}</span>
|
||||
</a>
|
||||
<div
|
||||
class="oh-sidebar__submenu"
|
||||
id="configNav"
|
||||
style="display: none"
|
||||
x-show="isOpen"
|
||||
x-transition
|
||||
style="display: none"
|
||||
>
|
||||
<ul class="oh-sidebar__submenu-items">
|
||||
<li class="oh-sidebar__submenu-item">
|
||||
<a
|
||||
href="{% url 'multiple-approval-condition' %}"
|
||||
class="oh-sidebar__submenu-link"
|
||||
>{% trans "Multiple Approvals " %}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user