lizerk 0217bc1a38 refactor: migrate comments to unified communications system
- Delete legacy Comment models (feed.Comment, documents.DocumentComment, requests_app.RequestComment)
- Create migration scripts to move data to communications.Message
- Add comments_helpers.py for unified comment operations
- Update API endpoints: /api/v1/posts/{id}/comments/, /api/v1/requests/{id}/comments/
- Fix notifications.0010_drop_old_tables migration with SeparateDatabaseAndState
- Implement real-time comments_count via GenericForeignKey annotations
- Update frontend API client to use new endpoints
- Update JavaScript files for new comment API routes
- Remove deprecated API endpoints and serializers
- Clean up signal handlers and admin registrations

Deployment tested: all migrations apply successfully on fresh database
2026-03-11 23:56:41 +03:00

158 lines
5.2 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
# Админ-форма: без принуждения вводить 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"),
"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")