202 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# backend/requests_app/admin.py
from django import forms
from django.contrib import admin, messages
from django.db import models
from django.utils.translation import gettext_lazy as _
from .enums import RequestStatus
from .models import Request, RequestSettings, RequestVacationTerms
@admin.register(RequestSettings)
class RequestSettingsAdmin(admin.ModelAdmin):
"""Единственная запись настроек получателей заявлений."""
list_display = ("name", "allowed_recipients_count", "updated_at")
fields = ("name", "allowed_recipients", "updated_at")
readonly_fields = ("updated_at",)
autocomplete_fields = ("allowed_recipients",)
def has_add_permission(self, request):
if RequestSettings.objects.exists():
return False
return super().has_add_permission(request)
def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
@admin.display(description="Доступные получатели")
def allowed_recipients_count(self, obj):
count = obj.allowed_recipients.count()
return count or "Все сотрудники"
@admin.register(RequestVacationTerms)
class RequestVacationTermsAdmin(admin.ModelAdmin):
list_display = (
"request",
"paid_days",
"finance_status",
"recorded_by",
"updated_at",
)
list_filter = ("finance_status",)
search_fields = (
"request__employee__last_name",
"request__employee__first_name",
"request__employee__email",
)
autocomplete_fields = ("request", "recorded_by")
readonly_fields = ("recorded_at", "updated_at")
# Админ-форма: без принуждения вводить approver (мы выставим его автоматически)
class RequestAdminForm(forms.ModelForm):
class Meta:
model = Request
fields = "__all__"
def clean(self):
cleaned = super().clean()
status = cleaned.get("status")
approver = cleaned.get("approver")
# Если статус "Отменено" — согласующий должен быть пустым
if status == RequestStatus.CANCELLED and approver:
cleaned["approver"] = None
# Для approved/rejected НЕ требуем approver — его поставим в save_model.
return cleaned
@admin.register(Request)
class RequestAdmin(admin.ModelAdmin):
form = RequestAdminForm
list_display = (
"id",
"display_title",
"type",
"status",
"employee",
"department",
"date_from",
"date_to",
"decided_at",
"created_at",
)
list_filter = (
"type",
"status",
"department",
("created_at", admin.DateFieldListFilter),
("decided_at", admin.DateFieldListFilter),
)
search_fields = (
"title",
"comment",
"employee__last_name",
"employee__first_name",
"employee__email",
)
autocomplete_fields = ("employee", "department")
# approver только для просмотра (покажем, кто согласовал)
readonly_fields = ("approver", "created_at", "updated_at", "decided_at")
fieldsets = (
(
_("Основное"),
{
"fields": (
"employee",
"department",
"type",
"title",
("date_from", "date_to"),
("time_from", "time_to"),
"comment",
"attachment",
)
},
),
(
_("Статус"),
{
"fields": (
"status",
# показываем, но не редактируем
"approver",
"decided_at",
)
},
),
(
_("Служебное"),
{
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
},
),
)
# HTML5-пикеры дат/времени в админке
formfield_overrides = {
models.DateField: {"widget": forms.DateInput(attrs={"type": "date"})},
models.DateTimeField: {
"widget": forms.DateTimeInput(attrs={"type": "datetime-local"})
},
}
def save_model(self, request, obj: Request, form, change):
"""
Приводим пару статус/согласующий к валидному виду ДО сохранения:
- approved/rejected -> approver = текущий пользователь, если не задан
- cancelled -> approver = None
Это гарантирует отсутствие конфликтов с БД-constraint.
"""
if obj.status in {RequestStatus.APPROVED, RequestStatus.REJECTED}:
if obj.approver_id is None:
obj.approver = request.user
elif obj.status == RequestStatus.CANCELLED:
if obj.approver_id is not None:
obj.approver = None
super().save_model(request, obj, form, change)
# Массовые действия работают через методы модели (ставят approver и
# decided_at)
@admin.action(description=_("Одобрить выбранные"))
def action_approve(self, request, queryset):
updated = 0
for obj in queryset:
if obj.status != RequestStatus.APPROVED:
obj.approve(request.user)
updated += 1
self.message_user(
request, _(f"Одобрено заявок: {updated}"), level=messages.SUCCESS
)
@admin.action(description=_("Отклонить выбранные"))
def action_reject(self, request, queryset):
updated = 0
for obj in queryset:
if obj.status != RequestStatus.REJECTED:
obj.reject(request.user)
updated += 1
self.message_user(
request, _(f"Отклонено заявок: {updated}"), level=messages.SUCCESS
)
@admin.action(description=_("Отменить выбранные"))
def action_cancel(self, request, queryset):
updated = 0
for obj in queryset:
if obj.status != RequestStatus.CANCELLED:
obj.cancel()
updated += 1
self.message_user(
request, _(f"Отменено заявок: {updated}"), level=messages.SUCCESS
)
actions = ("action_approve", "action_reject", "action_cancel")