- Add birth_date, created_at/updated_at to Employee; remove username field - Introduce EmployeeAction for tracking HR events and is_actually_active/status properties - Create BotSubscriber model (OneToOne relation) for TG/WA binding - Implement Aiogram-based Telegram bot: • request contact to bind by phone or @username • department selection keyboard from DEPARTMENTS dict • async ORM via sync_to_async, custom filters for bound & active employees - Update profile view & template: • show “Bind Telegram/WhatsApp” button only when not already bound • preview avatar on upload - Remove registration tokens in favor of contact-based auth - Clean up templates, forms and add avatar validation mixin
117 lines
4.2 KiB
Python
117 lines
4.2 KiB
Python
# documents/admin.py
|
||
|
||
from django.contrib import admin
|
||
from django.utils.translation import gettext_lazy as _
|
||
from django.utils.html import format_html, mark_safe
|
||
from django.urls import reverse
|
||
from .models import Document, DocumentAcknowledgement
|
||
from django.contrib.auth import get_user_model
|
||
|
||
User = get_user_model()
|
||
|
||
|
||
class AcknowledgementInline(admin.TabularInline):
|
||
model = DocumentAcknowledgement
|
||
extra = 0
|
||
can_delete = False
|
||
verbose_name = _("Ознакомление")
|
||
verbose_name_plural = _("Ознакомления")
|
||
fields = ('user_link', 'acknowledged_at')
|
||
readonly_fields = ('user_link', 'acknowledged_at')
|
||
|
||
def user_link(self, obj):
|
||
url = reverse(
|
||
'admin:{}_{}_change'.format(
|
||
User._meta.app_label, User._meta.model_name
|
||
), args=[obj.user.pk]
|
||
)
|
||
return format_html('<a href="{}">{}</a>', url, obj.user.get_full_name() or obj.user)
|
||
user_link.short_description = _("Сотрудник")
|
||
|
||
|
||
@admin.register(Document)
|
||
class DocumentAdmin(admin.ModelAdmin):
|
||
inlines = [AcknowledgementInline]
|
||
|
||
list_display = (
|
||
'title', 'uploaded_at', 'sent_to_all',
|
||
# убрали 'pending_summary'
|
||
)
|
||
list_filter = ('sent_to_all', 'uploaded_at')
|
||
search_fields = ('title', 'description')
|
||
filter_horizontal = ('recipients',)
|
||
actions = ['send_document']
|
||
|
||
fieldsets = (
|
||
(None, {
|
||
'fields': ('title', 'file', 'description', 'recipients', 'sent_to_all'),
|
||
}),
|
||
(_('Meta'), {
|
||
'fields': ('uploaded_by', 'uploaded_at'),
|
||
'classes': ('collapse',),
|
||
}),
|
||
(_('Ознакомление'), {
|
||
'fields': ('recipients_summary', 'pending_list'),
|
||
}),
|
||
)
|
||
readonly_fields = (
|
||
'uploaded_by', 'uploaded_at',
|
||
'recipients_summary', 'pending_list',
|
||
)
|
||
|
||
def save_model(self, request, obj, form, change):
|
||
if not obj.pk:
|
||
obj.uploaded_by = request.user
|
||
super().save_model(request, obj, form, change)
|
||
|
||
def send_document(self, request, queryset):
|
||
from .tasks import send_document_to_recipients
|
||
for doc in queryset:
|
||
send_document_to_recipients.delay(doc.id)
|
||
self.message_user(request, _("Рассылка запущена."))
|
||
send_document.short_description = _('Отправить документ')
|
||
|
||
def get_recipients_qs(self, obj):
|
||
return User.objects.filter(is_active=True) if obj.sent_to_all else obj.recipients.all()
|
||
|
||
def recipients_summary(self, obj):
|
||
qs = self.get_recipients_qs(obj)
|
||
names = [u.get_full_name() or str(u) for u in qs[:5]]
|
||
count = qs.count()
|
||
if count > 5:
|
||
return format_html("{}… (ещё {})", ", ".join(names), count - 5)
|
||
return ", ".join(names) if names else _("— никто —")
|
||
recipients_summary.short_description = _('Получатели')
|
||
|
||
def pending_list(self, obj):
|
||
recipients = set(self.get_recipients_qs(obj))
|
||
acked_ids = set(
|
||
DocumentAcknowledgement.objects
|
||
.filter(document=obj)
|
||
.values_list('user_id', flat=True)
|
||
)
|
||
pending = [u for u in recipients if u.id not in acked_ids]
|
||
if not pending:
|
||
return format_html('<span style="color:green;">{}</span>', _("Все ознакомились"))
|
||
links = []
|
||
for u in pending:
|
||
url = reverse(
|
||
'admin:{}_{}_change'.format(
|
||
User._meta.app_label, User._meta.model_name
|
||
), args=[u.pk]
|
||
)
|
||
links.append(format_html('<a href="{}">{}</a>',
|
||
url, u.get_full_name() or u))
|
||
return mark_safe('<br>'.join(links))
|
||
pending_list.short_description = _('Кто ещё не ознакомился')
|
||
|
||
|
||
@admin.register(DocumentAcknowledgement)
|
||
class DocumentAcknowledgementAdmin(admin.ModelAdmin):
|
||
list_display = ('document', 'user', 'acknowledged_at')
|
||
list_filter = ('document', 'acknowledged_at')
|
||
search_fields = ('user__phone_number',
|
||
'user__last_name', 'document__title')
|
||
readonly_fields = ('user', 'document', 'acknowledged_at')
|
||
ordering = ('-acknowledged_at',)
|