Intervention forms
* refactors intervention/forms and ../modalForms into individual files in separated packages * forms.py has been renamed into intervention.py, now can be found as intervention/forms/intervention.py * modalForms.py has been split into individual files living in modals package, can be found as intervention/forms/modals/...
This commit is contained in:
parent
ea7a53eb4f
commit
2f67c2f569
@ -50,7 +50,7 @@ class KonovaCode(models.Model):
|
|||||||
|
|
||||||
def __str__(self, with_parent: bool = True):
|
def __str__(self, with_parent: bool = True):
|
||||||
ret_val = ""
|
ret_val = ""
|
||||||
if self.parent and with_parent:
|
if self.parent and self.parent.long_name and with_parent:
|
||||||
ret_val += self.parent.long_name + " > "
|
ret_val += self.parent.long_name + " > "
|
||||||
ret_val += self.long_name
|
ret_val += self.long_name
|
||||||
if self.short_name and self.short_name != self.long_name:
|
if self.short_name and self.short_name != self.long_name:
|
||||||
|
@ -23,8 +23,9 @@ from compensation.forms.modals.state import NewCompensationStateModalForm, Remov
|
|||||||
EditCompensationStateModalForm
|
EditCompensationStateModalForm
|
||||||
from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction
|
from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction
|
||||||
from compensation.tables import EcoAccountTable
|
from compensation.tables import EcoAccountTable
|
||||||
from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm, \
|
from intervention.forms.modals.deduction import RemoveEcoAccountDeductionModalForm, NewEcoAccountDeductionModalForm, \
|
||||||
EditEcoAccountDeductionModalForm
|
EditEcoAccountDeductionModalForm
|
||||||
|
from intervention.forms.modals.share import ShareModalForm
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \
|
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \
|
||||||
shared_access_required
|
shared_access_required
|
||||||
@ -707,7 +708,7 @@ def new_deduction_view(request: HttpRequest, id: str):
|
|||||||
acc = get_object_or_404(EcoAccount, id=id)
|
acc = get_object_or_404(EcoAccount, id=id)
|
||||||
if not acc.recorded:
|
if not acc.recorded:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
form = NewDeductionModalForm(request.POST or None, instance=acc, request=request)
|
form = NewEcoAccountDeductionModalForm(request.POST or None, instance=acc, request=request)
|
||||||
return form.process_request(
|
return form.process_request(
|
||||||
request,
|
request,
|
||||||
msg_success=DEDUCTION_ADDED,
|
msg_success=DEDUCTION_ADDED,
|
||||||
|
@ -14,7 +14,7 @@ from compensation.forms.modals.state import NewCompensationStateModalForm, Remov
|
|||||||
from compensation.models import CompensationAction, CompensationState
|
from compensation.models import CompensationAction, CompensationState
|
||||||
from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentModalForm
|
from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentModalForm
|
||||||
from ema.tables import EmaTable
|
from ema.tables import EmaTable
|
||||||
from intervention.forms.modalForms import ShareModalForm
|
from intervention.forms.modals.share import ShareModalForm
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
from konova.decorators import conservation_office_group_required, shared_access_required
|
from konova.decorators import conservation_office_group_required, shared_access_required
|
||||||
from ema.models import Ema, EmaDocument
|
from ema.models import Ema, EmaDocument
|
||||||
|
@ -1,574 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
|
||||||
Created on: 27.09.21
|
|
||||||
|
|
||||||
"""
|
|
||||||
from dal import autocomplete
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
|
|
||||||
from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED, DEDUCTION_REMOVED, DEDUCTION_EDITED, \
|
|
||||||
REVOCATION_EDITED, ENTRY_REMOVE_MISSING_PERMISSION
|
|
||||||
from user.models import User, Team
|
|
||||||
from user.models import UserActionLogEntry
|
|
||||||
from django.db import transaction
|
|
||||||
from django import forms
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from compensation.models import EcoAccount, EcoAccountDeduction
|
|
||||||
from intervention.inputs import TextToClipboardInput
|
|
||||||
from intervention.models import Intervention, InterventionDocument, RevocationDocument
|
|
||||||
from konova.forms.modals import BaseModalForm
|
|
||||||
from konova.forms.modals import NewDocumentModalForm, RemoveModalForm
|
|
||||||
from konova.utils.general import format_german_float
|
|
||||||
from konova.utils.user_checks import is_default_group_only
|
|
||||||
|
|
||||||
|
|
||||||
class ShareModalForm(BaseModalForm):
|
|
||||||
url = forms.CharField(
|
|
||||||
label=_("Share link"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Send this link to users who you want to have writing access on the data"),
|
|
||||||
required=False,
|
|
||||||
widget=TextToClipboardInput(
|
|
||||||
attrs={
|
|
||||||
"readonly": True,
|
|
||||||
"class": "form-control",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
teams = forms.ModelMultipleChoiceField(
|
|
||||||
label=_("Add team to share with"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Multiple selection possible - You can only select teams which do not already have access."),
|
|
||||||
required=False,
|
|
||||||
queryset=Team.objects.all(),
|
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
|
||||||
url="share-team-autocomplete",
|
|
||||||
attrs={
|
|
||||||
"data-placeholder": _("Click for selection"),
|
|
||||||
"data-minimum-input-length": 3,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
users = forms.ModelMultipleChoiceField(
|
|
||||||
label=_("Add user to share with"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Multiple selection possible - You can only select users which do not already have access. Enter the full username."),
|
|
||||||
required=False,
|
|
||||||
queryset=User.objects.all(),
|
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
|
||||||
url="share-user-autocomplete",
|
|
||||||
attrs={
|
|
||||||
"data-placeholder": _("Click for selection"),
|
|
||||||
"data-minimum-input-length": 3,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Share")
|
|
||||||
self.form_caption = _("Share settings for {}").format(self.instance.identifier)
|
|
||||||
self.template = "modal/modal_form.html"
|
|
||||||
|
|
||||||
# Make sure an access_token is set
|
|
||||||
if self.instance.access_token is None:
|
|
||||||
self.instance.generate_access_token()
|
|
||||||
|
|
||||||
self._init_fields()
|
|
||||||
|
|
||||||
def _user_team_valid(self):
|
|
||||||
""" Checks whether users and teams have been removed by the user and if the user is allowed to do so or not
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
users = self.cleaned_data.get("users", User.objects.none())
|
|
||||||
teams = self.cleaned_data.get("teams", Team.objects.none())
|
|
||||||
|
|
||||||
_is_valid = True
|
|
||||||
if is_default_group_only(self.user):
|
|
||||||
shared_users = self.instance.shared_users
|
|
||||||
shared_teams = self.instance.shared_teams
|
|
||||||
|
|
||||||
shared_users_are_removed = not set(shared_users).issubset(users)
|
|
||||||
shared_teams_are_removed = not set(shared_teams).issubset(teams)
|
|
||||||
|
|
||||||
if shared_users_are_removed:
|
|
||||||
self.add_error(
|
|
||||||
"users",
|
|
||||||
ENTRY_REMOVE_MISSING_PERMISSION
|
|
||||||
)
|
|
||||||
_is_valid = False
|
|
||||||
if shared_teams_are_removed:
|
|
||||||
self.add_error(
|
|
||||||
"teams",
|
|
||||||
ENTRY_REMOVE_MISSING_PERMISSION
|
|
||||||
)
|
|
||||||
_is_valid = False
|
|
||||||
return _is_valid
|
|
||||||
|
|
||||||
def is_valid(self):
|
|
||||||
""" Extended validity check
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
super_valid = super().is_valid()
|
|
||||||
user_team_valid = self._user_team_valid()
|
|
||||||
_is_valid = super_valid and user_team_valid
|
|
||||||
return _is_valid
|
|
||||||
|
|
||||||
def _init_fields(self):
|
|
||||||
""" Wraps initializing of fields
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Initialize share_link field
|
|
||||||
share_link = self.instance.get_share_link()
|
|
||||||
self.share_link = self.request.build_absolute_uri(share_link)
|
|
||||||
self.initialize_form_field(
|
|
||||||
"url",
|
|
||||||
self.share_link
|
|
||||||
)
|
|
||||||
|
|
||||||
form_data = {
|
|
||||||
"teams": self.instance.teams.all(),
|
|
||||||
"users": self.instance.users.all(),
|
|
||||||
}
|
|
||||||
self.load_initial_data(form_data)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
self.instance.update_shared_access(self)
|
|
||||||
|
|
||||||
|
|
||||||
class NewRevocationModalForm(BaseModalForm):
|
|
||||||
date = forms.DateField(
|
|
||||||
label=_("Date"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
help_text=_("Date of revocation"),
|
|
||||||
widget=forms.DateInput(
|
|
||||||
attrs={
|
|
||||||
"type": "date",
|
|
||||||
"data-provide": "datepicker",
|
|
||||||
"class": "form-control",
|
|
||||||
},
|
|
||||||
format="%d.%m.%Y"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
file = forms.FileField(
|
|
||||||
label=_("Document"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
required=False,
|
|
||||||
help_text=_("Must be smaller than 15 Mb"),
|
|
||||||
widget=forms.FileInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control-file"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
comment = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
max_length=200,
|
|
||||||
label=_("Comment"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
help_text=_("Additional comment, maximum {} letters").format(200),
|
|
||||||
widget=forms.Textarea(
|
|
||||||
attrs={
|
|
||||||
"cols": 30,
|
|
||||||
"rows": 5,
|
|
||||||
"class": "form-control",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
document_model = RevocationDocument
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Add revocation")
|
|
||||||
self.form_caption = ""
|
|
||||||
self.form_attrs = {
|
|
||||||
"enctype": "multipart/form-data", # important for file upload
|
|
||||||
}
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
revocation = self.instance.add_revocation(self)
|
|
||||||
self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_ADDED)
|
|
||||||
return revocation
|
|
||||||
|
|
||||||
|
|
||||||
class EditRevocationModalForm(NewRevocationModalForm):
|
|
||||||
revocation = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.revocation = kwargs.pop("revocation", None)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Edit revocation")
|
|
||||||
try:
|
|
||||||
doc = self.revocation.document.file
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
doc = None
|
|
||||||
form_data = {
|
|
||||||
"date": str(self.revocation.date),
|
|
||||||
"file": doc,
|
|
||||||
"comment": self.revocation.comment,
|
|
||||||
}
|
|
||||||
self.load_initial_data(form_data)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
revocation = self.instance.edit_revocation(self)
|
|
||||||
self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_EDITED)
|
|
||||||
return revocation
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveRevocationModalForm(RemoveModalForm):
|
|
||||||
""" Removing modal form for Revocation
|
|
||||||
|
|
||||||
Can be used for anything, where removing shall be confirmed by the user a second time.
|
|
||||||
|
|
||||||
"""
|
|
||||||
revocation = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
revocation = kwargs.pop("revocation", None)
|
|
||||||
self.revocation = revocation
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
self.instance.remove_revocation(self)
|
|
||||||
|
|
||||||
|
|
||||||
class CheckModalForm(BaseModalForm):
|
|
||||||
""" The modal form for running a check on interventions and their compensations
|
|
||||||
|
|
||||||
"""
|
|
||||||
checked_intervention = forms.BooleanField(
|
|
||||||
label=_("Checked intervention data"),
|
|
||||||
label_suffix="",
|
|
||||||
widget=forms.CheckboxInput(),
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
checked_comps = forms.BooleanField(
|
|
||||||
label=_("Checked compensations data and payments"),
|
|
||||||
label_suffix="",
|
|
||||||
widget=forms.CheckboxInput(),
|
|
||||||
required=True
|
|
||||||
)
|
|
||||||
valid = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Run check")
|
|
||||||
self.form_caption = _("I, {} {}, confirm that all necessary control steps have been performed by myself.").format(self.user.first_name, self.user.last_name)
|
|
||||||
self.valid = False
|
|
||||||
|
|
||||||
def _are_deductions_valid(self):
|
|
||||||
""" Performs validity checks on deductions and their eco-account
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
deductions = self.instance.deductions.all()
|
|
||||||
for deduction in deductions:
|
|
||||||
checker = deduction.account.quality_check()
|
|
||||||
for msg in checker.messages:
|
|
||||||
self.add_error(
|
|
||||||
"checked_comps",
|
|
||||||
f"{deduction.account.identifier}: {msg}"
|
|
||||||
)
|
|
||||||
return checker.valid
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _are_comps_valid(self):
|
|
||||||
""" Performs validity checks on all types of compensations
|
|
||||||
|
|
||||||
Types of compensations are
|
|
||||||
* regular Compensations
|
|
||||||
* deductions from EcoAccounts
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comps = self.instance.compensations.filter(
|
|
||||||
deleted=None,
|
|
||||||
)
|
|
||||||
comps_valid = True
|
|
||||||
for comp in comps:
|
|
||||||
checker = comp.quality_check()
|
|
||||||
for msg in checker.messages:
|
|
||||||
self.add_error(
|
|
||||||
"checked_comps",
|
|
||||||
f"{comp.identifier}: {msg}"
|
|
||||||
)
|
|
||||||
comps_valid = checker.valid
|
|
||||||
deductions_valid = self._are_deductions_valid()
|
|
||||||
return deductions_valid and comps_valid
|
|
||||||
|
|
||||||
def is_valid(self) -> bool:
|
|
||||||
""" Perform a validity check based on quality_check() logic
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
result (bool)
|
|
||||||
"""
|
|
||||||
super_valid = super().is_valid()
|
|
||||||
# Perform check
|
|
||||||
checker = self.instance.quality_check()
|
|
||||||
for msg in checker.messages:
|
|
||||||
self.add_error(
|
|
||||||
"checked_intervention",
|
|
||||||
msg
|
|
||||||
)
|
|
||||||
all_comps_valid = self._are_comps_valid()
|
|
||||||
intervention_valid = checker.valid
|
|
||||||
|
|
||||||
return super_valid and intervention_valid and all_comps_valid
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
""" Saving logic
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
with transaction.atomic():
|
|
||||||
self.instance.set_checked(self.user)
|
|
||||||
|
|
||||||
|
|
||||||
class NewDeductionModalForm(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(
|
|
||||||
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²"),
|
|
||||||
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(
|
|
||||||
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 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
|
|
||||||
|
|
||||||
|
|
||||||
class EditEcoAccountDeductionModalForm(NewDeductionModalForm):
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
self.deduction.delete()
|
|
||||||
|
|
||||||
|
|
||||||
class NewInterventionDocumentModalForm(NewDocumentModalForm):
|
|
||||||
document_model = InterventionDocument
|
|
7
intervention/forms/modals/__init__.py
Normal file
7
intervention/forms/modals/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
107
intervention/forms/modals/check.py
Normal file
107
intervention/forms/modals/check.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django import forms
|
||||||
|
from django.db import transaction
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from konova.forms.modals import BaseModalForm
|
||||||
|
|
||||||
|
|
||||||
|
class CheckModalForm(BaseModalForm):
|
||||||
|
""" The modal form for running a check on interventions and their compensations
|
||||||
|
|
||||||
|
"""
|
||||||
|
checked_intervention = forms.BooleanField(
|
||||||
|
label=_("Checked intervention data"),
|
||||||
|
label_suffix="",
|
||||||
|
widget=forms.CheckboxInput(),
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
checked_comps = forms.BooleanField(
|
||||||
|
label=_("Checked compensations data and payments"),
|
||||||
|
label_suffix="",
|
||||||
|
widget=forms.CheckboxInput(),
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
valid = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Run check")
|
||||||
|
self.form_caption = _("I, {} {}, confirm that all necessary control steps have been performed by myself.").format(self.user.first_name, self.user.last_name)
|
||||||
|
self.valid = False
|
||||||
|
|
||||||
|
def _are_deductions_valid(self):
|
||||||
|
""" Performs validity checks on deductions and their eco-account
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
deductions = self.instance.deductions.all()
|
||||||
|
for deduction in deductions:
|
||||||
|
checker = deduction.account.quality_check()
|
||||||
|
for msg in checker.messages:
|
||||||
|
self.add_error(
|
||||||
|
"checked_comps",
|
||||||
|
f"{deduction.account.identifier}: {msg}"
|
||||||
|
)
|
||||||
|
return checker.valid
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _are_comps_valid(self):
|
||||||
|
""" Performs validity checks on all types of compensations
|
||||||
|
|
||||||
|
Types of compensations are
|
||||||
|
* regular Compensations
|
||||||
|
* deductions from EcoAccounts
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
comps = self.instance.compensations.filter(
|
||||||
|
deleted=None,
|
||||||
|
)
|
||||||
|
comps_valid = True
|
||||||
|
for comp in comps:
|
||||||
|
checker = comp.quality_check()
|
||||||
|
for msg in checker.messages:
|
||||||
|
self.add_error(
|
||||||
|
"checked_comps",
|
||||||
|
f"{comp.identifier}: {msg}"
|
||||||
|
)
|
||||||
|
comps_valid = checker.valid
|
||||||
|
deductions_valid = self._are_deductions_valid()
|
||||||
|
return deductions_valid and comps_valid
|
||||||
|
|
||||||
|
def is_valid(self) -> bool:
|
||||||
|
""" Perform a validity check based on quality_check() logic
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
result (bool)
|
||||||
|
"""
|
||||||
|
super_valid = super().is_valid()
|
||||||
|
# Perform check
|
||||||
|
checker = self.instance.quality_check()
|
||||||
|
for msg in checker.messages:
|
||||||
|
self.add_error(
|
||||||
|
"checked_intervention",
|
||||||
|
msg
|
||||||
|
)
|
||||||
|
all_comps_valid = self._are_comps_valid()
|
||||||
|
intervention_valid = checker.valid
|
||||||
|
|
||||||
|
return super_valid and intervention_valid and all_comps_valid
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
""" Saving logic
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
with transaction.atomic():
|
||||||
|
self.instance.set_checked(self.user)
|
252
intervention/forms/modals/deduction.py
Normal file
252
intervention/forms/modals/deduction.py
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
"""
|
||||||
|
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(
|
||||||
|
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²"),
|
||||||
|
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(
|
||||||
|
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 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
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
self.deduction.delete()
|
13
intervention/forms/modals/document.py
Normal file
13
intervention/forms/modals/document.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from intervention.models import InterventionDocument
|
||||||
|
from konova.forms.modals import NewDocumentModalForm
|
||||||
|
|
||||||
|
|
||||||
|
class NewInterventionDocumentModalForm(NewDocumentModalForm):
|
||||||
|
document_model = InterventionDocument
|
110
intervention/forms/modals/revocation.py
Normal file
110
intervention/forms/modals/revocation.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django import forms
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from intervention.models import RevocationDocument
|
||||||
|
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
||||||
|
from konova.utils.message_templates import REVOCATION_ADDED, REVOCATION_EDITED
|
||||||
|
|
||||||
|
|
||||||
|
class NewRevocationModalForm(BaseModalForm):
|
||||||
|
date = forms.DateField(
|
||||||
|
label=_("Date"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
help_text=_("Date of revocation"),
|
||||||
|
widget=forms.DateInput(
|
||||||
|
attrs={
|
||||||
|
"type": "date",
|
||||||
|
"data-provide": "datepicker",
|
||||||
|
"class": "form-control",
|
||||||
|
},
|
||||||
|
format="%d.%m.%Y"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
file = forms.FileField(
|
||||||
|
label=_("Document"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
required=False,
|
||||||
|
help_text=_("Must be smaller than 15 Mb"),
|
||||||
|
widget=forms.FileInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control-file"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
comment = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
max_length=200,
|
||||||
|
label=_("Comment"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
help_text=_("Additional comment, maximum {} letters").format(200),
|
||||||
|
widget=forms.Textarea(
|
||||||
|
attrs={
|
||||||
|
"cols": 30,
|
||||||
|
"rows": 5,
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
document_model = RevocationDocument
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Add revocation")
|
||||||
|
self.form_caption = ""
|
||||||
|
self.form_attrs = {
|
||||||
|
"enctype": "multipart/form-data", # important for file upload
|
||||||
|
}
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
revocation = self.instance.add_revocation(self)
|
||||||
|
self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_ADDED)
|
||||||
|
return revocation
|
||||||
|
|
||||||
|
|
||||||
|
class EditRevocationModalForm(NewRevocationModalForm):
|
||||||
|
revocation = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.revocation = kwargs.pop("revocation", None)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Edit revocation")
|
||||||
|
try:
|
||||||
|
doc = self.revocation.document.file
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
doc = None
|
||||||
|
form_data = {
|
||||||
|
"date": str(self.revocation.date),
|
||||||
|
"file": doc,
|
||||||
|
"comment": self.revocation.comment,
|
||||||
|
}
|
||||||
|
self.load_initial_data(form_data)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
revocation = self.instance.edit_revocation(self)
|
||||||
|
self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_EDITED)
|
||||||
|
return revocation
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveRevocationModalForm(RemoveModalForm):
|
||||||
|
""" Removing modal form for Revocation
|
||||||
|
|
||||||
|
Can be used for anything, where removing shall be confirmed by the user a second time.
|
||||||
|
|
||||||
|
"""
|
||||||
|
revocation = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
revocation = kwargs.pop("revocation", None)
|
||||||
|
self.revocation = revocation
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.instance.remove_revocation(self)
|
136
intervention/forms/modals/share.py
Normal file
136
intervention/forms/modals/share.py
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
"""
|
||||||
|
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.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from intervention.inputs import TextToClipboardInput
|
||||||
|
from konova.forms.modals import BaseModalForm
|
||||||
|
from konova.utils.message_templates import ENTRY_REMOVE_MISSING_PERMISSION
|
||||||
|
from konova.utils.user_checks import is_default_group_only
|
||||||
|
from user.models import Team, User
|
||||||
|
|
||||||
|
|
||||||
|
class ShareModalForm(BaseModalForm):
|
||||||
|
url = forms.CharField(
|
||||||
|
label=_("Share link"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Send this link to users who you want to have writing access on the data"),
|
||||||
|
required=False,
|
||||||
|
widget=TextToClipboardInput(
|
||||||
|
attrs={
|
||||||
|
"readonly": True,
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
teams = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Add team to share with"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Multiple selection possible - You can only select teams which do not already have access."),
|
||||||
|
required=False,
|
||||||
|
queryset=Team.objects.all(),
|
||||||
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
|
url="share-team-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Click for selection"),
|
||||||
|
"data-minimum-input-length": 3,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
users = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Add user to share with"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Multiple selection possible - You can only select users which do not already have access. Enter the full username."),
|
||||||
|
required=False,
|
||||||
|
queryset=User.objects.all(),
|
||||||
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
|
url="share-user-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Click for selection"),
|
||||||
|
"data-minimum-input-length": 3,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Share")
|
||||||
|
self.form_caption = _("Share settings for {}").format(self.instance.identifier)
|
||||||
|
self.template = "modal/modal_form.html"
|
||||||
|
|
||||||
|
# Make sure an access_token is set
|
||||||
|
if self.instance.access_token is None:
|
||||||
|
self.instance.generate_access_token()
|
||||||
|
|
||||||
|
self._init_fields()
|
||||||
|
|
||||||
|
def _user_team_valid(self):
|
||||||
|
""" Checks whether users and teams have been removed by the user and if the user is allowed to do so or not
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
users = self.cleaned_data.get("users", User.objects.none())
|
||||||
|
teams = self.cleaned_data.get("teams", Team.objects.none())
|
||||||
|
|
||||||
|
_is_valid = True
|
||||||
|
if is_default_group_only(self.user):
|
||||||
|
shared_users = self.instance.shared_users
|
||||||
|
shared_teams = self.instance.shared_teams
|
||||||
|
|
||||||
|
shared_users_are_removed = not set(shared_users).issubset(users)
|
||||||
|
shared_teams_are_removed = not set(shared_teams).issubset(teams)
|
||||||
|
|
||||||
|
if shared_users_are_removed:
|
||||||
|
self.add_error(
|
||||||
|
"users",
|
||||||
|
ENTRY_REMOVE_MISSING_PERMISSION
|
||||||
|
)
|
||||||
|
_is_valid = False
|
||||||
|
if shared_teams_are_removed:
|
||||||
|
self.add_error(
|
||||||
|
"teams",
|
||||||
|
ENTRY_REMOVE_MISSING_PERMISSION
|
||||||
|
)
|
||||||
|
_is_valid = False
|
||||||
|
return _is_valid
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
""" Extended validity check
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
super_valid = super().is_valid()
|
||||||
|
user_team_valid = self._user_team_valid()
|
||||||
|
_is_valid = super_valid and user_team_valid
|
||||||
|
return _is_valid
|
||||||
|
|
||||||
|
def _init_fields(self):
|
||||||
|
""" Wraps initializing of fields
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Initialize share_link field
|
||||||
|
share_link = self.instance.get_share_link()
|
||||||
|
self.share_link = self.request.build_absolute_uri(share_link)
|
||||||
|
self.initialize_form_field(
|
||||||
|
"url",
|
||||||
|
self.share_link
|
||||||
|
)
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
"teams": self.instance.teams.all(),
|
||||||
|
"users": self.instance.users.all(),
|
||||||
|
}
|
||||||
|
self.load_initial_data(form_data)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.instance.update_shared_access(self)
|
@ -4,10 +4,14 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from django.http import HttpRequest, JsonResponse, Http404
|
from django.http import HttpRequest, JsonResponse, Http404
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
|
||||||
from intervention.forms.forms import NewInterventionForm, EditInterventionForm
|
from intervention.forms.intervention import NewInterventionForm, EditInterventionForm
|
||||||
from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \
|
from intervention.forms.modals.check import CheckModalForm
|
||||||
CheckModalForm, NewDeductionModalForm, NewInterventionDocumentModalForm, RemoveEcoAccountDeductionModalForm, \
|
from intervention.forms.modals.deduction import NewEcoAccountDeductionModalForm, RemoveEcoAccountDeductionModalForm, \
|
||||||
RemoveRevocationModalForm, EditEcoAccountDeductionModalForm, EditRevocationModalForm
|
EditEcoAccountDeductionModalForm
|
||||||
|
from intervention.forms.modals.document import NewInterventionDocumentModalForm
|
||||||
|
from intervention.forms.modals.revocation import EditRevocationModalForm, RemoveRevocationModalForm, \
|
||||||
|
NewRevocationModalForm
|
||||||
|
from intervention.forms.modals.share import ShareModalForm
|
||||||
from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument
|
from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument
|
||||||
from intervention.tables import InterventionTable
|
from intervention.tables import InterventionTable
|
||||||
from konova.contexts import BaseContext
|
from konova.contexts import BaseContext
|
||||||
@ -583,7 +587,7 @@ def new_deduction_view(request: HttpRequest, id: str):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
form = NewDeductionModalForm(request.POST or None, instance=intervention, request=request)
|
form = NewEcoAccountDeductionModalForm(request.POST or None, instance=intervention, request=request)
|
||||||
return form.process_request(
|
return form.process_request(
|
||||||
request,
|
request,
|
||||||
msg_success=DEDUCTION_ADDED,
|
msg_success=DEDUCTION_ADDED,
|
||||||
|
@ -134,7 +134,7 @@ class BaseForm(forms.Form):
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from intervention.forms.modalForms import NewDeductionModalForm, EditEcoAccountDeductionModalForm, \
|
from intervention.forms.modals.deduction import NewEcoAccountDeductionModalForm, EditEcoAccountDeductionModalForm, \
|
||||||
RemoveEcoAccountDeductionModalForm
|
RemoveEcoAccountDeductionModalForm
|
||||||
from konova.forms.modals.resubmission_form import ResubmissionModalForm
|
from konova.forms.modals.resubmission_form import ResubmissionModalForm
|
||||||
is_none = self.instance is None
|
is_none = self.instance is None
|
||||||
@ -142,7 +142,7 @@ class BaseForm(forms.Form):
|
|||||||
is_deduction_form_from_account = isinstance(
|
is_deduction_form_from_account = isinstance(
|
||||||
self,
|
self,
|
||||||
(
|
(
|
||||||
NewDeductionModalForm,
|
NewEcoAccountDeductionModalForm,
|
||||||
ResubmissionModalForm,
|
ResubmissionModalForm,
|
||||||
EditEcoAccountDeductionModalForm,
|
EditEcoAccountDeductionModalForm,
|
||||||
RemoveEcoAccountDeductionModalForm,
|
RemoveEcoAccountDeductionModalForm,
|
||||||
|
Loading…
Reference in New Issue
Block a user