diff --git a/api/applications/libraries/document_helpers.py b/api/applications/libraries/document_helpers.py index d7235ff4b1..de01a6100e 100644 --- a/api/applications/libraries/document_helpers.py +++ b/api/applications/libraries/document_helpers.py @@ -6,8 +6,6 @@ from api.applications.serializers.good import DocumentOnOrganisationSerializer from api.audit_trail import service as audit_trail_service from api.audit_trail.enums import AuditType -from api.goodstype.document.models import GoodsTypeDocument -from api.goodstype.document.serializers import GoodsTypeDocumentSerializer from api.parties.enums import PartyDocumentType, PartyType from api.parties.models import PartyDocument from api.parties.serializers import PartyDocumentSerializer @@ -142,39 +140,3 @@ def delete_party_document(party, application, user): ) return HttpResponse(status=status.HTTP_204_NO_CONTENT) - - -def get_goods_type_document(goods_type): - if not goods_type: - return JsonResponse(data={"error": "No such goods type"}, status=status.HTTP_400_BAD_REQUEST) - - documents = GoodsTypeDocument.objects.filter(goods_type=goods_type) - return _get_document(documents) - - -def upload_goods_type_document(goods_type, data): - if not goods_type: - return JsonResponse(data={"error": "No such goods type"}, status=status.HTTP_400_BAD_REQUEST) - - documents = GoodsTypeDocument.objects.filter(goods_type=goods_type) - if documents.exists(): - return JsonResponse(data={"error": "Document already exists"}, status=status.HTTP_400_BAD_REQUEST) - - data["goods_type"] = goods_type.id - serializer = GoodsTypeDocumentSerializer(data=data) - - if not serializer.is_valid(): - return JsonResponse({"errors": serializer.errors}, status=status.HTTP_400_BAD_REQUEST) - - serializer.save() - - return JsonResponse({"document": serializer.data}, status=status.HTTP_201_CREATED) - - -def delete_goods_type_document(goods_type): - documents = GoodsTypeDocument.objects.filter(goods_type=goods_type) - for document in documents: - document.delete_s3() - document.delete() - - return HttpResponse(status=status.HTTP_204_NO_CONTENT) diff --git a/api/applications/libraries/goods_on_applications.py b/api/applications/libraries/goods_on_applications.py index 976b7c18f4..8a5d0bf018 100644 --- a/api/applications/libraries/goods_on_applications.py +++ b/api/applications/libraries/goods_on_applications.py @@ -7,7 +7,7 @@ def add_goods_flags_to_submitted_application(application: BaseApplication): """ When an application is submitted; - The 'not yet verified' system flag must be added to its Goods or GoodsTypes + The 'not yet verified' system flag must be added to its Goods A Good's status must also be updated to 'SUBMITTED' """ if application.case_type.sub_type == CaseTypeSubTypeEnum.STANDARD: diff --git a/api/applications/serializers/advice.py b/api/applications/serializers/advice.py index ed1ee2f40c..34bf31c94a 100644 --- a/api/applications/serializers/advice.py +++ b/api/applications/serializers/advice.py @@ -12,7 +12,6 @@ from api.flags.enums import FlagStatuses from api.goods.models import Good from api.applications.models import GoodOnApplication -from api.goodstype.models import GoodsType from api.gov_users.serializers import ( GovUserListSerializer, GovUserSimpleSerializer, @@ -59,7 +58,6 @@ class AdviceViewSerializer(serializers.Serializer): team = TeamReadOnlySerializer() good = GoodField() - goods_type = serializers.UUIDField(source="goods_type_id") country = serializers.UUIDField(source="country_id") end_user = serializers.UUIDField(source="end_user_id") ultimate_end_user = serializers.UUIDField(source="ultimate_end_user_id") @@ -111,7 +109,6 @@ class AdviceCreateSerializer(serializers.ModelSerializer): ) good = GoodField(required=False) - goods_type = serializers.PrimaryKeyRelatedField(queryset=GoodsType.objects.all(), required=False) country = serializers.PrimaryKeyRelatedField(queryset=Country.objects.all(), required=False) end_user = serializers.PrimaryKeyRelatedField( queryset=Party.objects.filter(type=PartyType.END_USER), required=False diff --git a/api/applications/tests/test_copy_application.py b/api/applications/tests/test_copy_application.py index 73ffc7ee11..a6acae9713 100644 --- a/api/applications/tests/test_copy_application.py +++ b/api/applications/tests/test_copy_application.py @@ -256,7 +256,6 @@ def _validate_third_party(self): def _validate_case_data(self): self.assertEqual(list(self.copied_application.case_ecju_query.all()), []) self.assertEqual(list(self.copied_application.case_notes.all()), []) - self.assertEqual(list(self.copied_application.goodcountrydecision_set.all()), []) self.assertEqual(list(self.copied_application.get_case().advice.all()), []) self.assertEqual(list(self.copied_application.applicationdocument_set.all()), []) self.assertEqual(list(self.copied_application.casedocument_set.all()), []) diff --git a/api/applications/tests/test_endpoints_response_applications.py b/api/applications/tests/test_endpoints_response_applications.py index 7ae2de95bf..992bfea37e 100644 --- a/api/applications/tests/test_endpoints_response_applications.py +++ b/api/applications/tests/test_endpoints_response_applications.py @@ -48,30 +48,6 @@ def test_application_goods(self): self.url + self.get_standard_application()["id"] + "/goods/", ) - def test_applications_goodstype_list(self): - self.call_endpoint( - self.get_exporter_headers(), - self.url + self.get_open_application()["id"] + "/goodstypes/", - ) - - def test_applications_goodstype_detail(self): - application = self.get_open_application() - application_id = application["id"] - goods_type = self.get_application_goodstype_id() - url = f"{self.url}{application_id}/goodstype/{goods_type}" - exporter_user = self.get_exporter_headers() - self.call_endpoint(exporter_user, url) - - def test_applications_goodstype_documents(self): - self.call_endpoint( - self.get_exporter_headers(), - self.url - + self.get_open_application()["id"] - + "/goodstype/" - + self.get_application_goodstype_id() - + "/document/", - ) - def test_application_parties_list(self): self.call_endpoint( self.get_exporter_headers(), diff --git a/api/applications/urls.py b/api/applications/urls.py index ea968fcbab..0d843bc54c 100644 --- a/api/applications/urls.py +++ b/api/applications/urls.py @@ -78,12 +78,6 @@ goods.ApplicationGoodOnApplicationUpdateSerialNumbers.as_view(), name="good_on_application_update_serial_numbers", ), - # Goods types - path( - "/goodstype//document/", - documents.GoodsTypeDocumentView.as_view(), - name="goods_type_document", - ), # Parties path("/parties/", parties.ApplicationPartyView.as_view(), name="parties"), path("/parties//", parties.ApplicationPartyView.as_view(), name="party"), diff --git a/api/applications/views/applications.py b/api/applications/views/applications.py index 7ba1c1bc0b..bff82acea4 100644 --- a/api/applications/views/applications.py +++ b/api/applications/views/applications.py @@ -82,7 +82,6 @@ from api.flags.enums import FlagStatuses, SystemFlags from api.goods.serializers import GoodCreateSerializer from api.goods.models import FirearmGoodDetails -from api.goodstype.models import GoodsType from api.licences.enums import LicenceStatus from api.licences.helpers import get_licence_reference_code from api.licences.models import Licence @@ -636,14 +635,10 @@ def post(self, request, pk): # Create new foreign key connection using data from old application (this is for tables pointing to the case) self.create_foreign_relations_for_new_application() - self.duplicate_goodstypes_for_new_application() # Get all parties connected to the application and produce a copy (and replace reference for each one) self.duplicate_parties_on_new_application() - # Remove usage & licenced quantity/ value - self.new_application.goods_type.update(usage=0) - # Save self.new_application.created_at = now() self.new_application.save() @@ -741,25 +736,6 @@ def create_foreign_relations_for_new_application(self): result.created_at = now() result.save() - def duplicate_goodstypes_for_new_application(self): - """ - Creates a duplicate GoodsType and attaches it to the new application if applicable. - """ - # GoodsType has more logic than in "create_foreign_relations_for_new_application", - # such as listing the countries on the goodstype, and flags as such it is seperated. - for good in GoodsType.objects.filter(application_id=self.old_application_id).all(): - old_good_countries = list(good.countries.all()) - old_good_flags = list(good.flags.all()) - old_good_control_list_entries = list(good.control_list_entries.all()) - good.pk = None - good.id = None - good.application = self.new_application - good.created_at = now() - good.save() - good.countries.set(old_good_countries) - good.flags.set(old_good_flags) - good.control_list_entries.set(old_good_control_list_entries) - class ApplicationRouteOfGoods(UpdateAPIView): authentication_classes = (ExporterAuthentication,) diff --git a/api/applications/views/documents.py b/api/applications/views/documents.py index ba74ba9f4d..fe90a00e35 100644 --- a/api/applications/views/documents.py +++ b/api/applications/views/documents.py @@ -1,21 +1,16 @@ from django.db import transaction from django.http import JsonResponse -from rest_framework import status from rest_framework.views import APIView from api.applications.libraries import document_helpers from api.applications.libraries.get_applications import get_application from api.applications.models import ApplicationDocument from api.applications.serializers.document import ApplicationDocumentSerializer -from api.cases.enums import CaseTypeSubTypeEnum from api.core.authentication import ExporterAuthentication from api.core.decorators import ( authorised_to_view_application, - allowed_application_types, application_is_editable, - application_is_major_editable, ) -from api.goodstype.helpers import get_goods_type from api.users.models import ExporterUser @@ -70,35 +65,3 @@ def delete(self, request, pk, doc_pk): """ application = get_application(pk) return document_helpers.delete_application_document(doc_pk, application, request.user) - - -class GoodsTypeDocumentView(APIView): - """ - Retrieve, add or delete a third party document from an application - """ - - authentication_classes = (ExporterAuthentication,) - - @allowed_application_types([CaseTypeSubTypeEnum.HMRC]) - @authorised_to_view_application(ExporterUser) - def get(self, request, pk, goods_type_pk): - goods_type = get_goods_type(goods_type_pk) - return document_helpers.get_goods_type_document(goods_type) - - @transaction.atomic - @allowed_application_types([CaseTypeSubTypeEnum.HMRC]) - @application_is_major_editable - @authorised_to_view_application(ExporterUser) - def post(self, request, pk, goods_type_pk): - goods_type = get_goods_type(goods_type_pk) - return document_helpers.upload_goods_type_document(goods_type, request.data) - - @transaction.atomic - @allowed_application_types([CaseTypeSubTypeEnum.HMRC]) - @authorised_to_view_application(ExporterUser) - def delete(self, request, pk, goods_type_pk): - goods_type = get_goods_type(goods_type_pk) - if not goods_type: - return JsonResponse(data={"error": "No such goods type"}, status=status.HTTP_400_BAD_REQUEST) - - return document_helpers.delete_goods_type_document(goods_type) diff --git a/api/cases/admin.py b/api/cases/admin.py index de7ac7011a..fd87e52d2a 100644 --- a/api/cases/admin.py +++ b/api/cases/admin.py @@ -79,11 +79,6 @@ class EcjuQueryAdmin(admin.ModelAdmin): list_display = ("id",) -@admin.register(models.GoodCountryDecision) -class GoodCountryDecisionAdmin(admin.ModelAdmin): - list_display = ("id",) - - @admin.register(models.EnforcementCheckID) class EnforcementCheckIDAdmin(admin.ModelAdmin): list_display = ( diff --git a/api/cases/libraries/get_goods_type_countries_decisions.py b/api/cases/libraries/get_goods_type_countries_decisions.py deleted file mode 100644 index 2e7367b0b8..0000000000 --- a/api/cases/libraries/get_goods_type_countries_decisions.py +++ /dev/null @@ -1,154 +0,0 @@ -from django.db.models import Q - -from api.cases.enums import AdviceLevel, AdviceType -from api.cases.models import Advice, GoodCountryDecision -from api.goodstype.models import GoodsType - - -def get_existing_good_type_to_country_decisions(case_pk): - """ - Get all existing GoodCountryDecisions for a case - """ - goods_type_countries_decisions = GoodCountryDecision.objects.filter(case_id=case_pk).values( - "goods_type_id", "country_id", "approve" - ) - return {f"{item['goods_type_id']}.{item['country_id']}": item["approve"] for item in goods_type_countries_decisions} - - -def _get_country_on_goods_type_context(country, decision, existing_choice): - """ - Get the dictionary context for a country on a good. - This includes the country name, decision for the country - and value if a decision for this combination already exists - """ - if existing_choice is not None: - existing_choice = AdviceType.APPROVE if existing_choice else AdviceType.REFUSE - - return { - "id": country.id, - "name": country.name, - "decision": decision, - "value": existing_choice, - } - - -def good_type_to_country_decisions(application_pk): - """ - Get data for the Good on Country matrix page. - This divides all good on country combinations into approved - (where both good & country are approved at the final advice level) - and refused (good and/or country is refused). - Returns the approved and refused dictionaries - """ - goods_types_advice = Advice.objects.filter( - case_id=application_pk, - level=AdviceLevel.FINAL, - goods_type__isnull=False, - type__in=[AdviceType.APPROVE, AdviceType.PROVISO, AdviceType.REFUSE], - ).values("goods_type_id", "type") - - approved_and_refused_goods_types = [] - approved_goods_types = {} - - for item in goods_types_advice: - goods_type_id = item["goods_type_id"] - approved_and_refused_goods_types.append(goods_type_id) - if item["type"] != AdviceType.REFUSE: - approved_goods_types[goods_type_id] = item["type"] - - countries_advice = Advice.objects.filter( - case_id=application_pk, - level=AdviceLevel.FINAL, - country__isnull=False, - type__in=[AdviceType.APPROVE, AdviceType.PROVISO, AdviceType.REFUSE], - ).values("country_id", "type") - - approved_and_refused_countries_ids = [] - approved_countries = {} - - for item in countries_advice: - country_id = item["country_id"] - approved_and_refused_countries_ids.append(country_id) - if item["type"] != AdviceType.REFUSE: - approved_countries[country_id] = item["type"] - - goods_types = ( - GoodsType.objects.filter(application_id=application_pk, id__in=approved_and_refused_goods_types) - .prefetch_related("control_list_entries", "countries") - .order_by("created_at") - ) - - goods_type_countries_decisions = get_existing_good_type_to_country_decisions(application_pk) - approved_goods_types_on_destinations = {} - refused_goods_types_on_destinations = {} - approved_goods_types_ids = approved_goods_types.keys() - approved_countries_ids = approved_countries.keys() - - for goods_type in goods_types: - for country in goods_type.countries.filter(id__in=approved_and_refused_countries_ids).order_by("name"): - goods_type_approved = goods_type.id in approved_goods_types_ids - country_approved = country.id in approved_countries_ids - - # Add to approve or refuse dictionary depending on whether both good & country is approved - if goods_type_approved and country_approved: - dictionary = approved_goods_types_on_destinations - else: - dictionary = refused_goods_types_on_destinations - - if goods_type.id not in dictionary.keys(): - dictionary[goods_type.id] = { - "id": goods_type.id, - "decision": approved_goods_types.get(goods_type.id) or AdviceType.REFUSE, - "control_list_entries": [ - {"rating": clc.rating, "text": clc.text} for clc in goods_type.control_list_entries.all() - ], - "description": goods_type.description, - "countries": [ - _get_country_on_goods_type_context( - country, - approved_countries.get(country.id) or AdviceType.REFUSE, - goods_type_countries_decisions.get(f"{goods_type.id}.{country.id}"), - ) - ], - } - else: - dictionary[goods_type.id]["countries"].append( - _get_country_on_goods_type_context( - country, - approved_countries.get(country.id) or AdviceType.REFUSE, - goods_type_countries_decisions.get(f"{goods_type.id}.{country.id}"), - ) - ) - - return approved_goods_types_on_destinations, refused_goods_types_on_destinations - - -def get_required_good_type_to_country_combinations(application_pk): - """ - Get all required good on country combinations. - (both good & country are approved at the final advice level) - """ - approved_ids = Advice.objects.filter( - Q(goods_type__isnull=False) | Q(country__isnull=False), - case_id=application_pk, - level=AdviceLevel.FINAL, - type__in=[AdviceType.APPROVE, AdviceType.PROVISO], - ).values("goods_type_id", "country_id") - - approved_goods_types_ids = [] - approved_countries_ids = [] - - for item in approved_ids: - if item.get("goods_type_id"): - approved_goods_types_ids.append(item["goods_type_id"]) - else: - approved_countries_ids.append(item["country_id"]) - - goods_types = GoodsType.objects.filter( - application_id=application_pk, id__in=approved_goods_types_ids - ).prefetch_related("countries") - - return { - goods_type.id: list(goods_type.countries.filter(id__in=approved_countries_ids).values_list("id", flat=True)) - for goods_type in goods_types - } diff --git a/api/cases/libraries/post_advice.py b/api/cases/libraries/post_advice.py index 695bd776a3..f556d6b1b7 100644 --- a/api/cases/libraries/post_advice.py +++ b/api/cases/libraries/post_advice.py @@ -1,4 +1,3 @@ -from django.db.models import Q from django.http import JsonResponse from rest_framework import status from rest_framework.exceptions import ErrorDetail @@ -14,7 +13,7 @@ from api.cases.enums import AdviceLevel, AdviceType from api.cases.generated_documents.models import GeneratedCaseDocument from api.cases.libraries.get_case import get_case -from api.cases.models import Advice, GoodCountryDecision +from api.cases.models import Advice from api.core import constants from api.core.constants import GovPermissions from api.core.permissions import assert_user_has_permission @@ -44,26 +43,6 @@ def check_refusal_errors(advice): return None -def update_good_country_decisions(data): - """ - Delete any GoodCountryDecision's that may now be invalid - (the country or goods type are no longer approved) - """ - refused_good_types_ids = [ - advice["goods_type"] - for advice in data - if advice.get("goods_type") and advice["type"] not in [AdviceType.APPROVE, AdviceType.PROVISO] - ] - refused_country_ids = [ - advice["country"] - for advice in data - if advice.get("country") and advice["type"] not in [AdviceType.APPROVE, AdviceType.PROVISO] - ] - GoodCountryDecision.objects.filter( - Q(country_id__in=refused_country_ids) | Q(goods_type_id__in=refused_good_types_ids) - ).delete() - - def post_advice(request, case, level, team=False): if CaseStatusEnum.is_terminal(case.status.status): return JsonResponse( @@ -112,8 +91,6 @@ def post_advice(request, case, level, team=False): ) if level == AdviceLevel.FINAL: - # Remove GoodCountryDecision if changing approve decision for applicable country/goods type - update_good_country_decisions(data) # Remove outdated draft decision documents if advice changes GeneratedCaseDocument.objects.filter( case_id=case.id, advice_type__isnull=False, visible_to_exporter=False diff --git a/api/cases/models.py b/api/cases/models.py index 48765c8c05..446d4eeed3 100644 --- a/api/cases/models.py +++ b/api/cases/models.py @@ -34,7 +34,6 @@ from api.goods.enums import PvGrading from api.organisations.models import Organisation from api.queues.models import Queue -from api.staticdata.countries.models import Country from api.staticdata.decisions.models import Decision from api.staticdata.denial_reasons.models import DenialReason from api.staticdata.statuses.enums import CaseStatusEnum, CaseSubStatusIdEnum @@ -254,7 +253,6 @@ def parameter_set(self): """ from api.applications.models import PartyOnApplication from api.applications.models import GoodOnApplication - from api.goodstype.models import GoodsType parameter_set = set(self.flags.all()) | {self.case_type} | set(self.organisation.flags.all()) @@ -266,9 +264,6 @@ def parameter_set(self): for goa in GoodOnApplication.objects.filter(application=self.id): parameter_set = parameter_set | set(goa.good.flags.all()) - for goods_type in GoodsType.objects.filter(application=self.id): - parameter_set = parameter_set | set(goods_type.flags.all()) - return parameter_set def remove_all_case_assignments(self): @@ -608,7 +603,7 @@ class Advice(TimestampableModel): Advice for goods and destinations on cases """ - ENTITY_FIELDS = ["good", "goods_type", "country", "end_user", "consignee", "ultimate_end_user", "third_party"] + ENTITY_FIELDS = ["good", "country", "end_user", "consignee", "ultimate_end_user", "third_party"] id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) case = models.ForeignKey(Case, related_name="advice", on_delete=models.CASCADE) @@ -626,7 +621,6 @@ class Advice(TimestampableModel): # Optional goods/destinations good = models.ForeignKey("goods.Good", related_name="advice", on_delete=models.CASCADE, null=True) - goods_type = models.ForeignKey("goodstype.GoodsType", related_name="advice", on_delete=models.CASCADE, null=True) country = models.ForeignKey("countries.Country", related_name="advice", on_delete=models.CASCADE, null=True) end_user = models.ForeignKey("parties.Party", on_delete=models.CASCADE, null=True) ultimate_end_user = models.ForeignKey( @@ -675,7 +669,6 @@ def save(self, *args, **kwargs): team=self.team, level=AdviceLevel.TEAM, good=self.good, - goods_type=self.goods_type, country=self.country, end_user=self.end_user, ultimate_end_user=self.ultimate_end_user, @@ -691,7 +684,6 @@ def save(self, *args, **kwargs): user=self.user, team=self.user.team, level=AdviceLevel.USER, - goods_type=self.goods_type, country=self.country, end_user=self.end_user, ultimate_end_user=self.ultimate_end_user, @@ -828,17 +820,6 @@ class EcjuQueryDocument(Document): description = models.TextField(default="", blank=True) -class GoodCountryDecision(TimestampableModel): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - case = models.ForeignKey(Case, on_delete=models.CASCADE) - goods_type = models.ForeignKey("goodstype.GoodsType", on_delete=models.CASCADE) - country = models.ForeignKey(Country, on_delete=models.CASCADE) - approve = models.BooleanField(blank=False, null=False) - - class Meta: - unique_together = [["case", "goods_type", "country"]] - - class EnforcementCheckID(models.Model): """ Enforcement XML doesn't support 64 bit ints (UUID's). diff --git a/api/cases/serializers.py b/api/cases/serializers.py index 224f0d9db6..2262a2a728 100644 --- a/api/cases/serializers.py +++ b/api/cases/serializers.py @@ -25,7 +25,6 @@ EcjuQuery, EcjuQueryDocument, Advice, - GoodCountryDecision, CaseType, ) from api.cases.service import retrieve_latest_activity @@ -34,7 +33,6 @@ from api.compliance.serializers.ComplianceVisitCaseSerializers import ComplianceVisitSerializer from api.core.serializers import KeyValueChoiceField, PrimaryKeyRelatedSerializerField from api.documents.libraries.process_document import process_document -from api.goodstype.models import GoodsType from api.gov_users.serializers import GovUserSimpleSerializer from api.organisations.models import Organisation from api.organisations.serializers import TinyOrganisationViewSerializer @@ -42,7 +40,6 @@ from api.queues.constants import ALL_CASES_QUEUE_ID from api.queues.models import Queue from api.queues.serializers import QueueListSerializer -from api.staticdata.countries.models import Country from api.staticdata.statuses.enums import CaseStatusEnum from api.teams.serializers import TeamSerializer from api.users.enums import UserStatuses @@ -708,17 +705,6 @@ class Meta: ) -class GoodCountryDecisionSerializer(serializers.ModelSerializer): - case = serializers.PrimaryKeyRelatedField(queryset=Case.objects.all()) - good = serializers.PrimaryKeyRelatedField(queryset=GoodsType.objects.all()) - country = serializers.PrimaryKeyRelatedField(queryset=Country.objects.all()) - decision = KeyValueChoiceField(choices=AdviceType.choices) - - class Meta: - model = GoodCountryDecision - fields = "__all__" - - class CaseOfficerUpdateSerializer(serializers.ModelSerializer): """ Serializer for assigning and removing case officers from a case. diff --git a/api/cases/tests/factories.py b/api/cases/tests/factories.py index e4b46507db..0e2147ece4 100644 --- a/api/cases/tests/factories.py +++ b/api/cases/tests/factories.py @@ -10,13 +10,10 @@ CaseStatus, CaseType, EcjuQuery, - GoodCountryDecision, DepartmentSLA, ) from api.queues.tests.factories import QueueFactory from api.organisations.tests.factories import OrganisationFactory -from api.goodstype.tests.factories import GoodsTypeFactory -from api.staticdata.countries.factories import CountryFactory from api.teams.tests.factories import TeamFactory, DepartmentFactory from api.users.tests.factories import BaseUserFactory, GovUserFactory @@ -72,15 +69,6 @@ class Meta: model = CountersignAdvice -class GoodCountryDecisionFactory(factory.django.DjangoModelFactory): - goods_type = factory.SubFactory(GoodsTypeFactory, application=factory.SelfAttribute("..case")) - country = factory.SubFactory(CountryFactory) - approve = True - - class Meta: - model = GoodCountryDecision - - class CaseSIELFactory(CaseFactory): case_type_id = CaseTypeEnum.SIEL.id diff --git a/api/cases/views/search/service.py b/api/cases/views/search/service.py index 3c53b670ee..aa85f8c990 100644 --- a/api/cases/views/search/service.py +++ b/api/cases/views/search/service.py @@ -75,13 +75,10 @@ def populate_goods_flags(cases: List[Dict]): qs1 = Flag.objects.filter(goods__goods_on_application__application_id__in=case_ids).annotate( case_id=F("goods__goods_on_application__application_id"), ) - qs2 = Flag.objects.filter(goods_type__application_id__in=case_ids).annotate( - case_id=F("goods_type__application_id"), - ) qs3 = Flag.objects.filter(goods__good__id__in=case_ids).annotate( case_id=F("goods__good__id"), ) - flags = qs1.union(qs2, qs3) + flags = qs1.union(qs3) for case in cases: case["goods_flags"] = CaseListFlagSerializer( diff --git a/api/cases/views/views.py b/api/cases/views/views.py index f295fbbcb6..048c031e07 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -28,11 +28,6 @@ from api.cases.libraries.get_case import get_case, get_case_document from api.cases.libraries.get_destination import get_destination from api.cases.libraries.get_ecju_queries import get_ecju_query -from api.cases.libraries.get_goods_type_countries_decisions import ( - good_type_to_country_decisions, - get_required_good_type_to_country_combinations, - get_existing_good_type_to_country_decisions, -) from api.cases.libraries.post_advice import ( post_advice, update_advice, @@ -46,7 +41,6 @@ EcjuQuery, EcjuQueryDocument, Advice, - GoodCountryDecision, CaseAssignment, ) from api.cases.models import CountersignAdvice @@ -499,8 +493,6 @@ def delete(self, request, pk): """ assert_user_has_permission(request.user.govuser, constants.GovPermissions.MANAGE_LICENCE_FINAL_ADVICE) self.final_advice.delete() - # Delete GoodCountryDecisions as final advice is no longer applicable - GoodCountryDecision.objects.filter(case_id=pk).delete() audit_trail_service.create( actor=request.user, verb=AuditType.CLEARED_FINAL_ADVICE, @@ -695,60 +687,6 @@ def delete(self, request, **kwargs): return JsonResponse({"document": "deleted success"}) -class GoodsCountriesDecisions(APIView): - authentication_classes = (GovAuthentication,) - - def get(self, request, pk): - assert_user_has_permission(request.user.govuser, constants.GovPermissions.MANAGE_LICENCE_FINAL_ADVICE) - approved, refused = good_type_to_country_decisions(pk) - return JsonResponse({"approved": list(approved.values()), "refused": list(refused.values())}) - - @transaction.atomic - def post(self, request, pk): - assert_user_has_permission(request.user.govuser, constants.GovPermissions.MANAGE_LICENCE_FINAL_ADVICE) - - data = {k: v for k, v in request.data.items() if v is not None} - - # Get list of all required item id's - required_decisions = get_required_good_type_to_country_combinations(pk) - required_decision_ids = set() - for goods_type, country_list in required_decisions.items(): - for country in country_list: - required_decision_ids.add(f"{goods_type}.{country}") - - if not required_decision_ids.issubset(data): - missing_ids = required_decision_ids.difference(request.data) - raise ParseError({missing_id: [Cases.GoodCountryMatrix.MISSING_ITEM] for missing_id in missing_ids}) - - # Delete existing decision documents if decision changes - existing_decisions = get_existing_good_type_to_country_decisions(pk) - for decision_id in required_decision_ids: - if (data.get(decision_id) != AdviceType.REFUSE) != existing_decisions.get(decision_id): - # Proviso N/A as there is no proviso document type - GeneratedCaseDocument.objects.filter( - case_id=pk, advice_type__in=[AdviceType.APPROVE, AdviceType.REFUSE], visible_to_exporter=False - ).delete() - break - - # Update or create GoodCountryDecisions - for id in required_decision_ids: - goods_type_id, country_id = id.split(".") - value = data[id] == AdviceType.APPROVE - GoodCountryDecision.objects.update_or_create( - case_id=pk, goods_type_id=goods_type_id, country_id=country_id, defaults={"approve": value} - ) - - audit_trail_service.create( - actor=request.user, - verb=AuditType.UPDATED_GOOD_ON_DESTINATION_MATRIX, - target=get_case(pk), - ) - - return JsonResponse( - data={"good_country_decisions": list(required_decision_ids)}, status=status.HTTP_201_CREATED - ) - - class Destination(APIView): authentication_classes = (GovAuthentication,) diff --git a/api/compliance/helpers.py b/api/compliance/helpers.py index 52aabf1cac..3001d6e53a 100644 --- a/api/compliance/helpers.py +++ b/api/compliance/helpers.py @@ -52,7 +52,7 @@ def get_record_holding_sites_for_case(case): Site.objects.filter(sites_on_application__application_id=case.id).values_list( "site_records_located_at_id", flat=True ) - ) + ) # /PS-IGNORE def generate_compliance_site_case(case: Case): diff --git a/api/conf/settings.py b/api/conf/settings.py index d548977897..75af7fb613 100644 --- a/api/conf/settings.py +++ b/api/conf/settings.py @@ -86,7 +86,6 @@ "api.documents", "api.flags", "api.goods", - "api.goodstype", "api.gov_users", "api.letter_templates", "api.licences", diff --git a/api/conf/urls.py b/api/conf/urls.py index 3bf3558f9a..bda28fb497 100644 --- a/api/conf/urls.py +++ b/api/conf/urls.py @@ -17,7 +17,6 @@ path("cases/", include("api.cases.urls")), path("compliance/", include("api.compliance.urls")), path("goods/", include("api.goods.urls")), - path("goods-types/", include("api.goodstype.urls")), path("letter-templates/", include("api.letter_templates.urls")), path("organisations/", include("api.organisations.urls")), path("queues/", include("api.queues.urls")), diff --git a/api/core/management/commands/create_er_diagrams.py b/api/core/management/commands/create_er_diagrams.py index 5ee4b33561..83bb08079b 100644 --- a/api/core/management/commands/create_er_diagrams.py +++ b/api/core/management/commands/create_er_diagrams.py @@ -11,7 +11,6 @@ "documents", "flags", "goods", - "goodstype", "letter_templates", "licences", "organisations", diff --git a/api/data_workspace/openapi.yaml b/api/data_workspace/openapi.yaml index 10a4aa6065..a9f9999f80 100644 --- a/api/data_workspace/openapi.yaml +++ b/api/data_workspace/openapi.yaml @@ -1897,10 +1897,6 @@ components: type: string format: uuid nullable: true - goods_type: - type: string - format: uuid - nullable: true country: type: string nullable: true diff --git a/api/data_workspace/v1/tests/test_advice_views.py b/api/data_workspace/v1/tests/test_advice_views.py index ad50614e2b..86dadeacfa 100644 --- a/api/data_workspace/v1/tests/test_advice_views.py +++ b/api/data_workspace/v1/tests/test_advice_views.py @@ -35,7 +35,6 @@ def test_advice(self): "user", "team", "good", - "goods_type", "country", "end_user", "ultimate_end_user", diff --git a/api/flags/helpers.py b/api/flags/helpers.py index 4de3acfc0c..6498f1dc6c 100644 --- a/api/flags/helpers.py +++ b/api/flags/helpers.py @@ -1,16 +1,12 @@ from api.cases.libraries.get_case import get_case from api.cases.libraries.get_destination import get_destination from api.goods.models import Good -from api.goodstype.helpers import get_goods_type from api.organisations.libraries.get_organisation import get_organisation_by_pk def get_object_of_level(level, pk): if level == "good": - try: - good = Good.objects.get(pk=pk) - except Good.DoesNotExist: - good = get_goods_type(pk) + good = Good.objects.get(pk=pk) return good elif level == "case": return get_case(pk) diff --git a/api/goodstype/__init__.py b/api/goodstype/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/goodstype/constants.py b/api/goodstype/constants.py deleted file mode 100644 index baf726e807..0000000000 --- a/api/goodstype/constants.py +++ /dev/null @@ -1 +0,0 @@ -DESCRIPTION_MAX_LENGTH = 2000 diff --git a/api/goodstype/document/__init__.py b/api/goodstype/document/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/goodstype/document/models.py b/api/goodstype/document/models.py deleted file mode 100644 index 35ab3afefe..0000000000 --- a/api/goodstype/document/models.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.db import models - -from api.documents.models import Document - - -class GoodsTypeDocument(Document): - goods_type = models.ForeignKey("goodstype.GoodsType", on_delete=models.CASCADE) - description = models.TextField(default=None, blank=True, null=True, max_length=280) diff --git a/api/goodstype/document/serializers.py b/api/goodstype/document/serializers.py deleted file mode 100644 index 95a0519506..0000000000 --- a/api/goodstype/document/serializers.py +++ /dev/null @@ -1,26 +0,0 @@ -from rest_framework import serializers - -from api.documents.libraries.process_document import process_document -from api.goodstype.document.models import GoodsTypeDocument -from api.goodstype.models import GoodsType - - -class GoodsTypeDocumentSerializer(serializers.ModelSerializer): - goods_type = serializers.PrimaryKeyRelatedField(queryset=GoodsType.objects.all()) - - class Meta: - model = GoodsTypeDocument - fields = ( - "id", - "name", - "s3_key", - "size", - "goods_type", - "safe", - ) - - def create(self, validated_data): - document = super(GoodsTypeDocumentSerializer, self).create(validated_data) - document.save() - process_document(document) - return document diff --git a/api/goodstype/helpers.py b/api/goodstype/helpers.py deleted file mode 100644 index d5ea63e6e6..0000000000 --- a/api/goodstype/helpers.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.http import Http404 - -from api.goodstype.document.models import GoodsTypeDocument -from api.goodstype.models import GoodsType - - -def get_goods_type(pk): - try: - return GoodsType.objects.get(pk=pk) - except GoodsType.DoesNotExist: - raise Http404 - - -def delete_goods_type_document_if_exists(goods_type: GoodsType): - try: - document = GoodsTypeDocument.objects.get(goods_type=goods_type) - document.delete_s3() - document.delete() - except GoodsTypeDocument.DoesNotExist: - pass diff --git a/api/goodstype/migrations/0001_initial.py b/api/goodstype/migrations/0001_initial.py deleted file mode 100644 index c027ef91d7..0000000000 --- a/api/goodstype/migrations/0001_initial.py +++ /dev/null @@ -1,87 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-10 13:26 - -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone -import model_utils.fields -import uuid - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ("flags", "0001_initial"), - ("countries", "0001_initial"), - ("documents", "0001_initial"), - ( - "applications", - "0002_baseapplication_countryonapplication_exhibitionclearanceapplication_externallocationonapplication_go", - ), - ] - - operations = [ - migrations.CreateModel( - name="GoodsType", - fields=[ - ( - "created_at", - model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, editable=False, verbose_name="created_at" - ), - ), - ( - "updated_at", - model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, editable=False, verbose_name="updated_at" - ), - ), - ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ("description", models.TextField(blank=True, default=None, max_length=2000, null=True)), - ("is_good_controlled", models.BooleanField(blank=True, default=None, null=True)), - ("control_code", models.TextField(blank=True, default=None, null=True)), - ("is_good_incorporated", models.BooleanField(blank=True, default=None, null=True)), - ("comment", models.TextField(blank=True, default=None, null=True)), - ("report_summary", models.TextField(blank=True, default=None, null=True)), - ( - "application", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="base_application", - to="applications.BaseApplication", - ), - ), - ("countries", models.ManyToManyField(default=[], related_name="goods_type", to="countries.Country")), - ("flags", models.ManyToManyField(related_name="goods_type", to="flags.Flag")), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="GoodsTypeDocument", - fields=[ - ( - "document_ptr", - models.OneToOneField( - auto_created=True, - on_delete=django.db.models.deletion.CASCADE, - parent_link=True, - primary_key=True, - serialize=False, - to="documents.Document", - ), - ), - ("description", models.TextField(blank=True, default=None, max_length=280, null=True)), - ( - "goods_type", - models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="goodstype.GoodsType"), - ), - ], - options={ - "abstract": False, - }, - bases=("documents.document",), - ), - ] diff --git a/api/goodstype/migrations/0002_auto_20200402_1302.py b/api/goodstype/migrations/0002_auto_20200402_1302.py deleted file mode 100644 index 4e0366e119..0000000000 --- a/api/goodstype/migrations/0002_auto_20200402_1302.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2.11 on 2020-04-02 13:02 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("goodstype", "0001_initial"), - ] - - operations = [ - migrations.AlterField( - model_name="goodstype", - name="application", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="goods_type", - to="applications.BaseApplication", - ), - ), - ] diff --git a/api/goodstype/migrations/0003_goodstype_usage.py b/api/goodstype/migrations/0003_goodstype_usage.py deleted file mode 100644 index 54fde12c21..0000000000 --- a/api/goodstype/migrations/0003_goodstype_usage.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.11 on 2020-04-17 14:17 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("goodstype", "0002_auto_20200402_1302"), - ] - - operations = [ - migrations.AddField( - model_name="goodstype", - name="usage", - field=models.FloatField(default=0), - ), - ] diff --git a/api/goodstype/migrations/0004_auto_20200423_1456.py b/api/goodstype/migrations/0004_auto_20200423_1456.py deleted file mode 100644 index 3e930aa6de..0000000000 --- a/api/goodstype/migrations/0004_auto_20200423_1456.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 2.2.12 on 2020-04-23 14:56 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("control_list_entries", "0002_auto_20200419_1827"), - ("goodstype", "0003_goodstype_usage"), - ] - - operations = [ - migrations.AlterModelOptions( - name="goodstype", - options={"ordering": ["-created_at"]}, - ), - migrations.RemoveField( - model_name="goodstype", - name="control_code", - ), - migrations.AddField( - model_name="goodstype", - name="control_list_entries", - field=models.ManyToManyField(related_name="goods_types", to="control_list_entries.ControlListEntry"), - ), - migrations.AlterModelTable( - name="goodstype", - table="goods_type", - ), - ] diff --git a/api/goodstype/migrations/0005_auto_20200924_1731.py b/api/goodstype/migrations/0005_auto_20200924_1731.py deleted file mode 100644 index 8b8727cfc0..0000000000 --- a/api/goodstype/migrations/0005_auto_20200924_1731.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 2.2.16 on 2020-09-24 16:31 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("goodstype", "0004_auto_20200423_1456"), - ] - - operations = [ - migrations.AlterField( - model_name="goodstype", - name="application", - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="applications.BaseApplication"), - ), - migrations.AlterField( - model_name="goodstype", - name="comment", - field=models.TextField(blank=True, default=None, help_text="control review comment", null=True), - ), - migrations.AlterField( - model_name="goodstype", - name="control_list_entries", - field=models.ManyToManyField(to="control_list_entries.ControlListEntry"), - ), - ] diff --git a/api/goodstype/migrations/0006_auto_20200925_1105.py b/api/goodstype/migrations/0006_auto_20200925_1105.py deleted file mode 100644 index bdd9f98598..0000000000 --- a/api/goodstype/migrations/0006_auto_20200925_1105.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 2.2.16 on 2020-09-25 10:05 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("goodstype", "0005_auto_20200924_1731"), - ] - - operations = [ - migrations.AlterModelOptions( - name="goodstype", - options={"default_related_name": "goods_type", "ordering": ["-created_at"]}, - ), - migrations.AlterField( - model_name="goodstype", - name="application", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="goods_type", - to="applications.BaseApplication", - ), - ), - migrations.AlterField( - model_name="goodstype", - name="control_list_entries", - field=models.ManyToManyField(related_name="goods_type", to="control_list_entries.ControlListEntry"), - ), - ] diff --git a/api/goodstype/migrations/0007_goodstype_end_use_control.py b/api/goodstype/migrations/0007_goodstype_end_use_control.py deleted file mode 100644 index 7020fd249b..0000000000 --- a/api/goodstype/migrations/0007_goodstype_end_use_control.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.16 on 2020-10-23 16:52 - -import django.contrib.postgres.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("goodstype", "0006_auto_20200925_1105"), - ] - - operations = [ - migrations.AddField( - model_name="goodstype", - name="end_use_control", - field=django.contrib.postgres.fields.ArrayField( - base_field=models.TextField(), - default=list, - help_text="Control code given to good due to the end use e.g, a wood screw may be used in a Harrier jump jet.", - size=None, - ), - ), - ] diff --git a/api/goodstype/migrations/0008_auto_20201104_1047.py b/api/goodstype/migrations/0008_auto_20201104_1047.py deleted file mode 100644 index 03a858021e..0000000000 --- a/api/goodstype/migrations/0008_auto_20201104_1047.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 2.2.16 on 2020-11-04 10:47 - -import django.contrib.postgres.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("goodstype", "0007_goodstype_end_use_control"), - ] - - operations = [ - migrations.AlterField( - model_name="goodstype", - name="end_use_control", - field=django.contrib.postgres.fields.ArrayField( - base_field=models.TextField(), - blank=True, - default=list, - help_text="Control code given to good due to the end use e.g, a wood screw may be used in a Harrier jump jet.", - size=None, - ), - ), - ] diff --git a/api/goodstype/migrations/0009_goodstype_firearm_details.py b/api/goodstype/migrations/0009_goodstype_firearm_details.py deleted file mode 100644 index f4f3daa343..0000000000 --- a/api/goodstype/migrations/0009_goodstype_firearm_details.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 2.2.16 on 2020-11-09 17:15 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("goods", "0016_firearmgooddetails_serial_number"), - ("goodstype", "0008_auto_20201104_1047"), - ] - - operations = [ - migrations.AddField( - model_name="goodstype", - name="firearm_details", - field=models.ForeignKey( - blank=True, - default=None, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="goods_type", - to="goods.FirearmGoodDetails", - ), - ), - ] diff --git a/api/goodstype/migrations/0010_goodstype_is_precedent.py b/api/goodstype/migrations/0010_goodstype_is_precedent.py deleted file mode 100644 index 73cbc24187..0000000000 --- a/api/goodstype/migrations/0010_goodstype_is_precedent.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.16 on 2020-12-07 11:59 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("goodstype", "0009_goodstype_firearm_details"), - ] - - operations = [ - migrations.AddField( - model_name="goodstype", - name="is_precedent", - field=models.BooleanField(default=False), - ), - ] diff --git a/api/goodstype/migrations/0011_alter_goodstype_application_and_more.py b/api/goodstype/migrations/0011_alter_goodstype_application_and_more.py deleted file mode 100644 index 32480a36c2..0000000000 --- a/api/goodstype/migrations/0011_alter_goodstype_application_and_more.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 4.2.8 on 2023-12-20 17:38 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("control_list_entries", "0005_adds_5D001e"), - ("applications", "0077_back_populate_product_report_summary_prefix_and_suffix"), - ("goods", "0026_auto_20230201_1536"), - ("goodstype", "0010_goodstype_is_precedent"), - ] - - operations = [ - migrations.AlterField( - model_name="goodstype", - name="application", - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="applications.baseapplication"), - ), - migrations.AlterField( - model_name="goodstype", - name="control_list_entries", - field=models.ManyToManyField(to="control_list_entries.controllistentry"), - ), - migrations.AlterField( - model_name="goodstype", - name="firearm_details", - field=models.ForeignKey( - blank=True, - default=None, - null=True, - on_delete=django.db.models.deletion.CASCADE, - to="goods.firearmgooddetails", - ), - ), - ] diff --git a/api/goodstype/migrations/__init__.py b/api/goodstype/migrations/__init__.py deleted file mode 100755 index e69de29bb2..0000000000 diff --git a/api/goodstype/models.py b/api/goodstype/models.py deleted file mode 100644 index 5ded4752c8..0000000000 --- a/api/goodstype/models.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.db import models - -from api.applications.models import AbstractGoodOnApplication -from api.flags.models import Flag -from api.goodstype.constants import DESCRIPTION_MAX_LENGTH -from api.staticdata.countries.models import Country - -# Import of GoodsTypeDocument necessary as django flush command cannot find this model otherwise -# flake8: noqa -from api.goodstype.document.models import GoodsTypeDocument - - -class GoodsType(AbstractGoodOnApplication): - description = models.TextField(default=None, blank=True, null=True, max_length=DESCRIPTION_MAX_LENGTH) - countries = models.ManyToManyField(Country, related_name="goods_type", default=[]) - usage = models.FloatField(null=False, blank=False, default=0) - flags = models.ManyToManyField(Flag, related_name="goods_type") - - class Meta: - db_table = "goods_type" - ordering = ["-created_at"] - default_related_name = "goods_type" diff --git a/api/goodstype/serializers.py b/api/goodstype/serializers.py deleted file mode 100644 index eb92653097..0000000000 --- a/api/goodstype/serializers.py +++ /dev/null @@ -1,112 +0,0 @@ -from rest_framework import serializers -from rest_framework.fields import empty - -from api.applications.models import BaseApplication -from api.cases.enums import CaseTypeSubTypeEnum -from api.core.serializers import ControlListEntryField, GoodControlReviewSerializer -from api.flags.enums import SystemFlags -from api.goods.enums import GoodControlled -from api.goodstype.constants import DESCRIPTION_MAX_LENGTH -from api.goodstype.document.models import GoodsTypeDocument -from api.goodstype.models import GoodsType -from api.staticdata.control_list_entries.serializers import ControlListEntrySerializer -from api.staticdata.countries.serializers import CountrySerializer - - -class GoodsTypeSerializer(serializers.ModelSerializer): - description = serializers.CharField(max_length=DESCRIPTION_MAX_LENGTH) - is_good_incorporated = serializers.BooleanField(required=True) - is_good_controlled = serializers.BooleanField(required=True) - control_list_entries = ControlListEntryField(many=True, required=False, allow_empty=True) - application = serializers.PrimaryKeyRelatedField(queryset=BaseApplication.objects.all()) - document = serializers.SerializerMethodField() - - class Meta: - model = GoodsType - fields = ( - "id", - "description", - "application", - "document", - "is_good_incorporated", - "is_good_controlled", - "control_list_entries", - ) - - def __init__(self, *args, **kwargs): - """ - Initializes serializer for Goods Type - """ - super(GoodsTypeSerializer, self).__init__(*args, **kwargs) - - # Only add is_good_incorporated if application is of type OPEN - # and not if it's a HMRC - application = self.get_initial().get("application") - - if application and application.case_type.sub_type == CaseTypeSubTypeEnum.HMRC: - if hasattr(self, "initial_data"): - self.fields["is_good_incorporated"].required = False - self.fields["is_good_incorporated"].allow_null = True - self.fields["is_good_controlled"].required = False - self.fields["is_good_controlled"].allow_null = True - self.fields["control_list_entries"].required = False - self.fields["control_list_entries"].allow_null = True - self.initial_data["is_good_controlled"] = False - self.initial_data["is_good_incorporated"] = None - self.initial_data["control_list_entries"] = [] - - def get_document(self, instance): - docs = GoodsTypeDocument.objects.filter(goods_type=instance).values() - return docs[0] if docs else None - - def update(self, instance, validated_data): - """ - Update Goods Type Serializer - """ - instance.description = validated_data.get("description", instance.description) - instance.is_good_controlled = validated_data.get("is_good_controlled", instance.is_good_controlled) - instance.control_list_entry = validated_data.get("control_list_entries", instance.control_list_entry) - instance.is_good_incorporated = validated_data.get("is_good_incorporated", instance.is_good_incorporated) - instance.save() - return instance - - -class GoodsTypeViewSerializer(serializers.Serializer): - id = serializers.UUIDField(read_only=True) - description = serializers.CharField(read_only=True) - is_good_controlled = serializers.ChoiceField(choices=GoodControlled.choices) - is_good_incorporated = serializers.BooleanField(read_only=True) - control_list_entries = ControlListEntrySerializer(many=True, read_only=True) - countries = serializers.SerializerMethodField() - document = serializers.SerializerMethodField() - flags = serializers.SerializerMethodField() - comment = serializers.CharField() - report_summary = serializers.CharField(allow_blank=True, required=False) - end_use_control = serializers.ListField(child=serializers.CharField(), required=False) - - def __init__(self, instance=None, data=empty, default_countries=None, **kwargs): - super().__init__(instance, data, **kwargs) - self.default_countries = default_countries - - def get_flags(self, instance): - return list(instance.flags.filter().values("id", "name", "colour", "label")) - - def get_countries(self, instance): - countries = instance.countries - if not countries.count(): - return CountrySerializer(self.default_countries or [], many=True).data - return CountrySerializer(countries, many=True).data - - def get_document(self, instance): - docs = GoodsTypeDocument.objects.filter(goods_type=instance).values() - return docs[0] if docs else None - - -class ClcControlGoodTypeSerializer(GoodControlReviewSerializer): - class Meta(GoodControlReviewSerializer.Meta): - model = GoodsType - fields = GoodControlReviewSerializer.Meta.fields + ("end_use_control",) - - def update(self, instance, validated_data): - instance.flags.remove(SystemFlags.GOOD_NOT_YET_VERIFIED_ID) - return super().update(instance, validated_data) diff --git a/api/goodstype/tests/factories.py b/api/goodstype/tests/factories.py deleted file mode 100644 index 4fbd7656f3..0000000000 --- a/api/goodstype/tests/factories.py +++ /dev/null @@ -1,31 +0,0 @@ -import factory - -from api.goodstype import models -from api.staticdata.control_list_entries.helpers import get_control_list_entry - - -class GoodsTypeFactory(factory.django.DjangoModelFactory): - description = factory.Faker("word") - is_good_incorporated = True - application = None - is_good_controlled = False - - class Meta: - model = models.GoodsType - - @factory.post_generation - def control_list_entries(self, create, extracted, **kwargs): - if not create: - # Simple build, do nothing. - return - control_list_entries = extracted or ["ML1a"] - for control_list_entry in control_list_entries: - self.control_list_entries.add(get_control_list_entry(control_list_entry)) - - @factory.post_generation - def flags(self, create, extracted, **kwargs): - if not create or not extracted: - # Simple build, do nothing. - return - - self.flags.set(extracted) diff --git a/api/goodstype/urls.py b/api/goodstype/urls.py deleted file mode 100644 index 0c6b4c60ac..0000000000 --- a/api/goodstype/urls.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.urls import path - -from api.goodstype import views - -app_name = "goodstype" - -urlpatterns = [ - path("/", views.RetrieveGoodsType.as_view(), name="retrieve"), -] diff --git a/api/goodstype/views.py b/api/goodstype/views.py deleted file mode 100644 index 25eedef55d..0000000000 --- a/api/goodstype/views.py +++ /dev/null @@ -1,11 +0,0 @@ -from rest_framework.generics import RetrieveAPIView - -from api.core.authentication import GovAuthentication -from api.goodstype.models import GoodsType -from api.goodstype.serializers import GoodsTypeViewSerializer - - -class RetrieveGoodsType(RetrieveAPIView): - authentication_classes = (GovAuthentication,) - queryset = GoodsType.objects.all() - serializer_class = GoodsTypeViewSerializer diff --git a/api/letter_templates/context_generator.py b/api/letter_templates/context_generator.py index 842ec854fd..c9c0d9dff0 100644 --- a/api/letter_templates/context_generator.py +++ b/api/letter_templates/context_generator.py @@ -2,7 +2,6 @@ from datetime import timedelta from django.contrib.humanize.templatetags.humanize import intcomma -from django.db.models import Q from django.utils import timezone from rest_framework import serializers @@ -30,8 +29,7 @@ GoodOnApplication, ) from api.goods.models import PvGradingDetails, Good, FirearmGoodDetails -from api.goodstype.models import GoodsType -from api.cases.models import Advice, EcjuQuery, CaseNote, Case, GoodCountryDecision, CaseType +from api.cases.models import Advice, EcjuQuery, CaseNote, Case, CaseType from api.organisations.models import Organisation from api.addresses.models import Address from api.parties.models import Party @@ -366,18 +364,6 @@ def get_grading(self, obj): return obj.custom_grading -class GoodsTypeSerializer(serializers.ModelSerializer): - class Meta: - model = GoodsType - fields = ["description", "control_list_entries", "is_controlled"] - - control_list_entries = serializers.SerializerMethodField() - is_controlled = FriendlyBooleanField(source="is_good_controlled") - - def get_control_list_entries(self, obj): - return [clc.rating for clc in obj.control_list_entries.all()] - - class GoodsQueryGoodSerializer(serializers.ModelSerializer): class Meta: model = Good @@ -699,13 +685,6 @@ def get_document_context(case, addressee=None): if getattr(base_application, "goods", "") and base_application.goods.exists(): goods = _get_goods_context(base_application, final_advice, licence) - elif getattr(base_application, "goods_type", "") and base_application.goods_type.exists(): - goods = _get_goods_type_context( - base_application.goods_type.all() - .order_by("created_at") - .prefetch_related("countries", "control_list_entries"), - case.pk, - ) else: goods = None @@ -906,100 +885,3 @@ def _get_goods_context(application, final_advice, licence=None): ordered_goods = sorted(approved_goods, key=lambda d: d["created_at"]) goods_context[AdviceType.APPROVE] = ordered_goods return goods_context - - -def _get_approved_goods_type_context(approved_goods_type_on_country_decisions): - # Approved goods types on country - if approved_goods_type_on_country_decisions: - context = {} - for decision in approved_goods_type_on_country_decisions: - if decision.country.name not in context: - context[decision.country.name] = [GoodsTypeSerializer(decision.goods_type).data] - else: - context[decision.country.name].append(GoodsTypeSerializer(decision.goods_type).data) - return context - - -def _get_entities_refused_at_the_final_advice_level(case_pk): - # Get Refused Final advice on Country & GoodsType - rejected_entities = Advice.objects.filter( - Q(goods_type__isnull=False) | Q(country__isnull=False), - case_id=case_pk, - level=AdviceLevel.FINAL, - type=AdviceType.REFUSE, - ).prefetch_related("goods_type", "goods_type__control_list_entries", "country") - - refused_final_advice_countries = [] - refused_final_advice_goods_types = [] - for rejected_entity in rejected_entities: - if rejected_entity.goods_type: - refused_final_advice_goods_types.append(rejected_entity.goods_type) - else: - refused_final_advice_countries.append(rejected_entity.country) - - return refused_final_advice_countries, refused_final_advice_goods_types - - -def _get_refused_goods_type_context(case_pk, goods_types, refused_goods_type_on_country_decisions): - # Refused goods types on country from GoodCountryDecisions - context = {} - if refused_goods_type_on_country_decisions: - for decision in refused_goods_type_on_country_decisions: - if decision.country.name not in context: - context[decision.country.name] = {decision.goods_type.id: GoodsTypeSerializer(decision.goods_type).data} - else: - context[decision.country.name][decision.goods_type.id] = GoodsTypeSerializer(decision.goods_type).data - - refused_final_advice_countries, refused_final_advice_goods_types = _get_entities_refused_at_the_final_advice_level( - case_pk - ) - - # Countries refused for all goods types at final advice level - if refused_final_advice_countries: - for country in refused_final_advice_countries: - goods_type_for_country = goods_types.filter(countries=country) - for goods_type in goods_type_for_country: - if country.name not in context: - context[country.name] = {goods_type.id: GoodsTypeSerializer(goods_type).data} - elif goods_type.id not in context[country.name]: - context[country.name][goods_type.id] = GoodsTypeSerializer(goods_type).data - - # Goods types refused for all countries at final advice level - if refused_final_advice_goods_types: - for goods_type in refused_final_advice_goods_types: - for country in goods_type.countries.all(): - if country.name not in context: - context[country.name] = {goods_type.id: GoodsTypeSerializer(goods_type).data} - elif goods_type.id not in context[country.name]: - context[country.name][goods_type.id] = GoodsTypeSerializer(goods_type).data - - # Remove ID's used to avoid duplication - return {key: list(context[key].values()) for key in context} if context else None - - -def _get_goods_type_context(goods_types, case_pk): - goods_type_context = {"all": GoodsTypeSerializer(goods_types, many=True).data} - - # Get GoodCountryDecisions - goods_type_on_country_decisions = GoodCountryDecision.objects.filter(case_id=case_pk).prefetch_related( - "goods_type", "goods_type__control_list_entries", "country" - ) - approved_goods_type_on_country_decisions = [] - refused_goods_type_on_country_decisions = [] - for goods_type_on_country_decision in goods_type_on_country_decisions: - if goods_type_on_country_decision.approve: - approved_goods_type_on_country_decisions.append(goods_type_on_country_decision) - else: - refused_goods_type_on_country_decisions.append(goods_type_on_country_decision) - - approved_goods_type_context = _get_approved_goods_type_context(approved_goods_type_on_country_decisions) - if approved_goods_type_context: - goods_type_context[AdviceType.APPROVE] = approved_goods_type_context - - refused_goods_type_context = _get_refused_goods_type_context( - case_pk, goods_types, refused_goods_type_on_country_decisions - ) - if refused_goods_type_context: - goods_type_context[AdviceType.REFUSE] = refused_goods_type_context - - return goods_type_context diff --git a/api/letter_templates/templates/letter_templates/application_form.html b/api/letter_templates/templates/letter_templates/application_form.html index e5994b3fa7..72d49912d9 100644 --- a/api/letter_templates/templates/letter_templates/application_form.html +++ b/api/letter_templates/templates/letter_templates/application_form.html @@ -25,8 +25,6 @@ {% if case_type.reference == "siel" %} Standard Individual Export Licence - {% elif case_type.reference == "oiel"%} - Open Individual Export Licence {% elif case_type.reference == "sitl"%} Standard Individual Transhipment Licence {% elif case_type.reference == "sicl"%} @@ -50,8 +48,6 @@ {% if case_type.reference in "siel,sitl,sicl" %} Standard Licence - {% elif case_type.reference == "oiel" or case_type.reference == "oicl"%} - Open Licence {% elif case_type.reference == "gift"%} MOD Gifting Clearance {% elif case_type.sub_type == "f680_clearance"%} @@ -63,16 +59,6 @@ {% endif %} - {% if details.goodstype_category %} - - - Type of open licence - - - {{ details.goodstype_category }} - - - {% endif %} Reference name @@ -228,280 +214,257 @@

Products

{% for good_on_application in goods.all %}

{% lcs 'GoodsDetailSummary.HEADING' %} {{ forloop.counter }}

-{# goods without an item_category are on open applications, and therefore goodstypes, which allows us to split that logic out #} - {% if not good_on_application.item_category%} +
+
+ {% lcs 'GoodsDetailSummary.SELECT_CATEGORY' %} +
+
+ {{ good_on_application.item_category }} +
+
+
+
+ {% lcs "GoodPage.Table.DESCRIPTION" %} +
+
+ {{ good_on_application.description }} +
+
+ +
+
+ {% lcs 'GoodsDetailSummary.PART_NUMBER' %} +
+
+ {{ good_on_application.part_number|default_na }} +
+
+ +
+
+ {% lcs 'GoodsDetailSummary.CONTROLLED' %} +
+
+ {{ good_on_application.is_controlled }} - {{ good_on_application.control_list_entries|join:", "|default_na }} +
+
+ + {% if good_on_application.software_or_technology_details %}
- {% lcs "GoodPage.Table.DESCRIPTION" %} + {% lcs "GoodsDetailSummary.PURPOSE_SOFTWARE_TECHNOLOGY" %}
- {% if good_on_application.good.name %}{{ good_on_application.good.name }}{% endif %} - {% if good_on_application.good.description %}{{ good_on_application.good.description }}{% endif %} + {{ good_on_application.software_or_technology_details|default_na }}
+ {% endif %} + + {% if good_on_application.is_military_use %}
- {% lcs 'GoodsDetailSummary.CONTROLLED' %} + {% lcs 'GoodsDetailSummary.MILITARY' %}
- {{ good_on_application.good.is_controlled }} - {{ good_on_application.good.control_list_entries|join:", "|default_na }} + {{ good_on_application.is_military_use }} + {% if good_on_application.modified_military_use_details %} +
+ {{ good_on_application.modified_military_use_details }} + {% endif %}
+ {% endif %} - {% else %} + {% if good_on_application.is_component %}
- {% lcs 'GoodsDetailSummary.SELECT_CATEGORY' %} + {% lcs 'GoodsDetailSummary.COMPONENT' %}
- {{ good_on_application.item_category }} + {{ good_on_application.is_component|default_na }} + {% if good_on_application.component_details %} +
+ {{ good_on_application.component_details }} + {% endif %}
+ {% endif %} + + {% if good_on_application.uses_information_security %}
- {% lcs "GoodPage.Table.DESCRIPTION" %} + {% lcs 'GoodsDetailSummary.DESIGNED_FOR_INFORMATION_SECURITY' %}
- {{ good_on_application.description }} + {{ good_on_application.uses_information_security }} + {% if good_on_application.information_security_details %} +
+ {{ good_on_application.information_security_details }} + {% endif %}
+ {% endif %} + {% if good_on_application.firearm_type %}
- {% lcs 'GoodsDetailSummary.PART_NUMBER' %} + {% lcs "GoodsDetailSummary.FirearmDetails.PRODUCT_TYPE" %}
- {{ good_on_application.part_number|default_na }} + {{ good_on_application.firearm_type|default_na }}
- {% lcs 'GoodsDetailSummary.CONTROLLED' %} + {% lcs "GoodsDetailSummary.FirearmDetails.YEAR_OF_MANUFACTURE" %}
- {{ good_on_application.is_controlled }} - {{ good_on_application.control_list_entries|join:", "|default_na }} + {{ good_on_application.year_of_manufacture|default_na }}
- {% if good_on_application.software_or_technology_details %} -
-
- {% lcs "GoodsDetailSummary.PURPOSE_SOFTWARE_TECHNOLOGY" %} -
-
- {{ good_on_application.software_or_technology_details|default_na }} -
-
- {% endif %} - - {% if good_on_application.is_military_use %} -
-
- {% lcs 'GoodsDetailSummary.MILITARY' %} -
-
- {{ good_on_application.is_military_use }} - {% if good_on_application.modified_military_use_details %} -
- {{ good_on_application.modified_military_use_details }} - {% endif %} -
-
- {% endif %} +
+
+ {% lcs "GoodsDetailSummary.FirearmDetails.CALIBRE" %} +
+
+ {{ good_on_application.calibre|default_na }} +
+
- {% if good_on_application.is_component %} + {% if good_on_application.is_covered_by_firearm_act_section_one_two_or_five %}
- {% lcs 'GoodsDetailSummary.COMPONENT' %} + {% lcs "GoodsDetailSummary.FirearmDetails.COVERED_BY_THE_FIREARMS_ACT_1968" %}
- {{ good_on_application.is_component|default_na }} - {% if good_on_application.component_details %} -
- {{ good_on_application.component_details }} + {% if good_on_application.is_covered_by_firearm_act_section_one_two_or_five %} + {{ good_on_application.is_covered_by_firearm_act_section_one_two_or_five}} + {% if good_on_application.is_covered_by_firearm_act_section_one_two_or_five %} + - certificate number {{ good_on_application.section_certificate_number|default_na }} expires on + {{ good_on_application.section_certificate_date_of_expiry }} + {% endif %} {% endif %}
{% endif %} - {% if good_on_application.uses_information_security %} + {% if good_on_application.has_serial_numbers %}
- {% lcs 'GoodsDetailSummary.DESIGNED_FOR_INFORMATION_SECURITY' %} + {% lcs "GoodsDetailSummary.FirearmDetails.IDENTIFICATION_MARKINGS" %}
- {{ good_on_application.uses_information_security }} - {% if good_on_application.information_security_details %} -
- {{ good_on_application.information_security_details }} - {% endif %} + {% if good_on_application.serial_numbers_available == "AVAILABLE" %} + Yes, I can add serial numbers now + {% elif good_on_application.serial_numbers_available == "LATER" %} + Yes, I can add serial numbers later + {% elif good_on_application.serial_numbers_available == "NOT_AVAILABLE" %} + No + {% endif %} + {% if not good_on_application.has_serial_numbers %} + +
+ {{ good_on_application.no_identification_markings_details|default_na }} +
+ {% endif %}
{% endif %} + {% endif %} - {% if good_on_application.firearm_type %} -
-
- {% lcs "GoodsDetailSummary.FirearmDetails.PRODUCT_TYPE" %} -
-
- {{ good_on_application.firearm_type|default_na }} -
-
- -
-
- {% lcs "GoodsDetailSummary.FirearmDetails.YEAR_OF_MANUFACTURE" %} -
-
- {{ good_on_application.year_of_manufacture|default_na }} -
-
- -
-
- {% lcs "GoodsDetailSummary.FirearmDetails.CALIBRE" %} -
-
- {{ good_on_application.calibre|default_na }} -
-
- - {% if good_on_application.is_covered_by_firearm_act_section_one_two_or_five %} -
-
- {% lcs "GoodsDetailSummary.FirearmDetails.COVERED_BY_THE_FIREARMS_ACT_1968" %} -
-
- {% if good_on_application.is_covered_by_firearm_act_section_one_two_or_five %} - {{ good_on_application.is_covered_by_firearm_act_section_one_two_or_five}} - {% if good_on_application.is_covered_by_firearm_act_section_one_two_or_five %} - - certificate number {{ good_on_application.section_certificate_number|default_na }} expires on - {{ good_on_application.section_certificate_date_of_expiry }} - {% endif %} - {% endif %} -
-
- {% endif %} - - {% if good_on_application.has_serial_numbers %} -
-
- {% lcs "GoodsDetailSummary.FirearmDetails.IDENTIFICATION_MARKINGS" %} -
-
- {% if good_on_application.serial_numbers_available == "AVAILABLE" %} - Yes, I can add serial numbers now - {% elif good_on_application.serial_numbers_available == "LATER" %} - Yes, I can add serial numbers later - {% elif good_on_application.serial_numbers_available == "NOT_AVAILABLE" %} - No - {% endif %} - {% if not good_on_application.has_serial_numbers %} - -
- {{ good_on_application.no_identification_markings_details|default_na }} -
- {% endif %} -
-
- {% endif %} - {% endif %} +
+
+ {% lcs 'CreateGoodForm.IsGraded.TITLE' %} +
+
+ {{ good_on_application.is_pv_graded }} +
+
+ {% if good_on_application.pv_grading.grading %}
- {% lcs 'CreateGoodForm.IsGraded.TITLE' %} + {% lcs 'GoodGradingForm.PREFIX' %}
- {{ good_on_application.is_pv_graded }} + {{ good_on_application.pv_grading.prefix|default_na }}
- {% if good_on_application.pv_grading.grading %} -
-
- {% lcs 'GoodGradingForm.PREFIX' %} -
-
- {{ good_on_application.pv_grading.prefix|default_na }} -
-
- -
-
- {% lcs 'GoodGradingForm.GRADING' %} -
-
- {{ good_on_application.pv_grading.grading }} -
-
- -
-
- {% lcs 'GoodGradingForm.SUFFIX' %} -
-
- {{ good_on_application.pv_grading.suffix|default_na }} -
-
- -
-
- {% lcs 'GoodGradingForm.ISSUING_AUTHORITY' %} -
-
- {{ good_on_application.pv_grading.issuing_authority }} -
-
- -
-
- {% lcs 'GoodGradingForm.REFERENCE' %} -
-
- {{ good_on_application.pv_grading.reference }} -
-
- -
-
- {% lcs 'GoodGradingForm.DATE_OF_ISSUE' %} -
-
- {{ good_on_application.pv_grading.date_of_issue }} -
-
- {% endif %} +
+
+ {% lcs 'GoodGradingForm.GRADING' %} +
+
+ {{ good_on_application.pv_grading.grading }} +
+
- {% lcs 'GoodsDetailSummary.INCORPORATED' %} + {% lcs 'GoodGradingForm.SUFFIX' %}
- {{ good_on_application.is_incorporated|default_na }} + {{ good_on_application.pv_grading.suffix|default_na }}
- {% lcs 'AddGoodToApplicationForm.Quantity.TITLE' %} + {% lcs 'GoodGradingForm.ISSUING_AUTHORITY' %}
- {{ good_on_application.applied_for_quantity }} + {{ good_on_application.pv_grading.issuing_authority }}
- {% lcs 'AddGoodToApplicationForm.Value.TITLE' %} + {% lcs 'GoodGradingForm.REFERENCE' %}
- {{ good_on_application.applied_for_value|default_na }} + {{ good_on_application.pv_grading.reference }}
+
+
+ {% lcs 'GoodGradingForm.DATE_OF_ISSUE' %} +
+
+ {{ good_on_application.pv_grading.date_of_issue }} +
+
{% endif %} + +
+
+ {% lcs 'GoodsDetailSummary.INCORPORATED' %} +
+
+ {{ good_on_application.is_incorporated|default_na }} +
+
+ +
+
+ {% lcs 'AddGoodToApplicationForm.Quantity.TITLE' %} +
+
+ {{ good_on_application.applied_for_quantity }} +
+
+ +
+
+ {% lcs 'AddGoodToApplicationForm.Value.TITLE' %} +
+
+ {{ good_on_application.applied_for_value|default_na }} +
+
{% endfor %} {% endif %} diff --git a/api/letter_templates/templates/letter_templates/case_context_test.html b/api/letter_templates/templates/letter_templates/case_context_test.html index ea5ccb82e8..c62e6ba2f8 100644 --- a/api/letter_templates/templates/letter_templates/case_context_test.html +++ b/api/letter_templates/templates/letter_templates/case_context_test.html @@ -107,16 +107,6 @@

Goods

- {% if goods_type %} -

Goods types

-
    -
  • goods_type: -
      - {{ goods_type|context_data_to_list|safe }} -
    -
  • -
- {% endif %} {% if ecju_queries %}

ECJU queries

    diff --git a/api/letter_templates/tests/test_context_generation.py b/api/letter_templates/tests/test_context_generation.py index 16f8b26be1..ce4d9ac55f 100644 --- a/api/letter_templates/tests/test_context_generation.py +++ b/api/letter_templates/tests/test_context_generation.py @@ -209,11 +209,6 @@ def _assert_good_with_advice(self, context, advice, good_on_application): self.assertEqual(goods[0]["proviso_reason"], advice.proviso) self.assertEqual(len(goods[0]["denial_reasons"]), advice.denial_reasons.count()) - def _assert_goods_type(self, context, goods_type): - self.assertEqual(goods_type.description, context["description"]) - self.assertEqual([clc.rating for clc in goods_type.control_list_entries.all()], context["control_list_entries"]) - self.assertEqual(friendly_boolean(goods_type.is_good_controlled), context["is_controlled"]) - def _assert_ecju_query(self, context, ecju_query): self.assertEqual(context["question"]["text"], ecju_query.question) self.assertEqual( diff --git a/api/licences/helpers.py b/api/licences/helpers.py index c3208bcc2b..991976a5e7 100644 --- a/api/licences/helpers.py +++ b/api/licences/helpers.py @@ -5,23 +5,11 @@ from api.applications.models import GoodOnApplication from api.applications.serializers.good import GoodOnApplicationViewSerializer from api.cases.enums import CaseTypeSubTypeEnum -from api.cases.models import GoodCountryDecision from api.licences.models import Licence from lite_content.lite_api import strings -from api.staticdata.countries.models import Country from api.staticdata.statuses.enums import CaseStatusEnum -def get_approved_goods_types(application): - approved_goods = GoodCountryDecision.objects.filter(case_id=application.id).values_list("goods_type", flat=True) - return application.goods_type.filter(id__in=approved_goods) - - -def get_approved_countries(application): - approved_countries = GoodCountryDecision.objects.filter(case_id=application.id).values_list("country", flat=True) - return Country.objects.filter(id__in=approved_countries).order_by("name") - - @transaction.atomic def get_licence_reference_code(application_reference): # Needs to lock so that 2 Licences don't get the same reference code @@ -36,15 +24,11 @@ def get_licence_reference_code(application_reference): def serialize_goods_on_licence(licence): - from api.licences.serializers.view_licence import GoodOnLicenceViewSerializer, GoodsTypeOnLicenceListSerializer + from api.licences.serializers.view_licence import GoodOnLicenceViewSerializer if licence.goods.exists(): # Standard Application return GoodOnLicenceViewSerializer(licence.goods, many=True).data - elif licence.case.baseapplication.goods_type.exists(): - # Open Application - approved_goods_types = get_approved_goods_types(licence.case.baseapplication) - return GoodsTypeOnLicenceListSerializer(approved_goods_types, many=True).data elif licence.case.case_type.sub_type != CaseTypeSubTypeEnum.STANDARD: # MOD clearances goods = GoodOnApplication.objects.filter(application=licence.case.baseapplication) diff --git a/api/licences/serializers/hmrc_integration.py b/api/licences/serializers/hmrc_integration.py index ec4037376b..d689ff7c1b 100644 --- a/api/licences/serializers/hmrc_integration.py +++ b/api/licences/serializers/hmrc_integration.py @@ -1,7 +1,6 @@ from rest_framework import serializers from api.licences.enums import LicenceStatus, licence_status_to_hmrc_integration_action, HMRCIntegrationActionEnum -from api.licences.helpers import get_approved_goods_types, get_approved_countries from api.licences.models import Licence @@ -66,12 +65,6 @@ class HMRCIntegrationGoodOnLicenceSerializer(serializers.Serializer): value = serializers.FloatField() -class HMRCIntegrationGoodsTypeSerializer(serializers.Serializer): - id = serializers.UUIDField() - description = serializers.CharField() - usage = serializers.IntegerField() - - class HMRCIntegrationLicenceSerializer(serializers.Serializer): id = serializers.UUIDField() reference = serializers.CharField(source="reference_code") @@ -124,9 +117,6 @@ def get_goods(self, instance): We also list the products in the same order in licence pdf which is used by exporters when declaring goods at the customs check""" return HMRCIntegrationGoodOnLicenceSerializer(instance.goods.order_by("created_at"), many=True).data - elif hasattr(instance.case, "baseapplication") and instance.case.baseapplication.goods_type.exists(): - approved_goods_types = get_approved_goods_types(instance.case.baseapplication) - return HMRCIntegrationGoodsTypeSerializer(approved_goods_types, many=True).data else: return [] diff --git a/api/licences/serializers/view_licence.py b/api/licences/serializers/view_licence.py index 4bd00b5073..681392ea70 100644 --- a/api/licences/serializers/view_licence.py +++ b/api/licences/serializers/view_licence.py @@ -12,9 +12,8 @@ from api.core.serializers import KeyValueChoiceField, CountrySerializerField, ControlListEntryField from api.goods.models import Good from api.goods.enums import GoodControlled -from api.goodstype.models import GoodsType from api.licences.enums import LicenceStatus -from api.licences.helpers import serialize_goods_on_licence, get_approved_countries +from api.licences.helpers import serialize_goods_on_licence from api.licences.models import ( GoodOnLicence, Licence, @@ -56,19 +55,6 @@ class Meta: read_only_fields = fields -class GoodsTypeOnLicenceSerializer(serializers.ModelSerializer): - control_list_entries = ControlListEntrySerializer(many=True) - - class Meta: - model = GoodsType - fields = ( - "description", - "control_list_entries", - "usage", - ) - read_only_fields = fields - - class CaseSubTypeSerializer(serializers.ModelSerializer): sub_type = KeyValueChoiceField(choices=CaseTypeSubTypeEnum.choices) @@ -310,20 +296,6 @@ class Meta: read_only_fields = fields -class GoodsTypeOnLicenceListSerializer(serializers.ModelSerializer): - control_list_entries = ControlListEntryField(many=True) - - class Meta: - model = GoodsType - fields = ( - "id", - "description", - "control_list_entries", - "usage", - ) - read_only_fields = fields - - class GoodOnLicenceListSerializer(serializers.ModelSerializer): good = GoodLicenceListSerializer(read_only=True) diff --git a/api/licences/tests/test_api_to_hmrc_integration.py b/api/licences/tests/test_api_to_hmrc_integration.py index c47e292074..186d6bee2f 100644 --- a/api/licences/tests/test_api_to_hmrc_integration.py +++ b/api/licences/tests/test_api_to_hmrc_integration.py @@ -280,12 +280,6 @@ def _assert_end_user(self, data, end_user): def _assert_countries(self, data, countries): self.assertEqual(data["countries"], [{"id": country.id, "name": country.name} for country in countries]) - def _assert_goods_types(self, data, goods): - self.assertEqual( - data["goods"], - [{"id": str(good.id), "description": good.description, "usage": good.usage} for good in goods], - ) - def _assert_goods_on_licence(self, data, goods): data = data["goods"] for i in range(len(goods)): diff --git a/api/licences/views/main.py b/api/licences/views/main.py index afc4956009..b030a90c2d 100644 --- a/api/licences/views/main.py +++ b/api/licences/views/main.py @@ -62,7 +62,6 @@ def get_queryset(self): if clc: licences = licences.filter( Q(case__baseapplication__goods__good__control_list_entries__rating=clc) - | Q(case__baseapplication__goods_type__control_list_entries__rating=clc) ).distinct() if end_user: diff --git a/docs/index.html b/docs/index.html index ceb60ce8b9..5532858b65 100644 --- a/docs/index.html +++ b/docs/index.html @@ -225,14 +225,6 @@

    Applications

  • delete /applications/good-on-application/{obj_pk}/
  • post /applications/{id}/goods/
  • get /applications/{id}/goods/
  • -
  • put /applications/{id}/goodstype/{goodstype_pk}/assign-countries/
  • -
  • delete /applications/{id}/goodstype/{goodstype_pk}/
  • -
  • post /applications/{id}/goodstype/{goods_type_pk}/document/
  • -
  • delete /applications/{id}/goodstype/{goods_type_pk}/document/
  • -
  • get /applications/{id}/goodstype/{goods_type_pk}/document/
  • -
  • get /applications/{id}/goodstype/{goodstype_pk}/
  • -
  • post /applications/{id}/goodstypes/
  • -
  • get /applications/{id}/goodstypes/
  • get /applications/
  • patch /applications/{id}/
  • get /applications/{id}/
  • @@ -310,11 +302,6 @@

    Goods

  • get /goods/{id}/
  • put /goods/{id}/
-

Goodstype

-

GovUsers

  • post /gov-users/authenticate/
  • @@ -1617,381 +1604,6 @@

    Consumes

    - - - -

    Produces

    - This API call produces the following media types according to the Accept request header; - the media type will be conveyed by the Content-Type response header. -
      -
    • application/json
    • -
    - -

    Responses

    -

    200

    - - - -
    -
    -
    - Up -
    put /applications/{id}/goodstype/{goodstype_pk}/assign-countries/
    -
    (applicationsGoodstypeAssignCountriesUpdate)
    -
    Sets countries on goodstype
    - -

    Path parameters

    -
    -
    goodstype_pk (required)
    - -
    Path Parameter
    id (required)
    - -
    Path Parameter
    -
    - -

    Consumes

    - This API call consumes the following media types via the Content-Type request header: -
      -
    • application/json
    • -
    - - - - - - - - - -

    Produces

    - This API call produces the following media types according to the Accept request header; - the media type will be conveyed by the Content-Type response header. -
      -
    • application/json
    • -
    - -

    Responses

    -

    200

    - - -
    -
    -
    -
    - Up -
    delete /applications/{id}/goodstype/{goodstype_pk}/
    -
    (applicationsGoodstypeDelete)
    -
    Deletes a goodstype
    - -

    Path parameters

    -
    -
    goodstype_pk (required)
    - -
    Path Parameter
    id (required)
    - -
    Path Parameter
    -
    - -

    Consumes

    - This API call consumes the following media types via the Content-Type request header: -
      -
    • application/json
    • -
    - - - - - - - - - -

    Produces

    - This API call produces the following media types according to the Accept request header; - the media type will be conveyed by the Content-Type response header. -
      -
    • application/json
    • -
    - -

    Responses

    -

    204

    - - -
    -
    -
    -
    - Up -
    post /applications/{id}/goodstype/{goods_type_pk}/document/
    -
    (applicationsGoodstypeDocumentCreate)
    -
    Retrieve, add or delete a third party document from an application
    - -

    Path parameters

    -
    -
    goods_type_pk (required)
    - -
    Path Parameter
    id (required)
    - -
    Path Parameter
    -
    - -

    Consumes

    - This API call consumes the following media types via the Content-Type request header: -
      -
    • application/json
    • -
    - -

    Request body

    -
    -
    data GoodsTypeDocument (required)
    - -
    Body Parameter
    - -
    - - - - -

    Return type

    - - - - -

    Example data

    -
    Content-Type: application/json
    -
    {
    -  "size" : -1803530559,
    -  "name" : "name",
    -  "s3_key" : "s3_key",
    -  "safe" : true,
    -  "id" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91",
    -  "goods_type" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91"
    -}
    - -

    Produces

    - This API call produces the following media types according to the Accept request header; - the media type will be conveyed by the Content-Type response header. -
      -
    • application/json
    • -
    - -

    Responses

    -

    201

    - - GoodsTypeDocument -

    400

    - JSON parse error - -
    -
    -
    -
    - Up -
    delete /applications/{id}/goodstype/{goods_type_pk}/document/
    -
    (applicationsGoodstypeDocumentDelete)
    -
    Retrieve, add or delete a third party document from an application
    - -

    Path parameters

    -
    -
    goods_type_pk (required)
    - -
    Path Parameter
    id (required)
    - -
    Path Parameter
    -
    - -

    Consumes

    - This API call consumes the following media types via the Content-Type request header: -
      -
    • application/json
    • -
    - -

    Request body

    -
    -
    data GoodsTypeDocument (required)
    - -
    Body Parameter
    - -
    - - - - - - - - -

    Produces

    - This API call produces the following media types according to the Accept request header; - the media type will be conveyed by the Content-Type response header. -
      -
    • application/json
    • -
    - -

    Responses

    -

    204

    - - -

    400

    - JSON parse error - -
    -
    -
    -
    - Up -
    get /applications/{id}/goodstype/{goods_type_pk}/document/
    -
    (applicationsGoodstypeDocumentList)
    -
    Retrieve, add or delete a third party document from an application
    - -

    Path parameters

    -
    -
    goods_type_pk (required)
    - -
    Path Parameter
    id (required)
    - -
    Path Parameter
    -
    - -

    Consumes

    - This API call consumes the following media types via the Content-Type request header: -
      -
    • application/json
    • -
    - - - - - - - - - -

    Produces

    - This API call produces the following media types according to the Accept request header; - the media type will be conveyed by the Content-Type response header. -
      -
    • application/json
    • -
    - -

    Responses

    -

    200

    - - -
    -
    -
    -
    - Up -
    get /applications/{id}/goodstype/{goodstype_pk}/
    -
    (applicationsGoodstypeRead)
    -
    Gets a goodstype
    - -

    Path parameters

    -
    -
    goodstype_pk (required)
    - -
    Path Parameter
    id (required)
    - -
    Path Parameter
    -
    - -

    Consumes

    - This API call consumes the following media types via the Content-Type request header: -
      -
    • application/json
    • -
    - - - - - - - - - -

    Produces

    - This API call produces the following media types according to the Accept request header; - the media type will be conveyed by the Content-Type response header. -
      -
    • application/json
    • -
    - -

    Responses

    -

    200

    - - -
    -
    -
    -
    - Up -
    post /applications/{id}/goodstypes/
    -
    (applicationsGoodstypesCreate)
    -
    Post a goodstype
    - -

    Path parameters

    -
    -
    id (required)
    - -
    Path Parameter
    -
    - -

    Consumes

    - This API call consumes the following media types via the Content-Type request header: -
      -
    • application/json
    • -
    - - - - - - - - - -

    Produces

    - This API call produces the following media types according to the Accept request header; - the media type will be conveyed by the Content-Type response header. -
      -
    • application/json
    • -
    - -

    Responses

    -

    201

    - - -
    -
    -
    -
    - Up -
    get /applications/{id}/goodstypes/
    -
    (applicationsGoodstypesList)
    -
    Goodstypes belonging to an open application.
    - -

    Path parameters

    -
    -
    id (required)
    - -
    Path Parameter
    -
    - -

    Consumes

    - This API call consumes the following media types via the Content-Type request header: -
      -
    • application/json
    • -
    - - - - - - @@ -3469,7 +3081,6 @@

    Example data

    "end_user" : "end_user", "id" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91", "text" : "text", - "goods_type" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91", "third_party" : "third_party", "user" : "user", "case" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91", @@ -3926,7 +3537,6 @@

    Example data

    "end_user" : "end_user", "id" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91", "text" : "text", - "goods_type" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91", "third_party" : "third_party", "user" : "user", "case" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91", @@ -4127,7 +3737,6 @@

    Example data

    "denial_reasons" : [ "denial_reasons", "denial_reasons" ], "end_user" : "end_user", "text" : "text", - "goods_type" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91", "third_party" : "third_party", "user" : "user", "case" : "046b6c7f-0b8a-43b9-b35d-6489e6daee91", @@ -4918,83 +4527,6 @@

    Consumes

    - - - -

    Produces

    - This API call produces the following media types according to the Accept request header; - the media type will be conveyed by the Content-Type response header. -
      -
    • application/json
    • -
    - -

    Responses

    -

    200

    - - -
    -
    -

    Goodstype

    -
    -
    - Up -
    get /goodstype/
    -
    (goodstypeList)
    -
    Gets list of all Goods Types
    - - -

    Consumes

    - This API call consumes the following media types via the Content-Type request header: -
      -
    • application/json
    • -
    - - - - - - - - - -

    Produces

    - This API call produces the following media types according to the Accept request header; - the media type will be conveyed by the Content-Type response header. -
      -
    • application/json
    • -
    - -

    Responses

    -

    200

    - - -
    -
    -
    -
    - Up -
    get /goodstype/{id}/
    -
    (goodstypeRead)
    -
    Gets a single Goods Type
    - -

    Path parameters

    -
    -
    id (required)
    - -
    Path Parameter
    -
    - -

    Consumes

    - This API call consumes the following media types via the Content-Type request header: -
      -
    • application/json
    • -
    - - - - - - @@ -8522,7 +8054,6 @@

    Table of Contents

  • GeneratedCaseDocumentGov -
  • GenericApplicationList -
  • GoodDocumentCreate -
  • -
  • GoodsTypeDocument -
  • GovUserCreate -
  • GovUserSimple -
  • LetterLayout -
  • @@ -8594,7 +8125,6 @@

    CaseAdvice - proviso (optional)

    denial_reasons (optional)
    good (optional)
    UUID format: uuid
    -
    goods_type (optional)
    UUID format: uuid
    country (optional)
    end_user (optional)
    ultimate_end_user (optional)
    @@ -8636,7 +8166,6 @@

    CaseFinalAdvice - type
    denial_reasons (optional)
    good (optional)
    UUID format: uuid
    -
    goods_type (optional)
    UUID format: uuid
    country (optional)
    end_user (optional)
    ultimate_end_user (optional)
    @@ -8658,7 +8187,6 @@

    CaseTeamAdvice - type
    denial_reasons (optional)
    good (optional)
    UUID format: uuid
    -
    goods_type (optional)
    UUID format: uuid
    country (optional)
    end_user (optional)
    ultimate_end_user (optional)
    @@ -8715,15 +8243,9 @@

    GoodDocumentCreate - description (optional) -
    -

    GoodsTypeDocument - Up

    -
    -
    -
    id (optional)
    UUID format: uuid
    name
    s3_key (optional)
    size (optional)
    -
    goods_type
    UUID format: uuid
    safe (optional)
    diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 08d1cfb74e..edec4f4b89 100755 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -383,175 +383,6 @@ paths: description: '' tags: - applications - /applications/{id}/goodstypes/: - get: - operationId: listApplicationGoodsTypes - description: Goodstypes belonging to an open application. - parameters: - - name: id - in: path - required: true - description: '' - schema: - type: string - responses: - '200': - content: - application/json: - schema: - type: array - items: {} - description: '' - tags: - - applications - post: - operationId: createApplicationGoodsTypes - description: Post a goodstype - parameters: - - name: id - in: path - required: true - description: '' - schema: - type: string - requestBody: - content: - application/json: - schema: {} - application/x-www-form-urlencoded: - schema: {} - responses: - '201': - content: - application/json: - schema: {} - description: '' - tags: - - applications - /applications/{id}/goodstype/{goodstype_pk}/: - get: - operationId: retrieveApplicationGoodsType - description: Gets a goodstype - parameters: - - name: id - in: path - required: true - description: '' - schema: - type: string - - name: goodstype_pk - in: path - required: true - description: '' - schema: - type: string - responses: - '200': - content: - application/json: - schema: {} - description: '' - tags: - - applications - delete: - operationId: destroyApplicationGoodsType - description: Deletes a goodstype - parameters: - - name: id - in: path - required: true - description: '' - schema: - type: string - - name: goodstype_pk - in: path - required: true - description: '' - schema: - type: string - responses: - '204': - description: '' - tags: - - applications - /applications/{id}/goodstype/{goods_type_pk}/document/: - get: - operationId: listGoodsTypeDocuments - description: Retrieve, add or delete a third party document from an application - parameters: - - name: id - in: path - required: true - description: '' - schema: - type: string - - name: goods_type_pk - in: path - required: true - description: '' - schema: - type: string - responses: - '200': - content: - application/json: - schema: - type: array - items: {} - description: '' - tags: - - applications - post: - operationId: createGoodsTypeDocument - description: Retrieve, add or delete a third party document from an application - parameters: - - name: id - in: path - required: true - description: '' - schema: - type: string - - name: goods_type_pk - in: path - required: true - description: '' - schema: - type: string - requestBody: - content: - application/json: - schema: {} - application/x-www-form-urlencoded: - schema: {} - responses: - '201': - content: - application/json: - schema: {} - description: '' - tags: - - applications - delete: - operationId: destroyGoodsTypeDocument - description: Retrieve, add or delete a third party document from an application - parameters: - - name: id - in: path - required: true - description: '' - schema: - type: string - - name: goods_type_pk - in: path - required: true - description: '' - schema: - type: string - responses: - '204': - description: '' - tags: - - applications /applications/{id}/parties/: get: operationId: listApplicationPartys @@ -3118,26 +2949,6 @@ paths: description: '' tags: - goods - /goods-types/{id}/: - get: - operationId: retrieveGoodsType - description: '' - parameters: - - name: id - in: path - required: true - description: A UUID string identifying this goods type. - schema: - type: string - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/GoodsTypeView' - description: '' - tags: - - goods-types /letter-templates/: get: operationId: listLetterTemplates @@ -6906,31 +6717,6 @@ paths: description: '' tags: - applications - /applications/{id}/goodstype/assign-countries/: - put: - operationId: updateApplicationGoodsTypeCountries - description: Sets countries on goodstype - parameters: - - name: id - in: path - required: true - description: '' - schema: - type: string - requestBody: - content: - application/json: - schema: {} - application/x-www-form-urlencoded: - schema: {} - responses: - '200': - content: - application/json: - schema: {} - description: '' - tags: - - applications /applications/{id}/contract-types/: put: operationId: updateApplicationContractTypes @@ -7900,56 +7686,6 @@ components: - control_list_entries - part_number - status - GoodsTypeView: - type: object - properties: - id: - type: string - format: uuid - readOnly: true - description: - type: string - readOnly: true - is_good_controlled: - enum: - - true - - false - - null - is_good_incorporated: - type: boolean - readOnly: true - control_list_entries: - type: array - items: - type: object - properties: - rating: - type: string - readOnly: true - text: - type: string - readOnly: true - readOnly: true - countries: - type: string - readOnly: true - document: - type: string - readOnly: true - flags: - type: string - readOnly: true - comment: - type: string - report_summary: - type: string - end_use_control: - type: array - items: - type: string - required: - - is_good_controlled - - comment LetterTemplate: type: object properties: diff --git a/test_helpers/clients.py b/test_helpers/clients.py index 9645dc72fc..31b0a6859d 100644 --- a/test_helpers/clients.py +++ b/test_helpers/clients.py @@ -54,8 +54,6 @@ from api.goods.models import Good, GoodDocument from api.applications.models import GoodOnApplicationInternalDocument from api.goods.tests.factories import GoodFactory -from api.goodstype.document.models import GoodsTypeDocument -from api.goodstype.models import GoodsType from api.letter_templates.models import LetterTemplate from api.licences.enums import LicenceStatus from api.licences.tests.factories import StandardLicenceFactory @@ -421,14 +419,6 @@ def create_document_for_party(party: Party, name="document_name.pdf", safe=True) document.save() return document - @staticmethod - def create_document_for_goods_type(goods_type: GoodsType, name="document_name.pdf", safe=True): - document = GoodsTypeDocument( - goods_type=goods_type, name=name, s3_key="s3_keykey.pdf", size=123456, virus_scanned_at=None, safe=safe - ) - document.save() - return document - @staticmethod def create_flag(name: str, level: str, team: Team): return FlagFactory(name=name, level=level, team=team) @@ -539,7 +529,6 @@ def create_advice( pv_grading=None, advice_text="This is some text", good=None, - goods_type=None, countersign_comments="", countersigned_by=None, ): @@ -568,8 +557,6 @@ def create_advice( if good: advice.good = good - elif goods_type: - advice.goods_type = goods_type elif advice_field == "good": if case.case_type.sub_type == CaseTypeSubTypeEnum.STANDARD: advice.good = GoodOnApplication.objects.filter(application=case).first().good diff --git a/test_helpers/test_endpoints/test_endpoint_response_time.py b/test_helpers/test_endpoints/test_endpoint_response_time.py index b609106c13..cc88d03fd8 100644 --- a/test_helpers/test_endpoints/test_endpoint_response_time.py +++ b/test_helpers/test_endpoints/test_endpoint_response_time.py @@ -32,7 +32,6 @@ class EndPointTests(SimpleTestCase): # stored variables for requests, so no need to get the data multiple times across tests standard_application_id = None open_application_id = None - goods_type_id = None application_party_id = None good_id = None good_document_id = None