From ad75e4468949a2ca405fec49d84866e6372b2154 Mon Sep 17 00:00:00 2001 From: Horilla Date: Thu, 18 Jul 2024 13:58:21 +0530 Subject: [PATCH] [UPDT] ATTENDANCE: Updated attendance app by adding additional fields and functionality for grace time --- attendance/filters.py | 2 +- attendance/forms.py | 8 - attendance/models.py | 15 +- .../attendance/break_point/condition.html | 14 - .../attendance/grace_time/grace_time.html | 322 +----------------- .../grace_time/grace_time_form.html | 94 +++-- .../grace_time/grace_time_table.html | 193 +++++++++++ attendance/urls.py | 2 +- attendance/views/clock_in_out.py | 35 +- attendance/views/views.py | 44 +-- base/methods.py | 3 + horilla/horilla_settings.py | 4 +- 12 files changed, 335 insertions(+), 401 deletions(-) create mode 100644 attendance/templates/attendance/grace_time/grace_time_table.html diff --git a/attendance/filters.py b/attendance/filters.py index 8ff3304ed..a61f83f50 100644 --- a/attendance/filters.py +++ b/attendance/filters.py @@ -348,7 +348,7 @@ class AttendanceFilters(FilterSet): """ id = django_filters.NumberFilter(field_name="id") - search = django_filters.CharFilter(method=filter_by_name) + 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( diff --git a/attendance/forms.py b/attendance/forms.py index 055fd00b2..6b2f1265a 100644 --- a/attendance/forms.py +++ b/attendance/forms.py @@ -864,14 +864,6 @@ class GraceTimeForm(ModelForm): exclude = ["objects", "allowed_time_in_secs", "is_active"] - def as_p(self, *args, **kwargs): - """ - Render the form fields as HTML table rows with Bootstrap styling. - """ - context = {"form": self} - table_html = render_to_string("attendance_form.html", context) - return table_html - class AttendanceRequestCommentForm(ModelForm): """ diff --git a/attendance/models.py b/attendance/models.py index 0e4e39bc5..cb4596aef 100644 --- a/attendance/models.py +++ b/attendance/models.py @@ -970,12 +970,19 @@ class GraceTime(HorillaModel): verbose_name=_("Allowed time"), ) allowed_time_in_secs = models.IntegerField() + allowed_clock_in = models.BooleanField( + default=True, help_text=_("Allcocate this grace time for Check-In Attendance") + ) + allowed_clock_out = models.BooleanField( + default=False, help_text=_("Allcocate this grace time for Check-Out Attendance") + ) is_default = models.BooleanField(default=False) + company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) objects = HorillaCompanyManager() def __str__(self) -> str: - return str(f"{self.allowed_time} - Minutes") + return str(f"{self.allowed_time} - Hours") def clean(self): """ @@ -999,7 +1006,11 @@ class GraceTime(HorillaModel): .exists() ): raise ValidationError( - _("There is already a grace time with this allowed time that exists.") + { + "allowed_time": _( + "There is already an existing grace time with this allowed time." + ) + } ) def save(self, *args, **kwargs): diff --git a/attendance/templates/attendance/break_point/condition.html b/attendance/templates/attendance/break_point/condition.html index 4ece5f555..18e9598eb 100644 --- a/attendance/templates/attendance/break_point/condition.html +++ b/attendance/templates/attendance/break_point/condition.html @@ -67,20 +67,6 @@ - - - - - - - - - - -
-
-

{% trans 'Grace Time' %}

- {% if perms.attendance.add_gracetime %} - - {% endif %} -
- {% if grace_times %} -
-
-
-
-
- {% trans 'Allowed time' %} -
-
- {% trans 'Is active' %} -
- {% if perms.attendance.change_gracetime %} -
{% trans 'Actions' %}
- {% endif %} -
-
-
- {% for grace_time in grace_times %} -
-
{{ grace_time.allowed_time }} {% trans 'Hours' %}
-
-
-
- {% if perms.attendance.change_gracetime%} - - {% else %} - - {% endif %} -
-
-
- {% if perms.attendance.change_gracetime%} -
-
- {% if perms.base.change_gracetime %} - - - {% endif %} - {% if perms.base.delete_gracetime %} -
- {% csrf_token %} - -
- {% else %} - - {% endif %} -
-
- {% endif %} -
- {% endfor %} - -
-
-
- {% else %} -
- Page not found. 404. -
{% trans "There is no grace time at this moment." %}
-
- {% endif %} -
- - - - - - - - {% endblock %} diff --git a/attendance/templates/attendance/grace_time/grace_time_form.html b/attendance/templates/attendance/grace_time/grace_time_form.html index 7fcf88bd6..bf0f27207 100644 --- a/attendance/templates/attendance/grace_time/grace_time_form.html +++ b/attendance/templates/attendance/grace_time/grace_time_form.html @@ -1,50 +1,92 @@ {% load i18n %} - +{% load widget_tweaks %}

{% if grace_id %} - {% trans "Update grace time" %} + {% trans 'Update grace time' %} {% else %} - {% trans "Create grace time" %} + {% trans 'Create grace time' %} {% endif %}

- +
{% if form.errors %} - -
-
- {% for error in form.non_field_errors %} -
- {{ error }} -
- {% endfor %} -
+ +
+
+ {% for error in form.non_field_errors %} +
{{ error }}
+ {% endfor %}
+
{% endif %} -
+ > {% csrf_token %} - {{form.as_p}} +
+
+
+ + {{ form.allowed_time }} + {{ form.allowed_time.errors }} +
+
+
+
+
+
+
+ + +
+
+ {{ form.allowed_clock_in|add_class:"oh-switch__checkbox" }} +
+ {{ form.allowed_clock_in.errors }} +
+
+
+
+
+ + +
+
+ {{ form.allowed_clock_out }} +
+ {{ form.allowed_clock_out.errors }} +
+
+
+
+
+
+ + {{ form.company_id }} + {{ form.company_id.errors }} +
+
+
+ {{form.is_default}} +
diff --git a/attendance/templates/attendance/grace_time/grace_time_table.html b/attendance/templates/attendance/grace_time/grace_time_table.html new file mode 100644 index 000000000..3e5ad533a --- /dev/null +++ b/attendance/templates/attendance/grace_time/grace_time_table.html @@ -0,0 +1,193 @@ +{% load static %}{% load i18n %} +{% if messages %} +
+ {% for message in messages %} +
+
+ {{ message }} +
+
+ {% endfor %} +
+{% endif %} +
+
+

{% trans 'Default Grace Time' %}

+ {% if not default_grace_time and perms.attendance.add_gracetime %} + + {% endif %} +
+ {% if default_grace_time %} +
+
+
+
+
+ {% trans 'Allowed time' %} +
+
+ {% trans 'Is active' %} +
+ {% if perms.attendance.change_gracetime %} +
{% trans 'Actions' %}
+ {% endif %} +
+
+
+
+
{{ default_grace_time.allowed_time }} {% trans 'Hours' %}
+
+
+
+ {% if perms.attendance.change_gracetime%} + + {% else %} + + {% endif %} +
+
+
+ {% if perms.attendance.change_gracetime%} +
+
+ {% if perms.base.change_gracetime %} + + + {% endif %} + {% if perms.base.delete_gracetime %} +
+ {% csrf_token %} + +
+ {% else %} + + {% endif %} +
+
+ {% endif %} +
+
+
+
+ {% else %} +
+ Page not found. 404. +
{% trans "There is no default grace time at this moment." %}
+
+ {% endif %} +
+ + + +
+
+

{% trans 'Grace Time' %}

+ {% if perms.attendance.add_gracetime %} + + {% endif %} +
+ {% if grace_times %} +
+
+
+
+
+ {% trans 'Allowed time' %} +
+
+ {% trans 'Is active' %} +
+ {% if perms.attendance.change_gracetime %} +
{% trans 'Actions' %}
+ {% endif %} +
+
+
+ {% for grace_time in grace_times %} +
+
{{ grace_time.allowed_time }} {% trans 'Hours' %}
+
+
+
+ {% if perms.attendance.change_gracetime%} + + {% else %} + + {% endif %} +
+
+
+ {% if perms.attendance.change_gracetime%} +
+
+ {% if perms.base.change_gracetime %} + + + {% endif %} + {% if perms.base.delete_gracetime %} +
+ {% csrf_token %} + +
+ {% else %} + + {% endif %} +
+
+ {% endif %} +
+ {% endfor %} +
+
+
+ {% else %} +
+ Page not found. 404. +
{% trans "There is no grace time at this moment." %}
+
+ {% endif %} +
+ \ No newline at end of file diff --git a/attendance/urls.py b/attendance/urls.py index b2a6fb289..0fb68ad03 100644 --- a/attendance/urls.py +++ b/attendance/urls.py @@ -354,7 +354,7 @@ urlpatterns = [ name="delete-grace-time", ), path( - "update-isactive-gracetime", + "update-isactive-gracetime/", views.update_isactive_gracetime, name="update-isactive-gracetime", ), diff --git a/attendance/views/clock_in_out.py b/attendance/views/clock_in_out.py index fbc7c5546..0ebf5a678 100644 --- a/attendance/views/clock_in_out.py +++ b/attendance/views/clock_in_out.py @@ -70,7 +70,10 @@ def late_come(attendance, start_time, end_time, shift): # Checking gracetime allowance before creating late come if shift.grace_time_id: # checking grace time in shift, it has the higher priority - if shift.grace_time_id.is_active == True: + if ( + shift.grace_time_id.is_active == True + and shift.grace_time_id.allowed_clock_in == True + ): # Setting allowance for the check in time now_sec -= shift.grace_time_id.allowed_time_in_secs # checking default grace time @@ -79,8 +82,9 @@ def late_come(attendance, start_time, end_time, shift): is_default=True, is_active=True, ).first() - # Setting allowance for the check in time - now_sec -= grace_time.allowed_time_in_secs + # Setting allowance for the check in time if grace allocate for clock in event + if grace_time.allowed_clock_in: + now_sec -= grace_time.allowed_time_in_secs else: pass if start_time > end_time and start_time != end_time: @@ -377,7 +381,7 @@ def early_out_create(attendance): return late_come_obj -def early_out(attendance, start_time, end_time): +def early_out(attendance, start_time, end_time, shift): """ This method is used to mark the early check-out attendance before the shift ends args: @@ -385,9 +389,25 @@ def early_out(attendance, start_time, end_time): start_time : attendance day shift start time start_end : attendance day shift end time """ - now_sec = strtime_seconds(attendance.attendance_clock_out.strftime("%H:%M")) mid_day_sec = strtime_seconds("12:00") + # Checking gracetime allowance before creating early out + if shift and shift.grace_time_id: + if ( + shift.grace_time_id.is_active == True + and shift.grace_time_id.allowed_clock_out == True + ): + now_sec += shift.grace_time_id.allowed_time_in_secs + elif GraceTime.objects.filter(is_default=True, is_active=True).exists(): + grace_time = GraceTime.objects.filter( + is_default=True, + is_active=True, + ).first() + # Setting allowance for the check out time if grace allocate for clock out event + if grace_time.allowed_clock_out: + now_sec += grace_time.allowed_time_in_secs + else: + pass if start_time > end_time: # Early out condition for night shift if now_sec < mid_day_sec: @@ -443,7 +463,10 @@ def clock_out(request): 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 + attendance=attendance, + start_time=start_time_sec, + end_time=end_time_sec, + shift=shift, ) script = "" diff --git a/attendance/views/views.py b/attendance/views/views.py index edac86b56..2ed3a2d76 100644 --- a/attendance/views/views.py +++ b/attendance/views/views.py @@ -1759,39 +1759,41 @@ def delete_grace_time(request, grace_id): messages.error(request, _("Grace Time Does not exists..")) except ProtectedError: messages.error(request, _("Related datas exists.")) - if request.GET.get("view") == "shift": - return redirect("/settings/grace-settings-view") - else: - return redirect("/settings/grace-settings-view") + context = { + "condition": AttendanceValidationCondition.objects.first(), + "default_grace_time": GraceTime.objects.filter(is_default=True).first(), + "grace_times": GraceTime.objects.all().exclude(is_default=True), + } + + return render(request, "attendance/grace_time/grace_time_table.html", context) @login_required @permission_required("attendance.update_gracetime") -def update_isactive_gracetime(request): +def update_isactive_gracetime(request, obj_id): """ ajax function to update is active field in grace time. Args: - - isChecked: Boolean value representing the state of grace time, - - graceId: Id of grace time object + - is_active: Boolean value representing the state of grace time, + - obj_id: Id of grace time object """ - isChecked = request.POST.get("isChecked") - graceId = request.POST.get("graceId") - grace_time = GraceTime.objects.get(id=graceId) - if isChecked == "true": + is_active = request.POST.get("is_active") + grace_time = GraceTime.objects.get(id=obj_id) + if is_active == "on": grace_time.is_active = True - - response = { - "type": "success", - "message": _("Default grace time activated successfully."), - } + messages.success(request, _("Grace time activated successfully.")) else: grace_time.is_active = False - response = { - "type": "success", - "message": _("Default grace time deactivated successfully."), - } + messages.success(request, _("Grace time deactivated successfully.")) grace_time.save() - return JsonResponse(response) + + context = { + "condition": AttendanceValidationCondition.objects.first(), + "default_grace_time": GraceTime.objects.filter(is_default=True).first(), + "grace_times": GraceTime.objects.all().exclude(is_default=True), + } + + return render(request, "attendance/grace_time/grace_time_table.html", context) @login_required diff --git a/base/methods.py b/base/methods.py index 6491b58d7..3b5d1e453 100644 --- a/base/methods.py +++ b/base/methods.py @@ -22,6 +22,7 @@ from base.models import Company, DynamicPagination from employee.models import Employee, EmployeeWorkInformation from horilla.decorators import login_required from leave.models import LeaveRequest, LeaveRequestConditionApproval +from recruitment.models import Candidate def filtersubordinates(request, queryset, perm=None, field=None): @@ -589,6 +590,8 @@ def reload_queryset(fields): if isinstance(v, ModelChoiceField): if v.queryset.model == Employee: v.queryset = v.queryset.model.objects.filter(is_active=True) + elif v.queryset.model == Candidate: + v.queryset = v.queryset.model.objects.filter(is_active=True) else: v.queryset = v.queryset.model.objects.all() return diff --git a/horilla/horilla_settings.py b/horilla/horilla_settings.py index 655cf6dbf..44a4f83d7 100644 --- a/horilla/horilla_settings.py +++ b/horilla/horilla_settings.py @@ -3,7 +3,7 @@ from horilla import settings """ DB_INIT_PASSWORD: str -The password used for database setup and initialization. This password is a -48-character alphanumeric string generated using a UUID to ensure high entropy and security. +The password used for database setup and initialization. This password is a +48-character alphanumeric string generated using a UUID to ensure high entropy and security. """ DB_INIT_PASSWORD = ""