Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LTD: Advice changes to handle cases sent back #2214

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/applications/serializers/advice.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class AdviceViewSerializer(serializers.Serializer):
type = KeyValueChoiceField(choices=AdviceType.choices)
level = serializers.CharField()
proviso = serializers.CharField()
valid = serializers.BooleanField()
denial_reasons = serializers.PrimaryKeyRelatedField(queryset=DenialReason.objects.all(), many=True)
footnote = serializers.CharField()
user = PrimaryKeyRelatedSerializerField(queryset=GovUser.objects.all(), serializer=GovUserListSerializer)
Expand Down
3 changes: 3 additions & 0 deletions api/applications/views/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,12 +425,14 @@ def get(self, request, pk):
approved_goods_on_application = (
GoodOnApplication.objects.filter(
application_id=pk,
good__advice__valid=True,
good__advice__level=AdviceLevel.FINAL,
good__advice__type__in=[AdviceType.APPROVE, AdviceType.PROVISO, AdviceType.NO_LICENCE_REQUIRED],
good__advice__case_id=pk,
good__advice__good_id__isnull=False,
)
.annotate(
advice_valid=F("good__advice__valid"),
advice_type=F("good__advice__type"),
advice_text=F("good__advice__text"),
advice_proviso=F("good__advice__proviso"),
Expand All @@ -450,6 +452,7 @@ def get(self, request, pk):
"is_good_controlled": goa.is_good_controlled,
"value": goa.value,
"advice": {
"valid": goa.advice_valid,
"type": AdviceType.as_representation(goa.advice_type),
"text": goa.advice_text,
"proviso": goa.advice_proviso,
Expand Down
12 changes: 11 additions & 1 deletion api/applications/views/helpers/advice.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from api.audit_trail import service as audit_trail_service
from api.audit_trail.enums import AuditType
from api.cases.enums import AdviceLevel, AdviceType, CountersignOrder
from api.cases.models import CountersignAdvice
from api.cases.models import Advice, CountersignAdvice

from api.flags.enums import FlagStatuses
from api.flags.models import Flag
Expand All @@ -27,6 +27,16 @@ class CounterSignatureIncompleteError(Exception):
pass


def mark_final_advice_as_invalid(case):
lu_team = Team.objects.get(id=TeamIdEnum.LICENSING_UNIT)
previous_final_advice = Advice.objects.filter(
case=case,
level=AdviceLevel.FINAL,
user__team=lu_team,
)
previous_final_advice.update(valid=False)


def lu_countersigning_flags_all():
return Flag.objects.filter(
id__in=[
Expand Down
5 changes: 5 additions & 0 deletions api/cases/libraries/post_advice.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from api.applications.views.helpers.advice import (
mark_lu_rejected_countersignatures_as_invalid,
remove_countersign_process_flags,
mark_final_advice_as_invalid,
)
from api.audit_trail import service as audit_trail_service
from api.audit_trail.enums import AuditType
Expand Down Expand Up @@ -92,6 +93,10 @@ def post_advice(request, case, level, team=False):
serializer = AdviceCreateSerializer(data=data, many=True, context={"footnote_permission": footnote_permission})

if serializer.is_valid() and not refusal_error:

if level == AdviceLevel.FINAL:
mark_final_advice_as_invalid(case)

serializer.save()

audit_verbs = {
Expand Down
23 changes: 23 additions & 0 deletions api/cases/migrations/0067_advice_valid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.13 on 2024-09-29 20:24

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("cases", "0066_delete_casereviewdate"),
]

operations = [
migrations.AddField(
model_name="advice",
name="valid",
field=models.BooleanField(
blank=True,
default=True,
help_text="Indicates whether it is valid or not. Existing advice become invalid if new advice is given by the user",
null=True,
),
),
]
6 changes: 6 additions & 0 deletions api/cases/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,12 @@ class Advice(TimestampableModel):
is_refusal_note = models.BooleanField(default=False)
team = models.ForeignKey(Team, on_delete=models.CASCADE, null=True)
level = models.CharField(choices=AdviceLevel.choices, max_length=30)
valid = models.BooleanField(
default=True,
blank=True,
null=True,
help_text="Indicates whether it is valid or not. Existing advice become invalid if new advice is given by the user",
)

# optional footnotes for advice
footnote = models.TextField(blank=True, null=True, default=None)
Expand Down
48 changes: 47 additions & 1 deletion api/cases/serializers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from collections import defaultdict
from rest_framework import serializers

from api.applications.libraries.get_applications import get_application
from api.applications.models import BaseApplication, StandardApplication
from api.applications.serializers.advice import AdviceViewSerializer, CountersignDecisionAdviceViewSerializer
from api.audit_trail.enums import AuditType
from api.audit_trail.models import Audit
from api.staticdata.statuses.serializers import CaseSubStatusSerializer

from api.cases.enums import (
Expand Down Expand Up @@ -54,6 +57,7 @@
ExporterUserSimpleSerializer,
)
from lite_content.lite_api import strings
from lite_routing.routing_rules_internal.helpers import get_move_case_audit_events


class CaseTypeSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -246,14 +250,56 @@ class Meta:
)


def is_case_sent_back(case):
"""Returns True if a Case is sent back after OGDs have given their recommendation"""
queues_data = defaultdict(list)
move_case_qs = get_move_case_audit_events(case)
move_case_qs = move_case_qs.order_by("created_at")

for item in move_case_qs:
payload = item.payload
if isinstance(payload["queues"], list):
for q in payload["queues"]:
queues_data[q].append(item.created_at)
else:
queues_data[payload["queues"]].append(item.created_at)

tau = Queue.objects.get(id="2876addb-1860-4226-b1f3-0bd0c1e21f9c").name
if tau not in queues_data:
return False

ogd_queues = [queue.name for queue in Queue.objects.filter(team__is_ogd=True)]

ogds_assessed = set(queues_data.keys()).intersection(set(ogd_queues))
if not ogds_assessed:
return False

if len(queues_data[tau]) > 1:
return True

return False


class CaseDetailBasicSerializer(serializers.ModelSerializer):
organisation = PrimaryKeyRelatedSerializerField(
queryset=Organisation.objects.all(), serializer=TinyOrganisationViewSerializer
)
circulation_details = serializers.SerializerMethodField()

class Meta:
model = Case
fields = ("id", "reference_code", "organisation")
fields = (
"id",
"reference_code",
"organisation",
"circulation_details",
)

def get_circulation_details(self, instance):
details = {}
details["is_case_sent_back"] = is_case_sent_back(instance)

return details


class CaseDetailSerializer(serializers.ModelSerializer):
Expand Down