From a1cc8f327aa078c345524ceac0806198ebcbde58 Mon Sep 17 00:00:00 2001 From: Horilla Date: Fri, 12 Jan 2024 21:32:09 +0530 Subject: [PATCH] [UPDT] ATTENDANCE: Grace time calculation condition updates --- attendance/models.py | 66 ++++++++++++++++++- .../break_point/condition_form.html | 62 ++++++++++------- attendance/views/clock_in_out.py | 24 +++++-- 3 files changed, 123 insertions(+), 29 deletions(-) diff --git a/attendance/models.py b/attendance/models.py index 59e286f5a..1ae2ea2d6 100644 --- a/attendance/models.py +++ b/attendance/models.py @@ -62,12 +62,31 @@ def validate_time_format(value): raise ValidationError(_("Invalid format, it should be HH:MM format")) try: hour, minute = value.split(":") + if len(hour) > 3 or len (minute)>2: + raise ValidationError(_("Invalid time")) hour = int(hour) minute = int(minute) - if len(str(hour)) > 3 or minute not in range(60): - raise ValidationError(_("Invalid time")) + if len(str(hour)) > 3 or len(str(minute)) > 2 or minute not in range(60) : + raise ValidationError(_("Invalid time, excepted MM:SS")) except ValueError as error: raise ValidationError(_("Invalid format")) from error + +def validate_time_in_minutes(value): + """ + this method is used to validate the format of duration like fields. + """ + if len(value) > 5: + raise ValidationError(_("Invalid format, it should be MM:SS format")) + try: + minutes, sec = value.split(":") + if len(minutes) > 2 or len (sec)>2: + raise ValidationError(_("Invalid time, excepted MM:SS")) + minutes = int(minutes) + sec = int(sec) + if minutes not in range(60) or sec not in range(60): + raise ValidationError(_("Invalid time, excepted MM:SS")) + except ValueError as e: + raise ValidationError(_("Invalid format, excepted MM:SS")) from e def attendance_date_validate(date): @@ -876,3 +895,46 @@ def create_initial_stage(sender, instance, created, **kwargs): ) available.save() + +class GraceTime(models.Model): + """ + Model for saving Grace time + """ + allowed_time = models.CharField( + default="00:00", + validators=[validate_time_in_minutes], + max_length=10, + verbose_name=_("Allowed time"), + ) + allowed_time_in_secs = models.IntegerField() + is_default = models.BooleanField(default=False) + is_active = models.BooleanField(default=True) + company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) + objects = HorillaCompanyManager() + + def __str__(self) -> str: + return str(f"{self.allowed_time} - Minutes") + + def clean(self): + """ + This method is used to perform some custom validations + """ + super().clean() + if self.is_default: + if GraceTime.objects.filter(is_default=True).exclude(id=self.id).exists(): + raise ValidationError(_("There is already a default grace time that exists.")) + + allowed_time = self.allowed_time + if GraceTime.objects.filter(allowed_time=allowed_time).exclude(is_default=True).exists(): + raise ValidationError(_("There is already a grace time with this allowed time that exists.")) + + def save(self, *args, **kwargs): + allowed_time = self.allowed_time + minute, secs = allowed_time.split(":") + minute_int= int(minute) + secs_int= int(secs) + minute_str = f'{minute_int:02d}' + secs_str = f'{secs_int:02d}' + self.allowed_time = f'{minute_str}:{secs_str}' + self.allowed_time_in_secs = minute_int *60 + secs_int + super().save(*args, **kwargs) \ No newline at end of file diff --git a/attendance/templates/attendance/break_point/condition_form.html b/attendance/templates/attendance/break_point/condition_form.html index 2f25a3e9d..ef7042578 100644 --- a/attendance/templates/attendance/break_point/condition_form.html +++ b/attendance/templates/attendance/break_point/condition_form.html @@ -1,24 +1,42 @@ {% load i18n %} -
- {% csrf_token %} -
-

- {% trans "Attendance Condition" %} -

-
-
-
{{form}}
-
- - -
+ +
+
+ {% csrf_token %} +
+

+ {% trans "Attendance Condition" %} +

+
+
+
{{form}}
+
+ + +
+
+ + + diff --git a/attendance/views/clock_in_out.py b/attendance/views/clock_in_out.py index 2e673e0c4..bde0e600c 100644 --- a/attendance/views/clock_in_out.py +++ b/attendance/views/clock_in_out.py @@ -9,7 +9,8 @@ from django.http import HttpResponse from django.utils.translation import gettext_lazy as _ from base.models import EmployeeShiftDay from horilla.decorators import login_required -from attendance.models import Attendance, AttendanceActivity, AttendanceLateComeEarlyOut +from base.thread_local_middleware import _thread_locals +from attendance.models import Attendance, AttendanceActivity, AttendanceLateComeEarlyOut, GraceTime from attendance.views.views import ( activity_datetime, attendance_validate, @@ -35,8 +36,7 @@ def late_come_create(attendance): late_come_obj.save() return late_come_obj - -def late_come(attendance, start_time, end_time): +def late_come(attendance, start_time, end_time,shift): """ this method is used to mark the late check-in attendance after the shift starts args: @@ -45,9 +45,24 @@ def late_come(attendance, start_time, end_time): end_time : attendance day shift end time """ + request = getattr(_thread_locals,"request",None) now_sec = strtime_seconds(datetime.now().strftime("%H:%M")) mid_day_sec = strtime_seconds("12:00") + + # 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: + # Setting allowance for the check in time + now_sec -= shift.grace_time_id.allowed_time_in_secs + # checking default grace time + 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 in time + now_sec -= grace_time.allowed_time_in_secs + else: + pass if start_time > end_time and start_time != end_time: # night shift if now_sec < mid_day_sec: @@ -60,7 +75,6 @@ def late_come(attendance, start_time, end_time): late_come_create(attendance) return True - def clock_in_attendance_and_activity( employee, date_today, @@ -113,7 +127,7 @@ def clock_in_attendance_and_activity( attendance.minimum_hour = minimum_hour attendance.save() # check here late come or not - late_come(attendance=attendance, start_time=start_time, end_time=end_time) + late_come(attendance=attendance, start_time=start_time, end_time=end_time,shift=shift) else: attendance = attendance[0] attendance.attendance_clock_out = None