#7 New forms
* refactors large forms.py into forms/modalForms.py and forms/forms.py * refactors custom input fields into intervention/inputs.py
This commit is contained in:
parent
e94dbbd3c8
commit
877a1ca335
@ -16,7 +16,7 @@ from django.shortcuts import render, get_object_or_404
|
|||||||
from compensation.forms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
|
from compensation.forms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
|
||||||
from compensation.models import EcoAccount, EcoAccountDocument
|
from compensation.models import EcoAccount, EcoAccountDocument
|
||||||
from compensation.tables import EcoAccountTable
|
from compensation.tables import EcoAccountTable
|
||||||
from intervention.forms import NewDeductionForm
|
from intervention.forms.modalForms import NewDeductionModalForm
|
||||||
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
|
||||||
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm
|
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm
|
||||||
@ -340,7 +340,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)
|
||||||
form = NewDeductionForm(request.POST or None, instance=acc, user=request.user)
|
form = NewDeductionModalForm(request.POST or None, instance=acc, user=request.user)
|
||||||
return form.process_request(
|
return form.process_request(
|
||||||
request,
|
request,
|
||||||
msg_success=_("Deduction added")
|
msg_success=_("Deduction added")
|
||||||
|
@ -12,7 +12,7 @@ from django.db.models import QuerySet, Q
|
|||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from intervention.forms import DummyFilterInput
|
from intervention.inputs import DummyFilterInput
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,744 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
|
||||||
Created on: 02.12.20
|
|
||||||
|
|
||||||
"""
|
|
||||||
from dal import autocomplete
|
|
||||||
from django import forms
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.contrib.gis.geos import Polygon
|
|
||||||
from django.db import transaction
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from codelist.models import KonovaCode
|
|
||||||
from codelist.settings import CODELIST_PROCESS_TYPE_ID, CODELIST_LAW_ID, \
|
|
||||||
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID
|
|
||||||
from compensation.models import EcoAccountDeduction, EcoAccount
|
|
||||||
from intervention.models import Intervention, Revocation, RevocationDocument, LegalData, ResponsibilityData
|
|
||||||
from konova.forms import BaseForm, BaseModalForm, SimpleGeomForm
|
|
||||||
from konova.settings import ZB_GROUP, ETS_GROUP
|
|
||||||
from konova.utils.general import format_german_float
|
|
||||||
from konova.utils.messenger import Messenger
|
|
||||||
from konova.utils.user_checks import in_group
|
|
||||||
from user.models import UserActionLogEntry, UserAction
|
|
||||||
|
|
||||||
|
|
||||||
class NewInterventionForm(BaseForm):
|
|
||||||
identifier = forms.CharField(
|
|
||||||
label=_("Identifier"),
|
|
||||||
label_suffix="",
|
|
||||||
max_length=255,
|
|
||||||
help_text=_("Generated automatically"),
|
|
||||||
)
|
|
||||||
title = forms.CharField(
|
|
||||||
label=_("Title"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("An explanatory name"),
|
|
||||||
max_length=255,
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={
|
|
||||||
"placeholder": _("Construction XY; Location ABC")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
type = forms.ModelChoiceField(
|
|
||||||
label=_("Process type"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_(""),
|
|
||||||
required=False,
|
|
||||||
queryset=KonovaCode.objects.filter(
|
|
||||||
is_archived=False,
|
|
||||||
is_leaf=True,
|
|
||||||
code_lists__in=[CODELIST_PROCESS_TYPE_ID],
|
|
||||||
),
|
|
||||||
widget=autocomplete.ModelSelect2(
|
|
||||||
url="codes-process-type-autocomplete",
|
|
||||||
attrs={
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
laws = forms.ModelMultipleChoiceField(
|
|
||||||
label=_("Law"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Multiple selection possible"),
|
|
||||||
required=False,
|
|
||||||
queryset=KonovaCode.objects.filter(
|
|
||||||
is_archived=False,
|
|
||||||
is_leaf=True,
|
|
||||||
code_lists__in=[CODELIST_LAW_ID],
|
|
||||||
),
|
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
|
||||||
url="codes-law-autocomplete",
|
|
||||||
attrs={
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
registration_office = forms.ModelChoiceField(
|
|
||||||
label=_("Registration office"),
|
|
||||||
label_suffix="",
|
|
||||||
required=False,
|
|
||||||
queryset=KonovaCode.objects.filter(
|
|
||||||
is_archived=False,
|
|
||||||
is_leaf=True,
|
|
||||||
code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
|
|
||||||
),
|
|
||||||
widget=autocomplete.ModelSelect2(
|
|
||||||
url="codes-registration-office-autocomplete",
|
|
||||||
attrs={
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
conservation_office = forms.ModelChoiceField(
|
|
||||||
label=_("Conservation office"),
|
|
||||||
label_suffix="",
|
|
||||||
required=False,
|
|
||||||
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={
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
registration_file_number = forms.CharField(
|
|
||||||
label=_("Registration office file number"),
|
|
||||||
label_suffix="",
|
|
||||||
max_length=255,
|
|
||||||
required=False,
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={
|
|
||||||
"placeholder": _("ZB-123/ABC.456")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
handler = forms.CharField(
|
|
||||||
label=_("Intervention handler"),
|
|
||||||
label_suffix="",
|
|
||||||
max_length=255,
|
|
||||||
required=False,
|
|
||||||
help_text=_("Who performs the intervention"),
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={
|
|
||||||
"placeholder": _("Company Mustermann")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
registration_date = forms.DateField(
|
|
||||||
label=_("Registration date"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
required=False,
|
|
||||||
widget=forms.DateInput(
|
|
||||||
attrs={
|
|
||||||
"type": "date",
|
|
||||||
},
|
|
||||||
format="%d.%m.%Y"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
binding_date = forms.DateField(
|
|
||||||
label=_("Binding on"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
required=False,
|
|
||||||
widget=forms.DateInput(
|
|
||||||
attrs={
|
|
||||||
"type": "date",
|
|
||||||
},
|
|
||||||
format="%d.%m.%Y"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
comment = forms.CharField(
|
|
||||||
label_suffix="",
|
|
||||||
label=_("Comment"),
|
|
||||||
required=False,
|
|
||||||
help_text=_("Additional comment"),
|
|
||||||
widget=forms.Textarea(
|
|
||||||
attrs={
|
|
||||||
"rows": 5,
|
|
||||||
"class": "w-100"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Define w-100 for all form fields
|
|
||||||
full_width_fields = True
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("New intervention")
|
|
||||||
self.action_url = reverse("intervention:new")
|
|
||||||
self.cancel_redirect = reverse("intervention:index")
|
|
||||||
|
|
||||||
tmp_intervention = Intervention()
|
|
||||||
identifier = tmp_intervention._generate_new_identifier()
|
|
||||||
self.initialize_form_field("identifier", identifier)
|
|
||||||
|
|
||||||
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)
|
|
||||||
_type = self.cleaned_data.get("type", None)
|
|
||||||
laws = self.cleaned_data.get("laws", None)
|
|
||||||
handler = self.cleaned_data.get("handler", None)
|
|
||||||
registration_office = self.cleaned_data.get("registration_office", None)
|
|
||||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
|
||||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
|
||||||
registration_file_number = self.cleaned_data.get("registration_file_number", None)
|
|
||||||
binding_date = self.cleaned_data.get("binding_date", None)
|
|
||||||
registration_date = self.cleaned_data.get("registration_date", None)
|
|
||||||
comment = self.cleaned_data.get("comment", None)
|
|
||||||
|
|
||||||
# Create log entry
|
|
||||||
action = UserActionLogEntry.objects.create(
|
|
||||||
user=user,
|
|
||||||
action=UserAction.CREATED,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create legal data object (without M2M laws first)
|
|
||||||
legal_data = LegalData.objects.create(
|
|
||||||
registration_date=registration_date,
|
|
||||||
binding_date=binding_date,
|
|
||||||
process_type=_type,
|
|
||||||
)
|
|
||||||
# Then add the M2M laws to the object
|
|
||||||
legal_data.laws.set(laws)
|
|
||||||
|
|
||||||
# Create responsible data object
|
|
||||||
responsibility_data = ResponsibilityData.objects.create(
|
|
||||||
registration_office=registration_office,
|
|
||||||
conservation_office=conservation_office,
|
|
||||||
registration_file_number=registration_file_number,
|
|
||||||
conservation_file_number=conservation_file_number,
|
|
||||||
handler=handler,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
# Finally create main object, holding the other objects
|
|
||||||
intervention = Intervention.objects.create(
|
|
||||||
identifier=identifier,
|
|
||||||
title=title,
|
|
||||||
responsible=responsibility_data,
|
|
||||||
legal=legal_data,
|
|
||||||
created=action,
|
|
||||||
geometry=geometry,
|
|
||||||
comment=comment,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add the log entry to the main objects log list
|
|
||||||
intervention.log.add(action)
|
|
||||||
|
|
||||||
# Add the performing user as the first user having access to the data
|
|
||||||
intervention.users.add(user)
|
|
||||||
return intervention
|
|
||||||
|
|
||||||
|
|
||||||
class EditInterventionForm(NewInterventionForm):
|
|
||||||
""" Subclasses NewInterventionForm
|
|
||||||
|
|
||||||
Simply adds initializing of a provided self.instance object into the form fields
|
|
||||||
|
|
||||||
"""
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
if self.instance is not None:
|
|
||||||
self.action_url = reverse("intervention:edit", args=(self.instance.id,))
|
|
||||||
self.cancel_redirect = reverse("intervention:open", args=(self.instance.id,))
|
|
||||||
self.form_title = _("Edit intervention")
|
|
||||||
self.form_caption = ""
|
|
||||||
|
|
||||||
reg_date = self.instance.legal.registration_date
|
|
||||||
bind_date = self.instance.legal.binding_date
|
|
||||||
if reg_date is not None:
|
|
||||||
reg_date = reg_date.isoformat()
|
|
||||||
if bind_date is not None:
|
|
||||||
bind_date = bind_date.isoformat()
|
|
||||||
|
|
||||||
# Initialize form data
|
|
||||||
form_data = {
|
|
||||||
"identifier": self.instance.identifier,
|
|
||||||
"title": self.instance.title,
|
|
||||||
"type": self.instance.legal.process_type,
|
|
||||||
"laws": list(self.instance.legal.laws.values_list("id", flat=True)),
|
|
||||||
"handler": self.instance.responsible.handler,
|
|
||||||
"registration_office": self.instance.responsible.registration_office,
|
|
||||||
"registration_file_number": self.instance.responsible.registration_file_number,
|
|
||||||
"conservation_office": self.instance.responsible.conservation_office,
|
|
||||||
"conservation_file_number": self.instance.responsible.conservation_file_number,
|
|
||||||
"registration_date": reg_date,
|
|
||||||
"binding_date": bind_date,
|
|
||||||
"comment": self.instance.comment,
|
|
||||||
}
|
|
||||||
disabled_fields = [
|
|
||||||
"identifier",
|
|
||||||
]
|
|
||||||
self.load_initial_data(
|
|
||||||
form_data,
|
|
||||||
disabled_fields,
|
|
||||||
)
|
|
||||||
|
|
||||||
def save(self, user: User, geom_form: SimpleGeomForm):
|
|
||||||
""" Overwrite instance with new form data
|
|
||||||
|
|
||||||
Args:
|
|
||||||
user ():
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
with transaction.atomic():
|
|
||||||
identifier = self.cleaned_data.get("identifier", None)
|
|
||||||
title = self.cleaned_data.get("title", None)
|
|
||||||
process_type = self.cleaned_data.get("type", None)
|
|
||||||
laws = self.cleaned_data.get("laws", None)
|
|
||||||
handler = self.cleaned_data.get("handler", None)
|
|
||||||
registration_office = self.cleaned_data.get("registration_office", None)
|
|
||||||
registration_file_number = self.cleaned_data.get("registration_file_number", None)
|
|
||||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
|
||||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
|
||||||
registration_date = self.cleaned_data.get("registration_date", None)
|
|
||||||
binding_date = self.cleaned_data.get("binding_date", None)
|
|
||||||
comment = self.cleaned_data.get("comment", None)
|
|
||||||
|
|
||||||
self.instance.legal.process_type = process_type
|
|
||||||
self.instance.legal.registration_date = registration_date
|
|
||||||
self.instance.legal.binding_date = binding_date
|
|
||||||
self.instance.legal.laws.set(laws)
|
|
||||||
self.instance.legal.save()
|
|
||||||
|
|
||||||
self.instance.responsible.handler = handler
|
|
||||||
self.instance.responsible.registration_office = registration_office
|
|
||||||
self.instance.responsible.registration_file_number = registration_file_number
|
|
||||||
self.instance.responsible.conservation_office = conservation_office
|
|
||||||
self.instance.responsible.conservation_file_number = conservation_file_number
|
|
||||||
self.instance.responsible.save()
|
|
||||||
|
|
||||||
user_action = UserActionLogEntry.objects.create(
|
|
||||||
user=user,
|
|
||||||
timestamp=timezone.now(),
|
|
||||||
action=UserAction.EDITED,
|
|
||||||
)
|
|
||||||
|
|
||||||
geometry = geom_form.save(user_action)
|
|
||||||
self.instance.geometry = geometry
|
|
||||||
self.instance.geometry.save()
|
|
||||||
|
|
||||||
self.instance.log.add(user_action)
|
|
||||||
|
|
||||||
self.instance.identifier = identifier
|
|
||||||
self.instance.title = title
|
|
||||||
self.instance.comment = comment
|
|
||||||
self.instance.modified = user_action
|
|
||||||
self.instance.save()
|
|
||||||
|
|
||||||
return self.instance
|
|
||||||
|
|
||||||
|
|
||||||
class OpenInterventionForm(EditInterventionForm):
|
|
||||||
"""
|
|
||||||
This form is not intended to be used as data-input form. It's used to simplify the rendering of intervention:open
|
|
||||||
"""
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# Resize map
|
|
||||||
self.fields["geometry"].widget.attrs["map_width"] = 500
|
|
||||||
self.fields["geometry"].widget.attrs["map_height"] = 300
|
|
||||||
|
|
||||||
# Disable all form fields
|
|
||||||
for field in self.fields:
|
|
||||||
self.disable_form_field(field)
|
|
||||||
|
|
||||||
|
|
||||||
class DummyFilterInput(forms.HiddenInput):
|
|
||||||
""" A dummy input widget
|
|
||||||
|
|
||||||
Does not render anything. Can be used to keep filter logic using django_filter without having a pre defined
|
|
||||||
filter widget being rendered to the template.
|
|
||||||
|
|
||||||
"""
|
|
||||||
template_name = "konova/custom_widgets/dummy-filter-input.html"
|
|
||||||
|
|
||||||
|
|
||||||
class TextToClipboardInput(forms.TextInput):
|
|
||||||
template_name = "konova/custom_widgets/text-to-clipboard-input.html"
|
|
||||||
|
|
||||||
|
|
||||||
class ShareInterventionForm(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
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
users = forms.MultipleChoiceField(
|
|
||||||
label=_("Shared with"),
|
|
||||||
label_suffix="",
|
|
||||||
required=True,
|
|
||||||
help_text=_("Remove check to remove access for this user"),
|
|
||||||
widget=forms.CheckboxSelectMultiple(
|
|
||||||
attrs={
|
|
||||||
"class": "list-unstyled",
|
|
||||||
}
|
|
||||||
),
|
|
||||||
choices=[]
|
|
||||||
)
|
|
||||||
|
|
||||||
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 _init_fields(self):
|
|
||||||
""" Wraps initializing of fields
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Initialize share_link field
|
|
||||||
self.share_link = self.request.build_absolute_uri(
|
|
||||||
reverse("intervention:share", args=(self.instance.id, self.instance.access_token,))
|
|
||||||
)
|
|
||||||
self.initialize_form_field(
|
|
||||||
"url",
|
|
||||||
self.share_link
|
|
||||||
)
|
|
||||||
|
|
||||||
# Initialize users field
|
|
||||||
# Remove field if user is not in registration or conservation group
|
|
||||||
if not in_group(self.request.user, ZB_GROUP) and not in_group(self.request.user, ETS_GROUP):
|
|
||||||
del self.fields["users"]
|
|
||||||
else:
|
|
||||||
users = self.instance.users.all()
|
|
||||||
choices = []
|
|
||||||
for n in users:
|
|
||||||
choices.append(
|
|
||||||
(n.id, n.username)
|
|
||||||
)
|
|
||||||
self.fields["users"].choices = choices
|
|
||||||
u_ids = list(users.values_list("id", flat=True))
|
|
||||||
self.initialize_form_field(
|
|
||||||
"users",
|
|
||||||
u_ids
|
|
||||||
)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
accessing_users = User.objects.filter(
|
|
||||||
id__in=self.cleaned_data["users"]
|
|
||||||
)
|
|
||||||
self.instance.users.set(accessing_users)
|
|
||||||
|
|
||||||
|
|
||||||
class NewRevocationForm(BaseModalForm):
|
|
||||||
date = forms.DateField(
|
|
||||||
label=_("Date"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
help_text=_("Date of revocation"),
|
|
||||||
widget=forms.DateInput(
|
|
||||||
attrs={
|
|
||||||
"type": "date",
|
|
||||||
"data-provide": "datepicker",
|
|
||||||
},
|
|
||||||
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": "w-75"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Define w-100 for all form fields
|
|
||||||
full_width_fields = True
|
|
||||||
|
|
||||||
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):
|
|
||||||
with transaction.atomic():
|
|
||||||
created_action = UserActionLogEntry.objects.create(
|
|
||||||
user=self.user,
|
|
||||||
action=UserAction.CREATED
|
|
||||||
)
|
|
||||||
edited_action = UserActionLogEntry.objects.create(
|
|
||||||
user=self.user,
|
|
||||||
action=UserAction.EDITED
|
|
||||||
)
|
|
||||||
revocation = Revocation.objects.create(
|
|
||||||
date=self.cleaned_data["date"],
|
|
||||||
comment=self.cleaned_data["comment"],
|
|
||||||
created=created_action,
|
|
||||||
)
|
|
||||||
self.instance.modified = edited_action
|
|
||||||
self.instance.save()
|
|
||||||
self.instance.log.add(edited_action)
|
|
||||||
self.instance.legal.revocation = revocation
|
|
||||||
self.instance.legal.save()
|
|
||||||
|
|
||||||
if self.cleaned_data["file"]:
|
|
||||||
RevocationDocument.objects.create(
|
|
||||||
title="revocation_of_{}".format(self.instance.identifier),
|
|
||||||
date_of_creation=self.cleaned_data["date"],
|
|
||||||
comment=self.cleaned_data["comment"],
|
|
||||||
file=self.cleaned_data["file"],
|
|
||||||
instance=revocation
|
|
||||||
)
|
|
||||||
return revocation
|
|
||||||
|
|
||||||
|
|
||||||
class RunCheckForm(BaseModalForm):
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def is_valid(self) -> bool:
|
|
||||||
""" Perform a validity check based on quality_check() logic
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
result (bool)
|
|
||||||
"""
|
|
||||||
super_result = super().is_valid()
|
|
||||||
# Perform check
|
|
||||||
msgs = self.instance.quality_check()
|
|
||||||
for msg in msgs:
|
|
||||||
self.add_error(
|
|
||||||
"checked_intervention",
|
|
||||||
msg
|
|
||||||
)
|
|
||||||
return super_result and (len(msgs) == 0)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
""" Saving logic
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
with transaction.atomic():
|
|
||||||
user_action = UserActionLogEntry.objects.create(
|
|
||||||
user=self.user,
|
|
||||||
action=UserAction.CHECKED
|
|
||||||
)
|
|
||||||
# Replace old checked
|
|
||||||
if self.instance.checked:
|
|
||||||
self.instance.checked.delete()
|
|
||||||
self.instance.checked = user_action
|
|
||||||
self.instance.log.add(user_action)
|
|
||||||
self.instance.save()
|
|
||||||
|
|
||||||
# Send message to the SSO server
|
|
||||||
messenger = Messenger(
|
|
||||||
self.instance.users.all(),
|
|
||||||
type="INFO",
|
|
||||||
)
|
|
||||||
messenger.send_object_checked(
|
|
||||||
self.instance.identifier,
|
|
||||||
self.user,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NewDeductionForm(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²"),
|
|
||||||
)
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Define w-100 for all form fields
|
|
||||||
full_width_fields = True
|
|
||||||
|
|
||||||
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")
|
|
||||||
self.is_intervention_initially = False
|
|
||||||
|
|
||||||
# Add a placeholder for field 'surface' without having to define the whole widget above
|
|
||||||
self.add_placeholder_for_field("surface", "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 deduction 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
|
|
||||||
|
|
||||||
if not acc.recorded:
|
|
||||||
self.add_error(
|
|
||||||
"account",
|
|
||||||
_("Eco-account {} is not recorded yet. You can only deduct from recorded accounts.").format(acc.identifier)
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Calculate valid surface
|
|
||||||
sum_surface = acc.get_surface()
|
|
||||||
sum_surface_deductions = acc.get_deductions_surface()
|
|
||||||
rest_surface = sum_surface - sum_surface_deductions
|
|
||||||
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 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)
|
|
||||||
self.instance.modified = user_action_edit
|
|
||||||
self.instance.save()
|
|
||||||
|
|
||||||
# Create deductions depending on Intervention or EcoAccount as the initial instance
|
|
||||||
if self.is_intervention_initially:
|
|
||||||
deduction = EcoAccountDeduction.objects.create(
|
|
||||||
intervention=self.instance,
|
|
||||||
account=self.cleaned_data["account"],
|
|
||||||
surface=self.cleaned_data["surface"],
|
|
||||||
created=user_action_create,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
deduction = EcoAccountDeduction.objects.create(
|
|
||||||
intervention=self.cleaned_data["intervention"],
|
|
||||||
account=self.instance,
|
|
||||||
surface=self.cleaned_data["surface"],
|
|
||||||
created=user_action_create,
|
|
||||||
)
|
|
||||||
return deduction
|
|
362
intervention/forms/forms.py
Normal file
362
intervention/forms/forms.py
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: michel.peltriaux@sgdnord.rlp.de
|
||||||
|
Created on: 02.12.20
|
||||||
|
|
||||||
|
"""
|
||||||
|
from dal import autocomplete
|
||||||
|
from django import forms
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.db import transaction
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from codelist.models import KonovaCode
|
||||||
|
from codelist.settings import CODELIST_PROCESS_TYPE_ID, CODELIST_LAW_ID, \
|
||||||
|
CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID
|
||||||
|
from intervention.models import Intervention, LegalData, ResponsibilityData
|
||||||
|
from konova.forms import BaseForm, SimpleGeomForm
|
||||||
|
from user.models import UserActionLogEntry, UserAction
|
||||||
|
|
||||||
|
|
||||||
|
class NewInterventionForm(BaseForm):
|
||||||
|
identifier = forms.CharField(
|
||||||
|
label=_("Identifier"),
|
||||||
|
label_suffix="",
|
||||||
|
max_length=255,
|
||||||
|
help_text=_("Generated automatically"),
|
||||||
|
)
|
||||||
|
title = forms.CharField(
|
||||||
|
label=_("Title"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("An explanatory name"),
|
||||||
|
max_length=255,
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"placeholder": _("Construction XY; Location ABC")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
type = forms.ModelChoiceField(
|
||||||
|
label=_("Process type"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_(""),
|
||||||
|
required=False,
|
||||||
|
queryset=KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
code_lists__in=[CODELIST_PROCESS_TYPE_ID],
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url="codes-process-type-autocomplete",
|
||||||
|
attrs={
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
laws = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Law"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Multiple selection possible"),
|
||||||
|
required=False,
|
||||||
|
queryset=KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
code_lists__in=[CODELIST_LAW_ID],
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
|
url="codes-law-autocomplete",
|
||||||
|
attrs={
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
registration_office = forms.ModelChoiceField(
|
||||||
|
label=_("Registration office"),
|
||||||
|
label_suffix="",
|
||||||
|
required=False,
|
||||||
|
queryset=KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url="codes-registration-office-autocomplete",
|
||||||
|
attrs={
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
conservation_office = forms.ModelChoiceField(
|
||||||
|
label=_("Conservation office"),
|
||||||
|
label_suffix="",
|
||||||
|
required=False,
|
||||||
|
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={
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
registration_file_number = forms.CharField(
|
||||||
|
label=_("Registration office file number"),
|
||||||
|
label_suffix="",
|
||||||
|
max_length=255,
|
||||||
|
required=False,
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"placeholder": _("ZB-123/ABC.456")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
handler = forms.CharField(
|
||||||
|
label=_("Intervention handler"),
|
||||||
|
label_suffix="",
|
||||||
|
max_length=255,
|
||||||
|
required=False,
|
||||||
|
help_text=_("Who performs the intervention"),
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"placeholder": _("Company Mustermann")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
registration_date = forms.DateField(
|
||||||
|
label=_("Registration date"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
required=False,
|
||||||
|
widget=forms.DateInput(
|
||||||
|
attrs={
|
||||||
|
"type": "date",
|
||||||
|
},
|
||||||
|
format="%d.%m.%Y"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
binding_date = forms.DateField(
|
||||||
|
label=_("Binding on"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
required=False,
|
||||||
|
widget=forms.DateInput(
|
||||||
|
attrs={
|
||||||
|
"type": "date",
|
||||||
|
},
|
||||||
|
format="%d.%m.%Y"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
comment = forms.CharField(
|
||||||
|
label_suffix="",
|
||||||
|
label=_("Comment"),
|
||||||
|
required=False,
|
||||||
|
help_text=_("Additional comment"),
|
||||||
|
widget=forms.Textarea(
|
||||||
|
attrs={
|
||||||
|
"rows": 5,
|
||||||
|
"class": "w-100"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Define w-100 for all form fields
|
||||||
|
full_width_fields = True
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("New intervention")
|
||||||
|
self.action_url = reverse("intervention:new")
|
||||||
|
self.cancel_redirect = reverse("intervention:index")
|
||||||
|
|
||||||
|
tmp_intervention = Intervention()
|
||||||
|
identifier = tmp_intervention._generate_new_identifier()
|
||||||
|
self.initialize_form_field("identifier", identifier)
|
||||||
|
|
||||||
|
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)
|
||||||
|
_type = self.cleaned_data.get("type", None)
|
||||||
|
laws = self.cleaned_data.get("laws", None)
|
||||||
|
handler = self.cleaned_data.get("handler", None)
|
||||||
|
registration_office = self.cleaned_data.get("registration_office", None)
|
||||||
|
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||||
|
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||||
|
registration_file_number = self.cleaned_data.get("registration_file_number", None)
|
||||||
|
binding_date = self.cleaned_data.get("binding_date", None)
|
||||||
|
registration_date = self.cleaned_data.get("registration_date", None)
|
||||||
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
|
||||||
|
# Create log entry
|
||||||
|
action = UserActionLogEntry.objects.create(
|
||||||
|
user=user,
|
||||||
|
action=UserAction.CREATED,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create legal data object (without M2M laws first)
|
||||||
|
legal_data = LegalData.objects.create(
|
||||||
|
registration_date=registration_date,
|
||||||
|
binding_date=binding_date,
|
||||||
|
process_type=_type,
|
||||||
|
)
|
||||||
|
# Then add the M2M laws to the object
|
||||||
|
legal_data.laws.set(laws)
|
||||||
|
|
||||||
|
# Create responsible data object
|
||||||
|
responsibility_data = ResponsibilityData.objects.create(
|
||||||
|
registration_office=registration_office,
|
||||||
|
conservation_office=conservation_office,
|
||||||
|
registration_file_number=registration_file_number,
|
||||||
|
conservation_file_number=conservation_file_number,
|
||||||
|
handler=handler,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Process the geometry form
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
|
||||||
|
# Finally create main object, holding the other objects
|
||||||
|
intervention = Intervention.objects.create(
|
||||||
|
identifier=identifier,
|
||||||
|
title=title,
|
||||||
|
responsible=responsibility_data,
|
||||||
|
legal=legal_data,
|
||||||
|
created=action,
|
||||||
|
geometry=geometry,
|
||||||
|
comment=comment,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add the log entry to the main objects log list
|
||||||
|
intervention.log.add(action)
|
||||||
|
|
||||||
|
# Add the performing user as the first user having access to the data
|
||||||
|
intervention.users.add(user)
|
||||||
|
return intervention
|
||||||
|
|
||||||
|
|
||||||
|
class EditInterventionForm(NewInterventionForm):
|
||||||
|
""" Subclasses NewInterventionForm
|
||||||
|
|
||||||
|
Simply adds initializing of a provided self.instance object into the form fields
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
if self.instance is not None:
|
||||||
|
self.action_url = reverse("intervention:edit", args=(self.instance.id,))
|
||||||
|
self.cancel_redirect = reverse("intervention:open", args=(self.instance.id,))
|
||||||
|
self.form_title = _("Edit intervention")
|
||||||
|
self.form_caption = ""
|
||||||
|
|
||||||
|
reg_date = self.instance.legal.registration_date
|
||||||
|
bind_date = self.instance.legal.binding_date
|
||||||
|
if reg_date is not None:
|
||||||
|
reg_date = reg_date.isoformat()
|
||||||
|
if bind_date is not None:
|
||||||
|
bind_date = bind_date.isoformat()
|
||||||
|
|
||||||
|
# Initialize form data
|
||||||
|
form_data = {
|
||||||
|
"identifier": self.instance.identifier,
|
||||||
|
"title": self.instance.title,
|
||||||
|
"type": self.instance.legal.process_type,
|
||||||
|
"laws": list(self.instance.legal.laws.values_list("id", flat=True)),
|
||||||
|
"handler": self.instance.responsible.handler,
|
||||||
|
"registration_office": self.instance.responsible.registration_office,
|
||||||
|
"registration_file_number": self.instance.responsible.registration_file_number,
|
||||||
|
"conservation_office": self.instance.responsible.conservation_office,
|
||||||
|
"conservation_file_number": self.instance.responsible.conservation_file_number,
|
||||||
|
"registration_date": reg_date,
|
||||||
|
"binding_date": bind_date,
|
||||||
|
"comment": self.instance.comment,
|
||||||
|
}
|
||||||
|
disabled_fields = [
|
||||||
|
"identifier",
|
||||||
|
]
|
||||||
|
self.load_initial_data(
|
||||||
|
form_data,
|
||||||
|
disabled_fields,
|
||||||
|
)
|
||||||
|
|
||||||
|
def save(self, user: User, geom_form: SimpleGeomForm):
|
||||||
|
""" Overwrite instance with new form data
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user ():
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
with transaction.atomic():
|
||||||
|
identifier = self.cleaned_data.get("identifier", None)
|
||||||
|
title = self.cleaned_data.get("title", None)
|
||||||
|
process_type = self.cleaned_data.get("type", None)
|
||||||
|
laws = self.cleaned_data.get("laws", None)
|
||||||
|
handler = self.cleaned_data.get("handler", None)
|
||||||
|
registration_office = self.cleaned_data.get("registration_office", None)
|
||||||
|
registration_file_number = self.cleaned_data.get("registration_file_number", None)
|
||||||
|
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||||
|
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||||
|
registration_date = self.cleaned_data.get("registration_date", None)
|
||||||
|
binding_date = self.cleaned_data.get("binding_date", None)
|
||||||
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
|
||||||
|
self.instance.legal.process_type = process_type
|
||||||
|
self.instance.legal.registration_date = registration_date
|
||||||
|
self.instance.legal.binding_date = binding_date
|
||||||
|
self.instance.legal.laws.set(laws)
|
||||||
|
self.instance.legal.save()
|
||||||
|
|
||||||
|
self.instance.responsible.handler = handler
|
||||||
|
self.instance.responsible.registration_office = registration_office
|
||||||
|
self.instance.responsible.registration_file_number = registration_file_number
|
||||||
|
self.instance.responsible.conservation_office = conservation_office
|
||||||
|
self.instance.responsible.conservation_file_number = conservation_file_number
|
||||||
|
self.instance.responsible.save()
|
||||||
|
|
||||||
|
user_action = UserActionLogEntry.objects.create(
|
||||||
|
user=user,
|
||||||
|
timestamp=timezone.now(),
|
||||||
|
action=UserAction.EDITED,
|
||||||
|
)
|
||||||
|
|
||||||
|
geometry = geom_form.save(user_action)
|
||||||
|
self.instance.geometry = geometry
|
||||||
|
self.instance.geometry.save()
|
||||||
|
|
||||||
|
self.instance.log.add(user_action)
|
||||||
|
|
||||||
|
self.instance.identifier = identifier
|
||||||
|
self.instance.title = title
|
||||||
|
self.instance.comment = comment
|
||||||
|
self.instance.modified = user_action
|
||||||
|
self.instance.save()
|
||||||
|
|
||||||
|
return self.instance
|
||||||
|
|
||||||
|
|
||||||
|
class OpenInterventionForm(EditInterventionForm):
|
||||||
|
"""
|
||||||
|
This form is not intended to be used as data-input form. It's used to simplify the rendering of intervention:open
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Resize map
|
||||||
|
self.fields["geometry"].widget.attrs["map_width"] = 500
|
||||||
|
self.fields["geometry"].widget.attrs["map_height"] = 300
|
||||||
|
|
||||||
|
# Disable all form fields
|
||||||
|
for field in self.fields:
|
||||||
|
self.disable_form_field(field)
|
385
intervention/forms/modalForms.py
Normal file
385
intervention/forms/modalForms.py
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
"""
|
||||||
|
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.contrib.auth.models import User
|
||||||
|
from django.db import transaction
|
||||||
|
from django import forms
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount, EcoAccountDeduction
|
||||||
|
from intervention.inputs import TextToClipboardInput
|
||||||
|
from intervention.models import Revocation, RevocationDocument, Intervention
|
||||||
|
from konova.forms import BaseModalForm
|
||||||
|
from konova.settings import ZB_GROUP, ETS_GROUP
|
||||||
|
from konova.utils.general import format_german_float
|
||||||
|
from konova.utils.messenger import Messenger
|
||||||
|
from konova.utils.user_checks import in_group
|
||||||
|
from user.models import UserActionLogEntry, UserAction
|
||||||
|
|
||||||
|
|
||||||
|
class ShareInterventionModalForm(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
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
users = forms.MultipleChoiceField(
|
||||||
|
label=_("Shared with"),
|
||||||
|
label_suffix="",
|
||||||
|
required=True,
|
||||||
|
help_text=_("Remove check to remove access for this user"),
|
||||||
|
widget=forms.CheckboxSelectMultiple(
|
||||||
|
attrs={
|
||||||
|
"class": "list-unstyled",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
choices=[]
|
||||||
|
)
|
||||||
|
|
||||||
|
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 _init_fields(self):
|
||||||
|
""" Wraps initializing of fields
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Initialize share_link field
|
||||||
|
self.share_link = self.request.build_absolute_uri(
|
||||||
|
reverse("intervention:share", args=(self.instance.id, self.instance.access_token,))
|
||||||
|
)
|
||||||
|
self.initialize_form_field(
|
||||||
|
"url",
|
||||||
|
self.share_link
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize users field
|
||||||
|
# Remove field if user is not in registration or conservation group
|
||||||
|
if not in_group(self.request.user, ZB_GROUP) and not in_group(self.request.user, ETS_GROUP):
|
||||||
|
del self.fields["users"]
|
||||||
|
else:
|
||||||
|
users = self.instance.users.all()
|
||||||
|
choices = []
|
||||||
|
for n in users:
|
||||||
|
choices.append(
|
||||||
|
(n.id, n.username)
|
||||||
|
)
|
||||||
|
self.fields["users"].choices = choices
|
||||||
|
u_ids = list(users.values_list("id", flat=True))
|
||||||
|
self.initialize_form_field(
|
||||||
|
"users",
|
||||||
|
u_ids
|
||||||
|
)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
accessing_users = User.objects.filter(
|
||||||
|
id__in=self.cleaned_data["users"]
|
||||||
|
)
|
||||||
|
self.instance.users.set(accessing_users)
|
||||||
|
|
||||||
|
|
||||||
|
class NewRevocationModalForm(BaseModalForm):
|
||||||
|
date = forms.DateField(
|
||||||
|
label=_("Date"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
help_text=_("Date of revocation"),
|
||||||
|
widget=forms.DateInput(
|
||||||
|
attrs={
|
||||||
|
"type": "date",
|
||||||
|
"data-provide": "datepicker",
|
||||||
|
},
|
||||||
|
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": "w-75"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Define w-100 for all form fields
|
||||||
|
full_width_fields = True
|
||||||
|
|
||||||
|
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):
|
||||||
|
with transaction.atomic():
|
||||||
|
created_action = UserActionLogEntry.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
action=UserAction.CREATED
|
||||||
|
)
|
||||||
|
edited_action = UserActionLogEntry.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
action=UserAction.EDITED
|
||||||
|
)
|
||||||
|
revocation = Revocation.objects.create(
|
||||||
|
date=self.cleaned_data["date"],
|
||||||
|
comment=self.cleaned_data["comment"],
|
||||||
|
created=created_action,
|
||||||
|
)
|
||||||
|
self.instance.modified = edited_action
|
||||||
|
self.instance.save()
|
||||||
|
self.instance.log.add(edited_action)
|
||||||
|
self.instance.legal.revocation = revocation
|
||||||
|
self.instance.legal.save()
|
||||||
|
|
||||||
|
if self.cleaned_data["file"]:
|
||||||
|
RevocationDocument.objects.create(
|
||||||
|
title="revocation_of_{}".format(self.instance.identifier),
|
||||||
|
date_of_creation=self.cleaned_data["date"],
|
||||||
|
comment=self.cleaned_data["comment"],
|
||||||
|
file=self.cleaned_data["file"],
|
||||||
|
instance=revocation
|
||||||
|
)
|
||||||
|
return revocation
|
||||||
|
|
||||||
|
|
||||||
|
class RunCheckModalForm(BaseModalForm):
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def is_valid(self) -> bool:
|
||||||
|
""" Perform a validity check based on quality_check() logic
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
result (bool)
|
||||||
|
"""
|
||||||
|
super_result = super().is_valid()
|
||||||
|
# Perform check
|
||||||
|
msgs = self.instance.quality_check()
|
||||||
|
for msg in msgs:
|
||||||
|
self.add_error(
|
||||||
|
"checked_intervention",
|
||||||
|
msg
|
||||||
|
)
|
||||||
|
return super_result and (len(msgs) == 0)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
""" Saving logic
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
with transaction.atomic():
|
||||||
|
user_action = UserActionLogEntry.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
action=UserAction.CHECKED
|
||||||
|
)
|
||||||
|
# Replace old checked
|
||||||
|
if self.instance.checked:
|
||||||
|
self.instance.checked.delete()
|
||||||
|
self.instance.checked = user_action
|
||||||
|
self.instance.log.add(user_action)
|
||||||
|
self.instance.save()
|
||||||
|
|
||||||
|
# Send message to the SSO server
|
||||||
|
messenger = Messenger(
|
||||||
|
self.instance.users.all(),
|
||||||
|
type="INFO",
|
||||||
|
)
|
||||||
|
messenger.send_object_checked(
|
||||||
|
self.instance.identifier,
|
||||||
|
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²"),
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Define w-100 for all form fields
|
||||||
|
full_width_fields = True
|
||||||
|
|
||||||
|
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")
|
||||||
|
self.is_intervention_initially = False
|
||||||
|
|
||||||
|
# Add a placeholder for field 'surface' without having to define the whole widget above
|
||||||
|
self.add_placeholder_for_field("surface", "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 deduction 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
|
||||||
|
|
||||||
|
if not acc.recorded:
|
||||||
|
self.add_error(
|
||||||
|
"account",
|
||||||
|
_("Eco-account {} is not recorded yet. You can only deduct from recorded accounts.").format(acc.identifier)
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Calculate valid surface
|
||||||
|
sum_surface = acc.get_surface()
|
||||||
|
sum_surface_deductions = acc.get_deductions_surface()
|
||||||
|
rest_surface = sum_surface - sum_surface_deductions
|
||||||
|
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 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)
|
||||||
|
self.instance.modified = user_action_edit
|
||||||
|
self.instance.save()
|
||||||
|
|
||||||
|
# Create deductions depending on Intervention or EcoAccount as the initial instance
|
||||||
|
if self.is_intervention_initially:
|
||||||
|
deduction = EcoAccountDeduction.objects.create(
|
||||||
|
intervention=self.instance,
|
||||||
|
account=self.cleaned_data["account"],
|
||||||
|
surface=self.cleaned_data["surface"],
|
||||||
|
created=user_action_create,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
deduction = EcoAccountDeduction.objects.create(
|
||||||
|
intervention=self.cleaned_data["intervention"],
|
||||||
|
account=self.instance,
|
||||||
|
surface=self.cleaned_data["surface"],
|
||||||
|
created=user_action_create,
|
||||||
|
)
|
||||||
|
return deduction
|
15
intervention/inputs.py
Normal file
15
intervention/inputs.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class DummyFilterInput(forms.HiddenInput):
|
||||||
|
""" A dummy input widget
|
||||||
|
|
||||||
|
Does not render anything. Can be used to keep filter logic using django_filter without having a pre defined
|
||||||
|
filter widget being rendered to the template.
|
||||||
|
|
||||||
|
"""
|
||||||
|
template_name = "konova/custom_widgets/dummy-filter-input.html"
|
||||||
|
|
||||||
|
|
||||||
|
class TextToClipboardInput(forms.TextInput):
|
||||||
|
template_name = "konova/custom_widgets/text-to-clipboard-input.html"
|
@ -1,11 +1,11 @@
|
|||||||
from django.contrib import messages
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
|
|
||||||
from intervention.forms import NewInterventionForm, EditInterventionForm, ShareInterventionForm, NewRevocationForm, \
|
from intervention.forms.forms import NewInterventionForm, EditInterventionForm
|
||||||
RunCheckForm, NewDeductionForm
|
from intervention.forms.modalForms import ShareInterventionModalForm, NewRevocationModalForm, \
|
||||||
|
RunCheckModalForm, NewDeductionModalForm
|
||||||
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
|
||||||
@ -342,7 +342,7 @@ def create_share_view(request: HttpRequest, id: str):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
form = ShareInterventionForm(request.POST or None, instance=intervention, request=request)
|
form = ShareInterventionModalForm(request.POST or None, instance=intervention, request=request)
|
||||||
return form.process_request(
|
return form.process_request(
|
||||||
request,
|
request,
|
||||||
msg_success=_("Share settings updated")
|
msg_success=_("Share settings updated")
|
||||||
@ -361,7 +361,7 @@ def run_check_view(request: HttpRequest, id: str):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
form = RunCheckForm(request.POST or None, instance=intervention, user=request.user)
|
form = RunCheckModalForm(request.POST or None, instance=intervention, user=request.user)
|
||||||
return form.process_request(
|
return form.process_request(
|
||||||
request,
|
request,
|
||||||
msg_success=_("Check performed"),
|
msg_success=_("Check performed"),
|
||||||
@ -381,7 +381,7 @@ def new_revocation_view(request: HttpRequest, id: str):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
form = NewRevocationForm(request.POST or None, request.FILES or None, instance=intervention, user=request.user)
|
form = NewRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention, user=request.user)
|
||||||
return form.process_request(
|
return form.process_request(
|
||||||
request,
|
request,
|
||||||
msg_success=_("Revocation added")
|
msg_success=_("Revocation added")
|
||||||
@ -425,7 +425,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 = NewDeductionForm(request.POST or None, instance=intervention, user=request.user)
|
form = NewDeductionModalForm(request.POST or None, instance=intervention, user=request.user)
|
||||||
return form.process_request(
|
return form.process_request(
|
||||||
request,
|
request,
|
||||||
msg_success=_("Deduction added")
|
msg_success=_("Deduction added")
|
||||||
|
Loading…
Reference in New Issue
Block a user