employees: HR-заявки, приглашения на пароль, профиль и 2FA

Модель/сервисы HR-заявок с заменами, приглашения на установку пароля
(инвайты), обновлённая страница профиля сотрудника и правки 2FA.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-09 18:03:32 +03:00
parent ae54d93442
commit 3359a8db13
34 changed files with 2789 additions and 404 deletions
+92 -4
View File
@@ -8,7 +8,9 @@ from django.utils.translation import gettext_lazy as _
from unfold.admin import ModelAdmin, TabularInline
from core.models import Directory, PortalAccess
from core.security import filter_portal_permissions_for_user, is_sensitive_portal_perm
from .models import Employee, PermissionGroup, EmployeeDefaults, EmployeePermissionOverride
from .models import EmployeeHrRequest, LeaveEntitlement
from apps.visit_manager.models import WorkSchedule, AttendanceLog
@@ -96,9 +98,20 @@ class EmployeePermissionOverrideInline(TabularInline):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'permission':
kwargs['queryset'] = _portal_permissions_qs()
kwargs['queryset'] = filter_portal_permissions_for_user(
_portal_permissions_qs(), request.user,
)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
def has_add_permission(self, request, obj=None):
return request.user.is_superuser
def has_change_permission(self, request, obj=None):
return request.user.is_superuser
def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
class AttendanceLogInline(TabularInline):
model = AttendanceLog
@@ -212,6 +225,23 @@ class EmployeeAdmin(ModelAdmin):
))
return fieldsets
def get_readonly_fields(self, request, obj=None):
readonly = list(super().get_readonly_fields(request, obj))
if not request.user.is_superuser:
readonly.extend([
'groups',
'ad_user_dn', 'ad_sync_status', 'ad_sync_error',
'ad_last_sync', 'ad_sync_enabled',
])
return readonly
def save_model(self, request, obj, form, change):
if not request.user.is_superuser and change:
previous = Employee.objects.filter(pk=obj.pk).prefetch_related('groups').first()
if previous:
obj.groups.set(previous.groups.all())
super().save_model(request, obj, form, change)
# def permissions_summary(self, obj):
# perms = obj.effective_permissions()
# group = obj.permission_group.name if obj.permission_group_id else None
@@ -336,7 +366,7 @@ class PermissionGroupAdminForm(forms.ModelForm):
@admin.register(PermissionGroup)
class PermissionGroupAdmin(ModelAdmin):
form = PermissionGroupAdminForm
list_display = ('group', 'is_active', 'description')
list_display = ('group', 'is_active', 'description', 'portal_matrix_link')
list_filter = ('is_active',)
search_fields = ('group__name', 'description')
@@ -346,9 +376,30 @@ class PermissionGroupAdmin(ModelAdmin):
}),
)
@admin.display(description=_('Матрица в портале'))
def portal_matrix_link(self, obj):
if not obj or not obj.group_id:
return '—'
from django.urls import reverse
from django.utils.html import format_html
url = reverse('user_group_access', kwargs={'group_id': obj.group_id})
return format_html('<a href="{}" target="_blank">{}</a>', url, _('Открыть'))
def get_readonly_fields(self, request, obj=None):
if request.user.is_superuser:
return ()
return ('group', 'description', 'is_active', 'portal_permissions')
def has_add_permission(self, request):
return request.user.is_superuser
def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
def save_model(self, request, obj, form, change):
super().save_model(request, obj, form, change)
form.save_portal_permissions(obj)
if request.user.is_superuser:
form.save_portal_permissions(obj)
@admin.register(EmployeePermissionOverride)
class EmployeePermissionOverrideAdmin(ModelAdmin):
@@ -384,5 +435,42 @@ class EmployeePermissionOverrideAdmin(ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'permission':
kwargs['queryset'] = _portal_permissions_qs()
kwargs['queryset'] = filter_portal_permissions_for_user(
_portal_permissions_qs(), request.user,
)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
def has_add_permission(self, request):
return request.user.is_superuser
def has_change_permission(self, request, obj=None):
return request.user.is_superuser
def has_delete_permission(self, request, obj=None):
return request.user.is_superuser
@admin.register(LeaveEntitlement)
class LeaveEntitlementAdmin(ModelAdmin):
list_display = ('employee', 'year', 'annual_days', 'additional_days', 'sick_days')
list_filter = ('year',)
search_fields = ('employee__username', 'employee__last_name', 'employee__first_name')
autocomplete_fields = ('employee',)
readonly_fields = ('uuid', 'created_at', 'updated_at')
@admin.register(EmployeeHrRequest)
class EmployeeHrRequestAdmin(ModelAdmin):
list_display = (
'employee', 'request_type', 'substitute', 'date_from', 'date_to',
'status', 'created_at', 'decided_by',
)
list_filter = ('status', 'request_type', 'created_at')
search_fields = (
'employee__username', 'employee__last_name', 'employee__first_name',
'note', 'decision_note',
)
list_select_related = ('employee', 'decided_by', 'substitute')
autocomplete_fields = ('employee', 'decided_by', 'substitute')
readonly_fields = ('uuid', 'created_at', 'updated_at', 'decided_at')
date_hierarchy = 'created_at'