from datetime import date, datetime, timedelta from django.shortcuts import render, redirect from django.urls import reverse from django.utils.translation import gettext_lazy as _ from django.http import HttpResponseRedirect, HttpResponse, JsonResponse from horilla.decorators import permission_required, login_required, hx_request_required, manager_can_enter from base.methods import filtersubordinates, choosesubordinates, sortby from base.models import EmployeeShiftDay, EmployeeShift, EmployeeShiftSchedule, Department from attendance.forms import AttendanceForm, AttendanceOverTimeForm, AttendanceActivityForm, AttendanceLateComeEarlyOutForm, AttendanceValidationConditionForm, AttendanceUpdateForm from attendance.models import Attendance, AttendanceActivity, AttendanceOverTime, AttendanceLateComeEarlyOut, AttendanceValidationCondition from attendance.filters import AttendanceReGroup, AttendanceOvertimeReGroup, LateComeEarlyOutReGroup, AttendanceActivityReGroup from notifications.signals import notify from django.db.models import Q from django.core.paginator import Paginator from attendance.filters import AttendanceFilters, AttendanceOverTimeFilter, LateComeEarlyOutFilter, AttendanceActivityFilter from django.views.decorators.http import require_http_methods from django.contrib import messages from employee.models import Employee import json # Create your views here. def intersection_list(list1, list2): """ This method is used to intersect two list """ return [value for value in list1 if value in list2] def format_time(seconds): """ this method is used to formate seconds to H:M and return it args: seconds : seconds """ hour = int(seconds//3600) minutes = int((seconds % 3600)//60) seconds = int((seconds % 3600) % 60) return "%02d:%02d" % (hour, minutes) def strtime_seconds(time): """ this method is used reconvert time in H:M formate string back to seconds and return it args: time : time in H:M format """ ftr = [3600, 60, 1] return sum(a*b for a, b in zip(ftr, map(int, time.split(':')))) def is_reportingmanger(request, instance): """ if the instance have employee id field then you can use this method to know the request user employee is the reporting manager of the instance args : request : request instance : an object or instance of any model contain employee_id foreign key field """ manager = request.user.employee_get try: employee_workinfo_manager = instance.employee_id.employee_work_info.reporting_manager_id except Exception: return HttpResponse('This Employee Dont Have any work information') return manager == employee_workinfo_manager def late_come_create(attendance): """ used to create late come report args: attendance : attendance object """ late_come = AttendanceLateComeEarlyOut() late_come.type = 'late_come' late_come.attendance_id = attendance late_come.employee_id = attendance.employee_id late_come.save() return def late_come(attendance, start_time, end_time): """ this method is used to mark the late check-in attendance after the shift starts args: attendance : attendance obj start_time : attendance day shift start time end_time : attendance day shift end time """ now_sec = strtime_seconds(datetime.now().strftime('%H:%M')) mid_day_sec = strtime_seconds('12:00') if start_time > end_time and start_time != end_time: """ night shift """ if now_sec < mid_day_sec: """ Here attendance or attendance activity for new day night shift """ late_come_create(attendance) elif now_sec > start_time: """ Here attendance or attendance activity for previous day night shift """ late_come_create(attendance) elif start_time < now_sec: late_come_create(attendance) return def early_out_create(attendance): """ Used to create early out report args: attendance : attendance obj """ late_come = AttendanceLateComeEarlyOut() late_come.type = 'early_out' late_come.attendance_id = attendance late_come.employee_id = attendance.employee_id late_come.save() return def early_out(attendance, start_time, end_time): """ This method is used to mark the early check-out attendance before the shift ends args: attendance : attendance obj start_time : attendance day shift start time start_end : attendance day shift end time """ now_sec = strtime_seconds(datetime.now().strftime('%H:%M')) mid_day_sec = strtime_seconds('12:00') if start_time > end_time: """ Early out condition for night shift """ if now_sec < mid_day_sec: if now_sec < end_time: """ Early out condition for general shift """ early_out_create(attendance) else: early_out_create(attendance) return if end_time > now_sec: early_out_create(attendance) return def attendance_validate(attendance): """ This method is is used to check condition for at work in AttendanceValidationCondition model instance it return true if at work is smaller than condition args: attendance : attendance object """ conditions = AttendanceValidationCondition.objects.all() # Set the default condition for 'at work' to 9:00 AM condition_for_at_work = strtime_seconds('09:00') if conditions.exists(): condition_for_at_work = strtime_seconds( conditions[0].validation_at_work) at_work = strtime_seconds(attendance.attendance_worked_hour) return condition_for_at_work >= at_work @login_required @manager_can_enter('attendance.add_attendance') def attendance_create(request): """ This method is used to render attendance create form and save if it is valid """ form = AttendanceForm() form = choosesubordinates(request, form, 'attendance.add_attendance') if request.method == 'POST': form = AttendanceForm(request.POST) form = choosesubordinates(request, form, 'attendance.add_attendance') if form.is_valid(): form.save() messages.success(request, _('Attendance added.')) response = render( request, 'attendance/attendance/form.html', {'form': form}) return HttpResponse(response.content.decode('utf-8') + '') return render(request, 'attendance/attendance/form.html', {'form': form}) def paginator_qry(qryset, page_number): """ This method is used to paginate queryset """ paginator = Paginator(qryset, 50) qryset = paginator.get_page(page_number) return qryset @login_required @manager_can_enter('attendance.view_attendance') def attendance_view(request): """f This method is used to view attendances. """ employee = Employee.objects.filter(employee_user_id=request.user).first() previous_data = request.environ['QUERY_STRING'] form = AttendanceForm() condition = AttendanceValidationCondition.objects.first() minot = strtime_seconds('00:30') if condition is not None: minot = strtime_seconds(condition.minimum_overtime_to_approve) validate_attendances = Attendance.objects.filter( attendance_validated=False) attendances = Attendance.objects.filter(attendance_validated=True) ot_attendances = Attendance.objects.filter( attendance_overtime_approve=False, overtime_second__gte=minot, attendance_validated=True) f = AttendanceFilters(queryset=Attendance.objects.all()) attendances = filtersubordinates( request, attendances, 'attendance.view_attendance') validate_attendances = filtersubordinates( request, validate_attendances, 'attendance.view_attendance') ot_attendances = filtersubordinates( request, ot_attendances, 'attendance.view_attendance') return render(request, 'attendance/attendance/attendance_view.html', { 'form': form, 'validate_attendances': paginator_qry(validate_attendances, request.GET.get('vpage')), 'attendances': paginator_qry(attendances, request.GET.get('page')), 'overtime_attendances': paginator_qry(ot_attendances, request.GET.get('opage')), 'f': f, 'pd': previous_data, 'gp_fields': AttendanceReGroup.fields }) @login_required @manager_can_enter('attendance.view_attendance') def attendance_search(request): """ This method is used to search attendance by employee """ previous_data = request.environ['QUERY_STRING'] field = request.GET.get('field') minot = strtime_seconds('00:30') condition = AttendanceValidationCondition.objects.first() if condition is not None: minot = strtime_seconds(condition.minimum_overtime_to_approve) validate_attendances = Attendance.objects.filter(attendance_validated=False) attendances = Attendance.objects.filter(attendance_validated=True) ot_attendances = Attendance.objects.filter(attendance_overtime_approve=False, overtime_second__gte=minot, attendance_validated=True,) validate_attendances = AttendanceFilters( request.GET, validate_attendances).qs attendances = AttendanceFilters(request.GET, attendances).qs ot_attendances = AttendanceFilters(request.GET, ot_attendances).qs template = 'attendance/attendance/tab_content.html' if field != '' and field is not None: field_copy = field.replace('.', '__') attendances = attendances.order_by(field_copy) validate_attendances = validate_attendances.order_by(field_copy) ot_attendances = ot_attendances.order_by(field_copy) template = 'attendance/attendance/group_by.html' attendances = filtersubordinates( request, attendances, 'attendance.view_attendance') validate_attendances = filtersubordinates( request, validate_attendances, 'attendance.view_attendance') ot_attendances = filtersubordinates( request, ot_attendances, 'attendance.view_attendance') attendances = sortby(request, attendances, 'sortby') validate_attendances = sortby(request, validate_attendances, 'sortby') ot_attendances = sortby(request, ot_attendances, 'sortby') return render(request, template, { 'validate_attendances': paginator_qry(validate_attendances, request.GET.get('vpage')), 'attendances': paginator_qry(attendances, request.GET.get('page')), 'overtime_attendances': paginator_qry(ot_attendances, request.GET.get('opage')), 'pd': previous_data, 'field': field, }) @login_required @manager_can_enter('attendance.change_attendance') def attendance_update(request, id): """ This method render form to update attendance and save if the form is valid args: id : attendance id """ attendance = Attendance.objects.get(id=id) form = AttendanceUpdateForm(instance=attendance,) form = choosesubordinates(request, form, 'attendance.change_attendance') if request.method == 'POST': form = AttendanceUpdateForm(request.POST, instance=attendance) form = choosesubordinates( request, form, 'attendance.change_attendance') if form.is_valid(): form.save() messages.success(request, _('Attendance Updated.')) response = render( request, 'attendance/attendance/update_form.html', {'form': form}) return HttpResponse(response.content.decode('utf-8') + '') return render(request, 'attendance/attendance/update_form.html', {'form': form, }) @login_required @permission_required('attendance.delete_attendance') @require_http_methods(['POST']) def attendance_delete(request, id): """ This method is used to delete attendance. args: id : attendance id """ attendance = Attendance.objects.get(id=id) month = attendance.attendance_date month = month.strftime('%B').lower() overtime = attendance.employee_id.employee_overtime.filter( month=month).last() if overtime is not None: if attendance.attendance_overtime_approve: """ Subtract overtime of this attendance """ total_overtime = strtime_seconds(overtime.overtime) attendance_overtime_seconds = strtime_seconds( attendance.attendance_overtime) if total_overtime > attendance_overtime_seconds: total_overtime = total_overtime - attendance_overtime_seconds else: total_overtime = attendance_overtime_seconds - total_overtime overtime.overtime = format_time(total_overtime) overtime.save() try: attendance.delete() messages.success(request, _('Attendance deleted.')) except Exception as e: messages.error(request, e) messages.error(request, _('You cannot delete this attendance')) return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) @require_http_methods(['POST']) def attendance_bulk_delete(request): """ This method is used to delete bulk of attendances """ ids = request.POST['ids'] ids = json.loads(ids) for id in ids: try: attendance = Attendance.objects.get(id=id) month = attendance.attendance_date month = month.strftime('%B').lower() overtime = attendance.employee_id.employee_overtime.filter( month=month).last() if overtime is not None: if attendance.attendance_overtime_approve: """ Subtract overtime of this attendance """ total_overtime = strtime_seconds(overtime.overtime) attendance_overtime_seconds = strtime_seconds( attendance.attendance_overtime) if total_overtime > attendance_overtime_seconds: total_overtime = total_overtime - attendance_overtime_seconds else: total_overtime = attendance_overtime_seconds - total_overtime overtime.overtime = format_time(total_overtime) overtime.save() try: attendance.delete() messages.success(request, _('Attendance Deleted')) except: messages.error( request, _('You cannot delete this %(attendance)s' % {'attendance':attendance})) except: pass return JsonResponse({'message': 'Success'}) @login_required def view_my_attendance(request): """ This method is used to view self attendances of employee """ user = request.user try: employee = user.employee_get except: return redirect('/employee/employee-profile') employee = user.employee_get employee_attendances = employee.employee_attendances.all() filter = AttendanceFilters() return render(request, 'attendance/own_attendance/view_own_attendances.html', {'attendances': paginator_qry(employee_attendances, request.GET.get('page')), 'f': filter}) @login_required @hx_request_required def filter_own_attendance(request): """ This method is used to filter own attendances """ attendances = Attendance.objects.filter( employee_id=request.user.employee_get) attendances = AttendanceFilters(request.GET, queryset=attendances).qs return render(request, 'attendance/own_attendance/attendances.html', {'attendances': paginator_qry(attendances, request.GET.get('page'))}) @login_required def own_attendance_sort(request): """ This method is used to sort out attendances """ attendances = Attendance.objects.filter( employee_id=request.user.employee_get) previous_data = request.environ['QUERY_STRING'] attendances = sortby(request, attendances, 'orderby') return render(request, 'attendance/own_attendance/attendances.html', {'attendances': paginator_qry(attendances, request.GET.get('page')), 'pd': previous_data}) @login_required @manager_can_enter('attendance.add_attendanceovertime') def attendance_overtime_create(request): """ This method is used to render overtime creating form and save if the form is valid """ form = AttendanceOverTimeForm() form = choosesubordinates( request, form, 'attendance.add_attendanceovertime') if request.method == 'POST': form = AttendanceOverTimeForm(request.POST) form = choosesubordinates( request, form, 'attendance.add_attendanceovertime') if form.is_valid(): form.save() messages.success(request, _('Attendance account added.')) response = render( request, 'attendance/attendance_account/form.html', {'form': form}) return HttpResponse(response.content.decode('utf-8') + '') return render(request, 'attendance/attendance_account/form.html', {'form': form}) @login_required @manager_can_enter('attendance.view_attendanceovertime') def attendance_overtime_view(request): """ This method is used to view attendance account or overtime account. """ previous_data = request.environ['QUERY_STRING'] accounts = AttendanceOverTime.objects.all() accounts = filtersubordinates( request, accounts, 'attendance.view_attendanceovertime') form = AttendanceOverTimeForm() form = choosesubordinates( request, form, 'attendance.add_attendanceovertime') f = AttendanceOverTimeFilter() return render(request, 'attendance/attendance_account/attendance_overtime_view.html', { 'accounts': paginator_qry(accounts, request.GET.get('page')), 'form': form, 'pd': previous_data, 'f': f, 'gp_fields': AttendanceOvertimeReGroup.fields }) @login_required @manager_can_enter('attendance.view_attendanceovertime') def attendance_overtime_search(request): """ This method is used to search attendance overtime account by employee. """ field = request.GET.get('field') previous_data = request.environ['QUERY_STRING'] accounts = AttendanceOverTimeFilter(request.GET).qs form = AttendanceOverTimeForm() template = 'attendance/attendance_account/overtime_list.html' if field != '' and field is not None: field_copy = field.replace('.', '__') accounts = accounts.order_by(field_copy) template = 'attendance/attendance_account/group_by.html' accounts = sortby(request, accounts, 'sortby') accounts = filtersubordinates( request, accounts, 'attendance.view_attendanceovertime') return render(request, template, { 'accounts': paginator_qry(accounts, request.GET.get('page')), 'form': form, 'pd': previous_data, 'field': field, }) @login_required @manager_can_enter('attendance.change_attendanceovertime') @hx_request_required def attendance_overtime_update(request, id): """ This method is used to update attendance overtime and save if the forms is valid args: id : attendance overtime id """ overtime = AttendanceOverTime.objects.get(id=id) form = AttendanceOverTimeForm(instance=overtime) form = choosesubordinates( request, form, 'attendance.change_attendanceovertime') if request.method == 'POST': form = AttendanceOverTimeForm(request.POST, instance=overtime) form = choosesubordinates( request, form, 'attendance.change_attendanceovertime') if form.is_valid(): form.save() messages.success( request, _('Attendance account updated successfully.')) response = render( request, 'attendance/attendance_account/update_form.html', {'form': form}) return HttpResponse(response.content.decode('utf-8') + '') return render(request, 'attendance/attendance_account/update_form.html', {'form': form}) @login_required @permission_required('attendance.delete_AttendanceOverTime') @require_http_methods(['POST']) def attendance_overtime_delete(request, id): """ This method is used to delete attendance overtime args: id : attendance overtime id """ try: overtime = AttendanceOverTime.objects.get(id=id).delete() messages.success(request, _('OT account deleted.')) except Exception as e: messages.error(request, e) messages.error(request, _('You cannot delete this attendance OT')) accounts = AttendanceOverTime.objects.all() return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) @login_required @permission_required('attendance.view_attendanceactivity') def attendance_activity_view(request): """ This method will render a template to view all attendance activities """ attendance_activities = AttendanceActivity.objects.all() previous_data = request.environ['QUERY_STRING'] filter = AttendanceActivityFilter() return render(request, 'attendance/attendance_activity/attendance_activity_view.html', { 'data': paginator_qry(attendance_activities, request.GET.get('page')), 'pd': previous_data, 'f': filter, 'gp_fields': AttendanceActivityReGroup.fields} ) @login_required @permission_required('attendance.view_attendanceactivity') def attendance_activity_search(request): """ This method is used to search attendance activity """ previous_data = request.environ['QUERY_STRING'] field = request.GET.get('field') attendance_activities = AttendanceActivityFilter( request.GET,).qs template = 'attendance/attendance_activity/activity_list.html' if field != '' and field is not None: field_copy = field.replace('.', '__') attendance_activities = attendance_activities.order_by(field_copy) template = 'attendance/attendance_activity/group_by.html' attendance_activities = filtersubordinates( request, attendance_activities, 'attendance.view_attendanceactivity') attendance_activities = sortby(request, attendance_activities, 'orderby') return render(request, template, { 'data': paginator_qry(attendance_activities, request.GET.get('page')), 'pd': previous_data, 'field': field }) @login_required @permission_required('attendance.delete_attendanceactivity') @require_http_methods(['POST', 'DELTE']) def attendance_activity_delete(request, id): """ This method is used to delete attendance activity args: id : attendance activity id """ try: attendance_activity = AttendanceActivity.objects.get(id=id).delete() messages.success(request, _('Attendance activity deleted')) except Exception as e: messages.error(request, e) messages.error(request, _('You cannot delete this activity')) return redirect('/attendance/attendance-activity-view') def employee_exists(request): """ This method return the employee instance and work info if not exists return None instead """ employee, employee_work_info = None, None try: employee = request.user.employee_get employee_work_info = employee.employee_work_info finally: return (employee, employee_work_info) def shift_schedule_today(day, shift): """ This function is used to find shift schedules for the day, it will returns min hour,start time seconds end time seconds args: shift : shift instance day : shift day object """ schedule_today = day.day_schedule.filter(shift_id=shift) start_time_sec, end_time_sec, minimum_hour = 0, 0, '00:00' if schedule_today.exists(): schedule_today = schedule_today[0] minimum_hour = schedule_today.minimum_working_hour start_time_sec = strtime_seconds( schedule_today.start_time.strftime('%H:%M')) end_time_sec = strtime_seconds( schedule_today.end_time.strftime('%H:%M')) return (minimum_hour, start_time_sec, end_time_sec) def overtime_calculation(attendance): """ This method is used to calculate overtime of the attendance, it will return difference between attendance worked hour and minimum hour if and only worked hour greater than minimum hour, else return 00:00 args: attendance : attendance instance """ minimum_hour = attendance.minimum_hour at_work = attendance.attendance_worked_hour at_work_sec = strtime_seconds(at_work) minimum_hour_sec = strtime_seconds(minimum_hour) if at_work_sec > minimum_hour_sec: return format_time((at_work_sec - minimum_hour_sec)) return '00:00' def clock_in_attendance_and_activity(employee, date_today, attendance_date, day, now, shift, minimum_hour, start_time, end_time): """ This method is used to create attendance activity or attendance when an employee clocks-in args: employee : employee instance date_today : date attendance_date : the date that attendance for day : shift day now : current time shift : shift object minimum_hour : minimum hour in shift schedule start_time : start time in shift schedule end_time : end time in shift schedule """ # attendance activity create attendance_activity = AttendanceActivity( employee_id=employee, attendance_date=attendance_date, clock_in_date=date_today, shift_day=day, clock_in=now, ).save() # create attendance if not exist attendance = Attendance.objects.filter( employee_id=employee, attendance_date=attendance_date) if not attendance.exists(): attendance = Attendance() attendance.employee_id = employee attendance.shift_id = shift attendance.work_type_id = attendance.employee_id.employee_work_info.work_type_id attendance.attendance_date = attendance_date attendance.attendance_day = day attendance.attendance_clock_in = now attendance.attendance_clock_in_date = date_today 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) else: attendance = attendance[0] attendance.attendance_clock_out = None attendance.attendance_clock_out_date = None attendance.save() """ delete if the attendance marked the early out """ early_out_instance = attendance.late_come_early_out.filter( type='early_out') if early_out_instance.exists(): early_out_instance[0].delete() return @login_required def clock_in(request): """ This method is used to mark the attendance once per a day and multiple attendance activities. """ employee, work_info = employee_exists(request) if employee and work_info is not None: shift = work_info.shift_id date_today = date.today() attendance_date = date_today day = date_today.strftime('%A').lower() day = EmployeeShiftDay.objects.get(day=day) now = datetime.now().strftime('%H:%M') now_sec = strtime_seconds(now) mid_day_sec = strtime_seconds('12:00') minimum_hour, start_time_sec, end_time_sec = shift_schedule_today( day=day, shift=shift) if start_time_sec >= end_time_sec: # night shift """ Night shift in open hrms consider a 24 hours from noon to next day noon, the shift day taken today if the attendance clocked in after 12 O clock. """ if mid_day_sec > now_sec: """ Here you need to create attendance for yesterday """ date_yesterday = date_today - timedelta(days=1) day_yesterday = date_yesterday.strftime('%A').lower() day_yesterday = EmployeeShiftDay.objects.get(day=day_yesterday) minimum_hour, start_time_sec, end_time_sec = shift_schedule_today( day=day_yesterday, shift=shift) attendance_date = date_yesterday day = day_yesterday clock_in_attendance_and_activity( employee=employee, date_today=date_today, attendance_date=attendance_date, day=day, now=now, shift=shift, minimum_hour=minimum_hour, start_time=start_time_sec, end_time=end_time_sec ) return HttpResponse( """ """.format(check_out=_('Check-Out')) ) return HttpResponse("You Don't have work information filled or your employee detail neither entered ") def activity_datetime(attendance_activity): """ This method is used to convert clock-in and clock-out of activity as datetime object args: attendance_activity : attendance activity instance """ # in in_year = attendance_activity.clock_in_date.year in_month = attendance_activity.clock_in_date.month in_day = attendance_activity.clock_in_date.day in_hour = attendance_activity.clock_in.hour in_minute = attendance_activity.clock_in.minute # out out_year = attendance_activity.clock_out_date.year out_month = attendance_activity.clock_out_date.month out_day = attendance_activity.clock_out_date.day out_hour = attendance_activity.clock_out.hour out_minute = attendance_activity.clock_out.minute return datetime(in_year, in_month, in_day, in_hour, in_minute), datetime(out_year, out_month, out_day, out_hour, out_minute) def clock_out_attendance_and_activity(employee, date_today, now): """ Clock out the attendance and activity args: employee : employee instance date_today : today date now : now """ attendance_activities = AttendanceActivity.objects.filter( employee_id=employee).order_by('attendance_date', 'id') if attendance_activities.exists(): attendance_activity = attendance_activities.last() attendance_activity.clock_out = now attendance_activity.clock_out_date = date_today attendance_activity.save() attendance_activities = attendance_activities.filter(~Q(clock_out=None)).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 attendance.attendance_clock_out_date = date_today attendance.attendance_worked_hour = duration attendance.save() """ Overtime calculation """ attendance.attendance_overtime = overtime_calculation(attendance) """ Validate the attendance as per the condition """ attendance.attendance_validated = attendance_validate(attendance) attendance.save() return @login_required def clock_out(request): """ This method is used to set the out date and time for attendance and attendance activity """ employee, work_info = employee_exists(request) shift = work_info.shift_id date_today = date.today() day = date_today.strftime('%A').lower() day = EmployeeShiftDay.objects.get(day=day) attendance = Attendance.objects.filter( employee_id=employee).order_by('id', 'attendance_date').last() if attendance is not None: day = attendance.attendance_day now = datetime.now().strftime('%H:%M') minimum_hour, start_time_sec, end_time_sec = shift_schedule_today( day=day, shift=shift) now_sec = strtime_seconds(now) mid_day_sec = strtime_seconds('12:00') if start_time_sec >= end_time_sec and mid_day_sec > now_sec: # here the shift will be night shift """ Night shift in open hrms consider a 24 hours from noon to next day noon, the shift day taken today if the attendance clocked-in before 12 O clock. """ # here can write night shift conditions 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) clock_out_attendance_and_activity( employee=employee, date_today=date_today, now=now) return HttpResponse( """ """.format(check_in=_('Check-In')) ) @login_required @manager_can_enter('attendance.view_attendancelatecomeearlyout') def late_come_early_out_view(request): """ This method render template to view all late come early out entries """ reports = AttendanceLateComeEarlyOut.objects.all() reports = filtersubordinates( request, reports, 'attendance.view_attendancelatecomeearlyout') f = LateComeEarlyOutFilter() return render(request, 'attendance/late_come_early_out/reports.html', { 'data': paginator_qry(reports, request.GET.get('page')), 'f': f, 'gp_fields': LateComeEarlyOutReGroup.fields, }) @login_required @manager_can_enter('attendance.view_attendancelatecomeearlyout') def late_come_early_out_search(request): """ This method is used to search late come early out by employee. Also include filter and pagination. """ field = request.GET.get('field') previous_data = request.environ['QUERY_STRING'] reports = LateComeEarlyOutFilter(request.GET,).qs template = 'attendance/late_come_early_out/report_list.html' if field != '' and field is not None: template = 'design\late_come_early_out\group_by.html' field_copy = field.replace('.', '__') reports = reports.order_by(field_copy) reports = filtersubordinates( request, reports, 'attendance.view_attendancelatecomeearlyout') reports = sortby(request, reports, 'sortby') return render(request, template, { 'data': paginator_qry(reports, request.GET.get('page')), 'pd': previous_data, 'field': field }) @login_required @permission_required('attendance.delete_attendancelatecomeearlyout') @require_http_methods(['POST']) def late_come_early_out_delete(request, id): """ This method is used to delete the late come early out instance args: id : late come early out instance id """ try: late_come_early_out = AttendanceLateComeEarlyOut.objects.get( id=id).delete() messages.success(request, _("Late-in early-out deleted")) except Exception as e: messages.error(request, e) messages.error(request, _('You cannot delete this Late-in early-out')) return redirect('/attendance/late-come-early-out-view') @login_required @permission_required('attendance.add_attendancevalidationcondition') def validation_condition_create(request): """ This method render a form to create attendance validation conditions, and create if the form is valid. """ form = AttendanceValidationConditionForm() condition = AttendanceValidationCondition.objects.first() if request.method == 'POST': form = AttendanceValidationConditionForm(request.POST) if form.is_valid(): form.save() return render(request, 'attendance/break_point/condition.html', {'form': form, 'condition': condition}) @login_required @permission_required('attendance.change_attendancevalidationcondition') def validation_condition_update(request, id): """ This method is used to update validation condition args: id : validation condition instance id """ condition = AttendanceValidationCondition.objects.get(id=id) form = AttendanceValidationConditionForm(instance=condition) if request.method == 'POST': form = AttendanceValidationConditionForm( request.POST, instance=condition) if form.is_valid(): form.save() return render(request, 'attendance/break_point/condition.html', {'form': form, 'condition': condition}) @login_required @permission_required('attendance.change_attendancevalidationcondition') @require_http_methods(['POST']) def validation_condition_delete(request, id): """ This method is used to delete created validation condition args: id : validation condition id """ try: condition = AttendanceValidationCondition.objects.get(id=id).delete() messages.success(request, _('validation condition deleted.')) except Exception as e: messages.error(request, e) messages.error(request, _('You cannot delete this validation condition.')) return redirect('/attendance/validation-condition-view') @login_required @require_http_methods(['POST']) @manager_can_enter('attendance.change_attendance') def validate_bulk_attendance(request): """ This method is used to validate bulk of attendances """ ids = request.POST['ids'] ids = json.loads(ids) for id in ids: attendance = Attendance.objects.get(id=id) attendance.attendance_validated = True attendance.save() messages.success(request, _('Attendance validated.')) notify.send(request.user.employee_get, recipient=attendance.employee_id.employee_user_id, verb=f"Your attendance for the date {attendance.attendance_date} is validated", redirect="/attendance/view-my-attendance", icon="checkmark") return JsonResponse({'message': f'{attendance.employee_id} success'}) @login_required def validate_this_attendance(request, id): """ This method is used to validate attendance args: id : attendance id """ attendance = Attendance.objects.get(id=id) if is_reportingmanger(request, attendance) or request.user.has_perm('attendance.change_attendance'): attendance = Attendance.objects.get(id=id) attendance.attendance_validated = True attendance.save() messages.success(request, _('Attendance validated.')) notify.send(request.user.employee_get, recipient=attendance.employee_id.employee_user_id, verb=f"Your attendance for the date {attendance.attendance_date} is validated", redirect="/attendance/view-my-attendance", icon="checkmark") return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) return HttpResponse('You Dont Have Permission') @login_required def revalidate_this_attendance(request, id): """ This method is used to not validate the attendance. args: id : attendance id """ attendance = Attendance.objects.get(id=id) if is_reportingmanger(request, attendance) or request.user.has_perm('attendance.change_attendance'): attendance.attendance_validated = False attendance.save() try: notify.send(request.user.employee_get, recipient=attendance.employee_id.employee_work_info.reporting_manager_id.employee_user_id, verb=f"{attendance.employee_id} requested revalidation for {attendance.attendance_date} attendance", redirect="/attendance/view-my-attendance", icon="refresh") except Exception: pass return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) return HttpResponse('You Cannot Request for others attendance') @login_required @manager_can_enter('attendance.change_attendance') def approve_overtime(request, id): """ This method is used to approve attendance overtime args: id : attendance id """ attendance = Attendance.objects.get(id=id) attendance.attendance_overtime_approve = True attendance.save() notify.send(request.user.employee_get, recipient=attendance.employee_id.employee_user_id, verb=f"Your {attendance.attendance_date}'s attendance overtime approved.", redirect="/attendance/attendance-overtime-view", icon="checkmark") return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) @login_required @manager_can_enter('attendance.change_attendance') def approve_bulk_overtime(request): ids = request.POST['ids'] ids = json.loads(ids) for id in ids: attendance = Attendance.objects.get(id=id) attendance.attendance_overtime_approve = True attendance.save() messages.success(request, _('Overtime approved')) notify.send(request.user.employee_get, recipient=attendance.employee_id.employee_user_id, verb=f"Overtime approved for {attendance.attendance_date}'s attendance", redirect="/attendance/attendance-overtime-view", icon="checkmark") return JsonResponse({'message': 'Success'}) def find_on_time(request, today, week_day, department=None): """ This method is used to find count for on time attendances """ on_time = 0 attendances = Attendance.objects.filter(attendance_date=today) attendances = filtersubordinates( request, attendances, 'attendance.view_attendance') if department is not None: attendances = attendances.filter( employee_id__employee_work_info__department_id=department) excepted_attendances = 0 for attendance in attendances: shift = attendance.shift_id schedules_today = shift.employeeshiftschedule_set.filter( day__day=week_day) if schedules_today.first() is not None: excepted_attendances = excepted_attendances + 1 late_come = attendance.late_come_early_out.filter( type='late_come').first() if late_come is None: on_time = on_time + 1 return on_time def find_late_come(today, department=None): """ This method is used to find count of late comers """ late_come = AttendanceLateComeEarlyOut.objects.filter( type='late_come', attendance_id__attendance_date=today) if department is not None: late_come = late_come.filter( employee_id__employee_work_info__department_id=department) return len(late_come) def find_expected_attendances(week_day): """ This method is used to find count of expected attendances for the week day """ schedules_today = EmployeeShiftSchedule.objects.filter(day__day=week_day) expected_attendances = 0 for schedule in schedules_today: shift = schedule.shift_id expected_attendances = expected_attendances + \ len(shift.employeeworkinformation_set.all()) return expected_attendances def find_early_out(today, department=None): """ This method is used to find early out attendances and it returns query set """ if department is not None: early_out = AttendanceLateComeEarlyOut.objects.filter( type='early_out', employee_id__employee_work_info__department_id=department, attendance_id__attendance_date=today) else: early_out = AttendanceLateComeEarlyOut.objects.filter( type='early_out', attendance_id__attendance_date=today) return early_out @login_required def dashboard(request): """ This method is used to render individual dashboard for attendance module """ employees = Employee.objects.filter(is_active=True,).filter( ~Q(employee_work_info__shift_id=None)) total_employees = len(employees) today = datetime.today() week_day = today.strftime('%A').lower() on_time = find_on_time(request, today=today, week_day=week_day) late_come = find_late_come(today=today) marked_attendances = late_come + on_time expected_attendances = find_expected_attendances(week_day=week_day) on_time_ratio = 0 late_come_ratio = 0 marked_attendances_ratio = 0 if expected_attendances != 0: on_time_ratio = "%.1f" % ((on_time/expected_attendances)*100) late_come_ratio = "%.1f" % ((late_come/expected_attendances)*100) marked_attendances_ratio = "%.1f" % ( (marked_attendances/expected_attendances)*100) early_outs = AttendanceLateComeEarlyOut.objects.filter( type='early_out', attendance_id__attendance_date=today) return render(request, 'attendance/dashboard/dashboard.html', { 'total_employees': total_employees, 'on_time': on_time, 'on_time_ratio': on_time_ratio, 'late_come': late_come, 'late_come_ratio': late_come_ratio, 'expected_attendances': expected_attendances, 'marked_attendances': marked_attendances, 'marked_attendances_ratio': marked_attendances_ratio, 'on_break': early_outs, }) def generate_data_set(request, dept): """ This method is used to generate all the dashboard data """ today = datetime.today() week_day = today.strftime('%A').lower() # below method will find all the on-time attendance corresponding to the employee shift and shift schedule. on_time = find_on_time(request, today=today, week_day=week_day, department=dept) # below method will find all the late-come attendance corresponding to the employee shift and schedule. late_come = find_late_come(today=today, department=dept) # below method will find all the early-out attendance corresponding to the employee shift and shift schedule early_out = find_early_out(department=dept, today=today) data = {'label': dept.department, 'data': [ on_time, late_come, len(early_out)]} return data @login_required def dashboard_attendance(request): labels = ['On Time', 'Late Come', 'On Break', ] data_set = [] departments = Department.objects.all() for dept in departments: data_set.append(generate_data_set(request, dept)) return JsonResponse({'dataSet': data_set, 'labels': labels})