177 lines
5.8 KiB
Python

"""ViewSet composition for procurement requests."""
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters, permissions, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from procurement.activity import create_procurement_activity
from procurement.models import (
ProcurementActivityAction,
ProcurementActivityObjectKind,
ProcurementRequest,
)
from ..permissions import (
CanApproveProcurementRequest,
CanManageProcurementRequest,
)
from ..serializers import (
ProcurementRequestActivitySerializer,
ProcurementRequestCreateSerializer,
ProcurementRequestDetailSerializer,
ProcurementRequestListSerializer,
)
from .mixins import (
ProcurementRequestQueryMixin,
RequestCommentsMixin,
RequestExecutionMixin,
RequestWorkflowMixin,
)
class ProcurementRequestViewSet(
ProcurementRequestQueryMixin,
RequestWorkflowMixin,
RequestCommentsMixin,
RequestExecutionMixin,
viewsets.ModelViewSet,
):
"""ViewSet для заявок на закупку."""
queryset = ProcurementRequest.objects.select_related(
"department",
"processing_department",
"requestor",
"executor",
).prefetch_related(
"items__attachments__uploaded_by",
"approvals__approver",
)
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter,
]
filterset_fields = {
"status": ["exact", "in"],
"urgency": ["exact"],
"department": ["exact"],
"processing_department": ["exact"],
"requestor": ["exact"],
"executor": ["exact"],
"fulfillment_status": ["exact"],
}
search_fields = ["title", "description"]
ordering_fields = ["created_at", "status"]
ordering = None
def get_serializer_class(self):
"""Выбрать сериализатор в зависимости от действия."""
if self.action == "create":
return ProcurementRequestCreateSerializer
if self.action in {
"retrieve",
"submit",
"approve",
"reject",
"start_work",
"complete",
"cancel",
"mark_all_received",
"notify_arrival",
"set_viewed",
"approval_options",
"activity",
}:
return ProcurementRequestDetailSerializer
return ProcurementRequestListSerializer
@action(detail=True, methods=["get"], url_path="activity")
def activity(self, request, pk=None):
procurement_request = self.get_object()
queryset = procurement_request.activities.select_related("actor").order_by(
"-created_at",
"-id",
)
serializer = ProcurementRequestActivitySerializer(queryset, many=True)
return Response(serializer.data)
def perform_update(self, serializer):
tracked_fields = [
"title",
"description",
"department_id",
"processing_department_id",
"urgency",
"actual_cost",
]
before = {
field: getattr(serializer.instance, field) for field in tracked_fields
}
procurement_request = serializer.save()
changed_fields = [
field
for field in tracked_fields
if before[field] != getattr(procurement_request, field)
]
if changed_fields:
create_procurement_activity(
procurement_request,
self.request.user,
ProcurementActivityAction.UPDATED,
object_kind=ProcurementActivityObjectKind.REQUEST,
object_id=procurement_request.pk,
metadata={"fields": changed_fields},
)
def get_permissions(self):
"""Выбрать права доступа в зависимости от действия."""
if self.action in {"approve", "reject"}:
permission_classes = [CanApproveProcurementRequest]
elif self.action in {
"submit",
"set_viewed",
"create_options",
"approval_options",
}:
permission_classes = [permissions.IsAuthenticated]
else:
permission_classes = [CanManageProcurementRequest]
return [permission() for permission in permission_classes]
def _attach_linked_task_payloads(
self,
requests: list[ProcurementRequest],
) -> None:
"""Предзагрузить компактные бейджи задач для списка закупок."""
if not requests:
return
user = getattr(self.request, "user", None)
from tasks.models import TaskLinkedObjectKind
from tasks.presentation import linked_task_badges_by_object
mapping = linked_task_badges_by_object(
model=ProcurementRequest,
kind=TaskLinkedObjectKind.PROCUREMENT_REQUEST,
object_ids=[request_obj.id for request_obj in requests],
user=user,
)
for request_obj in requests:
request_obj._linked_task_payloads = mapping.get(request_obj.id, [])
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
self._attach_linked_task_payloads(list(page))
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
requests = list(queryset)
self._attach_linked_task_payloads(requests)
serializer = self.get_serializer(requests, many=True)
return Response(serializer.data)