Compare commits

...

4 Commits

Author SHA1 Message Date
ea0a07890c #7 New forms
* introduces bootstrap class form-control for proper html form input rendering
* fixes bug where missing shared users for an entry resulted in a None exception
* adds GenerateInput with template in generate-content-input.html, which provides a generate button for fetching server-side content
* adds/updates translations
2021-09-27 13:57:56 +02:00
78ef1b79af #7 New forms
* removes unused OpenInterventionForm
2021-09-27 11:46:41 +02:00
877a1ca335 #7 New forms
* refactors large forms.py into forms/modalForms.py and forms/forms.py
* refactors custom input fields into intervention/inputs.py
2021-09-27 11:45:13 +02:00
e94dbbd3c8 #7 New forms
* fixes bug in case of unset dates for intervention
* adds missing template formater
* adds/updates translations
2021-09-27 11:12:40 +02:00
15 changed files with 1052 additions and 946 deletions

View File

@ -46,6 +46,11 @@ class NewPaymentForm(BaseModalForm):
label=_con("money", "Amount"), # contextual translation
label_suffix=_(""),
help_text=_("in Euro"),
widget=forms.NumberInput(
attrs={
"class": "form-control"
}
)
)
due = forms.DateField(
label=_("Due on"),
@ -56,6 +61,7 @@ class NewPaymentForm(BaseModalForm):
attrs={
"type": "date",
"data-provide": "datepicker",
"class": "form-control",
},
format="%d.%m.%Y"
)
@ -69,14 +75,11 @@ class NewPaymentForm(BaseModalForm):
widget=forms.Textarea(
attrs={
"rows": 5,
"class": "w-100"
"class": "form-control"
}
)
)
# Define w-100 for all form fields
full_width_fields = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.intervention = self.instance
@ -155,7 +158,12 @@ class NewStateModalForm(BaseModalForm):
label=_("Surface"),
label_suffix="",
required=True,
help_text=_("in m²")
help_text=_("in m²"),
widget=forms.NumberInput(
attrs={
"class": "form-control",
}
)
)
def __init__(self, *args, **kwargs):
@ -243,7 +251,7 @@ class NewDeadlineModalForm(BaseModalForm):
choices=DeadlineType.choices,
widget=forms.Select(
attrs={
"class": "custom-select"
"class": "form-control"
}
)
)
@ -256,6 +264,7 @@ class NewDeadlineModalForm(BaseModalForm):
attrs={
"type": "date",
"data-provide": "datepicker",
"class": "form-control",
},
format="%d.%m.%Y"
)
@ -270,6 +279,7 @@ class NewDeadlineModalForm(BaseModalForm):
attrs={
"cols": 30,
"rows": 5,
"class": "form-control",
}
)
)
@ -329,7 +339,7 @@ class NewActionModalForm(BaseModalForm):
choices=UnitChoices.choices,
widget=forms.Select(
attrs={
"class": "custom-select"
"class": "form-control"
}
)
)
@ -340,6 +350,11 @@ class NewActionModalForm(BaseModalForm):
help_text=_("Insert the amount"),
decimal_places=2,
min_value=0.00,
widget=forms.NumberInput(
attrs={
"class": "form-control",
}
)
)
comment = forms.CharField(
required=False,
@ -350,7 +365,7 @@ class NewActionModalForm(BaseModalForm):
widget=forms.Textarea(
attrs={
"rows": 5,
"class": "w-100"
"class": "form-control",
}
)
)

View File

@ -5,6 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 01.12.20
"""
from django.contrib.auth.models import User
from django.http import HttpRequest
from django.template.loader import render_to_string
from django.urls import reverse
@ -148,6 +149,8 @@ class CompensationTable(BaseTable):
"""
html = ""
if value is None:
value = User.objects.none()
has_access = value.filter(
username=self.user.username
).exists()

View File

@ -16,7 +16,7 @@ from django.shortcuts import render, get_object_or_404
from compensation.forms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
from compensation.models import EcoAccount, EcoAccountDocument
from compensation.tables import EcoAccountTable
from intervention.forms import NewDeductionForm
from intervention.forms.modalForms import NewDeductionModalForm
from konova.contexts import BaseContext
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required
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)
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(
request,
msg_success=_("Deduction added")

View File

@ -12,7 +12,7 @@ from django.db.models import QuerySet, Q
from django.utils.translation import gettext_lazy as _
from intervention.forms import DummyFilterInput
from intervention.inputs import DummyFilterInput
from intervention.models import Intervention

View File

@ -1,737 +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 = ""
# 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": self.instance.legal.registration_date.isoformat(),
"binding_date": self.instance.legal.binding_date.isoformat(),
"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

355
intervention/forms/forms.py Normal file
View File

@ -0,0 +1,355 @@
"""
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, reverse_lazy
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.inputs import GenerateInput
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"),
widget=GenerateInput(
attrs={
"class": "form-control",
"url": reverse_lazy("intervention:new-id"),
}
)
)
title = forms.CharField(
label=_("Title"),
label_suffix="",
help_text=_("An explanatory name"),
max_length=255,
widget=forms.TextInput(
attrs={
"placeholder": _("Construction XY; Location ABC"),
"class": "form-control",
}
)
)
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"),
"class": "form-control",
}
)
)
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"),
"class": "form-control",
}
)
)
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"),
"class": "form-control",
}
)
)
registration_date = forms.DateField(
label=_("Registration date"),
label_suffix=_(""),
required=False,
widget=forms.DateInput(
attrs={
"type": "date",
"class": "form-control",
},
format="%d.%m.%Y"
)
)
binding_date = forms.DateField(
label=_("Binding on"),
label_suffix=_(""),
required=False,
widget=forms.DateInput(
attrs={
"type": "date",
"class": "form-control",
},
format="%d.%m.%Y"
)
)
comment = forms.CharField(
label_suffix="",
label=_("Comment"),
required=False,
help_text=_("Additional comment"),
widget=forms.Textarea(
attrs={
"rows": 5,
"class": "form-control"
}
)
)
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 = []
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

View File

@ -0,0 +1,387 @@
"""
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,
"class": "form-control",
}
)
)
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",
"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",
}
)
)
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²"),
widget=forms.NumberInput(
attrs={
"class": "form-control",
}
)
)
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")
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

32
intervention/inputs.py Normal file
View File

@ -0,0 +1,32 @@
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"
class GenerateInput(forms.TextInput):
"""
Provides a form group with a button at the end, which generates new content for the input.
The url used to fetch new content can be added using the attrs like
widget=GenerateInput(
attrs={
"url": reverse_lazy("app_name:view_name")
...
}
)
"""
template_name = "konova/custom_widgets/generate-content-input.html"

View File

@ -9,12 +9,13 @@ from django.urls import path
from intervention.views import index_view, new_view, open_view, edit_view, remove_view, new_document_view, share_view, \
create_share_view, remove_revocation_view, new_revocation_view, run_check_view, log_view, new_deduction_view, \
record_view, remove_document_view, get_document_view, get_revocation_view
record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view
app_name = "intervention"
urlpatterns = [
path("", index_view, name="index"),
path('new/', new_view, name='new'),
path('new/id', new_id_view, name='new-id'),
path('<id>', open_view, name='open'),
path('<id>/log', log_view, name='log'),
path('<id>/edit', edit_view, name='edit'),

View File

@ -1,11 +1,11 @@
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.utils.translation import gettext_lazy as _
from django.http import HttpRequest
from django.http import HttpRequest, JsonResponse
from django.shortcuts import render, get_object_or_404
from intervention.forms import NewInterventionForm, EditInterventionForm, ShareInterventionForm, NewRevocationForm, \
RunCheckForm, NewDeductionForm
from intervention.forms.forms import NewInterventionForm, EditInterventionForm
from intervention.forms.modalForms import ShareInterventionModalForm, NewRevocationModalForm, \
RunCheckModalForm, NewDeductionModalForm
from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument
from intervention.tables import InterventionTable
from konova.contexts import BaseContext
@ -13,7 +13,7 @@ from konova.decorators import *
from konova.forms import SimpleGeomForm, NewDocumentForm, RemoveModalForm, RecordModalForm
from konova.sub_settings.django_settings import DEFAULT_DATE_FORMAT
from konova.utils.documents import remove_document, get_document
from konova.utils.message_templates import FORM_INVALID, INTERVENTION_INVALID
from konova.utils.message_templates import INTERVENTION_INVALID
from konova.utils.user_checks import in_group
@ -68,7 +68,10 @@ def new_view(request: HttpRequest):
if generated_identifier != intervention.identifier:
messages.info(
request,
_("The identifier '{}' had to be changed to '{}' since another entry has been added in the meanwhile, which uses this identifier")
_("The identifier '{}' had to be changed to '{}' since another entry has been added in the meanwhile, which uses this identifier").format(
generated_identifier,
intervention.identifier
)
)
messages.success(request, _("Intervention {} added").format(intervention.identifier))
return redirect("intervention:index")
@ -80,11 +83,30 @@ def new_view(request: HttpRequest):
context = {
"data_form": data_form,
"geom_form": geom_form,
"url": reverse("intervention:new-id")
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required
def new_id_view(request: HttpRequest):
""" JSON endpoint
Provides fetching of free identifiers for e.g. AJAX calls
"""
tmp_intervention = Intervention()
identifier = tmp_intervention._generate_new_identifier()
while Intervention.objects.filter(identifier=identifier).exists():
identifier = tmp_intervention._generate_new_identifier()
return JsonResponse(
data={
"identifier": identifier
}
)
@login_required
def new_document_view(request: HttpRequest, id: str):
""" Renders a form for uploading new documents
@ -221,20 +243,17 @@ def edit_view(request: HttpRequest, id: str):
"""
template = "intervention/new/view.html"
# Get object from db
intervention = get_object_or_404(Intervention, id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditInterventionForm(request.POST or None, instance=intervention)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=intervention)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
generated_identifier = data_form.cleaned_data.get("identifier", None)
# The data form takes the geom form for processing, as well as the performing user
intervention = data_form.save(request.user, geom_form)
if generated_identifier != intervention.identifier:
messages.info(
request,
_("The identifier '{}' had to be changed to '{}' since another entry has been added in the meanwhile, which uses this identifier")
)
messages.success(request, _("Intervention {} added").format(intervention.identifier))
return redirect("intervention:index")
messages.success(request, _("Intervention {} edited").format(intervention.identifier))
return redirect("intervention:open", id=intervention.id)
else:
messages.error(request, _("Invalid input"))
else:
@ -342,7 +361,7 @@ def create_share_view(request: HttpRequest, id: str):
"""
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(
request,
msg_success=_("Share settings updated")
@ -361,7 +380,7 @@ def run_check_view(request: HttpRequest, id: str):
"""
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(
request,
msg_success=_("Check performed"),
@ -381,7 +400,7 @@ def new_revocation_view(request: HttpRequest, id: str):
"""
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(
request,
msg_success=_("Revocation added")
@ -425,7 +444,7 @@ def new_deduction_view(request: HttpRequest, id: str):
"""
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(
request,
msg_success=_("Deduction added")

View File

@ -43,7 +43,6 @@ class BaseForm(forms.Form):
instance = None # The data holding model object
form_attrs = {} # Holds additional attributes, that can be used in the template
has_required_fields = False # Automatically set. Triggers hint rendering in templates
full_width_fields = False # w-100 for all input fields
def __init__(self, *args, **kwargs):
self.instance = kwargs.pop("instance", None)
@ -56,11 +55,6 @@ class BaseForm(forms.Form):
self.has_required_fields = True
break
if self.full_width_fields:
# Automatically add bootstrap w-100 class for maximum width of form fields in modals
for key, val in self.fields.items():
self.add_widget_html_class(key, "w-100")
@abstractmethod
def save(self):
# To be implemented in subclasses!
@ -276,9 +270,9 @@ class SimpleGeomForm(BaseForm):
geom = Polygon.from_bbox([0, 0, 0, 0])
# Zoom out to a very high level, so the user can see directly that there is no geometry for this entry
self.fields["geom"].widget.attrs["default_zoom"] = 1
if read_only:
self.initialize_form_field("geom", geom)
self.area = geom.area
if read_only:
self.fields["geom"].disabled = True
def save(self, action: UserActionLogEntry):
@ -326,6 +320,11 @@ class NewDocumentForm(BaseModalForm):
label=_("Title"),
label_suffix=_(""),
max_length=500,
widget=forms.TextInput(
attrs={
"class": "form-control",
}
)
)
creation_date = forms.DateField(
label=_("Created on"),
@ -335,6 +334,7 @@ class NewDocumentForm(BaseModalForm):
attrs={
"type": "date",
"data-provide": "datepicker",
"class": "form-control",
},
format="%d.%m.%Y"
)
@ -345,7 +345,7 @@ class NewDocumentForm(BaseModalForm):
help_text=_("Must be smaller than 15 Mb"),
widget=forms.FileInput(
attrs={
"class": "w-75"
"class": "form-control-file",
}
),
)
@ -359,6 +359,7 @@ class NewDocumentForm(BaseModalForm):
attrs={
"cols": 30,
"rows": 5,
"class": "form-control",
}
)
)
@ -370,9 +371,6 @@ class NewDocumentForm(BaseModalForm):
Ema: EmaDocument,
}
# Define w-100 for all form fields
full_width_fields = True
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_title = _("Add new document")

View File

@ -60,13 +60,7 @@ a {
color: var(--rlp-red);
}
input[type=text], input[type=date] {
border: 1px solid gray;
border-radius: 0.2rem;
padding: 0.3rem 0.5rem;
}
input:focus, textarea:focus, select:focus{
.form-control:focus{
outline: none;
border-color: var(--rlp-red);
box-shadow: 0 0 3px var(--rlp-red);

View File

@ -0,0 +1,22 @@
{% load i18n fontawesome_5 %}
<div class="input-group w-100" title="{{ widget.value|stringformat:'s' }}">
<input id="gen-id-input" aria-describedby="gen-id-btn" type="{{ widget.type }}" name="{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}{% include "django/forms/widgets/attrs.html" %}>
<div class="input-group-append" onclick="fetchNewIdentifier()">
<span id="gen-id-btn" class="btn btn-default" value="{% trans 'Generate new' %}" title="{% trans 'Generate new' %}">{% fa5_icon 'dice' %}</span>
</div>
</div>
<script>
function fetchNewIdentifier() {
fetch("{{ widget.attrs.url }}")
.then(function(response){
return response.json();
})
.then(function(data){
document.getElementById("gen-id-input").value = data["identifier"];
})
.catch(function(error){
console.log(error);
});
}
</script>

Binary file not shown.

View File

@ -3,21 +3,22 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#: compensation/filters.py:71 compensation/forms.py:47 compensation/forms.py:52
#: compensation/forms.py:67 compensation/forms.py:264 compensation/forms.py:345
#: compensation/filters.py:71 compensation/forms.py:47 compensation/forms.py:57
#: compensation/forms.py:73 compensation/forms.py:276 compensation/forms.py:363
#: intervention/filters.py:26 intervention/filters.py:40
#: intervention/filters.py:47 intervention/filters.py:48
#: intervention/forms.py:56 intervention/forms.py:151 intervention/forms.py:162
#: intervention/forms.py:422 intervention/forms.py:434
#: intervention/forms.py:447 konova/forms.py:140 konova/forms.py:250
#: konova/forms.py:296 konova/forms.py:331 konova/forms.py:336
#: konova/forms.py:348 konova/forms.py:360 konova/forms.py:380 user/forms.py:38
#: intervention/forms/forms.py:53 intervention/forms/forms.py:151
#: intervention/forms/forms.py:163 intervention/forms/modalForms.py:107
#: intervention/forms/modalForms.py:120 intervention/forms/modalForms.py:133
#: konova/forms.py:140 konova/forms.py:244 konova/forms.py:294
#: konova/forms.py:321 konova/forms.py:331 konova/forms.py:344
#: konova/forms.py:356 konova/forms.py:377 user/forms.py:38
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-23 12:46+0200\n"
"POT-Creation-Date: 2021-09-27 13:56+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -35,16 +36,16 @@ msgstr "Nur unverzeichnete anzeigen"
msgid "in Euro"
msgstr "in Euro"
#: compensation/forms.py:51
#: compensation/forms.py:56
#: intervention/templates/intervention/detail/includes/payments.html:31
msgid "Due on"
msgstr "Fällig am"
#: compensation/forms.py:54
#: compensation/forms.py:59
msgid "Due on which date"
msgstr "Zahlung wird an diesem Datum erwartet"
#: compensation/forms.py:66 compensation/forms.py:263 compensation/forms.py:344
#: compensation/forms.py:72 compensation/forms.py:275 compensation/forms.py:362
#: compensation/templates/compensation/detail/compensation/includes/actions.html:34
#: compensation/templates/compensation/detail/compensation/includes/deadlines.html:34
#: compensation/templates/compensation/detail/compensation/includes/documents.html:31
@ -54,115 +55,115 @@ msgstr "Zahlung wird an diesem Datum erwartet"
#: ema/templates/ema/detail/includes/actions.html:34
#: ema/templates/ema/detail/includes/deadlines.html:34
#: ema/templates/ema/detail/includes/documents.html:31
#: intervention/forms.py:446
#: intervention/forms/forms.py:175 intervention/forms/modalForms.py:132
#: intervention/templates/intervention/detail/includes/documents.html:31
#: intervention/templates/intervention/detail/includes/payments.html:34
#: intervention/templates/intervention/detail/includes/revocation.html:38
#: konova/forms.py:359
#: konova/forms.py:355
msgid "Comment"
msgstr "Kommentar"
#: compensation/forms.py:68 compensation/forms.py:265 compensation/forms.py:346
#: intervention/forms.py:448 konova/forms.py:361
#: compensation/forms.py:74 compensation/forms.py:277 compensation/forms.py:364
#: intervention/forms/modalForms.py:134 konova/forms.py:357
msgid "Additional comment, maximum {} letters"
msgstr "Zusätzlicher Kommentar, maximal {} Zeichen"
#: compensation/forms.py:80
#: compensation/forms.py:86
msgid "Payment"
msgstr "Zahlung"
#: compensation/forms.py:81
#: compensation/forms.py:87
msgid "Add a payment for intervention '{}'"
msgstr "Neue Ersatzzahlung zu Eingriff '{}' hinzufügen"
#: compensation/forms.py:102
#: compensation/forms.py:108
msgid "If there is no date you can enter, please explain why."
msgstr "Falls Sie kein Datum angeben können, erklären Sie bitte weshalb."
#: compensation/forms.py:116
#: compensation/forms.py:122
msgid "Added payment"
msgstr "Zahlung hinzufügen"
#: compensation/forms.py:133 compensation/forms.py:145
#: compensation/forms.py:139 compensation/forms.py:151
msgid "Biotope Type"
msgstr "Biotoptyp"
#: compensation/forms.py:136
#: compensation/forms.py:142
msgid "Select the biotope type"
msgstr "Biotoptyp wählen"
#: compensation/forms.py:152
#: compensation/forms.py:158
#: compensation/templates/compensation/detail/compensation/includes/states-after.html:36
#: compensation/templates/compensation/detail/compensation/includes/states-before.html:36
#: compensation/templates/compensation/detail/eco_account/includes/states-after.html:36
#: compensation/templates/compensation/detail/eco_account/includes/states-before.html:36
#: ema/templates/ema/detail/includes/states-after.html:36
#: ema/templates/ema/detail/includes/states-before.html:36
#: intervention/forms.py:587
#: intervention/forms/modalForms.py:274
msgid "Surface"
msgstr "Fläche"
#: compensation/forms.py:155 intervention/forms.py:589
#: compensation/forms.py:161 intervention/forms/modalForms.py:276
msgid "in m²"
msgstr ""
#: compensation/forms.py:160
#: compensation/forms.py:171
msgid "New state"
msgstr "Neuer Zustand"
#: compensation/forms.py:161
#: compensation/forms.py:172
msgid "Insert data for the new state"
msgstr "Geben Sie die Daten des neuen Zustandes ein"
#: compensation/forms.py:169
#: compensation/forms.py:180
msgid "Added state"
msgstr "Zustand hinzugefügt"
#: compensation/forms.py:185 konova/forms.py:199
#: compensation/forms.py:196 konova/forms.py:193
msgid "Object removed"
msgstr "Objekt entfernt"
#: compensation/forms.py:236
#: compensation/forms.py:247
msgid "Deadline Type"
msgstr "Fristart"
#: compensation/forms.py:239
#: compensation/forms.py:250
msgid "Select the deadline type"
msgstr "Fristart wählen"
#: compensation/forms.py:248
#: compensation/forms.py:259
#: compensation/templates/compensation/detail/compensation/includes/deadlines.html:31
#: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31
#: ema/templates/ema/detail/includes/deadlines.html:31
#: intervention/forms.py:421
#: intervention/forms/modalForms.py:106
msgid "Date"
msgstr "Datum"
#: compensation/forms.py:251
#: compensation/forms.py:262
msgid "Select date"
msgstr "Datum wählen"
#: compensation/forms.py:276
#: compensation/forms.py:289
msgid "New deadline"
msgstr "Neue Frist"
#: compensation/forms.py:277
#: compensation/forms.py:290
msgid "Insert data for the new deadline"
msgstr "Geben Sie die Daten der neuen Frist ein"
#: compensation/forms.py:294
#: compensation/forms.py:307
msgid "Added deadline"
msgstr "Frist/Termin hinzugefügt"
#: compensation/forms.py:305
#: compensation/forms.py:318
msgid "Action Type"
msgstr "Maßnahmentyp"
#: compensation/forms.py:308
#: compensation/forms.py:321
msgid "Select the action type"
msgstr "Maßnahmentyp wählen"
#: compensation/forms.py:317
#: compensation/forms.py:330
#: compensation/templates/compensation/detail/compensation/includes/actions.html:37
#: compensation/templates/compensation/detail/compensation/includes/deadlines.html:37
#: compensation/templates/compensation/detail/compensation/includes/documents.html:34
@ -188,33 +189,33 @@ msgstr "Maßnahmentyp wählen"
msgid "Action"
msgstr "Aktionen"
#: compensation/forms.py:322
#: compensation/forms.py:335
msgid "Unit"
msgstr "Einheit"
#: compensation/forms.py:325
#: compensation/forms.py:338
msgid "Select the unit"
msgstr "Einheit wählen"
#: compensation/forms.py:334
#: compensation/forms.py:347
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:34
#: intervention/templates/intervention/detail/includes/deductions.html:31
msgid "Amount"
msgstr "Menge"
#: compensation/forms.py:337
#: compensation/forms.py:350
msgid "Insert the amount"
msgstr "Menge eingeben"
#: compensation/forms.py:357
#: compensation/forms.py:375
msgid "New action"
msgstr "Neue Maßnahme"
#: compensation/forms.py:358
#: compensation/forms.py:376
msgid "Insert data for the new action"
msgstr "Geben Sie die Daten der neuen Maßnahme ein"
#: compensation/forms.py:377
#: compensation/forms.py:395
msgid "Added action"
msgstr "Maßnahme hinzugefügt"
@ -257,34 +258,34 @@ msgstr ""
"Es wurde bereits mehr Fläche abgebucht, als Sie nun als abbuchbar einstellen "
"wollen. Kontaktieren Sie die für die Abbuchungen verantwortlichen Nutzer!"
#: compensation/tables.py:24 compensation/tables.py:164 ema/tables.py:28
#: intervention/forms.py:37 intervention/tables.py:23
#: compensation/tables.py:25 compensation/tables.py:167 ema/tables.py:28
#: intervention/forms/forms.py:27 intervention/tables.py:23
#: intervention/templates/intervention/detail/includes/compensations.html:30
msgid "Identifier"
msgstr "Kennung"
#: compensation/tables.py:29 compensation/tables.py:169
#: compensation/tables.py:30 compensation/tables.py:172
#: compensation/templates/compensation/detail/compensation/includes/documents.html:28
#: compensation/templates/compensation/detail/compensation/view.html:31
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:28
#: compensation/templates/compensation/detail/eco_account/view.html:31
#: ema/tables.py:33 ema/templates/ema/detail/includes/documents.html:28
#: ema/templates/ema/detail/view.html:24 intervention/forms.py:43
#: ema/templates/ema/detail/view.html:24 intervention/forms/forms.py:39
#: intervention/tables.py:28
#: intervention/templates/intervention/detail/includes/compensations.html:33
#: intervention/templates/intervention/detail/includes/documents.html:28
#: intervention/templates/intervention/detail/view.html:31 konova/forms.py:330
#: intervention/templates/intervention/detail/view.html:31 konova/forms.py:320
msgid "Title"
msgstr "Bezeichnung"
#: compensation/tables.py:34
#: compensation/tables.py:35
#: compensation/templates/compensation/detail/compensation/view.html:43
#: intervention/tables.py:33
#: intervention/templates/intervention/detail/view.html:68 user/models.py:48
msgid "Checked"
msgstr "Geprüft"
#: compensation/tables.py:40 compensation/tables.py:179
#: compensation/tables.py:41 compensation/tables.py:182
#: compensation/templates/compensation/detail/compensation/view.html:57
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:31
#: compensation/templates/compensation/detail/eco_account/view.html:44
@ -294,82 +295,82 @@ msgstr "Geprüft"
msgid "Recorded"
msgstr "Verzeichnet"
#: compensation/tables.py:46 compensation/tables.py:185 ema/tables.py:44
#: compensation/tables.py:47 compensation/tables.py:188 ema/tables.py:44
#: intervention/tables.py:51
msgid "Editable"
msgstr "Freigegeben"
#: compensation/tables.py:52 compensation/tables.py:191 ema/tables.py:50
#: compensation/tables.py:53 compensation/tables.py:194 ema/tables.py:50
#: intervention/tables.py:57
msgid "Last edit"
msgstr "Zuletzt bearbeitet"
#: compensation/tables.py:61
#: compensation/tables.py:62
#: intervention/templates/intervention/detail/includes/compensations.html:8
msgid "Compensations"
msgstr "Kompensationen"
#: compensation/tables.py:83 compensation/tables.py:222 ema/tables.py:82
#: compensation/tables.py:84 compensation/tables.py:225 ema/tables.py:82
#: intervention/tables.py:88
msgid "Open {}"
msgstr "Öffne {}"
#: compensation/tables.py:83
#: compensation/tables.py:84
#: compensation/templates/compensation/detail/compensation/view.html:19
#: konova/templates/konova/home.html:49 templates/navbar.html:28
msgid "Compensation"
msgstr "Kompensation"
#: compensation/tables.py:104 intervention/tables.py:107
#: compensation/tables.py:105 intervention/tables.py:107
msgid "Not checked yet"
msgstr "Noch nicht geprüft"
#: compensation/tables.py:109 intervention/tables.py:112
#: compensation/tables.py:110 intervention/tables.py:112
msgid "Checked on {} by {}"
msgstr "Am {} von {} geprüft worden"
#: compensation/tables.py:128
#: compensation/tables.py:129
#: compensation/templates/compensation/detail/compensation/view.html:60
#: compensation/templates/compensation/detail/eco_account/view.html:47
#: ema/tables.py:101 ema/templates/ema/detail/view.html:31
#: intervention/models.py:352 intervention/tables.py:131
#: intervention/models.py:379 intervention/tables.py:131
#: intervention/templates/intervention/detail/view.html:85
msgid "Not recorded yet"
msgstr "Noch nicht verzeichnet"
#: compensation/tables.py:133 compensation/tables.py:260 ema/tables.py:106
#: intervention/models.py:357 intervention/tables.py:136
#: compensation/tables.py:134 compensation/tables.py:263 ema/tables.py:106
#: intervention/models.py:384 intervention/tables.py:136
msgid "Recorded on {} by {}"
msgstr "Am {} von {} verzeichnet worden"
#: compensation/tables.py:156 compensation/tables.py:283 ema/tables.py:129
#: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:129
#: intervention/tables.py:159
msgid "Full access granted"
msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden"
#: compensation/tables.py:156 compensation/tables.py:283 ema/tables.py:129
#: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:129
#: intervention/tables.py:159
msgid "Access not granted"
msgstr "Nicht freigegeben - Datensatz nur lesbar"
#: compensation/tables.py:174
#: compensation/tables.py:177
#: compensation/templates/compensation/detail/eco_account/view.html:35
#: konova/templates/konova/custom_widgets/progressbar.html:3
msgid "Available"
msgstr "Verfügbar"
#: compensation/tables.py:200
#: compensation/tables.py:203
msgid "Eco Accounts"
msgstr "Ökokonten"
#: compensation/tables.py:222
#: compensation/tables.py:225
#: compensation/templates/compensation/detail/eco_account/view.html:19
#: intervention/forms.py:571 intervention/forms.py:578
#: intervention/forms/modalForms.py:258 intervention/forms/modalForms.py:265
#: konova/templates/konova/home.html:88 templates/navbar.html:34
msgid "Eco-account"
msgstr "Ökokonto"
#: compensation/tables.py:255
#: compensation/tables.py:258
msgid "Not recorded yet. Can not be used for deductions, yet."
msgstr ""
"Noch nicht verzeichnet. Kann noch nicht für Abbuchungen genutzt werden."
@ -477,7 +478,7 @@ msgstr "Dokumente"
#: compensation/templates/compensation/detail/eco_account/includes/documents.html:14
#: ema/templates/ema/detail/includes/documents.html:14
#: intervention/templates/intervention/detail/includes/documents.html:14
#: konova/forms.py:379
#: konova/forms.py:376
msgid "Add new document"
msgstr "Neues Dokument hinzufügen"
@ -582,7 +583,7 @@ msgstr "Zuletzt bearbeitet"
#: compensation/templates/compensation/detail/compensation/view.html:92
#: compensation/templates/compensation/detail/eco_account/view.html:91
#: ema/templates/ema/detail/view.html:82 intervention/forms.py:355
#: ema/templates/ema/detail/view.html:82 intervention/forms/modalForms.py:40
#: intervention/templates/intervention/detail/view.html:116
msgid "Shared with"
msgstr "Freigegeben für"
@ -643,19 +644,19 @@ msgid "Missing"
msgstr "Fehlt"
#: compensation/templates/compensation/detail/eco_account/view.html:58
#: ema/templates/ema/detail/view.html:42 intervention/forms.py:101
#: ema/templates/ema/detail/view.html:42 intervention/forms/forms.py:98
#: intervention/templates/intervention/detail/view.html:56
msgid "Conservation office"
msgstr "Eintragungsstelle"
#: compensation/templates/compensation/detail/eco_account/view.html:62
#: ema/templates/ema/detail/view.html:46 intervention/forms.py:127
#: ema/templates/ema/detail/view.html:46 intervention/forms/forms.py:125
#: intervention/templates/intervention/detail/view.html:60
msgid "Conservation office file number"
msgstr "Aktenzeichen Eintragungsstelle"
#: compensation/templates/compensation/detail/eco_account/view.html:66
#: ema/templates/ema/detail/view.html:50 intervention/forms.py:138
#: ema/templates/ema/detail/view.html:50 intervention/forms/forms.py:137
#: intervention/templates/intervention/detail/view.html:64
msgid "Intervention handler"
msgstr "Eingriffsverursacher"
@ -667,7 +668,7 @@ msgstr ""
#: compensation/views/compensation_views.py:123
#: compensation/views/eco_account_views.py:190 ema/views.py:128
#: intervention/views.py:393
#: intervention/views.py:428
msgid "Log"
msgstr "Log"
@ -677,7 +678,7 @@ msgstr "Kompensation entfernt"
#: compensation/views/compensation_views.py:163
#: compensation/views/eco_account_views.py:289 ema/views.py:250
#: intervention/views.py:96
#: intervention/views.py:124
msgid "Document added"
msgstr "Dokument hinzugefügt"
@ -713,16 +714,16 @@ msgid "Deduction removed"
msgstr "Abbuchung entfernt"
#: compensation/views/eco_account_views.py:210 ema/views.py:171
#: intervention/views.py:433
#: intervention/views.py:468
msgid "{} unrecorded"
msgstr "{} entzeichnet"
#: compensation/views/eco_account_views.py:210 ema/views.py:171
#: intervention/views.py:433
#: intervention/views.py:468
msgid "{} recorded"
msgstr "{} verzeichnet"
#: compensation/views/eco_account_views.py:346 intervention/views.py:415
#: compensation/views/eco_account_views.py:346 intervention/views.py:450
msgid "Deduction added"
msgstr "Abbuchung hinzugefügt"
@ -774,129 +775,133 @@ msgstr "Gemarkung"
msgid "Search for district"
msgstr "Nach Gemarkung suchen"
#: intervention/forms.py:40
#: intervention/forms/forms.py:30
msgid "Generated automatically"
msgstr "Automatisch generiert"
#: intervention/forms.py:45
#: intervention/forms/forms.py:41
msgid "An explanatory name"
msgstr "Aussagekräftiger Titel"
#: intervention/forms.py:49
#: intervention/forms/forms.py:45
msgid "Construction XY; Location ABC"
msgstr "Bauvorhaben XY; Flur ABC"
#: intervention/forms.py:54
#: intervention/forms/forms.py:51
#: intervention/templates/intervention/detail/view.html:35
msgid "Process type"
msgstr "Verfahrenstyp"
#: intervention/forms.py:70
#: intervention/forms/forms.py:67
#: intervention/templates/intervention/detail/view.html:39
msgid "Law"
msgstr "Gesetz"
#: intervention/forms.py:72
#: intervention/forms/forms.py:69
msgid "Multiple selection possible"
msgstr "Mehrfachauswahl möglich"
#: intervention/forms.py:86
#: intervention/forms/forms.py:83
#: intervention/templates/intervention/detail/view.html:48
msgid "Registration office"
msgstr "Zulassungsbehörde"
#: intervention/forms.py:116
#: intervention/forms/forms.py:113
#: intervention/templates/intervention/detail/view.html:52
msgid "Registration office file number"
msgstr "Aktenzeichen Zulassungsbehörde"
#: intervention/forms.py:122
#: intervention/forms/forms.py:119
msgid "ZB-123/ABC.456"
msgstr ""
#: intervention/forms.py:133
#: intervention/forms/forms.py:131
msgid "ETS-123/ABC.456"
msgstr ""
#: intervention/forms.py:142
#: intervention/forms/forms.py:141
msgid "Who performs the intervention"
msgstr "Wer führt den Eingriff durch"
#: intervention/forms.py:145
#: intervention/forms/forms.py:144
msgid "Company Mustermann"
msgstr "Firma Mustermann"
#: intervention/forms.py:150
#: intervention/forms/forms.py:150
#: intervention/templates/intervention/detail/view.html:96
msgid "Registration date"
msgstr "Datum Zulassung bzw. Satzungsbeschluss"
#: intervention/forms.py:161
#: intervention/forms/forms.py:162
#: intervention/templates/intervention/detail/view.html:100
msgid "Binding on"
msgstr "Datum Bestandskraft"
#: intervention/forms.py:174
#: intervention/forms/forms.py:177
msgid "Additional comment"
msgstr "Zusätzlicher Kommentar"
#: intervention/forms/forms.py:188
msgid "New intervention"
msgstr "Neuer Eingriff"
#: intervention/forms.py:248
#: intervention/forms/forms.py:269
msgid "Edit intervention"
msgstr "Eingriff bearbeiten"
#: intervention/forms.py:344
#: intervention/forms/modalForms.py:28
msgid "Share link"
msgstr "Freigabelink"
#: intervention/forms.py:346
#: intervention/forms/modalForms.py:30
msgid "Send this link to users who you want to have writing access on the data"
msgstr "Andere Nutzer erhalten über diesen Link Zugriff auf die Daten"
#: intervention/forms.py:358
#: intervention/forms/modalForms.py:43
msgid "Remove check to remove access for this user"
msgstr "Wählen Sie die Nutzer ab, die keinen Zugriff mehr haben sollen"
#: intervention/forms.py:369
#: intervention/forms/modalForms.py:54
#: intervention/templates/intervention/detail/includes/controls.html:15
msgid "Share"
msgstr "Freigabe"
#: intervention/forms.py:370
#: intervention/forms/modalForms.py:55
msgid "Share settings for {}"
msgstr "Freigabe Einstellungen für {}"
#: intervention/forms.py:423
#: intervention/forms/modalForms.py:108
msgid "Date of revocation"
msgstr "Datum des Widerspruchs"
#: intervention/forms.py:433
#: intervention/forms/modalForms.py:119
#: intervention/templates/intervention/detail/includes/revocation.html:35
msgid "Document"
msgstr "Dokument"
#: intervention/forms.py:436 konova/forms.py:349
#: intervention/forms/modalForms.py:122 konova/forms.py:345
msgid "Must be smaller than 15 Mb"
msgstr "Muss kleiner als 15 Mb sein"
#: intervention/forms.py:459
#: intervention/forms/modalForms.py:146
#: intervention/templates/intervention/detail/includes/revocation.html:18
msgid "Add revocation"
msgstr "Widerspruch hinzufügen"
#: intervention/forms.py:499
#: intervention/forms/modalForms.py:186
msgid "Checked intervention data"
msgstr "Eingriffsdaten geprüft"
#: intervention/forms.py:505
#: intervention/forms/modalForms.py:192
msgid "Checked compensations data and payments"
msgstr "Kompensationen und Zahlungen geprüft"
#: intervention/forms.py:513
#: intervention/forms/modalForms.py:200
#: intervention/templates/intervention/detail/includes/controls.html:19
msgid "Run check"
msgstr "Prüfung vornehmen"
#: intervention/forms.py:514 konova/forms.py:433
#: intervention/forms/modalForms.py:201 konova/forms.py:430
msgid ""
"I, {} {}, confirm that all necessary control steps have been performed by "
"myself."
@ -904,30 +909,30 @@ msgstr ""
"Ich, {} {}, bestätige, dass die notwendigen Kontrollschritte durchgeführt "
"wurden:"
#: intervention/forms.py:573
#: intervention/forms/modalForms.py:260
msgid "Only recorded accounts can be selected for deductions"
msgstr "Nur verzeichnete Ökokonten können für Abbuchungen verwendet werden."
#: intervention/forms.py:592 intervention/forms.py:599
#: intervention/forms/modalForms.py:284 intervention/forms/modalForms.py:291
#: intervention/tables.py:88
#: intervention/templates/intervention/detail/view.html:19
#: konova/templates/konova/home.html:11 templates/navbar.html:22
msgid "Intervention"
msgstr "Eingriff"
#: intervention/forms.py:594
#: intervention/forms/modalForms.py:286
msgid "Only shared interventions can be selected"
msgstr "Nur freigegebene Eingriffe können gewählt werden"
#: intervention/forms.py:607
#: intervention/forms/modalForms.py:299
msgid "New Deduction"
msgstr "Neue Abbuchung"
#: intervention/forms.py:608
#: intervention/forms/modalForms.py:300
msgid "Enter the information for a new deduction from a chosen eco-account"
msgstr "Geben Sie die Informationen für eine neue Abbuchung ein."
#: intervention/forms.py:644
#: intervention/forms/modalForms.py:336
msgid ""
"Eco-account {} is not recorded yet. You can only deduct from recorded "
"accounts."
@ -935,7 +940,7 @@ msgstr ""
"Ökokonto {} ist noch nicht verzeichnet. Abbuchungen können nur von "
"verzeichneten Ökokonten erfolgen."
#: intervention/forms.py:657
#: intervention/forms/modalForms.py:349
msgid ""
"The account {} has not enough surface for a deduction of {} m². There are "
"only {} m² left"
@ -943,31 +948,31 @@ msgstr ""
"Das Ökokonto {} hat für eine Abbuchung von {} m² nicht ausreichend "
"Restfläche. Es stehen noch {} m² zur Verfügung."
#: intervention/models.py:298
#: intervention/models.py:325
msgid "Registration office file number missing"
msgstr "Aktenzeichen Zulassungsbehörde fehlt"
#: intervention/models.py:301
#: intervention/models.py:328
msgid "Conservation office file number missing"
msgstr "Aktenzeichen Naturschutzbehörde fehlt"
#: intervention/models.py:304
#: intervention/models.py:331
msgid "Responsible data missing"
msgstr "Daten zu Verantwortlichen fehlen"
#: intervention/models.py:318
#: intervention/models.py:345
msgid "Revocation exists"
msgstr "Widerspruch liegt vor"
#: intervention/models.py:321
#: intervention/models.py:348
msgid "Registration date missing"
msgstr "Datum Zulassung bzw. Satzungsbeschluss fehlt"
#: intervention/models.py:324
#: intervention/models.py:351
msgid "Binding on missing"
msgstr "Datum Bestandskraft fehlt"
#: intervention/models.py:326
#: intervention/models.py:353
msgid "Legal data missing"
msgstr "Rechtliche Daten fehlen"
@ -1040,19 +1045,27 @@ msgstr "Widerspruch entfernen"
msgid "Exists"
msgstr "vorhanden"
#: intervention/views.py:67
#: intervention/views.py:71
msgid ""
"The identifier '{}' had to be changed to '{}' since another entry has been "
"added in the meanwhile, which uses this identifier"
msgstr ""
"Die Kennung '{}' musste zu '{}' geändert werden, da ein anderer Eintrag in "
"der Zwischenzeit angelegt wurde, welcher diese Kennung nun bereits verwendet"
#: intervention/views.py:76
msgid "Intervention {} added"
msgstr "Eingriff {} hinzugefügt"
#: intervention/views.py:70 intervention/views.py:226
#: intervention/views.py:79 intervention/views.py:258
msgid "Invalid input"
msgstr "Eingabe fehlerhaft"
#: intervention/views.py:184
#: intervention/views.py:212
msgid "This intervention has a revocation from {}"
msgstr "Es existiert ein Widerspruch vom {}"
#: intervention/views.py:200
#: intervention/views.py:228
msgid ""
"Remember: This data has not been shared with you, yet. This means you can "
"only read but can not edit or perform any actions like running a check or "
@ -1062,43 +1075,43 @@ msgstr ""
"bedeutet, dass Sie nur lesenden Zugriff hierauf haben und weder bearbeiten, "
"noch Prüfungen durchführen oder verzeichnen können."
#: intervention/views.py:223
msgid "{} edited"
msgstr "{} bearbeitet"
#: intervention/views.py:255
msgid "Intervention {} edited"
msgstr "Eingriff {} bearbeitet"
#: intervention/views.py:252
#: intervention/views.py:287
msgid "{} removed"
msgstr "{} entfernt"
#: intervention/views.py:273
#: intervention/views.py:308
msgid "Revocation removed"
msgstr "Widerspruch entfernt"
#: intervention/views.py:299
#: intervention/views.py:334
msgid "{} has already been shared with you"
msgstr "{} wurde bereits für Sie freigegeben"
#: intervention/views.py:304
#: intervention/views.py:339
msgid "{} has been shared with you"
msgstr "{} ist nun für Sie freigegeben"
#: intervention/views.py:311
#: intervention/views.py:346
msgid "Share link invalid"
msgstr "Freigabelink ungültig"
#: intervention/views.py:332
#: intervention/views.py:367
msgid "Share settings updated"
msgstr "Freigabe Einstellungen aktualisiert"
#: intervention/views.py:351
#: intervention/views.py:386
msgid "Check performed"
msgstr "Prüfung durchgeführt"
#: intervention/views.py:371
#: intervention/views.py:406
msgid "Revocation added"
msgstr "Widerspruch hinzugefügt"
#: intervention/views.py:438
#: intervention/views.py:473
msgid "There are errors on this intervention:"
msgstr "Es liegen Fehler in diesem Eingriff vor:"
@ -1127,11 +1140,11 @@ msgstr "Hierfür müssen Sie einer anderen Nutzergruppe angehören!"
msgid "Not editable"
msgstr "Nicht editierbar"
#: konova/forms.py:139 konova/forms.py:295
#: konova/forms.py:139 konova/forms.py:293
msgid "Confirm"
msgstr "Bestätige"
#: konova/forms.py:151 konova/forms.py:304
#: konova/forms.py:151 konova/forms.py:302
msgid "Remove"
msgstr "Löschen"
@ -1139,48 +1152,48 @@ msgstr "Löschen"
msgid "You are about to remove {} {}"
msgstr "Sie sind dabei {} {} zu löschen"
#: konova/forms.py:249 templates/form/main_data_collapse_form.html:47
#: konova/forms.py:243 templates/form/main_data_collapse_form.html:47
msgid "Geometry"
msgstr "Geometrie"
#: konova/forms.py:305
#: konova/forms.py:303
msgid "Are you sure?"
msgstr "Sind Sie sicher?"
#: konova/forms.py:335
#: konova/forms.py:330
msgid "Created on"
msgstr "Erstellt"
#: konova/forms.py:337
#: konova/forms.py:332
msgid "When has this file been created? Important for photos."
msgstr "Wann wurde diese Datei erstellt oder das Foto aufgenommen?"
#: konova/forms.py:347
#: konova/forms.py:343
#: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231
msgid "File"
msgstr "Datei"
#: konova/forms.py:410
#: konova/forms.py:407
msgid "Added document"
msgstr "Dokument hinzugefügt"
#: konova/forms.py:424
#: konova/forms.py:421
msgid "Confirm record"
msgstr "Verzeichnen bestätigen"
#: konova/forms.py:432
#: konova/forms.py:429
msgid "Record data"
msgstr "Daten verzeichnen"
#: konova/forms.py:439
#: konova/forms.py:436
msgid "Confirm unrecord"
msgstr "Entzeichnen bestätigen"
#: konova/forms.py:440
#: konova/forms.py:437
msgid "Unrecord data"
msgstr "Daten entzeichnen"
#: konova/forms.py:441
#: konova/forms.py:438
msgid "I, {} {}, confirm that this data must be unrecorded."
msgstr ""
"Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen."
@ -1209,22 +1222,26 @@ msgstr "Wenn meine freigegebenen Daten gelöscht wurden"
msgid "On registered data edited"
msgstr "Wenn meine freigegebenen Daten bearbeitet wurden"
#: konova/models.py:190
#: konova/models.py:204
msgid "Finished"
msgstr "Umgesetzt bis"
#: konova/models.py:191
#: konova/models.py:205
msgid "Maintain"
msgstr "Unterhaltung bis"
#: konova/models.py:192
#: konova/models.py:206
msgid "Control"
msgstr "Kontrolle am"
#: konova/models.py:193
#: konova/models.py:207
msgid "Other"
msgstr "Sonstige"
#: konova/templates/konova/custom_widgets/generate-content-input.html:6
msgid "Generate new"
msgstr "Neu generieren"
#: konova/templates/konova/custom_widgets/text-to-clipboard-input.html:6
msgid "Copy to clipboard"
msgstr "In Zwischenablage kopieren"
@ -2720,6 +2737,9 @@ msgstr ""
msgid "A fontawesome icon field"
msgstr ""
#~ msgid "{} edited"
#~ msgstr "{} bearbeitet"
#~ msgid "Map"
#~ msgstr "Karte"
@ -2771,9 +2791,6 @@ msgstr ""
#~ msgid "Actions"
#~ msgstr "Aktionen"
#~ msgid "Additional comment"
#~ msgstr "Zusätzlicher Kommentar"
#~ msgid "Missing surfaces: "
#~ msgstr "Fehlende Flächen: "