diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 530ac27b..f7317873 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -33,7 +33,7 @@ from taiga.base.utils.slug import slugify_uniquely from taiga.projects.mixins.ordering import BulkUpdateOrderMixin from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin -from taiga.projects.userstories.models import UserStory +from taiga.projects.userstories.models import UserStory, RolePoints from taiga.projects.tasks.models import Task from taiga.projects.issues.models import Issue from taiga.permissions import service as permissions_service @@ -277,7 +277,7 @@ class ProjectViewSet(ModelCrudViewSet): ## Custom values for selectors ###################################################### -class PointsViewSet(ModelCrudViewSet, BulkUpdateOrderMixin): +class PointsViewSet(MoveOnDestroyMixin, ModelCrudViewSet, BulkUpdateOrderMixin): model = models.Points serializer_class = serializers.PointsSerializer permission_classes = (permissions.PointsPermission,) @@ -286,7 +286,9 @@ class PointsViewSet(ModelCrudViewSet, BulkUpdateOrderMixin): bulk_update_param = "bulk_points" bulk_update_perm = "change_points" bulk_update_order_action = services.bulk_update_points_order - + move_on_destroy_related_class = RolePoints + move_on_destroy_related_field = "points" + move_on_destroy_project_default_field = "default_points" class UserStoryStatusViewSet(MoveOnDestroyMixin, ModelCrudViewSet, BulkUpdateOrderMixin): model = models.UserStoryStatus diff --git a/taiga/projects/mixins/on_destroy.py b/taiga/projects/mixins/on_destroy.py index 681098be..6fe69c7b 100644 --- a/taiga/projects/mixins/on_destroy.py +++ b/taiga/projects/mixins/on_destroy.py @@ -32,12 +32,11 @@ class MoveOnDestroyMixin: return super().destroy(request, *args, **kwargs) obj = self.get_object_or_none() - move_item = get_object_or_404(self.model, project=obj.project, id=move_to) + move_item = get_object_or_404(self.model, id=move_to) self.check_permissions(request, 'destroy', obj) - qs = self.move_on_destroy_related_class.objects.filter(project=obj.project, - **{self.move_on_destroy_related_field: obj}) + qs = self.move_on_destroy_related_class.objects.filter(**{self.move_on_destroy_related_field: obj}) qs.update(**{self.move_on_destroy_related_field: move_item}) if getattr(obj.project, self.move_on_destroy_project_default_field) == obj: diff --git a/taiga/projects/userstories/models.py b/taiga/projects/userstories/models.py index c0abc9b4..5424cb7f 100644 --- a/taiga/projects/userstories/models.py +++ b/taiga/projects/userstories/models.py @@ -48,6 +48,9 @@ class RolePoints(models.Model): def __str__(self): return "{}: {}".format(self.role.name, self.points.name) + @property + def project(self): + return self.user_story.project class UserStory(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models.Model): ref = models.BigIntegerField(db_index=True, null=True, blank=True, default=None, diff --git a/tests/integration/test_projects.py b/tests/integration/test_projects.py index f7c01b14..6efa9a65 100644 --- a/tests/integration/test_projects.py +++ b/tests/integration/test_projects.py @@ -3,6 +3,7 @@ from taiga.base.utils import json from taiga.projects.services import stats as stats_services from taiga.projects.history.services import take_snapshot from taiga.permissions.permissions import ANON_PERMISSIONS +from taiga.projects.models import Project from .. import factories as f @@ -252,3 +253,27 @@ def test_anon_permissions_generation_when_making_project_public(client): anon_permissions = list(map(lambda perm: perm[0], ANON_PERMISSIONS)) assert set(anon_permissions).issubset(set(response.data["anon_permissions"])) assert set(anon_permissions).issubset(set(response.data["public_permissions"])) + + +def test_destroy_point_and_reassign(client): + project = f.ProjectFactory.create() + f.MembershipFactory.create(project=project, user=project.owner, is_owner=True) + p1 = f.PointsFactory(project=project) + project.default_points = p1 + project.save() + p2 = f.PointsFactory(project=project) + user_story = f.UserStoryFactory.create(project=project) + rp1 = f.RolePointsFactory.create(user_story=user_story, points=p1) + + url = reverse("points-detail", args=[p1.pk]) + "?moveTo={}".format(p2.pk) + + client.login(project.owner) + + assert user_story.role_points.all()[0].points.id == p1.id + assert project.default_points.id == p1.id + + response = client.delete(url) + + assert user_story.role_points.all()[0].points.id == p2.id + project = Project.objects.get(id=project.id) + assert project.default_points.id == p2.id