Skip to content

Commit

Permalink
Merge pull request #2243 from uktrade/LTD_5653_DESNZ_multiple_licence…
Browse files Browse the repository at this point in the history
…_conditions

Ltd 5653 desnz multiple licence conditions
  • Loading branch information
Tllew authored Dec 3, 2024
2 parents 664d9df + 5b68811 commit ec6c5fd
Show file tree
Hide file tree
Showing 15 changed files with 910 additions and 26 deletions.
15 changes: 15 additions & 0 deletions caseworker/advice/conditionals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from caseworker.advice import services


def form_add_licence_conditions(step_name):
"""Returns the boolean value for add_licence_conditions from the approval form"""

def _get_form_field_boolean(wizard):
cleaned_data = wizard.get_cleaned_data_for_step(step_name)
return cleaned_data.get("add_licence_conditions", False)

return _get_form_field_boolean


def is_desnz_team(wizard):
return wizard.caseworker["team"]["alias"] in services.DESNZ_TEAMS
6 changes: 6 additions & 0 deletions caseworker/advice/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@ class AdviceType:
PROVISO = "proviso"
REFUSE = "refuse"
NO_LICENCE_REQUIRED = "no_licence_required"


class AdviceSteps:
RECOMMEND_APPROVAL = "recommend_approval"
LICENCE_CONDITIONS = "licence_conditions"
LICENCE_FOOTNOTES = "licence_footnotes"
167 changes: 166 additions & 1 deletion caseworker/advice/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@
from django.forms.formsets import formset_factory
from django.utils.html import format_html

from core.common.forms import BaseForm
from crispy_forms_gds.helper import FormHelper
from crispy_forms_gds.layout import Field, Layout, Submit
from crispy_forms_gds.choices import Choice

from core.forms.layouts import ConditionalRadios, ConditionalRadiosQuestion, ExpandingFieldset, RadioTextArea
from core.forms.layouts import (
ConditionalCheckboxes,
ConditionalCheckboxesQuestion,
ConditionalRadios,
ConditionalRadiosQuestion,
ExpandingFieldset,
RadioTextArea,
)
from core.forms.utils import coerce_str_to_bool
from caseworker.tau.summaries import get_good_on_application_tau_summary
from caseworker.tau.widgets import GoodsMultipleSelect
Expand Down Expand Up @@ -484,3 +492,160 @@ def __init__(
self.application_pk = application_pk
self.is_user_rfd = is_user_rfd
self.organisation_documents = organisation_documents


class RecommendAnApprovalForm(PicklistAdviceForm, BaseForm):
class Layout:
TITLE = "Recommend an approval"

approval_reasons = forms.CharField(
widget=forms.Textarea(attrs={"rows": 7, "class": "govuk-!-margin-top-4"}),
label="",
error_messages={"required": "Enter a reason for approving"},
)
approval_radios = forms.ChoiceField(
label="What is your reason for approving?",
required=False,
widget=forms.RadioSelect,
choices=(),
)
add_licence_conditions = forms.BooleanField(
label="Add licence conditions, instructions to exporter or footnotes (optional)",
required=False,
)

def __init__(self, *args, **kwargs):
del kwargs["proviso"]
del kwargs["footnote_details"]
approval_reason = kwargs.pop("approval_reason")
# this follows the same pattern as denial_reasons.
approval_choices, approval_text = self._picklist_to_choices(approval_reason)
self.approval_text = approval_text
super().__init__(*args, **kwargs)

self.fields["approval_radios"].choices = approval_choices

def get_layout_fields(self):
return (
RadioTextArea("approval_radios", "approval_reasons", self.approval_text),
"add_licence_conditions",
)


class PicklistApprovalAdviceEditForm(BaseForm):
class Layout:
TITLE = "Add licence conditions, instructions to exporter or footnotes (optional)"

proviso = forms.CharField(
widget=forms.Textarea(attrs={"rows": 30, "class": "govuk-!-margin-top-4"}),
label="",
required=False,
)

def __init__(self, *args, **kwargs):
del kwargs["approval_reason"]
del kwargs["proviso"]
del kwargs["footnote_details"]
super().__init__(*args, **kwargs)

def get_layout_fields(self):
return ("proviso",)


class LicenceConditionsForm(PicklistAdviceForm, BaseForm):
class Layout:
TITLE = "Add licence conditions, instructions to exporter or footnotes (optional)"

proviso = forms.CharField(
widget=forms.Textarea(attrs={"rows": 7, "class": "govuk-!-margin-top-4"}),
label="",
required=False,
)

approval_radios = forms.ChoiceField(
label="What is your reason for approving?",
required=False,
widget=forms.RadioSelect,
choices=(),
)
proviso_checkboxes = forms.MultipleChoiceField(
label="Add a licence condition (optional)",
required=False,
widget=forms.CheckboxSelectMultiple,
choices=(),
)

def clean(self):
cleaned_data = super().clean()
# only return proviso (text) for selected radios, nothing else matters, join by 2 newlines
return {"proviso": "\r\n\r\n".join([cleaned_data[selected] for selected in cleaned_data["proviso_checkboxes"]])}

def __init__(self, *args, **kwargs):
del kwargs["approval_reason"]
del kwargs["footnote_details"]

proviso = kwargs.pop("proviso")

proviso_choices, proviso_text = self._picklist_to_choices(proviso)
self.proviso_text = proviso_text

self.conditional_checkbox_choices = (
ConditionalCheckboxesQuestion(choices.label, choices.value) for choices in proviso_choices
)

super().__init__(*args, **kwargs)

self.fields["proviso_checkboxes"].choices = proviso_choices
for choices in proviso_choices:
self.fields[choices.value] = forms.CharField(
widget=forms.Textarea(attrs={"rows": 3, "class": "govuk-!-margin-top-4"}),
label="Description",
required=False,
initial=proviso_text[choices.value],
)

def get_layout_fields(self):

return (ConditionalCheckboxes("proviso_checkboxes", *self.conditional_checkbox_choices),)


class FootnotesApprovalAdviceForm(PicklistAdviceForm, BaseForm):
class Layout:
TITLE = "Instructions for the exporter (optional)"

instructions_to_exporter = forms.CharField(
widget=forms.Textarea(attrs={"rows": "3"}),
label="Add any instructions for the exporter (optional)",
help_text="These may be added to the licence cover letter, subject to review by the Licensing Unit.",
required=False,
)

footnote_details_radios = forms.ChoiceField(
label="Add a reporting footnote (optional)",
required=False,
widget=forms.RadioSelect,
choices=(),
)
footnote_details = forms.CharField(
widget=forms.Textarea(attrs={"rows": 3, "class": "govuk-!-margin-top-4"}),
label="",
required=False,
)

def __init__(self, *args, **kwargs):
del kwargs["approval_reason"]
del kwargs["proviso"]

footnote_details = kwargs.pop("footnote_details")
footnote_details_choices, footnote_text = self._picklist_to_choices(footnote_details)
self.footnote_text = footnote_text

super().__init__(*args, **kwargs)

self.fields["footnote_details_radios"].choices = footnote_details_choices

def get_layout_fields(self):
return (
"instructions_to_exporter",
RadioTextArea("footnote_details_radios", "footnote_details", self.footnote_text),
)
14 changes: 14 additions & 0 deletions caseworker/advice/payloads.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from caseworker.advice.constants import AdviceSteps
from core.wizard.payloads import MergingPayloadBuilder


def get_cleaned_data(form):
return form.cleaned_data


class GiveApprovalAdvicePayloadBuilder(MergingPayloadBuilder):
payload_dict = {
AdviceSteps.RECOMMEND_APPROVAL: get_cleaned_data,
AdviceSteps.LICENCE_CONDITIONS: get_cleaned_data,
AdviceSteps.LICENCE_FOOTNOTES: get_cleaned_data,
}
23 changes: 23 additions & 0 deletions caseworker/advice/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,28 @@ def can_desnz_make_recommendation(user, case, queue_alias):
return True


def can_desnz_make_edit(team):
return team in services.DESNZ_TEAMS


def case_has_approval_advice(advice):
if advice:
return advice[0]["type"]["key"] in ["proviso", "approve"]
return False


@rules.predicate
def can_user_make_desnz_edit(request, case):
try:
user = request.lite_user
except AttributeError:
return False

team = user["team"]["alias"]
advice = services.filter_current_user_advice(case.advice, user["id"])
return can_desnz_make_edit(team) and case_has_approval_advice(advice)


@rules.predicate
def can_user_make_recommendation(request, case):
try:
Expand Down Expand Up @@ -62,3 +84,4 @@ def can_user_make_recommendation(request, case):

rules.add_rule("can_user_make_recommendation", is_user_allocated & can_user_make_recommendation)
rules.add_rule("can_user_allocate_and_approve", can_user_make_recommendation)
rules.add_rule("can_user_make_desnz_edit", can_user_make_desnz_edit)
10 changes: 5 additions & 5 deletions caseworker/advice/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,12 +321,12 @@ def get_advice_subjects(case, countries=None):
def post_approval_advice(request, case, data, level="user-advice"):
json = [
{
"type": "proviso" if data["proviso"] else "approve",
"type": "proviso" if data.get("proviso", False) else "approve",
"text": data["approval_reasons"],
"proviso": data["proviso"],
"note": data["instructions_to_exporter"],
"footnote_required": True if data["footnote_details"] else False,
"footnote": data["footnote_details"],
"proviso": data.get("proviso", ""),
"note": data.get("instructions_to_exporter", ""),
"footnote_required": True if data.get("footnote_details") else False,
"footnote": data.get("footnote_details", ""),
subject_name: subject_id,
"denial_reasons": [],
}
Expand Down
23 changes: 14 additions & 9 deletions caseworker/advice/templates/advice/view_my_advice.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{% extends 'layouts/case.html' %}
{% load crispy_forms_tags advice_tags %}
{% load static advice_tags %}
{% load rules %}
{% block header_tabs %}
<div id="tab-bar" class="app-case-tab-bar">
Expand Down Expand Up @@ -33,16 +34,20 @@ <h2 class="govuk-error-summary__title" id="error-summary-title">
<div class="govuk-grid-column-three-quarters">
<h1 class="govuk-heading-xl">View recommendation</h1>
{% if my_advice %}
{% if buttons.edit_recommendation %}
<a role="button" draggable="false" class="govuk-button govuk-button--secondary" href="{% url 'cases:edit_advice' queue_pk case.id %}">
Edit recommendation
</a>
{% endif %}
{% if buttons.clear_recommendation %}
<a role="button" draggable="false" class="govuk-button govuk-button--secondary" href="{% url 'cases:delete_advice' queue_pk case.id %}">
Clear recommendation
</a>
{% test_rule 'can_user_make_desnz_edit' request case as can_user_make_desnz_edit %}

{% if buttons.edit_recommendation %}
{% if can_user_make_desnz_edit %}
<a role="button" draggable="false" class="govuk-button govuk-button--secondary" href="{% url 'cases:edit_advice' queue_pk case.id %}">Edit recommendation</a>
{% else %}
<a role="button" draggable="false" class="govuk-button govuk-button--secondary" href="{% url 'cases:edit_advice_legacy' queue_pk case.id %}">Edit recommendation</a>
{% endif %}
{% endif %}
{% if buttons.clear_recommendation %}
<a role="button" draggable="false" class="govuk-button govuk-button--secondary" href="{% url 'cases:delete_advice' queue_pk case.id %}">
Clear recommendation
</a>
{% endif %}
{% for advice in my_advice %}
{% include "advice/advice_details.html" %}
{% endfor %}
Expand Down
2 changes: 2 additions & 0 deletions caseworker/advice/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
path("", views.AdviceView.as_view(), name="advice_view"),
path("case-details/", views.CaseDetailView.as_view(), name="case_details"),
path("select-advice/", views.SelectAdviceView.as_view(), name="select_advice"),
path("approve-all-legacy/", views.GiveApprovalAdviceViewLegacy.as_view(), name="approve_all_legacy"),
path("approve-all/", views.GiveApprovalAdviceView.as_view(), name="approve_all"),
path("refuse-all/", views.RefusalAdviceView.as_view(), name="refuse_all"),
path("view-my-advice/", views.AdviceDetailView.as_view(), name="view_my_advice"),
path("edit-advice-legacy/", views.EditAdviceViewLegacy.as_view(), name="edit_advice_legacy"),
path("edit-advice/", views.EditAdviceView.as_view(), name="edit_advice"),
path("delete-advice/", views.DeleteAdviceView.as_view(), name="delete_advice"),
path("countersign/", views.CountersignAdviceView.as_view(), name="countersign_advice_view"),
Expand Down
Loading

0 comments on commit ec6c5fd

Please sign in to comment.