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:
@@ -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
|
||||
@@ -14,7 +14,7 @@
|
||||
Only show add-button if no revocation exists, yet.
|
||||
{% endcomment %}
|
||||
{% if is_default_member and has_access and not intervention.legal.revocation %}
|
||||
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:revocation-new' intervention.id %}" title="{% trans 'Add revocation' %}">
|
||||
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:new-revocation' intervention.id %}" title="{% trans 'Add revocation' %}">
|
||||
{% fa5_icon 'plus' %}
|
||||
{% fa5_icon 'ban' %}
|
||||
</button>
|
||||
@@ -56,7 +56,7 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if is_default_member and has_access %}
|
||||
<button data-form-url="{% url 'intervention:revocation-remove' rev.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove revocation' %}">
|
||||
<button data-form-url="{% url 'intervention:remove-revocation' rev.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove revocation' %}">
|
||||
{% fa5_icon 'trash' %}
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
@@ -11,12 +11,10 @@
|
||||
<div class="col-sm-6">
|
||||
<div class="d-flex justify-content-end">
|
||||
{% if is_default_member and has_access %}
|
||||
<a href="{% url 'compensation:new' %}" title="{% trans 'Add new withdraw' %}">
|
||||
<button class="btn btn-outline-default">
|
||||
{% fa5_icon 'plus' %}
|
||||
{% fa5_icon 'tree' %}
|
||||
</button>
|
||||
</a>
|
||||
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:acc-new-withdraw' intervention.id %}" title="{% trans 'Add new withdraw' %}">
|
||||
{% fa5_icon 'plus' %}
|
||||
{% fa5_icon 'tree' %}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,6 +30,9 @@
|
||||
<th scope="col">
|
||||
{% trans 'Amount' %}
|
||||
</th>
|
||||
<th scope="col">
|
||||
{% trans 'Created' %}
|
||||
</th>
|
||||
<th scope="col">
|
||||
{% trans 'Action' %}
|
||||
</th>
|
||||
@@ -49,6 +50,7 @@
|
||||
</a>
|
||||
</td>
|
||||
<td class="align-middle">{{ withdraw.surface|floatformat:2|intcomma }} m²</td>
|
||||
<td class="align-middle">{{ withdraw.created.timestamp|default_if_none:""|naturalday}}</td>
|
||||
<td>
|
||||
{% if is_default_member and has_access %}
|
||||
<button data-form-url="{% url 'compensation:withdraw-remove' withdraw.account.id withdraw.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove Withdraw' %}">
|
||||
|
||||
@@ -8,7 +8,7 @@ Created on: 30.11.20
|
||||
from django.urls import path
|
||||
|
||||
from intervention.views import index_view, new_view, open_view, edit_view, remove_view, new_document_view, share_view, \
|
||||
create_share_view, remove_revocation_view, new_revocation_view, run_check_view, log_view
|
||||
create_share_view, remove_revocation_view, new_revocation_view, run_check_view, log_view, new_withdraw_view
|
||||
|
||||
app_name = "intervention"
|
||||
urlpatterns = [
|
||||
@@ -23,7 +23,10 @@ urlpatterns = [
|
||||
path('<id>/share', create_share_view, name='share-create'),
|
||||
path('<id>/check', run_check_view, name='run-check'),
|
||||
|
||||
# Withdraws
|
||||
path('<id>/withdraw/new', new_withdraw_view, name='acc-new-withdraw'),
|
||||
|
||||
# Revocation routes
|
||||
path('<id>/revocation/new', new_revocation_view, name='revocation-new'),
|
||||
path('revocation/<id>/remove', remove_revocation_view, name='revocation-remove'),
|
||||
path('<id>/revocation/new', new_revocation_view, name='new-revocation'),
|
||||
path('revocation/<id>/remove', remove_revocation_view, name='remove-revocation'),
|
||||
]
|
||||
@@ -5,7 +5,7 @@ from django.http import HttpRequest
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
|
||||
from intervention.forms import NewInterventionForm, EditInterventionForm, ShareInterventionForm, NewRevocationForm, \
|
||||
RunCheckForm
|
||||
RunCheckForm, NewWithdrawForm
|
||||
from intervention.models import Intervention, Revocation
|
||||
from intervention.tables import InterventionTable
|
||||
from konova.contexts import BaseContext
|
||||
@@ -392,3 +392,23 @@ def log_view(request: HttpRequest, id: str):
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, template, context)
|
||||
|
||||
|
||||
@login_required
|
||||
@default_group_required
|
||||
def new_withdraw_view(request: HttpRequest, id: str):
|
||||
""" Renders a modal form view for creating withdraws
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The intervention's id which shall get a new withdraw
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
intervention = get_object_or_404(Intervention, id=id)
|
||||
form = NewWithdrawForm(request.POST or None, instance=intervention, user=request.user)
|
||||
return form.process_request(
|
||||
request,
|
||||
msg_success=_("Withdraw added")
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user