feat(requests): Этап 4 - обновлена система уведомлений для получателей

- notify_new_request теперь отправляет уведомления всем recipients и cc_users
- При sent_to_all_department уведомления идут всем сотрудникам отделов
- notify_status_change уведомляет автора, получателей, CC и сотрудников отделов
- create_comment_notification уведомляет всех заинтересованных лиц
- Добавлены метаданные is_primary_recipient, is_cc, is_approver
- Сохранена обратная совместимость со старым полем department
This commit is contained in:
admin i 2025-12-02 17:17:22 +03:00
parent ca056a15f8
commit 60fc506ef9

View File

@ -65,6 +65,12 @@ def track_status_change(sender, instance, **kwargs):
def create_comment_notification(sender, instance, created, **kwargs): def create_comment_notification(sender, instance, created, **kwargs):
""" """
Создает уведомление при добавлении комментария к заявке. Создает уведомление при добавлении комментария к заявке.
Уведомляет:
- Автора заявки
- Всех получателей
- Всех в копии
- Согласующего
- Сотрудников отделов (если sent_to_all_department)
""" """
if not created: if not created:
return return
@ -72,35 +78,42 @@ def create_comment_notification(sender, instance, created, **kwargs):
comment = instance comment = instance
request_obj = comment.request request_obj = comment.request
author = comment.author author = comment.author
recipients_set = set()
# Уведомляем автора заявки (если комментарий не от него) # Автор заявки
if request_obj.employee.id != author.id: if request_obj.employee.id != author.id:
NotificationService.create_notification( recipients_set.add(request_obj.employee)
recipient=request_obj.employee,
notification_type_code='request_comment',
title='Новый комментарий к вашей заявке',
message=(
f'{author.get_full_name() or author.username} '
f'прокомментировал заявку: {comment.text[:100]}'
),
content_object=request_obj,
action_url=f'/requests/{request_obj.id}/',
metadata={
'request_id': request_obj.id,
'request_type': request_obj.type,
'comment_id': comment.id,
'author_id': author.id,
}
)
# Также уведомляем согласующего (если есть и это не он) # Получатели
if (request_obj.approver and recipients_set.update(
request_obj.approver.id != author.id and request_obj.recipients.filter(is_active=True).exclude(id=author.id)
request_obj.approver.id != request_obj.employee.id): )
# CC
recipients_set.update(
request_obj.cc_users.filter(is_active=True).exclude(id=author.id)
)
# Согласующий
if request_obj.approver and request_obj.approver.id != author.id:
recipients_set.add(request_obj.approver)
# Если sent_to_all_department - все сотрудники отделов
if request_obj.sent_to_all_department:
dept_employees = Employee.objects.filter(
employee_departments__department__in=request_obj.departments.all(),
employee_departments__is_active=True,
is_active=True
).exclude(id__in=[author.id, request_obj.employee.id]).distinct()
recipients_set.update(dept_employees)
# Отправляем уведомления
for recipient in recipients_set:
NotificationService.create_notification( NotificationService.create_notification(
recipient=request_obj.approver, recipient=recipient,
notification_type_code='request_comment', notification_type_code='request_comment',
title='Комментарий к заявке', title='Новый комментарий к заявке',
message=( message=(
f'{author.get_full_name() or author.username} ' f'{author.get_full_name() or author.username} '
f'прокомментировал заявку: {comment.text[:100]}' f'прокомментировал заявку: {comment.text[:100]}'
@ -120,108 +133,193 @@ def create_comment_notification(sender, instance, created, **kwargs):
def notify_new_request(request_obj): def notify_new_request(request_obj):
""" """
Отправляет уведомление о новой заявке ответственным лицам. Отправляет уведомление о новой заявке:
- Всем основным получателям (recipients)
- Всем в копии (cc_users)
- Согласующему (approver)
- Руководителям отделов
- Пользователям с правом can_process_requests
При sent_to_all_department=True отправляет всем сотрудникам отделов
""" """
# Определяем получателей в зависимости от типа заявки recipients_set = set()
recipients = []
# 1. Руководитель отдела сотрудника # 1. Основные получатели
for recipient in request_obj.recipients.filter(is_active=True):
recipients_set.add(recipient)
# 2. Копия (CC)
for cc_user in request_obj.cc_users.filter(is_active=True):
recipients_set.add(cc_user)
# 3. Если sent_to_all_department - все сотрудники отделов
if request_obj.sent_to_all_department:
dept_employees = Employee.objects.filter(
employee_departments__department__in=request_obj.departments.all(),
employee_departments__is_active=True,
is_active=True
).exclude(id=request_obj.employee.id).distinct()
recipients_set.update(dept_employees)
# 4. Согласующий
if request_obj.approver and request_obj.approver.id != request_obj.employee.id:
recipients_set.add(request_obj.approver)
# 5. Руководители отделов
for department in request_obj.departments.all():
if department.head and department.head.id != request_obj.employee.id:
recipients_set.add(department.head)
# Также проверяем старое поле department для обратной совместимости
if request_obj.department and request_obj.department.head: if request_obj.department and request_obj.department.head:
head = request_obj.department.head if request_obj.department.head.id != request_obj.employee.id:
if head.id != request_obj.employee.id: recipients_set.add(request_obj.department.head)
recipients.append(head)
# 2. Если есть назначенный согласующий # 6. Пользователи с правом обрабатывать заявки в этих отделах
if request_obj.approver: dept_ids = list(request_obj.departments.values_list('id', flat=True))
if request_obj.approver not in recipients: if request_obj.department_id and request_obj.department_id not in dept_ids:
recipients.append(request_obj.approver) dept_ids.append(request_obj.department_id)
# 3. Пользователи с правом обрабатывать заявки if dept_ids:
users_with_permission = Employee.objects.filter( dept_processors = Employee.objects.filter(
user_permissions__codename='can_process_requests' employee_departments__department_id__in=dept_ids,
).exclude(id=request_obj.employee.id) employee_departments__is_active=True,
employee_departments__role__scoped_permissions__code='can_process_requests',
is_active=True
).exclude(id=request_obj.employee.id).distinct()
recipients_set.update(dept_processors)
for user in users_with_permission: # Определяем тип уведомления для каждого получателя
if user not in recipients: author_name = (
recipients.append(user) request_obj.employee.get_full_name() or
request_obj.employee.username
)
# Отправляем уведомления for recipient in recipients_set:
for recipient in recipients: # Определяем роль получателя
is_primary = request_obj.recipients.filter(id=recipient.id).exists()
is_cc = request_obj.cc_users.filter(id=recipient.id).exists()
is_approver = request_obj.approver_id == recipient.id
# Формируем заголовок и сообщение
if is_approver:
title = f'Новая заявка на согласование от {author_name}'
notification_type = 'request_new'
elif is_primary:
title = f'Вам адресована заявка от {author_name}'
notification_type = 'request_new'
elif is_cc:
title = f'Вы в копии заявки от {author_name}'
notification_type = 'request_new'
else:
title = f'Новая заявка в отделе от {author_name}'
notification_type = 'request_new'
message = (
f'Тип: {request_obj.get_type_display()}. '
f'{request_obj.comment[:100] if request_obj.comment else ""}'
)
NotificationService.create_notification( NotificationService.create_notification(
recipient=recipient, recipient=recipient,
notification_type_code='request_new', notification_type_code=notification_type,
title=f'Новая заявка от {request_obj.employee.get_full_name()}', title=title,
message=( message=message,
f'Тип: {request_obj.get_type_display()}. '
f'{request_obj.comment[:100] if request_obj.comment else ""}'
),
content_object=request_obj, content_object=request_obj,
action_url=f'/requests/{request_obj.id}/', action_url=f'/requests/{request_obj.id}/',
metadata={ metadata={
'request_id': request_obj.id, 'request_id': request_obj.id,
'request_type': request_obj.type, 'request_type': request_obj.type,
'employee_id': request_obj.employee.id, 'employee_id': request_obj.employee.id,
'department_id': ( 'is_primary_recipient': is_primary,
request_obj.department.id 'is_cc': is_cc,
if request_obj.department else None 'is_approver': is_approver,
),
} }
) )
def notify_status_change(request_obj, old_status, new_status): def notify_status_change(request_obj, old_status, new_status):
""" """
Отправляет уведомление при изменении статуса заявки. Уведомляет о изменении статуса:
- Автора
- Всех получателей (recipients)
- Всех в копии (cc_users)
- Сотрудников отделов (если sent_to_all_department)
""" """
# Уведомляем автора заявки recipients_to_notify = set()
recipient = request_obj.employee
# Определяем тип уведомления и сообщение # 1. Автор
if new_status == 'approved': recipients_to_notify.add(request_obj.employee)
notification_type = 'request_approved'
title = 'Ваша заявка одобрена'
approver_name = (
request_obj.approver.get_full_name()
if request_obj.approver else 'Руководитель'
)
message = (
f'Заявка "{request_obj.get_type_display()}" '
f'одобрена пользователем {approver_name}'
)
elif new_status == 'rejected':
notification_type = 'request_rejected'
title = 'Ваша заявка отклонена'
approver_name = (
request_obj.approver.get_full_name()
if request_obj.approver else 'Руководитель'
)
message = (
f'Заявка "{request_obj.get_type_display()}" '
f'отклонена пользователем {approver_name}'
)
else:
# Общее уведомление об изменении статуса
notification_type = 'request_status_changed'
title = 'Статус заявки изменен'
message = (
f'Статус заявки "{request_obj.get_type_display()}" '
f'изменен: {old_status} → {new_status}'
)
NotificationService.create_notification( # 2. Основные получатели
recipient=recipient, recipients_to_notify.update(
notification_type_code=notification_type, request_obj.recipients.filter(is_active=True)
title=title,
message=message,
content_object=request_obj,
action_url=f'/requests/{request_obj.id}/',
metadata={
'request_id': request_obj.id,
'request_type': request_obj.type,
'old_status': old_status,
'new_status': new_status,
'approver_id': (
request_obj.approver.id if request_obj.approver else None
),
}
) )
# 3. Копия
recipients_to_notify.update(
request_obj.cc_users.filter(is_active=True)
)
# 4. Если sent_to_all_department - все сотрудники отделов
if request_obj.sent_to_all_department:
dept_employees = Employee.objects.filter(
employee_departments__department__in=request_obj.departments.all(),
employee_departments__is_active=True,
is_active=True
).exclude(id=request_obj.employee.id).distinct()
recipients_to_notify.update(dept_employees)
# Формируем уведомления
for recipient in recipients_to_notify:
if new_status == 'approved':
notification_type = 'request_approved'
title = 'Заявка одобрена'
approver_name = (
request_obj.approver.get_full_name()
if request_obj.approver else 'Руководитель'
)
message = (
f'Заявка "{request_obj.get_type_display()}" '
f'одобрена пользователем {approver_name}'
)
elif new_status == 'rejected':
notification_type = 'request_rejected'
title = 'Заявка отклонена'
approver_name = (
request_obj.approver.get_full_name()
if request_obj.approver else 'Руководитель'
)
message = (
f'Заявка "{request_obj.get_type_display()}" '
f'отклонена пользователем {approver_name}'
)
else:
# Общее уведомление об изменении статуса
notification_type = 'request_status_changed'
title = 'Статус заявки изменен'
message = (
f'Статус заявки "{request_obj.get_type_display()}" '
f'изменен: {old_status} → {new_status}'
)
NotificationService.create_notification(
recipient=recipient,
notification_type_code=notification_type,
title=title,
message=message,
content_object=request_obj,
action_url=f'/requests/{request_obj.id}/',
metadata={
'request_id': request_obj.id,
'request_type': request_obj.type,
'old_status': old_status,
'new_status': new_status,
'approver_id': (
request_obj.approver.id if request_obj.approver else None
),
}
)