[UPDT] ATTENDANCE: Updated attendance activity page by adding option to import attendance activities in excel format

This commit is contained in:
Horilla
2024-08-09 11:20:28 +05:30
parent 83e7ec5e5d
commit 765f94bf17
13 changed files with 1198 additions and 1013 deletions

View File

@@ -5,8 +5,9 @@ This module is used write custom methods
"""
import calendar
from datetime import datetime, timedelta
from datetime import datetime, time, timedelta
import pandas as pd
from django.core.exceptions import ValidationError
from django.core.paginator import Paginator
from django.db import models
@@ -16,6 +17,7 @@ from django.utils.translation import gettext_lazy as _
from base.methods import get_pagination
from base.models import WEEK_DAYS, CompanyLeaves, Holidays
from employee.models import Employee
MONTH_MAPPING = {
"january": 1,
@@ -474,3 +476,112 @@ def validate_time_in_minutes(value):
raise ValidationError(_("Invalid time, excepted MM:SS"))
except ValueError as e:
raise ValidationError(_("Invalid format, excepted MM:SS")) from e
class Request:
"""
Represents a request for clock-in or clock-out.
Attributes:
- user: The user associated with the request.
- date: The date of the request.
- time: The time of the request.
- path: The path associated with the request (default: "/").
- session: The session data associated with the request (default: {"title": None}).
"""
def __init__(
self,
user,
date,
time,
datetime,
) -> None:
self.user = user
self.path = "/"
self.session = {"title": None}
self.date = date
self.time = time
self.datetime = datetime
self.META = META()
class META:
"""
Provides access to HTTP metadata keys.
"""
@classmethod
def keys(cls):
"""
Retrieve the list of available HTTP metadata keys.
Returns:
list: A list of HTTP metadata keys.
"""
return ["HTTP_HX_REQUEST"]
def parse_time(time_str):
time_formats = {
"hh:mm A": "%I:%M %p", # 12-hour format
"HH:mm": "%H:%M", # 24-hour format
}
if isinstance(time_str, time): # Check if it's already a time object
return time_str
if isinstance(time_str, str):
for format_str in time_formats.values():
try:
return datetime.strptime(time_str, format_str).time()
except ValueError:
continue
return None
def parse_date(date_str, error_key, activity):
try:
return pd.to_datetime(date_str).date()
except (pd.errors.ParserError, ValueError):
activity[error_key] = f"Invalid date format for {error_key.split()[-1]}"
return None
def get_date(date):
date_formats = {
"DD-MM-YYYY": "%d-%m-%Y",
"DD.MM.YYYY": "%d.%m.%Y",
"DD/MM/YYYY": "%d/%m/%Y",
"MM/DD/YYYY": "%m/%d/%Y",
"YYYY-MM-DD": "%Y-%m-%d",
"YYYY/MM/DD": "%Y/%m/%d",
"MMMM D, YYYY": "%B %d, %Y",
"DD MMMM, YYYY": "%d %B, %Y",
"MMM. D, YYYY": "%b. %d, %Y",
"D MMM. YYYY": "%d %b. %Y",
"dddd, MMMM D, YYYY": "%A, %B %d, %Y",
}
if isinstance(date, datetime):
return date
elif isinstance(date, str):
for format_name, format_str in date_formats.items():
try:
return datetime.strptime(date, format_str)
except ValueError:
continue
return None
def sort_activity_dicts(activity_dicts):
for activity in activity_dicts:
activity["Attendance Date"] = get_date(activity["Attendance Date"])
# Filter out any entries where the date could not be parsed
activity_dicts = [
activity
for activity in activity_dicts
if activity["Attendance Date"] is not None
]
sorted_activity_dicts = sorted(activity_dicts, key=lambda x: x["Attendance Date"])
return sorted_activity_dicts

View File

@@ -2,7 +2,7 @@ import datetime
from datetime import datetime
from apscheduler.schedulers.background import BackgroundScheduler
from django.utils import timezone
from django.utils import timezone as django_timezone
def auto_check_out():
@@ -13,7 +13,7 @@ def auto_check_out():
from employee.models import Employee
try:
today = datetime.now()
today = django_timezone.make_aware(datetime.now())
shift_schedules = EmployeeShiftSchedule.objects.all()
employees = Employee.objects.all()
for employee in employees:
@@ -34,7 +34,7 @@ def auto_check_out():
not attendance_activity.clock_out
and shift_schedule.start_time <= today.time()
):
clock_out_attendance_and_activity(
attendance = clock_out_attendance_and_activity(
employee=employee,
date_today=today.date(),
now=today.time().strftime("%H:%M"),

View File

@@ -2,24 +2,20 @@
{% load static %}
{% load i18n %}
<section class="oh-wrapper oh-main__topbar " x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{% trans "Attendance Activity" %}</h1>
</div>
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{% trans "Attendance Activity" %}</h1>
</div>
</section>
<div
class="oh-wrapper-main"
>
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
<div class="oh-wrapper">
<div class="oh-404">
<img style=" width: 190px;height: 200px;" src="{% static 'images/ui/present.png' %}" class="oh-404__image mb-4" alt="Page not found. 404."/>
<h5 class="oh-404__subtitle">{% trans "There are no attendance records to display." %}</h5>
</div>
</div>
</main>
<div class="oh-wrapper-main">
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
<div class="oh-wrapper">
<div class="oh-404">
<img style=" width: 190px;height: 200px;" src="{% static 'images/ui/present.png' %}"
class="oh-404__image mb-4" alt="Page not found. 404." />
<h5 class="oh-404__subtitle">{% trans "There are no attendance activity records to display." %}</h5>
</div>
</div>
</main>
</div>
</div>
{% endblock %}

View File

@@ -1,117 +1,114 @@
{% load static %} {% load i18n %}
<div class="oh-dropdown__filter-body">
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Work Info" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id.id_for_label}}">{% trans "Employee" %}</label>
{{f.form.employee_id}}
<div class="oh-accordion-header">{% trans "Work Info" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id.id_for_label}}">{% trans "Employee" %}</label>
{{f.form.employee_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__department_id.id_for_label}}">{% trans "Department" %}</label>
{{f.form.employee_id__employee_work_info__department_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__shift_id.id_for_label}}">{% trans "Shift" %}</label>
{{f.form.employee_id__employee_work_info__shift_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__reporting_manager_id.id_for_label}}">{% trans "Reporting Manager" %}</label>
{{f.form.employee_id__employee_work_info__reporting_manager_id}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__company_id.id_for_label}}">{% trans "Company" %}</label>
{{f.form.employee_id__employee_work_info__company_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__job_position_id.id_for_label}}">{% trans "Job Position" %}</label>
{{f.form.employee_id__employee_work_info__job_position_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__work_type_id.id_for_label}}">{% trans "Work Type" %}</label>
{{f.form.employee_id__employee_work_info__work_type_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__location.id_for_label}}">{% trans "Work Location" %}</label>
{{f.form.employee_id__employee_work_info__location}}
</div>
</div>
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__department_id.id_for_label}}">{% trans "Department" %}</label>
{{f.form.employee_id__employee_work_info__department_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__shift_id.id_for_label}}">{% trans "Shift" %}</label>
{{f.form.employee_id__employee_work_info__shift_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__reporting_manager_id.id_for_label}}">{% trans "Reporting Manager" %}</label>
{{f.form.employee_id__employee_work_info__reporting_manager_id}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__company_id.id_for_label}}">{% trans "Company" %}</label>
{{f.form.employee_id__employee_work_info__company_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__job_position_id.id_for_label}}">{% trans "Job Position" %}</label>
{{f.form.employee_id__employee_work_info__job_position_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__work_type_id.id_for_label}}">{% trans "Work Type" %}</label>
{{f.form.employee_id__employee_work_info__work_type_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.employee_id__employee_work_info__location.id_for_label}}">{% trans "Work Location" %}</label>
{{f.form.employee_id__employee_work_info__location}}
</div>
</div>
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Attendance Activity" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.attendance_date.id_for_label}}">{% trans "Attendance Date" %}</label>
{{f.form.attendance_date}}
<div class="oh-accordion-header">{% trans "Attendance Activity" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.attendance_date.id_for_label}}">{% trans "Attendance Date" %}</label>
{{f.form.attendance_date}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.clock_out_date.id_for_label}}">{% trans "Out Date" %}</label>
{{f.form.clock_out_date}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.clock_in_date.id_for_label}}">{% trans "In Date" %}</label>
{{f.form.clock_in_date}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.shift_day.id_for_label}}">{% trans "Shift Day" %}</label>
{{f.form.shift_day}}
</div>
</div>
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.clock_out_date.id_for_label}}">{% trans "Out Date" %}</label>
{{f.form.clock_out_date}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.clock_in_date.id_for_label}}">{% trans "In Date" %}</label>
{{f.form.clock_in_date}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.shift_day.id_for_label}}">{% trans "Shift Day" %}</label>
{{f.form.shift_day}}
</div>
</div>
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Advanced" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.attendance_date_from.id_for_label}}">{% trans "Attendance From" %}</label>
{{f.form.attendance_date_from}}
<div class="oh-accordion-header">{% trans "Advanced" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.attendance_date_from.id_for_label}}">{% trans "Attendance From" %}</label>
{{f.form.attendance_date_from}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.in_from.id_for_label}}">{% trans "In From" %}</label>
{{f.form.in_from}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.out_from.id_for_label}}">{% trans "Out From" %}</label>
{{f.form.out_from}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.attendance_date_till.id_for_label}}">{% trans "Attendance Till" %}</label>
{{f.form.attendance_date_till}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.in_till.id_for_label}}">{% trans "In Till" %}</label>
{{f.form.in_till}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.out_till.id_for_label}}">{% trans "Out Till" %}</label>
{{f.form.out_till}}
</div>
</div>
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.in_from.id_for_label}}">{% trans "In From" %}</label>
{{f.form.in_from}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.out_from.id_for_label}}">{% trans "Out From" %}</label>
{{f.form.out_from}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.attendance_date_till.id_for_label}}">{% trans "Attendance Till" %}</label>
{{f.form.attendance_date_till}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.in_till.id_for_label}}">{% trans "In Till" %}</label>
{{f.form.in_till}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{f.form.out_till.id_for_label}}">{% trans "Out Till" %}</label>
{{f.form.out_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-dropdown__filter-footer">
<button
class="oh-btn oh-btn--secondary oh-btn--small w-100 filterButton"
id="filterSubmit"
>
{% trans "Filter" %}
</div>
<div class="oh-dropdown__filter-footer">
<button class="oh-btn oh-btn--secondary oh-btn--small w-100 filterButton" id="filterSubmit">
{% trans "Filter" %}
</button>
</div>
</div>
<script src="{% static '/base/filter.js' %}"></script>

View File

@@ -1,194 +1,227 @@
{% load i18n %}
{% load static %}
{% include 'filter_tags.html' %}
{% if messages %}
<div class="oh-wrapper">
{% for message in messages %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
<div class="oh-wrapper">
{% for message in messages %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% if data %}
<div class="oh-table_sticky--wrapper">
<div class="oh-sticky-dropdown--header">
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-sticky-dropdown_btn " @click="open = !open"><ion-icon name="ellipsis-vertical-sharp"
role="img" class="md hydrated" aria-label="ellipsis vertical sharp"></ion-icon></button>
<div class="oh-dropdown__menu oh-sticky-table_dropdown" x-show="open" @click.outside="open = false">
<ul class="oh-dropdown__items" id="AttendanceActivityCells">
</ul>
</div>
</div>
</div>
</div>
<div id="attendance-activity-table" data-table-name="attendance_activity_tab">
<div class="oh-sticky-table" >
<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" style="width: 20px;">
<div class="centered-div">
<input type="checkbox" title="{% trans 'Select All' %}"
class="oh-input oh-input__checkbox all-attendance-activity"/>
<!-- start of attendance activity table page -->
<div class="oh-table_sticky--wrapper">
<div class="oh-sticky-dropdown--header">
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-sticky-dropdown_btn " @click="open = !open"><ion-icon name="ellipsis-vertical-sharp"
role="img" class="md hydrated" aria-label="ellipsis vertical sharp"></ion-icon></button>
<div class="oh-dropdown__menu oh-sticky-table_dropdown" x-show="open" @click.outside="open = false">
<ul class="oh-dropdown__items" id="AttendanceActivityCells">
</ul>
</div>
</div>
<div class="oh-sticky-table__th {% if request.sort_option.order == '-employee_id__employee_first_name' %}arrow-up {% elif request.sort_option.order == 'employee_id__employee_first_name' %}arrow-down {% else %}arrow-up-down {% endif %}" hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=employee_id__employee_first_name">{% trans "Employee" %}</div>
<div data-cell-index="1" data-cell-title='{% trans "Attendance Date" %}' class="oh-sticky-table__th {% if request.sort_option.order == '-attendance_date' %}arrow-up {% elif request.sort_option.order == 'attendance_date' %}arrow-down {% else %}arrow-up-down {% endif %}" hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=attendance_date">{% trans "Attendance Date" %}</div>
<div data-cell-index="2" data-cell-title='{% trans "In Date" %}' class="oh-sticky-table__th {% if request.sort_option.order == '-clock_in_date' %}arrow-up {% elif request.sort_option.order == 'clock_in_date' %}arrow-down {% else %}arrow-up-down {% endif %}" hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=clock_in_date">{% trans "In Date" %}</div>
<div data-cell-index="3" data-cell-title='{% trans "Check In" %}' class='oh-sticky-table__th'>{% trans "Check In" %}</div>
<div data-cell-index="4" data-cell-title='{% trans "Check Out" %}' class='oh-sticky-table__th'>{% trans "Check Out" %}</div>
<div data-cell-index="5" data-cell-title='{% trans "Out Date" %}' class="oh-sticky-table__th {% if request.sort_option.order == '-clock_out_date' %}arrow-up {% elif request.sort_option.order == 'clock_out_date' %}arrow-down {% else %}arrow-up-down {% endif %}" hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=clock_out_date">{% trans "Out Date" %}</div>
{% if perms.attendance.delete_attendanceactivity %}
<div class='oh-sticky-table__th oh-sticky-table__right' scope="col" style="width: 86px;">{% trans "Actions" %}</div>
{% endif %}
</div>
</div>
<div class="oh-sticky-table__tbody">
{% for activity in data %}
<div class="oh-sticky-table__tr" draggable="false"
data-toggle="oh-modal-toggle" data-target="#objectDetailsModalW25"
hx-target="#objectDetailsModalW25Target"
hx-get="{% url 'attendance-activity-single-view' activity.id %}?{{pd}}&instances_ids={{activity_ids}}">
<div class="oh-sticky-table__sd" onclick="event.stopPropagation();">
<div class="centered-div">
<input type="checkbox"
id="{{activity.id}}"
onchange="highlightRow($(this))"
class="oh-input attendance-checkbox oh-input__checkbox all-attendance-activity-row" />
</div>
</div>
<div class="oh-sticky-table__td">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img
src="{{activity.employee_id.get_avatar}}"
class="oh-profile__image"
alt=""
/>
</div>
<span class="oh-profile__name oh-text--dark"
>{{activity.employee_id}}</span
>
</div>
</div>
<div data-cell-index="1" class="oh-sticky-table__td dateformat_changer">{{activity.attendance_date}}</div>
<div data-cell-index="2" class="oh-sticky-table__td dateformat_changer">{{activity.clock_in_date}}</div>
<div data-cell-index="3" class="oh-sticky-table__td timeformat_changer">{{activity.clock_in}}</div>
<div data-cell-index="4" class="oh-sticky-table__td timeformat_changer">{{activity.clock_out}}</div>
<div data-cell-index="5" class="oh-sticky-table__td dateformat_changer">{{activity.clock_out_date}}</div>
{% if perms.attendance.delete_attendanceactivity %}
<div class="oh-sticky-table__td oh-sticky-table__right">
<form hx-confirm="{% trans 'Are you sure want to delete this attendance?' %}"
hx-post="{% url 'attendance-activity-delete' activity.id %}?{{pd}}"
onclick="event.stopPropagation()" hx-target="#activity-table">
{% csrf_token %}
<button type='submit' class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100" title="{% trans 'Remove' %}">
<ion-icon name="trash-outline"></ion-icon>
</button>
</form>
</div>
</div>
<div id="attendance-activity-table" data-table-name="attendance_activity_tab">
<div class="oh-sticky-table">
<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" style="width: 20px;">
<div class="centered-div">
<input type="checkbox" title="{% trans 'Select All' %}"
class="oh-input oh-input__checkbox all-attendance-activity" />
</div>
</div>
<div class="oh-sticky-table__th
{% if request.sort_option.order == '-employee_id__employee_first_name' %}
arrow-up
{% elif request.sort_option.order == 'employee_id__employee_first_name' %}
arrow-down
{% else %}
arrow-up-down
{% endif %}"
hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=employee_id__employee_first_name">
{% trans "Employee" %}
</div>
<div data-cell-index="1" data-cell-title="{% trans 'Attendance Date' %}"
class="oh-sticky-table__th
{% if request.sort_option.order == '-attendance_date' %}
arrow-up
{% elif request.sort_option.order == 'attendance_date' %}
arrow-down
{% else %}
arrow-up-down
{% endif %}"
hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=attendance_date">
{% trans "Attendance Date" %}
</div>
<div data-cell-index="2" data-cell-title="{% trans 'In Date' %}"
class="oh-sticky-table__th
{% if request.sort_option.order == '-clock_in_date' %}
arrow-up
{% elif request.sort_option.order == 'clock_in_date' %}
arrow-down
{% else %}
arrow-up-down
{% endif %}"
hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=clock_in_date">
{% trans "In Date" %}
</div>
<div data-cell-index="3" data-cell-title="{% trans 'Check In' %}" class='oh-sticky-table__th'>{% trans "Check In" %}</div>
<div data-cell-index="4" data-cell-title="{% trans 'Check Out' %}" class='oh-sticky-table__th'>{% trans "Check Out" %}</div>
<div data-cell-index="5" data-cell-title="{% trans 'Out Date' %}"
class="oh-sticky-table__th
{% if request.sort_option.order == '-clock_out_date' %}
arrow-up
{% elif request.sort_option.order == 'clock_out_date' %}
arrow-down
{% else %}
arrow-up-down
{% endif %}"
hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=clock_out_date">
{% trans "Out Date" %}
</div>
{% if perms.attendance.delete_attendanceactivity %}
<div class='oh-sticky-table__th oh-sticky-table__right' scope="col" style="width: 86px;">{% trans "Actions" %}</div>
{% endif %}
</div>
{% endif %}
</div>
<div class="oh-sticky-table__tbody">
{% for activity in data %}
<div class="oh-sticky-table__tr" draggable="false" data-toggle="oh-modal-toggle"
data-target="#objectDetailsModalW25" hx-target="#objectDetailsModalW25Target"
hx-get="{% url 'attendance-activity-single-view' activity.id %}?{{pd}}&instances_ids={{activity_ids}}">
<div class="oh-sticky-table__sd" onclick="event.stopPropagation();">
<div class="centered-div">
<input type="checkbox" id="{{activity.id}}" onchange="highlightRow($(this))"
class="oh-input attendance-checkbox oh-input__checkbox all-attendance-activity-row" />
</div>
</div>
<div class="oh-sticky-table__td">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img src="{{activity.employee_id.get_avatar}}" class="oh-profile__image" alt="" />
</div>
<span class="oh-profile__name oh-text--dark">{{activity.employee_id}}</span>
</div>
</div>
<div data-cell-index="1" class="oh-sticky-table__td dateformat_changer">{{activity.attendance_date}}</div>
<div data-cell-index="2" class="oh-sticky-table__td dateformat_changer">{{activity.clock_in_date}}</div>
<div data-cell-index="3" class="oh-sticky-table__td timeformat_changer">{{activity.clock_in}}</div>
<div data-cell-index="4" class="oh-sticky-table__td timeformat_changer">{{activity.clock_out}}</div>
<div data-cell-index="5" class="oh-sticky-table__td dateformat_changer">{{activity.clock_out_date}}</div>
{% if perms.attendance.delete_attendanceactivity %}
<div class="oh-sticky-table__td oh-sticky-table__right">
<form hx-confirm="{% trans 'Are you sure want to delete this attendance?' %}"
hx-post="{% url 'attendance-activity-delete' activity.id %}?{{pd}}"
onclick="event.stopPropagation()" hx-target="#activity-table">
{% csrf_token %}
<button type='submit' class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
title="{% trans 'Remove' %}">
<ion-icon name="trash-outline"></ion-icon>
</button>
</form>
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input type="number" name="page" class="oh-pagination__input" value="{{data.number}}"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}" hx-target="#activity-table" min="1" />
<span class="oh-pagination__label">{% trans "of" %} {{data.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if data.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page=1"
class="oh-pagination__link" onclick="tickactivityCheckboxes()">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.previous_page_number }}"
class="oh-pagination__link" onclick="tickactivityCheckboxes()">{% trans "Previous" %}</a>
</li>
{% endif %}
{% if data.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.next_page_number }}"
class="oh-pagination__link" onclick="tickactivityCheckboxes()">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.paginator.num_pages }}"
class="oh-pagination__link" onclick="tickactivityCheckboxes()">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
</div>
<div class="oh-pagination">
<span
class="oh-pagination__page"
>
{% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}.
</span
>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{data.number}}"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}"
hx-target="#activity-table"
min="1"
/>
<span class="oh-pagination__label">{% trans "of" %} {{data.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if data.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page=1" class="oh-pagination__link" onclick="tickactivityCheckboxes()">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.previous_page_number }}" class="oh-pagination__link" onclick="tickactivityCheckboxes()">{% trans "Previous" %}</a>
</li>
{% endif %}
{% if data.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.next_page_number }}" class="oh-pagination__link" onclick="tickactivityCheckboxes()">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.paginator.num_pages }}" class="oh-pagination__link" onclick="tickactivityCheckboxes()">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
<!-- end of attendance activity table page -->
{% else %}
<!-- start of empty page -->
<div class="oh-404">
<img
style="width: 150px; height: 150px"
src="{% static 'images/ui/no-results.png' %}"
class="oh-404__image mb-4"
/>
<h5 class="oh-404__subtitle">
{% trans "No search result found!" %}
</h5>
</div>
<!-- end of empty page -->
<!-- start of empty page -->
<div class="oh-404">
<img style="width: 150px; height: 150px" src="{% static 'images/ui/no-results.png' %}" class="oh-404__image mb-4" />
<h5 class="oh-404__subtitle">
{% trans "No search result found!" %}
</h5>
</div>
<!-- end of empty page -->
{% endif %}
<script>
$(document).ready(function () {
$('#selectAllActivity').show();
tickactivityCheckboxes();
$(".all-attendance-activity-row").change(function () {
var parentTable = $(this).closest(".oh-sticky-table");
var body = parentTable.find(".oh-sticky-table__tbody");
var parentCheckbox = parentTable.find(".all-attendance-activity");
parentCheckbox.prop(
"checked",
body.find(".all-attendance-activity-row:checked").length ===
body.find(".all-attendance-activity-row").length
);
addingActivityIds();
});
$(document).ready(function () {
$('#selectAllActivity').show();
tickactivityCheckboxes();
$(".all-attendance-activity-row").change(function () {
var parentTable = $(this).closest(".oh-sticky-table");
var body = parentTable.find(".oh-sticky-table__tbody");
var parentCheckbox = parentTable.find(".all-attendance-activity");
parentCheckbox.prop(
"checked",
body.find(".all-attendance-activity-row:checked").length ===
body.find(".all-attendance-activity-row").length
);
addingActivityIds();
});
$(".all-attendance-activity").change(function () {
addingActivityIds();
});
$(".all-attendance-activity").change(function () {
addingActivityIds();
});
$("#selectAllActivity").click(function () {
selectAllActivity();
});
$("#selectAllActivity").click(function () {
selectAllActivity();
});
$("#unselectAllActivity").click(function () {
unselectAllActivity();
$("#unselectAllActivity").click(function () {
unselectAllActivity();
});
});
});
// toggle columns //
toggleColumns("attendance-activity-table", "AttendanceActivityCells");
localStorageAttendanceActivityCells = localStorage.getItem(
"attendance_activity_tab"
);
if (!localStorageAttendanceActivityCells) {
$("#AttendanceActivityCells").find("[type=checkbox]").prop("checked", true);
}
$("[type=checkbox]").change();
// toggle columns //
toggleColumns("attendance-activity-table", "AttendanceActivityCells");
localStorageAttendanceActivityCells = localStorage.getItem(
"attendance_activity_tab"
);
if (!localStorageAttendanceActivityCells) {
$("#AttendanceActivityCells").find("[type=checkbox]").prop("checked", true);
}
$("[type=checkbox]").change();
</script>

View File

@@ -1,47 +1,27 @@
{% extends 'index.html' %}{% block content %} {% load i18n %}{% load static %}
{% load basefilters %}
<div>
{% include 'attendance/attendance_activity/nav.html' %}
<div
class="oh-checkpoint-badge mb-2"
id="selectedActivity"
data-ids="[]"
data-clicked=""
style="display: none"
>
{% trans "Selected Attendance" %}
</div>
<div class="oh-wrapper">
{% if perms.attendance.change_attendancelatecomeearlyout or request.user|is_reportingmanager %}
<div
class="oh-checkpoint-badge text-success mb-2"
id="selectAllActivity"
style="cursor: pointer"
>
{% trans "Select All Attendance" %}
{% include 'attendance/attendance_activity/nav.html' %}
<div class="oh-checkpoint-badge mb-2" id="selectedActivity" data-ids="[]" data-clicked="" style="display: none">
{% trans "Selected Attendance" %}
</div>
<div
class="oh-checkpoint-badge text-secondary mb-2"
id="unselectAllActivity"
style="cursor: pointer; display: none"
>
{% trans "Unselect All Attendance" %}
<div class="oh-wrapper">
{% if perms.attendance.change_attendancelatecomeearlyout or request.user|is_reportingmanager %}
<div class="oh-checkpoint-badge text-success mb-2" id="selectAllActivity" style="cursor: pointer">
{% trans "Select All Attendance" %}
</div>
<div class="oh-checkpoint-badge text-secondary mb-2" id="unselectAllActivity"
style="cursor: pointer; display: none">
{% trans "Unselect All Attendance" %}
</div>
<div class="oh-checkpoint-badge text-info mb-2" id="exportActivity" style="cursor: pointer; display: none">
{% trans "Export Attendance" %}
</div>
<div class="oh-checkpoint-badge text-danger mb-2" id="selectedShowActivity"></div>
{% endif %}
<div id="activity-table">
{% include 'attendance/attendance_activity/activity_list.html' %}
</div>
</div>
<div
class="oh-checkpoint-badge text-info mb-2"
id="exportActivity"
style="cursor: pointer; display: none"
>
{% trans "Export Attendance" %}
</div>
<div
class="oh-checkpoint-badge text-danger mb-2"
id="selectedShowActivity"
></div>
{% endif %}
<div id="activity-table">
{% include 'attendance/attendance_activity/activity_list.html' %}
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,195 +1,151 @@
{% load static %} {% load i18n %}
<div class="oh-dropdown__filter-body" id="export_attendance_form">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Excel columns" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="select-all-fields">
<input type="checkbox" id="select-all-fields" /> {% trans "Select All" %}
</label>
</div>
</div>
</div>
<div class="row">
{% for field in export_form.selected_fields %}
<div class="col-sm-4 col-md-4 col-lg-4">
<div class="oh-input-group">
<label class="oh-label" for="{{field.id_for_label}}">
{{ field|capfirst }}
</label>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Work Info" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.employee_id.id_for_label}}"
>{% trans "Employee" %}</label
>
{{export.form.employee_id}}
</div>
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.employee_id__employee_work_info__department_id.id_for_label}}"
>{% trans "Department" %}</label
>
{{export.form.employee_id__employee_work_info__department_id}}
</div>
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.employee_id__employee_work_info__shift_id.id_for_label}}"
>{% trans "Shift" %}</label
>
{{export.form.employee_id__employee_work_info__shift_id}}
</div>
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.employee_id__employee_work_info__reporting_manager_id.id_for_label}}"
>{% trans "Reporting Manager" %}</label
>
{{export.form.employee_id__employee_work_info__reporting_manager_id}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.employee_id__employee_work_info__company_id.id_for_label}}"
>{% trans "Company" %}</label
>
{{export.form.employee_id__employee_work_info__company_id}}
</div>
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.employee_id__employee_work_info__job_position_id.id_for_label}}"
>{% trans "Job Position" %}</label
>
{{export.form.employee_id__employee_work_info__job_position_id}}
</div>
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.employee_id__employee_work_info__work_type_id.id_for_label}}"
>{% trans "Work Type" %}</label
>
{{export.form.employee_id__employee_work_info__work_type_id}}
</div>
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.employee_id__employee_work_info__location.id_for_label}}"
>{% trans "Work Location" %}</label
>
{{export.form.employee_id__employee_work_info__location}}
</div>
</div>
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Attendance Activity" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.attendance_date.id_for_label}}"
>{% trans "Attendance Date" %}</label
>
{{export.form.attendance_date}}
</div>
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.clock_out_date.id_for_label}}"
>{% trans "Out Date" %}</label
>
{{export.form.clock_out_date}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.clock_in_date.id_for_label}}"
>{% trans "In Date" %}</label
>
{{export.form.clock_in_date}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.shift_day.id_for_label}}"
>{% trans "Shift Day" %}</label
>
{{export.form.shift_day}}
</div>
</div>
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Advanced" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.attendance_date_from.id_for_label}}"
>{% trans "Attendance From" %}</label
>
{{export.form.attendance_date_from}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.in_from.id_for_label}}"
>{% trans "In From" %}</label
>
{{export.form.in_from}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.out_from.id_for_label}}"
>{% trans "Out From" %}</label
>
{{export.form.out_from}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label
class="oh-label"
for="{{export.form.attendance_date_till.id_for_label}}"
>{% trans "Attendance Till" %}</label
>
{{export.form.attendance_date_till}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.in_till.id_for_label}}"
>{% trans "In Till" %}</label
>
{{export.form.in_till}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.out_till.id_for_label}}"
>{% trans "Out Till" %}</label
>
{{export.form.out_till}}
</div>
</div>
</div>
</div>
</div>
<div class="oh-modal__dialog-header pb-0">
<h2 class="oh-modal__dialog-title" id="attendanceExportLavel">
{% trans "Export Attendance Activities" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body" id="attendanceExportModalBody">
<form action="{%url 'attendance-activity-info-export' %}" method="get"
onsubmit="event.stopPropagation();$(this).parents().find('.oh-modal--show').last().toggleClass('oh-modal--show');"
id="attendanceExportForm" class="oh-profile-section">
{% csrf_token %}
<div class="oh-dropdown__filter-body" id="export_attendance_form">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Excel columns" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="select-all-fields">
<input type="checkbox" id="select-all-fields" /> {% trans "Select All" %}
</label>
</div>
</div>
</div>
<div class="row">
{% for field in export_form.selected_fields %}
<div class="col-sm-4 col-md-4 col-lg-4">
<div class="oh-input-group">
<label class="oh-label" for="{{field.id_for_label}}">
{{ field|capfirst }}
</label>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Work Info" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.employee_id.id_for_label}}">{% trans "Employee" %}</label>
{{export.form.employee_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.employee_id__employee_work_info__department_id.id_for_label}}">{% trans "Department" %}</label>
{{export.form.employee_id__employee_work_info__department_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.employee_id__employee_work_info__shift_id.id_for_label}}">{% trans "Shift" %}</label>
{{export.form.employee_id__employee_work_info__shift_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.employee_id__employee_work_info__reporting_manager_id.id_for_label}}">{% trans "Reporting Manager" %}</label>
{{export.form.employee_id__employee_work_info__reporting_manager_id}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.employee_id__employee_work_info__company_id.id_for_label}}">{% trans "Company" %}</label>
{{export.form.employee_id__employee_work_info__company_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.employee_id__employee_work_info__job_position_id.id_for_label}}">{% trans "Job Position" %}</label>
{{export.form.employee_id__employee_work_info__job_position_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.employee_id__employee_work_info__work_type_id.id_for_label}}">{% trans "Work Type" %}</label>
{{export.form.employee_id__employee_work_info__work_type_id}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.employee_id__employee_work_info__location.id_for_label}}">{% trans "Work Location" %}</label>
{{export.form.employee_id__employee_work_info__location}}
</div>
</div>
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Attendance Activity" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.attendance_date.id_for_label}}">{% trans "Attendance Date" %}</label>
{{export.form.attendance_date}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.clock_out_date.id_for_label}}">{% trans "Out Date" %}</label>
{{export.form.clock_out_date}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.clock_in_date.id_for_label}}">{% trans "In Date" %}</label>
{{export.form.clock_in_date}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.shift_day.id_for_label}}">{% trans "Shift Day" %}</label>
{{export.form.shift_day}}
</div>
</div>
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Advanced" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.attendance_date_from.id_for_label}}">{% trans "Attendance From" %}</label>
{{export.form.attendance_date_from}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.in_from.id_for_label}}">{% trans "In From" %}</label>
{{export.form.in_from}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.out_from.id_for_label}}">{% trans "Out From" %}</label>
{{export.form.out_from}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.attendance_date_till.id_for_label}}">{% trans "Attendance Till" %}</label>
{{export.form.attendance_date_till}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.in_till.id_for_label}}">{% trans "In Till" %}</label>
{{export.form.in_till}}
</div>
<div class="oh-input-group">
<label class="oh-label" for="{{export.form.out_till.id_for_label}}">{% trans "Out Till" %}</label>
{{export.form.out_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-modal__dialog-footer p-0 pt-3">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">{% trans 'Export' %}</button>
</div>
</form>
</div>

View File

@@ -2,268 +2,253 @@
{% load static %}
{% load attendancefilters %}
{% include 'filter_tags.html' %}
{% if messages %}
<div class="oh-wrapper">
{% for message in messages %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
<div class="oh-wrapper">
{% for message in messages %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% if data %}
<div class="oh-card">
{% for attendance_list in data %}
<div class="oh-accordion-meta">
<div class="oh-accordion-meta__item">
<div class="oh-accordion-meta__header" onclick='$(this).toggleClass("oh-accordion-meta__header--show");'>
<span class="oh-accordion-meta__title pt-3 pb-3">
<div class="oh-tabs__input-badge-container">
<span
class="oh-badge oh-badge--secondary oh-badge--small oh-badge--round mr-1"
>
{{attendance_list.list.paginator.count}}
</span>
{{attendance_list.grouper}}
</div>
</span>
</div>
<div class="oh-accordion-meta__body d-none">
<div class="oh-sticky-table oh-sticky-table--no-overflow mb-5">
<div class="oh-sticky-table" >
<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" style="width: 20px;">
<div class="centered-div">
<input type="checkbox" title="{% trans 'Select All' %}"
class="oh-input oh-input__checkbox all-attendance-activity"/>
</div>
<div class="oh-card">
{% for attendance_list in data %}
<div class="oh-accordion-meta">
<div class="oh-accordion-meta__item">
<div class="oh-accordion-meta__header" onclick='$(this).toggleClass("oh-accordion-meta__header--show");'>
<span class="oh-accordion-meta__title pt-3 pb-3">
<div class="oh-tabs__input-badge-container">
<span class="oh-badge oh-badge--secondary oh-badge--small oh-badge--round mr-1">
{{attendance_list.list.paginator.count}}
</span>
{{attendance_list.grouper}}
</div>
</span>
</div>
<div class="oh-accordion-meta__body d-none">
<div class="oh-sticky-table oh-sticky-table--no-overflow mb-5">
<div class="oh-sticky-table">
<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" style="width: 20px;">
<div class="centered-div">
<input type="checkbox" title="{% trans 'Select All' %}"
class="oh-input oh-input__checkbox all-attendance-activity" />
</div>
</div>
<div class="oh-sticky-table__th" hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=employee_id__employee_first_name">
{% trans "Employee" %}
</div>
<div class='oh-sticky-table__th' hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=attendance_date">
{% trans "Attendnace Date" %}
</div>
<div class='oh-sticky-table__th' hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=clock_in_date">
{% trans "In Date" %}
</div>
<div class='oh-sticky-table__th'>{% trans "Check In" %}</div>
<div class='oh-sticky-table__th'>{% trans "Check Out" %}</div>
<div class='oh-sticky-table__th' hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=clock_out_date">
{% trans "Out Date" %}
</div>
{% if perms.attendance.delete_attendanceactivity %}
<div class='oh-sticky-table__th oh-sticky-table__right' scope="col" style="width: 80px;">{% trans "Actions" %}</div>
{% endif %}
</div>
</div>
<div class="oh-sticky-table__tbody">
{% for activity in attendance_list.list %}
<div class="oh-sticky-table__tr" draggable="false" data-toggle="oh-modal-toggle"
data-target="#objectDetailsModalW25" hx-target="#objectDetailsModalW25Target"
hx-get="{% url 'attendance-activity-single-view' activity.id %}?{{pd}}&instances_ids={{activity_ids}}">
<div class="oh-sticky-table__sd" onclick="event.stopPropagation();">
<div class="centered-div">
<input type="checkbox" id="{{activity.id}}"
class="oh-input attendance-checkbox oh-input__checkbox all-attendance-activity-row" />
</div>
</div>
<div class="oh-sticky-table__td">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img src="{{activity.employee_id.get_avatar}}" class="oh-profile__image"
alt="" />
</div>
<span class="oh-profile__name oh-text--dark">{{activity.employee_id}}</span>
</div>
</div>
<div class="oh-sticky-table__td dateformat_changer">{{activity.attendance_date}}
</div>
<div class="oh-sticky-table__td dateformat_changer">{{activity.clock_in_date}}</div>
<div class="oh-sticky-table__td timeformat_changer">{{activity.clock_in}}</div>
<div class="oh-sticky-table__td timeformat_changer">{{activity.clock_out}}</div>
<div class="oh-sticky-table__td dateformat_changer">{{activity.clock_out_date}}
</div>
{% if perms.attendance.delete_attendanceactivity %}
<div class="oh-sticky-table__td oh-sticky-table__right">
<form hx-confirm="{% trans 'Are you sure want to delete this attendance?' %}"
hx-post="{% url 'attendance-activity-delete' activity.id %}?{{pd}}"
onclick="event.stopPropagation()" hx-target="#activity-table">
{% csrf_token %}
<button type='submit' class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100" title="{% trans 'Remove' %}">
<ion-icon name="trash-outline"></ion-icon>
</button>
</form>
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ attendance_list.list.number }} {% trans "of" %} {{ attendance_list.list.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input type="number" name="{{attendance_list.dynamic_name}}"
class="oh-pagination__input" value="{{attendance_list.list.number}}"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}" hx-target="#activity-table"
min="1" />
<span class="oh-pagination__label">{% trans "of" %}
{{attendance_list.list.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if attendance_list.list.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target="#activity-table"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&{{attendance_list.dynamic_name}}=1"
class="oh-pagination__link">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target="#activity-table"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&{{attendance_list.dynamic_name}}={{ attendance_list.list.previous_page_number }}"
class="oh-pagination__link">{% trans "Previous" %}</a>
</li>
{% endif %} {% if attendance_list.list.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target="#activity-table"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&{{attendance_list.dynamic_name}}={{ attendance_list.list.next_page_number }}"
class="oh-pagination__link">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target="#activity-table"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&{{attendance_list.dynamic_name}}={{ attendance_list.list.paginator.num_pages }}"
class="oh-pagination__link">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
</div>
</div>
<div class="oh-sticky-table__th" hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=employee_id__employee_first_name">{% trans "Employee" %}</div>
<div class='oh-sticky-table__th' hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=attendance_date">{% trans "Attendnace Date" %}</div>
<div class='oh-sticky-table__th' hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=clock_in_date">{% trans "In Date" %}</div>
<div class='oh-sticky-table__th'>{% trans "Check In" %}</div>
<div class='oh-sticky-table__th'>{% trans "Check Out" %}</div>
<div class='oh-sticky-table__th' hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&orderby=clock_out_date">{% trans "Out Date" %}</div>
{% if perms.attendance.delete_attendanceactivity %}
<div class='oh-sticky-table__th oh-sticky-table__right' scope="col" style="width: 80px;">{% trans "Actions" %}</div>
{% endif %}
</div>
</div>
<div class="oh-sticky-table__tbody">
{% for activity in attendance_list.list %}
<div class="oh-sticky-table__tr" draggable="false"
data-toggle="oh-modal-toggle" data-target="#objectDetailsModalW25"
hx-target="#objectDetailsModalW25Target"
hx-get="{% url 'attendance-activity-single-view' activity.id %}?{{pd}}&instances_ids={{activity_ids}}">
<div class="oh-sticky-table__sd" onclick="event.stopPropagation();">
<div class="centered-div">
<input type="checkbox" id="{{activity.id}}"
class="oh-input attendance-checkbox oh-input__checkbox all-attendance-activity-row" />
</div>
</div>
<div class="oh-sticky-table__td">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img
src="{{activity.employee_id.get_avatar}}"
class="oh-profile__image"
alt=""
/>
</div>
<span class="oh-profile__name oh-text--dark"
>{{activity.employee_id}}</span
>
</div>
</div>
<div class="oh-sticky-table__td dateformat_changer">{{activity.attendance_date}}</div>
<div class="oh-sticky-table__td dateformat_changer">{{activity.clock_in_date}}</div>
<div class="oh-sticky-table__td timeformat_changer">{{activity.clock_in}}</div>
<div class="oh-sticky-table__td timeformat_changer">{{activity.clock_out}}</div>
<div class="oh-sticky-table__td dateformat_changer">{{activity.clock_out_date}}</div>
{% if perms.attendance.delete_attendanceactivity %}
<div class="oh-sticky-table__td oh-sticky-table__right">
<form hx-confirm="{% trans 'Are you sure want to delete this attendance?' %}"
hx-post="{% url 'attendance-activity-delete' activity.id %}?{{pd}}"
onclick="event.stopPropagation()" hx-target="#activity-table">
{% csrf_token %}
<button type='submit' class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100" title="{% trans 'Remove' %}">
<ion-icon name="trash-outline"></ion-icon>
</button>
</form>
</div>
{% endfor %}
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input type="number" name="page" class="oh-pagination__input" value="{{data.number}}"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}" hx-target="#activity-table" min="1" />
<span class="oh-pagination__label">{% trans "of" %} {{data.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if data.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page=1"
class="oh-pagination__link">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.previous_page_number }}"
class="oh-pagination__link">{% trans "Previous" %}</a>
</li>
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ attendance_list.list.number }} {% trans "of" %} {{ attendance_list.list.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="{{attendance_list.dynamic_name}}"
class="oh-pagination__input"
value="{{attendance_list.list.number}}"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}"
hx-target="#activity-table"
min="1"
/>
<span class="oh-pagination__label"
>{% trans "of" %} {{attendance_list.list.paginator.num_pages}}</span
>
</div>
<ul class="oh-pagination__items">
{% if attendance_list.list.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#activity-table"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&{{attendance_list.dynamic_name}}=1"
class="oh-pagination__link"
>{% trans "First" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#activity-table"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&{{attendance_list.dynamic_name}}={{ attendance_list.list.previous_page_number }}"
class="oh-pagination__link"
>{% trans "Previous" %}</a
>
</li>
{% endif %} {% if attendance_list.list.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#activity-table"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&{{attendance_list.dynamic_name}}={{ attendance_list.list.next_page_number }}"
class="oh-pagination__link"
>{% trans "Next" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#activity-table"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&{{attendance_list.dynamic_name}}={{ attendance_list.list.paginator.num_pages }}"
class="oh-pagination__link"
>{% trans "Last" %}</a
>
</li>
{% endif %}
</ul>
</nav>
{% if data.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.next_page_number }}"
class="oh-pagination__link">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table'
hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.paginator.num_pages }}"
class="oh-pagination__link">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="oh-pagination">
<span
class="oh-pagination__page"
>
{% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}.
</span
>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{data.number}}"
hx-get="{% url 'attendance-activity-search' %}?{{pd}}"
hx-target="#activity-table"
min="1"
/>
<span class="oh-pagination__label">{% trans "of" %} {{data.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if data.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page=1" class="oh-pagination__link">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.previous_page_number }}" class="oh-pagination__link">{% trans "Previous" %}</a>
</li>
{% endif %}
{% if data.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.next_page_number }}" class="oh-pagination__link">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#activity-table' hx-get="{% url 'attendance-activity-search' %}?{{pd}}&page={{ data.paginator.num_pages }}" class="oh-pagination__link">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
{% else %}
<div
class="oh-wrapper-main"
>
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
<div class="oh-wrapper-main">
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
<div class="oh-wrapper">
<div class="oh-404">
<img style=" width: 190px;height: 200px;" src="{% static 'images/ui/present.png' %}" class="oh-404__image mb-4" alt="Page not found. 404."/>
<h5 class="oh-404__subtitle">{% trans "No group result found!" %}</h5>
</div>
</div>
</main>
</div>
<div class="oh-wrapper">
<div class="oh-404">
<img style=" width: 190px;height: 200px;" src="{% static 'images/ui/present.png' %}"
class="oh-404__image mb-4" alt="Page not found. 404." />
<h5 class="oh-404__subtitle">{% trans "No group result found!" %}</h5>
</div>
</div>
</main>
</div>
{% endif %}
<script>
$(".oh-table__sticky-collaspable-sort").click(function (e) {
e.preventDefault();
let clickedEl = $(e.target).closest(".oh-table__toggle-parent");
let targetSelector = clickedEl.data("target");
let toggleBtn = clickedEl.find(".oh-table__toggle-button");
$(`[data-group='${targetSelector}']`).toggleClass(
"oh-table__toggle-child--show"
);
if (toggleBtn) {
toggleBtn.toggleClass("oh-table__toggle-button--show");
}
});
$(document).ready(function () {
{% if data %}
$('#selectAllActivity').show();
{% else %}
$('#selectAllActivity').hide();
{% endif %}
$(".all-attendance-activity-row").change(function () {
var parentTable = $(this).closest(".oh-sticky-table");
var body = parentTable.find(".oh-sticky-table__tbody");
var parentCheckbox = parentTable.find(".all-attendance-activity");
parentCheckbox.prop(
"checked",
body.find(".all-attendance-activity-row:checked").length ===
body.find(".all-attendance-activity-row").length
);
addingActivityIds();
$(".oh-table__sticky-collaspable-sort").click(function (e) {
e.preventDefault();
let clickedEl = $(e.target).closest(".oh-table__toggle-parent");
let targetSelector = clickedEl.data("target");
let toggleBtn = clickedEl.find(".oh-table__toggle-button");
$(`[data-group='${targetSelector}']`).toggleClass(
"oh-table__toggle-child--show"
);
if (toggleBtn) {
toggleBtn.toggleClass("oh-table__toggle-button--show");
}
});
$(document).ready(function () {
{% if data %}
$('#selectAllActivity').show();
{% else %}
$('#selectAllActivity').hide();
{% endif %}
$(".all-attendance-activity-row").change(function () {
var parentTable = $(this).closest(".oh-sticky-table");
var body = parentTable.find(".oh-sticky-table__tbody");
var parentCheckbox = parentTable.find(".all-attendance-activity");
parentCheckbox.prop(
"checked",
body.find(".all-attendance-activity-row:checked").length ===
body.find(".all-attendance-activity-row").length
);
addingActivityIds();
});
$(".all-attendance-activity").change(function () {
addingActivityIds();
});
$(".all-attendance-activity").change(function () {
addingActivityIds();
});
$("#selectAllActivity").click(function () {
selectAllActivity();
});
$("#selectAllActivity").click(function () {
selectAllActivity();
});
$("#unselectAllActivity").click(function () {
unselectAllActivity();
$("#unselectAllActivity").click(function () {
unselectAllActivity();
});
});
});
</script>

View File

@@ -0,0 +1,78 @@
{% load i18n %}
<div class="oh-modal__dialog-header pb-0">
<h2 class="oh-modal__dialog-title" id="activityImportLabel">
{% trans "Import Attendance Activities" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body" id="activityImportModalBody">
<form action="{% url 'attendance-activity-import' %}" enctype="multipart/form-data" method="post"
class="oh-profile-section" id="activityImportForm">
{% csrf_token %}
<div id="uploading" class="oh-modal__dialog-body mr-5" style="display: none;">
<div class="loader-container" style="margin: auto;">
<div class="loader"></div>
<div class="loader-text">{% trans "Uploading..." %}</div>
<div class="text-center" style="-webkit-text-stroke: medium;">
{% trans "Don't refresh the page" %}
</div>
</div>
</div>
<div id="uploadContainer">
<label for="uploadFile" class="oh-dropdown__import-label">
<ion-icon name="cloud-upload" class="oh-dropdown__import-form-icon"></ion-icon>
<span class="oh-dropdown__import-form-title">{% trans "Upload a File" %}</span>
<span class="oh-dropdown__import-form-text">{% trans "Drag and drop files here" %}</span>
</label>
<input type="file" name="activity_import" required />
<div class="d-flex flex-row-reverse">
<button type="submit" class="oh-btn oh-btn--small oh-btn--secondary mt-3">
{% trans "Upload" %}
</button>
</div>
</div>
</form>
</div>
<script>
$(document).ready(function () {
Swal.fire({
text: "{% trans 'Do you want to download the template?' %}",
icon: "question",
showCancelButton: true,
confirmButtonColor: "#008000",
cancelButtonColor: "#d33",
confirmButtonText: "{% trans 'Confirm' %}"
}).then((result) => {
if (result.isConfirmed) {
$.ajax({
type: "GET",
url: "{% url 'attendance-activity-import-excel' %}",
xhrFields: { responseType: "blob" },
success: function (response) {
const url = URL.createObjectURL(new Blob([response], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }));
const link = document.createElement("a");
link.href = url;
link.download = "activity_excel.xlsx";
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(url);
},
error: function () {
console.error("{% trans 'Error downloading file' %}");
}
});
}
});
$('#activityImportForm').on('submit', function () {
$('#uploadContainer').hide();
$('#uploading').show();
});
});
</script>

View File

@@ -1,191 +1,109 @@
{% load i18n %} {% load basefilters %}
<section class="oh-wrapper oh-main__topbar " x-data="{searchShow: false}">
<div
class="oh-modal"
id="attendanceExport"
role="dialog"
aria-labelledby="attendanceExport"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="attendanceExportLavel">
{% trans "Export Attendances" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
<div
class="oh-modal__dialog-body p-0 pt-2"
id="attendanceExportModalBody"
>
<form
action="{%url 'attendance-activity-info-export' %}"
method="get"
onsubmit="event.stopPropagation();$(this).parents().find('.oh-modal--show').last().toggleClass('oh-modal--show');"
id="attendanceExportForm"
>
{% csrf_token %} {% include 'attendance/attendance_activity/export_filter.html'%}
<div class="oh-dropdown__filter-footer">
<button class="oh-btn oh-btn--secondary oh-btn--small w-100">
{% trans "Export" %}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">
<a href="{% url 'attendance-activity-view' %}" class='text-dark'>
{% trans "Attendance Activity" %}
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">
<a href="{% url 'attendance-activity-view' %}" class='text-dark'>
{% trans "Attendance Activity" %}
</a>
</h1>
<a class="oh-main__titlebar-search-toggle" role="button" aria-label="Toggle Search"
@click="searchShow = !searchShow">
<ion-icon name="search-outline" class="oh-main__titlebar-serach-icon"></ion-icon>
</a>
</h1>
<a
class="oh-main__titlebar-search-toggle"
role="button"
aria-label="Toggle Search"
@click="searchShow = !searchShow"
>
<ion-icon
name="search-outline"
class="oh-main__titlebar-serach-icon"
></ion-icon>
</a>
</div>
<form
hx-get='{% url "attendance-activity-search" %}'
id="filterForm"
hx-swap="innerHTML"
hx-target="#activity-table"
class="d-flex"
onsubmit="event.preventDefault()"
>
<div class="oh-main__titlebar oh-main__titlebar--right">
<div
class="oh-input-group oh-input__search-group"
:class="searchShow ? 'oh-input__search-group--show' : ''"
>
<ion-icon
name="search-outline"
class="oh-input-group__icon oh-input-group__icon--left"
></ion-icon>
<input
type="text"
class="oh-input oh-input__icon"
aria-label="Search Input"
id="attendance-search"
name='search'
placeholder="{% trans 'Search' %}"
onkeyup="$('.filterButton')[0].click()"
/>
</div>
<div class="oh-main__titlebar-button-container">
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn ml-2" @click="open = !open" onclick="event.preventDefault()">
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}<div id="filterCount"></div>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4"
x-show="open"
@click.outside="open = false"
style="display: none;"
>
{% include 'attendance/attendance_activity/activity_filters.html' %}
</div>
</div>
</div>
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn ml-2" @click="open = !open" onclick="event.preventDefault()">
<ion-icon name="library-outline" class="mr-1"></ion-icon>{% trans "Group By" %}
<div id="filterCount"></div>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4"
x-show="open"
@click.outside="open = false"
style="display: none"
>
<div class="oh-accordion">
<label for="id_field">{% trans "Group By" %}</label>
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="id_field">{% trans "Field" %}</label>
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<select
class="oh-select mt-1 w-100"
id="id_field"
name="field"
class="select2-selection select2-selection--single"
>
{% for field in gp_fields %}
<option value="{{ field.0 }}">{% trans field.1 %}</option>
{% endfor %}
</select>
</div>
</div>
</div>
</div>
</div>
</div>
{% if perms.attendance.change_attendancelatecomeearlyout or request.user|is_reportingmanager or perms.attendance.delete_attendancelatecomeearlyout %}
<div class="oh-dropdown ml-2" x-data="{open: false}" onclick="event.stopPropagation();event.preventDefault();">
<button
class="oh-btn oh-btn--dropdown"
@click="open = !open"
@click.outside="open = false"
>
{% trans "Actions" %}
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right"
x-show="open"
style="display: none"
>
<ul class="oh-dropdown__items">
{% if perms.attendance.change_attendancelatecomeearlyout or request.user|is_reportingmanager %}
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link"
id="attendance-info-export"
data-toggle="oh-modal-toggle"
data-target="#attendanceExport"
>{% trans "Export" %}</a
>
</li>
{% endif %}
{% if perms.attendance.delete_attendancelatecomeearlyout %}
<li class="oh-dropdown__item">
<a
href="#"
id="attendanceActivityDelete"
class="oh-dropdown__link oh-dropdown__link--danger"
>{% trans "Delete" %}</a
>
</li>
{% endif %}
</ul>
</div>
</div>
{% endif %}
</div>
</form>
<script>
$('#attendance-search').keydown(function (e) {
var val = $(this).val();
$('.pg').attr('hx-vals', `{"search":${val}}`);
});
$(document).ready(function(){
$('#id_field').on('change',function(){
$('.filterButton')[0].click();
})
})
</script>
<form hx-get="{% url 'attendance-activity-search' %}" id="filterForm" hx-swap="innerHTML"
hx-target="#activity-table" class="d-flex" onsubmit="event.preventDefault()">
<div class="oh-main__titlebar oh-main__titlebar--right">
<div class="oh-input-group oh-input__search-group"
:class="searchShow ? 'oh-input__search-group--show' : ''">
<ion-icon name="search-outline" class="oh-input-group__icon oh-input-group__icon--left"></ion-icon>
<input type="text" class="oh-input oh-input__icon" aria-label="Search Input" id="attendance-search"
name='search' placeholder="{% trans 'Search' %}" onkeyup="$('.filterButton')[0].click()" />
</div>
<div class="oh-main__titlebar-button-container">
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn ml-2" @click="open = !open" onclick="event.preventDefault()">
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}<div id="filterCount"></div>
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4" x-show="open"
@click.outside="open = false" style="display: none;">
{% include 'attendance/attendance_activity/activity_filters.html' %}
</div>
</div>
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn ml-2" @click="open = !open" onclick="event.preventDefault()">
<ion-icon name="library-outline" class="mr-1"></ion-icon>{% trans "Group By" %}
<div id="filterCount"></div>
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4" x-show="open"
@click.outside="open = false" style="display: none">
<div class="oh-accordion">
<label for="id_field">{% trans "Group By" %}</label>
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label" for="id_field">{% trans "Field" %}</label>
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<select class="oh-select mt-1 w-100" id="id_field" name="field"
class="select2-selection select2-selection--single">
{% for field in gp_fields %}
<option value="{{ field.0 }}">{% trans field.1 %}</option>
{% endfor %}
</select>
</div>
</div>
</div>
</div>
</div>
</div>
{% if perms.attendance.change_attendancelatecomeearlyout or request.user|is_reportingmanager or perms.attendance.delete_attendancelatecomeearlyout %}
<div class="oh-dropdown ml-2" x-data="{open: false}" onclick="event.stopPropagation();event.preventDefault();">
<button class="oh-btn oh-btn--dropdown" @click="open = !open" @click.outside="open = false">
{% trans "Actions" %}
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right" x-show="open" style="display: none">
<ul class="oh-dropdown__items">
{% if perms.attendance.add_attendanceactivity or request.user|is_reportingmanager %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link" id="activityInfoImport"
data-toggle="oh-modal-toggle" data-target="#objectCreateModal"
hx-get="{% url 'attendance-activity-import' %}" hx-target="#objectCreateModalTarget"
>
{% trans "Import" %}
</a>
</li>
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link" id="activityInfoExport" data-toggle="oh-modal-toggle"
data-target="#objectUpdateModal" hx-get="{% url 'attendance-activity-info-export' %}"
hx-target="#objectUpdateModalTarget">{% trans "Export" %}</a>
</li>
{% endif %}
{% if perms.attendance.delete_attendanceactivity %}
<li class="oh-dropdown__item">
<a href="#" id="attendanceActivityDelete"
class="oh-dropdown__link oh-dropdown__link--danger">{% trans "Delete" %}</a>
</li>
{% endif %}
</ul>
</div>
</div>
{% endif %}
</div>
</div>
</form>
<script>
$('#attendance-search').keydown(function (e) {
var val = $(this).val();
$('.pg').attr('hx-vals', `{"search":${val}}`);
});
$(document).ready(function () {
$('#id_field').on('change', function () {
$('.filterButton')[0].click();
})
})
</script>
</section>

View File

@@ -115,6 +115,16 @@ urlpatterns = [
views.attendance_activity_bulk_delete,
name="attendance-activity-bulk-delete",
),
path(
"attendance-activity-import",
views.attendance_activity_import,
name="attendance-activity-import",
),
path(
"attendance-activity-import-excel",
views.attendance_activity_import_excel,
name="attendance-activity-import-excel",
),
path(
"attendance-activity-info-export",
views.attendance_activity_export,

View File

@@ -4,6 +4,9 @@ clock_in_out.py
This module is used register endpoints to the check-in check-out functionalities
"""
import logging
logger = logging.getLogger(__name__)
from datetime import date, datetime, timedelta
from django.db.models import Q
@@ -145,15 +148,14 @@ def clock_in_attendance_and_activity(
activity.clock_out_date = date_today
activity.save()
AttendanceActivity(
new_activity = AttendanceActivity.objects.create(
employee_id=employee,
attendance_date=attendance_date,
clock_in_date=date_today,
shift_day=day,
clock_in=in_datetime,
in_datetime=in_datetime,
).save()
)
# create attendance if not exist
attendance = Attendance.objects.filter(
employee_id=employee, attendance_date=attendance_date
@@ -248,7 +250,7 @@ def clock_in(request):
)
attendance_date = date_yesterday
day = day_yesterday
clock_in_attendance_and_activity(
attendance = clock_in_attendance_and_activity(
employee=employee,
date_today=date_today,
attendance_date=attendance_date,
@@ -324,6 +326,7 @@ def clock_out_attendance_and_activity(employee, date_today, now, out_datetime=No
attendance_activities = AttendanceActivity.objects.filter(
employee_id=employee,
).order_by("attendance_date", "id")
attendance_activity = None # Initialize attendance_activity
if attendance_activities.filter(clock_out__isnull=True).exists():
attendance_activity = attendance_activities.filter(
@@ -334,34 +337,37 @@ def clock_out_attendance_and_activity(employee, date_today, now, out_datetime=No
attendance_activity.out_datetime = out_datetime
attendance_activity.save()
attendance_activities = attendance_activities.filter(
attendance_date=attendance_activity.attendance_date
)
# Here calculate the total durations between the attendance activities
attendance_activities = attendance_activities.filter(
attendance_date=attendance_activity.attendance_date
)
# Here calculate the total durations between the attendance activities
duration = 0
for attendance_activity in attendance_activities:
in_datetime, out_datetime = activity_datetime(attendance_activity)
difference = out_datetime - in_datetime
days_second = difference.days * 24 * 3600
seconds = difference.seconds
total_seconds = days_second + seconds
duration = duration + total_seconds
duration = format_time(duration)
# update clock out of attendance
attendance = Attendance.objects.filter(employee_id=employee).order_by(
"-attendance_date", "-id"
)[0]
attendance.attendance_clock_out = now + ":00"
attendance.attendance_clock_out_date = date_today
attendance.attendance_worked_hour = duration
# Overtime calculation
attendance.attendance_overtime = overtime_calculation(attendance)
duration = 0
for activity in attendance_activities:
in_datetime, out_datetime = activity_datetime(activity)
difference = out_datetime - in_datetime
days_second = difference.days * 24 * 3600
seconds = difference.seconds
total_seconds = days_second + seconds
duration = duration + total_seconds
duration = format_time(duration)
# update clock out of attendance
attendance = Attendance.objects.filter(employee_id=employee).order_by(
"-attendance_date", "-id"
)[0]
attendance.attendance_clock_out = now + ":00"
attendance.attendance_clock_out_date = date_today
attendance.attendance_worked_hour = duration
# Overtime calculation
attendance.attendance_overtime = overtime_calculation(attendance)
# Validate the attendance as per the condition
attendance.attendance_validated = attendance_validate(attendance)
attendance.save()
# Validate the attendance as per the condition
attendance.attendance_validated = attendance_validate(attendance)
attendance.save()
return attendance
logger.error("No attendance clock in activity found that needs clocking out.")
return
@@ -396,7 +402,12 @@ def early_out(attendance, start_time, end_time, shift):
"""
if not enable_late_come_early_out_tracking(None).get("tracking"):
return
now_sec = strtime_seconds(attendance.attendance_clock_out.strftime("%H:%M"))
clock_out_time = attendance.attendance_clock_out
if isinstance(clock_out_time, str):
clock_out_time = datetime.strptime(clock_out_time, "%H:%M:%S")
now_sec = strtime_seconds(clock_out_time.strftime("%H:%M"))
mid_day_sec = strtime_seconds("12:00")
# Checking gracetime allowance before creating early out
if shift and shift.grace_time_id:
@@ -458,23 +469,18 @@ def clock_out(request):
minimum_hour, start_time_sec, end_time_sec = shift_schedule_today(
day=day, shift=shift
)
clock_out_attendance_and_activity(
attendance = clock_out_attendance_and_activity(
employee=employee, date_today=date_today, now=now, out_datetime=datetime_now
)
attendance = (
Attendance.objects.filter(employee_id=employee)
.order_by("id", "attendance_date")
.last()
)
early_out_instance = attendance.late_come_early_out.filter(type="early_out")
if not early_out_instance.exists():
early_out(
attendance=attendance,
start_time=start_time_sec,
end_time=end_time_sec,
shift=shift,
)
if attendance:
early_out_instance = attendance.late_come_early_out.filter(type="early_out")
if not early_out_instance.exists():
early_out(
attendance=attendance,
start_time=start_time_sec,
end_time=end_time_sec,
shift=shift,
)
script = ""
hidden_label = ""

View File

@@ -11,6 +11,10 @@ This module is part of the recruitment project and is intended to
provide the main entry points for interacting with the application's functionality.
"""
import logging
logger = logging.getLogger(__name__)
import calendar
import contextlib
import io
@@ -28,6 +32,7 @@ from django.forms import ValidationError
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils import timezone as django_timezone
from django.utils.translation import gettext as __
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods
@@ -55,11 +60,15 @@ from attendance.forms import (
LateComeEarlyOutExportForm,
)
from attendance.methods.utils import (
Request,
attendance_day_checking,
format_time,
is_reportingmanger,
monthly_leave_days,
paginator_qry,
parse_date,
parse_time,
sort_activity_dicts,
strtime_seconds,
)
from attendance.models import (
@@ -89,11 +98,8 @@ from base.methods import (
get_key_instances,
)
from base.models import (
WEEK_DAYS,
AttendanceAllowedIP,
CompanyLeaves,
EmployeeShiftSchedule,
Holidays,
TrackLateComeEarlyOut,
)
from employee.filters import EmployeeFilter
@@ -211,6 +217,8 @@ def attendance_create(request):
return render(request, "attendance/attendance/form.html", {"form": form})
@login_required
@permission_required("attendance.add_attendance")
def attendance_excel(_request):
"""
Generate an empty Excel template for attendance data with predefined columns.
@@ -745,7 +753,6 @@ def attendance_activity_view(request):
activity_ids = json.dumps(
[instance.id for instance in paginator_qry(attendance_activities, None)]
)
export_form = AttendanceActivityExportForm()
if attendance_activities.exists():
template = "attendance/attendance_activity/attendance_activity_view.html"
else:
@@ -757,11 +764,7 @@ def attendance_activity_view(request):
"data": paginator_qry(attendance_activities, request.GET.get("page")),
"pd": previous_data,
"f": filter_obj,
"export": AttendanceActivityFilter(
queryset=AttendanceActivity.objects.all()
),
"gp_fields": AttendanceActivityReGroup.fields,
"export_form": export_form,
"activity_ids": activity_ids,
},
)
@@ -854,9 +857,121 @@ def attendance_activity_bulk_delete(request):
return JsonResponse({"message": "Success"})
def process_activity_dicts(activity_dicts):
from attendance.views.clock_in_out import clock_in, clock_out
if not activity_dicts:
return
sorted_activity_dicts = sort_activity_dicts(activity_dicts)
for activity in sorted_activity_dicts:
badge_id = activity.get("Badge ID")
if not badge_id:
activity["Error 1"] = "Please add the Badge ID column in the Excel sheet."
continue
employee = Employee.objects.filter(badge_id=badge_id).first()
if not employee:
activity["Error 2"] = "Invalid Badge ID"
continue
check_in_date = parse_date(activity["In Date"], "Error 4", activity)
check_out_date = parse_date(activity["Out Date"], "Error 5", activity)
check_in_time = (
parse_time(activity["Check In"])
if not pd.isna(activity["Check In"])
else None
)
check_out_time = (
parse_time(activity["Check Out"])
if not pd.isna(activity["Check Out"])
else None
)
if not any(key.startswith("Error") for key in activity.keys()):
if check_in_time:
try:
clock_in(
Request(
user=employee.employee_user_id,
date=check_in_date,
time=check_in_time,
datetime=django_timezone.make_aware(
datetime.combine(check_in_date, check_in_time)
),
)
)
except Exception as e:
logger.error(f"Got an error in import clock in {e}")
if check_out_time and check_out_date:
try:
clock_out(
Request(
user=employee.employee_user_id,
date=check_out_date,
time=check_out_time,
datetime=django_timezone.make_aware(
datetime.combine(check_out_date, check_out_time)
),
)
)
except Exception as e:
logger.error(f"Got an error in import clock out {e}")
return activity_dicts
@login_required
@permission_required("attendance.add_attendanceactivity")
def attendance_activity_import(request):
if request.method == "POST":
file = request.FILES["activity_import"]
data_frame = pd.read_excel(file)
activity_dicts = data_frame.to_dict("records")
if activity_dicts:
activity_dicts = process_activity_dicts(activity_dicts)
messages.success(request, _("Attendance activity imported successfully"))
return redirect(attendance_activity_view)
return render(request, "attendance/attendance_activity/import_activity.html")
@login_required
@permission_required("attendance.add_attendanceactivity")
def attendance_activity_import_excel(request):
if request.method == "GET":
data_frame = pd.DataFrame(
columns=[
"Badge ID",
"Employee",
"Attendance Date",
"In Date",
"Check In",
"Check Out",
"Out Date",
]
)
response = HttpResponse(
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
response["Content-Disposition"] = 'attachment; filename="activity_excel.xlsx"'
data_frame.to_excel(response, index=False)
return response
@login_required
@permission_required("attendance.change_attendanceactivity")
def attendance_activity_export(request):
if request.META.get("HTTP_HX_REQUEST") == "true":
export_form = AttendanceActivityExportForm()
context = {
"export_form": export_form,
"export": AttendanceActivityFilter(
queryset=AttendanceActivity.objects.all()
),
}
return render(
request,
"attendance/attendance_activity/export_filter.html",
context=context,
)
return export_data(
request=request,
model=AttendanceActivity,