2024-10-10 16:30:22 +05:30
|
|
|
from datetime import date, datetime, timedelta, timezone
|
|
|
|
|
|
|
|
|
|
from django import template
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
from django.core.mail import EmailMessage
|
|
|
|
|
from django.db.models import Case, CharField, F, Value, When
|
2024-10-10 15:41:46 +05:30
|
|
|
from django.http import QueryDict
|
2024-10-10 16:30:22 +05:30
|
|
|
from django.shortcuts import get_object_or_404
|
|
|
|
|
from django.utils.decorators import method_decorator
|
|
|
|
|
from rest_framework.pagination import PageNumberPagination
|
2024-10-10 16:20:23 +05:30
|
|
|
from rest_framework.permissions import IsAuthenticated
|
2024-10-10 16:30:22 +05:30
|
|
|
from rest_framework.response import Response
|
|
|
|
|
from rest_framework.views import APIView
|
|
|
|
|
|
|
|
|
|
from attendance.models import Attendance, AttendanceActivity, EmployeeShiftDay
|
|
|
|
|
from attendance.views.clock_in_out import *
|
2024-10-15 17:06:39 +05:30
|
|
|
from attendance.views.clock_in_out import clock_out
|
2024-10-10 15:41:46 +05:30
|
|
|
from attendance.views.dashboard import (
|
|
|
|
|
find_expected_attendances,
|
|
|
|
|
find_late_come,
|
|
|
|
|
find_on_time,
|
|
|
|
|
)
|
|
|
|
|
from attendance.views.views import *
|
2024-10-10 16:30:22 +05:30
|
|
|
from base.backends import ConfiguredEmailBackend
|
|
|
|
|
from base.methods import generate_pdf, is_reportingmanager
|
2025-02-28 18:57:58 +05:30
|
|
|
from base.models import HorillaMailTemplate
|
2024-10-10 16:30:22 +05:30
|
|
|
from employee.filters import EmployeeFilter
|
|
|
|
|
|
2024-10-15 17:06:39 +05:30
|
|
|
from ...api_decorators.base.decorators import (
|
|
|
|
|
manager_permission_required,
|
|
|
|
|
permission_required,
|
|
|
|
|
)
|
2024-10-10 16:20:23 +05:30
|
|
|
from ...api_methods.base.methods import groupby_queryset, permission_based_queryset
|
2024-10-10 16:30:22 +05:30
|
|
|
from ...api_serializers.attendance.serializers import (
|
2024-10-10 15:41:46 +05:30
|
|
|
AttendanceActivitySerializer,
|
|
|
|
|
AttendanceLateComeEarlyOutSerializer,
|
|
|
|
|
AttendanceOverTimeSerializer,
|
|
|
|
|
AttendanceRequestSerializer,
|
|
|
|
|
AttendanceSerializer,
|
|
|
|
|
MailTemplateSerializer,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Create your views here.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def query_dict(data):
|
|
|
|
|
query_dict = QueryDict("", mutable=True)
|
|
|
|
|
for key, value in data.items():
|
|
|
|
|
if isinstance(value, list):
|
|
|
|
|
for item in value:
|
|
|
|
|
query_dict.appendlist(key, item)
|
|
|
|
|
else:
|
|
|
|
|
query_dict.update({key: value})
|
|
|
|
|
return query_dict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ClockInAPIView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Allows authenticated employees to clock in, determining the correct shift and attendance date, including handling night shifts.
|
|
|
|
|
|
|
|
|
|
Methods:
|
|
|
|
|
post(request): Processes and records the clock-in time.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
|
|
|
|
def post(self, request):
|
2025-05-22 15:48:58 +05:30
|
|
|
print("========", request.user.employee_get.check_online())
|
2025-05-02 10:18:58 +05:30
|
|
|
if not request.user.employee_get.check_online():
|
|
|
|
|
try:
|
|
|
|
|
if request.user.employee_get.get_company().geo_fencing.start:
|
|
|
|
|
from geofencing.views import GeoFencingEmployeeLocationCheckAPIView
|
|
|
|
|
|
|
|
|
|
location_api_view = GeoFencingEmployeeLocationCheckAPIView()
|
|
|
|
|
response = location_api_view.post(request)
|
|
|
|
|
if response.status_code != 200:
|
|
|
|
|
return response
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
employee, work_info = employee_exists(request)
|
|
|
|
|
datetime_now = datetime.now()
|
|
|
|
|
if request.__dict__.get("datetime"):
|
|
|
|
|
datetime_now = request.datetime
|
|
|
|
|
if employee and work_info is not None:
|
|
|
|
|
shift = work_info.shift_id
|
|
|
|
|
date_today = date.today()
|
|
|
|
|
if request.__dict__.get("date"):
|
|
|
|
|
date_today = request.date
|
|
|
|
|
attendance_date = date_today
|
|
|
|
|
day = date_today.strftime("%A").lower()
|
|
|
|
|
day = EmployeeShiftDay.objects.get(day=day)
|
|
|
|
|
now = datetime.now().strftime("%H:%M")
|
|
|
|
|
if request.__dict__.get("time"):
|
|
|
|
|
now = request.time.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 Horilla 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,
|
|
|
|
|
in_datetime=datetime_now,
|
|
|
|
|
)
|
|
|
|
|
return Response({"message": "Clocked-In"}, status=200)
|
|
|
|
|
return Response(
|
|
|
|
|
{
|
|
|
|
|
"error": "You Don't have work information filled or your employee detail neither entered "
|
|
|
|
|
}
|
2024-10-10 15:41:46 +05:30
|
|
|
)
|
2025-05-02 10:18:58 +05:30
|
|
|
return Response({"message": "Already clocked-in"}, status=400)
|
2024-10-10 15:41:46 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class ClockOutAPIView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Allows authenticated employees to clock out, updating the latest attendance record and handling early outs.
|
|
|
|
|
|
|
|
|
|
Methods:
|
|
|
|
|
post(request): Records the clock-out time.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
|
|
|
|
def post(self, request):
|
2024-10-15 17:06:39 +05:30
|
|
|
|
|
|
|
|
try:
|
2025-05-02 10:18:58 +05:30
|
|
|
if request.user.employee_get.get_company().geo_fencing.start:
|
|
|
|
|
from geofencing.views import GeoFencingEmployeeLocationCheckAPIView
|
|
|
|
|
|
|
|
|
|
location_api_view = GeoFencingEmployeeLocationCheckAPIView()
|
|
|
|
|
response = location_api_view.post(request)
|
|
|
|
|
if response.status_code != 200:
|
|
|
|
|
return response
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
if request.user.employee_get.check_online():
|
2025-05-19 10:40:08 +05:30
|
|
|
print("----------------")
|
2025-05-02 10:18:58 +05:30
|
|
|
current_date = date.today()
|
|
|
|
|
current_time = datetime.now().time()
|
|
|
|
|
current_datetime = datetime.now()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
clock_out(
|
|
|
|
|
Request(
|
|
|
|
|
user=request.user,
|
|
|
|
|
date=current_date,
|
|
|
|
|
time=current_time,
|
|
|
|
|
datetime=current_datetime,
|
|
|
|
|
)
|
2024-10-15 17:06:39 +05:30
|
|
|
)
|
2025-05-02 10:18:58 +05:30
|
|
|
return Response({"message": "Clocked-Out"}, status=200)
|
2024-10-10 15:41:46 +05:30
|
|
|
|
2025-05-02 10:18:58 +05:30
|
|
|
except Exception as error:
|
|
|
|
|
logger.error("Got an error in clock_out", error)
|
2025-05-19 10:40:08 +05:30
|
|
|
# return Response({"message": "Clocked-Out"}, status=200)
|
2025-05-02 10:18:58 +05:30
|
|
|
return Response({"message": "Already clocked-out"}, status=400)
|
2024-10-10 15:41:46 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
class AttendanceView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Handles CRUD operations for attendance records.
|
|
|
|
|
|
|
|
|
|
Methods:
|
|
|
|
|
get_queryset(request, type): Returns filtered attendance records.
|
|
|
|
|
get(request, pk=None, type=None): Retrieves a specific record or a list of records.
|
|
|
|
|
post(request): Creates a new attendance record.
|
|
|
|
|
put(request, pk): Updates an existing attendance record.
|
|
|
|
|
delete(request, pk): Deletes an attendance record and adjusts related overtime if needed.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
filterset_class = AttendanceFilters
|
|
|
|
|
|
|
|
|
|
def get_queryset(self, request, type):
|
|
|
|
|
if type == "ot":
|
2024-10-15 14:41:40 +05:30
|
|
|
|
2024-10-10 15:41:46 +05:30
|
|
|
condition = AttendanceValidationCondition.objects.first()
|
|
|
|
|
minot = strtime_seconds("00:30")
|
|
|
|
|
if condition is not None:
|
|
|
|
|
minot = strtime_seconds(condition.minimum_overtime_to_approve)
|
|
|
|
|
queryset = Attendance.objects.filter(
|
|
|
|
|
overtime_second__gte=minot,
|
|
|
|
|
attendance_validated=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
elif type == "validated":
|
|
|
|
|
queryset = Attendance.objects.filter(attendance_validated=True)
|
|
|
|
|
elif type == "non-validated":
|
|
|
|
|
queryset = Attendance.objects.filter(attendance_validated=False)
|
|
|
|
|
else:
|
|
|
|
|
queryset = Attendance.objects.all()
|
|
|
|
|
user = request.user
|
|
|
|
|
# checking user level permissions
|
|
|
|
|
perm = "attendance.view_attendance"
|
|
|
|
|
queryset = permission_based_queryset(user, perm, queryset, user_obj=True)
|
|
|
|
|
return queryset
|
|
|
|
|
|
|
|
|
|
def get(self, request, pk=None, type=None):
|
|
|
|
|
# individual object workflow
|
|
|
|
|
if pk:
|
|
|
|
|
attendance = get_object_or_404(Attendance, pk=pk)
|
|
|
|
|
serializer = AttendanceSerializer(instance=attendance)
|
|
|
|
|
return Response(serializer.data, status=200)
|
|
|
|
|
# permission based querysete
|
|
|
|
|
attendances = self.get_queryset(request, type)
|
|
|
|
|
# filtering queryset
|
|
|
|
|
attendances_filter_queryset = self.filterset_class(
|
|
|
|
|
request.GET, queryset=attendances
|
|
|
|
|
).qs
|
|
|
|
|
field_name = request.GET.get("groupby_field", None)
|
|
|
|
|
if field_name:
|
|
|
|
|
url = request.build_absolute_uri()
|
|
|
|
|
return groupby_queryset(
|
|
|
|
|
request, url, field_name, attendances_filter_queryset
|
|
|
|
|
)
|
|
|
|
|
# pagination workflow
|
|
|
|
|
paginater = PageNumberPagination()
|
|
|
|
|
page = paginater.paginate_queryset(attendances_filter_queryset, request)
|
|
|
|
|
serializer = AttendanceSerializer(page, many=True)
|
|
|
|
|
return paginater.get_paginated_response(serializer.data)
|
|
|
|
|
|
|
|
|
|
@manager_permission_required("attendance.add_attendance")
|
|
|
|
|
def post(self, request):
|
|
|
|
|
serializer = AttendanceSerializer(data=request.data)
|
|
|
|
|
if serializer.is_valid():
|
2024-10-16 12:23:59 +05:30
|
|
|
serializer.save()
|
2024-10-10 15:41:46 +05:30
|
|
|
return Response(serializer.data, status=200)
|
2024-10-16 12:23:59 +05:30
|
|
|
employee_id = request.data.get("employee_id")
|
|
|
|
|
attendance_date = request.data.get("attendance_date", date.today())
|
|
|
|
|
if Attendance.objects.filter(
|
|
|
|
|
employee_id=employee_id, attendance_date=attendance_date
|
|
|
|
|
).exists():
|
|
|
|
|
return Response(
|
|
|
|
|
{
|
|
|
|
|
"error": [
|
|
|
|
|
"Attendance for this employee on the current date already exists."
|
|
|
|
|
]
|
2024-10-19 16:06:50 +05:30
|
|
|
},
|
|
|
|
|
status=400,
|
2024-10-16 12:23:59 +05:30
|
|
|
)
|
2024-10-10 15:41:46 +05:30
|
|
|
return Response(serializer.errors, status=400)
|
|
|
|
|
|
2024-10-10 16:30:22 +05:30
|
|
|
@method_decorator(permission_required("attendance.change_attendance"))
|
2024-10-10 15:41:46 +05:30
|
|
|
def put(self, request, pk):
|
|
|
|
|
try:
|
|
|
|
|
attendance = Attendance.objects.get(id=pk)
|
|
|
|
|
except Attendance.DoesNotExist:
|
|
|
|
|
return Response({"detail": "Attendance record not found."}, status=404)
|
|
|
|
|
|
|
|
|
|
serializer = AttendanceSerializer(instance=attendance, data=request.data)
|
|
|
|
|
|
|
|
|
|
if serializer.is_valid():
|
|
|
|
|
serializer.save()
|
|
|
|
|
return Response(serializer.data, status=200)
|
|
|
|
|
|
|
|
|
|
# Customize error message for unique constraint
|
|
|
|
|
serializer_errors = serializer.errors
|
|
|
|
|
if "non_field_errors" in serializer.errors:
|
|
|
|
|
unique_error_msg = (
|
|
|
|
|
"The fields employee_id, attendance_date must make a unique set."
|
|
|
|
|
)
|
|
|
|
|
if unique_error_msg in serializer.errors["non_field_errors"]:
|
|
|
|
|
serializer_errors = {
|
|
|
|
|
"non_field_errors": [
|
|
|
|
|
"The employee already has attendance on this date."
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
return Response(serializer_errors, status=400)
|
|
|
|
|
|
2024-10-10 16:30:22 +05:30
|
|
|
@method_decorator(permission_required("attendance.delete_attendance"))
|
2024-10-10 15:41:46 +05:30
|
|
|
def delete(self, request, pk):
|
|
|
|
|
attendance = Attendance.objects.get(id=pk)
|
|
|
|
|
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()
|
|
|
|
|
return Response({"status", "deleted"}, status=200)
|
|
|
|
|
except Exception as error:
|
|
|
|
|
return Response({"error:", f"{error}"}, status=400)
|
|
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
attendance.delete()
|
|
|
|
|
return Response({"status", "deleted"}, status=200)
|
|
|
|
|
except Exception as error:
|
|
|
|
|
return Response({"error:", f"{error}"}, status=400)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ValidateAttendanceView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Validates an attendance record and sends a notification to the employee.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
put(request, pk): Marks the attendance as validated and notifies the employee.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-05-05 16:41:55 +05:30
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
2024-10-10 15:41:46 +05:30
|
|
|
def put(self, request, pk):
|
|
|
|
|
attendance = Attendance.objects.filter(id=pk).update(attendance_validated=True)
|
|
|
|
|
attendance = Attendance.objects.filter(id=pk).first()
|
|
|
|
|
try:
|
|
|
|
|
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",
|
|
|
|
|
verb_ar=f"تم تحقيق حضورك في تاريخ {attendance.attendance_date}",
|
|
|
|
|
verb_de=f"Deine Anwesenheit für das Datum {attendance.attendance_date} ist bestätigt.",
|
|
|
|
|
verb_es=f"Se valida tu asistencia para la fecha {attendance.attendance_date}.",
|
|
|
|
|
verb_fr=f"Votre présence pour la date {attendance.attendance_date} est validée.",
|
|
|
|
|
redirect="/attendance/view-my-attendance",
|
|
|
|
|
icon="checkmark",
|
|
|
|
|
api_redirect=f"/api/attendance/attendance?employee_id{attendance.employee_id}",
|
|
|
|
|
)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
return Response(status=200)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OvertimeApproveView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Approves overtime for an attendance record and sends a notification to the employee.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
put(request, pk): Marks the overtime as approved and notifies the employee.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-05-05 16:41:55 +05:30
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
2024-10-10 15:41:46 +05:30
|
|
|
def put(self, request, pk):
|
|
|
|
|
try:
|
|
|
|
|
attendance = Attendance.objects.filter(id=pk).update(
|
|
|
|
|
attendance_overtime_approve=True
|
|
|
|
|
)
|
|
|
|
|
except Exception as E:
|
|
|
|
|
return Response({"error": str(E)}, status=400)
|
|
|
|
|
|
|
|
|
|
attendance = Attendance.objects.filter(id=pk).first()
|
|
|
|
|
try:
|
|
|
|
|
notify.send(
|
|
|
|
|
request.user.employee_get,
|
|
|
|
|
recipient=attendance.employee_id.employee_user_id,
|
|
|
|
|
verb=f"Your {attendance.attendance_date}'s attendance overtime approved.",
|
|
|
|
|
verb_ar=f"تمت الموافقة على إضافة ساعات العمل الإضافية لتاريخ {attendance.attendance_date}.",
|
|
|
|
|
verb_de=f"Die Überstunden für den {attendance.attendance_date} wurden genehmigt.",
|
|
|
|
|
verb_es=f"Se ha aprobado el tiempo extra de asistencia para el {attendance.attendance_date}.",
|
|
|
|
|
verb_fr=f"Les heures supplémentaires pour la date {attendance.attendance_date} ont été approuvées.",
|
|
|
|
|
redirect="/attendance/attendance-overtime-view",
|
|
|
|
|
icon="checkmark",
|
|
|
|
|
api_redirect="/api/attendance/attendance-hour-account/",
|
|
|
|
|
)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
return Response(status=200)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AttendanceRequestView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Handles requests for creating, updating, and viewing attendance records.
|
|
|
|
|
|
|
|
|
|
Methods:
|
|
|
|
|
get(request, pk=None): Retrieves a specific attendance request by `pk` or a filtered list of requests.
|
|
|
|
|
post(request): Creates a new attendance request.
|
|
|
|
|
put(request, pk): Updates an existing attendance request.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
serializer_class = AttendanceRequestSerializer
|
|
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
|
|
|
|
def get(self, request, pk=None):
|
|
|
|
|
if pk:
|
|
|
|
|
attendance = Attendance.objects.get(id=pk)
|
|
|
|
|
serializer = AttendanceRequestSerializer(instance=attendance)
|
|
|
|
|
return Response(serializer.data, status=200)
|
|
|
|
|
|
|
|
|
|
requests = Attendance.objects.filter(
|
|
|
|
|
is_validate_request=True,
|
|
|
|
|
)
|
|
|
|
|
requests = filtersubordinates(
|
|
|
|
|
request=request,
|
|
|
|
|
perm="attendance.view_attendance",
|
|
|
|
|
queryset=requests,
|
|
|
|
|
)
|
|
|
|
|
requests = requests | Attendance.objects.filter(
|
|
|
|
|
employee_id__employee_user_id=request.user,
|
|
|
|
|
is_validate_request=True,
|
|
|
|
|
)
|
|
|
|
|
request_filtered_queryset = AttendanceFilters(request.GET, requests).qs
|
|
|
|
|
field_name = request.GET.get("groupby_field", None)
|
|
|
|
|
if field_name:
|
|
|
|
|
# groupby workflow
|
|
|
|
|
url = request.build_absolute_uri()
|
|
|
|
|
return groupby_queryset(request, url, field_name, request_filtered_queryset)
|
|
|
|
|
|
|
|
|
|
pagenation = PageNumberPagination()
|
|
|
|
|
page = pagenation.paginate_queryset(request_filtered_queryset, request)
|
|
|
|
|
serializer = self.serializer_class(page, many=True)
|
|
|
|
|
return pagenation.get_paginated_response(serializer.data)
|
|
|
|
|
|
|
|
|
|
def post(self, request):
|
|
|
|
|
serializer = AttendanceRequestSerializer(data=request.data)
|
|
|
|
|
if serializer.is_valid():
|
|
|
|
|
serializer.save()
|
|
|
|
|
return Response(serializer.data, status=200)
|
2024-10-21 10:12:32 +05:30
|
|
|
employee_id = request.data.get("employee_id")
|
|
|
|
|
attendance_date = request.data.get("attendance_date", date.today())
|
|
|
|
|
if Attendance.objects.filter(
|
|
|
|
|
employee_id=employee_id, attendance_date=attendance_date
|
|
|
|
|
).exists():
|
|
|
|
|
return Response(
|
|
|
|
|
{
|
|
|
|
|
"error": [
|
|
|
|
|
"Attendance for this employee on the current date already exists."
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
status=400,
|
|
|
|
|
)
|
2024-10-10 15:41:46 +05:30
|
|
|
return Response(serializer.errors, status=404)
|
|
|
|
|
|
|
|
|
|
@manager_permission_required("attendance.update_attendance")
|
|
|
|
|
def put(self, request, pk):
|
|
|
|
|
attendance = Attendance.objects.get(id=pk)
|
|
|
|
|
serializer = AttendanceRequestSerializer(instance=attendance, data=request.data)
|
|
|
|
|
if serializer.is_valid():
|
|
|
|
|
instance = serializer.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()
|
|
|
|
|
return Response(serializer.data, status=200)
|
|
|
|
|
return Response(serializer.errors, status=404)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AttendanceRequestApproveView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Approves and updates an attendance request.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
put(request, pk): Approves the attendance request, updates attendance records, and handles related activities.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-05-05 16:41:55 +05:30
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
2024-10-10 15:41:46 +05:30
|
|
|
@manager_permission_required("attendance.change_attendance")
|
|
|
|
|
def put(self, request, pk):
|
|
|
|
|
try:
|
|
|
|
|
attendance = Attendance.objects.get(id=pk)
|
|
|
|
|
prev_attendance_date = attendance.attendance_date
|
|
|
|
|
prev_attendance_clock_in_date = attendance.attendance_clock_in_date
|
|
|
|
|
prev_attendance_clock_in = attendance.attendance_clock_in
|
|
|
|
|
attendance.attendance_validated = True
|
|
|
|
|
attendance.is_validate_request_approved = True
|
|
|
|
|
attendance.is_validate_request = False
|
|
|
|
|
attendance.request_description = None
|
|
|
|
|
attendance.save()
|
|
|
|
|
if attendance.requested_data is not None:
|
|
|
|
|
requested_data = json.loads(attendance.requested_data)
|
|
|
|
|
requested_data["attendance_clock_out"] = (
|
|
|
|
|
None
|
|
|
|
|
if requested_data["attendance_clock_out"] == "None"
|
|
|
|
|
else requested_data["attendance_clock_out"]
|
|
|
|
|
)
|
|
|
|
|
requested_data["attendance_clock_out_date"] = (
|
|
|
|
|
None
|
|
|
|
|
if requested_data["attendance_clock_out_date"] == "None"
|
|
|
|
|
else requested_data["attendance_clock_out_date"]
|
|
|
|
|
)
|
|
|
|
|
Attendance.objects.filter(id=pk).update(**requested_data)
|
|
|
|
|
# DUE TO AFFECT THE OVERTIME CALCULATION ON SAVE METHOD, SAVE THE INSTANCE ONCE MORE
|
|
|
|
|
attendance = Attendance.objects.get(id=pk)
|
|
|
|
|
attendance.save()
|
|
|
|
|
if (
|
|
|
|
|
attendance.attendance_clock_out is None
|
|
|
|
|
or attendance.attendance_clock_out_date is None
|
|
|
|
|
):
|
|
|
|
|
attendance.attendance_validated = True
|
|
|
|
|
activity = AttendanceActivity.objects.filter(
|
|
|
|
|
employee_id=attendance.employee_id,
|
|
|
|
|
attendance_date=prev_attendance_date,
|
|
|
|
|
clock_in_date=prev_attendance_clock_in_date,
|
|
|
|
|
clock_in=prev_attendance_clock_in,
|
|
|
|
|
)
|
|
|
|
|
if activity:
|
|
|
|
|
activity.update(
|
|
|
|
|
employee_id=attendance.employee_id,
|
|
|
|
|
attendance_date=attendance.attendance_date,
|
|
|
|
|
clock_in_date=attendance.attendance_clock_in_date,
|
|
|
|
|
clock_in=attendance.attendance_clock_in,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
AttendanceActivity.objects.create(
|
|
|
|
|
employee_id=attendance.employee_id,
|
|
|
|
|
attendance_date=attendance.attendance_date,
|
|
|
|
|
clock_in_date=attendance.attendance_clock_in_date,
|
|
|
|
|
clock_in=attendance.attendance_clock_in,
|
|
|
|
|
)
|
|
|
|
|
except Exception as E:
|
|
|
|
|
return Response({"error": str(E)}, status=400)
|
|
|
|
|
return Response({"status": "approved"}, status=200)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AttendanceRequestCancelView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Cancels an attendance request.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
put(request, pk): Cancels the attendance request, resetting its status and data, and deletes the request if it was a create request.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-05-05 16:41:55 +05:30
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
2024-10-10 15:41:46 +05:30
|
|
|
def put(self, request, pk):
|
|
|
|
|
try:
|
|
|
|
|
attendance = Attendance.objects.get(id=pk)
|
|
|
|
|
if (
|
|
|
|
|
attendance.employee_id.employee_user_id == request.user
|
|
|
|
|
or is_reportingmanager(request)
|
|
|
|
|
or request.user.has_perm("attendance.change_attendance")
|
|
|
|
|
):
|
|
|
|
|
attendance.is_validate_request_approved = False
|
|
|
|
|
attendance.is_validate_request = False
|
|
|
|
|
attendance.request_description = None
|
|
|
|
|
attendance.requested_data = None
|
|
|
|
|
attendance.request_type = None
|
|
|
|
|
|
|
|
|
|
attendance.save()
|
|
|
|
|
if attendance.request_type == "create_request":
|
|
|
|
|
attendance.delete()
|
|
|
|
|
except Exception as E:
|
|
|
|
|
return Response({"error": str(E)}, status=400)
|
|
|
|
|
return Response({"status": "success"}, status=200)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AttendanceOverTimeView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Manages CRUD operations for attendance overtime records.
|
|
|
|
|
|
|
|
|
|
Methods:
|
|
|
|
|
get(request, pk=None): Retrieves a specific overtime record by `pk` or a list of records with filtering and pagination.
|
|
|
|
|
post(request): Creates a new overtime record.
|
|
|
|
|
put(request, pk): Updates an existing overtime record.
|
|
|
|
|
delete(request, pk): Deletes an overtime record.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
|
|
|
|
def get(self, request, pk=None):
|
|
|
|
|
if pk:
|
|
|
|
|
attendance_ot = get_object_or_404(AttendanceOverTime, pk=pk)
|
|
|
|
|
serializer = AttendanceOverTimeSerializer(attendance_ot)
|
|
|
|
|
return Response(serializer.data, status=200)
|
|
|
|
|
|
|
|
|
|
filterset_class = AttendanceOverTimeFilter(request.GET)
|
|
|
|
|
queryset = filterset_class.qs
|
|
|
|
|
self_account = queryset.filter(employee_id__employee_user_id=request.user)
|
|
|
|
|
permission_based_queryset = filtersubordinates(
|
|
|
|
|
request, queryset, "attendance.view_attendanceovertime"
|
|
|
|
|
)
|
|
|
|
|
queryset = permission_based_queryset | self_account
|
|
|
|
|
field_name = request.GET.get("groupby_field", None)
|
|
|
|
|
if field_name:
|
|
|
|
|
# groupby workflow
|
|
|
|
|
url = request.build_absolute_uri()
|
|
|
|
|
return groupby_queryset(request, url, field_name, queryset)
|
|
|
|
|
|
|
|
|
|
pagenation = PageNumberPagination()
|
|
|
|
|
page = pagenation.paginate_queryset(queryset, request)
|
|
|
|
|
serializer = AttendanceOverTimeSerializer(page, many=True)
|
|
|
|
|
return pagenation.get_paginated_response(serializer.data)
|
|
|
|
|
|
|
|
|
|
@manager_permission_required("attendance.add_attendanceovertime")
|
|
|
|
|
def post(self, request):
|
|
|
|
|
serializer = AttendanceOverTimeSerializer(data=request.data)
|
|
|
|
|
if serializer.is_valid():
|
|
|
|
|
serializer.save()
|
|
|
|
|
return Response(serializer.data, status=200)
|
|
|
|
|
return Response(serializer.errors, status=400)
|
|
|
|
|
|
|
|
|
|
@manager_permission_required("attendance.change_attendanceovertime")
|
|
|
|
|
def put(self, request, pk):
|
|
|
|
|
attendance_ot = get_object_or_404(AttendanceOverTime, pk=pk)
|
|
|
|
|
serializer = AttendanceOverTimeSerializer(
|
|
|
|
|
instance=attendance_ot, data=request.data
|
|
|
|
|
)
|
|
|
|
|
if serializer.is_valid():
|
|
|
|
|
serializer.save()
|
|
|
|
|
return Response(serializer.data, status=200)
|
|
|
|
|
return Response(serializer.errors, status=400)
|
|
|
|
|
|
2024-10-10 16:30:22 +05:30
|
|
|
@method_decorator(permission_required("attendance.delete_attendanceovertime"))
|
2024-10-10 15:41:46 +05:30
|
|
|
def delete(self, request, pk):
|
|
|
|
|
attendance = get_object_or_404(AttendanceOverTime, pk=pk)
|
|
|
|
|
attendance.delete()
|
|
|
|
|
|
|
|
|
|
return Response({"message": "Overtime deleted successfully"}, status=204)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class LateComeEarlyOutView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Handles retrieval and deletion of late come and early out records.
|
|
|
|
|
|
|
|
|
|
Methods:
|
|
|
|
|
get(request, pk=None): Retrieves a list of late come and early out records with filtering.
|
|
|
|
|
delete(request, pk=None): Deletes a specific late come or early out record by `pk`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
|
|
|
|
def get(self, request, pk=None):
|
|
|
|
|
data = LateComeEarlyOutFilter(request.GET)
|
|
|
|
|
serializer = AttendanceLateComeEarlyOutSerializer(data.qs, many=True)
|
|
|
|
|
return Response(serializer.data, status=200)
|
|
|
|
|
|
|
|
|
|
def delete(self, request, pk=None):
|
|
|
|
|
attendance = get_object_or_404(AttendanceLateComeEarlyOut, pk=pk)
|
|
|
|
|
attendance.delete()
|
|
|
|
|
return Response({"message": "Attendance deleted successfully"}, status=204)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AttendanceActivityView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Retrieves attendance activity records.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
get(request, pk=None): Retrieves a list of all attendance activity records.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-05-05 16:41:55 +05:30
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
2024-10-10 15:41:46 +05:30
|
|
|
def get(self, request, pk=None):
|
|
|
|
|
data = AttendanceActivity.objects.all()
|
|
|
|
|
serializer = AttendanceActivitySerializer(data, many=True)
|
|
|
|
|
return Response(serializer.data, status=200)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TodayAttendance(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Provides the ratio of marked attendances to expected attendances for the current day.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
get(request): Calculates and returns the attendance ratio for today.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-05-05 16:41:55 +05:30
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
2024-10-10 15:41:46 +05:30
|
|
|
def get(self, request):
|
|
|
|
|
|
|
|
|
|
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(start_date=today)
|
|
|
|
|
late_come_obj = len(late_come)
|
|
|
|
|
|
|
|
|
|
marked_attendances = late_come_obj + on_time
|
|
|
|
|
|
|
|
|
|
expected_attendances = find_expected_attendances(week_day=week_day)
|
|
|
|
|
marked_attendances_ratio = 0
|
|
|
|
|
if expected_attendances != 0:
|
|
|
|
|
marked_attendances_ratio = (
|
|
|
|
|
f"{(marked_attendances / expected_attendances) * 100:.2f}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return Response(
|
|
|
|
|
{"marked_attendances_ratio": marked_attendances_ratio}, status=200
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OfflineEmployeesCountView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Retrieves the count of active employees who have not clocked in today.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
get(request): Returns the number of active employees who are not yet clocked in.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-05-05 16:41:55 +05:30
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
2024-10-10 15:41:46 +05:30
|
|
|
def get(self, request):
|
|
|
|
|
count = (
|
|
|
|
|
EmployeeFilter({"not_in_yet": date.today()})
|
|
|
|
|
.qs.exclude(employee_work_info__isnull=True)
|
|
|
|
|
.filter(is_active=True)
|
|
|
|
|
.count()
|
|
|
|
|
)
|
|
|
|
|
return Response({"count": count}, status=200)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OfflineEmployeesListView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Lists active employees who have not clocked in today, including their leave status.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
get(request): Retrieves and paginates a list of employees not clocked in today with their leave status.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-05-05 16:41:55 +05:30
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
2024-10-10 15:41:46 +05:30
|
|
|
def get(self, request):
|
|
|
|
|
queryset = (
|
|
|
|
|
EmployeeFilter({"not_in_yet": date.today()})
|
|
|
|
|
.qs.exclude(employee_work_info__isnull=True)
|
|
|
|
|
.filter(is_active=True)
|
|
|
|
|
)
|
|
|
|
|
leave_status = self.get_leave_status(queryset)
|
|
|
|
|
pagenation = PageNumberPagination()
|
|
|
|
|
page = pagenation.paginate_queryset(leave_status, request)
|
|
|
|
|
return pagenation.get_paginated_response(page)
|
|
|
|
|
|
|
|
|
|
def get_leave_status(self, queryset):
|
|
|
|
|
|
|
|
|
|
today = date.today()
|
|
|
|
|
queryset = queryset.distinct()
|
|
|
|
|
# Annotate each employee with their leave status
|
|
|
|
|
employees_with_leave_status = queryset.annotate(
|
|
|
|
|
leave_status=Case(
|
|
|
|
|
# Define different cases based on leave requests and attendance
|
|
|
|
|
When(
|
|
|
|
|
leaverequest__start_date__lte=today,
|
|
|
|
|
leaverequest__end_date__gte=today,
|
|
|
|
|
leaverequest__status="approved",
|
|
|
|
|
then=Value("On Leave"),
|
|
|
|
|
),
|
|
|
|
|
When(
|
|
|
|
|
leaverequest__start_date__lte=today,
|
|
|
|
|
leaverequest__end_date__gte=today,
|
|
|
|
|
leaverequest__status="requested",
|
|
|
|
|
then=Value("Waiting Approval"),
|
|
|
|
|
),
|
|
|
|
|
When(
|
|
|
|
|
leaverequest__start_date__lte=today,
|
|
|
|
|
leaverequest__end_date__gte=today,
|
|
|
|
|
then=Value("Canceled / Rejected"),
|
|
|
|
|
),
|
|
|
|
|
When(
|
|
|
|
|
employee_attendances__attendance_date=today, then=Value("Working")
|
|
|
|
|
),
|
|
|
|
|
default=Value("Expected working"), # Default status
|
|
|
|
|
output_field=CharField(),
|
|
|
|
|
),
|
|
|
|
|
job_position_id=F("employee_work_info__job_position_id"),
|
|
|
|
|
).values(
|
|
|
|
|
"employee_first_name",
|
|
|
|
|
"employee_last_name",
|
|
|
|
|
"leave_status",
|
|
|
|
|
"employee_profile",
|
|
|
|
|
"id",
|
|
|
|
|
"job_position_id",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for employee in employees_with_leave_status:
|
|
|
|
|
|
|
|
|
|
if employee["employee_profile"]:
|
|
|
|
|
employee["employee_profile"] = (
|
|
|
|
|
settings.MEDIA_URL + employee["employee_profile"]
|
|
|
|
|
)
|
|
|
|
|
return employees_with_leave_status
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CheckingStatus(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Checks and provides the current attendance status for the authenticated user.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
get(request): Returns the attendance status, duration at work, and clock-in time if available.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _format_seconds(cls, seconds):
|
|
|
|
|
hours = seconds // 3600
|
|
|
|
|
minutes = (seconds % 3600) // 60
|
|
|
|
|
seconds = seconds % 60
|
|
|
|
|
return f"{hours:02}:{minutes:02}:{seconds:02}"
|
|
|
|
|
|
|
|
|
|
def get(self, request):
|
|
|
|
|
attendance_activity = (
|
|
|
|
|
AttendanceActivity.objects.filter(employee_id=request.user.employee_get)
|
|
|
|
|
.order_by("-id")
|
|
|
|
|
.first()
|
|
|
|
|
)
|
|
|
|
|
duration = None
|
|
|
|
|
work_seconds = request.user.employee_get.get_forecasted_at_work()[
|
|
|
|
|
"forecasted_at_work_seconds"
|
|
|
|
|
]
|
|
|
|
|
duration = CheckingStatus._format_seconds(int(work_seconds))
|
|
|
|
|
status = False
|
|
|
|
|
clock_in_time = None
|
|
|
|
|
|
|
|
|
|
today = datetime.now()
|
|
|
|
|
attendance_activity_first = (
|
|
|
|
|
AttendanceActivity.objects.filter(
|
|
|
|
|
employee_id=request.user.employee_get, clock_in_date=today
|
|
|
|
|
)
|
|
|
|
|
.order_by("in_datetime")
|
|
|
|
|
.first()
|
|
|
|
|
)
|
|
|
|
|
if attendance_activity:
|
|
|
|
|
clock_in_time = attendance_activity_first.clock_in.strftime("%I:%M %p")
|
|
|
|
|
if attendance_activity.clock_out_date:
|
|
|
|
|
status = False
|
|
|
|
|
else:
|
|
|
|
|
status = True
|
|
|
|
|
return Response(
|
|
|
|
|
{"status": status, "duration": duration, "clock_in": clock_in_time},
|
|
|
|
|
status=200,
|
|
|
|
|
)
|
|
|
|
|
return Response(
|
|
|
|
|
{"status": status, "duration": duration, "clock_in_time": clock_in_time},
|
|
|
|
|
status=200,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MailTemplateView(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Retrieves a list of recruitment mail templates.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
get(request): Returns all recruitment mail templates.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
|
|
|
|
def get(self, request):
|
2025-02-28 18:57:58 +05:30
|
|
|
instances = HorillaMailTemplate.objects.all()
|
2024-10-10 15:41:46 +05:30
|
|
|
serializer = MailTemplateSerializer(instances, many=True)
|
|
|
|
|
return Response(serializer.data, status=200)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConvertedMailTemplateConvert(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Renders a recruitment mail template with data from a specified employee.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
put(request): Renders the mail template body with employee and user data and returns the result.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
|
|
|
|
def put(self, request):
|
|
|
|
|
template_id = request.data.get("template_id", None)
|
|
|
|
|
employee_id = request.data.get("employee_id", None)
|
|
|
|
|
employee = Employee.objects.filter(id=employee_id).first()
|
2025-02-28 18:57:58 +05:30
|
|
|
bdy = HorillaMailTemplate.objects.filter(id=template_id).first()
|
2024-10-10 15:41:46 +05:30
|
|
|
template_bdy = template.Template(bdy.body)
|
|
|
|
|
context = template.Context(
|
|
|
|
|
{"instance": employee, "self": request.user.employee_get}
|
|
|
|
|
)
|
|
|
|
|
render_bdy = template_bdy.render(context)
|
|
|
|
|
return Response(render_bdy)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OfflineEmployeeMailsend(APIView):
|
|
|
|
|
"""
|
|
|
|
|
Sends an email with attachments and rendered templates to a specified employee.
|
|
|
|
|
|
|
|
|
|
Method:
|
|
|
|
|
post(request): Renders email templates with employee and user data, attaches files, and sends the email.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
|
|
|
|
|
|
def post(self, request):
|
|
|
|
|
employee_id = request.POST.get("employee_id")
|
|
|
|
|
subject = request.POST.get("subject", "")
|
|
|
|
|
bdy = request.POST.get("body", "")
|
|
|
|
|
other_attachments = request.FILES.getlist("other_attachments")
|
|
|
|
|
attachments = [
|
|
|
|
|
(file.name, file.read(), file.content_type) for file in other_attachments
|
|
|
|
|
]
|
|
|
|
|
email_backend = ConfiguredEmailBackend()
|
|
|
|
|
host = email_backend.dynamic_username
|
|
|
|
|
employee = Employee.objects.get(id=employee_id)
|
|
|
|
|
template_attachment_ids = request.POST.getlist("template_attachments")
|
|
|
|
|
bodys = list(
|
2025-02-28 18:57:58 +05:30
|
|
|
HorillaMailTemplate.objects.filter(
|
2024-10-10 15:41:46 +05:30
|
|
|
id__in=template_attachment_ids
|
|
|
|
|
).values_list("body", flat=True)
|
|
|
|
|
)
|
|
|
|
|
for html in bodys:
|
|
|
|
|
# due to not having solid template we first need to pass the context
|
|
|
|
|
template_bdy = template.Template(html)
|
|
|
|
|
context = template.Context(
|
|
|
|
|
{"instance": employee, "self": request.user.employee_get}
|
|
|
|
|
)
|
|
|
|
|
render_bdy = template_bdy.render(context)
|
|
|
|
|
attachments.append(
|
|
|
|
|
(
|
|
|
|
|
"Document",
|
|
|
|
|
generate_pdf(render_bdy, {}, path=False, title="Document").content,
|
|
|
|
|
"application/pdf",
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
template_bdy = template.Template(bdy)
|
|
|
|
|
context = template.Context(
|
|
|
|
|
{"instance": employee, "self": request.user.employee_get}
|
|
|
|
|
)
|
|
|
|
|
render_bdy = template_bdy.render(context)
|
|
|
|
|
|
|
|
|
|
email = EmailMessage(
|
|
|
|
|
subject,
|
|
|
|
|
render_bdy,
|
|
|
|
|
host,
|
|
|
|
|
[employee.employee_work_info.email],
|
|
|
|
|
)
|
|
|
|
|
email.content_subtype = "html"
|
|
|
|
|
|
|
|
|
|
email.attachments = attachments
|
|
|
|
|
try:
|
|
|
|
|
email.send()
|
|
|
|
|
if employee.employee_work_info.email:
|
|
|
|
|
return Response(f"Mail sent to {employee.get_full_name()}")
|
|
|
|
|
else:
|
|
|
|
|
return Response(f"Email not set for {employee.get_full_name()}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return Response("Something went wrong")
|