diff --git a/backend/employees/admin.py b/backend/employees/admin.py index 1af30f88..93b1a0fa 100644 --- a/backend/employees/admin.py +++ b/backend/employees/admin.py @@ -24,6 +24,45 @@ from .models import ( # Формы для кастомного User (Employee) # ========================= + +class LdapSyncedFilter(admin.SimpleListFilter): + """Фильтр для отображения сотрудников по состоянию LDAP синхронизации.""" + + title = "Статус LDAP синхронизации" + parameter_name = "ldap_synced" + + def lookups(self, request, model_admin): + return ( + ("yes", "Есть запись синхронизации"), + ("no", "Нет записи синхронизации"), + ) + + def queryset(self, request, queryset): + from django.db.models import Exists, OuterRef + + if self.value() == "yes": + # Показываем только тех, у кого есть запись синхронизации + return queryset.filter( + Exists( + LdapSyncState.objects.filter( + model="employee", + object_pk=OuterRef("pk") + ) + ) + ) + elif self.value() == "no": + # Показываем только тех, у кого нет записи синхронизации + return queryset.exclude( + Exists( + LdapSyncState.objects.filter( + model="employee", + object_pk=OuterRef("pk") + ) + ) + ) + return queryset + + class EmployeeCreationForm(forms.ModelForm): """ Создание сотрудника в админке: логин — email, без username. @@ -158,6 +197,7 @@ class EmployeeAdmin(DjangoUserAdmin): "is_active", "email_verified", "is_ldap_managed", + "ldap_sync_status", "is_staff", ) list_filter = ( @@ -166,6 +206,7 @@ class EmployeeAdmin(DjangoUserAdmin): "is_staff", "is_superuser", "is_ldap_managed", + LdapSyncedFilter, "position", "groups", ) @@ -183,6 +224,42 @@ class EmployeeAdmin(DjangoUserAdmin): "email_activation_code", "sms_activation_code", ) + + def ldap_sync_status(self, obj): + """Краткий статус LDAP синхронизации для списка.""" + if not obj.pk or not obj.is_ldap_managed: + return "-" + + try: + sync_state = LdapSyncState.objects.filter( + model="employee", + object_pk=str(obj.pk) + ).first() + + if not sync_state: + return "❌ Нет записи" + + from django.utils.html import format_html + + # Показываем GUID и дату последнего обновления + if sync_state.ldap_guid and sync_state.updated_at: + return format_html( + '✅ {}', + sync_state.ldap_guid, + sync_state.updated_at.strftime("%d.%m.%Y %H:%M") + ) + elif sync_state.updated_at: + return format_html( + '⚠️ {}', + sync_state.updated_at.strftime("%d.%m.%Y %H:%M") + ) + else: + return "⚠️ Неполные данные" + except Exception: + return "❌ Ошибка" + + ldap_sync_status.short_description = "LDAP статус" + ldap_sync_status.admin_order_field = "id" # Сортировка по ID fieldsets = ( (None, {"fields": ("email", "password")}), @@ -236,8 +313,21 @@ class EmployeeAdmin(DjangoUserAdmin): return "Нет записи синхронизации" from django.utils.html import format_html + from django.urls import reverse + + # Ссылка на запись синхронизации в админке + sync_url = reverse( + 'admin:employees_ldapsyncstate_change', + args=[sync_state.pk] + ) + + info = [ + format_html( + '📋 Открыть запись синхронизации', + sync_url + ) + ] - info = [] if sync_state.ldap_dn: info.append(f"DN: {sync_state.ldap_dn}") if sync_state.ldap_guid: