agdar/hr/views.py
2025-11-02 14:35:35 +03:00

314 lines
11 KiB
Python

"""
Views for HR app.
"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import ListView, CreateView, DetailView, UpdateView, TemplateView
from django.utils.translation import gettext_lazy as _
from django.contrib import messages
from django.utils import timezone
from datetime import datetime, timedelta
from core.mixins import TenantFilterMixin
from .models import Attendance, Schedule, Holiday
from .forms import AttendanceForm, ScheduleForm, HolidayForm
# ============================================================================
# Attendance Views
# ============================================================================
class AttendanceListView(LoginRequiredMixin, TenantFilterMixin, ListView):
"""List all attendance records."""
model = Attendance
template_name = 'hr/attendance_list.html'
context_object_name = 'attendances'
paginate_by = 20
def get_queryset(self):
queryset = super().get_queryset().filter(
tenant=self.request.user.tenant
).select_related('employee')
# Filter by employee if provided
employee_id = self.request.GET.get('employee')
if employee_id:
queryset = queryset.filter(employee_id=employee_id)
# Filter by status if provided
status = self.request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
# Filter by date range if provided
date_from = self.request.GET.get('date_from')
date_to = self.request.GET.get('date_to')
if date_from:
queryset = queryset.filter(date__gte=date_from)
if date_to:
queryset = queryset.filter(date__lte=date_to)
return queryset.order_by('-date', 'employee')
class AttendanceCreateView(LoginRequiredMixin, TenantFilterMixin, CreateView):
"""Create a new attendance record."""
model = Attendance
form_class = AttendanceForm
template_name = 'hr/attendance_form.html'
success_url = reverse_lazy('hr:attendance-list')
def form_valid(self, form):
form.instance.tenant = self.request.user.tenant
messages.success(self.request, _('Attendance record created successfully.'))
return super().form_valid(form)
class AttendanceDetailView(LoginRequiredMixin, TenantFilterMixin, DetailView):
"""View attendance record details."""
model = Attendance
template_name = 'hr/attendance_detail.html'
context_object_name = 'attendance'
def get_queryset(self):
return super().get_queryset().filter(
tenant=self.request.user.tenant
).select_related('employee')
class AttendanceUpdateView(LoginRequiredMixin, TenantFilterMixin, UpdateView):
"""Update an attendance record."""
model = Attendance
form_class = AttendanceForm
template_name = 'hr/attendance_form.html'
def get_queryset(self):
return super().get_queryset().filter(tenant=self.request.user.tenant)
def get_success_url(self):
return reverse_lazy('hr:attendance-detail', kwargs={'pk': self.object.pk})
def form_valid(self, form):
messages.success(self.request, _('Attendance record updated successfully.'))
return super().form_valid(form)
class AttendanceKioskView(LoginRequiredMixin, TemplateView):
"""Kiosk view for quick clock in/out."""
template_name = 'hr/attendance_kiosk.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
today = timezone.now().date()
# Get today's attendance for current user
try:
attendance = Attendance.objects.get(
employee=self.request.user,
date=today,
tenant=self.request.user.tenant
)
context['attendance'] = attendance
except Attendance.DoesNotExist:
context['attendance'] = None
return context
def post(self, request, *args, **kwargs):
"""Handle clock in/out."""
today = timezone.now().date()
# Get current time in Riyadh timezone
now = timezone.localtime(timezone.now()).time()
attendance, created = Attendance.objects.get_or_create(
employee=request.user,
date=today,
tenant=request.user.tenant,
defaults={'check_in': now, 'status': Attendance.Status.PRESENT}
)
if not created and not attendance.check_out:
# Clock out
attendance.check_out = now
attendance.save()
messages.success(request, _('Clocked out successfully.'))
elif created:
messages.success(request, _('Clocked in successfully.'))
else:
messages.warning(request, _('You have already clocked out for today.'))
return self.get(request, *args, **kwargs)
# ============================================================================
# Schedule Views
# ============================================================================
class ScheduleListView(LoginRequiredMixin, TenantFilterMixin, ListView):
"""List all schedules."""
model = Schedule
template_name = 'hr/schedule_list.html'
context_object_name = 'schedules'
paginate_by = 50
def get_queryset(self):
queryset = super().get_queryset().filter(
tenant=self.request.user.tenant
).select_related('employee')
# Filter by employee if provided
employee_id = self.request.GET.get('employee')
if employee_id:
queryset = queryset.filter(employee_id=employee_id)
# Filter by day if provided
day = self.request.GET.get('day')
if day:
queryset = queryset.filter(day_of_week=day)
# Filter by active status
is_active = self.request.GET.get('is_active')
if is_active:
queryset = queryset.filter(is_active=is_active == 'true')
return queryset.order_by('employee', 'day_of_week')
class ScheduleCreateView(LoginRequiredMixin, TenantFilterMixin, CreateView):
"""Create a new schedule."""
model = Schedule
form_class = ScheduleForm
template_name = 'hr/schedule_form.html'
success_url = reverse_lazy('hr:schedule-list')
def form_valid(self, form):
form.instance.tenant = self.request.user.tenant
messages.success(self.request, _('Schedule created successfully.'))
return super().form_valid(form)
class ScheduleDetailView(LoginRequiredMixin, TenantFilterMixin, DetailView):
"""View schedule details."""
model = Schedule
template_name = 'hr/schedule_detail.html'
context_object_name = 'schedule'
def get_queryset(self):
return super().get_queryset().filter(
tenant=self.request.user.tenant
).select_related('employee')
class ScheduleUpdateView(LoginRequiredMixin, TenantFilterMixin, UpdateView):
"""Update a schedule."""
model = Schedule
form_class = ScheduleForm
template_name = 'hr/schedule_form.html'
def get_queryset(self):
return super().get_queryset().filter(tenant=self.request.user.tenant)
def get_success_url(self):
return reverse_lazy('hr:schedule-detail', kwargs={'pk': self.object.pk})
def form_valid(self, form):
messages.success(self.request, _('Schedule updated successfully.'))
return super().form_valid(form)
class ScheduleGridView(LoginRequiredMixin, TenantFilterMixin, TemplateView):
"""Grid view of all schedules."""
template_name = 'hr/schedule_grid.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Get all active schedules
schedules = Schedule.objects.filter(
tenant=self.request.user.tenant,
is_active=True
).select_related('employee').order_by('employee', 'day_of_week')
# Organize by employee and day
schedule_grid = {}
for schedule in schedules:
employee_name = schedule.employee.get_full_name()
if employee_name not in schedule_grid:
schedule_grid[employee_name] = {}
schedule_grid[employee_name][schedule.day_of_week] = schedule
context['schedule_grid'] = schedule_grid
context['days'] = Schedule.DayOfWeek.choices
return context
# ============================================================================
# Holiday Views
# ============================================================================
class HolidayListView(LoginRequiredMixin, TenantFilterMixin, ListView):
"""List all holidays."""
model = Holiday
template_name = 'hr/holiday_list.html'
context_object_name = 'holidays'
paginate_by = 20
def get_queryset(self):
queryset = super().get_queryset().filter(
tenant=self.request.user.tenant
)
# Filter by recurring status
is_recurring = self.request.GET.get('is_recurring')
if is_recurring:
queryset = queryset.filter(is_recurring=is_recurring == 'true')
# Filter by year
year = self.request.GET.get('year')
if year:
queryset = queryset.filter(date__year=year)
return queryset.order_by('date')
class HolidayCreateView(LoginRequiredMixin, TenantFilterMixin, CreateView):
"""Create a new holiday."""
model = Holiday
form_class = HolidayForm
template_name = 'hr/holiday_form.html'
success_url = reverse_lazy('hr:holiday-list')
def form_valid(self, form):
form.instance.tenant = self.request.user.tenant
messages.success(self.request, _('Holiday created successfully.'))
return super().form_valid(form)
class HolidayDetailView(LoginRequiredMixin, TenantFilterMixin, DetailView):
"""View holiday details."""
model = Holiday
template_name = 'hr/holiday_detail.html'
context_object_name = 'holiday'
def get_queryset(self):
return super().get_queryset().filter(tenant=self.request.user.tenant)
class HolidayUpdateView(LoginRequiredMixin, TenantFilterMixin, UpdateView):
"""Update a holiday."""
model = Holiday
form_class = HolidayForm
template_name = 'hr/holiday_form.html'
def get_queryset(self):
return super().get_queryset().filter(tenant=self.request.user.tenant)
def get_success_url(self):
return reverse_lazy('hr:holiday-detail', kwargs={'pk': self.object.pk})
def form_valid(self, form):
messages.success(self.request, _('Holiday updated successfully.'))
return super().form_valid(form)