555 lines
20 KiB
Python
555 lines
20 KiB
Python
"""
|
|
Attendance requests
|
|
"""
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
from django.contrib import messages
|
|
from django.http import HttpResponse
|
|
from django.urls import reverse
|
|
from django.utils.decorators import method_decorator
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from attendance.filters import AttendanceFilters
|
|
from attendance.forms import (
|
|
AttendanceRequestForm,
|
|
BulkAttendanceRequestForm,
|
|
NewRequestForm,
|
|
)
|
|
from attendance.methods.utils import get_employee_last_name
|
|
from attendance.models import Attendance
|
|
from base.methods import choosesubordinates, filtersubordinates, is_reportingmanager
|
|
from employee.models import Employee
|
|
from horilla_views.cbv_methods import login_required
|
|
from horilla_views.generic.cbv.views import (
|
|
HorillaDetailedView,
|
|
HorillaFormView,
|
|
HorillaListView,
|
|
HorillaNavView,
|
|
HorillaTabView,
|
|
TemplateView,
|
|
)
|
|
from notifications.signals import notify
|
|
|
|
|
|
@method_decorator(login_required, name="dispatch")
|
|
class AttendancesRequestView(TemplateView):
|
|
"""
|
|
for attendance request page
|
|
"""
|
|
|
|
template_name = "cbv/attendance_request/attendance_request.html"
|
|
|
|
|
|
@method_decorator(login_required, name="dispatch")
|
|
class AttendancesRequestTabView(HorillaTabView):
|
|
"""
|
|
tabview of attendance request page
|
|
"""
|
|
|
|
def __init__(self, **kwargs: Any) -> None:
|
|
super().__init__(**kwargs)
|
|
self.view_id = "attendance-container"
|
|
self.tabs = [
|
|
{
|
|
"title": _("Requested Attendances"),
|
|
"url": f"{reverse('attendance-request-list-tab')}",
|
|
},
|
|
{
|
|
"title": _("All Attendances"),
|
|
"url": f"{reverse('attendance-list-tab')}",
|
|
},
|
|
]
|
|
|
|
|
|
def request_approved_by(self):
|
|
"""
|
|
Approve the attendance request
|
|
"""
|
|
return self.approved_by if self.approved_by else "-"
|
|
|
|
|
|
Attendance.request_approved_by = request_approved_by
|
|
|
|
|
|
@method_decorator(login_required, name="dispatch")
|
|
class AttendancesRequestListView(HorillaListView):
|
|
"""
|
|
list view
|
|
"""
|
|
|
|
def __init__(self, **kwargs: Any) -> None:
|
|
super().__init__(**kwargs)
|
|
self.search_url = reverse("attendance-request-tab")
|
|
|
|
filter_class = AttendanceFilters
|
|
model = Attendance
|
|
columns = [
|
|
(_("Employee"), "employee_id", "employee_id__get_avatar"),
|
|
(_("Date"), "attendance_date"),
|
|
(_("Day"), "attendance_day"),
|
|
(_("Check-In"), "attendance_clock_in"),
|
|
(_("In Date"), "attendance_clock_in_date"),
|
|
(_("Check-Out"), "attendance_clock_out"),
|
|
(_("Out Date"), "attendance_clock_out_date"),
|
|
(_("Shift"), "shift_id"),
|
|
(_("Work Type"), "work_type_id"),
|
|
(_("Min Hour"), "minimum_hour"),
|
|
(_("At Work"), "attendance_worked_hour"),
|
|
(_("Overtime"), "attendance_overtime"),
|
|
(_("Approved By"), "request_approved_by"),
|
|
]
|
|
sortby_mapping = [
|
|
("Employee", "employee_id__get_full_name", "employee_id__get_avatar"),
|
|
("Date", "attendance_date"),
|
|
("In Date", "attendance_clock_in_date"),
|
|
("Out Date", "attendance_clock_out_date"),
|
|
("At Work", "attendance_worked_hour"),
|
|
("Overtime", "attendance_overtime"),
|
|
]
|
|
row_status_indications = [
|
|
(
|
|
"bulk-request--dot",
|
|
_("Bulk-Requests"),
|
|
"""
|
|
onclick="
|
|
$('#applyFilter').closest('form').find('[name=is_bulk_request]').val('true');
|
|
$('[name=attendance_validated]').val('unknown').change();
|
|
$('#applyFilter').click();
|
|
"
|
|
""",
|
|
),
|
|
(
|
|
"not-validated--dot",
|
|
_("Not Validated"),
|
|
"""
|
|
onclick="
|
|
$('#applyFilter').closest('form').find('[name=attendance_validated]').val('false');
|
|
$('[name=is_bulk_request]').val('unknown').change();
|
|
$('#applyFilter').click();
|
|
"
|
|
""",
|
|
),
|
|
(
|
|
"validated--dot",
|
|
_("Validated"),
|
|
"""
|
|
onclick="
|
|
$('#applyFilter').closest('form').find('[name=attendance_validated]').val('true');
|
|
$('[name=is_bulk_request]').val('unknown').change();
|
|
$('#applyFilter').click();
|
|
|
|
"
|
|
""",
|
|
),
|
|
]
|
|
|
|
row_status_class = "validated-{attendance_validated}"
|
|
|
|
|
|
@method_decorator(login_required, name="dispatch")
|
|
class AttendanceRequestListTab(AttendancesRequestListView):
|
|
"""
|
|
Attendance request tab
|
|
"""
|
|
|
|
def __init__(self, **kwargs: Any) -> None:
|
|
super().__init__(**kwargs)
|
|
self.view_id = "attendance-requests-container"
|
|
|
|
template_name = "cbv/attendance_request/attendance_request_tab.html"
|
|
|
|
columns = [
|
|
col for col in AttendancesRequestListView.columns if col[1] != "status_col"
|
|
]
|
|
|
|
action_method = "request_actions"
|
|
row_attrs = """
|
|
id = "requestedattendanceTr{get_instance_id}"
|
|
data-attendance-id="{get_instance_id}"
|
|
data-toggle="oh-modal-toggle"
|
|
data-target="#validateAttendanceRequest"
|
|
hx-get = "{detail_view}?instance_ids={ordered_ids}"
|
|
hx-trigger ="click"
|
|
hx-target="#validateAttendanceRequestModalBody"
|
|
"""
|
|
|
|
def get_queryset(self):
|
|
queryset = super().get_queryset()
|
|
self_data = queryset
|
|
queryset = queryset.filter(
|
|
is_validate_request=True,
|
|
)
|
|
queryset = filtersubordinates(
|
|
request=self.request,
|
|
perm="attendance.view_attendance",
|
|
queryset=queryset,
|
|
)
|
|
queryset = queryset | self_data.filter(
|
|
employee_id__employee_user_id=self.request.user,
|
|
is_validate_request=True,
|
|
)
|
|
return queryset
|
|
|
|
|
|
def change_accessibility(request, instance: object = None, *args, **kwargs) -> bool:
|
|
|
|
if (
|
|
request.user.has_perm("attendance.change_attendance")
|
|
or is_reportingmanager(request)
|
|
or request.user == instance.employee_id.employee_user_id
|
|
):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
@method_decorator(login_required, name="dispatch")
|
|
class AttendanceListTab(AttendancesRequestListView):
|
|
"""
|
|
Attendance tab
|
|
"""
|
|
|
|
def get_queryset(self):
|
|
queryset = super().get_queryset()
|
|
data = queryset
|
|
attendances = filtersubordinates(
|
|
request=self.request,
|
|
perm="attendance.view_attendance",
|
|
queryset=queryset,
|
|
)
|
|
queryset = attendances | data.filter(
|
|
employee_id__employee_user_id=self.request.user
|
|
)
|
|
queryset = queryset.filter(
|
|
employee_id__is_active=True,
|
|
)
|
|
return queryset
|
|
|
|
actions = [
|
|
{
|
|
"action": _("Edit"),
|
|
"icon": "create-outline",
|
|
"accessibility": "attendance.cbv.attendance_request.change_accessibility",
|
|
"attrs": """
|
|
class="oh-btn oh-btn--light-bkg w-100"
|
|
data-toggle="oh-modal-toggle"
|
|
data-target="#genericModal"
|
|
hx-get="{change_attendance}"
|
|
hx-target="#genericModalBody"
|
|
|
|
""",
|
|
}
|
|
]
|
|
|
|
row_attrs = """
|
|
{diff_cell}
|
|
id = "allattendanceTr{get_instance_id}"
|
|
hx-get='{attendance_detail_view}?instance_ids={ordered_ids}'
|
|
hx-target="#genericModalBody"
|
|
hx-trigger ="click"
|
|
data-target="#genericModal"
|
|
data-toggle="oh-modal-toggle"
|
|
"""
|
|
|
|
|
|
@method_decorator(login_required, name="dispatch")
|
|
class AttendanceRequestNav(HorillaNavView):
|
|
"""
|
|
nav bar
|
|
"""
|
|
|
|
def __init__(self, **kwargs: Any) -> None:
|
|
super().__init__(**kwargs)
|
|
self.search_url = reverse("attendance-request-tab")
|
|
self.create_attrs = f"""
|
|
data-toggle="oh-modal-toggle"
|
|
data-target="#genericModal"
|
|
hx-get="{reverse('request-new-attendance')}"
|
|
hx-target="#genericModalBody"
|
|
"""
|
|
if self.request.user.has_perm(
|
|
"attendance.add_attendanceovertime"
|
|
) or is_reportingmanager(self.request):
|
|
|
|
self.actions = [
|
|
{
|
|
"action": _("Bulk Approve"),
|
|
"attrs": """
|
|
onclick="
|
|
reqAttendanceBulkApprove();
|
|
"
|
|
style="cursor: pointer;"
|
|
""",
|
|
},
|
|
{
|
|
"action": _("Bulk Reject"),
|
|
"attrs": """
|
|
onclick="reqAttendanceBulkReject();"
|
|
style="color:red !important"
|
|
""",
|
|
},
|
|
]
|
|
else:
|
|
self.actions = None
|
|
|
|
nav_title = _("Attendances")
|
|
filter_body_template = "cbv/attendances/attendances_filter_page.html"
|
|
filter_instance = AttendanceFilters()
|
|
filter_form_context_name = "form"
|
|
search_swap_target = "#listContainer"
|
|
|
|
group_by_fields = [
|
|
("employee_id", "Employee"),
|
|
("attendance_date", "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"),
|
|
("shift_id", "Shift"),
|
|
("work_type_id", "Work Type"),
|
|
("minimum_hour", " Min Hour"),
|
|
("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", "Employement Type"),
|
|
("employee_id__employee_work_info__company_id", "Company"),
|
|
]
|
|
|
|
|
|
@method_decorator(login_required, name="dispatch")
|
|
class AttendanceListTabDetailView(HorillaDetailedView):
|
|
"""
|
|
Detail view of page
|
|
"""
|
|
|
|
model = Attendance
|
|
|
|
title = _("Details")
|
|
header = {
|
|
"title": "employee_id__get_full_name",
|
|
"subtitle": "attendances_detail_subtitle",
|
|
"avatar": "employee_id__get_avatar",
|
|
}
|
|
body = [
|
|
(_("Date"), "attendance_date"),
|
|
(_("Day"), "attendance_day"),
|
|
(_("Check-In"), "attendance_clock_in"),
|
|
(_("Check In Date"), "attendance_clock_in_date"),
|
|
(_("Check-Out"), "attendance_clock_out"),
|
|
(_("Check Out Date"), "attendance_clock_out_date"),
|
|
(_("Shift"), "shift_id"),
|
|
(_("Work Type"), "work_type_id"),
|
|
(_("Min Hour"), "minimum_hour"),
|
|
(_("At Work"), "attendance_worked_hour"),
|
|
(_("Overtime"), "attendance_overtime"),
|
|
(_("Approved By"), "request_approved_by"),
|
|
(_("Activities"), "attendance_detail_activity_col", True),
|
|
]
|
|
|
|
def get_context_data(self, **kwargs):
|
|
if (
|
|
self.request.user.has_perm("attendance.change_attendance")
|
|
or is_reportingmanager(self.request)
|
|
or self.request.user == self.get_object().employee_id.employee_user_id
|
|
):
|
|
|
|
self.actions = [
|
|
{
|
|
"action": _("Edit"),
|
|
"icon": "create-outline",
|
|
"attrs": """
|
|
onclick="event.stopPropagation();"
|
|
class="oh-btn oh-btn--info w-100"
|
|
data-toggle="oh-modal-toggle"
|
|
data-target="#genericModalEdit"
|
|
hx-get="{change_attendance}?all_attendance=true"
|
|
hx-target="#genericModalEditBody"
|
|
|
|
""",
|
|
}
|
|
]
|
|
return super().get_context_data(**kwargs)
|
|
|
|
|
|
class NewAttendanceRequestFormView(HorillaFormView):
|
|
"""
|
|
form view for create attendance request
|
|
"""
|
|
|
|
form_class = NewRequestForm
|
|
model = Attendance
|
|
new_display_title = _("New Attendance Request")
|
|
template_name = "requests/attendance/request_form.html"
|
|
|
|
def __init__(self, **kwargs: Any) -> None:
|
|
super().__init__(**kwargs)
|
|
self.view_id = "attendanceRequest"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
self.form = choosesubordinates(
|
|
self.request, self.form, "attendance.change_attendance"
|
|
)
|
|
self.form.fields["employee_id"].queryset = (
|
|
self.form.fields["employee_id"].queryset
|
|
).distinct() | (
|
|
Employee.objects.filter(employee_user_id=self.request.user)
|
|
).distinct()
|
|
self.form.fields["employee_id"].initial = self.request.user.employee_get.id
|
|
if self.request.GET.get("emp_id"):
|
|
emp_id = self.request.GET.get("emp_id")
|
|
self.form.fields["employee_id"].queryset = Employee.objects.filter(
|
|
id=emp_id
|
|
)
|
|
self.form.fields["employee_id"].initial = emp_id
|
|
if self.form.instance.pk:
|
|
self.form_class.verbose_name = _("Update Attendance Request")
|
|
return context
|
|
|
|
def form_valid(self, form: NewRequestForm) -> HttpResponse:
|
|
if form.is_valid():
|
|
message = _("New Attendance request created")
|
|
if form.new_instance is not None:
|
|
form.new_instance.save()
|
|
messages.success(self.request, message)
|
|
return self.HttpResponse()
|
|
return super().form_valid(form)
|
|
|
|
|
|
class BulkAttendanceRequestFormView(HorillaFormView):
|
|
"""
|
|
form view for create bulk attendance request
|
|
"""
|
|
|
|
form_class = BulkAttendanceRequestForm
|
|
model = Attendance
|
|
new_display_title = _("New Attendance Request")
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
self.form = choosesubordinates(
|
|
self.request, self.form, "attendance.change_attendance"
|
|
)
|
|
self.form.fields["employee_id"].queryset = self.form.fields[
|
|
"employee_id"
|
|
].queryset | Employee.objects.filter(employee_user_id=self.request.user)
|
|
self.form.fields["employee_id"].initial = self.request.user.employee_get.id
|
|
return context
|
|
|
|
def post(self, request, *args, pk=None, **kwargs):
|
|
self.get_form()
|
|
form = self.form
|
|
form.instance.attendance_clock_in_date = self.request.POST.get("from_date")
|
|
form.instance.attendance_date = self.request.POST.get("from_date")
|
|
if form.is_valid():
|
|
if form.instance.pk:
|
|
message = _("New Attendance request updated")
|
|
else:
|
|
message = _("New Attendance request created")
|
|
instance = form.save(commit=False)
|
|
messages.success(self.request, message)
|
|
return self.HttpResponse()
|
|
return super().post(request, *args, pk=pk, **kwargs)
|
|
|
|
def form_valid(self, form: BulkAttendanceRequestForm) -> HttpResponse:
|
|
form.instance.attendance_clock_in_date = self.request.POST.get("from_date")
|
|
form.instance.attendance_date = self.request.POST.get("from_date")
|
|
if form.is_valid():
|
|
if form.instance.pk:
|
|
message = _("New Attendance request updated")
|
|
else:
|
|
message = _("New Attendance request created")
|
|
instance = form.save(commit=False)
|
|
messages.success(self.request, message)
|
|
return self.HttpResponse()
|
|
return super().form_valid(form)
|
|
|
|
|
|
class UpdateAttendanceRequestFormView(HorillaFormView):
|
|
"""
|
|
form view for update attendance request
|
|
"""
|
|
|
|
form_class = AttendanceRequestForm
|
|
model = Attendance
|
|
|
|
def __init__(self, **kwargs: Any) -> None:
|
|
super().__init__(**kwargs)
|
|
self.view_id = "attendanceRequest"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
if self.form.instance.pk:
|
|
self.form_class.verbose_name = _("Update Attendance Request")
|
|
return context
|
|
|
|
def form_valid(self, form: AttendanceRequestForm) -> HttpResponse:
|
|
if form.is_valid():
|
|
attendance = Attendance.objects.get(id=self.form.instance.pk)
|
|
instance = form.save()
|
|
instance.employee_id = attendance.employee_id
|
|
instance.id = attendance.id
|
|
if attendance.request_type != "create_request":
|
|
attendance.requested_data = json.dumps(instance.serialize())
|
|
attendance.request_description = instance.request_description
|
|
# set the user level validation here
|
|
attendance.is_validate_request = True
|
|
attendance.save()
|
|
else:
|
|
instance.is_validate_request_approved = False
|
|
instance.is_validate_request = True
|
|
instance.save()
|
|
messages.success(self.request, _("Attendance update request created."))
|
|
employee = attendance.employee_id
|
|
if attendance.employee_id.employee_work_info.reporting_manager_id:
|
|
reporting_manager = (
|
|
attendance.employee_id.employee_work_info.reporting_manager_id.employee_user_id
|
|
)
|
|
user_last_name = get_employee_last_name(attendance)
|
|
notify.send(
|
|
self.request.user,
|
|
recipient=reporting_manager,
|
|
verb=f"{employee.employee_first_name} {user_last_name}'s\
|
|
attendance update request for {attendance.attendance_date} is created",
|
|
verb_ar=f"تم إنشاء طلب تحديث الحضور لـ {employee.employee_first_name} \
|
|
{user_last_name }في {attendance.attendance_date}",
|
|
verb_de=f"Die Anfrage zur Aktualisierung der Anwesenheit von \
|
|
{employee.employee_first_name} {user_last_name} \
|
|
für den {attendance.attendance_date} wurde erstellt",
|
|
verb_es=f"Se ha creado la solicitud de actualización de asistencia para {employee.employee_first_name}\
|
|
{user_last_name} el {attendance.attendance_date}",
|
|
verb_fr=f"La demande de mise à jour de présence de {employee.employee_first_name}\
|
|
{user_last_name} pour le {attendance.attendance_date} a été créée",
|
|
redirect=reverse("request-attendance-view")
|
|
+ f"?id={attendance.id}",
|
|
icon="checkmark-circle-outline",
|
|
)
|
|
detail_view = self.request.GET.get("detail_view")
|
|
all_attendance = self.request.GET.get("all_attendance")
|
|
if detail_view == "true":
|
|
return HttpResponse(
|
|
f"""<script>
|
|
var reqModal = $('#requestedattendanceTr{form.instance.pk}');
|
|
reqModal[0].click();
|
|
$('#genericModalEdit').removeClass('oh-modal--show');
|
|
$('.reload-record').click();
|
|
$('#reloadMessagesButton').click();
|
|
</script>
|
|
"""
|
|
)
|
|
elif all_attendance == "true":
|
|
return HttpResponse(
|
|
f"""<script>
|
|
var attendaceModal = $('#allattendanceTr{form.instance.pk}');
|
|
attendaceModal[0].click();
|
|
$('#genericModalEdit').removeClass('oh-modal--show');
|
|
$('.reload-record').click();
|
|
$('#reloadMessagesButton').click();
|
|
</script>
|
|
"""
|
|
)
|
|
|
|
return self.HttpResponse()
|
|
return super().form_valid(form)
|