Add RRO, portal section perms, dislocation presets, and hiring intern flow.
Ship cash-register registry, section-based portal access, configurable dislocation columns, and trial-hire intern accounts with credentials PDF.
This commit is contained in:
+133
-4
@@ -1,14 +1,22 @@
|
||||
from django import forms
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.html import format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from unfold.admin import ModelAdmin, TabularInline
|
||||
|
||||
from core.models import Directory
|
||||
from .models import Employee, PermissionGroup
|
||||
from core.models import Directory, PortalAccess
|
||||
from .models import Employee, PermissionGroup, EmployeeDefaults, EmployeePermissionOverride
|
||||
from apps.visit_manager.models import WorkSchedule, AttendanceLog
|
||||
|
||||
|
||||
def _portal_permissions_qs():
|
||||
ct = ContentType.objects.get_for_model(PortalAccess, for_concrete_model=False)
|
||||
return Permission.objects.filter(content_type=ct, codename__startswith='access_').order_by('codename')
|
||||
|
||||
|
||||
class HierarchicalDepartmentChoiceField(forms.ModelChoiceField):
|
||||
"""Поле выбора отдела с отступами по иерархии в выпадающем списке."""
|
||||
|
||||
@@ -78,6 +86,20 @@ class WorkScheduleInline(TabularInline):
|
||||
return False
|
||||
|
||||
|
||||
class EmployeePermissionOverrideInline(TabularInline):
|
||||
model = EmployeePermissionOverride
|
||||
extra = 0
|
||||
fields = ('permission', 'override_type', 'valid_from', 'valid_until', 'is_active', 'comment')
|
||||
autocomplete_fields = []
|
||||
verbose_name = _('Доп. право на раздел портала')
|
||||
verbose_name_plural = _('Доп. права на разделы портала')
|
||||
|
||||
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||
if db_field.name == 'permission':
|
||||
kwargs['queryset'] = _portal_permissions_qs()
|
||||
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||
|
||||
|
||||
class AttendanceLogInline(TabularInline):
|
||||
model = AttendanceLog
|
||||
fk_name = 'employee'
|
||||
@@ -98,7 +120,7 @@ class AttendanceLogInline(TabularInline):
|
||||
@admin.register(Employee)
|
||||
class EmployeeAdmin(ModelAdmin):
|
||||
form = EmployeeAdminForm
|
||||
inlines = [WorkScheduleInline, AttendanceLogInline]
|
||||
inlines = [EmployeePermissionOverrideInline, WorkScheduleInline, AttendanceLogInline]
|
||||
|
||||
class Media:
|
||||
css = {'all': ('users/css/employee_admin.css',)}
|
||||
@@ -256,4 +278,111 @@ class EmployeeAdmin(ModelAdmin):
|
||||
)
|
||||
except Exception:
|
||||
return obj.department.name or '—'
|
||||
department_display.short_description = _('Отдел')
|
||||
department_display.short_description = _('Отдел')
|
||||
|
||||
@admin.register(EmployeeDefaults)
|
||||
class EmployeeDefaultsAdmin(ModelAdmin):
|
||||
"""Единственная запись настроек по умолчанию для сотрудников."""
|
||||
|
||||
fieldsets = (
|
||||
(_('Офисные сотрудники'), {
|
||||
'fields': ('office_email_domain',),
|
||||
'description': _(
|
||||
'При утверждении заявки на найм офисному сотруднику '
|
||||
'назначается рабочий email: логин@домен.'
|
||||
),
|
||||
}),
|
||||
)
|
||||
|
||||
def has_add_permission(self, request):
|
||||
return not EmployeeDefaults.objects.exists()
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
|
||||
class PermissionGroupAdminForm(forms.ModelForm):
|
||||
portal_permissions = forms.ModelMultipleChoiceField(
|
||||
label=_('Разделы портала'),
|
||||
queryset=Permission.objects.none(),
|
||||
required=False,
|
||||
widget=admin.widgets.FilteredSelectMultiple(_('разделы'), is_stacked=False),
|
||||
help_text=_('Базовый набор доступов к разделам для этой роли. Доп. права сотруднику — через overrides.'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PermissionGroup
|
||||
fields = ('group', 'description', 'is_active')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['portal_permissions'].queryset = _portal_permissions_qs()
|
||||
if self.instance and self.instance.pk and self.instance.group_id:
|
||||
portal_pks = set(_portal_permissions_qs().values_list('pk', flat=True))
|
||||
self.fields['portal_permissions'].initial = [
|
||||
p for p in self.instance.group.permissions.all() if p.pk in portal_pks
|
||||
]
|
||||
|
||||
def save_portal_permissions(self, obj):
|
||||
portal_qs = list(_portal_permissions_qs())
|
||||
selected = self.cleaned_data.get('portal_permissions')
|
||||
if selected is None:
|
||||
return
|
||||
group = obj.group
|
||||
group.permissions.remove(*portal_qs)
|
||||
group.permissions.add(*selected)
|
||||
|
||||
|
||||
@admin.register(PermissionGroup)
|
||||
class PermissionGroupAdmin(ModelAdmin):
|
||||
form = PermissionGroupAdminForm
|
||||
list_display = ('group', 'is_active', 'description')
|
||||
list_filter = ('is_active',)
|
||||
search_fields = ('group__name', 'description')
|
||||
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'fields': ('group', 'description', 'is_active', 'portal_permissions'),
|
||||
}),
|
||||
)
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
super().save_model(request, obj, form, change)
|
||||
form.save_portal_permissions(obj)
|
||||
|
||||
@admin.register(EmployeePermissionOverride)
|
||||
class EmployeePermissionOverrideAdmin(ModelAdmin):
|
||||
list_display = (
|
||||
'employee', 'permission', 'override_type',
|
||||
'valid_from', 'valid_until', 'is_active',
|
||||
)
|
||||
list_filter = ('override_type', 'is_active')
|
||||
search_fields = (
|
||||
'employee__username', 'employee__last_name', 'employee__first_name',
|
||||
'permission__codename', 'comment',
|
||||
)
|
||||
list_select_related = ('employee', 'permission')
|
||||
autocomplete_fields = ('employee',)
|
||||
readonly_fields = ('created_at', 'updated_at', 'uuid')
|
||||
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'fields': ('employee', 'permission', 'override_type', 'is_active'),
|
||||
'description': _(
|
||||
'grant — выдать раздел сверх группы; '
|
||||
'revoke — запретить раздел даже если право есть в группе.'
|
||||
),
|
||||
}),
|
||||
(_('Срок действия'), {
|
||||
'fields': ('valid_from', 'valid_until', 'comment'),
|
||||
}),
|
||||
(_('Служебное'), {
|
||||
'classes': ('collapse',),
|
||||
'fields': ('uuid', 'created_at', 'updated_at'),
|
||||
}),
|
||||
)
|
||||
|
||||
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||
if db_field.name == 'permission':
|
||||
kwargs['queryset'] = _portal_permissions_qs()
|
||||
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user