EcoAccount withdraws

* adds functionality for withdraws from eco accounts in detail view of interventions and eco account as well
* adds get_surface() method to AbstractCompensation class to provide a simple getter for a sql calculation
* adds get_surface_withdraws() method to EcoAccount class to provide a simple getter for a sql calculation
* renames some routes to match coherent rout naming
* adds logic check on NewWithdrawForm
* renames templates/table directory to templates/form, since there are form-table templates inside --> more clarity
* adds new autocomplete routes to konova/urls.py for Interventions and EcoAccounts
* adds/updates translations
* adds/updates template comments
* updates requirements.txt
This commit is contained in:
mipel
2021-08-10 10:42:04 +02:00
parent 17d697da92
commit 72d61a23da
21 changed files with 388 additions and 121 deletions
+124
View File
@@ -14,6 +14,7 @@ from django.db import transaction
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from compensation.models import EcoAccountWithdraw, EcoAccount
from intervention.models import Intervention, Revocation
from konova.forms import BaseForm, BaseModalForm
from konova.models import Document
@@ -423,3 +424,126 @@ class RunCheckForm(BaseModalForm):
self.instance.checked = user_action
self.instance.log.add(user_action)
self.instance.save()
class NewWithdrawForm(BaseModalForm):
""" Form for creating new withdraws
Can be used for Intervention view as well as for EcoAccount views.
Parameter 'instance' can be an intervention, as well as an ecoAccount.
An instance check handles both workflows properly.
"""
account = forms.ModelChoiceField(
label=_("Eco-account"),
label_suffix="",
help_text=_("Only recorded accounts can be selected for withdraws"),
queryset=EcoAccount.objects.filter(deleted=None, recorded__isnull=False),
widget=autocomplete.ModelSelect2(
url="accounts-autocomplete",
attrs={
"data-placeholder": _("Eco-account"),
"data-minimum-input-length": 3,
"readonly": True,
}
),
)
surface = forms.DecimalField(
min_value=0.00,
decimal_places=2,
label=_("Surface"),
label_suffix="",
help_text=_("in m²"),
)
intervention = forms.ModelChoiceField(
label=_("Intervention"),
label_suffix="",
help_text=_("Only shared interventions can be selected"),
queryset=Intervention.objects.filter(deleted=None),
widget=autocomplete.ModelSelect2(
url="interventions-autocomplete",
attrs={
"data-placeholder": _("Intervention"),
"data-minimum-input-length": 3,
}
),
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_title = _("New Withdraw")
self.form_caption = _("Enter the information for a new withdraw from a chosen eco-account")
self.is_intervention_initially = False
# Add a placeholder for field 'surface' without having to define the whole widget above
self.fields["surface"].widget.attrs["placeholder"] = "0,00"
# Check for Intervention or EcoAccount
if isinstance(self.instance, Intervention):
# Form has been called with a given intervention
self.initialize_form_field("intervention", self.instance)
self.disable_form_field("intervention")
self.is_intervention_initially = True
elif isinstance(self.instance, EcoAccount):
# Form has been called with a given account --> make it initial in the form and read-only
self.initialize_form_field("account", self.instance)
self.disable_form_field("account")
else:
raise NotImplementedError
def is_valid(self):
""" Custom validity check
Makes sure the withdraw can not contain more surface than the account still provides
Returns:
is_valid (bool)
"""
super_result = super().is_valid()
if self.is_intervention_initially:
acc = self.cleaned_data["account"]
else:
acc = self.instance
sum_surface = acc.get_surface()
sum_surface_withdraws = acc.get_surface_withdraws()
rest_surface = sum_surface - sum_surface_withdraws
form_surface = float(self.cleaned_data["surface"])
is_valid_surface = form_surface < rest_surface
if not is_valid_surface:
self.add_error(
"surface",
_("The account {} has not enough surface for a withdraw of {} m². There are only {} m² left").format(acc.identifier, form_surface, rest_surface),
)
return is_valid_surface and super_result
def save(self):
with transaction.atomic():
# Create log entry
user_action_edit = UserActionLogEntry.objects.create(
user=self.user,
action=UserAction.EDITED
)
user_action_create = UserActionLogEntry.objects.create(
user=self.user,
action=UserAction.CREATED
)
self.instance.log.add(user_action_edit)
# Create withdraw depending on Intervention or EcoAccount as the initial instance
if self.is_intervention_initially:
withdraw = EcoAccountWithdraw.objects.create(
intervention=self.instance,
account=self.cleaned_data["account"],
surface=self.cleaned_data["surface"],
created=user_action_create,
)
else:
withdraw = EcoAccountWithdraw.objects.create(
intervention=self.cleaned_data["intervention"],
account=self.instance,
surface=self.cleaned_data["surface"],
created=user_action_create,
)
return withdraw