Skip to content

Commit

Permalink
Merge pull request #2367 from uktrade/dev
Browse files Browse the repository at this point in the history
UAT Release
  • Loading branch information
depsiatwal authored Feb 18, 2025
2 parents ef97b90 + 5323fbd commit 15da2b5
Show file tree
Hide file tree
Showing 22 changed files with 610 additions and 15 deletions.
6 changes: 4 additions & 2 deletions caseworker/queues/views/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,11 @@ def get_params(self):
if session_sort_by:
params["sort_by"] = session_sort_by
elif self.queue_pk == ALL_CASES_QUEUE_ID:
params["sort_by"] = "submitted_at"
else:
# newest to oldest
params["sort_by"] = "-submitted_at"
else:
# oldest to newest
params["sort_by"] = "submitted_at"

self.request.session["case_search_sort_by"] = params["sort_by"]

Expand Down
28 changes: 28 additions & 0 deletions core/forms/layouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,39 @@ class ConditionalCheckboxesQuestion(BaseConditionalQuestion):
template = "%s/layout/conditional_checkboxes_question.html"


class F680ConditionalCheckboxesQuestion(ConditionalCheckboxesQuestion):
def render(self, bound_field, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs):
template = self.get_template_name(template_pack)

mapped_choices = {choice[1]: choice for choice in bound_field.field.choices}
value = self.value
choice = mapped_choices[value]
position = list(mapped_choices.keys()).index(self.value)

conditional_content = ""
for field in self.fields:
if field in form.declared_fields:
conditional_content += render_field(
field, form, form_style, context, template_pack=template_pack, **kwargs
)

context.update(
{"choice": choice, "field": bound_field, "position": position, "conditional_content": conditional_content}
)

return render_to_string(template, context.flatten())


class ConditionalCheckboxes(BaseConditional):
question_class = ConditionalCheckboxesQuestion
template = "%s/layout/conditional_checkboxes.html"


class F680ConditionalCheckboxes(BaseConditional):
question_class = F680ConditionalCheckboxesQuestion
template = "%s/layout/conditional_checkboxes.html"


class ConditionalCheckbox(TemplateNameMixin):
template = "%s/layout/conditional_checkbox.html"

Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class FormSteps:
NOTES_FOR_CASEWORKER = "NOTES_FOR_CASEWORKER"
17 changes: 17 additions & 0 deletions exporter/f680/application_sections/additional_information/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django import forms

from core.common.forms import BaseForm


class NotesForCaseOfficerForm(BaseForm):
class Layout:
TITLE = "Notes"
SUBMIT_BUTTON_TEXT = "Save and continue"

note = forms.CharField(
label="Add note",
widget=forms.Textarea(attrs={"cols": "80"}),
)

def get_layout_fields(self):
return ("note",)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import pytest

from django.urls import reverse

from core import client

from ..forms import NotesForCaseOfficerForm
from ..constants import FormSteps


@pytest.fixture()
def unset_f680_feature_flag(settings):
settings.FEATURE_FLAG_ALLOW_F680 = False


@pytest.fixture(autouse=True)
def setup(mock_exporter_user_me, settings):
settings.FEATURE_FLAG_ALLOW_F680 = True


@pytest.fixture
def missing_application_id():
return "6bb0828c-1520-4624-b729-7f3e6e5b9f5d"


@pytest.fixture
def missing_f680_application_wizard_url(missing_application_id):
return reverse(
"f680:additional_information:notes_wizard",
kwargs={"pk": missing_application_id},
)


@pytest.fixture
def f680_application_wizard_url(data_f680_case):
return reverse(
"f680:additional_information:notes_wizard",
kwargs={"pk": data_f680_case["id"]},
)


@pytest.fixture
def mock_f680_application_get_404(requests_mock, missing_application_id):
url = client._build_absolute_uri(f"/exporter/f680/application/{missing_application_id}/")
return requests_mock.get(url=url, json={}, status_code=404)


@pytest.fixture
def mock_f680_application_get(requests_mock, data_f680_case):
application_id = data_f680_case["id"]
url = client._build_absolute_uri(f"/exporter/f680/application/{application_id}/")
return requests_mock.get(url=url, json=data_f680_case)


@pytest.fixture
def mock_f680_application_get_existing_data(requests_mock, data_f680_case):
data_f680_case["application"] = {
"additional_information": {"answers": {"note": "Some note text"}, "questions": {"note": "Add note"}}
}
application_id = data_f680_case["id"]
url = client._build_absolute_uri(f"/exporter/f680/application/{application_id}/")
return requests_mock.get(url=url, json=data_f680_case)


@pytest.fixture
def mock_patch_f680_application(requests_mock, data_f680_case):
application_id = data_f680_case["id"]
url = client._build_absolute_uri(f"/exporter/f680/application/{application_id}/")
return requests_mock.patch(url=url, json=data_f680_case)


@pytest.fixture
def post_to_step(post_to_step_factory, f680_application_wizard_url):
return post_to_step_factory(f680_application_wizard_url)


@pytest.fixture
def goto_step(goto_step_factory, f680_application_wizard_url):
return goto_step_factory(f680_application_wizard_url)


class TestAdditionalInformationView:

def test_GET_no_application_404(
self,
authorized_client,
missing_f680_application_wizard_url,
mock_f680_application_get_404,
):
response = authorized_client.get(missing_f680_application_wizard_url)
assert response.status_code == 404

def test_GET_success(
self,
authorized_client,
mock_f680_application_get,
f680_application_wizard_url,
):
response = authorized_client.get(f680_application_wizard_url)
assert response.status_code == 200
assert isinstance(response.context["form"], NotesForCaseOfficerForm)

def test_GET_no_feature_flag_forbidden(
self,
authorized_client,
mock_f680_application_get,
f680_application_wizard_url,
unset_f680_feature_flag,
):
response = authorized_client.get(f680_application_wizard_url)
assert response.status_code == 200
assert response.context["title"] == "Forbidden"

def test_POST_approval_type_and_submit_wizard_success(
self, post_to_step, goto_step, mock_f680_application_get, mock_patch_f680_application
):
response = post_to_step(
FormSteps.NOTES_FOR_CASEWORKER,
{"note": "Some information"},
)
assert response.status_code == 302
assert mock_patch_f680_application.called_once
assert mock_patch_f680_application.last_request.json() == {
"application": {
"name": "F680 Test 1",
"additional_information": {"answers": {"note": "Some information"}, "questions": {"note": "Add note"}},
}
}

def test_POST_to_step_validation_error(
self,
post_to_step,
goto_step,
mock_f680_application_get,
):
goto_step(FormSteps.NOTES_FOR_CASEWORKER)
response = post_to_step(
FormSteps.NOTES_FOR_CASEWORKER,
{},
)
assert response.status_code == 200
assert response.context["form"]["note"].errors == ["This field is required."]

def test_GET_with_existing_data_success(
self,
authorized_client,
mock_f680_application_get_existing_data,
f680_application_wizard_url,
):
response = authorized_client.get(f680_application_wizard_url)
assert response.status_code == 200
assert isinstance(response.context["form"], NotesForCaseOfficerForm)
assert response.context["form"]["note"].initial == "Some note text"
10 changes: 10 additions & 0 deletions exporter/f680/application_sections/additional_information/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.urls import path

from . import views


app_name = "additional_information"

urlpatterns = [
path("notes/", views.NotesForCaseOfficersView.as_view(), name="notes_wizard"),
]
11 changes: 11 additions & 0 deletions exporter/f680/application_sections/additional_information/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from exporter.f680.application_sections.views import F680ApplicationSectionWizard

from .constants import FormSteps
from .forms import NotesForCaseOfficerForm


class NotesForCaseOfficersView(F680ApplicationSectionWizard):
form_list = [
(FormSteps.NOTES_FOR_CASEWORKER, NotesForCaseOfficerForm),
]
section = "additional_information"
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class FormSteps:
APPROVAL_TYPE = "APPROVAL_TYPE"
84 changes: 84 additions & 0 deletions exporter/f680/application_sections/approval_details/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from django import forms
from django.db.models import TextChoices
from django.template.loader import render_to_string

from crispy_forms_gds.layout.content import HTML

from core.common.forms import BaseForm, TextChoice
from core.forms.layouts import F680ConditionalCheckboxes, F680ConditionalCheckboxesQuestion


class ApprovalTypeForm(BaseForm):
class Layout:
TITLE = "Select the types of approvals you need"
TITLE_AS_LABEL_FOR = "approval_choices"
SUBMIT_BUTTON_TEXT = "Save and continue"

class ApprovalTypeChoices(TextChoices):
INITIAL_DISCUSSIONS_OR_PROMOTING = (
"initial_discussion_or_promoting",
"Initial discussions or promoting products",
)
DEMONSTRATION_IN_THE_UK = (
"demonstration_in_uk",
"Demonstration in the United Kingdom to overseas customers",
)
DEMONSTRATION_OVERSEAS = "demonstration_overseas", "Demonstration overseas"
TRAINING = "training", "Training"
THROUGH_LIFE_SUPPORT = "through_life_support", "Through life support"
SUPPLY = "supply", "Supply"

ApprovalTypeChoices = (
TextChoice(ApprovalTypeChoices.INITIAL_DISCUSSIONS_OR_PROMOTING),
TextChoice(ApprovalTypeChoices.DEMONSTRATION_IN_THE_UK),
TextChoice(ApprovalTypeChoices.DEMONSTRATION_OVERSEAS),
TextChoice(ApprovalTypeChoices.TRAINING),
TextChoice(ApprovalTypeChoices.THROUGH_LIFE_SUPPORT),
TextChoice(ApprovalTypeChoices.SUPPLY),
)

approval_choices = forms.MultipleChoiceField(
label=Layout.TITLE,
choices=(),
error_messages={
"required": "Select an approval choice",
},
widget=forms.CheckboxSelectMultiple(),
)

demonstration_in_uk = forms.CharField(
label="Explain what you are demonstrating and why",
help_text="Explain what materials will be involved and if you'll use a substitute product",
widget=forms.Textarea(attrs={"rows": 5}),
required=False,
)

demonstration_overseas = forms.CharField(
label="Explain what you are demonstrating and why",
help_text="Explain what materials will be involved and if you'll use a substitute product",
widget=forms.Textarea(attrs={"rows": 5}),
required=False,
)

approval_details_text = forms.CharField(
label="Provide details about what you're seeking approval to do",
widget=forms.Textarea(attrs={"rows": 5}),
required=False,
)

def __init__(self, *args, **kwargs):
self.conditional_checkbox_choices = (
F680ConditionalCheckboxesQuestion(choices.label, choices.value) for choices in self.ApprovalTypeChoices
)
super().__init__(*args, **kwargs)
self.fields["approval_choices"].choices = self.ApprovalTypeChoices

def get_layout_fields(self):
return (
F680ConditionalCheckboxes("approval_choices", *self.conditional_checkbox_choices),
"approval_details_text",
HTML.details(
"Help with exceptional circumstances",
render_to_string("f680/forms/help_with_approval_type.html"),
),
)
Empty file.
Loading

0 comments on commit 15da2b5

Please sign in to comment.