diff --git a/exporter/applications/forms/parties.py b/exporter/applications/forms/parties.py
index ce5c46f0b0..28a9ea23ce 100644
--- a/exporter/applications/forms/parties.py
+++ b/exporter/applications/forms/parties.py
@@ -132,8 +132,10 @@ def new_party_form_group(request, application, strings, back_url, clearance_opti
return FormGroup(forms)
-class PartyReuseForm(forms.Form):
- title = "Do you want to reuse an existing party?"
+class PartyReuseForm(BaseForm):
+ class Layout:
+ TITLE = "Do you want to reuse an existing party? - LITE - GOV.UK"
+ TITLE_AS_LABEL_FOR = "reuse_party"
reuse_party = forms.ChoiceField(
choices=(
@@ -147,19 +149,15 @@ class PartyReuseForm(forms.Form):
},
)
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- self.helper = FormHelper()
- self.helper.layout = Layout(
- HTML.h1(self.title),
- "reuse_party",
- Submit("submit", "Continue"),
- )
+ def get_layout_fields(self):
+ return ("reuse_party",)
-class PartySubTypeSelectForm(forms.Form):
- title = "Select the type of end user"
+class PartySubTypeSelectForm(BaseForm):
+ """
+ This form needs to be instantiated with a Layout.TITLE for the type of party whose data is being set
+ as per the BaseForm.
+ """
CHOICES = (
Choice("government", PartyForm.Options.GOVERNMENT),
@@ -178,12 +176,8 @@ class PartySubTypeSelectForm(forms.Form):
)
sub_type_other = forms.CharField(required=False, label="")
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- self.helper = FormHelper()
- self.helper.layout = Layout(
- HTML.h1(self.title),
+ def get_layout_fields(self):
+ return (
ConditionalRadios(
"sub_type",
PartyForm.Options.GOVERNMENT,
@@ -191,7 +185,6 @@ def __init__(self, *args, **kwargs):
PartyForm.Options.INDIVIDUAL,
ConditionalRadiosQuestion(PartyForm.Options.OTHER, "sub_type_other"),
),
- Submit("submit", "Continue"),
)
def clean(self):
@@ -203,10 +196,23 @@ def clean(self):
return cleaned_data
-class PartyNameForm(BaseForm):
+class EndUserSubTypeSelectForm(PartySubTypeSelectForm):
class Layout:
- TITLE = "End user name"
- TITLE_AS_LABEL_FOR = "name"
+ TITLE = "Select the type of end user"
+ TITLE_AS_LABEL_FOR = "sub_type"
+
+
+class ConsigneeSubTypeSelectForm(PartySubTypeSelectForm):
+ class Layout:
+ TITLE = "Select the type of consignee"
+ TITLE_AS_LABEL_FOR = "sub_type"
+
+
+class PartyNameForm(BaseForm):
+ """
+ This form needs to be instantiated with a Layout.TITLE for the type of party whose data is being set
+ as per the BaseForm.
+ """
name = forms.CharField(
label="",
@@ -223,12 +229,30 @@ def get_layout_fields(self):
return ("name",)
-class PartyWebsiteForm(BaseForm):
+class EndUserNameForm(PartyNameForm):
class Layout:
- TITLE = "End user website address (optional)"
- TITLE_AS_LABEL_FOR = "website"
+ TITLE = "End user name"
+ TITLE_AS_LABEL_FOR = "name"
+
- website = forms.CharField(required=False, label="", help_text="Use the format https://www.example.com")
+class ConsigneeNameForm(PartyNameForm):
+ class Layout:
+ TITLE = "Consignee name"
+ TITLE_AS_LABEL_FOR = "name"
+
+
+class PartyWebsiteForm(BaseForm):
+ """
+ This form needs to be instantiated with a Layout.TITLE for the type of party whose data is being set
+ as per the BaseForm.
+ """
+
+ website = forms.CharField(
+ required=False,
+ label="",
+ help_text="Use the format https://www.example.com",
+ validators=[MaxLengthValidator(200, f"Website address should be 200 characters or less")],
+ )
def clean_website(self):
website = self.cleaned_data.get("website")
@@ -255,9 +279,23 @@ def get_layout_fields(self):
return ("website",)
-class PartyAddressForm(BaseForm):
+class EndUserWebsiteForm(PartyWebsiteForm):
class Layout:
- TITLE = "End user address"
+ TITLE = "End user website address (optional)"
+ TITLE_AS_LABEL_FOR = "website"
+
+
+class ConsigneeWebsiteForm(PartyWebsiteForm):
+ class Layout:
+ TITLE = "Consignee website address (optional)"
+ TITLE_AS_LABEL_FOR = "website"
+
+
+class PartyAddressForm(BaseForm):
+ """
+ This form needs to be instantiated with a Layout.TITLE for the type of party whose data is being set
+ as per the BaseForm.
+ """
address = forms.CharField(
widget=forms.Textarea(attrs={"rows": "10"}),
@@ -284,6 +322,18 @@ def get_layout_fields(self):
)
+class EndUserAddressForm(PartyAddressForm):
+ class Layout:
+ TITLE = "End user address"
+ TITLE_AS_LABEL_FOR = "address"
+
+
+class ConsigneeAddressForm(PartyAddressForm):
+ class Layout:
+ TITLE = "Consignee address"
+ TITLE_AS_LABEL_FOR = "address"
+
+
class PartySignatoryNameForm(BaseForm):
class Layout:
TITLE = "Signatory name"
@@ -303,10 +353,10 @@ class PartyDocumentsForm(forms.Form):
title = "Do you have an end-user document?"
text_p1 = """
You will be asked to upload either an
-
- end-user undertaking (opens in new tab) or
-
- stockist undertaking (opens in new tab) completed by the end-user or stockist.
+
+ end-user undertaking (opens in new tab) or
+
+ stockist undertaking (opens in new tab) completed by the end-user or stockist.
"""
text_p2 = "You must include at least one page on company letterhead. This can either be within the end-user document or on a separate document."
text_p3 = (
diff --git a/exporter/applications/views/parties/consignees.py b/exporter/applications/views/parties/consignees.py
index 2595ae9615..fec0c22f31 100644
--- a/exporter/applications/views/parties/consignees.py
+++ b/exporter/applications/views/parties/consignees.py
@@ -1,14 +1,44 @@
+import logging
+
+from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect
from django.urls import reverse
-from django.views.generic import TemplateView
+from django.views.generic import TemplateView, FormView
+
+from core.auth.views import LoginRequiredMixin
+from core.wizard.views import BaseSessionWizardView
from exporter.applications.forms.parties import new_party_form_group
from exporter.applications.helpers.check_your_answers import convert_party
-from exporter.applications.services import get_application, post_party, delete_party, validate_party
-from exporter.applications.views.parties.base import AddParty, CopyParties, SetParty, DeleteParty, CopyAndSetParty
+from exporter.applications.services import validate_party
+from exporter.applications.views.parties.base import CopyParties, DeleteParty, CopyAndSetParty
+from exporter.applications.forms.parties import (
+ PartyReuseForm,
+ ConsigneeSubTypeSelectForm,
+ ConsigneeNameForm,
+ ConsigneeWebsiteForm,
+ ConsigneeAddressForm,
+)
+from exporter.applications.services import (
+ get_application,
+ post_party,
+ delete_party,
+)
+from exporter.core.helpers import (
+ str_to_bool,
+)
+from exporter.core.constants import (
+ SetPartyFormSteps,
+)
+
+from http import HTTPStatus
from lite_content.lite_exporter_frontend.applications import ConsigneeForm, ConsigneePage
-from core.auth.views import LoginRequiredMixin
+from .payloads import SetConsigneePayloadBuilder
+
+log = logging.getLogger(__name__)
+
+from core.decorators import expect_status
class Consignee(LoginRequiredMixin, TemplateView):
@@ -32,26 +62,57 @@ def get(self, request, **kwargs):
return redirect(reverse("applications:add_consignee", kwargs={"pk": application_id}))
-class AddConsignee(LoginRequiredMixin, AddParty):
- def __init__(self):
- super().__init__(new_url="applications:set_consignee", copy_url="applications:consignees_copy")
+class AddConsignee(LoginRequiredMixin, FormView):
+ form_class = PartyReuseForm
+ template_name = "core/form.html"
- @property
- def back_url(self):
- return reverse("applications:task_list", kwargs={"pk": self.kwargs["pk"]}) + "#consignee"
+ def form_valid(self, form):
+ reuse_party = str_to_bool(form.cleaned_data.get("reuse_party"))
+ if reuse_party:
+ success_url = reverse("applications:consignees_copy", kwargs=self.kwargs)
+ else:
+ success_url = reverse("applications:set_consignee", kwargs=self.kwargs)
+ return HttpResponseRedirect(success_url)
-class SetConsignee(LoginRequiredMixin, SetParty):
- def __init__(self):
- super().__init__(
- url="applications:consignee_attach_document",
- party_type="consignee",
- form=new_party_form_group,
- back_url="applications:consignee",
- strings=ConsigneeForm,
- post_action=post_party,
- validate_action=validate_party,
- )
+
+class SetConsignee(LoginRequiredMixin, BaseSessionWizardView):
+ party_type = "consignee"
+ form_list = [
+ (SetPartyFormSteps.PARTY_SUB_TYPE, ConsigneeSubTypeSelectForm),
+ (SetPartyFormSteps.PARTY_NAME, ConsigneeNameForm),
+ (SetPartyFormSteps.PARTY_WEBSITE, ConsigneeWebsiteForm),
+ (SetPartyFormSteps.PARTY_ADDRESS, ConsigneeAddressForm),
+ ]
+
+ def get_form_kwargs(self, step=None):
+ kwargs = super().get_form_kwargs(step)
+ if step == SetPartyFormSteps.PARTY_ADDRESS:
+ kwargs["request"] = self.request
+
+ return kwargs
+
+ def get_payload(self, form_dict):
+ return SetConsigneePayloadBuilder().build(form_dict)
+
+ @expect_status(
+ HTTPStatus.CREATED,
+ "Error adding consignee to application",
+ "Unexpected error adding consignee to application",
+ )
+ def post_party_with_payload(self, pk, form_dict):
+ payload = self.get_payload(form_dict)
+ payload.update({"type": self.party_type})
+
+ return post_party(self.request, pk, payload)
+
+ def get_success_url(self, party_id):
+ return reverse("applications:consignee_attach_document", kwargs={"pk": self.kwargs["pk"], "obj_pk": party_id})
+
+ def done(self, form_list, form_dict, **kwargs):
+ response, _ = self.post_party_with_payload(self.kwargs["pk"], form_dict)
+
+ return redirect(self.get_success_url(response[self.party_type]["id"]))
class RemoveConsignee(LoginRequiredMixin, DeleteParty):
diff --git a/exporter/applications/views/parties/end_users.py b/exporter/applications/views/parties/end_users.py
index 6ddadbde6f..979e082388 100644
--- a/exporter/applications/views/parties/end_users.py
+++ b/exporter/applications/views/parties/end_users.py
@@ -13,10 +13,10 @@
from exporter.applications.forms.parties import (
PartyReuseForm,
- PartySubTypeSelectForm,
- PartyNameForm,
- PartyWebsiteForm,
- PartyAddressForm,
+ EndUserSubTypeSelectForm,
+ EndUserNameForm,
+ EndUserWebsiteForm,
+ EndUserAddressForm,
PartySignatoryNameForm,
PartyDocumentsForm,
PartyDocumentUploadForm,
@@ -77,11 +77,6 @@ class AddEndUserView(LoginRequiredMixin, FormView):
form_class = PartyReuseForm
template_name = "core/form.html"
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context["form_title"] = self.form_class.title
- return context
-
def form_valid(self, form):
reuse_party = str_to_bool(form.cleaned_data.get("reuse_party"))
if reuse_party:
@@ -112,10 +107,10 @@ def _post_party_document(request, application_id, party_id, document_type, docum
class SetPartyView(LoginRequiredMixin, BaseSessionWizardView):
form_list = [
- (SetPartyFormSteps.PARTY_SUB_TYPE, PartySubTypeSelectForm),
- (SetPartyFormSteps.PARTY_NAME, PartyNameForm),
- (SetPartyFormSteps.PARTY_WEBSITE, PartyWebsiteForm),
- (SetPartyFormSteps.PARTY_ADDRESS, PartyAddressForm),
+ (SetPartyFormSteps.PARTY_SUB_TYPE, EndUserSubTypeSelectForm),
+ (SetPartyFormSteps.PARTY_NAME, EndUserNameForm),
+ (SetPartyFormSteps.PARTY_WEBSITE, EndUserWebsiteForm),
+ (SetPartyFormSteps.PARTY_ADDRESS, EndUserAddressForm),
(SetPartyFormSteps.PARTY_SIGNATORY_NAME, PartySignatoryNameForm),
(SetPartyFormSteps.PARTY_DOCUMENTS, PartyDocumentsForm),
(SetPartyFormSteps.PARTY_DOCUMENT_UPLOAD, PartyDocumentUploadForm),
@@ -143,6 +138,7 @@ def get_context_data(self, form, **kwargs):
context["title"] = form.Layout.TITLE
else:
context["title"] = form.title
+
return context
def get_form_kwargs(self, step=None):
@@ -287,6 +283,12 @@ def get(self, request, *args, **kwargs):
class PartyEditView(LoginRequiredMixin, PartyContextMixin, FormView):
+
+ def get_form_kwargs(self):
+ kwargs = super().get_form_kwargs()
+
+ return kwargs
+
def form_valid(self, form):
update_party(self.request, self.application_id, self.party_id, form.cleaned_data)
return super().form_valid(form)
@@ -296,28 +298,28 @@ def get_success_url(self):
class PartySubTypeEditView(PartyEditView):
- form_class = PartySubTypeSelectForm
+ form_class = EndUserSubTypeSelectForm
def get_initial(self):
return {"sub_type": self.party["sub_type"]["key"], "sub_type_other": self.party["sub_type_other"]}
class PartyNameEditView(PartyEditView):
- form_class = PartyNameForm
+ form_class = EndUserNameForm
def get_initial(self):
return {"name": self.party["name"]}
class PartyWebsiteEditView(PartyEditView):
- form_class = PartyWebsiteForm
+ form_class = EndUserWebsiteForm
def get_initial(self):
return {"website": self.party["website"]}
class PartyAddressEditView(PartyEditView):
- form_class = PartyAddressForm
+ form_class = EndUserAddressForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
@@ -408,7 +410,6 @@ def get_form_kwargs(self, step=None):
if step == SetPartyFormSteps.PARTY_COMPANY_LETTERHEAD_DOCUMENT_UPLOAD:
kwargs["edit"] = self.company_letterhead_document_exists
-
return kwargs
def get_form_initial(self, step):
diff --git a/exporter/applications/views/parties/payloads.py b/exporter/applications/views/parties/payloads.py
new file mode 100644
index 0000000000..6618396512
--- /dev/null
+++ b/exporter/applications/views/parties/payloads.py
@@ -0,0 +1,14 @@
+from core.wizard.payloads import MergingPayloadBuilder
+from exporter.applications.views.goods.common.payloads import get_cleaned_data
+from exporter.core.constants import (
+ SetPartyFormSteps,
+)
+
+
+class SetConsigneePayloadBuilder(MergingPayloadBuilder):
+ payload_dict = {
+ SetPartyFormSteps.PARTY_SUB_TYPE: get_cleaned_data,
+ SetPartyFormSteps.PARTY_NAME: get_cleaned_data,
+ SetPartyFormSteps.PARTY_WEBSITE: get_cleaned_data,
+ SetPartyFormSteps.PARTY_ADDRESS: get_cleaned_data,
+ }
diff --git a/unit_tests/exporter/applications/forms/test_parties.py b/unit_tests/exporter/applications/forms/test_parties.py
index e4f4f662d8..723bec4b5d 100644
--- a/unit_tests/exporter/applications/forms/test_parties.py
+++ b/unit_tests/exporter/applications/forms/test_parties.py
@@ -31,8 +31,26 @@ def test_party_reuse_form(data, valid, errors):
({"sub_type": "other"}, False, {"sub_type_other": ["Enter the type of the party you're adding"]}),
),
)
-def test_party_subtype_select_form(data, valid, errors):
- form = parties.PartySubTypeSelectForm(data=data)
+def test_end_user_subtype_select_form(data, valid, errors):
+ form = parties.EndUserSubTypeSelectForm(data=data)
+
+ assert form.is_valid() == valid
+
+ if not valid:
+ assert form.errors == errors
+
+
+@pytest.mark.parametrize(
+ "data, valid, errors",
+ (
+ ({"sub_type": "government"}, True, None),
+ ({"sub_type": "other", "sub_type_other": "test_sub_type"}, True, None),
+ ({"sub_type": ""}, False, {"sub_type": ["Select what type of party you're creating"]}),
+ ({"sub_type": "other"}, False, {"sub_type_other": ["Enter the type of the party you're adding"]}),
+ ),
+)
+def test_consignee_subtype_select_form(data, valid, errors):
+ form = parties.ConsigneeSubTypeSelectForm(data=data)
assert form.is_valid() == valid
@@ -57,8 +75,56 @@ def test_party_subtype_select_form(data, valid, errors):
),
),
)
-def test_party_name_form(data, valid, errors):
- form = parties.PartyNameForm(data=data)
+def test_consignee_name_form(data, valid, errors):
+ form = parties.ConsigneeNameForm(data=data)
+
+ assert form.is_valid() == valid
+
+ if not valid:
+ assert form.errors == errors
+
+
+@pytest.mark.parametrize(
+ "data, valid, errors",
+ (
+ ({"name": "test"}, True, None),
+ ({"name": ""}, False, {"name": ["Enter a name"]}),
+ (
+ {"name": "department of internation trade in collaboration with the department of national trade"},
+ False,
+ {"name": [f"End user name should be 80 characters or less"]},
+ ),
+ ),
+)
+def test_end_user_name_form(data, valid, errors):
+ form = parties.EndUserNameForm(data=data)
+
+ assert form.is_valid() == valid
+
+ if not valid:
+ assert form.errors == errors
+
+
+@pytest.mark.parametrize(
+ "data, valid, errors",
+ (
+ ({"website": "test"}, False, {"website": ["Enter a valid URL."]}),
+ ({"website": "https://www.example.com"}, True, None),
+ ({"website": "www.example.com"}, True, None),
+ ({"website": "example.com"}, True, None),
+ ({"website": ""}, True, None),
+ (
+ {
+ "website": "https://www.example.com/asfhadjksfhadsklfhalskfhjsakfhsdfkshfskfhsdkfhskfjhfkdshfksfhdksfhsdkjfhksfhsakadfshdsmnfbdsfbdsfsbdfdmsbfdfsngdfsbgdfsgdfsbgdfsgbdfsgbdfsgmnbdfsgmnbdfsgmdfsbgdfsgbdfsgbdfsbgdfsbg/"
+ },
+ False,
+ {"website": ["Website address should be 200 characters or less"]},
+ ),
+ ({}, True, None),
+ ),
+)
+def test_end_user_website_form(data, valid, errors):
+ form = parties.EndUserWebsiteForm(data=data)
assert form.is_valid() == valid
@@ -74,11 +140,18 @@ def test_party_name_form(data, valid, errors):
({"website": "www.example.com"}, True, None),
({"website": "example.com"}, True, None),
({"website": ""}, True, None),
+ (
+ {
+ "website": "https://www.example.com/asfhadjksfhadsklfhalskfhjsakfhsdfkshfskfhsdkfhskfjhfkdshfksfhdksfhsdkjfhksfhsakadfshdsmnfbdsfbdsfsbdfdmsbfdfsngdfsbgdfsgdfsbgdfsgbdfsgbdfsgmnbdfsgmnbdfsgmdfsbgdfsgbdfsgbdfsbgdfsbg/"
+ },
+ False,
+ {"website": ["Website address should be 200 characters or less"]},
+ ),
({}, True, None),
),
)
-def test_party_website_form(data, valid, errors):
- form = parties.PartyWebsiteForm(data=data)
+def test_consignee_website_form(data, valid, errors):
+ form = parties.ConsigneeWebsiteForm(data=data)
assert form.is_valid() == valid
@@ -98,13 +171,38 @@ def test_party_website_form(data, valid, errors):
),
)
@patch("exporter.applications.forms.parties.get_countries")
-def test_party_address_form(mock_get_countries, data, valid, errors):
+def test_end_user_address_form(mock_get_countries, data, valid, errors):
+ class Request:
+ csp_nonce = "test"
+
+ request = Request()
+ mock_get_countries.return_value = [{"id": "aus", "name": "Austria"}, {"id": "fr", "name": "France"}]
+ form = parties.EndUserAddressForm(request=request, data=data)
+
+ assert form.is_valid() == valid
+ mock_get_countries.assert_called_once_with(request)
+
+ if not valid:
+ assert form.errors == errors
+
+
+@pytest.mark.parametrize(
+ "data, valid, errors",
+ (
+ ({"address": "1 somewhere", "country": "aus"}, True, None),
+ ({"address": "", "country": ""}, False, {"address": ["Enter an address"], "country": ["Select the country"]}),
+ ({"address": "This-is-a-valid-address", "country": "aus"}, True, None),
+ ({"address": "this\r\nis\r\ninvalid", "country": "aus"}, True, None),
+ ),
+)
+@patch("exporter.applications.forms.parties.get_countries")
+def test_consignee_address_form(mock_get_countries, data, valid, errors):
class Request:
csp_nonce = "test"
request = Request()
mock_get_countries.return_value = [{"id": "aus", "name": "Austria"}, {"id": "fr", "name": "France"}]
- form = parties.PartyAddressForm(request=request, data=data)
+ form = parties.ConsigneeAddressForm(request=request, data=data)
assert form.is_valid() == valid
mock_get_countries.assert_called_once_with(request)
diff --git a/unit_tests/exporter/applications/views/parties/test_consignees.py b/unit_tests/exporter/applications/views/parties/test_consignees.py
index faf4a8d951..ff267f7a65 100644
--- a/unit_tests/exporter/applications/views/parties/test_consignees.py
+++ b/unit_tests/exporter/applications/views/parties/test_consignees.py
@@ -1,9 +1,20 @@
+import logging
import pytest
+
+from bs4 import BeautifulSoup
from pytest_django.asserts import assertTemplateUsed
from django.urls import reverse
from core import client
+from exporter.core.constants import SetPartyFormSteps
+from exporter.applications.forms.parties import (
+ PartyReuseForm,
+ PartySubTypeSelectForm,
+ PartyNameForm,
+ PartyWebsiteForm,
+ PartyAddressForm,
+)
@pytest.fixture(autouse=True)
@@ -17,11 +28,203 @@ def application_pk(data_standard_case):
return data_standard_case["case"]["id"]
+@pytest.fixture
+def mock_party_create_success(requests_mock, data_standard_case):
+ party_id = data_standard_case["case"]["data"]["consignee"]["id"]
+ url = client._build_absolute_uri(f'/applications/{data_standard_case["case"]["id"]}/parties/')
+ yield requests_mock.post(url=url, status_code=201, json={"consignee": {"id": party_id}})
+
+
+@pytest.fixture
+def mock_party_create_fail(requests_mock, data_standard_case):
+ url = client._build_absolute_uri(f'/applications/{data_standard_case["case"]["id"]}/parties/')
+ yield requests_mock.post(url=url, status_code=500, json={})
+
+
+@pytest.fixture
+def add_consignee_url(application_pk):
+ return reverse("applications:add_consignee", kwargs={"pk": application_pk})
+
+
+@pytest.fixture
+def set_consignee_url(application_pk):
+ return reverse("applications:set_consignee", kwargs={"pk": application_pk})
+
+
+@pytest.fixture
+def post_to_step(post_to_step_factory, set_consignee_url):
+ return post_to_step_factory(set_consignee_url)
+
+
@pytest.fixture
def application_parties_consignee_summary_url(application_pk):
return reverse("applications:consignee", kwargs={"pk": application_pk})
-def test_application_parties_consignee_summary(authorized_client, application_parties_consignee_summary_url):
+@pytest.fixture(autouse=True)
+def setup(
+ mock_countries,
+):
+ yield
+
+
+@pytest.mark.parametrize("reuse_party_response, expected_url", ((True, "consignees_copy"), (False, "set_consignee")))
+def test_add_consignee_view(
+ application_pk, data_standard_case, add_consignee_url, authorized_client, reuse_party_response, expected_url
+):
+ response = authorized_client.get(add_consignee_url)
+
+ assert response.status_code == 200
+ assert isinstance(response.context["form"], PartyReuseForm)
+
+ response = authorized_client.post(
+ add_consignee_url,
+ data={
+ "reuse_party": reuse_party_response,
+ },
+ )
+
+ assert response.status_code == 302
+ assert response.url == reverse(f"applications:{expected_url}", kwargs={"pk": application_pk})
+
+
+def test_set_consignee_end_to_end_post_success(
+ set_consignee_url,
+ authorized_client,
+ post_to_step,
+ requests_mock,
+ data_standard_case,
+ application_pk,
+ mock_party_create_success,
+):
+ response = authorized_client.get(set_consignee_url)
+
+ assert response.status_code == 200
+ assert not response.context["form"].errors
+ assert isinstance(response.context["form"], PartySubTypeSelectForm)
+
+ response = post_to_step(
+ SetPartyFormSteps.PARTY_SUB_TYPE,
+ {"sub_type": "government"},
+ )
+
+ assert response.status_code == 200
+ assert not response.context["form"].errors
+ content = BeautifulSoup(response.content, "html.parser")
+ heading_element = content.find("h1", class_="govuk-heading-xl")
+
+ assert heading_element.string == "Consignee name"
+
+ assert isinstance(response.context["form"], PartyNameForm)
+
+ response = post_to_step(
+ SetPartyFormSteps.PARTY_NAME,
+ {"name": "test-name"},
+ )
+
+ assert response.status_code == 200
+ assert not response.context["form"].errors
+
+ content = BeautifulSoup(response.content, "html.parser")
+ heading_element = content.find("h1", class_="govuk-heading-xl")
+
+ assert heading_element.string == "Consignee website address (optional)"
+
+ assert isinstance(response.context["form"], PartyWebsiteForm)
+
+ response = post_to_step(
+ SetPartyFormSteps.PARTY_WEBSITE,
+ {"website": "https://www.example.com"},
+ )
+ assert not response.context["form"].errors
+
+ content = BeautifulSoup(response.content, "html.parser")
+ heading_element = content.find("h1", class_="govuk-heading-xl")
+
+ assert heading_element.string == "Consignee address"
+ assert isinstance(response.context["form"], PartyAddressForm)
+
+ response = post_to_step(
+ SetPartyFormSteps.PARTY_ADDRESS,
+ {"address": "1 somewhere", "country": "US"},
+ )
+
+ assert response.status_code == 302
+
+ party_id = data_standard_case["case"]["data"]["consignee"]["id"]
+
+ assert response.url == reverse(
+ "applications:consignee_attach_document", kwargs={"pk": application_pk, "obj_pk": party_id}
+ )
+
+ consignee_data = requests_mock.request_history.pop().json()
+ assert consignee_data == {
+ "sub_type": "government",
+ "sub_type_other": "",
+ "name": "test-name",
+ "website": "https://www.example.com",
+ "address": "1 somewhere",
+ "country": "US",
+ "type": "consignee",
+ }
+
+
+def test_set_consignee_end_to_end_post_fail(
+ set_consignee_url,
+ authorized_client,
+ requests_mock,
+ data_standard_case,
+ application_pk,
+ mock_party_create_fail,
+ caplog,
+ post_to_step,
+):
+ response = authorized_client.get(set_consignee_url)
+
+ assert response.status_code == 200
+ assert not response.context["form"].errors
+ assert isinstance(response.context["form"], PartySubTypeSelectForm)
+
+ response = post_to_step(
+ SetPartyFormSteps.PARTY_SUB_TYPE,
+ {"sub_type": "government"},
+ )
+
+ assert response.status_code == 200
+ assert not response.context["form"].errors
+ assert isinstance(response.context["form"], PartyNameForm)
+
+ response = post_to_step(
+ SetPartyFormSteps.PARTY_NAME,
+ {"name": "test-name"},
+ )
+
+ assert response.status_code == 200
+ assert not response.context["form"].errors
+ assert isinstance(response.context["form"], PartyWebsiteForm)
+
+ response = post_to_step(
+ SetPartyFormSteps.PARTY_WEBSITE,
+ {"website": "https://www.example.com"},
+ )
+ assert not response.context["form"].errors
+ assert isinstance(response.context["form"], PartyAddressForm)
+
+ response = post_to_step(
+ SetPartyFormSteps.PARTY_ADDRESS,
+ {"address": "1 somewhere", "country": "US"},
+ )
+
+ assert response.status_code == 200
+ assert len(caplog.records) == 1
+ log = caplog.records[0]
+
+ assert log.message == "Error adding consignee to application - response was: 500 - {}"
+ assert log.levelno == logging.ERROR
+
+
+def test_application_parties_consignee_summary(
+ authorized_client, application_parties_consignee_summary_url, mock_get_application
+):
response = authorized_client.get(application_parties_consignee_summary_url)
assertTemplateUsed(response, "applications/consignee.html")
diff --git a/unit_tests/exporter/applications/views/parties/test_end_users.py b/unit_tests/exporter/applications/views/parties/test_end_users.py
index c9f35c6f76..20813e6863 100644
--- a/unit_tests/exporter/applications/views/parties/test_end_users.py
+++ b/unit_tests/exporter/applications/views/parties/test_end_users.py
@@ -126,6 +126,11 @@ def set_end_user(url, authorized_client):
response = authorized_client.get(url)
assert not response.context["form"].errors
+ content = BeautifulSoup(response.content, "html.parser")
+ heading_element = content.find("h1", class_="govuk-heading-xl")
+
+ assert heading_element.string == "Select the type of end user"
+
response = authorized_client.post(
url,
data={
@@ -135,6 +140,11 @@ def set_end_user(url, authorized_client):
)
assert not response.context["form"].errors
+ content = BeautifulSoup(response.content, "html.parser")
+ heading_element = content.find("h1", class_="govuk-heading-xl")
+
+ assert heading_element.string == "End user name"
+
response = authorized_client.post(
url,
data={
@@ -144,6 +154,11 @@ def set_end_user(url, authorized_client):
)
assert not response.context["form"].errors
+ content = BeautifulSoup(response.content, "html.parser")
+ heading_element = content.find("h1", class_="govuk-heading-xl")
+
+ assert heading_element.string == "End user website address (optional)"
+
response = authorized_client.post(
url,
data={
@@ -152,6 +167,10 @@ def set_end_user(url, authorized_client):
},
)
assert not response.context["form"].errors
+ content = BeautifulSoup(response.content, "html.parser")
+ heading_element = content.find("h1", class_="govuk-heading-xl")
+
+ assert heading_element.string == "End user address"
response = authorized_client.post(
url,
@@ -438,5 +457,6 @@ def test_add_end_user_view(authorized_client, data_standard_case):
url = reverse("applications:add_end_user", kwargs={"pk": application_id})
response = authorized_client.get(url)
soup = BeautifulSoup(response.content, "html.parser")
+ heading_element = soup.find("h1", class_="govuk-heading-xl")
- assert soup.title.string == "Do you want to reuse an existing party? - LITE - GOV.UK"
+ assert heading_element.string == "Do you want to reuse an existing party? - LITE - GOV.UK"