diff --git a/taiga/projects/tasks/signals.py b/taiga/projects/tasks/signals.py
index cbc633d1..03e65ddd 100644
--- a/taiga/projects/tasks/signals.py
+++ b/taiga/projects/tasks/signals.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+from contextlib import suppress
+from django.core.exceptions import ObjectDoesNotExist
####################################
# Signals for cached prev task
@@ -84,7 +86,8 @@ def _try_to_close_or_open_milestone_when_create_or_edit_task(instance):
def _try_to_close_milestone_when_delete_task(instance):
- from taiga.projects.milestones import services as milestone_service
+ from taiga.projects.milestones import services
- if instance.milestone_id and milestone_service.calculate_milestone_is_closed(instance.milestone):
- milestone_service.close_milestone(instance.milestone)
+ with suppress(ObjectDoesNotExist):
+ if instance.milestone_id and services.calculate_milestone_is_closed(instance.milestone):
+ services.close_milestone(instance.milestone)
diff --git a/tests/integration/test_close_uss.py b/tests/integration/test_close_uss.py
index 64f515d8..992f30de 100644
--- a/tests/integration/test_close_uss.py
+++ b/tests/integration/test_close_uss.py
@@ -15,12 +15,13 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from tests import factories as f
-
-from taiga.projects.userstories.models import UserStory
import pytest
+from taiga.projects.userstories.models import UserStory
+from taiga.projects.tasks.models import Task
+
+from tests import factories as f
pytestmark = pytest.mark.django_db
@@ -191,7 +192,33 @@ def test_us_delete_task_then_all_closed(data):
assert data.user_story1.is_closed is True
-def test_us_change_task_us_then_all_closed(data):
+def test_auto_close_userstory_with_milestone_when_task_and_milestone_are_removed(data):
+ milestone = f.MilestoneFactory.create()
+
+ data.task1.status = data.task_closed_status
+ data.task1.milestone = milestone
+ data.task1.save()
+ data.task2.status = data.task_closed_status
+ data.task2.milestone = milestone
+ data.task2.save()
+ data.task3.status = data.task_open_status
+ data.task3.milestone = milestone
+ data.task3.save()
+ data.user_story1.milestone = milestone
+ data.user_story1.save()
+
+ data.user_story1 = UserStory.objects.get(pk=data.user_story1.pk)
+ assert data.user_story1.is_closed is False
+
+ data.task3 = Task.objects.get(pk=data.task3.pk)
+ milestone.delete()
+ data.task3.delete()
+
+ data.user_story1 = UserStory.objects.get(pk=data.user_story1.pk)
+ assert data.user_story1.is_closed is False
+
+
+def test_auto_close_us_when_all_tasks_are_changed_to_close_status(data):
data.task1.status = data.task_closed_status
data.task1.save()
data.task2.status = data.task_closed_status