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

TP2000-1048 Add geo area exclusions step to multiple measure edit journey #1039

Merged
merged 15 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from 11 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
9 changes: 9 additions & 0 deletions measures/conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,19 @@ def show_step_duties(wizard):
return MeasureEditSteps.DUTIES.value in cleaned_data.get("fields_to_edit")


def show_step_geographical_area_exclusions(wizard):
cleaned_data = wizard.get_cleaned_data_for_step(START)
if cleaned_data:
return MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS.value in cleaned_data.get(
"fields_to_edit",
)


measure_edit_condition_dict = {
MeasureEditSteps.START_DATE: show_step_start_date,
MeasureEditSteps.END_DATE: show_step_end_date,
MeasureEditSteps.QUOTA_ORDER_NUMBER: show_step_quota_order_number,
MeasureEditSteps.REGULATION: show_step_regulation,
MeasureEditSteps.DUTIES: show_step_duties,
MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS: show_step_geographical_area_exclusions,
}
5 changes: 5 additions & 0 deletions measures/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.db import models

START = "start"
GEOGRAPHICAL_AREA_EXCLUSIONS = "geographical_area_exclusions"
dalecannon marked this conversation as resolved.
Show resolved Hide resolved


class MeasureEditSteps(models.TextChoices):
Expand All @@ -9,3 +10,7 @@ class MeasureEditSteps(models.TextChoices):
QUOTA_ORDER_NUMBER = ("quota_order_number", "Quota order number")
REGULATION = ("regulation", "Regulation")
DUTIES = ("duties", "Duties")
GEOGRAPHICAL_AREA_EXCLUSIONS = (
"geographical_area_exclusions",
"Geographical area exclusions",
)
39 changes: 39 additions & 0 deletions measures/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1634,3 +1634,42 @@ def clean(self):
validate_duties(duties, measure.valid_between.lower)

return cleaned_data


class MeasureGeographicalAreaExclusionsForm(forms.Form):
excluded_area = forms.ModelChoiceField(
label="",
queryset=GeographicalArea.objects.all(),
help_text="Select a geographical area to be excluded",
required=False,
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.fields["excluded_area"].queryset = (
GeographicalArea.objects.current()
.with_latest_description()
.as_at_today_and_beyond()
.order_by("description")
)

self.fields[
"excluded_area"
].label_from_instance = lambda obj: f"{obj.area_id} - {obj.description}"

self.helper = FormHelper(self)
self.helper.form_tag = False
self.helper.legend_size = Size.SMALL
self.helper.layout = Layout(
Fieldset(
"excluded_area",
),
)


class MeasureGeographicalAreaExclusionsFormSet(FormSet):
"""Allows editing the geographical area exclusions of multiple measures in
`MeasureEditWizard`."""

form = MeasureGeographicalAreaExclusionsForm
58 changes: 58 additions & 0 deletions measures/jinja2/measures/edit-multiple-formset.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{% extends "measures/edit-wizard-step.jinja" %}

{% from "components/fieldset/macro.njk" import govukFieldset %}
{% from "components/error-summary/macro.njk" import govukErrorSummary %}

{% block form %}
{% set formset = form %}
{{ crispy(formset.management_form, no_form_tags) }}

{% if formset.non_form_errors() %}
{% set error_list = [] %}
{% for error in formset.non_form_errors() %}
{{ error_list.append({
"html": '<p class="govuk-error-message">' ~ error ~ '</p>',
}) or "" }}
{% endfor %}

{{ govukErrorSummary({
"titleText": "There is a problem",
"errorList": error_list
}) }}
{% endif %}

{% for form in formset %}
{% if form.non_field_errors() %}
{% set error_list = [] %}
{% for error in form.non_field_errors() %}
{{ error_list.append({
"html": '<p class="govuk-error-message">' ~ error ~ '</p>',
}) or "" }}
{% endfor %}

{{ govukErrorSummary({
"titleText": "There is a problem",
"errorList": error_list
}) }}
{% endif %}
{{ crispy(form) }}
{% endfor %}

{% if formset.data[formset.prefix ~ "-ADD"] %}
{{ crispy(formset.empty_form)|replace("__prefix__", formset.forms|length)|safe }}
{% endif %}

<div class="govuk-button-group">
{{ govukButton({"text": "Save"}) }}

{% if formset.total_form_count() + 1 < formset.max_num %}
{{ govukButton({
"text": "Add another",
"attributes": {"id": "add-new"},
"classes": "govuk-button--secondary",
"value": "1",
"name": formset.prefix ~ "-ADD",
}) }}
{% endif %}
</div>
{% endblock %}
26 changes: 26 additions & 0 deletions measures/tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1582,3 +1582,29 @@ def test_measure_review_form_validates_components_applicability_exclusivity(
"A duty cannot be specified on both commodities and conditions"
in form.errors["__all__"]
)


def test_measure_geographical_area_exclusions_form_valid_choice():
"""Tests that `MeasureGeographicalAreaExclusionsForm` is valid when an
available geographical area is selected."""
geo_area = factories.GeographicalAreaFactory.create()
data = {
"excluded_area": geo_area.pk,
}
with override_current_transaction(geo_area.transaction):
form = forms.MeasureGeographicalAreaExclusionsForm(data)
assert form.is_valid()
assert form.cleaned_data["excluded_area"] == geo_area


def test_measure_geographical_area_exclusions_form_invalid_choice():
"""Tests that `MeasureGeographicalAreaExclusionsForm` raises an raises an
invalid choice error when an unavailable geographical area is selected."""
data = {
"excluded_area": "geo_area",
}
form = forms.MeasureGeographicalAreaExclusionsForm(data)
assert not form.is_valid()
assert form.errors["excluded_area"] == [
"Select a valid choice. That choice is not one of the available choices.",
]
94 changes: 94 additions & 0 deletions measures/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@
pytestmark = pytest.mark.django_db


@pytest.fixture()
def mocked_diff_components():
"""Mocks `diff_components()` inside `update_measure_components()` in
`MeasureEditWizard` to prevent parsing errors where test measures lack a
duty sentence."""
with patch(
"measures.views.MeasureEditWizard.update_measure_components",
) as update_measure_components:
yield update_measure_components


def test_measure_footnotes_update_get_delete_key():
footnote_key = "form-0-footnote"
expected = "form-0-DELETE"
Expand Down Expand Up @@ -1718,6 +1729,7 @@ def test_measuretype_api_list_view(valid_user_client):
def test_multiple_measure_start_and_end_date_edit_functionality(
valid_user_client,
session_workbasket,
mocked_diff_components,
):
"""Tests that MeasureEditWizard takes a list of measures, and sets their
update type to update, updates their end dates and start dates, and clears
Expand Down Expand Up @@ -1841,6 +1853,7 @@ def test_multiple_measure_edit_single_form_functionality(
data,
valid_user_client,
session_workbasket,
mocked_diff_components,
):
"""Tests that MeasureEditWizard takes a list of measures, and sets their
update type to update, updates their end dates and start dates, and clears
Expand Down Expand Up @@ -1913,6 +1926,7 @@ def test_multiple_measure_edit_single_form_functionality(
def test_multiple_measure_edit_only_regulation(
valid_user_client,
session_workbasket,
mocked_diff_components,
):
"""Tests the regulation step in MeasureEditWizard."""
measure_1 = factories.MeasureFactory.create()
Expand Down Expand Up @@ -2147,6 +2161,7 @@ def test_measure_list_selected_measures_list(valid_user_client):
def test_multiple_measure_edit_only_quota_order_number(
valid_user_client,
session_workbasket,
mocked_diff_components,
):
"""Tests the regulation step in MeasureEditWizard."""
measure_1 = factories.MeasureFactory.create()
Expand Down Expand Up @@ -2298,6 +2313,7 @@ def test_multiple_measure_edit_only_duties(
def test_multiple_measure_edit_preserves_footnote_associations(
valid_user_client,
session_workbasket,
mocked_diff_components,
):
"""Tests that footnote associations are preserved in MeasureEditWizard."""

Expand Down Expand Up @@ -2375,6 +2391,84 @@ def test_multiple_measure_edit_preserves_footnote_associations(
assert footnote in expected_footnotes


def test_multiple_measure_edit_geographical_area_exclusions(
valid_user_client,
session_workbasket,
mocked_diff_components,
):
"""Tests that the geographical area exclusions of multiple measures can be
edited in `MeasureEditWizard`."""
measure_1 = factories.MeasureFactory.create(with_exclusion=True)
measure_2 = factories.MeasureFactory.create()
new_excluded_area = factories.CountryFactory.create()

url = reverse("measure-ui-edit-multiple")
session = valid_user_client.session
session.update(
{
"workbasket": {
"id": session_workbasket.pk,
},
"MULTIPLE_MEASURE_SELECTIONS": {
measure_1.pk: 1,
measure_2.pk: 1,
},
},
)
session.save()

STEP_KEY = "measure_edit_wizard-current_step"

wizard_data = [
{
"data": {
STEP_KEY: START,
"start-fields_to_edit": [MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS],
},
"next_step": MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS,
},
{
"data": {
STEP_KEY: MeasureEditSteps.GEOGRAPHICAL_AREA_EXCLUSIONS,
"form-0-excluded_area": new_excluded_area.pk,
},
"next_step": "complete",
},
]
for step_data in wizard_data:
url = reverse(
"measure-ui-edit-multiple",
kwargs={"step": step_data["data"][STEP_KEY]},
)
response = valid_user_client.get(url)
assert response.status_code == 200

response = valid_user_client.post(url, step_data["data"])
assert response.status_code == 302

assert response.url == reverse(
"measure-ui-edit-multiple",
kwargs={"step": step_data["next_step"]},
)

complete_response = valid_user_client.get(response.url)
assert complete_response.status_code == 302
assert valid_user_client.session["MULTIPLE_MEASURE_SELECTIONS"] == {}

workbasket_measures = Measure.objects.filter(
transaction__workbasket=session_workbasket,
)
assert workbasket_measures

with override_current_transaction(Transaction.objects.last()):
for measure in workbasket_measures:
assert measure.update_type == UpdateType.UPDATE
assert (
measure.exclusions.current().first().excluded_geographical_area
== new_excluded_area
)


def test_measure_list_redirects_to_search_with_no_params(valid_user_client):
response = valid_user_client.get(reverse("measure-ui-list"))
assert response.status_code == 302
Expand Down
Loading
Loading