[UPDT] ATTENDANCE: Updated attendance activity page by adding option to import attendance activities in excel format
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = ""
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user