From 1daa54ca684ed53634ef1fb453106218b19e2224 Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Fri, 24 Sep 2021 11:52:46 +0100 Subject: [PATCH] Identify if the application is amended or not User can do minor and major edits and there is no database field for this. Add changes to determine if an application is amended or not by using the audit logs created when minor/major edits are done. --- .../serializers/standard_application.py | 32 +++++++++++++++++++ .../tests/test_application_status.py | 4 +++ .../tests/test_edit_application.py | 1 + api/applications/tests/test_removing_goods.py | 3 ++ .../tests/test_application_views.py | 1 + 5 files changed, 41 insertions(+) diff --git a/api/applications/serializers/standard_application.py b/api/applications/serializers/standard_application.py index 9d995a15a..6643d749e 100644 --- a/api/applications/serializers/standard_application.py +++ b/api/applications/serializers/standard_application.py @@ -1,3 +1,4 @@ +from django.db.models import Q from rest_framework import serializers from rest_framework.fields import CharField @@ -17,10 +18,13 @@ from api.applications.serializers.good import GoodOnApplicationViewSerializer from api.licences.serializers.view_licence import CaseLicenceViewSerializer from api.applications.serializers.serializer_helper import validate_field +from api.audit_trail.enums import AuditType +from api.audit_trail.models import Audit from api.cases.enums import CaseTypeEnum from api.core.serializers import KeyValueChoiceField from api.licences.models import Licence from lite_content.lite_api import strings +from api.staticdata.statuses.enums import CaseStatusEnum from api.staticdata.trade_control.enums import TradeControlProductCategory, TradeControlActivity @@ -34,6 +38,7 @@ class StandardApplicationViewSerializer(PartiesSerializerMixin, GenericApplicati trade_control_activity = serializers.SerializerMethodField() trade_control_product_categories = serializers.SerializerMethodField() sanction_matches = serializers.SerializerMethodField() + is_amended = serializers.SerializerMethodField() class Meta: model = StandardApplication @@ -69,6 +74,7 @@ class Meta: "trade_control_activity", "trade_control_product_categories", "sanction_matches", + "is_amended", ) ) @@ -99,6 +105,32 @@ def get_denial_matches(self, instance): denial_matches = DenialMatchOnApplication.objects.filter(application=instance, denial__is_revoked=False) return DenialMatchOnApplicationViewSerializer(denial_matches, many=True).data + def get_is_amended(self, instance): + """Determines whether an application is major/minor edited using Audit logs + and returns True if either of the amends are done, False otherwise""" + audit_qs = Audit.objects.filter(target_object_id=instance.id) + is_reference_name_updated = audit_qs.filter(verb=AuditType.UPDATED_APPLICATION_NAME).exists() + is_product_removed = audit_qs.filter(verb=AuditType.REMOVE_GOOD_FROM_APPLICATION).exists() + app_letter_ref_updated = audit_qs.filter( + Q( + verb__in=[ + AuditType.ADDED_APPLICATION_LETTER_REFERENCE, + AuditType.UPDATE_APPLICATION_LETTER_REFERENCE, + AuditType.REMOVED_APPLICATION_LETTER_REFERENCE, + ] + ) + ) + # in case of doing major edits then the status is set as "Applicant editing" + # Here we are detecting the transition from "Submitted" -> "Applicant editing" + for item in audit_qs.filter(verb=AuditType.UPDATED_STATUS): + status = item.payload["status"] + if status["old"] == CaseStatusEnum.get_text(CaseStatusEnum.SUBMITTED) and status[ + "new" + ] == CaseStatusEnum.get_text(CaseStatusEnum.APPLICANT_EDITING): + return True + + return any([is_reference_name_updated, app_letter_ref_updated, is_product_removed]) + class StandardApplicationCreateSerializer(GenericApplicationCreateSerializer): export_type = KeyValueChoiceField( diff --git a/api/applications/tests/test_application_status.py b/api/applications/tests/test_application_status.py index 58efad2eb..3f97b5a33 100644 --- a/api/applications/tests/test_application_status.py +++ b/api/applications/tests/test_application_status.py @@ -5,6 +5,7 @@ from parameterized import parameterized from rest_framework import status +from api.audit_trail.models import AuditType, Audit from api.cases.models import CaseAssignment from gov_notify.enums import TemplateType from api.licences.enums import LicenceStatus @@ -57,6 +58,9 @@ def test_exporter_set_application_status_applicant_editing_when_in_editable_stat self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(self.standard_application.status, get_case_status_by_status(CaseStatusEnum.APPLICANT_EDITING)) + audit_event = Audit.objects.first() + self.assertEqual(audit_event.verb, AuditType.UPDATED_STATUS) + self.assertEqual(audit_event.payload, {"status": {"new": "Applicant editing", "old": "Submitted"}}) def test_exporter_set_application_status_withdrawn_when_application_not_terminal_success(self): self.submit_application(self.standard_application) diff --git a/api/applications/tests/test_edit_application.py b/api/applications/tests/test_edit_application.py index df37aff65..1a5452637 100644 --- a/api/applications/tests/test_edit_application.py +++ b/api/applications/tests/test_edit_application.py @@ -56,6 +56,7 @@ def test_edit_application_name_in_editable_status_success(self, editable_status) self.assertEqual(application.name, self.data["name"]) self.assertNotEqual(application.updated_at, updated_at) self.assertEqual(audit_qs.count(), 2) + self.assertEqual(audit_object.verb, AuditType.UPDATED_APPLICATION_NAME) self.assertEqual(audit_object.payload, {"new_name": self.data["name"], "old_name": old_name}) @parameterized.expand(get_case_statuses(read_only=True)) diff --git a/api/applications/tests/test_removing_goods.py b/api/applications/tests/test_removing_goods.py index d53d38d80..27b14c2c6 100644 --- a/api/applications/tests/test_removing_goods.py +++ b/api/applications/tests/test_removing_goods.py @@ -5,6 +5,7 @@ from api.applications.libraries.case_status_helpers import get_case_statuses from api.applications.models import GoodOnApplication +from api.audit_trail.models import Audit, AuditType from api.flags.enums import SystemFlags from api.goods.enums import GoodStatus from api.goods.models import Good, FirearmGoodDetails @@ -88,6 +89,8 @@ def test_remove_a_good_from_application_success_when_good_is_on_multiple_applica self.assertEqual( Good.objects.get(pk=good_on_application2.good.pk).status, GoodStatus.SUBMITTED, ) + audit_event = Audit.objects.first() + self.assertEqual(audit_event.verb, AuditType.REMOVE_GOOD_FROM_APPLICATION) def test_remove_a_good_that_does_not_exist_from_draft(self): """ diff --git a/api/data_workspace/tests/test_application_views.py b/api/data_workspace/tests/test_application_views.py index a8f68b29d..551cf5c4e 100644 --- a/api/data_workspace/tests/test_application_views.py +++ b/api/data_workspace/tests/test_application_views.py @@ -70,6 +70,7 @@ def test_dw_standard_application_views(self): "destinations", "denial_matches", "licence", + "is_amended", ) for key in expected_keys: self.assertTrue(key in actual_keys)