Skip to content

Commit

Permalink
TP2000-1537 Bulk create QuotaDefinitionPeriods (#1320)
Browse files Browse the repository at this point in the history
  • Loading branch information
CPrich905 authored and dalecannon committed Jan 10, 2025
1 parent 74fe490 commit 1e6b416
Show file tree
Hide file tree
Showing 21 changed files with 1,624 additions and 147 deletions.
6 changes: 6 additions & 0 deletions common/static/common/scss/_quota-definitions.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@

.definition-original {
color: $govuk-secondary-text-colour;
}

.bulk-create-review {
details.govuk-details[open] {
height: 420px !important;
}
}
16 changes: 16 additions & 0 deletions common/templates/common/widgets/decimal_suffix.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div class="govuk-input__wrapper">
<input
class="numberinput govuk-input govuk-input govuk-input--width-2"
type="{{ widget.type }}"
name="{{ widget.name }}"
{% if widget.value != None %}
value="{{ widget.value|stringformat:'s' }}"
{% endif %}
{% include "django/forms/widgets/attrs.html" %}>
{% if widget.suffix %}
<div class="govuk-input__suffix" aria-hidden="true">{{widget.suffix}}</div>
{% endif %}
<label{% if widget.attrs.id %} id="{{ widget.attrs.id }}-label" for="{{ widget.attrs.id }}"{% endif %} class="govuk-label">
{{ widget.label }}
</label>
</div>
20 changes: 20 additions & 0 deletions common/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,23 @@ class MultipleFileInput(FileInput):
"""Enable multiple files to be selected using FileInput widget."""

allow_multiple_selected = True


class DecimalSuffix(widgets.NumberInput):
"""Identical to the Decimal widget but allows the addition of a suffix."""

template_name = "common/widgets/decimal_suffix.html"

def __init__(self, attrs=None, suffix="", **kwargs):
self.suffix = suffix
super().__init__(attrs, **kwargs)

def render(self, name, value, attrs=None, renderer=None):
if attrs is None:
attrs = {}
context = self.get_context(name, value, attrs)
if self.suffix:
context["widget"]["suffix"] = self.suffix

template = loader.get_template(self.template_name).render(context)
return mark_safe(template)
128 changes: 90 additions & 38 deletions quotas/forms/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

from crispy_forms_gds.helper import FormHelper
from crispy_forms_gds.layout import HTML
from crispy_forms_gds.layout import Accordion
from crispy_forms_gds.layout import AccordionSection
from crispy_forms_gds.layout import Div
from crispy_forms_gds.layout import Field
from crispy_forms_gds.layout import Layout
Expand All @@ -14,6 +12,7 @@

from common.forms import ValidityPeriodForm
from common.serializers import deserialize_date
from common.widgets import DecimalSuffix
from measures.models import MeasurementUnit
from quotas import business_rules
from quotas import models
Expand Down Expand Up @@ -145,6 +144,7 @@ class QuotaDefinitionCreateForm(
class Meta:
model = models.QuotaDefinition
fields = [
"order_number",
"valid_between",
"description",
"volume",
Expand All @@ -156,9 +156,14 @@ class Meta:
"maximum_precision",
]

order_number = forms.ModelChoiceField(
queryset=models.QuotaOrderNumber.objects.current(),
widget=forms.HiddenInput(),
)
description = forms.CharField(label="", widget=forms.Textarea(), required=False)
volume = forms.DecimalField(
label="Current volume",
help_text="The current volume is the starting balance for the quota",
widget=forms.TextInput(),
error_messages={
"invalid": "Volume must be a number",
Expand All @@ -167,20 +172,22 @@ class Meta:
)
initial_volume = forms.DecimalField(
widget=forms.TextInput(),
help_text="The initial volume is the legal balance applied to the definition period",
error_messages={
"invalid": "Initial volume must be a number",
"required": "Enter the initial volume",
},
)
measurement_unit = forms.ModelChoiceField(
queryset=MeasurementUnit.objects.current(),
empty_label="Choose measurement unit",
error_messages={"required": "Select the measurement unit"},
)

quota_critical_threshold = forms.DecimalField(
label="Threshold",
widget=DecimalSuffix(suffix="%"),
help_text="The point at which this quota definition period becomes critical, as a percentage of the total volume.",
widget=forms.TextInput(),
error_messages={
"invalid": "Critical threshold must be a number",
"required": "Enter the critical threshold",
Expand All @@ -199,13 +206,21 @@ class Meta:
)

def __init__(self, *args, **kwargs):
self.buttons = kwargs.pop("buttons", None)
self.order_number = kwargs.pop("order_number", None)
super().__init__(*args, **kwargs)
self.init_layout()
self.init_fields()

def clean(self):
cleaned_data = super().clean()
"""The "order_number" field is hidden, but is not correctly populated by
.initial when creating a single QuotaDefinitionPeriod."""
if "order_number" in self.errors:
self.cleaned_data["order_number"] = self.order_number
self.errors.pop("order_number")
validators.validate_quota_volume(self.cleaned_data)
return super().clean()
return cleaned_data

def init_fields(self):
# This is always set to 3 for current definitions
Expand All @@ -230,50 +245,87 @@ def init_fields(self):
lambda obj: f"{obj.code} - {obj.description}"
)

def init_layout(self):
self.fields["end_date"].help_text = ""
self.fields["end_date"].required = True
self.fields["measurement_unit_qualifier"].help_text = (
"A measurement unit qualifier is not always required."
)
self.fields["measurement_unit_qualifier"].empty_label = (
"Choose measurement unit qualifier."
)

def init_layout(self, *args, **kwargs):
self.helper = FormHelper(self)
self.helper.label_size = Size.SMALL
self.helper.legend_size = Size.SMALL

link_text = self.buttons["link_text"]
link = self.buttons["link"]
self.helper.layout = Layout(
Accordion(
AccordionSection(
"Description",
HTML.p("Adding a description is optional."),
"description",
"order_number",
Div(
HTML(
'<h3 class="govuk-heading">Validity period</h3>',
),
AccordionSection(
"Validity period",
"start_date",
"end_date",
"start_date",
"end_date",
),
HTML(
"<br />",
),
Div(
HTML(
'<h3 class="govuk-heading">Measurements</h3>',
),
AccordionSection(
"Measurements",
HTML.p("A measurement unit qualifier is not always required."),
Field("measurement_unit", css_class="govuk-!-width-full"),
Field("measurement_unit_qualifier", css_class="govuk-!-width-full"),
Field("measurement_unit", css_class="govuk-!-width-two-thirds"),
Field(
"measurement_unit_qualifier",
css_class="govuk-!-width-two-thirds",
),
AccordionSection(
"Volume",
HTML.p(
"The initial volume is the legal balance applied to the definition period.<br><br>The current volume is the starting balance for the quota.",
),
"initial_volume",
"volume",
"maximum_precision",
),
HTML(
"<br />",
),
Div(
HTML(
'<h3 class="govuk-heading">Volume</h3>',
),
Field("initial_volume", css_class="govuk-!-width-one-third"),
Field("volume", css_class="govuk-!-width-one-third"),
"maximum_precision",
),
HTML(
"<br />",
),
Div(
HTML(
'<h3 class="govuk-heading">Criticality</h3>',
),
AccordionSection(
"Criticality",
"quota_critical_threshold",
"quota_critical",
"quota_critical_threshold",
"quota_critical",
),
HTML(
"<br />",
),
Div(
HTML(
'<h3 class="govuk-heading">Description</h3>',
),
HTML.p("Adding a description is optional."),
Field("description", css_class="govuk-!-width-two-thirds"),
),
Submit(
"submit",
"Save",
data_module="govuk-button",
data_prevent_double_click="true",
HTML(
"<br />",
),
Div(
Submit(
"submit",
self.buttons["submit"],
data_module="govuk-button",
data_prevent_double_click="true",
),
HTML(
f'<a class="govuk-link" href={link}>{link_text}</a>',
),
css_class="govuk-button-group",
),
)

Expand Down
Loading

0 comments on commit 1e6b416

Please sign in to comment.