"""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( "projects", "items__attachments__uploaded_by", "approvals__approver", ) filter_backends = [ DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter, ] filterset_fields = { "status": ["exact", "in"], "request_type": ["exact"], "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", "set_deferred", "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", "set_deferred", "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)