All checks were successful
Deploy production / deploy (push) Successful in 13m7s
197 lines
7.0 KiB
Python
197 lines
7.0 KiB
Python
"""
|
||
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
|