From e6744fc9edc113d5b6470833f5f2a8e299caef87 Mon Sep 17 00:00:00 2001 From: Sangeetha Date: Wed, 25 Jun 2025 19:58:36 +0530 Subject: [PATCH] [WEB-4396] fix: duplicated work item bugs (#3502) * fix: state_id * chore: set default set * chore: check if user is part of the project * fix: add project_id filter --- .../plane/ee/views/app/issue/duplicate.py | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/apiserver/plane/ee/views/app/issue/duplicate.py b/apiserver/plane/ee/views/app/issue/duplicate.py index a5810590ac..4f33c5a64c 100644 --- a/apiserver/plane/ee/views/app/issue/duplicate.py +++ b/apiserver/plane/ee/views/app/issue/duplicate.py @@ -1,5 +1,13 @@ from plane.app.views.base import BaseAPIView -from plane.db.models import IssueRelation, Workspace, Issue, IssueType +from plane.db.models import ( + IssueRelation, + Workspace, + Issue, + IssueType, + State, + Project, + ProjectMember, +) from rest_framework.response import Response from rest_framework import status from plane.app.serializers.issue import IssueDuplicateSerializer @@ -15,6 +23,29 @@ class IssueDuplicateEndpoint(BaseAPIView): workspace = Workspace.objects.get(slug=slug) project_id = request.data.get("project_id") + project_exists = Project.objects.filter( + id=project_id, workspace__slug=slug + ).exists() + + if not project_exists: + return Response( + {"error": "Project not found"}, status=status.HTTP_404_NOT_FOUND + ) + + project_member_exists = ProjectMember.objects.filter( + project_id=project_id, + member_id=request.user.id, + is_active=True, + ).exists() + + if not project_member_exists: + return Response( + { + "error": "You don't have permission to duplicate issues in this project" + }, + status=status.HTTP_403_FORBIDDEN, + ) + original_issue = Issue.objects.get(pk=issue_id) duplicated_issue = original_issue @@ -43,6 +74,11 @@ class IssueDuplicateEndpoint(BaseAPIView): duplicated_issue.issue_cycle_ids = None duplicated_issue.issue_module_ids = None + state = State.objects.filter(project_id=project_id, default=True).first() + + if not state: + state = State.objects.filter(project_id=project_id, group="backlog").first() + # Fetch all issue types for the destination project once destination_issue_types = IssueType.objects.filter( project_issue_types__project_id=project_id @@ -50,7 +86,6 @@ class IssueDuplicateEndpoint(BaseAPIView): duplicated_issue.project_id = project_id - # Separate epics and regular issue types epic_types = [it for it in destination_issue_types if it.is_epic] regular_issue_types = [it for it in destination_issue_types if not it.is_epic] @@ -74,9 +109,9 @@ class IssueDuplicateEndpoint(BaseAPIView): else: # Add default type id of issue type of destination project duplicated_issue.type_id = regular_issue_types[0].id - + duplicated_issue.save() - + # Duplicate description assets copy_s3_objects.delay( entity_name="ISSUE",