2022-08-18 10:08:51 +02:00
|
|
|
"""
|
|
|
|
Author: Michel Peltriaux
|
|
|
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
|
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
|
|
|
Created on: 18.08.22
|
|
|
|
|
|
|
|
"""
|
|
|
|
from dal import autocomplete
|
|
|
|
from django import forms
|
|
|
|
from django.db import transaction
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
|
|
|
|
from compensation.models import EcoAccount, EcoAccountDeduction
|
|
|
|
from intervention.models import Intervention
|
|
|
|
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
|
|
|
from konova.utils.general import format_german_float
|
|
|
|
from konova.utils.message_templates import DEDUCTION_ADDED, DEDUCTION_REMOVED, DEDUCTION_EDITED
|
|
|
|
from user.models import UserActionLogEntry
|
|
|
|
|
|
|
|
|
|
|
|
class NewEcoAccountDeductionModalForm(BaseModalForm):
|
|
|
|
""" Form for creating new deduction
|
|
|
|
|
|
|
|
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 deductions"),
|
|
|
|
queryset=EcoAccount.objects.filter(deleted=None),
|
|
|
|
widget=autocomplete.ModelSelect2(
|
2022-08-18 11:25:06 +02:00
|
|
|
url="compensation:acc:autocomplete",
|
2022-08-18 10:08:51 +02:00
|
|
|
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²"),
|
|
|
|
widget=forms.NumberInput(
|
|
|
|
attrs={
|
|
|
|
"class": "form-control",
|
|
|
|
"placeholder": "0,00",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
intervention = forms.ModelChoiceField(
|
|
|
|
label=_("Intervention"),
|
|
|
|
label_suffix="",
|
|
|
|
help_text=_("Only shared interventions can be selected"),
|
|
|
|
queryset=Intervention.objects.filter(deleted=None),
|
|
|
|
widget=autocomplete.ModelSelect2(
|
2022-08-18 11:25:06 +02:00
|
|
|
url="intervention:autocomplete",
|
2022-08-18 10:08:51 +02:00
|
|
|
attrs={
|
|
|
|
"data-placeholder": _("Intervention"),
|
|
|
|
"data-minimum-input-length": 3,
|
|
|
|
}
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.form_title = _("New Deduction")
|
|
|
|
self.form_caption = _("Enter the information for a new deduction from a chosen eco-account")
|
|
|
|
|
|
|
|
# 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")
|
|
|
|
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 _get_available_surface(self, acc):
|
|
|
|
""" Calculates how much available surface is left on the account
|
|
|
|
|
|
|
|
Args:
|
|
|
|
acc (EcoAccount):
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
"""
|
|
|
|
# Calculate valid surface
|
|
|
|
deductable_surface = acc.deductable_surface
|
|
|
|
sum_surface_deductions = acc.get_deductions_surface()
|
|
|
|
rest_surface = deductable_surface - sum_surface_deductions
|
|
|
|
return rest_surface
|
|
|
|
|
|
|
|
def is_valid(self):
|
|
|
|
""" Custom validity check
|
|
|
|
|
|
|
|
Makes sure the deduction can not contain more surface than the account still provides
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
is_valid (bool)
|
|
|
|
"""
|
|
|
|
super_result = super().is_valid()
|
|
|
|
acc = self.cleaned_data["account"]
|
|
|
|
intervention = self.cleaned_data["intervention"]
|
|
|
|
objects_valid = True
|
|
|
|
|
|
|
|
if not acc.recorded:
|
|
|
|
self.add_error(
|
|
|
|
"account",
|
|
|
|
_("Eco-account {} is not recorded yet. You can only deduct from recorded accounts.").format(acc.identifier)
|
|
|
|
)
|
|
|
|
objects_valid = False
|
|
|
|
|
|
|
|
if intervention.is_recorded:
|
|
|
|
self.add_error(
|
|
|
|
"intervention",
|
|
|
|
_("Intervention {} is currently recorded. To change any data on it, the entry must be unrecorded.").format(intervention.identifier)
|
|
|
|
)
|
|
|
|
objects_valid = False
|
|
|
|
|
|
|
|
rest_surface = self._get_available_surface(acc)
|
|
|
|
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 deduction of {} m². There are only {} m² left").format(
|
|
|
|
acc.identifier,
|
|
|
|
format_german_float(form_surface),
|
|
|
|
format_german_float(rest_surface),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
return is_valid_surface and objects_valid and super_result
|
|
|
|
|
|
|
|
def __create_deduction(self):
|
|
|
|
""" Creates the deduction
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
"""
|
|
|
|
with transaction.atomic():
|
|
|
|
user_action_create = UserActionLogEntry.get_created_action(self.user)
|
|
|
|
deduction = EcoAccountDeduction.objects.create(
|
|
|
|
intervention=self.cleaned_data["intervention"],
|
|
|
|
account=self.cleaned_data["account"],
|
|
|
|
surface=self.cleaned_data["surface"],
|
|
|
|
created=user_action_create,
|
|
|
|
)
|
|
|
|
return deduction
|
|
|
|
|
|
|
|
def save(self):
|
|
|
|
deduction = self.__create_deduction()
|
|
|
|
self.cleaned_data["intervention"].mark_as_edited(self.user, edit_comment=DEDUCTION_ADDED)
|
|
|
|
self.cleaned_data["account"].mark_as_edited(self.user, edit_comment=DEDUCTION_ADDED)
|
|
|
|
return deduction
|
|
|
|
|
2022-10-11 16:32:12 +02:00
|
|
|
def check_for_recorded_instance(self):
|
|
|
|
# Ignore super() implementation
|
|
|
|
return
|
|
|
|
|
2022-08-18 10:08:51 +02:00
|
|
|
|
|
|
|
class EditEcoAccountDeductionModalForm(NewEcoAccountDeductionModalForm):
|
|
|
|
deduction = None
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.deduction = kwargs.pop("deduction", None)
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.form_title = _("Edit Deduction")
|
|
|
|
form_data = {
|
|
|
|
"account": self.deduction.account,
|
|
|
|
"intervention": self.deduction.intervention,
|
|
|
|
"surface": self.deduction.surface,
|
|
|
|
}
|
|
|
|
self.load_initial_data(form_data)
|
|
|
|
|
|
|
|
def _get_available_surface(self, acc):
|
|
|
|
rest_surface = super()._get_available_surface(acc)
|
|
|
|
# Increase available surface by the currently deducted surface, so we can 'deduct' the same amount again or
|
|
|
|
# increase the surface only a little, which will still be valid.
|
|
|
|
# Example: 200 m² left, 500 m² deducted. Entering 700 m² would fail if we would not add the 500 m² to the available
|
|
|
|
# surface again.
|
|
|
|
rest_surface += self.deduction.surface
|
|
|
|
return rest_surface
|
|
|
|
|
|
|
|
def save(self):
|
|
|
|
deduction = self.deduction
|
|
|
|
form_account = self.cleaned_data.get("account", None)
|
|
|
|
form_intervention = self.cleaned_data.get("intervention", None)
|
|
|
|
old_account = deduction.account
|
|
|
|
old_intervention = deduction.intervention
|
|
|
|
old_surface = deduction.surface
|
|
|
|
|
|
|
|
# If account or intervention has been changed, we put that change in the logs just as if the deduction has
|
|
|
|
# been removed for this entry. Act as if the deduction is newly created for the new entries
|
|
|
|
if old_account != form_account:
|
|
|
|
old_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_REMOVED)
|
|
|
|
form_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_ADDED)
|
|
|
|
else:
|
|
|
|
old_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_EDITED)
|
|
|
|
|
|
|
|
if old_intervention != form_intervention:
|
|
|
|
old_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_REMOVED)
|
|
|
|
form_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_ADDED)
|
|
|
|
else:
|
|
|
|
old_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_EDITED)
|
|
|
|
|
|
|
|
deduction.account = form_account
|
|
|
|
deduction.intervention = self.cleaned_data.get("intervention", None)
|
|
|
|
deduction.surface = self.cleaned_data.get("surface", None)
|
|
|
|
deduction.save()
|
|
|
|
|
|
|
|
data_changes = {
|
|
|
|
"surface": {
|
|
|
|
"old": old_surface,
|
|
|
|
"new": deduction.surface,
|
|
|
|
},
|
|
|
|
"intervention": {
|
|
|
|
"old": old_intervention.identifier,
|
|
|
|
"new": deduction.intervention.identifier,
|
|
|
|
},
|
|
|
|
"account": {
|
|
|
|
"old": old_account.identifier,
|
|
|
|
"new": deduction.account.identifier,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
old_account.send_notification_mail_on_deduction_change(data_changes)
|
|
|
|
return deduction
|
|
|
|
|
2022-10-11 16:32:12 +02:00
|
|
|
def check_for_recorded_instance(self):
|
|
|
|
"""
|
|
|
|
Extension to super class base method
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
|
|
"""
|
|
|
|
if self.deduction.intervention.is_recorded:
|
|
|
|
self.block_form()
|
|
|
|
|
2022-08-18 10:08:51 +02:00
|
|
|
|
|
|
|
class RemoveEcoAccountDeductionModalForm(RemoveModalForm):
|
|
|
|
""" Removing modal form for EcoAccountDeduction
|
|
|
|
|
|
|
|
Can be used for anything, where removing shall be confirmed by the user a second time.
|
|
|
|
|
|
|
|
"""
|
|
|
|
deduction = None
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
deduction = kwargs.pop("deduction", None)
|
|
|
|
self.deduction = deduction
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
def save(self):
|
|
|
|
with transaction.atomic():
|
|
|
|
self.deduction.intervention.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
|
|
|
|
self.deduction.account.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
|
2022-10-11 16:32:12 +02:00
|
|
|
self.deduction.delete()
|
|
|
|
|
|
|
|
def check_for_recorded_instance(self):
|
|
|
|
if self.deduction.intervention.is_recorded:
|
|
|
|
self.block_form()
|