Skip to content

Commit

Permalink
Merge pull request #874 from uktrade/revert-859-LTD-1450-locations-ne…
Browse files Browse the repository at this point in the history
…w-fields

Revert "Add new fields for locations form, make export_type not required"
  • Loading branch information
wkeeling authored Dec 6, 2021
2 parents c341984 + 3de5db1 commit d2df8c3
Show file tree
Hide file tree
Showing 12 changed files with 62 additions and 149 deletions.
41 changes: 10 additions & 31 deletions api/applications/creators.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
GoodOnApplication,
SiteOnApplication,
ExternalLocationOnApplication,
StandardApplication,
)
from api.cases.enums import CaseTypeSubTypeEnum
from api.core.helpers import str_to_bool
Expand All @@ -33,19 +32,6 @@ def _validate_locations(application, errors):
return errors


def _validate_siel_locations(application, errors):
""" Location errors """
if (
not getattr(application, "export_type")
or not getattr(application, "is_shipped_waybill_or_lading")
or not getattr(application, "goods_recipients")
or not getattr(application, "goods_starting_point")
):
errors["location"] = [strings.Applications.Generic.NO_LOCATION_SET]

return errors


def _get_document_errors(documents, processing_error, virus_error):
document_statuses = documents.values_list("safe", flat=True)

Expand Down Expand Up @@ -122,23 +108,16 @@ def _validate_end_user(draft, errors, is_mandatory, open_application=False):


def _validate_consignee(draft, errors, is_mandatory):
"""
Checks there is an consignee if goods_recipients is set to VIA_CONSIGNEE or VIA_CONSIGNEE_AND_THIRD_PARTIES
(with a document if is_document_mandatory)
"""
""" Checks there is an consignee (with a document if is_document_mandatory) """

if (
draft.goods_recipients == StandardApplication.VIA_CONSIGNEE
or draft.goods_recipients == StandardApplication.VIA_CONSIGNEE_AND_THIRD_PARTIES
):
consignee_errors = check_party_error(
draft.consignee.party if draft.consignee else None,
object_not_found_error=strings.Applications.Standard.NO_CONSIGNEE_SET,
is_mandatory=is_mandatory,
is_document_mandatory=False,
)
if consignee_errors:
errors["consignee"] = [consignee_errors]
consignee_errors = check_party_error(
draft.consignee.party if draft.consignee else None,
object_not_found_error=strings.Applications.Standard.NO_CONSIGNEE_SET,
is_mandatory=is_mandatory,
is_document_mandatory=False,
)
if consignee_errors:
errors["consignee"] = [consignee_errors]

return errors

Expand Down Expand Up @@ -345,7 +324,7 @@ def _validate_exhibition_details(draft, errors):
def _validate_standard_licence(draft, errors):
""" Checks that a standard licence has all party types & goods """

errors = _validate_siel_locations(draft, errors)
errors = _validate_locations(draft, errors)
errors = _validate_end_user(draft, errors, is_mandatory=True)
errors = _validate_consignee(draft, errors, is_mandatory=True)
errors = _validate_third_parties(draft, errors, is_mandatory=False)
Expand Down
11 changes: 11 additions & 0 deletions api/applications/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from elasticsearch_dsl import Search, Q
from elasticsearch.exceptions import NotFoundError

from api.applications.enums import ApplicationExportType
from api.applications.models import BaseApplication, GoodOnApplication
from api.applications.serializers.end_use_details import (
F680EndUseDetailsUpdateSerializer,
Expand Down Expand Up @@ -40,6 +41,7 @@
StandardApplicationViewSerializer,
)
from api.applications.serializers.good import GoodOnStandardLicenceSerializer
from api.applications.serializers.temporary_export_details import TemporaryExportDetailsUpdateSerializer
from api.cases.enums import CaseTypeSubTypeEnum, CaseTypeEnum, AdviceType, AdviceLevel
from api.core.exceptions import BadRequestError
from api.documents.models import Document
Expand Down Expand Up @@ -128,6 +130,15 @@ def get_application_end_use_details_update_serializer(application: BaseApplicati
)


def get_temp_export_details_update_serializer(export_type):
if export_type == ApplicationExportType.TEMPORARY:
return TemporaryExportDetailsUpdateSerializer
else:
raise BadRequestError(
{f"get_temp_export_details_update_serializer does " f"not support this export type: {export_type}"}
)


def validate_and_create_goods_on_licence(application_id, licence_id, data):
errors = {}
good_on_applications = (
Expand Down
22 changes: 22 additions & 0 deletions api/applications/migrations/0049_auto_20211206_1031.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 3.1.13 on 2021-12-06 10:31

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("applications", "0048_auto_20211124_1526"),
]

operations = [
migrations.RemoveField(model_name="standardapplication", name="goods_recipients",),
migrations.RemoveField(model_name="standardapplication", name="goods_starting_point",),
migrations.AlterField(
model_name="standardapplication",
name="export_type",
field=models.CharField(
choices=[("permanent", "Permanent"), ("temporary", "Temporary")], default=None, max_length=50
),
),
]
20 changes: 1 addition & 19 deletions api/applications/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,23 +179,7 @@ class Meta:

# Licence Applications
class StandardApplication(BaseApplication):
GB = "GB"
NI = "NI"
GOODS_STARTING_POINT_CHOICES = [
(GB, "Great Britain"),
(NI, "Northern Ireland"),
]
DIRECT_TO_END_USER = "direct_to_end_user"
VIA_CONSIGNEE = "via_consignee"
VIA_CONSIGNEE_AND_THIRD_PARTIES = "via_consignee_and_third_parties"

GOODS_RECIPIENTS_CHOICES = [
(DIRECT_TO_END_USER, "Directly to the end-user"),
(VIA_CONSIGNEE, "To an end-user via a consignee"),
(VIA_CONSIGNEE_AND_THIRD_PARTIES, "To an end-user via a consignee, with additional third parties"),
]

export_type = models.TextField(choices=ApplicationExportType.choices, blank=True, default="")
export_type = models.CharField(choices=ApplicationExportType.choices, default=None, max_length=50)
reference_number_on_information_form = models.CharField(blank=True, null=True, max_length=255)
have_you_been_informed = models.CharField(
choices=ApplicationExportLicenceOfficialType.choices, blank=True, null=True, default=None, max_length=50,
Expand All @@ -213,8 +197,6 @@ class StandardApplication(BaseApplication):
trade_control_product_categories = SeparatedValuesField(
choices=TradeControlProductCategory.choices, blank=False, null=True, max_length=50
)
goods_recipients = models.TextField(choices=GOODS_RECIPIENTS_CHOICES, default="")
goods_starting_point = models.TextField(choices=GOODS_STARTING_POINT_CHOICES, default="")


class OpenApplication(BaseApplication):
Expand Down
14 changes: 3 additions & 11 deletions api/applications/serializers/standard_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ class StandardApplicationViewSerializer(PartiesSerializerMixin, GenericApplicati
trade_control_product_categories = serializers.SerializerMethodField()
sanction_matches = serializers.SerializerMethodField()
is_amended = serializers.SerializerMethodField()
goods_starting_point = serializers.CharField()
goods_recipients = serializers.CharField()

class Meta:
model = StandardApplication
Expand Down Expand Up @@ -77,8 +75,6 @@ class Meta:
"trade_control_product_categories",
"sanction_matches",
"is_amended",
"goods_starting_point",
"goods_recipients",
)
)

Expand Down Expand Up @@ -137,7 +133,9 @@ def get_is_amended(self, instance):


class StandardApplicationCreateSerializer(GenericApplicationCreateSerializer):
export_type = KeyValueChoiceField(choices=ApplicationExportType.choices, required=False)
export_type = KeyValueChoiceField(
choices=ApplicationExportType.choices, error_messages={"required": strings.Applications.Generic.NO_EXPORT_TYPE},
)
have_you_been_informed = KeyValueChoiceField(
choices=ApplicationExportLicenceOfficialType.choices, error_messages={"required": strings.Goods.INFORMED},
)
Expand Down Expand Up @@ -194,21 +192,15 @@ def create(self, validated_data):


class StandardApplicationUpdateSerializer(GenericApplicationUpdateSerializer):
export_type = KeyValueChoiceField(choices=ApplicationExportType.choices, required=False)
goods_starting_point = serializers.CharField()
goods_recipients = serializers.CharField()
reference_number_on_information_form = CharField(max_length=100, required=False, allow_blank=True, allow_null=True)

class Meta:
model = StandardApplication
fields = GenericApplicationUpdateSerializer.Meta.fields + (
"export_type",
"have_you_been_informed",
"reference_number_on_information_form",
"is_shipped_waybill_or_lading",
"non_waybill_or_lading_route_details",
"goods_starting_point",
"goods_recipients",
)

def __init__(self, *args, **kwargs):
Expand Down
23 changes: 2 additions & 21 deletions api/applications/tests/test_create_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,25 +45,6 @@ def test_create_draft_standard_individual_export_application_successful(self):
self.assertEqual(response_data["id"], str(standard_application.id))
self.assertEqual(StandardApplication.objects.count(), 1)

def test_create_draft_standard_individual_export_application_empty_export_type_successful(self):
"""
Ensure we can create a new standard individual export application draft without the export_type field populated
"""
data = {
"name": "Test",
"application_type": CaseTypeReferenceEnum.SIEL,
"have_you_been_informed": ApplicationExportLicenceOfficialType.YES,
"reference_number_on_information_form": "123",
}

response = self.client.post(self.url, data, **self.exporter_headers)
response_data = response.json()
standard_application = StandardApplication.objects.get()

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response_data["id"], str(standard_application.id))
self.assertEqual(StandardApplication.objects.count(), 1)

def test_create_draft_exhibition_clearance_application_successful(self):
"""
Ensure we can create a new Exhibition Clearance draft object
Expand All @@ -82,7 +63,7 @@ def test_create_draft_exhibition_clearance_application_successful(self):

def test_create_draft_gifting_clearance_application_successful(self):
"""
Ensure we can create a new Gifting Clearance draft object
Ensure we can create a new Exhibition Clearance draft object
"""
self.assertEqual(GiftingClearanceApplication.objects.count(), 0)

Expand All @@ -101,7 +82,7 @@ def test_create_draft_gifting_clearance_application_successful(self):

def test_create_draft_f680_clearance_application_successful(self):
"""
Ensure we can create a new F680 Clearance draft object
Ensure we can create a new Exhibition Clearance draft object
"""
self.assertEqual(F680ClearanceApplication.objects.count(), 0)

Expand Down
40 changes: 0 additions & 40 deletions api/applications/tests/test_edit_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,46 +38,6 @@ def test_edit_unsubmitted_application_name_success(self):
# Unsubmitted (draft) applications should not create audit entries when edited
self.assertEqual(Audit.objects.count(), 0)

def test_edit_unsubmitted_application_export_type_success(self):
""" Test edit the application export_type of an unsubmitted application. An unsubmitted application
has the 'draft' status.
"""
application = self.create_draft_standard_application(self.organisation)
# export_type is set to permanent in create_draft_standard_application

url = reverse("applications:application", kwargs={"pk": application.id})
updated_at = application.updated_at

response = self.client.put(url, {"export_type": "temporary"}, **self.exporter_headers)

application.refresh_from_db()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(application.export_type, "temporary")
self.assertGreater(application.updated_at, updated_at)
# Unsubmitted (draft) applications should not create audit entries when edited
self.assertEqual(Audit.objects.count(), 0)

def test_edit_unsubmitted_application_locations_success(self):
application = self.create_draft_standard_application(self.organisation)

url = reverse("applications:application", kwargs={"pk": application.id})
updated_at = application.updated_at

data = {
"goods_starting_point": "GB",
"goods_recipients": "via_consignee",
}

response = self.client.put(url, data, **self.exporter_headers)

application.refresh_from_db()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(application.goods_starting_point, "GB")
self.assertEqual(application.goods_recipients, "via_consignee")
self.assertGreater(application.updated_at, updated_at)
# Unsubmitted (draft) applications should not create audit entries when edited
self.assertEqual(Audit.objects.count(), 0)

@parameterized.expand(get_case_statuses(read_only=False))
def test_edit_application_name_in_editable_status_success(self, editable_status):
old_name = "Old Name"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def test_perform_action_on_non_temporary_export_type_standard_applications_failu
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.json()["errors"],
{"temp_export_details": ["Cannot update temporary export details for a permanent export type"]},
["{'get_temp_export_details_update_serializer does not support this export type: permanent'}"],
)

def test_perform_action_on_non_open_or_standard_applications_failure(self):
Expand Down
17 changes: 7 additions & 10 deletions api/applications/tests/test_standard_application_submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from uuid import UUID

from api.applications.enums import ApplicationExportType
from api.applications.models import SiteOnApplication, GoodOnApplication, PartyOnApplication, StandardApplication
from api.applications.models import SiteOnApplication, GoodOnApplication, PartyOnApplication
from api.audit_trail.enums import AuditType
from api.audit_trail.models import Audit
from api.cases.enums import CaseTypeEnum, CaseDocumentState
Expand Down Expand Up @@ -60,10 +60,8 @@ def test_submit_standard_application_with_invalid_id_failure(self):

self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

def test_submit_standard_application_without_location_info_failure(self):
self.draft.goods_recipients = ""
self.draft.goods_starting_point = ""
self.draft.save()
def test_submit_standard_application_without_site_or_external_location_failure(self):
SiteOnApplication.objects.get(application=self.draft).delete()
url = reverse("applications:application_submit", kwargs={"pk": self.draft.id})

response = self.client.put(url, **self.exporter_headers)
Expand Down Expand Up @@ -96,17 +94,16 @@ def test_submit_standard_application_without_end_user_document_failure(self):
status_code=status.HTTP_400_BAD_REQUEST,
)

def test_submit_standard_application_without_consignee_success(self):
# Consignee is optional if goods_recipients is DIRECT_TO_END_USER
def test_submit_standard_application_without_consignee_failure(self):
self.draft.delete_party(self.draft.consignee)
self.draft.goods_recipients = StandardApplication.DIRECT_TO_END_USER
self.draft.save()

url = reverse("applications:application_submit", kwargs={"pk": self.draft.id})

response = self.client.put(url, **self.exporter_headers)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertContains(
response, text=strings.Applications.Standard.NO_CONSIGNEE_SET, status_code=status.HTTP_400_BAD_REQUEST,
)

def test_submit_standard_application_without_consignee_document_success(self):
# Consignee document is optional
Expand Down
7 changes: 2 additions & 5 deletions api/applications/views/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,10 @@ def put(self, request, pk):
Update an application instance
"""
application = get_application(pk)
update_serializer = get_application_update_serializer(application)
serializer = get_application_update_serializer(application)
case = application.get_case()
data = request.data.copy()
serializer = update_serializer(
application, data=data, context=get_request_user_organisation(request), partial=True
)
serializer = serializer(application, data=data, context=get_request_user_organisation(request), partial=True)

# Prevent minor edits of the clearance level
if not application.is_major_editable() and request.data.get("clearance_level"):
Expand Down Expand Up @@ -280,7 +278,6 @@ def put(self, request, pk):

if application.case_type.sub_type == CaseTypeSubTypeEnum.STANDARD:
save_and_audit_have_you_been_informed_ref(request, application, serializer)
serializer.save()

return JsonResponse(data={}, status=status.HTTP_200_OK)

Expand Down
Loading

0 comments on commit d2df8c3

Please sign in to comment.