diff --git a/tasks/forms.py b/tasks/forms.py index 5fab477ca..5108463b3 100644 --- a/tasks/forms.py +++ b/tasks/forms.py @@ -25,7 +25,13 @@ class TaskBaseForm(ModelForm): class Meta: model = Task - exclude = ["parent_task", "creator"] + fields = [ + "title", + "description", + "progress_state", + "category", + "workbasket", + ] error_messages = { "title": { diff --git a/tasks/jinja2/tasks/workflows/detail.jinja b/tasks/jinja2/tasks/workflows/detail.jinja index db9be5e80..b9f395a0a 100644 --- a/tasks/jinja2/tasks/workflows/detail.jinja +++ b/tasks/jinja2/tasks/workflows/detail.jinja @@ -8,7 +8,7 @@ {% if object_name == "workflow" %} {% set task_object_name = "task" %} - {% set task_object_create_url = "#TODO" %} + {% set task_object_create_url = url("workflow:task-workflow-task-ui-create", kwargs={"task_workflow_pk": object.pk}) %} {% elif object_name == "workflow template" %} {% set task_object_name = "task template" %} {% set task_object_create_url = url("workflow:task-template-ui-create", kwargs={"workflow_template_pk": object.pk}) %} diff --git a/tasks/tests/test_views.py b/tasks/tests/test_views.py index 6a1a77155..3ba03db33 100644 --- a/tasks/tests/test_views.py +++ b/tasks/tests/test_views.py @@ -815,3 +815,78 @@ def test_task_and_workflow_list_view(valid_user_client, task, task_workflow): assert table.select("tr:nth-child(2) > td:nth-child(1) > a:nth-child(1)")[ 0 ].text == str(task_workflow.summary_task.pk) + + +def test_create_workflow_task_view(valid_user_client, task_workflow): + """Test the view for creating new Tasks for an existing workflow and the + confirmation view that a successful creation redirects to.""" + + assert task_workflow.get_tasks().count() == 0 + + progress_state = ProgressStateFactory.create() + + create_url = reverse( + "workflow:task-workflow-task-ui-create", + kwargs={"task_workflow_pk": task_workflow.pk}, + ) + + form_data = { + "title": factory.Faker("sentence"), + "description": factory.Faker("sentence"), + "progress_state": progress_state.pk, + } + create_response = valid_user_client.post(create_url, form_data) + created_workflow_task = task_workflow.get_tasks().get() + confirmation_url = reverse( + "workflow:task-workflow-task-ui-confirm-create", + kwargs={"pk": created_workflow_task.pk}, + ) + + assert create_response.status_code == 302 + assert task_workflow.get_tasks().count() == 1 + assert create_response.url == confirmation_url + + confirmation_response = valid_user_client.get(confirmation_url) + + soup = BeautifulSoup(str(confirmation_response.content), "html.parser") + + assert confirmation_response.status_code == 200 + assert created_workflow_task.title in soup.select("h1.govuk-panel__title")[0].text + + +def test_workflow_delete_view_deletes_related_tasks( + valid_user_client, + task_workflow_single_task_item, +): + """Tests that a workflow can be deleted (along with related Task and + TaskItem objects) and that the corresponding confirmation view returns a + HTTP 200 response.""" + + task_workflow_pk = task_workflow_single_task_item.pk + task_pk = task_workflow_single_task_item.get_tasks().get().pk + + delete_url = task_workflow_single_task_item.get_url("delete") + delete_response = valid_user_client.post(delete_url) + assert delete_response.status_code == 302 + + assert not TaskWorkflow.objects.filter( + pk=task_workflow_pk, + ).exists() + assert not TaskItem.objects.filter( + workflow_id=task_workflow_pk, + ).exists() + assert not Task.objects.filter(pk=task_pk).exists() + + confirmation_url = reverse( + "workflow:task-workflow-ui-confirm-delete", + kwargs={"pk": task_workflow_pk}, + ) + assert delete_response.url == confirmation_url + + confirmation_response = valid_user_client.get(confirmation_url) + assert confirmation_response.status_code == 200 + + soup = BeautifulSoup(str(confirmation_response.content), "html.parser") + assert ( + f"Workflow ID: {task_workflow_pk}" in soup.select(".govuk-panel__title")[0].text + ) diff --git a/tasks/urls.py b/tasks/urls.py index ad00db22f..e2fb46a5c 100644 --- a/tasks/urls.py +++ b/tasks/urls.py @@ -111,6 +111,16 @@ views.TaskWorkflowConfirmDeleteView.as_view(), name="task-workflow-ui-confirm-delete", ), + path( + "/task/create/", + views.TaskWorkflowTaskCreateView.as_view(), + name="task-workflow-task-ui-create", + ), + path( + "task/confirm-create/", + views.TaskWorkflowTaskConfirmCreateView.as_view(), + name="task-workflow-task-ui-confirm-create", + ), ] task_and_workflow_ui_patterns = [ diff --git a/tasks/views.py b/tasks/views.py index 5eddb8d4e..0bae2e1cc 100644 --- a/tasks/views.py +++ b/tasks/views.py @@ -553,6 +553,51 @@ def get_context_data(self, **kwargs): return context_data +class TaskWorkflowTaskCreateView(PermissionRequiredMixin, CreateView): + model = Task + template_name = "layouts/create.jinja" + permission_required = "tasks.add_task" + form_class = TaskCreateForm + + def get_task_workflow(self): + """Get the associated TaskWorkflow via its pk in the URL.""" + return TaskWorkflow.objects.get( + pk=self.kwargs["task_workflow_pk"], + ) + + def get_context_data(self, **kwargs) -> dict: + context = super().get_context_data(**kwargs) + + context["page_title"] = "Create a task" + return context + + def form_valid(self, form) -> HttpResponseRedirect: + with transaction.atomic(): + self.object = form.save(user=self.request.user) + TaskItem.objects.create( + workflow=self.get_task_workflow(), + task=self.object, + ) + return HttpResponseRedirect(self.get_success_url(), self.object.pk) + + def get_success_url(self): + return reverse( + "workflow:task-workflow-task-ui-confirm-create", + kwargs={"pk": self.object.pk}, + ) + + +class TaskWorkflowTaskConfirmCreateView(PermissionRequiredMixin, DetailView): + model = Task + template_name = "tasks/confirm_create.jinja" + permission_required = "tasks.add_task" + + def get_context_data(self, **kwargs) -> dict: + context = super().get_context_data(**kwargs) + context["verbose_name"] = "task" + return context + + class TaskWorkflowTemplateDetailView( PermissionRequiredMixin, QueuedItemManagementMixin,