from django.utils import timezone from rest_framework import viewsets, permissions, status from rest_framework.decorators import action from rest_framework.response import Response from .models import QCResult from .serializers import QCResultSerializer, QCResultCreateSerializer from apps.compliance.services import create_coa_for_qc class IsOpsOrAdmin(permissions.BasePermission): def has_permission(self, request, view): user = request.user return bool(user and user.is_authenticated and (user.role in ("ops", "admin") or user.is_superuser)) class QCResultViewSet(viewsets.ModelViewSet): queryset = QCResult.objects.all() permission_classes = [permissions.IsAuthenticated] def get_serializer_class(self): if self.action == "create": return QCResultCreateSerializer return QCResultSerializer def perform_create(self, serializer): serializer.save() @action(detail=True, methods=["post"], permission_classes=[IsOpsOrAdmin]) def verify(self, request, pk=None): """Four-eyes quality approval. A separate authorised user validates the entered result. The submitter cannot approve their own result.""" qc = self.get_object() user = request.user if qc.verification_status == QCResult.VerificationStatus.APPROVED: return Response({"detail": "QC result already approved"}, status=status.HTTP_400_BAD_REQUEST) if qc.entered_by is not None and qc.entered_by_id == user.id: return Response( {"detail": "The submitter cannot approve their own QC result (four-eyes control)"}, status=status.HTTP_400_BAD_REQUEST, ) approved = request.data.get("approved", "true") approved = str(approved).lower() in ("true", "1", "yes") if approved: if not qc.overall_pass: return Response( {"detail": "A failed QC result cannot be approved; it remains quarantined with an open CAPA"}, status=status.HTTP_400_BAD_REQUEST, ) qc.verification_status = QCResult.VerificationStatus.APPROVED qc.verified_by = user qc.approved_at = timezone.now() qc.save() coa = create_coa_for_qc(qc, approved_by=user) return Response( { "detail": "QC result approved", "coa_id": str(coa.id), "coa_status": coa.status, "coa_url": coa.file_url, } ) qc.verification_status = QCResult.VerificationStatus.REJECTED qc.verified_by = user qc.approved_at = timezone.now() qc.save() return Response({"detail": "QC result rejected"})