diff --git a/api/applications/creators.py b/api/applications/creators.py index 89930f2519..327eceb626 100644 --- a/api/applications/creators.py +++ b/api/applications/creators.py @@ -96,23 +96,28 @@ def check_party_error(party, object_not_found_error, is_mandatory, is_document_m return document_error -def _validate_end_user(draft, errors, is_mandatory, open_application=False): +def _validate_end_users(draft, errors, is_mandatory, open_application=False): """Validates end user. If a document is mandatory, this is also validated.""" # Document is only mandatory if application is standard permanent or HMRC query is_document_mandatory = ( draft.case_type.sub_type == CaseTypeSubTypeEnum.STANDARD and draft.export_type == ApplicationExportType.PERMANENT - ) or draft.case_type.sub_type == CaseTypeSubTypeEnum.HMRC - - end_user_errors = check_party_error( - draft.end_user.party if draft.end_user else None, - object_not_found_error=strings.Applications.Standard.NO_END_USER_SET, - is_mandatory=is_mandatory, - is_document_mandatory=is_document_mandatory, ) - if end_user_errors: - errors["end_user"] = [end_user_errors] + error_messages = [] + + for end_user in draft.end_users: + end_user_errors = check_party_error( + end_user.party if end_user else None, + object_not_found_error=strings.Applications.Standard.NO_END_USER_SET, + is_mandatory=is_mandatory, + is_document_mandatory=is_document_mandatory, + ) + if end_user_errors: + error_messages.append(end_user_errors) + + if error_messages: + errors["end_user"] = error_messages return errors @@ -263,7 +268,7 @@ def _validate_standard_licence(draft, errors): """Checks that a standard licence has all party types & goods""" errors = _validate_siel_locations(draft, errors) - errors = _validate_end_user(draft, errors, is_mandatory=True) + errors = _validate_end_users(draft, errors, is_mandatory=True) errors = _validate_security_approvals(draft, errors, is_mandatory=True) errors = _validate_consignee(draft, errors, is_mandatory=True) errors = _validate_third_parties(draft, errors, is_mandatory=False) diff --git a/api/applications/helpers.py b/api/applications/helpers.py index d9f70c4a8b..0b5717e929 100644 --- a/api/applications/helpers.py +++ b/api/applications/helpers.py @@ -161,8 +161,8 @@ def delete_uploaded_document(data): def auto_match_sanctions(application): parties = [] - if application.end_user: - parties.append(application.end_user.party) + if application.end_users[0]: + parties.append(application.end_users[0].party) for item in application.ultimate_end_users: parties.append(item.party) diff --git a/api/applications/models.py b/api/applications/models.py index 4610d6f472..df3820e6a5 100644 --- a/api/applications/models.py +++ b/api/applications/models.py @@ -151,7 +151,14 @@ def end_user(self): Standard and HMRC Query applications """ try: - return self.active_parties.get(party__type=PartyType.END_USER) + return self.active_parties.filter(party__type=PartyType.END_USER).first() + except PartyOnApplication.DoesNotExist: + pass + + @property + def end_users(self): + try: + return self.active_parties.filter(party__type=PartyType.END_USER) except PartyOnApplication.DoesNotExist: pass diff --git a/api/data_workspace/v2/tests/bdd/conftest.py b/api/data_workspace/v2/tests/bdd/conftest.py index e052c8471c..2e2c8f9c4e 100644 --- a/api/data_workspace/v2/tests/bdd/conftest.py +++ b/api/data_workspace/v2/tests/bdd/conftest.py @@ -9,6 +9,7 @@ from django.urls import reverse from freezegun import freeze_time from moto import mock_aws +from operator import itemgetter from pytest_bdd import ( given, parsers, @@ -23,6 +24,7 @@ from api.applications.tests.factories import ( DraftStandardApplicationFactory, GoodOnApplicationFactory, + EndUserFactory, PartyOnApplicationFactory, StandardApplicationFactory, ) @@ -439,6 +441,20 @@ def add_end_user_to_application(draft_standard_application, country): end_user.party.save() +@given(parsers.parse("a new end-user added to the application of `{country}`")) +def add_new_end_user_to_application(draft_standard_application, country): + country = Country.objects.get(name=country) + end_user = PartyOnApplicationFactory( + application=draft_standard_application, + party=EndUserFactory(country=country, organisation=draft_standard_application.organisation), + ) + PartyDocumentFactory( + party=end_user.party, + s3_key="party-document-second", + safe=True, + ) + + @when( "the application is submitted", target_fixture="submitted_standard_application", @@ -522,8 +538,8 @@ def check_rows(client, parse_table, unpage_data, table_name, rows): for row in parsed_rows[1:]: expected_data.append({key: value for key, value in zip(keys, row)}) expected_data = cast_to_types(expected_data, table_metadata["fields"]) - actual_data = sorted(actual_data, key=lambda item, keys=keys: item[keys[0]]) - expected_data = sorted(expected_data, key=lambda item, keys=keys: item[keys[0]]) + actual_data = sorted(actual_data, key=itemgetter(*keys)) + expected_data = sorted(expected_data, key=itemgetter(*keys)) assert actual_data == expected_data diff --git a/api/data_workspace/v2/tests/bdd/scenarios/destinations.feature b/api/data_workspace/v2/tests/bdd/scenarios/destinations.feature index b20ec219f3..f8bf4e0773 100644 --- a/api/data_workspace/v2/tests/bdd/scenarios/destinations.feature +++ b/api/data_workspace/v2/tests/bdd/scenarios/destinations.feature @@ -13,10 +13,12 @@ Scenario: Check that the country code and type are included in the extract | id | 03fb08eb-1564-4b68-9336-3ca8906543f9 | And a consignee added to the application in `Australia` And an end-user added to the application of `New Zealand` + And a new end-user added to the application of `South Korea` When the application is submitted And the application is issued at 2024-11-22T13:35:15 Then the `destinations` table has the following rows: | application_id | country_code | type | + | 03fb08eb-1564-4b68-9336-3ca8906543f9 | KR | end_user | | 03fb08eb-1564-4b68-9336-3ca8906543f9 | NZ | end_user | | 03fb08eb-1564-4b68-9336-3ca8906543f9 | AU | consignee | diff --git a/api/letter_templates/context_generator.py b/api/letter_templates/context_generator.py index 7db1fdc574..f648980015 100644 --- a/api/letter_templates/context_generator.py +++ b/api/letter_templates/context_generator.py @@ -737,6 +737,7 @@ def get_document_context(case, addressee=None): appeal_deadline = timezone.localtime() + timedelta(days=APPEAL_DAYS) exporter_reference = "" date_application_submitted = "" + end_users = [] if base_application: if base_application.name: @@ -745,6 +746,8 @@ def get_document_context(case, addressee=None): if base_application.submitted_at: date_application_submitted = base_application.submitted_at.strftime("%d %B %Y") + end_users = base_application.end_users + return { "case_reference": case.reference_code, "case_submitted_at": case.submitted_at, @@ -756,11 +759,7 @@ def get_document_context(case, addressee=None): "addressee": AddresseeSerializer(addressee).data, "organisation": OrganisationSerializer(case.organisation).data, "licence": LicenceSerializer(licence).data if licence else None, - "end_user": ( - PartySerializer(base_application.end_user.party).data - if base_application and base_application.end_user - else None - ), + "end_user": (PartySerializer(end_users[0].party).data if end_users else None), "consignee": ( PartySerializer(base_application.consignee.party).data if base_application and base_application.consignee