158 lines
4.9 KiB
Python
158 lines
4.9 KiB
Python
from django.contrib.contenttypes.models import ContentType
|
|
from django.db.models import Count, IntegerField, OuterRef, Subquery, Value
|
|
from django.db.models.functions import Coalesce
|
|
from django.utils import timezone
|
|
from rest_framework.exceptions import PermissionDenied
|
|
|
|
from attendance.models import AttendanceRecord
|
|
from communications.models import Message
|
|
from employees.models import Employee
|
|
|
|
|
|
def _can_access_employee(user, employee_id) -> bool:
|
|
try:
|
|
normalized_employee_id = int(employee_id)
|
|
except (TypeError, ValueError):
|
|
return False
|
|
|
|
return bool(
|
|
user
|
|
and user.is_authenticated
|
|
and (
|
|
user.is_staff or user.is_superuser or normalized_employee_id == int(user.id)
|
|
)
|
|
)
|
|
|
|
|
|
def _check_employee_access(user, employee_id) -> None:
|
|
if not _can_access_employee(user, employee_id):
|
|
raise PermissionDenied(
|
|
"You don't have permission to access this attendance data"
|
|
)
|
|
|
|
|
|
def _records_visible_to(user):
|
|
queryset = AttendanceRecord.objects.all()
|
|
if user.is_staff or user.is_superuser:
|
|
return queryset
|
|
return queryset.filter(employee_id=user.id)
|
|
|
|
|
|
def _day_event_records_visible_to(user):
|
|
"""Allow payroll viewers to inspect evidence used by point projections."""
|
|
|
|
queryset = AttendanceRecord.objects.all()
|
|
if user.is_staff or user.is_superuser or user.has_perm("finance.view_all_payroll"):
|
|
return queryset
|
|
return queryset.filter(employee_id=user.id)
|
|
|
|
|
|
def _check_staff_access(user) -> None:
|
|
if not (user and user.is_authenticated and (user.is_staff or user.is_superuser)):
|
|
raise PermissionDenied("You don't have permission to manage attendance")
|
|
|
|
|
|
def _has_attendance_pass(record: AttendanceRecord) -> bool:
|
|
return bool(
|
|
record.arrival_time
|
|
or record.departure_time
|
|
or (record.work_hours is not None and record.work_hours > 0)
|
|
)
|
|
|
|
|
|
def _has_absence_marker(record: AttendanceRecord) -> bool:
|
|
values = [
|
|
record.statuses,
|
|
record.employee_issues,
|
|
]
|
|
return bool(
|
|
record.is_absent
|
|
or any(
|
|
"absent" in normalized
|
|
or "absence" in normalized
|
|
or "отсутств" in normalized
|
|
for items in values
|
|
for normalized in [str(item).strip().lower() for item in (items or [])]
|
|
)
|
|
)
|
|
|
|
|
|
def _employee_attendance_aliases(record: AttendanceRecord) -> list[str]:
|
|
return [
|
|
str(alias).strip()
|
|
for alias in (getattr(record.employee, "attendance_aliases", None) or [])
|
|
if str(alias).strip()
|
|
]
|
|
|
|
|
|
def _attendance_employee_summary(employee: Employee) -> dict[str, str | int]:
|
|
full_name = f"{employee.last_name or ''} {employee.first_name or ''}".strip()
|
|
if getattr(employee, "patronymic", ""):
|
|
full_name = f"{full_name} {employee.patronymic}".strip()
|
|
return {
|
|
"id": employee.id,
|
|
"name": full_name or employee.email or f"Сотрудник #{employee.id}",
|
|
"email": employee.email or "",
|
|
}
|
|
|
|
|
|
def _comments_count_annotation():
|
|
record_ct = ContentType.objects.get_for_model(AttendanceRecord)
|
|
comments_subquery = (
|
|
Message.objects.filter(
|
|
chat__type="comments",
|
|
chat__context_content_type=record_ct,
|
|
chat__context_object_id=OuterRef("pk"),
|
|
is_deleted=False,
|
|
)
|
|
.values("chat")
|
|
.annotate(count=Count("id"))
|
|
.values("count")
|
|
)
|
|
return {
|
|
"comments_count": Coalesce(
|
|
Subquery(comments_subquery, output_field=IntegerField()),
|
|
Value(0),
|
|
)
|
|
}
|
|
|
|
|
|
def _comment_author_name(author) -> str:
|
|
if not author:
|
|
return "Сотрудник"
|
|
first_name = getattr(author, "first_name", "") or ""
|
|
last_name = getattr(author, "last_name", "") or ""
|
|
full_name = f"{last_name} {first_name}".strip()
|
|
return full_name or getattr(author, "email", "") or "Сотрудник"
|
|
|
|
|
|
def _attendance_record_comments_map(
|
|
record_ids: list[int],
|
|
) -> dict[int, list[dict[str, str]]]:
|
|
if not record_ids:
|
|
return {}
|
|
record_ct = ContentType.objects.get_for_model(AttendanceRecord)
|
|
messages = (
|
|
Message.objects.filter(
|
|
chat__type="comments",
|
|
chat__context_content_type=record_ct,
|
|
chat__context_object_id__in=record_ids,
|
|
is_deleted=False,
|
|
)
|
|
.select_related("author", "chat")
|
|
.order_by("created_at", "id")
|
|
)
|
|
result: dict[int, list[dict[str, str]]] = {}
|
|
for message in messages:
|
|
record_id = int(message.chat.context_object_id)
|
|
result.setdefault(record_id, []).append(
|
|
{
|
|
"author": _comment_author_name(message.author),
|
|
"text": message.content,
|
|
"created_at": timezone.localtime(message.created_at).strftime(
|
|
"%d.%m.%Y %H:%M"
|
|
),
|
|
}
|
|
)
|
|
return result
|