Игорь Надеин 47bf0cbcb9
All checks were successful
Deploy production / deploy (push) Successful in 13m7s
Notify processing departments about subscriptions
2026-11-03 10:06:17 +03:00

197 lines
7.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Django сигналы для автоматической генерации уведомлений в модуле Procurement.
Обрабатывает события:
- Создание новой заявки на закупку
- Изменение статуса заявки
- Изменение статуса согласования
Все уведомления отправляются через универсальную систему notifications:
notify.send() → channels.py → Celery → WebSocket/Email/Push
"""
import logging
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from ..constants import ApprovalStatus, ProcurementStatus
from ..models import Approval, ProcurementRequest
from .handlers import (
notify_approvers,
notify_processing_department_request,
notify_request_approved,
notify_request_cancelled,
notify_request_completed,
notify_request_in_progress,
notify_request_rejected,
notify_request_returned_to_processing,
notify_stage_approved,
notify_stage_rejected,
)
logger = logging.getLogger(__name__)
@receiver(post_save, sender=ProcurementRequest)
def create_request_notifications(sender, instance, created, **kwargs):
"""
Создает уведомления при создании или изменении заявки.
Обрабатывает:
1. Новая адресная заявка - уведомление отделу-исполнителю
2. Изменение статуса - уведомления соответствующим получателям
"""
try:
request = instance
if created:
if (
request.status == ProcurementStatus.WAITING
and request.processing_department_id
):
notify_processing_department_request(
request,
actor=getattr(request, "_notification_actor", None),
)
return
# Проверяем изменение статуса
if hasattr(request, "_original_status"):
old_status = request._original_status
new_status = request.status
if old_status == new_status:
return
_handle_status_change(request, new_status)
except Exception as error:
logger.exception(
"Procurement request notification signal failed",
extra={
"event": "procurement.notification_signal.failed",
"procurement_request_id": getattr(instance, "id", None),
"signal": "request_saved",
"error_type": type(error).__name__,
},
)
def _handle_status_change(request, new_status):
"""
Обработать изменение статуса заявки.
Args:
request: Объект ProcurementRequest
new_status: Новый статус
"""
if new_status == ProcurementStatus.PENDING:
# Заявка отправлена на согласование
notify_approvers(request)
elif (
getattr(request, "_original_status", None) == ProcurementStatus.PENDING
and request.processing_department_id
and new_status
in {
ProcurementStatus.WAITING,
ProcurementStatus.IN_PROGRESS,
}
):
# Адресная заявка после согласования возвращается в тот же отдел.
notify_request_approved(request)
notify_request_returned_to_processing(request)
elif new_status == ProcurementStatus.APPROVED:
# Заявка одобрена
notify_request_approved(request)
elif new_status == ProcurementStatus.REJECTED:
# Заявка отклонена
if not getattr(request, "_requestor_rejected_notified", False):
notify_request_rejected(request)
elif new_status == ProcurementStatus.COMPLETED:
# Заявка завершена
notify_request_completed(request)
elif new_status == ProcurementStatus.IN_PROGRESS:
# Заявка взята в работу
executor = request.executor
notify_request_in_progress(request, executor)
elif new_status == ProcurementStatus.CANCELLED:
# Заявка отменена
notify_request_cancelled(request)
@receiver(post_save, sender=Approval)
def create_approval_notifications(sender, instance, created, **kwargs):
"""
Создает уведомления при создании или изменении согласования.
Обрабатывает:
1. Одобрение - уведомление создателю заявки и следующему этапу
2. Отклонение - уведомление создателю заявки
"""
try:
approval = instance
if created:
# Нотификацию при submit запускает сигнал заявки, когда уже
# созданы все Approval и можно определить только текущий этап.
return
# Проверяем изменение статуса согласования
if hasattr(approval, "_original_approval_status"):
old_status = approval._original_approval_status
new_status = approval.status
if old_status == new_status:
return
# Обработка изменения статуса согласования
if new_status == ApprovalStatus.APPROVED:
notify_stage_approved(approval)
elif new_status == ApprovalStatus.REJECTED:
notify_stage_rejected(approval)
except Exception as error:
logger.exception(
"Procurement approval notification signal failed",
extra={
"event": "procurement.notification_signal.failed",
"approval_id": getattr(instance, "id", None),
"signal": "approval_saved",
"error_type": type(error).__name__,
},
)
@receiver(pre_save, sender=ProcurementRequest)
def track_request_status_change(sender, instance, **kwargs):
"""
Сохранить оригинальный статус для отслеживания изменений.
"""
if instance.pk:
try:
original = ProcurementRequest.objects.get(pk=instance.pk)
instance._original_status = original.status
except ProcurementRequest.DoesNotExist:
pass
@receiver(pre_save, sender=Approval)
def track_approval_status_change(sender, instance, **kwargs):
"""
Сохранить оригинальный статус согласования для отслеживания изменений.
"""
if instance.pk:
try:
original = Approval.objects.get(pk=instance.pk)
instance._original_approval_status = original.status
except Approval.DoesNotExist:
pass