Skip to content

LTD-6168-drop-customisation-text #2471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 26 additions & 16 deletions caseworker/f680/document/forms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from crispy_forms_gds.layout import Button
from django import forms

from core.common.forms import BaseForm
from crispy_forms_gds.layout import HTML


class FinaliseForm(BaseForm):
Expand All @@ -17,20 +15,32 @@ def get_layout_fields(self):
class GenerateDocumentForm(BaseForm):
class Layout:
TITLE = "Generate document"
SUBMIT_BUTTON_TEXT = "Generate"

text = forms.CharField(
label="Customisation text",
widget=forms.Textarea(attrs={"rows": 3}),
required=False,
)
def __init__(self, *args, cancel_url, **kwargs):
self.cancel_url = cancel_url
super().__init__(*args, **kwargs)

def get_layout_fields(self):
return (
"text",
Button(name="preview", value="Preview"),
Button(name="generate", value="Generate"),
def get_layout_actions(self):
layout_actions = super().get_layout_actions()
layout_actions.append(
HTML(
f'<a class="govuk-button govuk-button--secondary" href="{self.cancel_url}" id="cancel-id-cancel">Cancel</a>'
),
)
return layout_actions

def get_layout_actions(self):
# Prevent the standard submit button being shown
return []
def get_layout_fields(self):
return [
HTML.p(
"""
This is a preview of the letter. Check the preview is correct and then hit “Generate”.
"""
),
HTML.p(
"""
If anything is incorrect in the letter, update the incorrect content in the ‘Recommendations’ section and generate another letter.
"""
),
HTML('<div class="app-letter-preview__page" id="preview">{{ preview|safe }}</div><br>'),
]
71 changes: 13 additions & 58 deletions caseworker/f680/document/tests/test_document_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ def f680_refusal_template_id():
return "98a37258-af0f-429e-922d-259459795a2d"


@pytest.fixture
def customisation_text():
return "my customisation"


@pytest.fixture
def mock_preview_f680_letter_missing_template(f680_case_id, f680_approval_template_id, requests_mock):
get_params = "&".join(
Expand All @@ -58,11 +53,6 @@ def data_preview_response():
return {"preview": "my big f680 preview!"}


@pytest.fixture
def data_preview_response_with_customisation_text(customisation_text):
return {"preview": f"my big f680 preview! {customisation_text}"}


@pytest.fixture
def mock_preview_f680_letter(f680_case_id, f680_approval_template_id, requests_mock, data_preview_response):
get_params = "&".join(
Expand All @@ -79,23 +69,21 @@ def mock_preview_f680_letter(f680_case_id, f680_approval_template_id, requests_m


@pytest.fixture
def mock_preview_f680_letter_with_customisation(
def mock_preview_f680_letter(
f680_case_id,
f680_approval_template_id,
requests_mock,
data_preview_response_with_customisation_text,
customisation_text,
data_preview_response,
):
get_params = "&".join(
[
f"pk={f680_case_id}",
f"template={f680_approval_template_id}",
f"text={customisation_text}",
]
)
return requests_mock.get(
client._build_absolute_uri(f"/cases/{f680_case_id}/generated-documents/preview/?{get_params}"),
json=data_preview_response_with_customisation_text,
json=data_preview_response,
status_code=200,
)

Expand All @@ -105,8 +93,7 @@ def mock_preview_f680_letter_api_error(
f680_case_id,
f680_approval_template_id,
requests_mock,
data_preview_response_with_customisation_text,
customisation_text,
data_preview_response,
):
get_params = "&".join(
[
Expand Down Expand Up @@ -211,6 +198,8 @@ def test_GET_success(
generate_approval_document_url,
mock_preview_f680_letter,
data_preview_response,
data_queue,
f680_case_id,
mock_f680_case_under_final_review,
mock_outcomes_approve_refuse,
mock_letter_template_filter,
Expand All @@ -219,6 +208,10 @@ def test_GET_success(
assert response.status_code == 200
assert response.context["preview"] == data_preview_response["preview"]

assert response.context["form"].cancel_url == reverse(
"cases:f680:document:all", kwargs={"queue_pk": data_queue["id"], "pk": f680_case_id}
)

def test_GET_success_approval_not_allowed(
self,
authorized_client,
Expand All @@ -232,36 +225,19 @@ def test_GET_success_approval_not_allowed(
response = authorized_client.get(generate_approval_document_url)
assert response.status_code == 404

def test_POST_preview_success(
self,
authorized_client,
generate_approval_document_url,
mock_preview_f680_letter_with_customisation,
data_preview_response_with_customisation_text,
mock_f680_case_under_final_review,
mock_outcomes_approve_refuse,
mock_letter_template_filter,
customisation_text,
):
response = authorized_client.post(generate_approval_document_url, {"preview": "", "text": customisation_text})
assert response.status_code == 200
assert response.context["preview"] == data_preview_response_with_customisation_text["preview"]
assert mock_preview_f680_letter_with_customisation.call_count == 1

def test_POST_generate_approval_success(
self,
authorized_client,
generate_approval_document_url,
mock_generate_f680_letter,
data_queue,
f680_case_id,
customisation_text,
f680_approval_template_id,
mock_f680_case_under_final_review,
mock_outcomes_approve_refuse,
mock_letter_template_filter,
):
response = authorized_client.post(generate_approval_document_url, {"generate": "", "text": customisation_text})
response = authorized_client.post(generate_approval_document_url)
assert response.status_code == 302
assert response.url == reverse(
"cases:f680:document:all", kwargs={"queue_pk": data_queue["id"], "pk": f680_case_id}
Expand All @@ -271,7 +247,6 @@ def test_POST_generate_approval_success(
"addressee": None,
"advice_type": "approve",
"template": f680_approval_template_id,
"text": customisation_text,
"visible_to_exporter": False,
}

Expand All @@ -282,13 +257,12 @@ def test_POST_generate_refusal_success(
mock_generate_f680_letter,
data_queue,
f680_case_id,
customisation_text,
f680_refusal_template_id,
mock_f680_case_under_final_review,
mock_outcomes_approve_refuse,
mock_letter_template_filter,
):
response = authorized_client.post(generate_refusal_document_url, {"generate": "", "text": customisation_text})
response = authorized_client.post(generate_refusal_document_url)
assert response.status_code == 302
assert response.url == reverse(
"cases:f680:document:all", kwargs={"queue_pk": data_queue["id"], "pk": f680_case_id}
Expand All @@ -298,25 +272,9 @@ def test_POST_generate_refusal_success(
"addressee": None,
"advice_type": "refuse",
"template": f680_refusal_template_id,
"text": customisation_text,
"visible_to_exporter": False,
}

def test_POST_preview_api_error(
self,
authorized_client,
generate_approval_document_url,
data_submitted_f680_case,
mock_preview_f680_letter_api_error,
mock_outcomes_complete,
customisation_text,
mock_letter_template_filter,
):
data_submitted_f680_case["case"]["data"]["status"]["key"] = CaseStatusEnum.UNDER_FINAL_REVIEW
with pytest.raises(ServiceError):
authorized_client.get(generate_approval_document_url, {"preview": "", "text": customisation_text})
assert mock_preview_f680_letter_api_error.call_count == 1

def test_POST_generate_api_error(
self,
authorized_client,
Expand All @@ -326,15 +284,12 @@ def test_POST_generate_api_error(
data_queue,
f680_case_id,
mock_outcomes_complete,
customisation_text,
f680_approval_template_id,
mock_letter_template_filter,
):
data_submitted_f680_case["case"]["data"]["status"]["key"] = CaseStatusEnum.UNDER_FINAL_REVIEW
with pytest.raises(ServiceError):
response = authorized_client.post(
generate_approval_document_url, {"generate": "", "text": customisation_text}
)
response = authorized_client.post(generate_approval_document_url)
assert mock_generate_f680_letter_api_error.call_count == 1


Expand Down
35 changes: 11 additions & 24 deletions caseworker/f680/document/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from http import HTTPStatus
from urllib.parse import quote

from django.contrib import messages
from django.http import HttpResponseForbidden
Expand Down Expand Up @@ -78,7 +77,7 @@ def get_success_url(self):


class F680GenerateDocument(LoginRequiredMixin, F680DocumentMixin, FormView):
template_name = "f680/document/preview.html"
template_name = "f680/core/base_form.html"
form_class = GenerateDocumentForm
current_tab = "recommendations"

Expand All @@ -88,19 +87,19 @@ class F680GenerateDocument(LoginRequiredMixin, F680DocumentMixin, FormView):
"Unexpected error generating document preview",
reraise_404=True,
)
def get_generated_document_preview(self, template_id, text):
def get_generated_document_preview(self, template_id):
# TODO: Use of get_generated_document_preview service helper should
# be replaced with something that doesn't require text to be quoted
return get_generated_document_preview(
self.request, self.case_id, template=template_id, text=text, addressee=None
self.request, self.case_id, template=template_id, text=None, addressee=None
)

@expect_status(
HTTPStatus.CREATED,
"Error generating document",
"Unexpected error generating document",
)
def generate_document(self, template_id, text):
def generate_document(self, template_id):
advice_type = None
template_decisions = [decision["name"]["key"] for decision in self.template["decisions"]]
if "approve" in template_decisions:
Expand All @@ -112,7 +111,6 @@ def generate_document(self, template_id, text):
self.case_id,
{
"template": template_id,
"text": text,
"addressee": None,
"visible_to_exporter": False,
"advice_type": advice_type,
Expand All @@ -128,13 +126,13 @@ def extra_setup(self, request):
except IndexError:
raise Http404

def form_valid(self, form):
if "generate" not in self.request.POST:
# Just show the preview screen again if the user has clicked preview
return self.form_invalid(form)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["cancel_url"] = self.get_success_url()
return kwargs

# TODO: Think about a payload builder
self.generate_document(str(self.kwargs["template_id"]), form.cleaned_data["text"])
def form_valid(self, form):
self.generate_document(str(self.kwargs["template_id"]))
success_message = "Generated document successfully"
messages.success(self.request, success_message)
return super().form_valid(form)
Expand All @@ -143,21 +141,10 @@ def get_success_url(self, *args, **kwargs):
# TODO: Redirect the user to the landing screen when we have a document
return reverse("cases:f680:document:all", kwargs={"queue_pk": self.queue_id, "pk": self.case_id})

def get_text(self, form):
text = None
# The form can be submitted to preview the latest customisation text
# In that case we should retrieve the text from the form for the preview
# This can be determined by interrogating form.is_bound
if form.is_bound:
text = quote(form.cleaned_data.get("text", ""))
return text

def get_context_data(self, *args, **kwargs):
context_data = super().get_context_data(**kwargs)
template_id = str(self.kwargs["template_id"])
form = context_data["form"]
text = self.get_text(form)
preview_response, _ = self.get_generated_document_preview(template_id=template_id, text=text)
preview_response, _ = self.get_generated_document_preview(template_id=template_id)
context_data.update(
{
"preview": preview_response["preview"],
Expand Down
6 changes: 6 additions & 0 deletions caseworker/f680/templates/f680/core/base_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% extends 'f680/case/base.html' %}
{% load crispy_forms_tags %}

{% block details %}
{% crispy form %}
{% endblock %}
14 changes: 0 additions & 14 deletions caseworker/f680/templates/f680/document/preview.html

This file was deleted.