#7 New Form
* adds NewEcoAccountForm * refactors NewCompensationForm into AbstractCompensationForm so main fields can be reused again * fixes template bug in account detail view where the amount of deductions has been displayed instead of the available rest * refactors _generate_new_identifier() into generate_new_identifier() * refactors get_available_rest() into returning both, the total and relative amount * improves saving of SimpleGeometryForm() * adds/updates translations
This commit is contained in:
@@ -11,6 +11,7 @@ from compensation.views.eco_account_views import *
|
||||
urlpatterns = [
|
||||
path("", index_view, name="acc-index"),
|
||||
path('new/', new_view, name='acc-new'),
|
||||
path('new/id', new_id_view, name='acc-new-id'),
|
||||
path('<id>', open_view, name='acc-open'),
|
||||
path('<id>/log', log_view, name='acc-log'),
|
||||
path('<id>/record', record_view, name='acc-record'),
|
||||
|
||||
@@ -13,18 +13,18 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django import forms
|
||||
|
||||
from codelist.models import KonovaCode
|
||||
from codelist.settings import CODELIST_COMPENSATION_FUNDING_ID
|
||||
from compensation.models import Compensation
|
||||
from codelist.settings import CODELIST_COMPENSATION_FUNDING_ID, CODELIST_CONSERVATION_OFFICE_ID
|
||||
from compensation.models import Compensation, EcoAccount
|
||||
from intervention.inputs import GenerateInput
|
||||
from intervention.models import Intervention
|
||||
from intervention.models import Intervention, ResponsibilityData
|
||||
from konova.forms import BaseForm, SimpleGeomForm
|
||||
from user.models import UserActionLogEntry, UserAction
|
||||
|
||||
|
||||
class NewCompensationForm(BaseForm):
|
||||
""" Form for creating new compensations.
|
||||
class AbstractCompensationForm(BaseForm):
|
||||
""" Abstract form for compensations
|
||||
|
||||
Can be initialized with an intervention id for preselecting the related intervention.
|
||||
Holds all important form fields, which are used in compensation and eco account forms
|
||||
|
||||
"""
|
||||
identifier = forms.CharField(
|
||||
@@ -35,7 +35,7 @@ class NewCompensationForm(BaseForm):
|
||||
widget=GenerateInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"url": reverse_lazy("compensation:new-id"),
|
||||
"url": None, # Needs to be set in inheriting constructors
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -51,21 +51,6 @@ class NewCompensationForm(BaseForm):
|
||||
}
|
||||
)
|
||||
)
|
||||
intervention = forms.ModelChoiceField(
|
||||
label=_("compensates intervention"),
|
||||
label_suffix="",
|
||||
help_text=_("Select the intervention for which this compensation compensates"),
|
||||
queryset=Intervention.objects.filter(
|
||||
deleted=None,
|
||||
),
|
||||
widget=autocomplete.ModelSelect2(
|
||||
url="interventions-autocomplete",
|
||||
attrs={
|
||||
"data-placeholder": _("Intervention"),
|
||||
"data-minimum-input-length": 3,
|
||||
}
|
||||
),
|
||||
)
|
||||
fundings = forms.ModelMultipleChoiceField(
|
||||
label=_("Fundings"),
|
||||
label_suffix="",
|
||||
@@ -96,6 +81,41 @@ class NewCompensationForm(BaseForm):
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class NewCompensationForm(AbstractCompensationForm):
|
||||
""" Form for creating new compensations.
|
||||
|
||||
Can be initialized with an intervention id for preselecting the related intervention.
|
||||
|
||||
"""
|
||||
intervention = forms.ModelChoiceField(
|
||||
label=_("compensates intervention"),
|
||||
label_suffix="",
|
||||
help_text=_("Select the intervention for which this compensation compensates"),
|
||||
queryset=Intervention.objects.filter(
|
||||
deleted=None,
|
||||
),
|
||||
widget=autocomplete.ModelSelect2(
|
||||
url="interventions-autocomplete",
|
||||
attrs={
|
||||
"data-placeholder": _("Intervention"),
|
||||
"data-minimum-input-length": 3,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
# Define a field order for a nicer layout instead of running with the inheritance result
|
||||
field_order = [
|
||||
"identifier",
|
||||
"title",
|
||||
"intervention",
|
||||
"fundings",
|
||||
"comment",
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
intervention_id = kwargs.pop("intervention_id", None)
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -114,8 +134,9 @@ class NewCompensationForm(BaseForm):
|
||||
self.cancel_redirect = reverse("compensation:index")
|
||||
|
||||
tmp = Compensation()
|
||||
identifier = tmp._generate_new_identifier()
|
||||
identifier = tmp.generate_new_identifier()
|
||||
self.initialize_form_field("identifier", identifier)
|
||||
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:new-id")
|
||||
|
||||
def save(self, user: User, geom_form: SimpleGeomForm):
|
||||
with transaction.atomic():
|
||||
@@ -199,4 +220,125 @@ class EditCompensationForm(NewCompensationForm):
|
||||
self.instance.save()
|
||||
|
||||
self.instance.log.add(action)
|
||||
return self.instance
|
||||
return self.instance
|
||||
|
||||
|
||||
class NewEcoAccountForm(AbstractCompensationForm):
|
||||
conservation_office = forms.ModelChoiceField(
|
||||
label=_("Conservation office"),
|
||||
label_suffix="",
|
||||
help_text=_("Select the responsible office"),
|
||||
queryset=KonovaCode.objects.filter(
|
||||
is_archived=False,
|
||||
is_leaf=True,
|
||||
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
||||
),
|
||||
widget=autocomplete.ModelSelect2(
|
||||
url="codes-conservation-office-autocomplete",
|
||||
attrs={
|
||||
}
|
||||
),
|
||||
)
|
||||
conservation_file_number = forms.CharField(
|
||||
label=_("Conservation office file number"),
|
||||
label_suffix="",
|
||||
max_length=255,
|
||||
required=False,
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"placeholder": _("ETS-123/ABC.456"),
|
||||
"class": "form-control",
|
||||
}
|
||||
)
|
||||
)
|
||||
handler = forms.CharField(
|
||||
label=_("Eco-account handler"),
|
||||
label_suffix="",
|
||||
max_length=255,
|
||||
required=False,
|
||||
help_text=_("Who handles the eco-account"),
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"placeholder": _("Company Mustermann"),
|
||||
"class": "form-control",
|
||||
}
|
||||
)
|
||||
)
|
||||
surface = forms.DecimalField(
|
||||
min_value=0.00,
|
||||
decimal_places=2,
|
||||
label=_("Available Surface"),
|
||||
label_suffix="",
|
||||
help_text=_("The amount that can be used for deductions"),
|
||||
widget=forms.NumberInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
}
|
||||
)
|
||||
)
|
||||
field_order = [
|
||||
"identifier",
|
||||
"title",
|
||||
"conservation_office",
|
||||
"surface",
|
||||
"conservation_file_number",
|
||||
"handler",
|
||||
"fundings",
|
||||
"comment",
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.form_title = _("New Eco-Account")
|
||||
|
||||
self.action_url = reverse("compensation:acc-new")
|
||||
self.cancel_redirect = reverse("compensation:acc-index")
|
||||
|
||||
tmp = EcoAccount()
|
||||
identifier = tmp.generate_new_identifier()
|
||||
self.initialize_form_field("identifier", identifier)
|
||||
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:acc-new-id")
|
||||
self.fields["title"].widget.attrs["placeholder"] = _("Eco-Account XY; Location ABC")
|
||||
|
||||
def save(self, user: User, geom_form: SimpleGeomForm):
|
||||
with transaction.atomic():
|
||||
# Fetch data from cleaned POST values
|
||||
identifier = self.cleaned_data.get("identifier", None)
|
||||
title = self.cleaned_data.get("title", None)
|
||||
fundings = self.cleaned_data.get("fundings", None)
|
||||
handler = self.cleaned_data.get("handler", None)
|
||||
deductable_surface = self.cleaned_data.get("surface", None)
|
||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||
comment = self.cleaned_data.get("comment", None)
|
||||
|
||||
# Create log entry
|
||||
action = UserActionLogEntry.objects.create(
|
||||
user=user,
|
||||
action=UserAction.CREATED,
|
||||
)
|
||||
# Process the geometry form
|
||||
geometry = geom_form.save(action)
|
||||
|
||||
responsible = ResponsibilityData.objects.create(
|
||||
handler=handler,
|
||||
conservation_file_number=conservation_file_number,
|
||||
conservation_office=conservation_office,
|
||||
)
|
||||
|
||||
# Finally create main object
|
||||
acc = EcoAccount.objects.create(
|
||||
identifier=identifier,
|
||||
title=title,
|
||||
responsible=responsible,
|
||||
deductable_surface=deductable_surface,
|
||||
created=action,
|
||||
geometry=geometry,
|
||||
comment=comment,
|
||||
)
|
||||
acc.fundings.set(fundings)
|
||||
acc.users.add(user)
|
||||
|
||||
# Add the log entry to the main objects log list
|
||||
acc.log.add(action)
|
||||
return acc
|
||||
@@ -185,9 +185,9 @@ class Compensation(AbstractCompensation):
|
||||
def save(self, *args, **kwargs):
|
||||
if self.identifier is None or len(self.identifier) == 0:
|
||||
# Create new identifier
|
||||
new_id = self._generate_new_identifier()
|
||||
new_id = self.generate_new_identifier()
|
||||
while Compensation.objects.filter(identifier=new_id).exists():
|
||||
new_id = self._generate_new_identifier()
|
||||
new_id = self.generate_new_identifier()
|
||||
self.identifier = new_id
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@@ -322,9 +322,9 @@ class EcoAccount(AbstractCompensation):
|
||||
def save(self, *args, **kwargs):
|
||||
if self.identifier is None or len(self.identifier) == 0:
|
||||
# Create new identifier
|
||||
new_id = self._generate_new_identifier()
|
||||
new_id = self.generate_new_identifier()
|
||||
while EcoAccount.objects.filter(identifier=new_id).exists():
|
||||
new_id = self._generate_new_identifier()
|
||||
new_id = self.generate_new_identifier()
|
||||
self.identifier = new_id
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@@ -355,28 +355,28 @@ class EcoAccount(AbstractCompensation):
|
||||
"""
|
||||
return self.after_states.all().aggregate(Sum("surface"))["surface__sum"] or 0
|
||||
|
||||
def get_available_rest(self, as_percentage: bool = False) -> float:
|
||||
def get_available_rest(self) -> (float, float):
|
||||
""" Calculates available rest surface of the eco account
|
||||
|
||||
Args:
|
||||
as_percentage (bool): Whether to return the result as m² or %
|
||||
|
||||
Returns:
|
||||
|
||||
ret_val_total (float): Total amount
|
||||
ret_val_relative (float): Amount as percentage (0-100)
|
||||
"""
|
||||
deductions = self.deductions.filter(
|
||||
intervention__deleted=None,
|
||||
)
|
||||
deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||
available_surfaces = self.deductable_surface or deductions_surfaces ## no division by zero
|
||||
ret_val = available_surfaces - deductions_surfaces
|
||||
ret_val_total = available_surfaces - deductions_surfaces
|
||||
|
||||
if as_percentage:
|
||||
if available_surfaces > 0:
|
||||
ret_val = int((ret_val / available_surfaces) * 100)
|
||||
else:
|
||||
ret_val = 0
|
||||
return ret_val
|
||||
if available_surfaces > 0:
|
||||
ret_val_relative = int((ret_val_total / available_surfaces) * 100)
|
||||
else:
|
||||
ret_val_relative = 0
|
||||
|
||||
return ret_val_total, ret_val_relative
|
||||
|
||||
def get_LANIS_link(self) -> str:
|
||||
""" Generates a link for LANIS depending on the geometry
|
||||
|
||||
@@ -239,8 +239,8 @@ class EcoAccountTable(BaseTable):
|
||||
Returns:
|
||||
|
||||
"""
|
||||
value = record.get_available_rest(as_percentage=True)
|
||||
html = render_to_string("konova/custom_widgets/progressbar.html", {"value": value})
|
||||
value_total, value_relative = record.get_available_rest()
|
||||
html = render_to_string("konova/custom_widgets/progressbar.html", {"value": value_relative})
|
||||
return format_html(html)
|
||||
|
||||
def render_r(self, value, record: EcoAccount):
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<tr>
|
||||
<th scope="row">{% trans 'Available' %}</th>
|
||||
<td class="align-middle">
|
||||
{{obj.deductions_surface_sum|floatformat:2}} / {{obj.deductable_surface|floatformat:2}} m²
|
||||
{{available_total|floatformat:2}} / {{obj.deductable_surface|floatformat:2}} m²
|
||||
{% with available as value %}
|
||||
{% include 'konova/custom_widgets/progressbar.html' %}
|
||||
{% endwith %}
|
||||
|
||||
@@ -97,9 +97,9 @@ def new_id_view(request: HttpRequest):
|
||||
|
||||
"""
|
||||
tmp = Compensation()
|
||||
identifier = tmp._generate_new_identifier()
|
||||
identifier = tmp.generate_new_identifier()
|
||||
while Compensation.objects.filter(identifier=identifier).exists():
|
||||
identifier = tmp._generate_new_identifier()
|
||||
identifier = tmp.generate_new_identifier()
|
||||
return JsonResponse(
|
||||
data={
|
||||
"identifier": identifier
|
||||
|
||||
@@ -5,14 +5,16 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
||||
Created on: 09.08.21
|
||||
|
||||
"""
|
||||
from django.contrib import messages
|
||||
from django.db.models import Sum
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.http import HttpRequest, Http404
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.http import HttpRequest, Http404, JsonResponse
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
|
||||
from compensation.forms.forms import NewEcoAccountForm
|
||||
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
|
||||
from compensation.models import EcoAccount, EcoAccountDocument
|
||||
from compensation.tables import EcoAccountTable
|
||||
@@ -22,6 +24,7 @@ from konova.decorators import any_group_check, default_group_required, conservat
|
||||
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm
|
||||
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
||||
from konova.utils.documents import get_document, remove_document
|
||||
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID
|
||||
from konova.utils.user_checks import in_group
|
||||
|
||||
|
||||
@@ -56,8 +59,62 @@ def index_view(request: HttpRequest):
|
||||
@login_required
|
||||
@default_group_required
|
||||
def new_view(request: HttpRequest):
|
||||
# ToDo
|
||||
pass
|
||||
"""
|
||||
Renders a view for a new eco account creation
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
template = "compensation/new/view.html"
|
||||
data_form = NewEcoAccountForm(request.POST or None)
|
||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||
if request.method == "POST":
|
||||
if data_form.is_valid() and geom_form.is_valid():
|
||||
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
||||
acc = data_form.save(request.user, geom_form)
|
||||
if generated_identifier != acc.identifier:
|
||||
messages.info(
|
||||
request,
|
||||
IDENTIFIER_REPLACED.format(
|
||||
generated_identifier,
|
||||
acc.identifier
|
||||
)
|
||||
)
|
||||
messages.success(request, _("Eco-Account {} added").format(acc.identifier))
|
||||
return redirect("compensation:acc-open", id=acc.id)
|
||||
else:
|
||||
messages.error(request, FORM_INVALID)
|
||||
else:
|
||||
# For clarification: nothing in this case
|
||||
pass
|
||||
context = {
|
||||
"form": data_form,
|
||||
"geom_form": geom_form,
|
||||
"url": reverse("compensation:acc-new-id")
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
|
||||
|
||||
@login_required
|
||||
def new_id_view(request: HttpRequest):
|
||||
""" JSON endpoint
|
||||
|
||||
Provides fetching of free identifiers for e.g. AJAX calls
|
||||
|
||||
"""
|
||||
tmp = EcoAccount()
|
||||
identifier = tmp.generate_new_identifier()
|
||||
while EcoAccount.objects.filter(identifier=identifier).exists():
|
||||
identifier = tmp.generate_new_identifier()
|
||||
return JsonResponse(
|
||||
data={
|
||||
"identifier": identifier
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -96,7 +153,7 @@ def open_view(request: HttpRequest, id: str):
|
||||
diff_states = abs(sum_before_states - sum_after_states)
|
||||
|
||||
# Calculate rest of available surface for deductions
|
||||
available = acc.get_available_rest(as_percentage=True)
|
||||
available_total, available_relative = acc.get_available_rest()
|
||||
|
||||
deductions = acc.deductions.filter(
|
||||
intervention__deleted=None,
|
||||
@@ -111,7 +168,8 @@ def open_view(request: HttpRequest, id: str):
|
||||
"sum_before_states": sum_before_states,
|
||||
"sum_after_states": sum_after_states,
|
||||
"diff_states": diff_states,
|
||||
"available": available,
|
||||
"available": available_relative,
|
||||
"available_total": available_total,
|
||||
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
||||
"is_zb_member": in_group(_user, ZB_GROUP),
|
||||
"is_ets_member": in_group(_user, ETS_GROUP),
|
||||
|
||||
Reference in New Issue
Block a user