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-5769: Add option to approve multiple cases #2294

Merged
merged 19 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
42 changes: 42 additions & 0 deletions caseworker/advice/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
# Teams
DESNZ_CHEMICAL = "56273dd4-4634-4ad7-a782-e480f85a85a9" # /PS-IGNORE
DESNZ_NUCLEAR = "88164d59-8724-4596-811b-40b60b5cf892" # /PS-IGNORE
FCDO_TEAM = "67b9a4a3-6f3d-4511-8a19-23ccff221a74" # /PS-IGNORE
LICENSING_UNIT_TEAM = "58e77e47-42c8-499f-a58d-94f94541f8c6" # /PS-IGNORE
MOD_CAPPROT_TEAM = "a06aec31-47d7-443b-860d-66ab0c6d7cfd" # /PS-IGNORE
NCSC_TEAM = "2b5492ad-0ef5-4a2c-bba2-b208fbe80956" # /PS-IGNORE
ENFORCEMENT_UNIT = "48b4fe54-7d4f-4c03-b143-f8d1dcef9f5b" # /PS-IGNORE
MOD_ECJU = "b7640925-2577-4c24-8081-b85bd635b62a" # /PS-IGNORE
MOD_DI_TEAM = "2e5fab3c-4599-432e-9540-74ccfafb18ee" # /PS-IGNORE
MOD_DSR_TEAM = "4c62ce4a-18f8-4ada-8d18-4b53a565250f" # /PS-IGNORE
MOD_DSTL_TEAM = "809eba0f-f197-4f0f-949b-9af309a844fb" # /PS-IGNORE

# Queues
DESNZ_NUCLEAR_CASES_TO_REVIEW = "f26cd433-b23c-4bb0-95d3-3def83f7fd19" # /PS-IGNORE
DESNZ_NUCLEAR_COUNTERSIGNING = "91213b45-f69f-492d-9d61-84e3a27cceb3" # /PS-IGNORE
DESNZ_CHEMICAL_CASES_TO_REVIEW = "58e79b78-8817-40d0-afb3-fda57978a502" # /PS-IGNORE
DESNZ_RUSSIA_SANCTIONS = "3ac48607-8102-49d9-bf49-55ef7b3cecef" # /PS-IGNORE
FCDO_CASES_TO_REVIEW_QUEUE = "f458094c-1fed-4222-ac70-ff5fa20ff649" # /PS-IGNORE
FCDO_COUNTERSIGNING_QUEUE = "5e772575-9ae4-4a16-b55b-7e1476d810c4" # /PS-IGNORE
FCDO_CPACC_CASES_TO_REVIEW_QUEUE = "a7ac8131-8eac-4dab-9eb1-aa2e9d0c0d42" # /PS-IGNORE
LU_POST_CIRC_FINALISE_QUEUE = "f0e7c2fa-100f-42ad-b740-bb072393e664" # /PS-IGNORE
LU_LICENSING_MANAGER_QUEUE = "9f5a2a93-03ed-4416-a8f8-8b728e5ea9d0" # /PS-IGNORE
LU_SR_LICENSING_MANAGER_QUEUE = "7b643901-565a-4ec8-8a7a-de34bc541a0e" # /PS-IGNORE
MOD_CAPPROT_CASES_TO_REVIEW = "93d1bc19-979d-4ba3-a57c-b0ce253c6237" # /PS-IGNORE
MOD_DI_DIRECT_CASES_TO_REVIEW = "c93f1e56-c577-4910-a01c-434e47ac6c9c" # /PS-IGNORE
MOD_DI_INDIRECT_CASES_TO_REVIEW = "0dd6c6f0-8f8b-4c03-b68f-0d8b04225369" # /PS-IGNORE
MOD_DSR_CASES_TO_REVIEW = "a84d6556-782e-4002-abe2-8bc1e5c2b162" # /PS-IGNORE
MOD_DSTL_CASES_TO_REVIEW = "1a5f47ee-ef5e-456b-914c-4fa629b4559c" # /PS-IGNORE
MOD_ECJU_REVIEW_AND_COMBINE = "432a8587-fc0e-4d34-9b50-92ad6d45bb16" # /PS-IGNORE
NCSC_CASES_TO_REVIEW = "bbfc426b-a1af-4a4c-a97b-ae1557de4210" # /PS-IGNORE


BULK_APPROVE_ALLOWED_QUEUES = (
MOD_CAPPROT_CASES_TO_REVIEW,
MOD_DI_DIRECT_CASES_TO_REVIEW,
MOD_DI_INDIRECT_CASES_TO_REVIEW,
MOD_DSR_CASES_TO_REVIEW,
MOD_DSTL_CASES_TO_REVIEW,
NCSC_CASES_TO_REVIEW,
)

DECISION_TYPE_VERB_MAPPING = {
"Approve": "approved",
"Proviso": "approved",
Expand Down
5 changes: 5 additions & 0 deletions caseworker/advice/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,11 @@ def unassessed_trigger_list_goods(case):
]


def post_bulk_approval_recommendation(request, queue_id, data):
response = client.post(request, f"/caseworker/queues/{queue_id}/bulk-approval/", data)
return response.json(), response.status_code


def get_advice_tab_context(case, caseworker, queue_id):
"""Get contextual information for the advice tab such as the tab's URL and
button visibility, based off the case, the current user and current user's queue.
Expand Down
73 changes: 73 additions & 0 deletions caseworker/advice/views/bulk_approval.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import rules

from http import HTTPStatus

from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.http import Http404, HttpResponseRedirect
from django.urls import reverse
from django.views import View


from caseworker.advice.services import post_bulk_approval_recommendation
from caseworker.users.services import get_gov_user

from core.auth.views import LoginRequiredMixin
from core.decorators import expect_status


class BulkApprovalView(LoginRequiredMixin, SuccessMessageMixin, View):
"""
Submit approval recommendation for the selected cases
"""

saruniitr marked this conversation as resolved.
Show resolved Hide resolved
def dispatch(self, *args, **kwargs):

if not rules.test_rule("can_user_bulk_approve_cases", self.request, self.kwargs["pk"]):
raise Http404()
return super().dispatch(*args, **kwargs)

@property
def caseworker_id(self):
return str(self.request.session["lite_api_user_id"])

@property
def caseworker(self):
data, _ = get_gov_user(self.request, self.caseworker_id)
return data["user"]

def get_success_url(self):
return reverse("queues:cases", kwargs={"queue_pk": self.kwargs["pk"]})

@expect_status(
HTTPStatus.CREATED,
"Error submitting bulk approval recommendation",
"Unexpected error submitting bulk approval recommendation",
)
def submit_bulk_approval_recommendation(self, queue_id, payload):
return post_bulk_approval_recommendation(self.request, queue_id, payload)

def post(self, request, *args, **kwargs):
queue_id = self.kwargs["pk"]
cases = self.request.POST.getlist("cases", [])
currycoder marked this conversation as resolved.
Show resolved Hide resolved
payload = {
"cases": cases,
"advice": {
"text": "No concerns: Approved using bulk approval",
"proviso": "",
"note": "",
"footnote_required": False,
"footnote": "",
"team": str(self.caseworker["team"]["id"]),
},
}

self.submit_bulk_approval_recommendation(queue_id, payload)

num_cases = len(cases)
success_message = f"Successfully approved {num_cases} cases"
if num_cases == 1:
success_message = "Successfully approved 1 case"
messages.success(self.request, success_message)

return HttpResponseRedirect(self.get_success_url())
11 changes: 11 additions & 0 deletions caseworker/queues/rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import rules

from caseworker.advice.constants import BULK_APPROVE_ALLOWED_QUEUES


@rules.predicate
def can_user_bulk_approve_cases(request, queue_id):
return str(queue_id) in BULK_APPROVE_ALLOWED_QUEUES


rules.add_rule("can_user_bulk_approve_cases", can_user_bulk_approve_cases)
2 changes: 2 additions & 0 deletions caseworker/queues/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.urls import path

from caseworker.advice.views.bulk_approval import BulkApprovalView
from caseworker.queues.views import case_assignments, cases, enforcement, queues

app_name = "queues"
Expand Down Expand Up @@ -36,4 +37,5 @@
path(
"<uuid:pk>/enforcement-xml-import/", enforcement.EnforcementXMLImport.as_view(), name="enforcement_xml_import"
),
path("<uuid:pk>/bulk-approve/", BulkApprovalView.as_view(), name="bulk_approval"),
]
17 changes: 16 additions & 1 deletion caseworker/templates/queues/cases.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% extends 'layouts/base.html' %}

{% load svg static %}
{% load crispy_forms_tags %}
{% load crispy_forms_tags rules %}

{% block back_link %}{% endblock %}

Expand All @@ -15,7 +15,21 @@
</a>
</div>
{% if not queue.is_system_queue %}
{% test_rule 'can_user_bulk_approve_cases' request queue.id as can_user_bulk_approve_cases %}
<div class="lite-app-bar__controls lite-buttons-row" >
{% if can_user_bulk_approve_cases %}
<div data-enable-on-checkboxes="#table-cases">
<button
id="bulk-approve-button"
type="submit" class="govuk-button govuk-button--secondary"
form="form-cases"
formmethod="post"
formaction="{% url 'queues:bulk_approval' queue.id %}"
>
Approve
</button>
</div>
{% endif %}
<div data-enable-on-checkboxes="#table-cases">
<button id="assign-users-button" form="form-cases" type="submit" class="govuk-button govuk-button--secondary">{% lcs 'cases.CasesListPage.ALLOCATE_CASE' %}</button>
</div>
Expand Down Expand Up @@ -66,6 +80,7 @@
</div>

<form id="form-cases" method="get" action="{% url 'queues:case_assignment_select_role' queue.id %}">
{% csrf_token %}
{% if not data.results.cases %}
{% if tab_data.my_cases.is_selected %}
{% include "includes/notice.html" with text='cases.CasesListPage.NO_CASES_ALLOCATED' %}
Expand Down
1 change: 1 addition & 0 deletions conf/caseworker.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"caseworker.teams",
"caseworker.cases",
"caseworker.activities",
"caseworker.queues",
]

MIDDLEWARE += [
Expand Down
Loading
Loading