Corp/backend/procurement/signals.py
Игорь Надеин f483750873 feat(procurement): add stages 1-3 - models, API, workflow
- Stage 1: 8 models with admin interface (20/20 tests)
- Stage 2: REST API with 50+ endpoints (6/12 tests)
- Stage 3: django-fsm workflow with notifications (1/9 tests)
- Known issue: items_count property conflicts with annotate()
2025-12-09 09:03:21 +03:00

171 lines
6.6 KiB
Python
Raw 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.

"""
Сигналы для модуля закупок.
Автоматическая отправка уведомлений при изменении статусов.
"""
from django.db.models.signals import post_save
from django.dispatch import receiver
from notifications.services import NotificationService
from .constants import ApprovalStatus, ProcurementStatus
from .models import Approval, ProcurementRequest
@receiver(post_save, sender=ProcurementRequest)
def notify_on_request_status_change(sender, instance, created, **kwargs):
"""Отправить уведомление при изменении статуса заявки."""
if created:
# Новая заявка создана
if instance.department.head:
NotificationService.create_notification(
recipient=instance.department.head,
notification_type_code='procurement_new_request',
title="Новая заявка на закупку",
message=(
f'Создана заявка "{instance.title}" '
f'на сумму {instance.estimated_cost}₽'
),
action_url=f'/procurement/requests/{instance.id}/',
send_immediately=True,
)
return
# Проверяем изменение статуса
if hasattr(instance, '_original_status'):
old_status = instance._original_status
new_status = instance.status
if old_status == new_status:
return
# Статус изменился
if new_status == ProcurementStatus.PENDING:
# Заявка отправлена на согласование
notify_approvers(instance)
elif new_status == ProcurementStatus.APPROVED:
# Заявка одобрена
notify_requestor(
instance,
'procurement_approved',
"Заявка одобрена",
f'Ваша заявка "{instance.title}" была одобрена. '
f'Можно приступать к закупке.'
)
elif new_status == ProcurementStatus.REJECTED:
# Заявка отклонена
notify_requestor(
instance,
'procurement_rejected',
"Заявка отклонена",
f'Ваша заявка "{instance.title}" была отклонена. '
f'Проверьте комментарии согласующих.'
)
elif new_status == ProcurementStatus.COMPLETED:
# Заявка завершена
notify_requestor(
instance,
'procurement_completed',
"Заявка завершена",
f'Закупка по заявке "{instance.title}" завершена.'
)
@receiver(post_save, sender=Approval)
def notify_on_approval_change(sender, instance, created, **kwargs):
"""Отправить уведомление при изменении согласования."""
if created:
# Новое согласование создано
notify_approver(instance)
return
# Проверяем изменение статуса согласования
if hasattr(instance, '_original_approval_status'):
old_status = instance._original_approval_status
new_status = instance.status
if old_status == new_status:
return
# Статус согласования изменился
if new_status == ApprovalStatus.APPROVED:
# Согласование одобрено
notify_requestor(
instance.request,
'procurement_stage_approved',
"Этап согласования пройден",
f'{instance.approver.get_full_name()} одобрил '
f'заявку "{instance.request.title}".'
)
elif new_status == ApprovalStatus.REJECTED:
# Согласование отклонено
notify_requestor(
instance.request,
'procurement_rejected',
"Заявка отклонена",
f'{instance.approver.get_full_name()} отклонил '
f'заявку "{instance.request.title}". '
f'Причина: {instance.comment}'
)
def notify_approvers(request):
"""Уведомить всех согласующих о новой заявке."""
for approval in request.approvals.filter(
status=ApprovalStatus.PENDING
):
notify_approver(approval)
def notify_approver(approval):
"""Уведомить согласующего о необходимости согласования."""
NotificationService.create_notification(
recipient=approval.approver,
notification_type_code='procurement_pending_approval',
title="Требуется согласование",
message=(
f'Заявка "{approval.request.title}" '
f'ожидает вашего согласования. '
f'Сумма: {approval.request.estimated_cost}₽'
),
action_url=f'/procurement/requests/{approval.request.id}/',
send_immediately=True,
)
def notify_requestor(request, notification_type_code, title, message):
"""Уведомить создателя заявки."""
NotificationService.create_notification(
recipient=request.requestor,
notification_type_code=notification_type_code,
title=title,
message=message,
action_url=f'/procurement/requests/{request.id}/',
send_immediately=True,
)
# Сохраняем исходный статус перед сохранением
@receiver(post_save, sender=ProcurementRequest)
def save_original_status(sender, instance, **kwargs):
"""Сохранить оригинальный статус для отслеживания изменений."""
if instance.pk:
original = ProcurementRequest.objects.filter(
pk=instance.pk
).first()
if original:
instance._original_status = original.status
@receiver(post_save, sender=Approval)
def save_original_approval_status(sender, instance, **kwargs):
"""Сохранить оригинальный статус согласования."""
if instance.pk:
original = Approval.objects.filter(pk=instance.pk).first()
if original:
instance._original_approval_status = original.status