189 lines
7.2 KiB
Python
189 lines
7.2 KiB
Python
from django.views.generic import ListView, CreateView, UpdateView, TemplateView, DetailView, DeleteView
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.urls import reverse_lazy
|
|
from django.utils.translation import gettext_lazy as _
|
|
from .models import Employee, Department, Attendance, LeaveRequest, JobPosition, BiometricDevice
|
|
from .forms import EmployeeForm
|
|
from django.db.models import Count
|
|
from django.shortcuts import get_object_or_404, redirect
|
|
from django.contrib import messages
|
|
import socket
|
|
from django.utils import timezone
|
|
|
|
class HRDashboardView(LoginRequiredMixin, TemplateView):
|
|
template_name = 'hr/dashboard.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['total_employees'] = Employee.objects.count()
|
|
context['active_employees'] = Employee.objects.filter(status='active').count()
|
|
context['departments_count'] = Department.objects.count()
|
|
context['pending_leaves'] = LeaveRequest.objects.filter(status='pending').count()
|
|
return context
|
|
|
|
class EmployeeListView(LoginRequiredMixin, ListView):
|
|
model = Employee
|
|
template_name = 'hr/employee_list.html'
|
|
context_object_name = 'employees'
|
|
|
|
class EmployeeCreateView(LoginRequiredMixin, CreateView):
|
|
model = Employee
|
|
form_class = EmployeeForm
|
|
template_name = 'hr/employee_form.html'
|
|
success_url = reverse_lazy('hr:employee_list')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['title'] = _("Add New Employee")
|
|
return context
|
|
|
|
class EmployeeUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = Employee
|
|
form_class = EmployeeForm
|
|
template_name = 'hr/employee_form.html'
|
|
success_url = reverse_lazy('hr:employee_list')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['title'] = _("Edit Employee")
|
|
return context
|
|
|
|
class EmployeeDetailView(LoginRequiredMixin, DetailView):
|
|
model = Employee
|
|
template_name = 'hr/employee_detail.html'
|
|
|
|
class DepartmentListView(LoginRequiredMixin, ListView):
|
|
model = Department
|
|
template_name = 'hr/department_list.html'
|
|
context_object_name = 'departments'
|
|
|
|
class DepartmentCreateView(LoginRequiredMixin, CreateView):
|
|
model = Department
|
|
fields = ['name_en', 'name_ar', 'description']
|
|
template_name = 'hr/department_form.html'
|
|
success_url = reverse_lazy('hr:department_list')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['title'] = _("Add New Department")
|
|
return context
|
|
|
|
class DepartmentUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = Department
|
|
fields = ['name_en', 'name_ar', 'description']
|
|
template_name = 'hr/department_form.html'
|
|
success_url = reverse_lazy('hr:department_list')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['title'] = _("Edit Department")
|
|
return context
|
|
|
|
class AttendanceListView(LoginRequiredMixin, ListView):
|
|
model = Attendance
|
|
template_name = 'hr/attendance_list.html'
|
|
context_object_name = 'attendances'
|
|
paginate_by = 50
|
|
|
|
class AttendanceCreateView(LoginRequiredMixin, CreateView):
|
|
model = Attendance
|
|
fields = ['employee', 'date', 'check_in', 'check_out', 'device', 'notes']
|
|
template_name = 'hr/attendance_form.html'
|
|
success_url = reverse_lazy('hr:attendance_list')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['title'] = _("Add Attendance Record")
|
|
return context
|
|
|
|
class AttendanceUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = Attendance
|
|
fields = ['employee', 'date', 'check_in', 'check_out', 'device', 'notes']
|
|
template_name = 'hr/attendance_form.html'
|
|
success_url = reverse_lazy('hr:attendance_list')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['title'] = _("Edit Attendance Record")
|
|
return context
|
|
|
|
class LeaveRequestListView(LoginRequiredMixin, ListView):
|
|
model = LeaveRequest
|
|
template_name = 'hr/leave_list.html'
|
|
context_object_name = 'leaves'
|
|
|
|
class LeaveRequestCreateView(LoginRequiredMixin, CreateView):
|
|
model = LeaveRequest
|
|
fields = ['employee', 'leave_type', 'start_date', 'end_date', 'reason']
|
|
template_name = 'hr/leave_form.html'
|
|
success_url = reverse_lazy('hr:leave_list')
|
|
|
|
class BiometricDeviceListView(LoginRequiredMixin, ListView):
|
|
model = BiometricDevice
|
|
template_name = 'hr/biometric_device_list.html'
|
|
context_object_name = 'devices'
|
|
|
|
class BiometricDeviceCreateView(LoginRequiredMixin, CreateView):
|
|
model = BiometricDevice
|
|
fields = ['name', 'ip_address', 'port', 'device_type', 'status']
|
|
template_name = 'hr/biometric_device_form.html'
|
|
success_url = reverse_lazy('hr:device_list')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['title'] = _("Add Biometric Device")
|
|
return context
|
|
|
|
class BiometricDeviceUpdateView(LoginRequiredMixin, UpdateView):
|
|
model = BiometricDevice
|
|
fields = ['name', 'ip_address', 'port', 'device_type', 'status']
|
|
template_name = 'hr/biometric_device_form.html'
|
|
success_url = reverse_lazy('hr:device_list')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['title'] = _("Edit Biometric Device")
|
|
return context
|
|
|
|
class BiometricDeviceDeleteView(LoginRequiredMixin, DeleteView):
|
|
model = BiometricDevice
|
|
template_name = 'hr/biometric_device_confirm_delete.html'
|
|
success_url = reverse_lazy('hr:device_list')
|
|
|
|
def test_device_connection(request, pk):
|
|
device = get_object_or_404(BiometricDevice, pk=pk)
|
|
|
|
try:
|
|
# Simple socket connect to see if port is open (timeout 3s)
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(3)
|
|
result = sock.connect_ex((device.ip_address, device.port))
|
|
sock.close()
|
|
|
|
if result == 0:
|
|
messages.success(request, _("Connection successful! Device is online."))
|
|
device.status = 'active'
|
|
device.last_sync = timezone.now()
|
|
device.save()
|
|
else:
|
|
# If failed, we still report it but since this is often run in dev/cloud
|
|
# where the device isn't reachable, we warn.
|
|
messages.warning(request, _("Connection failed: Port closed or host unreachable. (Error Code: %(code)s)") % {'code': result})
|
|
device.status = 'inactive'
|
|
device.save()
|
|
|
|
except Exception as e:
|
|
messages.error(request, _("Connection Error: %(error)s") % {'error': str(e)})
|
|
|
|
return redirect('hr:device_list')
|
|
|
|
def sync_device_logs(request, pk):
|
|
device = get_object_or_404(BiometricDevice, pk=pk)
|
|
result = device.sync_data() # This method we added to the model
|
|
|
|
if result.get('error'):
|
|
messages.error(request, _("Sync Failed: %(error)s") % {'error': result['error']})
|
|
else:
|
|
messages.success(request, _("Sync Successful! Fetched %(total)s records, %(new)s new.") % {'total': result['total'], 'new': result['new']})
|
|
|
|
return redirect('hr:device_list') |