Files
ihrm/attendance/filters.py

689 lines
25 KiB
Python

"""
filters.py
This page is used to register filter for attendance models
"""
import datetime
import uuid
import django_filters
from django import forms
from django.forms import DateTimeInput
from django.utils.translation import gettext_lazy as _
from attendance.models import (
Attendance,
AttendanceActivity,
AttendanceLateComeEarlyOut,
AttendanceOverTime,
strtime_seconds,
)
from base.filters import FilterSet
from employee.filters import EmployeeFilter
from employee.models import Employee
from horilla.filters import filter_by_name
class DurationInSecondsFilter(django_filters.CharFilter):
"""
Custom CharFilter class that applies specific filter process.
"""
def filter(self, qs, value):
"""
FilterSet filter method
Args:
qs (self): FilterSet instance
value (str): duration formatted string
Returns:
qs: queryset object
"""
if value:
ftr = [3600, 60, 1]
duration_sec = sum(a * b for a, b in zip(ftr, map(int, value.split(":"))))
lookup = self.lookup_expr or "exact"
return qs.filter(**{f"{self.field_name}__{lookup}": duration_sec})
return qs
class AttendanceOverTimeFilter(FilterSet):
"""
Filter set class for AttendanceOverTime model
Args:
FilterSet (class): custom filter set class to apply styling
"""
MONTH_CHOICES = [
("January", _("January")),
("February", _("February")),
("March", _("March")),
("April", _("April")),
("May", _("May")),
("June", _("June")),
("July", _("July")),
("August", _("August")),
("September", _("September")),
("October", _("October")),
("November", _("November")),
("December", _("December")),
]
search = django_filters.CharFilter(method=filter_by_name)
worked_hours__gte = DurationInSecondsFilter(
field_name="hour_account_second", lookup_expr="gte"
)
worked_hours__lte = DurationInSecondsFilter(
field_name="hour_account_second", lookup_expr="lte"
)
pending_hours__lte = DurationInSecondsFilter(
field_name="hour_pending_second", lookup_expr="lte"
)
pending_hours__gte = DurationInSecondsFilter(
field_name="hour_pending_second", lookup_expr="gte"
)
overtime__gte = DurationInSecondsFilter(
field_name="overtime_second", lookup_expr="gte"
)
overtime__lte = DurationInSecondsFilter(
field_name="overtime_second", lookup_expr="lte"
)
month = django_filters.ChoiceFilter(choices=MONTH_CHOICES, lookup_expr="icontains")
department_name = django_filters.CharFilter(
field_name="employee_id__employee_work_info__department_id__department",
lookup_expr="icontains",
)
class Meta:
"""
Meta class to add additional options
"""
model = AttendanceOverTime
fields = [
"employee_id",
"month",
"overtime",
"worked_hours",
"year",
"department_name",
"employee_id__employee_work_info__department_id",
"employee_id__employee_work_info__company_id",
"employee_id__employee_work_info__job_position_id",
"employee_id__employee_work_info__location",
"employee_id__employee_work_info__reporting_manager_id",
"employee_id__employee_work_info__shift_id",
"employee_id__employee_work_info__work_type_id",
]
def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
super().__init__(data=data, queryset=queryset, request=request, prefix=prefix)
for field in self.form.fields.keys():
self.form.fields[field].widget.attrs["id"] = f"{uuid.uuid4()}"
class LateComeEarlyOutFilter(FilterSet):
"""
LateComeEarlyOutFilter class
"""
search = django_filters.CharFilter(method=filter_by_name)
employee_id = django_filters.ModelMultipleChoiceFilter(
queryset=Employee.objects.all(),
widget=forms.SelectMultiple(),
)
attendance_date__gte = django_filters.DateFilter(
field_name="attendance_id__attendance_date",
lookup_expr="gte",
widget=forms.DateInput(attrs={"type": "date"}),
)
attendance_date__lte = django_filters.DateFilter(
field_name="attendance_id__attendance_date",
lookup_expr="lte",
widget=forms.DateInput(attrs={"type": "date"}),
)
attendance_clock_in__lte = django_filters.TimeFilter(
field_name="attendance_id__attendance_clock_in",
widget=forms.TimeInput(attrs={"type": "time"}),
lookup_expr="lte",
)
attendance_clock_in__gte = django_filters.TimeFilter(
field_name="attendance_id__attendance_clock_in",
widget=forms.TimeInput(attrs={"type": "time"}),
lookup_expr="gte",
)
attendance_clock_out__gte = django_filters.TimeFilter(
field_name="attendance_id__attendance_clock_out",
widget=forms.TimeInput(attrs={"type": "time"}),
lookup_expr="gte",
)
attendance_clock_out__lte = django_filters.TimeFilter(
field_name="attendance_id__attendance_clock_out",
widget=forms.TimeInput(attrs={"type": "time"}),
lookup_expr="lte",
)
attendance_clock_in = django_filters.TimeFilter(
field_name="attendance_id__attendance_clock_in",
widget=forms.TimeInput(attrs={"type": "time"}),
lookup_expr="exact",
)
attendance_clock_out = django_filters.TimeFilter(
field_name="attendance_id__attendance_clock_out",
widget=forms.TimeInput(attrs={"type": "time"}),
lookup_expr="exact",
)
attendance_date = django_filters.DateFilter(
field_name="attendance_id__attendance_date",
widget=forms.DateInput(attrs={"type": "date"}),
)
overtime_second__lte = DurationInSecondsFilter(
field_name="attendance_id__overtime_second", lookup_expr="lte"
)
overtime_second__gte = DurationInSecondsFilter(
field_name="attendance_id__overtime_second", lookup_expr="gte"
)
at_work_second__lte = DurationInSecondsFilter(
field_name="attendance_id__at_work_second", lookup_expr="lte"
)
at_work_second__gte = DurationInSecondsFilter(
field_name="attendance_id__at_work_second", lookup_expr="gte"
)
department = django_filters.CharFilter(
field_name="employee_id__employee_work_info__department_id__department",
lookup_expr="icontains",
)
year = django_filters.CharFilter(
field_name="attendance_id__attendance_date", lookup_expr="year"
)
month = django_filters.CharFilter(
field_name="attendance_id__attendance_date", lookup_expr="month"
)
week = django_filters.CharFilter(
field_name="attendance_id__attendance_date", lookup_expr="week"
)
class Meta:
"""
Meta class for additional options"""
model = AttendanceLateComeEarlyOut
fields = [
"employee_id",
"type",
"attendance_id__minimum_hour",
"attendance_id__attendance_worked_hour",
"attendance_id__attendance_overtime_approve",
"attendance_id__attendance_validated",
"employee_id__employee_work_info__department_id",
"employee_id__employee_work_info__company_id",
"employee_id__employee_work_info__job_position_id",
"employee_id__employee_work_info__location",
"employee_id__employee_work_info__reporting_manager_id",
"attendance_id__shift_id",
"attendance_id__work_type_id",
"attendance_date__gte",
"attendance_date__lte",
"attendance_clock_in__lte",
"attendance_clock_in__gte",
"attendance_clock_out__gte",
"attendance_clock_out__lte",
"attendance_clock_in",
"attendance_clock_out",
"attendance_date",
"department",
"year",
"month",
"week",
]
def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
super().__init__(data=data, queryset=queryset, request=request, prefix=prefix)
for field in self.form.fields.keys():
self.form.fields[field].widget.attrs["id"] = f"{uuid.uuid4()}"
class AttendanceActivityFilter(FilterSet):
"""
Filter set class for AttendanceActivity model
Args:
FilterSet (class): custom filter set class to apply styling
"""
search = django_filters.CharFilter(method=filter_by_name)
attendance_date = django_filters.DateFilter(
field_name="attendance_date", widget=forms.DateInput(attrs={"type": "date"})
)
attendance_date_from = django_filters.DateFilter(
field_name="attendance_date",
lookup_expr="gte",
widget=forms.DateInput(attrs={"type": "date"}),
)
attendance_date_till = django_filters.DateFilter(
field_name="attendance_date",
lookup_expr="lte",
widget=forms.DateInput(attrs={"type": "date"}),
)
in_from = django_filters.DateFilter(
field_name="clock_in",
lookup_expr="gte",
widget=forms.DateInput(attrs={"type": "time"}),
)
out_from = django_filters.DateFilter(
field_name="clock_out",
lookup_expr="gte",
widget=forms.DateInput(attrs={"type": "time"}),
)
in_till = django_filters.DateFilter(
field_name="clock_in",
lookup_expr="lte",
widget=forms.DateInput(attrs={"type": "time"}),
)
out_till = django_filters.DateFilter(
field_name="clock_out",
lookup_expr="lte",
widget=forms.DateInput(attrs={"type": "time"}),
)
clock_in_date = django_filters.DateFilter(
field_name="clock_in_date", widget=forms.DateInput(attrs={"type": "date"})
)
clock_out_date = django_filters.DateFilter(
field_name="clock_out_date", widget=forms.DateInput(attrs={"type": "date"})
)
class Meta:
"""
Meta class to add additional options
"""
fields = [
"employee_id",
"attendance_date",
"attendance_date_from",
"attendance_date_till",
"in_from",
"in_till",
"out_from",
"shift_day",
"out_till",
"clock_in_date",
"clock_out_date",
"employee_id__employee_work_info__department_id",
"employee_id__employee_work_info__company_id",
"employee_id__employee_work_info__shift_id",
"employee_id__employee_work_info__work_type_id",
"employee_id__employee_work_info__job_position_id",
"employee_id__employee_work_info__location",
"employee_id__employee_work_info__reporting_manager_id",
]
model = AttendanceActivity
def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
super().__init__(data=data, queryset=queryset, request=request, prefix=prefix)
for field in self.form.fields.keys():
self.form.fields[field].widget.attrs["id"] = f"{uuid.uuid4()}"
class AttendanceFilters(FilterSet):
"""
Filter set class for Attendance model
Args:
FilterSet (class): custom filter set class to apply styling
"""
id = django_filters.NumberFilter(field_name="id")
search = django_filters.CharFilter(method="filter_by_name")
employee = django_filters.CharFilter(field_name="employee_id__id")
date_attendance = django_filters.DateFilter(field_name="attendance_date")
employee_id = django_filters.ModelMultipleChoiceFilter(
queryset=Employee.objects.all(),
widget=forms.SelectMultiple(),
)
attendance_date__gte = django_filters.DateFilter(
field_name="attendance_date",
lookup_expr="gte",
widget=forms.DateInput(attrs={"type": "date"}),
)
attendance_date__lte = django_filters.DateFilter(
field_name="attendance_date",
lookup_expr="lte",
widget=forms.DateInput(attrs={"type": "date"}),
)
attendance_clock_in__lte = django_filters.TimeFilter(
field_name="attendance_clock_in",
widget=forms.TimeInput(attrs={"type": "time"}),
lookup_expr="lte",
)
attendance_clock_in__gte = django_filters.TimeFilter(
field_name="attendance_clock_in",
widget=forms.TimeInput(attrs={"type": "time"}),
lookup_expr="gte",
)
attendance_clock_out__gte = django_filters.TimeFilter(
field_name="attendance_clock_out",
widget=forms.TimeInput(attrs={"type": "time"}),
lookup_expr="gte",
)
attendance_clock_out__lte = django_filters.TimeFilter(
field_name="attendance_clock_out",
widget=forms.TimeInput(attrs={"type": "time"}),
lookup_expr="lte",
)
attendance_clock_in = django_filters.TimeFilter(
field_name="attendance_clock_in",
widget=forms.TimeInput(attrs={"type": "time"}),
)
attendance_clock_out = django_filters.TimeFilter(
field_name="attendance_clock_out",
widget=forms.TimeInput(attrs={"type": "time"}),
)
attendance_date = django_filters.DateFilter(
widget=forms.DateInput(attrs={"type": "date"}),
)
pending_hour__lte = DurationInSecondsFilter(
method="filter_pending_hour",
)
pending_hour__gte = DurationInSecondsFilter(
method="filter_pending_hour",
)
at_work_second__lte = DurationInSecondsFilter(
field_name="at_work_second", lookup_expr="lte"
)
at_work_second__gte = DurationInSecondsFilter(
field_name="at_work_second", lookup_expr="gte"
)
overtime_second__lte = DurationInSecondsFilter(
field_name="overtime_second", lookup_expr="lte"
)
overtime_second__gte = DurationInSecondsFilter(
field_name="overtime_second", lookup_expr="gte"
)
year = django_filters.CharFilter(field_name="attendance_date", lookup_expr="year")
month = django_filters.CharFilter(field_name="attendance_date", lookup_expr="month")
week = django_filters.CharFilter(field_name="attendance_date", lookup_expr="week")
department = django_filters.CharFilter(
field_name="employee_id__employee_work_info__department_id__department",
lookup_expr="icontains",
)
def filter_pending_hour(self, queryset, name, value):
"""
This method calculates the pending hours for each attendance record in the
queryset and filters the records based on whether the pending hours are less
than or equal to (`pending_hour__lte`) or greater than the specified value.
"""
if value is not None:
value = strtime_seconds(value)
filtered_attendance = []
for attendance in queryset:
minimum_hour_second = strtime_seconds(attendance.minimum_hour)
worked_hour_second = attendance.at_work_second
pending_hour_second = minimum_hour_second - worked_hour_second
if name == "pending_hour__lte":
if value >= pending_hour_second:
filtered_attendance.append(attendance)
else:
if value <= pending_hour_second:
filtered_attendance.append(attendance)
return queryset.filter(
id__in=[attendance.id for attendance in filtered_attendance]
)
class Meta:
"""
Meta class to add additional options
"""
model = Attendance
fields = [
"id",
"employee_id",
"employee_id__employee_work_info__department_id",
"employee_id__employee_work_info__company_id",
"employee_id__employee_work_info__job_position_id",
"employee_id__employee_work_info__location",
"employee_id__employee_work_info__reporting_manager_id",
"attendance_day",
"attendance_date",
"work_type_id",
"shift_id",
"minimum_hour",
"attendance_validated",
"attendance_clock_in",
"attendance_clock_out",
"at_work_second",
"overtime_second",
"late_come_early_out__type",
"attendance_overtime_approve",
"attendance_validated",
"is_validate_request",
"is_validate_request_approved",
"is_bulk_request",
"at_work_second__lte",
"at_work_second__gte",
"overtime_second__lte",
"overtime_second__gte",
"overtime_second",
"department",
"month",
"year",
"batch_attendance_id",
]
widgets = {
"attendance_date": DateTimeInput(attrs={"type": "date"}),
}
def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
super().__init__(data=data, queryset=queryset, request=request, prefix=prefix)
for field in self.form.fields.keys():
self.form.fields[field].widget.attrs["id"] = f"{uuid.uuid4()}"
def filter_by_name(self, queryset, name, value):
# Call the imported function
"""
This method allows filtering by the employee's first and/or last name or by other
fields such as day, shift, work type, department, job position, or company, depending
on the value of `search_field` provided in the request data.
"""
filter_method = {
"day": "attendance_day__day__icontains",
"shift": "shift_id__employee_shift__icontains",
"work_type": "work_type_id__work_type__icontains",
"department": "employee_id__employee_work_info__department_id__department__icontains",
"job_position": "employee_id__employee_work_info__\
job_position_id__job_position__icontains",
"company": "employee_id__employee_work_info__company_id__company__icontains",
}
search_field = self.data.get("search_field")
if not search_field:
parts = value.split()
first_name = parts[0]
last_name = " ".join(parts[1:]) if len(parts) > 1 else ""
# Filter the queryset by first name and last name
if first_name and last_name:
queryset = queryset.filter(
employee_id__employee_first_name__icontains=first_name,
employee_id__employee_last_name__icontains=last_name,
)
elif first_name:
queryset = queryset.filter(
employee_id__employee_first_name__icontains=first_name
)
elif last_name:
queryset = queryset.filter(
employee_id__employee_last_name__icontains=last_name
)
else:
filter = filter_method.get(search_field)
queryset = queryset.filter(**{filter: value})
return queryset
class LateComeEarlyOutReGroup:
"""
Class to keep the field name for group by option
"""
fields = [
("", "Select"),
("employee_id", "Employee"),
("type", "Type"),
("attendance_id__attendance_date", "Attendance Date"),
("attendance_id__shift_id", "Shift"),
("attendance_id__work_type_id", "Work Type"),
("attendance_id__minimum_hour", "Minimum Hour"),
("attendance_id__employee_id__country", "Country"),
(
"attendance_id__employee_id__employee_work_info__reporting_manager_id",
"Reporting Manager",
),
("attendance_id__employee_id__employee_work_info__department_id", "Department"),
(
"attendance_id__employee_id__employee_work_info__job_position_id",
"Job Position",
),
(
"attendance_id__employee_id__employee_work_info__employee_type_id",
"Employment Type",
),
("attendance_id__employee_id__employee_work_info__company_id", "Company"),
]
class AttendanceReGroup:
"""
Class to keep the field name for group by option
"""
fields = [
("", "Select"),
("employee_id", "Employee"),
("batch_attendance_id", "Batch"),
("attendance_date", "Attendance Date"),
("shift_id", "Shift"),
("work_type_id", "Work Type"),
("minimum_hour", "Minimum Hour"),
("employee_id__country", "Country"),
("employee_id__employee_work_info__reporting_manager_id", "Reporting Manager"),
("employee_id__employee_work_info__department_id", "Department"),
("employee_id__employee_work_info__job_position_id", "Job Position"),
("employee_id__employee_work_info__employee_type_id", "Employment Type"),
("employee_id__employee_work_info__company_id", "Company"),
]
class AttendanceOvertimeReGroup:
"""
Class to keep the field name for group by option
"""
fields = [
("", "Select"),
("employee_id", "Employee"),
("month", "Month"),
("year", "Year"),
("employee_id__country", "Country"),
("employee_id__employee_work_info__reporting_manager_id", "Reporting Manager"),
("employee_id__employee_work_info__shift_id", "Shift"),
("employee_id__employee_work_info__work_type_id", "Work Type"),
("employee_id__employee_work_info__department_id", "Department"),
("employee_id__employee_work_info__job_position_id", "Job Position"),
("employee_id__employee_work_info__employee_type_id", "Employment Type"),
("employee_id__employee_work_info__company_id", "Company"),
]
class AttendanceActivityReGroup:
"""
Class to keep the field name for group by option
"""
fields = [
("", "Select"),
("employee_id", "Employee"),
("attendance_date", "Attendance Date"),
("clock_in_date", "In Date"),
("clock_out_date", "Out Date"),
("shift_day", "Shift Day"),
("employee_id__country", "Country"),
("employee_id__employee_work_info__reporting_manager_id", "Reporting Manager"),
("employee_id__employee_work_info__shift_id", "Shift"),
("employee_id__employee_work_info__work_type_id", "Work Type"),
("employee_id__employee_work_info__department_id", "Department"),
("employee_id__employee_work_info__job_position_id", "Job Position"),
("employee_id__employee_work_info__employee_type_id", "Employment Type"),
("employee_id__employee_work_info__company_id", "Company"),
]
class AttendanceRequestReGroup:
"""
Class to keep the field name for group by option
"""
fields = [
("", "Select"),
("employee_id", "Employee"),
("batch_attendance_id", "Batch"),
("attendance_day", "Attendance Date"),
("attendance_clock_in_date", "In Date"),
("attendance_clock_out_date", "Out Date"),
("employee_id__country", "Country"),
("employee_id__employee_work_info__reporting_manager_id", "Reporting Manager"),
("employee_id__employee_work_info__shift_id", "Shift"),
("employee_id__employee_work_info__work_type_id", "Work Type"),
("employee_id__employee_work_info__department_id", "Department"),
("employee_id__employee_work_info__job_position_id", "Job Position"),
("employee_id__employee_work_info__employee_type_id", "Employment Type"),
("employee_id__employee_work_info__company_id", "Company"),
]
def get_working_today(queryset, _name, value):
today = datetime.datetime.now().date()
yesterday = today - datetime.timedelta(days=1)
working_employees = Attendance.objects.filter(
attendance_date__gte=yesterday,
attendance_date__lte=today,
attendance_clock_out_date__isnull=True,
).values_list("employee_id", flat=True)
if value:
queryset = queryset.filter(id__in=working_employees)
else:
queryset = queryset.exclude(id__in=working_employees)
return queryset
og_init = EmployeeFilter.__init__
def online_init(self, *args, **kwargs):
og_init(self, *args, **kwargs)
custom_field = django_filters.BooleanFilter(
label="Working", method=get_working_today
)
self.filters["working_today"] = custom_field
self.form.fields["working_today"] = custom_field.field
self.form.fields["working_today"].widget.attrs.update(
{
"class": "oh-select oh-select-2 w-100",
}
)
EmployeeFilter.__init__ = online_init