104_Class_based_views #200
@ -55,7 +55,7 @@ class TimespanReportForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codes-conservation-office-autocomplete",
|
url="codelist:conservation-office-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection")
|
"data-placeholder": _("Click for selection")
|
||||||
}
|
}
|
||||||
|
7
codelist/autocomplete/__init__.py
Normal file
7
codelist/autocomplete/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
74
codelist/autocomplete/base.py
Normal file
74
codelist/autocomplete/base.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from dal_select2.views import Select2GroupQuerySetView
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from codelist.models import KonovaCode
|
||||||
|
|
||||||
|
|
||||||
|
class KonovaCodeAutocomplete(Select2GroupQuerySetView):
|
||||||
|
"""
|
||||||
|
Provides simple autocomplete functionality for codes
|
||||||
|
|
||||||
|
Parameter support:
|
||||||
|
* q: Search for a word inside long_name of a code
|
||||||
|
* c: Search inside a special codelist
|
||||||
|
|
||||||
|
"""
|
||||||
|
paginate_by = 50
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
""" Orders by a predefined value
|
||||||
|
|
||||||
|
Wrapped in a function to provide inheritance-based different orders
|
||||||
|
|
||||||
|
Args:
|
||||||
|
qs (QuerySet): The queryset to be ordered
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
qs (QuerySet): The ordered queryset
|
||||||
|
"""
|
||||||
|
return qs.order_by(
|
||||||
|
"long_name"
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.request.user.is_anonymous:
|
||||||
|
return KonovaCode.objects.none()
|
||||||
|
qs = KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_selectable=True,
|
||||||
|
is_leaf=True,
|
||||||
|
)
|
||||||
|
qs = self.order_by(qs)
|
||||||
|
if self.c:
|
||||||
|
qs = qs.filter(
|
||||||
|
code_lists__in=[self.c]
|
||||||
|
)
|
||||||
|
if self.q:
|
||||||
|
# Remove whitespaces from self.q and split input in all keywords (if multiple given)
|
||||||
|
q = dict.fromkeys(self.q.strip().split(" "))
|
||||||
|
# Create one filter looking up for all keys where all keywords can be found in the same result
|
||||||
|
_filter = Q()
|
||||||
|
for keyword in q:
|
||||||
|
q_or = Q()
|
||||||
|
q_or |= Q(long_name__icontains=keyword)
|
||||||
|
q_or |= Q(short_name__icontains=keyword)
|
||||||
|
q_or |= Q(parent__long_name__icontains=keyword)
|
||||||
|
q_or |= Q(parent__short_name__icontains=keyword)
|
||||||
|
q_or |= Q(parent__parent__long_name__icontains=keyword)
|
||||||
|
q_or |= Q(parent__parent__short_name__icontains=keyword)
|
||||||
|
_filter.add(q_or, Q.AND)
|
||||||
|
qs = qs.filter(_filter).distinct()
|
||||||
|
return qs
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return f"{result.long_name}"
|
||||||
|
|
||||||
|
def get_selected_result_label(self, result):
|
||||||
|
return f"{result.__str__()}"
|
110
codelist/autocomplete/biotope.py
Normal file
110
codelist/autocomplete/biotope.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
import collections
|
||||||
|
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
|
from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID
|
||||||
|
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
||||||
|
from konova.utils.message_templates import UNGROUPED
|
||||||
|
|
||||||
|
|
||||||
|
class BiotopeCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_BIOTOPES_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
""" Orders by a predefined value
|
||||||
|
|
||||||
|
Wrapped in a function to provide inheritance-based different orders
|
||||||
|
|
||||||
|
Args:
|
||||||
|
qs (QuerySet): The queryset to be ordered
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
qs (QuerySet): The ordered queryset
|
||||||
|
"""
|
||||||
|
return qs.order_by(
|
||||||
|
"short_name",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return f"{result.long_name} ({result.short_name})"
|
||||||
|
|
||||||
|
def get_results(self, context):
|
||||||
|
"""Return the options grouped by a common related model.
|
||||||
|
|
||||||
|
Raises ImproperlyConfigured if self.group_by_name is not configured
|
||||||
|
"""
|
||||||
|
if not self.group_by_related:
|
||||||
|
raise ImproperlyConfigured("Missing group_by_related.")
|
||||||
|
|
||||||
|
super_groups = collections.OrderedDict()
|
||||||
|
|
||||||
|
object_list = context['object_list']
|
||||||
|
|
||||||
|
for result in object_list:
|
||||||
|
group = result.parent if result.parent else None
|
||||||
|
group_name = f"{group.long_name} ({group.short_name})" if group else UNGROUPED
|
||||||
|
super_group = result.parent.parent if result.parent else None
|
||||||
|
super_group_name = f"{super_group.long_name} ({super_group.short_name})" if super_group else UNGROUPED
|
||||||
|
super_groups.setdefault(super_group_name, {})
|
||||||
|
super_groups[super_group_name].setdefault(group_name, [])
|
||||||
|
super_groups[super_group_name][group_name].append(result)
|
||||||
|
|
||||||
|
return [{
|
||||||
|
'id': None,
|
||||||
|
'text': super_group,
|
||||||
|
'children': [{
|
||||||
|
"id": None,
|
||||||
|
"text": group,
|
||||||
|
"children": [{
|
||||||
|
'id': self.get_result_value(result),
|
||||||
|
'text': self.get_result_label(result),
|
||||||
|
'selected_text': self.get_selected_result_label(result),
|
||||||
|
} for result in results]
|
||||||
|
} for group, results in groups.items()]
|
||||||
|
} for super_group, groups in super_groups.items()]
|
||||||
|
|
||||||
|
|
||||||
|
class BiotopeExtraCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
paginate_by = 200
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_BIOTOPES_EXTRA_CODES_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
""" Orders by a predefined value
|
||||||
|
|
||||||
|
Wrapped in a function to provide inheritance-based different orders
|
||||||
|
|
||||||
|
Args:
|
||||||
|
qs (QuerySet): The queryset to be ordered
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
qs (QuerySet): The ordered queryset
|
||||||
|
"""
|
||||||
|
return qs.order_by(
|
||||||
|
"long_name",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return f"{result.long_name} ({result.short_name})"
|
45
codelist/autocomplete/compensation_action.py
Normal file
45
codelist/autocomplete/compensation_action.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
||||||
|
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
||||||
|
|
||||||
|
|
||||||
|
class CompensationActionCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_COMPENSATION_ACTION_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
return qs.order_by(
|
||||||
|
"parent__long_name"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CompensationActionDetailCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
paginate_by = 200
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
return qs.order_by(
|
||||||
|
"long_name"
|
||||||
|
)
|
||||||
|
|
24
codelist/autocomplete/handler.py
Normal file
24
codelist/autocomplete/handler.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from codelist.settings import CODELIST_HANDLER_ID
|
||||||
|
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
||||||
|
|
||||||
|
|
||||||
|
class HandlerCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_HANDLER_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return result.long_name
|
24
codelist/autocomplete/law.py
Normal file
24
codelist/autocomplete/law.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from codelist.settings import CODELIST_LAW_ID
|
||||||
|
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
||||||
|
|
||||||
|
|
||||||
|
class LawCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_LAW_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return f"{result.long_name} ({result.short_name})"
|
41
codelist/autocomplete/office.py
Normal file
41
codelist/autocomplete/office.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_REGISTRATION_OFFICE_ID
|
||||||
|
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
||||||
|
|
||||||
|
|
||||||
|
class RegistrationOfficeCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_REGISTRATION_OFFICE_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def order_by(self, qs):
|
||||||
|
return qs.order_by(
|
||||||
|
"parent__long_name"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConservationOfficeCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_CONSERVATION_OFFICE_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_result_label(self, result):
|
||||||
|
return f"{result.long_name} ({result.short_name})"
|
21
codelist/autocomplete/process_type.py
Normal file
21
codelist/autocomplete/process_type.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from codelist.autocomplete.base import KonovaCodeAutocomplete
|
||||||
|
from codelist.settings import CODELIST_PROCESS_TYPE_ID
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessTypeCodeAutocomplete(KonovaCodeAutocomplete):
|
||||||
|
"""
|
||||||
|
Due to limitations of the django dal package, we need to subclass for each code list
|
||||||
|
"""
|
||||||
|
group_by_related = "parent"
|
||||||
|
related_field_name = "long_name"
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.c = CODELIST_PROCESS_TYPE_ID
|
||||||
|
super().__init__(*args, **kwargs)
|
@ -50,7 +50,7 @@ class KonovaCode(models.Model):
|
|||||||
|
|
||||||
def __str__(self, with_parent: bool = True):
|
def __str__(self, with_parent: bool = True):
|
||||||
ret_val = ""
|
ret_val = ""
|
||||||
if self.parent and with_parent:
|
if self.parent and self.parent.long_name and with_parent:
|
||||||
ret_val += self.parent.long_name + " > "
|
ret_val += self.parent.long_name + " > "
|
||||||
ret_val += self.long_name
|
ret_val += self.long_name
|
||||||
if self.short_name and self.short_name != self.long_name:
|
if self.short_name and self.short_name != self.long_name:
|
||||||
|
@ -7,8 +7,24 @@ Created on: 23.08.21
|
|||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
|
from codelist.autocomplete.biotope import BiotopeCodeAutocomplete, BiotopeExtraCodeAutocomplete
|
||||||
|
from codelist.autocomplete.compensation_action import CompensationActionDetailCodeAutocomplete, \
|
||||||
|
CompensationActionCodeAutocomplete
|
||||||
|
from codelist.autocomplete.handler import HandlerCodeAutocomplete
|
||||||
|
from codelist.autocomplete.law import LawCodeAutocomplete
|
||||||
|
from codelist.autocomplete.office import ConservationOfficeCodeAutocomplete, RegistrationOfficeCodeAutocomplete
|
||||||
|
from codelist.autocomplete.process_type import ProcessTypeCodeAutocomplete
|
||||||
|
|
||||||
app_name = "codelist"
|
app_name = "codelist"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path("atcmplt/codes/biotope", BiotopeCodeAutocomplete.as_view(), name="biotope-autocomplete"),
|
||||||
|
path("atcmplt/codes/biotope/extra", BiotopeExtraCodeAutocomplete.as_view(),
|
||||||
|
name="biotope-extra-type-autocomplete"),
|
||||||
|
path("atcmplt/codes/law", LawCodeAutocomplete.as_view(), name="law-autocomplete"),
|
||||||
|
path("atcmplt/codes/reg-off", RegistrationOfficeCodeAutocomplete.as_view(), name="registration-office-autocomplete"),
|
||||||
|
path("atcmplt/codes/cons-off", ConservationOfficeCodeAutocomplete.as_view(), name="conservation-office-autocomplete"),
|
||||||
|
path("atcmplt/codes/handler", HandlerCodeAutocomplete.as_view(), name="handler-autocomplete"),
|
||||||
|
path("atcmplt/codes/comp/action", CompensationActionCodeAutocomplete.as_view(), name="compensation-action-autocomplete"),
|
||||||
|
path("atcmplt/codes/comp/action/detail", CompensationActionDetailCodeAutocomplete.as_view(), name="compensation-action-detail-autocomplete"),
|
||||||
|
path("atcmplt/codes/prc-type", ProcessTypeCodeAutocomplete.as_view(), name="process-type-autocomplete"),
|
||||||
]
|
]
|
7
compensation/autocomplete/__init__.py
Normal file
7
compensation/autocomplete/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
34
compensation/autocomplete/eco_account.py
Normal file
34
compensation/autocomplete/eco_account.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from dal_select2.views import Select2QuerySetView
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountAutocomplete(Select2QuerySetView):
|
||||||
|
""" Autocomplete for ecoAccount entries
|
||||||
|
|
||||||
|
Only returns entries that are already recorded and not deleted
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.request.user.is_anonymous:
|
||||||
|
return EcoAccount.objects.none()
|
||||||
|
qs = EcoAccount.objects.filter(
|
||||||
|
deleted=None,
|
||||||
|
recorded__isnull=False,
|
||||||
|
).order_by(
|
||||||
|
"identifier"
|
||||||
|
)
|
||||||
|
if self.q:
|
||||||
|
qs = qs.filter(
|
||||||
|
Q(identifier__icontains=self.q) |
|
||||||
|
Q(title__icontains=self.q)
|
||||||
|
).distinct()
|
||||||
|
return qs
|
7
compensation/filters/__init__.py
Normal file
7
compensation/filters/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
@ -1,17 +1,14 @@
|
|||||||
"""
|
"""
|
||||||
Author: Michel Peltriaux
|
Author: Michel Peltriaux
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
Created on: 29.07.21
|
Created on: 18.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import django_filters
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django import forms
|
|
||||||
from django.db.models import QuerySet, Q
|
from django.db.models import QuerySet, Q
|
||||||
|
|
||||||
from konova.filters.mixins import ConservationOfficeTableFilterMixin
|
from konova.filters.table_filters import AbstractTableFilter, CheckboxTableFilter, QueryTableFilter, \
|
||||||
from konova.filters.table_filters import QueryTableFilter, CheckboxTableFilter, SelectionTableFilter, AbstractTableFilter
|
SelectionTableFilter
|
||||||
|
|
||||||
|
|
||||||
class SelectionCompensationTableFilter(SelectionTableFilter):
|
class SelectionCompensationTableFilter(SelectionTableFilter):
|
||||||
@ -114,71 +111,3 @@ class CompensationTableFilter(AbstractTableFilter):
|
|||||||
)
|
)
|
||||||
# Overwrite final queryset as well
|
# Overwrite final queryset as well
|
||||||
self.qs = self.checkbox_filter.qs
|
self.qs = self.checkbox_filter.qs
|
||||||
|
|
||||||
|
|
||||||
class CheckboxEcoAccountTableFilter(CheckboxTableFilter):
|
|
||||||
sr = django_filters.BooleanFilter(
|
|
||||||
method='filter_only_show_unrecorded',
|
|
||||||
label=_("Show only unrecorded"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
widget=forms.CheckboxInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-check-input",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def filter_only_show_unrecorded(self, queryset, name, value) -> QuerySet:
|
|
||||||
""" Filters queryset depending on value of 'show_recorded' setting
|
|
||||||
|
|
||||||
Args:
|
|
||||||
queryset ():
|
|
||||||
name ():
|
|
||||||
value ():
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
if value:
|
|
||||||
return queryset.filter(
|
|
||||||
recorded=None,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class SelectionEcoAccountTableFilter(ConservationOfficeTableFilterMixin):
|
|
||||||
""" Special selection table filter for eco accounts
|
|
||||||
|
|
||||||
EcoAccounts only need a selection filter for conservation office
|
|
||||||
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountTableFilter(AbstractTableFilter):
|
|
||||||
""" TableFilter for eco accounts
|
|
||||||
|
|
||||||
"""
|
|
||||||
def __init__(self, user=None, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.user = user
|
|
||||||
qs = kwargs.get("queryset", None)
|
|
||||||
request_data = kwargs.get("data", None)
|
|
||||||
|
|
||||||
# Pipe the queryset through all needed filters
|
|
||||||
self.selection_filter = SelectionEcoAccountTableFilter(
|
|
||||||
data=request_data,
|
|
||||||
queryset=qs,
|
|
||||||
)
|
|
||||||
self.query_filter = QueryTableFilter(
|
|
||||||
data=request_data,
|
|
||||||
queryset=self.selection_filter.qs,
|
|
||||||
)
|
|
||||||
self.checkbox_filter = CheckboxEcoAccountTableFilter(
|
|
||||||
user=user,
|
|
||||||
data=request_data,
|
|
||||||
queryset=self.query_filter.qs,
|
|
||||||
)
|
|
||||||
# Overwrite the final queryset result
|
|
||||||
self.qs = self.checkbox_filter.qs
|
|
82
compensation/filters/eco_account.py
Normal file
82
compensation/filters/eco_account.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django import forms
|
||||||
|
from django.db.models import QuerySet
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
import django_filters
|
||||||
|
|
||||||
|
from konova.filters.mixins.office import ConservationOfficeTableFilterMixin
|
||||||
|
from konova.filters.table_filters import AbstractTableFilter, CheckboxTableFilter, QueryTableFilter
|
||||||
|
|
||||||
|
|
||||||
|
class CheckboxEcoAccountTableFilter(CheckboxTableFilter):
|
||||||
|
sr = django_filters.BooleanFilter(
|
||||||
|
method='filter_only_show_unrecorded',
|
||||||
|
label=_("Show only unrecorded"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
widget=forms.CheckboxInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-check-input",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_only_show_unrecorded(self, queryset, name, value) -> QuerySet:
|
||||||
|
""" Filters queryset depending on value of 'show_recorded' setting
|
||||||
|
|
||||||
|
Args:
|
||||||
|
queryset ():
|
||||||
|
name ():
|
||||||
|
value ():
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
if value:
|
||||||
|
return queryset.filter(
|
||||||
|
recorded=None,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class SelectionEcoAccountTableFilter(ConservationOfficeTableFilterMixin):
|
||||||
|
""" Special selection table filter for eco accounts
|
||||||
|
|
||||||
|
EcoAccounts only need a selection filter for conservation office
|
||||||
|
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountTableFilter(AbstractTableFilter):
|
||||||
|
""" TableFilter for eco accounts
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, user=None, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.user = user
|
||||||
|
qs = kwargs.get("queryset", None)
|
||||||
|
request_data = kwargs.get("data", None)
|
||||||
|
|
||||||
|
# Pipe the queryset through all needed filters
|
||||||
|
self.selection_filter = SelectionEcoAccountTableFilter(
|
||||||
|
data=request_data,
|
||||||
|
queryset=qs,
|
||||||
|
)
|
||||||
|
self.query_filter = QueryTableFilter(
|
||||||
|
data=request_data,
|
||||||
|
queryset=self.selection_filter.qs,
|
||||||
|
)
|
||||||
|
self.checkbox_filter = CheckboxEcoAccountTableFilter(
|
||||||
|
user=user,
|
||||||
|
data=request_data,
|
||||||
|
queryset=self.query_filter.qs,
|
||||||
|
)
|
||||||
|
# Overwrite the final queryset result
|
||||||
|
self.qs = self.checkbox_filter.qs
|
7
compensation/forms/__init__.py
Normal file
7
compensation/forms/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
238
compensation/forms/compensation.py
Normal file
238
compensation/forms/compensation.py
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from dal import autocomplete
|
||||||
|
from django import forms
|
||||||
|
from django.db import transaction
|
||||||
|
from django.urls import reverse, reverse_lazy
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from compensation.forms.mixins import CEFCompensationFormMixin, CoherenceCompensationFormMixin, PikCompensationFormMixin
|
||||||
|
from compensation.models import Compensation
|
||||||
|
from intervention.inputs import GenerateInput
|
||||||
|
from intervention.models import Intervention
|
||||||
|
from konova.forms import BaseForm, SimpleGeomForm
|
||||||
|
from konova.utils.message_templates import COMPENSATION_ADDED_TEMPLATE, EDITED_GENERAL_DATA
|
||||||
|
from user.models import UserActionLogEntry, User
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractCompensationForm(BaseForm):
|
||||||
|
""" Abstract form for compensations
|
||||||
|
|
||||||
|
Holds all important form fields, which are used in compensation and eco account forms
|
||||||
|
|
||||||
|
"""
|
||||||
|
identifier = forms.CharField(
|
||||||
|
label=_("Identifier"),
|
||||||
|
label_suffix="",
|
||||||
|
max_length=255,
|
||||||
|
help_text=_("Generated automatically"),
|
||||||
|
widget=GenerateInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control",
|
||||||
|
"url": None, # Needs to be set in inheriting constructors
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
title = forms.CharField(
|
||||||
|
label=_("Title"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("An explanatory name"),
|
||||||
|
max_length=255,
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"placeholder": _("Compensation XY; Location ABC"),
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
comment = forms.CharField(
|
||||||
|
label_suffix="",
|
||||||
|
label=_("Comment"),
|
||||||
|
required=False,
|
||||||
|
help_text=_("Additional comment"),
|
||||||
|
widget=forms.Textarea(
|
||||||
|
attrs={
|
||||||
|
"rows": 5,
|
||||||
|
"class": "form-control"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class NewCompensationForm(AbstractCompensationForm,
|
||||||
|
CEFCompensationFormMixin,
|
||||||
|
CoherenceCompensationFormMixin,
|
||||||
|
PikCompensationFormMixin):
|
||||||
|
""" Form for creating new compensations.
|
||||||
|
|
||||||
|
Can be initialized with an intervention id for preselecting the related intervention.
|
||||||
|
form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
|
||||||
|
...
|
||||||
|
The intervention id will not be resolved into the intervention ORM object but instead will be used to initialize
|
||||||
|
the related form field.
|
||||||
|
|
||||||
|
"""
|
||||||
|
intervention = forms.ModelChoiceField(
|
||||||
|
label=_("compensates intervention"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Select the intervention for which this compensation compensates"),
|
||||||
|
queryset=Intervention.objects.filter(
|
||||||
|
deleted=None,
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url="intervention:autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Click for selection"),
|
||||||
|
"data-minimum-input-length": 3,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Define a field order for a nicer layout instead of running with the inheritance result
|
||||||
|
field_order = [
|
||||||
|
"identifier",
|
||||||
|
"title",
|
||||||
|
"intervention",
|
||||||
|
"is_pik",
|
||||||
|
"is_cef",
|
||||||
|
"is_coherence_keeping",
|
||||||
|
"comment",
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
intervention_id = kwargs.pop("intervention_id", None)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("New compensation")
|
||||||
|
|
||||||
|
# If the compensation shall directly be initialized from an intervention, we need to fill in the intervention id
|
||||||
|
# and disable the form field.
|
||||||
|
# Furthermore the action_url needs to be set accordingly.
|
||||||
|
if intervention_id is not None:
|
||||||
|
self.initialize_form_field("intervention", intervention_id)
|
||||||
|
self.disable_form_field("intervention")
|
||||||
|
self.action_url = reverse("compensation:new", args=(intervention_id,))
|
||||||
|
self.cancel_redirect = reverse("intervention:detail", args=(intervention_id,))
|
||||||
|
else:
|
||||||
|
self.action_url = reverse("compensation:new")
|
||||||
|
self.cancel_redirect = reverse("compensation:index")
|
||||||
|
|
||||||
|
tmp = Compensation()
|
||||||
|
identifier = tmp.generate_new_identifier()
|
||||||
|
self.initialize_form_field("identifier", identifier)
|
||||||
|
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:new-id")
|
||||||
|
|
||||||
|
def __create_comp(self, user, geom_form) -> Compensation:
|
||||||
|
""" Creates the compensation from form data
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User): The performing user
|
||||||
|
geom_form (SimpleGeomForm): The geometry form
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
comp (Compensation): The compensation object
|
||||||
|
"""
|
||||||
|
# Fetch data from cleaned POST values
|
||||||
|
identifier = self.cleaned_data.get("identifier", None)
|
||||||
|
title = self.cleaned_data.get("title", None)
|
||||||
|
intervention = self.cleaned_data.get("intervention", None)
|
||||||
|
is_cef = self.cleaned_data.get("is_cef", None)
|
||||||
|
is_coherence_keeping = self.cleaned_data.get("is_coherence_keeping", None)
|
||||||
|
is_pik = self.cleaned_data.get("is_pik", None)
|
||||||
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
|
||||||
|
# Create log entry
|
||||||
|
action = UserActionLogEntry.get_created_action(user)
|
||||||
|
# Process the geometry form
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
|
||||||
|
# Finally create main object
|
||||||
|
comp = Compensation.objects.create(
|
||||||
|
identifier=identifier,
|
||||||
|
title=title,
|
||||||
|
intervention=intervention,
|
||||||
|
created=action,
|
||||||
|
is_cef=is_cef,
|
||||||
|
is_coherence_keeping=is_coherence_keeping,
|
||||||
|
is_pik=is_pik,
|
||||||
|
geometry=geometry,
|
||||||
|
comment=comment,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add the log entry to the main objects log list
|
||||||
|
comp.log.add(action)
|
||||||
|
return comp
|
||||||
|
|
||||||
|
def save(self, user: User, geom_form: SimpleGeomForm):
|
||||||
|
with transaction.atomic():
|
||||||
|
comp = self.__create_comp(user, geom_form)
|
||||||
|
comp.intervention.mark_as_edited(user, edit_comment=COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
|
||||||
|
return comp
|
||||||
|
|
||||||
|
|
||||||
|
class EditCompensationForm(NewCompensationForm):
|
||||||
|
""" Form for editing compensations
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Edit compensation")
|
||||||
|
self.action_url = reverse("compensation:edit", args=(self.instance.id,))
|
||||||
|
self.cancel_redirect = reverse("compensation:detail", args=(self.instance.id,))
|
||||||
|
|
||||||
|
# Initialize form data
|
||||||
|
form_data = {
|
||||||
|
"identifier": self.instance.identifier,
|
||||||
|
"title": self.instance.title,
|
||||||
|
"intervention": self.instance.intervention,
|
||||||
|
"is_cef": self.instance.is_cef,
|
||||||
|
"is_coherence_keeping": self.instance.is_coherence_keeping,
|
||||||
|
"is_pik": self.instance.is_pik,
|
||||||
|
"comment": self.instance.comment,
|
||||||
|
}
|
||||||
|
disabled_fields = []
|
||||||
|
self.load_initial_data(
|
||||||
|
form_data,
|
||||||
|
disabled_fields
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
intervention = self.cleaned_data.get("intervention", None)
|
||||||
|
is_cef = self.cleaned_data.get("is_cef", None)
|
||||||
|
is_coherence_keeping = self.cleaned_data.get("is_coherence_keeping", None)
|
||||||
|
is_pik = self.cleaned_data.get("is_pik", None)
|
||||||
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
|
||||||
|
# Create log entry
|
||||||
|
action = UserActionLogEntry.get_edited_action(user)
|
||||||
|
|
||||||
|
# Process the geometry form
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
|
||||||
|
# Finally create main object
|
||||||
|
self.instance.identifier = identifier
|
||||||
|
self.instance.title = title
|
||||||
|
self.instance.intervention = intervention
|
||||||
|
self.instance.geometry = geometry
|
||||||
|
self.instance.is_cef = is_cef
|
||||||
|
self.instance.is_coherence_keeping = is_coherence_keeping
|
||||||
|
self.instance.comment = comment
|
||||||
|
self.instance.is_pik = is_pik
|
||||||
|
self.instance.modified = action
|
||||||
|
self.instance.save()
|
||||||
|
|
||||||
|
self.instance.log.add(action)
|
||||||
|
|
||||||
|
intervention.mark_as_edited(user, self.request, EDITED_GENERAL_DATA)
|
||||||
|
return self.instance
|
212
compensation/forms/eco_account.py
Normal file
212
compensation/forms/eco_account.py
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django import forms
|
||||||
|
from django.db import transaction
|
||||||
|
from django.urls import reverse, reverse_lazy
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from compensation.forms.compensation import AbstractCompensationForm
|
||||||
|
from compensation.forms.mixins import CompensationResponsibleFormMixin, PikCompensationFormMixin
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from intervention.models import Handler, Responsibility, Legal
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from user.models import User, UserActionLogEntry
|
||||||
|
|
||||||
|
|
||||||
|
class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMixin, PikCompensationFormMixin):
|
||||||
|
""" Form for creating eco accounts
|
||||||
|
|
||||||
|
Inherits from basic AbstractCompensationForm and further form fields from CompensationResponsibleFormMixin
|
||||||
|
|
||||||
|
"""
|
||||||
|
surface = forms.DecimalField(
|
||||||
|
min_value=0.00,
|
||||||
|
decimal_places=2,
|
||||||
|
label=_("Available Surface"),
|
||||||
|
label_suffix="",
|
||||||
|
required=False,
|
||||||
|
help_text=_("The amount that can be used for deductions"),
|
||||||
|
widget=forms.NumberInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control",
|
||||||
|
"placeholder": "0,00"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
registration_date = forms.DateField(
|
||||||
|
label=_("Agreement date"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("When did the parties agree on this?"),
|
||||||
|
required=False,
|
||||||
|
widget=forms.DateInput(
|
||||||
|
attrs={
|
||||||
|
"type": "date",
|
||||||
|
"class": "form-control",
|
||||||
|
},
|
||||||
|
format="%d.%m.%Y"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
field_order = [
|
||||||
|
"identifier",
|
||||||
|
"title",
|
||||||
|
"conservation_office",
|
||||||
|
"registration_date",
|
||||||
|
"surface",
|
||||||
|
"conservation_file_number",
|
||||||
|
"is_pik",
|
||||||
|
"handler_type",
|
||||||
|
"handler_detail",
|
||||||
|
"comment",
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("New Eco-Account")
|
||||||
|
|
||||||
|
self.action_url = reverse("compensation:acc:new")
|
||||||
|
self.cancel_redirect = reverse("compensation:acc:index")
|
||||||
|
|
||||||
|
tmp = EcoAccount()
|
||||||
|
identifier = tmp.generate_new_identifier()
|
||||||
|
self.initialize_form_field("identifier", identifier)
|
||||||
|
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:acc:new-id")
|
||||||
|
self.fields["title"].widget.attrs["placeholder"] = _("Eco-Account XY; Location ABC")
|
||||||
|
|
||||||
|
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)
|
||||||
|
registration_date = self.cleaned_data.get("registration_date", None)
|
||||||
|
handler_type = self.cleaned_data.get("handler_type", None)
|
||||||
|
handler_detail = self.cleaned_data.get("handler_detail", None)
|
||||||
|
surface = self.cleaned_data.get("surface", None)
|
||||||
|
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||||
|
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||||
|
is_pik = self.cleaned_data.get("is_pik", None)
|
||||||
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
|
||||||
|
# Create log entry
|
||||||
|
action = UserActionLogEntry.get_created_action(user)
|
||||||
|
# Process the geometry form
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
|
||||||
|
handler = Handler.objects.create(
|
||||||
|
type=handler_type,
|
||||||
|
detail=handler_detail,
|
||||||
|
)
|
||||||
|
|
||||||
|
responsible = Responsibility.objects.create(
|
||||||
|
handler=handler,
|
||||||
|
conservation_file_number=conservation_file_number,
|
||||||
|
conservation_office=conservation_office,
|
||||||
|
)
|
||||||
|
|
||||||
|
legal = Legal.objects.create(
|
||||||
|
registration_date=registration_date
|
||||||
|
)
|
||||||
|
|
||||||
|
# Finally create main object
|
||||||
|
acc = EcoAccount.objects.create(
|
||||||
|
identifier=identifier,
|
||||||
|
title=title,
|
||||||
|
responsible=responsible,
|
||||||
|
deductable_surface=surface,
|
||||||
|
created=action,
|
||||||
|
geometry=geometry,
|
||||||
|
comment=comment,
|
||||||
|
is_pik=is_pik,
|
||||||
|
legal=legal
|
||||||
|
)
|
||||||
|
acc.share_with_user(user)
|
||||||
|
|
||||||
|
# Add the log entry to the main objects log list
|
||||||
|
acc.log.add(action)
|
||||||
|
return acc
|
||||||
|
|
||||||
|
|
||||||
|
class EditEcoAccountForm(NewEcoAccountForm):
|
||||||
|
""" Form for editing eco accounts
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Edit Eco-Account")
|
||||||
|
|
||||||
|
self.action_url = reverse("compensation:acc:edit", args=(self.instance.id,))
|
||||||
|
self.cancel_redirect = reverse("compensation:acc:detail", args=(self.instance.id,))
|
||||||
|
|
||||||
|
# Initialize form data
|
||||||
|
reg_date = self.instance.legal.registration_date
|
||||||
|
if reg_date is not None:
|
||||||
|
reg_date = reg_date.isoformat()
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
"identifier": self.instance.identifier,
|
||||||
|
"title": self.instance.title,
|
||||||
|
"surface": self.instance.deductable_surface,
|
||||||
|
"handler_type": self.instance.responsible.handler.type,
|
||||||
|
"handler_detail": self.instance.responsible.handler.detail,
|
||||||
|
"registration_date": reg_date,
|
||||||
|
"conservation_office": self.instance.responsible.conservation_office,
|
||||||
|
"conservation_file_number": self.instance.responsible.conservation_file_number,
|
||||||
|
"is_pik": self.instance.is_pik,
|
||||||
|
"comment": self.instance.comment,
|
||||||
|
}
|
||||||
|
disabled_fields = []
|
||||||
|
self.load_initial_data(
|
||||||
|
form_data,
|
||||||
|
disabled_fields
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
registration_date = self.cleaned_data.get("registration_date", None)
|
||||||
|
handler_type = self.cleaned_data.get("handler_type", None)
|
||||||
|
handler_detail = self.cleaned_data.get("handler_detail", None)
|
||||||
|
surface = self.cleaned_data.get("surface", None)
|
||||||
|
conservation_office = self.cleaned_data.get("conservation_office", None)
|
||||||
|
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
||||||
|
comment = self.cleaned_data.get("comment", None)
|
||||||
|
is_pik = self.cleaned_data.get("is_pik", None)
|
||||||
|
|
||||||
|
# Create log entry
|
||||||
|
action = UserActionLogEntry.get_edited_action(user)
|
||||||
|
|
||||||
|
# Process the geometry form
|
||||||
|
geometry = geom_form.save(action)
|
||||||
|
|
||||||
|
# Update responsible data
|
||||||
|
self.instance.responsible.handler.type = handler_type
|
||||||
|
self.instance.responsible.handler.detail = handler_detail
|
||||||
|
self.instance.responsible.handler.save()
|
||||||
|
self.instance.responsible.conservation_office = conservation_office
|
||||||
|
self.instance.responsible.conservation_file_number = conservation_file_number
|
||||||
|
self.instance.responsible.save()
|
||||||
|
|
||||||
|
# Update legal data
|
||||||
|
self.instance.legal.registration_date = registration_date
|
||||||
|
self.instance.legal.save()
|
||||||
|
|
||||||
|
# Update main oject data
|
||||||
|
self.instance.identifier = identifier
|
||||||
|
self.instance.title = title
|
||||||
|
self.instance.deductable_surface = surface
|
||||||
|
self.instance.geometry = geometry
|
||||||
|
self.instance.comment = comment
|
||||||
|
self.instance.is_pik = is_pik
|
||||||
|
self.instance.modified = action
|
||||||
|
self.instance.save()
|
||||||
|
|
||||||
|
# Add the log entry to the main objects log list
|
||||||
|
self.instance.log.add(action)
|
||||||
|
return self.instance
|
@ -1,539 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
|
||||||
Created on: 04.12.20
|
|
||||||
|
|
||||||
"""
|
|
||||||
from dal import autocomplete
|
|
||||||
from user.models import User
|
|
||||||
from django.db import transaction
|
|
||||||
from django.urls import reverse_lazy, reverse
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django import forms
|
|
||||||
|
|
||||||
from codelist.models import KonovaCode
|
|
||||||
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_ID
|
|
||||||
from compensation.models import Compensation, EcoAccount
|
|
||||||
from intervention.inputs import GenerateInput
|
|
||||||
from intervention.models import Intervention, Responsibility, Legal, Handler
|
|
||||||
from konova.forms import BaseForm, SimpleGeomForm
|
|
||||||
from konova.utils.message_templates import EDITED_GENERAL_DATA, COMPENSATION_ADDED_TEMPLATE
|
|
||||||
from user.models import UserActionLogEntry
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractCompensationForm(BaseForm):
|
|
||||||
""" Abstract form for compensations
|
|
||||||
|
|
||||||
Holds all important form fields, which are used in compensation and eco account forms
|
|
||||||
|
|
||||||
"""
|
|
||||||
identifier = forms.CharField(
|
|
||||||
label=_("Identifier"),
|
|
||||||
label_suffix="",
|
|
||||||
max_length=255,
|
|
||||||
help_text=_("Generated automatically"),
|
|
||||||
widget=GenerateInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control",
|
|
||||||
"url": None, # Needs to be set in inheriting constructors
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
title = forms.CharField(
|
|
||||||
label=_("Title"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("An explanatory name"),
|
|
||||||
max_length=255,
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={
|
|
||||||
"placeholder": _("Compensation XY; Location ABC"),
|
|
||||||
"class": "form-control",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
comment = forms.CharField(
|
|
||||||
label_suffix="",
|
|
||||||
label=_("Comment"),
|
|
||||||
required=False,
|
|
||||||
help_text=_("Additional comment"),
|
|
||||||
widget=forms.Textarea(
|
|
||||||
attrs={
|
|
||||||
"rows": 5,
|
|
||||||
"class": "form-control"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class CompensationResponsibleFormMixin(forms.Form):
|
|
||||||
""" Encapsulates form fields used in different compensation related models like EcoAccount or EMA
|
|
||||||
|
|
||||||
"""
|
|
||||||
conservation_office = forms.ModelChoiceField(
|
|
||||||
label=_("Conservation office"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Select the responsible office"),
|
|
||||||
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={
|
|
||||||
"data-placeholder": _("Click for selection")
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
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_type = forms.ModelChoiceField(
|
|
||||||
label=_("Eco-Account handler type"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("What type of handler is responsible for the ecoaccount?"),
|
|
||||||
required=False,
|
|
||||||
queryset=KonovaCode.objects.filter(
|
|
||||||
is_archived=False,
|
|
||||||
is_leaf=True,
|
|
||||||
code_lists__in=[CODELIST_HANDLER_ID],
|
|
||||||
),
|
|
||||||
widget=autocomplete.ModelSelect2(
|
|
||||||
url="codes-handler-autocomplete",
|
|
||||||
attrs={
|
|
||||||
"data-placeholder": _("Click for selection"),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
handler_detail = forms.CharField(
|
|
||||||
label=_("Eco-Account handler detail"),
|
|
||||||
label_suffix="",
|
|
||||||
max_length=255,
|
|
||||||
required=False,
|
|
||||||
help_text=_("Detail input on the handler"),
|
|
||||||
widget=forms.TextInput(
|
|
||||||
attrs={
|
|
||||||
"placeholder": _("Company Mustermann"),
|
|
||||||
"class": "form-control",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CEFCompensationFormMixin(forms.Form):
|
|
||||||
""" A form mixin, providing CEF compensation field
|
|
||||||
|
|
||||||
"""
|
|
||||||
is_cef = forms.BooleanField(
|
|
||||||
label_suffix="",
|
|
||||||
label=_("Is CEF"),
|
|
||||||
help_text=_("Optionally: Whether this compensation is a CEF compensation?"),
|
|
||||||
required=False,
|
|
||||||
widget=forms.CheckboxInput()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CoherenceCompensationFormMixin(forms.Form):
|
|
||||||
""" A form mixin, providing coherence compensation field
|
|
||||||
|
|
||||||
"""
|
|
||||||
is_coherence_keeping = forms.BooleanField(
|
|
||||||
label_suffix="",
|
|
||||||
label=_("Is coherence keeping"),
|
|
||||||
help_text=_("Optionally: Whether this compensation is a coherence keeping compensation?"),
|
|
||||||
required=False,
|
|
||||||
widget=forms.CheckboxInput()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PikCompensationFormMixin(forms.Form):
|
|
||||||
""" A form mixin, providing PIK compensation field
|
|
||||||
|
|
||||||
"""
|
|
||||||
is_pik = forms.BooleanField(
|
|
||||||
label_suffix="",
|
|
||||||
label=_("Is PIK"),
|
|
||||||
help_text=_("Optionally: Whether this compensation is a compensation integrated in production?"),
|
|
||||||
required=False,
|
|
||||||
widget=forms.CheckboxInput()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class NewCompensationForm(AbstractCompensationForm,
|
|
||||||
CEFCompensationFormMixin,
|
|
||||||
CoherenceCompensationFormMixin,
|
|
||||||
PikCompensationFormMixin):
|
|
||||||
""" Form for creating new compensations.
|
|
||||||
|
|
||||||
Can be initialized with an intervention id for preselecting the related intervention.
|
|
||||||
form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
|
|
||||||
...
|
|
||||||
The intervention id will not be resolved into the intervention ORM object but instead will be used to initialize
|
|
||||||
the related form field.
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = forms.ModelChoiceField(
|
|
||||||
label=_("compensates intervention"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Select the intervention for which this compensation compensates"),
|
|
||||||
queryset=Intervention.objects.filter(
|
|
||||||
deleted=None,
|
|
||||||
),
|
|
||||||
widget=autocomplete.ModelSelect2(
|
|
||||||
url="interventions-autocomplete",
|
|
||||||
attrs={
|
|
||||||
"data-placeholder": _("Click for selection"),
|
|
||||||
"data-minimum-input-length": 3,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Define a field order for a nicer layout instead of running with the inheritance result
|
|
||||||
field_order = [
|
|
||||||
"identifier",
|
|
||||||
"title",
|
|
||||||
"intervention",
|
|
||||||
"is_pik",
|
|
||||||
"is_cef",
|
|
||||||
"is_coherence_keeping",
|
|
||||||
"comment",
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
intervention_id = kwargs.pop("intervention_id", None)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("New compensation")
|
|
||||||
|
|
||||||
# If the compensation shall directly be initialized from an intervention, we need to fill in the intervention id
|
|
||||||
# and disable the form field.
|
|
||||||
# Furthermore the action_url needs to be set accordingly.
|
|
||||||
if intervention_id is not None:
|
|
||||||
self.initialize_form_field("intervention", intervention_id)
|
|
||||||
self.disable_form_field("intervention")
|
|
||||||
self.action_url = reverse("compensation:new", args=(intervention_id,))
|
|
||||||
self.cancel_redirect = reverse("intervention:detail", args=(intervention_id,))
|
|
||||||
else:
|
|
||||||
self.action_url = reverse("compensation:new")
|
|
||||||
self.cancel_redirect = reverse("compensation:index")
|
|
||||||
|
|
||||||
tmp = Compensation()
|
|
||||||
identifier = tmp.generate_new_identifier()
|
|
||||||
self.initialize_form_field("identifier", identifier)
|
|
||||||
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:new-id")
|
|
||||||
|
|
||||||
def __create_comp(self, user, geom_form) -> Compensation:
|
|
||||||
""" Creates the compensation from form data
|
|
||||||
|
|
||||||
Args:
|
|
||||||
user (User): The performing user
|
|
||||||
geom_form (SimpleGeomForm): The geometry form
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
comp (Compensation): The compensation object
|
|
||||||
"""
|
|
||||||
# Fetch data from cleaned POST values
|
|
||||||
identifier = self.cleaned_data.get("identifier", None)
|
|
||||||
title = self.cleaned_data.get("title", None)
|
|
||||||
intervention = self.cleaned_data.get("intervention", None)
|
|
||||||
is_cef = self.cleaned_data.get("is_cef", None)
|
|
||||||
is_coherence_keeping = self.cleaned_data.get("is_coherence_keeping", None)
|
|
||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
|
||||||
comment = self.cleaned_data.get("comment", None)
|
|
||||||
|
|
||||||
# Create log entry
|
|
||||||
action = UserActionLogEntry.get_created_action(user)
|
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
# Finally create main object
|
|
||||||
comp = Compensation.objects.create(
|
|
||||||
identifier=identifier,
|
|
||||||
title=title,
|
|
||||||
intervention=intervention,
|
|
||||||
created=action,
|
|
||||||
is_cef=is_cef,
|
|
||||||
is_coherence_keeping=is_coherence_keeping,
|
|
||||||
is_pik=is_pik,
|
|
||||||
geometry=geometry,
|
|
||||||
comment=comment,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add the log entry to the main objects log list
|
|
||||||
comp.log.add(action)
|
|
||||||
return comp
|
|
||||||
|
|
||||||
def save(self, user: User, geom_form: SimpleGeomForm):
|
|
||||||
with transaction.atomic():
|
|
||||||
comp = self.__create_comp(user, geom_form)
|
|
||||||
comp.intervention.mark_as_edited(user, edit_comment=COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
|
|
||||||
return comp
|
|
||||||
|
|
||||||
|
|
||||||
class EditCompensationForm(NewCompensationForm):
|
|
||||||
""" Form for editing compensations
|
|
||||||
|
|
||||||
"""
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Edit compensation")
|
|
||||||
self.action_url = reverse("compensation:edit", args=(self.instance.id,))
|
|
||||||
self.cancel_redirect = reverse("compensation:detail", args=(self.instance.id,))
|
|
||||||
|
|
||||||
# Initialize form data
|
|
||||||
form_data = {
|
|
||||||
"identifier": self.instance.identifier,
|
|
||||||
"title": self.instance.title,
|
|
||||||
"intervention": self.instance.intervention,
|
|
||||||
"is_cef": self.instance.is_cef,
|
|
||||||
"is_coherence_keeping": self.instance.is_coherence_keeping,
|
|
||||||
"is_pik": self.instance.is_pik,
|
|
||||||
"comment": self.instance.comment,
|
|
||||||
}
|
|
||||||
disabled_fields = []
|
|
||||||
self.load_initial_data(
|
|
||||||
form_data,
|
|
||||||
disabled_fields
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
intervention = self.cleaned_data.get("intervention", None)
|
|
||||||
is_cef = self.cleaned_data.get("is_cef", None)
|
|
||||||
is_coherence_keeping = self.cleaned_data.get("is_coherence_keeping", None)
|
|
||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
|
||||||
comment = self.cleaned_data.get("comment", None)
|
|
||||||
|
|
||||||
# Create log entry
|
|
||||||
action = UserActionLogEntry.get_edited_action(user)
|
|
||||||
|
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
# Finally create main object
|
|
||||||
self.instance.identifier = identifier
|
|
||||||
self.instance.title = title
|
|
||||||
self.instance.intervention = intervention
|
|
||||||
self.instance.geometry = geometry
|
|
||||||
self.instance.is_cef = is_cef
|
|
||||||
self.instance.is_coherence_keeping = is_coherence_keeping
|
|
||||||
self.instance.comment = comment
|
|
||||||
self.instance.is_pik = is_pik
|
|
||||||
self.instance.modified = action
|
|
||||||
self.instance.save()
|
|
||||||
|
|
||||||
self.instance.log.add(action)
|
|
||||||
|
|
||||||
intervention.mark_as_edited(user, self.request, EDITED_GENERAL_DATA)
|
|
||||||
return self.instance
|
|
||||||
|
|
||||||
|
|
||||||
class NewEcoAccountForm(AbstractCompensationForm, CompensationResponsibleFormMixin, PikCompensationFormMixin):
|
|
||||||
""" Form for creating eco accounts
|
|
||||||
|
|
||||||
Inherits from basic AbstractCompensationForm and further form fields from CompensationResponsibleFormMixin
|
|
||||||
|
|
||||||
"""
|
|
||||||
surface = forms.DecimalField(
|
|
||||||
min_value=0.00,
|
|
||||||
decimal_places=2,
|
|
||||||
label=_("Available Surface"),
|
|
||||||
label_suffix="",
|
|
||||||
required=False,
|
|
||||||
help_text=_("The amount that can be used for deductions"),
|
|
||||||
widget=forms.NumberInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control",
|
|
||||||
"placeholder": "0,00"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
registration_date = forms.DateField(
|
|
||||||
label=_("Agreement date"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("When did the parties agree on this?"),
|
|
||||||
required=False,
|
|
||||||
widget=forms.DateInput(
|
|
||||||
attrs={
|
|
||||||
"type": "date",
|
|
||||||
"class": "form-control",
|
|
||||||
},
|
|
||||||
format="%d.%m.%Y"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
field_order = [
|
|
||||||
"identifier",
|
|
||||||
"title",
|
|
||||||
"conservation_office",
|
|
||||||
"registration_date",
|
|
||||||
"surface",
|
|
||||||
"conservation_file_number",
|
|
||||||
"is_pik",
|
|
||||||
"handler_type",
|
|
||||||
"handler_detail",
|
|
||||||
"comment",
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("New Eco-Account")
|
|
||||||
|
|
||||||
self.action_url = reverse("compensation:acc:new")
|
|
||||||
self.cancel_redirect = reverse("compensation:acc:index")
|
|
||||||
|
|
||||||
tmp = EcoAccount()
|
|
||||||
identifier = tmp.generate_new_identifier()
|
|
||||||
self.initialize_form_field("identifier", identifier)
|
|
||||||
self.fields["identifier"].widget.attrs["url"] = reverse_lazy("compensation:acc:new-id")
|
|
||||||
self.fields["title"].widget.attrs["placeholder"] = _("Eco-Account XY; Location ABC")
|
|
||||||
|
|
||||||
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)
|
|
||||||
registration_date = self.cleaned_data.get("registration_date", None)
|
|
||||||
handler_type = self.cleaned_data.get("handler_type", None)
|
|
||||||
handler_detail = self.cleaned_data.get("handler_detail", None)
|
|
||||||
surface = self.cleaned_data.get("surface", None)
|
|
||||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
|
||||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
|
||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
|
||||||
comment = self.cleaned_data.get("comment", None)
|
|
||||||
|
|
||||||
# Create log entry
|
|
||||||
action = UserActionLogEntry.get_created_action(user)
|
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
handler = Handler.objects.create(
|
|
||||||
type=handler_type,
|
|
||||||
detail=handler_detail,
|
|
||||||
)
|
|
||||||
|
|
||||||
responsible = Responsibility.objects.create(
|
|
||||||
handler=handler,
|
|
||||||
conservation_file_number=conservation_file_number,
|
|
||||||
conservation_office=conservation_office,
|
|
||||||
)
|
|
||||||
|
|
||||||
legal = Legal.objects.create(
|
|
||||||
registration_date=registration_date
|
|
||||||
)
|
|
||||||
|
|
||||||
# Finally create main object
|
|
||||||
acc = EcoAccount.objects.create(
|
|
||||||
identifier=identifier,
|
|
||||||
title=title,
|
|
||||||
responsible=responsible,
|
|
||||||
deductable_surface=surface,
|
|
||||||
created=action,
|
|
||||||
geometry=geometry,
|
|
||||||
comment=comment,
|
|
||||||
is_pik=is_pik,
|
|
||||||
legal=legal
|
|
||||||
)
|
|
||||||
acc.share_with_user(user)
|
|
||||||
|
|
||||||
# Add the log entry to the main objects log list
|
|
||||||
acc.log.add(action)
|
|
||||||
return acc
|
|
||||||
|
|
||||||
|
|
||||||
class EditEcoAccountForm(NewEcoAccountForm):
|
|
||||||
""" Form for editing eco accounts
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Edit Eco-Account")
|
|
||||||
|
|
||||||
self.action_url = reverse("compensation:acc:edit", args=(self.instance.id,))
|
|
||||||
self.cancel_redirect = reverse("compensation:acc:detail", args=(self.instance.id,))
|
|
||||||
|
|
||||||
# Initialize form data
|
|
||||||
reg_date = self.instance.legal.registration_date
|
|
||||||
if reg_date is not None:
|
|
||||||
reg_date = reg_date.isoformat()
|
|
||||||
|
|
||||||
form_data = {
|
|
||||||
"identifier": self.instance.identifier,
|
|
||||||
"title": self.instance.title,
|
|
||||||
"surface": self.instance.deductable_surface,
|
|
||||||
"handler_type": self.instance.responsible.handler.type,
|
|
||||||
"handler_detail": self.instance.responsible.handler.detail,
|
|
||||||
"registration_date": reg_date,
|
|
||||||
"conservation_office": self.instance.responsible.conservation_office,
|
|
||||||
"conservation_file_number": self.instance.responsible.conservation_file_number,
|
|
||||||
"is_pik": self.instance.is_pik,
|
|
||||||
"comment": self.instance.comment,
|
|
||||||
}
|
|
||||||
disabled_fields = []
|
|
||||||
self.load_initial_data(
|
|
||||||
form_data,
|
|
||||||
disabled_fields
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
registration_date = self.cleaned_data.get("registration_date", None)
|
|
||||||
handler_type = self.cleaned_data.get("handler_type", None)
|
|
||||||
handler_detail = self.cleaned_data.get("handler_detail", None)
|
|
||||||
surface = self.cleaned_data.get("surface", None)
|
|
||||||
conservation_office = self.cleaned_data.get("conservation_office", None)
|
|
||||||
conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
|
|
||||||
comment = self.cleaned_data.get("comment", None)
|
|
||||||
is_pik = self.cleaned_data.get("is_pik", None)
|
|
||||||
|
|
||||||
# Create log entry
|
|
||||||
action = UserActionLogEntry.get_edited_action(user)
|
|
||||||
|
|
||||||
# Process the geometry form
|
|
||||||
geometry = geom_form.save(action)
|
|
||||||
|
|
||||||
# Update responsible data
|
|
||||||
self.instance.responsible.handler.type = handler_type
|
|
||||||
self.instance.responsible.handler.detail = handler_detail
|
|
||||||
self.instance.responsible.handler.save()
|
|
||||||
self.instance.responsible.conservation_office = conservation_office
|
|
||||||
self.instance.responsible.conservation_file_number = conservation_file_number
|
|
||||||
self.instance.responsible.save()
|
|
||||||
|
|
||||||
# Update legal data
|
|
||||||
self.instance.legal.registration_date = registration_date
|
|
||||||
self.instance.legal.save()
|
|
||||||
|
|
||||||
# Update main oject data
|
|
||||||
self.instance.identifier = identifier
|
|
||||||
self.instance.title = title
|
|
||||||
self.instance.deductable_surface = surface
|
|
||||||
self.instance.geometry = geometry
|
|
||||||
self.instance.comment = comment
|
|
||||||
self.instance.is_pik = is_pik
|
|
||||||
self.instance.modified = action
|
|
||||||
self.instance.save()
|
|
||||||
|
|
||||||
# Add the log entry to the main objects log list
|
|
||||||
self.instance.log.add(action)
|
|
||||||
return self.instance
|
|
117
compensation/forms/mixins.py
Normal file
117
compensation/forms/mixins.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from dal import autocomplete
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from codelist.models import KonovaCode
|
||||||
|
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID, CODELIST_HANDLER_ID
|
||||||
|
|
||||||
|
|
||||||
|
class CompensationResponsibleFormMixin(forms.Form):
|
||||||
|
""" Encapsulates form fields used in different compensation related models like EcoAccount or EMA
|
||||||
|
|
||||||
|
"""
|
||||||
|
conservation_office = forms.ModelChoiceField(
|
||||||
|
label=_("Conservation office"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Select the responsible office"),
|
||||||
|
queryset=KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url="codelist:conservation-office-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Click for selection")
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
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_type = forms.ModelChoiceField(
|
||||||
|
label=_("Eco-Account handler type"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("What type of handler is responsible for the ecoaccount?"),
|
||||||
|
required=False,
|
||||||
|
queryset=KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
code_lists__in=[CODELIST_HANDLER_ID],
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url="codelist:handler-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Click for selection"),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
handler_detail = forms.CharField(
|
||||||
|
label=_("Eco-Account handler detail"),
|
||||||
|
label_suffix="",
|
||||||
|
max_length=255,
|
||||||
|
required=False,
|
||||||
|
help_text=_("Detail input on the handler"),
|
||||||
|
widget=forms.TextInput(
|
||||||
|
attrs={
|
||||||
|
"placeholder": _("Company Mustermann"),
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CEFCompensationFormMixin(forms.Form):
|
||||||
|
""" A form mixin, providing CEF compensation field
|
||||||
|
|
||||||
|
"""
|
||||||
|
is_cef = forms.BooleanField(
|
||||||
|
label_suffix="",
|
||||||
|
label=_("Is CEF"),
|
||||||
|
help_text=_("Optionally: Whether this compensation is a CEF compensation?"),
|
||||||
|
required=False,
|
||||||
|
widget=forms.CheckboxInput()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CoherenceCompensationFormMixin(forms.Form):
|
||||||
|
""" A form mixin, providing coherence compensation field
|
||||||
|
|
||||||
|
"""
|
||||||
|
is_coherence_keeping = forms.BooleanField(
|
||||||
|
label_suffix="",
|
||||||
|
label=_("Is coherence keeping"),
|
||||||
|
help_text=_("Optionally: Whether this compensation is a coherence keeping compensation?"),
|
||||||
|
required=False,
|
||||||
|
widget=forms.CheckboxInput()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PikCompensationFormMixin(forms.Form):
|
||||||
|
""" A form mixin, providing PIK compensation field
|
||||||
|
|
||||||
|
"""
|
||||||
|
is_pik = forms.BooleanField(
|
||||||
|
label_suffix="",
|
||||||
|
label=_("Is PIK"),
|
||||||
|
help_text=_("Optionally: Whether this compensation is a compensation integrated in production?"),
|
||||||
|
required=False,
|
||||||
|
widget=forms.CheckboxInput()
|
||||||
|
)
|
@ -1,534 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
|
||||||
Created on: 04.10.21
|
|
||||||
|
|
||||||
"""
|
|
||||||
from bootstrap_modal_forms.utils import is_ajax
|
|
||||||
from dal import autocomplete
|
|
||||||
from django import forms
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.http import HttpRequest, HttpResponseRedirect
|
|
||||||
from django.shortcuts import render
|
|
||||||
from django.utils.translation import pgettext_lazy as _con, gettext_lazy as _
|
|
||||||
|
|
||||||
from codelist.models import KonovaCode
|
|
||||||
from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, \
|
|
||||||
CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
|
||||||
from compensation.models import CompensationDocument, EcoAccountDocument
|
|
||||||
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple, \
|
|
||||||
CompensationStateTreeRadioSelect
|
|
||||||
from konova.contexts import BaseContext
|
|
||||||
from konova.forms.modals import BaseModalForm, NewDocumentModalForm, RemoveModalForm
|
|
||||||
from konova.models import DeadlineType
|
|
||||||
from konova.utils.message_templates import FORM_INVALID, ADDED_COMPENSATION_STATE, \
|
|
||||||
ADDED_COMPENSATION_ACTION, PAYMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED, DEADLINE_EDITED
|
|
||||||
|
|
||||||
|
|
||||||
class NewPaymentForm(BaseModalForm):
|
|
||||||
""" Form handling payment related input
|
|
||||||
|
|
||||||
"""
|
|
||||||
amount = forms.DecimalField(
|
|
||||||
min_value=0.00,
|
|
||||||
decimal_places=2,
|
|
||||||
label=_con("money", "Amount"), # contextual translation
|
|
||||||
label_suffix=_(""),
|
|
||||||
help_text=_("in Euro"),
|
|
||||||
widget=forms.NumberInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control",
|
|
||||||
"placeholder": "0,00",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
due = forms.DateField(
|
|
||||||
label=_("Due on"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
required=False,
|
|
||||||
help_text=_("Due on which date"),
|
|
||||||
widget=forms.DateInput(
|
|
||||||
attrs={
|
|
||||||
"type": "date",
|
|
||||||
"data-provide": "datepicker",
|
|
||||||
"class": "form-control",
|
|
||||||
},
|
|
||||||
format="%d.%m.%Y"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
comment = forms.CharField(
|
|
||||||
max_length=200,
|
|
||||||
required=False,
|
|
||||||
label=_("Comment"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
help_text=_("Additional comment, maximum {} letters").format(200),
|
|
||||||
widget=forms.Textarea(
|
|
||||||
attrs={
|
|
||||||
"rows": 5,
|
|
||||||
"class": "form-control"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.intervention = self.instance
|
|
||||||
self.form_title = _("Payment")
|
|
||||||
self.form_caption = _("Add a payment for intervention '{}'").format(self.intervention.title)
|
|
||||||
|
|
||||||
def is_valid(self):
|
|
||||||
"""
|
|
||||||
Checks on form validity.
|
|
||||||
|
|
||||||
For this form we need to make sure that a date or a comment is set.
|
|
||||||
If both are missing, the user needs to enter at least an explanation why
|
|
||||||
there is no date to be entered.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
is_valid (bool): True if valid, False otherwise
|
|
||||||
"""
|
|
||||||
super_valid = super().is_valid()
|
|
||||||
date = self.cleaned_data["due"]
|
|
||||||
comment = self.cleaned_data["comment"] or None
|
|
||||||
if not date and not comment:
|
|
||||||
# At least one needs to be set!
|
|
||||||
self.add_error(
|
|
||||||
"comment",
|
|
||||||
_("If there is no date you can enter, please explain why.")
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
return super_valid
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
pay = self.instance.add_payment(self)
|
|
||||||
return pay
|
|
||||||
|
|
||||||
|
|
||||||
class EditPaymentModalForm(NewPaymentForm):
|
|
||||||
""" Form handling edit for Payment
|
|
||||||
|
|
||||||
"""
|
|
||||||
payment = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.payment = kwargs.pop("payment", None)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Edit payment")
|
|
||||||
form_date = {
|
|
||||||
"amount": self.payment.amount,
|
|
||||||
"due": str(self.payment.due_on),
|
|
||||||
"comment": self.payment.comment,
|
|
||||||
}
|
|
||||||
self.load_initial_data(form_date, disabled_fields=[])
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
payment = self.payment
|
|
||||||
payment.amount = self.cleaned_data.get("amount", None)
|
|
||||||
payment.due_on = self.cleaned_data.get("due", None)
|
|
||||||
payment.comment = self.cleaned_data.get("comment", None)
|
|
||||||
payment.save()
|
|
||||||
self.instance.mark_as_edited(self.user, self.request, edit_comment=PAYMENT_EDITED)
|
|
||||||
self.instance.send_data_to_egon()
|
|
||||||
return payment
|
|
||||||
|
|
||||||
|
|
||||||
class RemovePaymentModalForm(RemoveModalForm):
|
|
||||||
""" Removing modal form for Payment
|
|
||||||
|
|
||||||
Can be used for anything, where removing shall be confirmed by the user a second time.
|
|
||||||
|
|
||||||
"""
|
|
||||||
payment = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
payment = kwargs.pop("payment", None)
|
|
||||||
self.payment = payment
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
self.instance.remove_payment(self)
|
|
||||||
|
|
||||||
|
|
||||||
class NewStateModalForm(BaseModalForm):
|
|
||||||
""" Form handling state related input
|
|
||||||
|
|
||||||
Compensation states refer to 'before' and 'after' states of a compensated surface. Basically it means:
|
|
||||||
What has been on this area before changes/compensations have been applied and what will be the result ('after')?
|
|
||||||
|
|
||||||
"""
|
|
||||||
biotope_type = forms.ChoiceField(
|
|
||||||
label=_("Biotope Type"),
|
|
||||||
label_suffix="",
|
|
||||||
required=True,
|
|
||||||
help_text=_("Select the biotope type"),
|
|
||||||
widget=CompensationStateTreeRadioSelect(),
|
|
||||||
)
|
|
||||||
biotope_extra = forms.ModelMultipleChoiceField(
|
|
||||||
label=_("Biotope additional type"),
|
|
||||||
label_suffix="",
|
|
||||||
required=False,
|
|
||||||
help_text=_("Select an additional biotope type"),
|
|
||||||
queryset=KonovaCode.objects.filter(
|
|
||||||
is_archived=False,
|
|
||||||
is_leaf=True,
|
|
||||||
code_lists__in=[CODELIST_BIOTOPES_EXTRA_CODES_ID],
|
|
||||||
),
|
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
|
||||||
url="codes-biotope-extra-type-autocomplete",
|
|
||||||
attrs={
|
|
||||||
"data-placeholder": _("Biotope additional type"),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
surface = forms.DecimalField(
|
|
||||||
min_value=0.00,
|
|
||||||
decimal_places=2,
|
|
||||||
label=_("Surface"),
|
|
||||||
label_suffix="",
|
|
||||||
required=True,
|
|
||||||
help_text=_("in m²"),
|
|
||||||
widget=forms.NumberInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control",
|
|
||||||
"placeholder": "0,00"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("New state")
|
|
||||||
self.form_caption = _("Insert data for the new state")
|
|
||||||
choices = KonovaCode.objects.filter(
|
|
||||||
code_lists__in=[CODELIST_BIOTOPES_ID],
|
|
||||||
is_archived=False,
|
|
||||||
is_leaf=True,
|
|
||||||
).values_list("id", flat=True)
|
|
||||||
choices = [
|
|
||||||
(choice, choice)
|
|
||||||
for choice in choices
|
|
||||||
]
|
|
||||||
self.fields["biotope_type"].choices = choices
|
|
||||||
|
|
||||||
def save(self, is_before_state: bool = False):
|
|
||||||
state = self.instance.add_state(self, is_before_state)
|
|
||||||
self.instance.mark_as_edited(self.user, self.request, ADDED_COMPENSATION_STATE)
|
|
||||||
return state
|
|
||||||
|
|
||||||
def process_request(self, request: HttpRequest, msg_success: str = _("Object removed"), msg_error: str = FORM_INVALID, redirect_url: str = None):
|
|
||||||
""" Generic processing of request
|
|
||||||
|
|
||||||
Wraps the request processing logic, so we don't need the same code everywhere a RemoveModalForm is being used
|
|
||||||
|
|
||||||
+++
|
|
||||||
The generic method from super class can not be used, since we need to do some request parameter check in here.
|
|
||||||
+++
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
msg_success (str): The message in case of successful removing
|
|
||||||
msg_error (str): The message in case of an error
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
redirect_url = redirect_url if redirect_url is not None else request.META.get("HTTP_REFERER", "home")
|
|
||||||
template = self.template
|
|
||||||
if request.method == "POST":
|
|
||||||
if self.is_valid():
|
|
||||||
# Modal forms send one POST for checking on data validity. This can be used to return possible errors
|
|
||||||
# on the form. A second POST (if no errors occured) is sent afterwards and needs to process the
|
|
||||||
# saving/commiting of the data to the database. is_ajax() performs this check. The first request is
|
|
||||||
# an ajax call, the second is a regular form POST.
|
|
||||||
if not is_ajax(request.META):
|
|
||||||
is_before_state = bool(request.GET.get("before", False))
|
|
||||||
self.save(is_before_state=is_before_state)
|
|
||||||
messages.success(
|
|
||||||
request,
|
|
||||||
msg_success
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(redirect_url)
|
|
||||||
else:
|
|
||||||
context = {
|
|
||||||
"form": self,
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
elif request.method == "GET":
|
|
||||||
context = {
|
|
||||||
"form": self,
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
else:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
class EditCompensationStateModalForm(NewStateModalForm):
|
|
||||||
state = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.state = kwargs.pop("state", None)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Edit state")
|
|
||||||
biotope_type_id = self.state.biotope_type.id if self.state.biotope_type else None
|
|
||||||
form_data = {
|
|
||||||
"biotope_type": biotope_type_id,
|
|
||||||
"biotope_extra": self.state.biotope_type_details.all(),
|
|
||||||
"surface": self.state.surface,
|
|
||||||
}
|
|
||||||
self.load_initial_data(form_data)
|
|
||||||
|
|
||||||
def save(self, is_before_state: bool = False):
|
|
||||||
state = self.state
|
|
||||||
biotope_type_id = self.cleaned_data.get("biotope_type", None)
|
|
||||||
state.biotope_type = KonovaCode.objects.get(id=biotope_type_id)
|
|
||||||
state.biotope_type_details.set(self.cleaned_data.get("biotope_extra", []))
|
|
||||||
state.surface = self.cleaned_data.get("surface", None)
|
|
||||||
state.save()
|
|
||||||
self.instance.mark_as_edited(self.user, self.request, edit_comment=COMPENSATION_STATE_EDITED)
|
|
||||||
return state
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveCompensationStateModalForm(RemoveModalForm):
|
|
||||||
""" Removing modal form for CompensationState
|
|
||||||
|
|
||||||
Can be used for anything, where removing shall be confirmed by the user a second time.
|
|
||||||
|
|
||||||
"""
|
|
||||||
state = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
state = kwargs.pop("state", None)
|
|
||||||
self.state = state
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
self.instance.remove_state(self)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveCompensationActionModalForm(RemoveModalForm):
|
|
||||||
""" Removing modal form for CompensationAction
|
|
||||||
|
|
||||||
Can be used for anything, where removing shall be confirmed by the user a second time.
|
|
||||||
|
|
||||||
"""
|
|
||||||
action = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
action = kwargs.pop("action", None)
|
|
||||||
self.action = action
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
self.instance.remove_action(self)
|
|
||||||
|
|
||||||
|
|
||||||
class NewDeadlineModalForm(BaseModalForm):
|
|
||||||
""" Form handling deadline related input
|
|
||||||
|
|
||||||
"""
|
|
||||||
type = forms.ChoiceField(
|
|
||||||
label=_("Deadline Type"),
|
|
||||||
label_suffix="",
|
|
||||||
required=True,
|
|
||||||
help_text=_("Select the deadline type"),
|
|
||||||
choices=DeadlineType.choices,
|
|
||||||
widget=forms.Select(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
date = forms.DateField(
|
|
||||||
label=_("Date"),
|
|
||||||
label_suffix="",
|
|
||||||
required=True,
|
|
||||||
help_text=_("Select date"),
|
|
||||||
widget=forms.DateInput(
|
|
||||||
attrs={
|
|
||||||
"type": "date",
|
|
||||||
"data-provide": "datepicker",
|
|
||||||
"class": "form-control",
|
|
||||||
},
|
|
||||||
format="%d.%m.%Y"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
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 = _("New deadline")
|
|
||||||
self.form_caption = _("Insert data for the new deadline")
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
deadline = self.instance.add_deadline(self)
|
|
||||||
return deadline
|
|
||||||
|
|
||||||
|
|
||||||
class EditDeadlineModalForm(NewDeadlineModalForm):
|
|
||||||
deadline = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.deadline = kwargs.pop("deadline", None)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Edit deadline")
|
|
||||||
form_data = {
|
|
||||||
"type": self.deadline.type,
|
|
||||||
"date": str(self.deadline.date),
|
|
||||||
"comment": self.deadline.comment,
|
|
||||||
}
|
|
||||||
self.load_initial_data(form_data)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
deadline = self.deadline
|
|
||||||
deadline.type = self.cleaned_data.get("type", None)
|
|
||||||
deadline.date = self.cleaned_data.get("date", None)
|
|
||||||
deadline.comment = self.cleaned_data.get("comment", None)
|
|
||||||
deadline.save()
|
|
||||||
self.instance.mark_as_edited(self.user, self.request, edit_comment=DEADLINE_EDITED)
|
|
||||||
return deadline
|
|
||||||
|
|
||||||
|
|
||||||
class NewActionModalForm(BaseModalForm):
|
|
||||||
""" Form handling action related input
|
|
||||||
|
|
||||||
Compensation actions are the actions performed on the area, which shall be compensated. Actions will change the
|
|
||||||
surface of the area, the biotopes, and have an environmental impact. With actions the before-after states can change
|
|
||||||
(not in the process logic in Konova, but in the real world).
|
|
||||||
|
|
||||||
"""
|
|
||||||
from compensation.models import UnitChoices
|
|
||||||
action_type = forms.MultipleChoiceField(
|
|
||||||
label=_("Action Type"),
|
|
||||||
label_suffix="",
|
|
||||||
required=True,
|
|
||||||
help_text=_("An action can consist of multiple different action types. All the selected action types are expected to be performed according to the amount and unit below on this form."),
|
|
||||||
choices=[],
|
|
||||||
widget=CompensationActionTreeCheckboxSelectMultiple(),
|
|
||||||
)
|
|
||||||
action_type_details = forms.ModelMultipleChoiceField(
|
|
||||||
label=_("Action Type detail"),
|
|
||||||
label_suffix="",
|
|
||||||
required=False,
|
|
||||||
help_text=_("Select the action type detail"),
|
|
||||||
queryset=KonovaCode.objects.filter(
|
|
||||||
is_archived=False,
|
|
||||||
is_leaf=True,
|
|
||||||
code_lists__in=[CODELIST_COMPENSATION_ACTION_DETAIL_ID],
|
|
||||||
),
|
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
|
||||||
url="codes-compensation-action-detail-autocomplete",
|
|
||||||
attrs={
|
|
||||||
"data-placeholder": _("Action Type detail"),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
unit = forms.ChoiceField(
|
|
||||||
label=_("Unit"),
|
|
||||||
label_suffix="",
|
|
||||||
required=True,
|
|
||||||
help_text=_("Select the unit"),
|
|
||||||
choices=UnitChoices.choices,
|
|
||||||
widget=forms.Select(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
amount = forms.DecimalField(
|
|
||||||
label=_("Amount"),
|
|
||||||
label_suffix="",
|
|
||||||
required=True,
|
|
||||||
help_text=_("Insert the amount"),
|
|
||||||
decimal_places=2,
|
|
||||||
min_value=0.00,
|
|
||||||
widget=forms.NumberInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control",
|
|
||||||
"placeholder": "0,00",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
comment = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
label=_("Comment"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
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 action")
|
|
||||||
self.form_caption = _("Insert data for the new action")
|
|
||||||
choices =KonovaCode.objects.filter(
|
|
||||||
code_lists__in=[CODELIST_COMPENSATION_ACTION_ID],
|
|
||||||
is_archived=False,
|
|
||||||
is_leaf=True,
|
|
||||||
).values_list("id", flat=True)
|
|
||||||
choices = [
|
|
||||||
(choice, choice)
|
|
||||||
for choice in choices
|
|
||||||
]
|
|
||||||
self.fields["action_type"].choices = choices
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
action = self.instance.add_action(self)
|
|
||||||
self.instance.mark_as_edited(self.user, self.request, ADDED_COMPENSATION_ACTION)
|
|
||||||
return action
|
|
||||||
|
|
||||||
|
|
||||||
class EditCompensationActionModalForm(NewActionModalForm):
|
|
||||||
action = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.action = kwargs.pop("action", None)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Edit action")
|
|
||||||
form_data = {
|
|
||||||
"action_type": list(self.action.action_type.values_list("id", flat=True)),
|
|
||||||
"action_type_details": self.action.action_type_details.all(),
|
|
||||||
"amount": self.action.amount,
|
|
||||||
"unit": self.action.unit,
|
|
||||||
"comment": self.action.comment,
|
|
||||||
}
|
|
||||||
self.load_initial_data(form_data)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
action = self.action
|
|
||||||
action.action_type.set(self.cleaned_data.get("action_type", []))
|
|
||||||
action.action_type_details.set(self.cleaned_data.get("action_type_details", []))
|
|
||||||
action.amount = self.cleaned_data.get("amount", None)
|
|
||||||
action.unit = self.cleaned_data.get("unit", None)
|
|
||||||
action.comment = self.cleaned_data.get("comment", None)
|
|
||||||
action.save()
|
|
||||||
self.instance.mark_as_edited(self.user, self.request, edit_comment=COMPENSATION_ACTION_EDITED)
|
|
||||||
return action
|
|
||||||
|
|
||||||
|
|
||||||
class NewCompensationDocumentModalForm(NewDocumentModalForm):
|
|
||||||
document_model = CompensationDocument
|
|
||||||
|
|
||||||
|
|
||||||
class NewEcoAccountDocumentModalForm(NewDocumentModalForm):
|
|
||||||
document_model = EcoAccountDocument
|
|
7
compensation/forms/modals/__init__.py
Normal file
7
compensation/forms/modals/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
155
compensation/forms/modals/compensation_action.py
Normal file
155
compensation/forms/modals/compensation_action.py
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from dal import autocomplete
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from codelist.models import KonovaCode
|
||||||
|
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID
|
||||||
|
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple
|
||||||
|
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
||||||
|
from konova.utils.message_templates import COMPENSATION_ACTION_EDITED, ADDED_COMPENSATION_ACTION
|
||||||
|
|
||||||
|
|
||||||
|
class NewCompensationActionModalForm(BaseModalForm):
|
||||||
|
""" Form handling action related input
|
||||||
|
|
||||||
|
Compensation actions are the actions performed on the area, which shall be compensated. Actions will change the
|
||||||
|
surface of the area, the biotopes, and have an environmental impact. With actions the before-after states can change
|
||||||
|
(not in the process logic in Konova, but in the real world).
|
||||||
|
|
||||||
|
"""
|
||||||
|
from compensation.models import UnitChoices
|
||||||
|
action_type = forms.MultipleChoiceField(
|
||||||
|
label=_("Action Type"),
|
||||||
|
label_suffix="",
|
||||||
|
required=True,
|
||||||
|
help_text=_("An action can consist of multiple different action types. All the selected action types are expected to be performed according to the amount and unit below on this form."),
|
||||||
|
choices=[],
|
||||||
|
widget=CompensationActionTreeCheckboxSelectMultiple(),
|
||||||
|
)
|
||||||
|
action_type_details = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Action Type detail"),
|
||||||
|
label_suffix="",
|
||||||
|
required=False,
|
||||||
|
help_text=_("Select the action type detail"),
|
||||||
|
queryset=KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
code_lists__in=[CODELIST_COMPENSATION_ACTION_DETAIL_ID],
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
|
url="codelist:compensation-action-detail-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Action Type detail"),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
unit = forms.ChoiceField(
|
||||||
|
label=_("Unit"),
|
||||||
|
label_suffix="",
|
||||||
|
required=True,
|
||||||
|
help_text=_("Select the unit"),
|
||||||
|
choices=UnitChoices.choices,
|
||||||
|
widget=forms.Select(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
amount = forms.DecimalField(
|
||||||
|
label=_("Amount"),
|
||||||
|
label_suffix="",
|
||||||
|
required=True,
|
||||||
|
help_text=_("Insert the amount"),
|
||||||
|
decimal_places=2,
|
||||||
|
min_value=0.00,
|
||||||
|
widget=forms.NumberInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control",
|
||||||
|
"placeholder": "0,00",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
comment = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label=_("Comment"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
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 action")
|
||||||
|
self.form_caption = _("Insert data for the new action")
|
||||||
|
choices =KonovaCode.objects.filter(
|
||||||
|
code_lists__in=[CODELIST_COMPENSATION_ACTION_ID],
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
).values_list("id", flat=True)
|
||||||
|
choices = [
|
||||||
|
(choice, choice)
|
||||||
|
for choice in choices
|
||||||
|
]
|
||||||
|
self.fields["action_type"].choices = choices
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
action = self.instance.add_action(self)
|
||||||
|
self.instance.mark_as_edited(self.user, self.request, ADDED_COMPENSATION_ACTION)
|
||||||
|
return action
|
||||||
|
|
||||||
|
|
||||||
|
class EditCompensationActionModalForm(NewCompensationActionModalForm):
|
||||||
|
action = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.action = kwargs.pop("action", None)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Edit action")
|
||||||
|
form_data = {
|
||||||
|
"action_type": list(self.action.action_type.values_list("id", flat=True)),
|
||||||
|
"action_type_details": self.action.action_type_details.all(),
|
||||||
|
"amount": self.action.amount,
|
||||||
|
"unit": self.action.unit,
|
||||||
|
"comment": self.action.comment,
|
||||||
|
}
|
||||||
|
self.load_initial_data(form_data)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
action = self.action
|
||||||
|
action.action_type.set(self.cleaned_data.get("action_type", []))
|
||||||
|
action.action_type_details.set(self.cleaned_data.get("action_type_details", []))
|
||||||
|
action.amount = self.cleaned_data.get("amount", None)
|
||||||
|
action.unit = self.cleaned_data.get("unit", None)
|
||||||
|
action.comment = self.cleaned_data.get("comment", None)
|
||||||
|
action.save()
|
||||||
|
self.instance.mark_as_edited(self.user, self.request, edit_comment=COMPENSATION_ACTION_EDITED)
|
||||||
|
return action
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveCompensationActionModalForm(RemoveModalForm):
|
||||||
|
""" Removing modal form for CompensationAction
|
||||||
|
|
||||||
|
Can be used for anything, where removing shall be confirmed by the user a second time.
|
||||||
|
|
||||||
|
"""
|
||||||
|
action = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
action = kwargs.pop("action", None)
|
||||||
|
self.action = action
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.instance.remove_action(self)
|
92
compensation/forms/modals/deadline.py
Normal file
92
compensation/forms/modals/deadline.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from konova.forms.modals import BaseModalForm
|
||||||
|
from konova.models import DeadlineType
|
||||||
|
from konova.utils.message_templates import DEADLINE_EDITED
|
||||||
|
|
||||||
|
|
||||||
|
class NewDeadlineModalForm(BaseModalForm):
|
||||||
|
""" Form handling deadline related input
|
||||||
|
|
||||||
|
"""
|
||||||
|
type = forms.ChoiceField(
|
||||||
|
label=_("Deadline Type"),
|
||||||
|
label_suffix="",
|
||||||
|
required=True,
|
||||||
|
help_text=_("Select the deadline type"),
|
||||||
|
choices=DeadlineType.choices,
|
||||||
|
widget=forms.Select(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
date = forms.DateField(
|
||||||
|
label=_("Date"),
|
||||||
|
label_suffix="",
|
||||||
|
required=True,
|
||||||
|
help_text=_("Select date"),
|
||||||
|
widget=forms.DateInput(
|
||||||
|
attrs={
|
||||||
|
"type": "date",
|
||||||
|
"data-provide": "datepicker",
|
||||||
|
"class": "form-control",
|
||||||
|
},
|
||||||
|
format="%d.%m.%Y"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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 = _("New deadline")
|
||||||
|
self.form_caption = _("Insert data for the new deadline")
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
deadline = self.instance.add_deadline(self)
|
||||||
|
return deadline
|
||||||
|
|
||||||
|
|
||||||
|
class EditDeadlineModalForm(NewDeadlineModalForm):
|
||||||
|
deadline = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.deadline = kwargs.pop("deadline", None)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Edit deadline")
|
||||||
|
form_data = {
|
||||||
|
"type": self.deadline.type,
|
||||||
|
"date": str(self.deadline.date),
|
||||||
|
"comment": self.deadline.comment,
|
||||||
|
}
|
||||||
|
self.load_initial_data(form_data)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
deadline = self.deadline
|
||||||
|
deadline.type = self.cleaned_data.get("type", None)
|
||||||
|
deadline.date = self.cleaned_data.get("date", None)
|
||||||
|
deadline.comment = self.cleaned_data.get("comment", None)
|
||||||
|
deadline.save()
|
||||||
|
self.instance.mark_as_edited(self.user, self.request, edit_comment=DEADLINE_EDITED)
|
||||||
|
return deadline
|
17
compensation/forms/modals/document.py
Normal file
17
compensation/forms/modals/document.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from compensation.models import CompensationDocument, EcoAccountDocument
|
||||||
|
from konova.forms.modals import NewDocumentModalForm
|
||||||
|
|
||||||
|
|
||||||
|
class NewCompensationDocumentModalForm(NewDocumentModalForm):
|
||||||
|
document_model = CompensationDocument
|
||||||
|
|
||||||
|
|
||||||
|
class NewEcoAccountDocumentModalForm(NewDocumentModalForm):
|
||||||
|
document_model = EcoAccountDocument
|
136
compensation/forms/modals/payment.py
Normal file
136
compensation/forms/modals/payment.py
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import pgettext_lazy as _con, gettext_lazy as _
|
||||||
|
|
||||||
|
from konova.forms.modals import RemoveModalForm, BaseModalForm
|
||||||
|
from konova.utils.message_templates import PAYMENT_EDITED
|
||||||
|
|
||||||
|
|
||||||
|
class NewPaymentForm(BaseModalForm):
|
||||||
|
""" Form handling payment related input
|
||||||
|
|
||||||
|
"""
|
||||||
|
amount = forms.DecimalField(
|
||||||
|
min_value=0.00,
|
||||||
|
decimal_places=2,
|
||||||
|
label=_con("money", "Amount"), # contextual translation
|
||||||
|
label_suffix=_(""),
|
||||||
|
help_text=_("in Euro"),
|
||||||
|
widget=forms.NumberInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control",
|
||||||
|
"placeholder": "0,00",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
due = forms.DateField(
|
||||||
|
label=_("Due on"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
required=False,
|
||||||
|
help_text=_("Due on which date"),
|
||||||
|
widget=forms.DateInput(
|
||||||
|
attrs={
|
||||||
|
"type": "date",
|
||||||
|
"data-provide": "datepicker",
|
||||||
|
"class": "form-control",
|
||||||
|
},
|
||||||
|
format="%d.%m.%Y"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
comment = forms.CharField(
|
||||||
|
max_length=200,
|
||||||
|
required=False,
|
||||||
|
label=_("Comment"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
help_text=_("Additional comment, maximum {} letters").format(200),
|
||||||
|
widget=forms.Textarea(
|
||||||
|
attrs={
|
||||||
|
"rows": 5,
|
||||||
|
"class": "form-control"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.intervention = self.instance
|
||||||
|
self.form_title = _("Payment")
|
||||||
|
self.form_caption = _("Add a payment for intervention '{}'").format(self.intervention.title)
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
"""
|
||||||
|
Checks on form validity.
|
||||||
|
|
||||||
|
For this form we need to make sure that a date or a comment is set.
|
||||||
|
If both are missing, the user needs to enter at least an explanation why
|
||||||
|
there is no date to be entered.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
is_valid (bool): True if valid, False otherwise
|
||||||
|
"""
|
||||||
|
super_valid = super().is_valid()
|
||||||
|
date = self.cleaned_data["due"]
|
||||||
|
comment = self.cleaned_data["comment"] or None
|
||||||
|
if not date and not comment:
|
||||||
|
# At least one needs to be set!
|
||||||
|
self.add_error(
|
||||||
|
"comment",
|
||||||
|
_("If there is no date you can enter, please explain why.")
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
return super_valid
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
pay = self.instance.add_payment(self)
|
||||||
|
return pay
|
||||||
|
|
||||||
|
|
||||||
|
class EditPaymentModalForm(NewPaymentForm):
|
||||||
|
""" Form handling edit for Payment
|
||||||
|
|
||||||
|
"""
|
||||||
|
payment = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.payment = kwargs.pop("payment", None)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Edit payment")
|
||||||
|
form_date = {
|
||||||
|
"amount": self.payment.amount,
|
||||||
|
"due": str(self.payment.due_on),
|
||||||
|
"comment": self.payment.comment,
|
||||||
|
}
|
||||||
|
self.load_initial_data(form_date, disabled_fields=[])
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
payment = self.payment
|
||||||
|
payment.amount = self.cleaned_data.get("amount", None)
|
||||||
|
payment.due_on = self.cleaned_data.get("due", None)
|
||||||
|
payment.comment = self.cleaned_data.get("comment", None)
|
||||||
|
payment.save()
|
||||||
|
self.instance.mark_as_edited(self.user, self.request, edit_comment=PAYMENT_EDITED)
|
||||||
|
self.instance.send_data_to_egon()
|
||||||
|
return payment
|
||||||
|
|
||||||
|
|
||||||
|
class RemovePaymentModalForm(RemoveModalForm):
|
||||||
|
""" Removing modal form for Payment
|
||||||
|
|
||||||
|
Can be used for anything, where removing shall be confirmed by the user a second time.
|
||||||
|
|
||||||
|
"""
|
||||||
|
payment = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
payment = kwargs.pop("payment", None)
|
||||||
|
self.payment = payment
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.instance.remove_payment(self)
|
179
compensation/forms/modals/state.py
Normal file
179
compensation/forms/modals/state.py
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from bootstrap_modal_forms.utils import is_ajax
|
||||||
|
from dal import autocomplete
|
||||||
|
from django import forms
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.http import HttpResponseRedirect, HttpRequest
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from codelist.models import KonovaCode
|
||||||
|
from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID
|
||||||
|
from intervention.inputs import CompensationStateTreeRadioSelect
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms.modals import RemoveModalForm, BaseModalForm
|
||||||
|
from konova.utils.message_templates import COMPENSATION_STATE_EDITED, FORM_INVALID, ADDED_COMPENSATION_STATE
|
||||||
|
|
||||||
|
|
||||||
|
class NewCompensationStateModalForm(BaseModalForm):
|
||||||
|
""" Form handling state related input
|
||||||
|
|
||||||
|
Compensation states refer to 'before' and 'after' states of a compensated surface. Basically it means:
|
||||||
|
What has been on this area before changes/compensations have been applied and what will be the result ('after')?
|
||||||
|
|
||||||
|
"""
|
||||||
|
biotope_type = forms.ChoiceField(
|
||||||
|
label=_("Biotope Type"),
|
||||||
|
label_suffix="",
|
||||||
|
required=True,
|
||||||
|
help_text=_("Select the biotope type"),
|
||||||
|
widget=CompensationStateTreeRadioSelect(),
|
||||||
|
)
|
||||||
|
biotope_extra = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Biotope additional type"),
|
||||||
|
label_suffix="",
|
||||||
|
required=False,
|
||||||
|
help_text=_("Select an additional biotope type"),
|
||||||
|
queryset=KonovaCode.objects.filter(
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
code_lists__in=[CODELIST_BIOTOPES_EXTRA_CODES_ID],
|
||||||
|
),
|
||||||
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
|
url="codelist:biotope-extra-type-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Biotope additional type"),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
surface = forms.DecimalField(
|
||||||
|
min_value=0.00,
|
||||||
|
decimal_places=2,
|
||||||
|
label=_("Surface"),
|
||||||
|
label_suffix="",
|
||||||
|
required=True,
|
||||||
|
help_text=_("in m²"),
|
||||||
|
widget=forms.NumberInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control",
|
||||||
|
"placeholder": "0,00"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("New state")
|
||||||
|
self.form_caption = _("Insert data for the new state")
|
||||||
|
choices = KonovaCode.objects.filter(
|
||||||
|
code_lists__in=[CODELIST_BIOTOPES_ID],
|
||||||
|
is_archived=False,
|
||||||
|
is_leaf=True,
|
||||||
|
).values_list("id", flat=True)
|
||||||
|
choices = [
|
||||||
|
(choice, choice)
|
||||||
|
for choice in choices
|
||||||
|
]
|
||||||
|
self.fields["biotope_type"].choices = choices
|
||||||
|
|
||||||
|
def save(self, is_before_state: bool = False):
|
||||||
|
state = self.instance.add_state(self, is_before_state)
|
||||||
|
self.instance.mark_as_edited(self.user, self.request, ADDED_COMPENSATION_STATE)
|
||||||
|
return state
|
||||||
|
|
||||||
|
def process_request(self, request: HttpRequest, msg_success: str = _("Object removed"), msg_error: str = FORM_INVALID, redirect_url: str = None):
|
||||||
|
""" Generic processing of request
|
||||||
|
|
||||||
|
Wraps the request processing logic, so we don't need the same code everywhere a RemoveModalForm is being used
|
||||||
|
|
||||||
|
+++
|
||||||
|
The generic method from super class can not be used, since we need to do some request parameter check in here.
|
||||||
|
+++
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
msg_success (str): The message in case of successful removing
|
||||||
|
msg_error (str): The message in case of an error
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
redirect_url = redirect_url if redirect_url is not None else request.META.get("HTTP_REFERER", "home")
|
||||||
|
template = self.template
|
||||||
|
if request.method == "POST":
|
||||||
|
if self.is_valid():
|
||||||
|
# Modal forms send one POST for checking on data validity. This can be used to return possible errors
|
||||||
|
# on the form. A second POST (if no errors occured) is sent afterwards and needs to process the
|
||||||
|
# saving/commiting of the data to the database. is_ajax() performs this check. The first request is
|
||||||
|
# an ajax call, the second is a regular form POST.
|
||||||
|
if not is_ajax(request.META):
|
||||||
|
is_before_state = bool(request.GET.get("before", False))
|
||||||
|
self.save(is_before_state=is_before_state)
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
msg_success
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(redirect_url)
|
||||||
|
else:
|
||||||
|
context = {
|
||||||
|
"form": self,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
elif request.method == "GET":
|
||||||
|
context = {
|
||||||
|
"form": self,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class EditCompensationStateModalForm(NewCompensationStateModalForm):
|
||||||
|
state = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.state = kwargs.pop("state", None)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Edit state")
|
||||||
|
biotope_type_id = self.state.biotope_type.id if self.state.biotope_type else None
|
||||||
|
form_data = {
|
||||||
|
"biotope_type": biotope_type_id,
|
||||||
|
"biotope_extra": self.state.biotope_type_details.all(),
|
||||||
|
"surface": self.state.surface,
|
||||||
|
}
|
||||||
|
self.load_initial_data(form_data)
|
||||||
|
|
||||||
|
def save(self, is_before_state: bool = False):
|
||||||
|
state = self.state
|
||||||
|
biotope_type_id = self.cleaned_data.get("biotope_type", None)
|
||||||
|
state.biotope_type = KonovaCode.objects.get(id=biotope_type_id)
|
||||||
|
state.biotope_type_details.set(self.cleaned_data.get("biotope_extra", []))
|
||||||
|
state.surface = self.cleaned_data.get("surface", None)
|
||||||
|
state.save()
|
||||||
|
self.instance.mark_as_edited(self.user, self.request, edit_comment=COMPENSATION_STATE_EDITED)
|
||||||
|
return state
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveCompensationStateModalForm(RemoveModalForm):
|
||||||
|
""" Removing modal form for CompensationState
|
||||||
|
|
||||||
|
Can be used for anything, where removing shall be confirmed by the user a second time.
|
||||||
|
|
||||||
|
"""
|
||||||
|
state = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
state = kwargs.pop("state", None)
|
||||||
|
self.state = state
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.instance.remove_state(self)
|
@ -160,7 +160,7 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return reverse("compensation:acc:share", args=(self.id, self.access_token))
|
return reverse("compensation:acc:share-token", args=(self.id, self.access_token))
|
||||||
|
|
||||||
def send_notification_mail_on_deduction_change(self, data_change: dict):
|
def send_notification_mail_on_deduction_change(self, data_change: dict):
|
||||||
""" Sends notification mails for changes on the deduction
|
""" Sends notification mails for changes on the deduction
|
||||||
|
7
compensation/tables/__init__.py
Normal file
7
compensation/tables/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
@ -1,19 +1,19 @@
|
|||||||
"""
|
"""
|
||||||
Author: Michel Peltriaux
|
Author: Michel Peltriaux
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
Created on: 01.12.20
|
Created on: 18.08.22
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from konova.utils.message_templates import DATA_IS_UNCHECKED, DATA_CHECKED_ON_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.filters import CompensationTableFilter, EcoAccountTableFilter
|
from compensation.filters.compensation import CompensationTableFilter
|
||||||
from compensation.models import Compensation, EcoAccount
|
from compensation.models import Compensation
|
||||||
|
from konova.utils.message_templates import DATA_IS_UNCHECKED, DATA_CHECKED_ON_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
||||||
from konova.utils.tables import BaseTable, TableRenderMixin
|
from konova.utils.tables import BaseTable, TableRenderMixin
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
|
||||||
@ -187,160 +187,3 @@ class CompensationTable(BaseTable, TableRenderMixin):
|
|||||||
icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
|
icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
|
||||||
)
|
)
|
||||||
return format_html(html)
|
return format_html(html)
|
||||||
|
|
||||||
|
|
||||||
class EcoAccountTable(BaseTable, TableRenderMixin):
|
|
||||||
id = tables.Column(
|
|
||||||
verbose_name=_("Identifier"),
|
|
||||||
orderable=True,
|
|
||||||
accessor="identifier",
|
|
||||||
)
|
|
||||||
t = tables.Column(
|
|
||||||
verbose_name=_("Title"),
|
|
||||||
orderable=True,
|
|
||||||
accessor="title",
|
|
||||||
)
|
|
||||||
d = tables.Column(
|
|
||||||
verbose_name=_("Parcel gmrkng"),
|
|
||||||
orderable=True,
|
|
||||||
accessor="geometry",
|
|
||||||
)
|
|
||||||
av = tables.Column(
|
|
||||||
verbose_name=_("Available"),
|
|
||||||
orderable=True,
|
|
||||||
empty_values=[],
|
|
||||||
attrs={
|
|
||||||
"th": {
|
|
||||||
"class": "w-20",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
r = tables.Column(
|
|
||||||
verbose_name=_("Recorded"),
|
|
||||||
orderable=True,
|
|
||||||
empty_values=[],
|
|
||||||
accessor="recorded",
|
|
||||||
)
|
|
||||||
e = tables.Column(
|
|
||||||
verbose_name=_("Editable"),
|
|
||||||
orderable=True,
|
|
||||||
empty_values=[],
|
|
||||||
accessor="users",
|
|
||||||
)
|
|
||||||
lm = tables.Column(
|
|
||||||
verbose_name=_("Last edit"),
|
|
||||||
orderable=True,
|
|
||||||
accessor="modified__timestamp",
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
|
||||||
template_name = "django_tables2/bootstrap4.html"
|
|
||||||
|
|
||||||
def __init__(self, request: HttpRequest, *args, **kwargs):
|
|
||||||
self.title = _("Eco Accounts")
|
|
||||||
self.add_new_url = reverse("compensation:acc:new")
|
|
||||||
qs = kwargs.get("queryset", None)
|
|
||||||
self.filter = EcoAccountTableFilter(
|
|
||||||
user=request.user,
|
|
||||||
data=request.GET,
|
|
||||||
queryset=qs,
|
|
||||||
)
|
|
||||||
kwargs["queryset"] = self.filter.qs
|
|
||||||
super().__init__(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def render_id(self, value, record: EcoAccount):
|
|
||||||
""" Renders the id column for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (str): The identifier value
|
|
||||||
record (EcoAccount): The eco account record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
html = ""
|
|
||||||
html += self.render_link(
|
|
||||||
tooltip=_("Open {}").format(_("Eco-account")),
|
|
||||||
href=reverse("compensation:acc:detail", args=(record.id,)),
|
|
||||||
txt=value,
|
|
||||||
new_tab=False,
|
|
||||||
)
|
|
||||||
return format_html(html)
|
|
||||||
|
|
||||||
def render_av(self, value, record: EcoAccount):
|
|
||||||
""" Renders the available column for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (str): The identifier value
|
|
||||||
record (EcoAccount): The eco account record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
value_total, value_relative = record.get_available_rest()
|
|
||||||
html = render_to_string("konova/widgets/progressbar.html", {"value": value_relative})
|
|
||||||
return format_html(html)
|
|
||||||
|
|
||||||
def render_d(self, value, record: Compensation):
|
|
||||||
""" Renders the parcel district column for a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (str): The geometry
|
|
||||||
record (Compensation): The compensation record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
parcels = value.get_underlying_parcels().values_list(
|
|
||||||
"parcel_group__name",
|
|
||||||
flat=True
|
|
||||||
).distinct()
|
|
||||||
html = render_to_string(
|
|
||||||
"table/gmrkng_col.html",
|
|
||||||
{
|
|
||||||
"entries": parcels
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return html
|
|
||||||
|
|
||||||
def render_r(self, value, record: EcoAccount):
|
|
||||||
""" Renders the recorded column for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (str): The identifier value
|
|
||||||
record (EcoAccount): The eco account record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
html = ""
|
|
||||||
checked = value is not None
|
|
||||||
tooltip = _("Not recorded yet. Can not be used for deductions, yet.")
|
|
||||||
if checked:
|
|
||||||
on = value.get_timestamp_str_formatted()
|
|
||||||
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
|
||||||
html += self.render_bookmark(
|
|
||||||
tooltip=tooltip,
|
|
||||||
icn_filled=checked,
|
|
||||||
)
|
|
||||||
return format_html(html)
|
|
||||||
|
|
||||||
def render_e(self, value, record: EcoAccount):
|
|
||||||
""" Renders the editable column for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (str): The identifier value
|
|
||||||
record (EcoAccount): The eco account record
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
html = ""
|
|
||||||
# Do not use value in here, since value does use unprefetched 'users' manager, where record has already
|
|
||||||
# prefetched users data
|
|
||||||
has_access = record.is_shared_with(self.user)
|
|
||||||
html += self.render_icn(
|
|
||||||
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
|
||||||
icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
|
|
||||||
)
|
|
||||||
return format_html(html)
|
|
175
compensation/tables/eco_account.py
Normal file
175
compensation/tables/eco_account.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from compensation.filters.eco_account import EcoAccountTableFilter
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.utils.tables import TableRenderMixin, BaseTable
|
||||||
|
|
||||||
|
import django_tables2 as tables
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountTable(BaseTable, TableRenderMixin):
|
||||||
|
id = tables.Column(
|
||||||
|
verbose_name=_("Identifier"),
|
||||||
|
orderable=True,
|
||||||
|
accessor="identifier",
|
||||||
|
)
|
||||||
|
t = tables.Column(
|
||||||
|
verbose_name=_("Title"),
|
||||||
|
orderable=True,
|
||||||
|
accessor="title",
|
||||||
|
)
|
||||||
|
d = tables.Column(
|
||||||
|
verbose_name=_("Parcel gmrkng"),
|
||||||
|
orderable=True,
|
||||||
|
accessor="geometry",
|
||||||
|
)
|
||||||
|
av = tables.Column(
|
||||||
|
verbose_name=_("Available"),
|
||||||
|
orderable=True,
|
||||||
|
empty_values=[],
|
||||||
|
attrs={
|
||||||
|
"th": {
|
||||||
|
"class": "w-20",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
r = tables.Column(
|
||||||
|
verbose_name=_("Recorded"),
|
||||||
|
orderable=True,
|
||||||
|
empty_values=[],
|
||||||
|
accessor="recorded",
|
||||||
|
)
|
||||||
|
e = tables.Column(
|
||||||
|
verbose_name=_("Editable"),
|
||||||
|
orderable=True,
|
||||||
|
empty_values=[],
|
||||||
|
accessor="users",
|
||||||
|
)
|
||||||
|
lm = tables.Column(
|
||||||
|
verbose_name=_("Last edit"),
|
||||||
|
orderable=True,
|
||||||
|
accessor="modified__timestamp",
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
template_name = "django_tables2/bootstrap4.html"
|
||||||
|
|
||||||
|
def __init__(self, request: HttpRequest, *args, **kwargs):
|
||||||
|
self.title = _("Eco Accounts")
|
||||||
|
self.add_new_url = reverse("compensation:acc:new")
|
||||||
|
qs = kwargs.get("queryset", None)
|
||||||
|
self.filter = EcoAccountTableFilter(
|
||||||
|
user=request.user,
|
||||||
|
data=request.GET,
|
||||||
|
queryset=qs,
|
||||||
|
)
|
||||||
|
kwargs["queryset"] = self.filter.qs
|
||||||
|
super().__init__(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def render_id(self, value, record: EcoAccount):
|
||||||
|
""" Renders the id column for an eco account
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (str): The identifier value
|
||||||
|
record (EcoAccount): The eco account record
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
html = ""
|
||||||
|
html += self.render_link(
|
||||||
|
tooltip=_("Open {}").format(_("Eco-account")),
|
||||||
|
href=reverse("compensation:acc:detail", args=(record.id,)),
|
||||||
|
txt=value,
|
||||||
|
new_tab=False,
|
||||||
|
)
|
||||||
|
return format_html(html)
|
||||||
|
|
||||||
|
def render_av(self, value, record: EcoAccount):
|
||||||
|
""" Renders the available column for an eco account
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (str): The identifier value
|
||||||
|
record (EcoAccount): The eco account record
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
value_total, value_relative = record.get_available_rest()
|
||||||
|
html = render_to_string("konova/widgets/progressbar.html", {"value": value_relative})
|
||||||
|
return format_html(html)
|
||||||
|
|
||||||
|
def render_d(self, value, record):
|
||||||
|
""" Renders the parcel district column for a compensation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (str): The geometry
|
||||||
|
record (Compensation): The compensation record
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
parcels = value.get_underlying_parcels().values_list(
|
||||||
|
"parcel_group__name",
|
||||||
|
flat=True
|
||||||
|
).distinct()
|
||||||
|
html = render_to_string(
|
||||||
|
"table/gmrkng_col.html",
|
||||||
|
{
|
||||||
|
"entries": parcels
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return html
|
||||||
|
|
||||||
|
def render_r(self, value, record: EcoAccount):
|
||||||
|
""" Renders the recorded column for an eco account
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (str): The identifier value
|
||||||
|
record (EcoAccount): The eco account record
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
html = ""
|
||||||
|
checked = value is not None
|
||||||
|
tooltip = _("Not recorded yet. Can not be used for deductions, yet.")
|
||||||
|
if checked:
|
||||||
|
on = value.get_timestamp_str_formatted()
|
||||||
|
tooltip = _("Recorded on {} by {}").format(on, record.recorded.user)
|
||||||
|
html += self.render_bookmark(
|
||||||
|
tooltip=tooltip,
|
||||||
|
icn_filled=checked,
|
||||||
|
)
|
||||||
|
return format_html(html)
|
||||||
|
|
||||||
|
def render_e(self, value, record: EcoAccount):
|
||||||
|
""" Renders the editable column for an eco account
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (str): The identifier value
|
||||||
|
record (EcoAccount): The eco account record
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
html = ""
|
||||||
|
# Do not use value in here, since value does use unprefetched 'users' manager, where record has already
|
||||||
|
# prefetched users data
|
||||||
|
has_access = record.is_shared_with(self.user)
|
||||||
|
html += self.render_icn(
|
||||||
|
tooltip=_("Full access granted") if has_access else _("Access not granted"),
|
||||||
|
icn_class="fas fa-edit rlp-r-inv" if has_access else "far fa-edit",
|
||||||
|
)
|
||||||
|
return format_html(html)
|
@ -15,7 +15,7 @@
|
|||||||
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Resubmission' %}" data-form-url="{% url 'compensation:acc:resubmission-create' obj.id %}">
|
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Resubmission' %}" data-form-url="{% url 'compensation:acc:resubmission-create' obj.id %}">
|
||||||
{% fa5_icon 'bell' %}
|
{% fa5_icon 'bell' %}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'compensation:acc:share-create' obj.id %}">
|
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'compensation:acc:share-form' obj.id %}">
|
||||||
{% fa5_icon 'share-alt' %}
|
{% fa5_icon 'share-alt' %}
|
||||||
</button>
|
</button>
|
||||||
{% if is_ets_member %}
|
{% if is_ets_member %}
|
||||||
|
@ -6,7 +6,20 @@ Created on: 24.08.21
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from compensation.views.compensation import *
|
|
||||||
|
from compensation.views.compensation.document import EditCompensationDocumentView, NewCompensationDocumentView, \
|
||||||
|
GetCompensationDocumentView, RemoveCompensationDocumentView
|
||||||
|
from compensation.views.compensation.resubmission import CompensationResubmissionView
|
||||||
|
from compensation.views.compensation.report import report_view
|
||||||
|
from compensation.views.compensation.deadline import NewCompensationDeadlineView, EditCompensationDeadlineView, \
|
||||||
|
RemoveCompensationDeadlineView
|
||||||
|
from compensation.views.compensation.action import NewCompensationActionView, EditCompensationActionView, \
|
||||||
|
RemoveCompensationActionView
|
||||||
|
from compensation.views.compensation.state import NewCompensationStateView, EditCompensationStateView, \
|
||||||
|
RemoveCompensationStateView
|
||||||
|
from compensation.views.compensation.compensation import index_view, new_view, new_id_view, detail_view, edit_view, \
|
||||||
|
remove_view
|
||||||
|
from compensation.views.compensation.log import CompensationLogView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Main compensation
|
# Main compensation
|
||||||
@ -15,28 +28,28 @@ urlpatterns = [
|
|||||||
path('new/<intervention_id>', new_view, name='new'),
|
path('new/<intervention_id>', new_view, name='new'),
|
||||||
path('new', new_view, name='new'),
|
path('new', new_view, name='new'),
|
||||||
path('<id>', detail_view, name='detail'),
|
path('<id>', detail_view, name='detail'),
|
||||||
path('<id>/log', log_view, name='log'),
|
path('<id>/log', CompensationLogView.as_view(), name='log'),
|
||||||
path('<id>/edit', edit_view, name='edit'),
|
path('<id>/edit', edit_view, name='edit'),
|
||||||
path('<id>/remove', remove_view, name='remove'),
|
path('<id>/remove', remove_view, name='remove'),
|
||||||
|
|
||||||
path('<id>/state/new', state_new_view, name='new-state'),
|
path('<id>/state/new', NewCompensationStateView.as_view(), name='new-state'),
|
||||||
path('<id>/state/<state_id>/edit', state_edit_view, name='state-edit'),
|
path('<id>/state/<state_id>/edit', EditCompensationStateView.as_view(), name='state-edit'),
|
||||||
path('<id>/state/<state_id>/remove', state_remove_view, name='state-remove'),
|
path('<id>/state/<state_id>/remove', RemoveCompensationStateView.as_view(), name='state-remove'),
|
||||||
|
|
||||||
path('<id>/action/new', action_new_view, name='new-action'),
|
path('<id>/action/new', NewCompensationActionView.as_view(), name='new-action'),
|
||||||
path('<id>/action/<action_id>/edit', action_edit_view, name='action-edit'),
|
path('<id>/action/<action_id>/edit', EditCompensationActionView.as_view(), name='action-edit'),
|
||||||
path('<id>/action/<action_id>/remove', action_remove_view, name='action-remove'),
|
path('<id>/action/<action_id>/remove', RemoveCompensationActionView.as_view(), name='action-remove'),
|
||||||
|
|
||||||
path('<id>/deadline/new', deadline_new_view, name="new-deadline"),
|
path('<id>/deadline/new', NewCompensationDeadlineView.as_view(), name="new-deadline"),
|
||||||
path('<id>/deadline/<deadline_id>/edit', deadline_edit_view, name='deadline-edit'),
|
path('<id>/deadline/<deadline_id>/edit', EditCompensationDeadlineView.as_view(), name='deadline-edit'),
|
||||||
path('<id>/deadline/<deadline_id>/remove', deadline_remove_view, name='deadline-remove'),
|
path('<id>/deadline/<deadline_id>/remove', RemoveCompensationDeadlineView.as_view(), name='deadline-remove'),
|
||||||
path('<id>/report', report_view, name='report'),
|
path('<id>/report', report_view, name='report'),
|
||||||
path('<id>/resub', create_resubmission_view, name='resubmission-create'),
|
path('<id>/resub', CompensationResubmissionView.as_view(), name='resubmission-create'),
|
||||||
|
|
||||||
# Documents
|
# Documents
|
||||||
path('<id>/document/new/', new_document_view, name='new-doc'),
|
path('<id>/document/new/', NewCompensationDocumentView.as_view(), name='new-doc'),
|
||||||
path('<id>/document/<doc_id>', get_document_view, name='get-doc'),
|
path('<id>/document/<doc_id>', GetCompensationDocumentView.as_view(), name='get-doc'),
|
||||||
path('<id>/document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
|
path('<id>/document/<doc_id>/remove/', RemoveCompensationDocumentView.as_view(), name='remove-doc'),
|
||||||
path('<id>/document/<doc_id>/edit/', edit_document_view, name='edit-doc'),
|
path('<id>/document/<doc_id>/edit/', EditCompensationDocumentView.as_view(), name='edit-doc'),
|
||||||
|
|
||||||
]
|
]
|
@ -6,7 +6,25 @@ Created on: 24.08.21
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from compensation.views.eco_account import *
|
|
||||||
|
from compensation.autocomplete.eco_account import EcoAccountAutocomplete
|
||||||
|
from compensation.views.eco_account.eco_account import index_view, new_view, new_id_view, edit_view, remove_view, \
|
||||||
|
detail_view
|
||||||
|
from compensation.views.eco_account.log import EcoAccountLogView
|
||||||
|
from compensation.views.eco_account.record import EcoAccountRecordView
|
||||||
|
from compensation.views.eco_account.report import report_view
|
||||||
|
from compensation.views.eco_account.resubmission import EcoAccountResubmissionView
|
||||||
|
from compensation.views.eco_account.state import NewEcoAccountStateView, EditEcoAccountStateView, \
|
||||||
|
RemoveEcoAccountStateView
|
||||||
|
from compensation.views.eco_account.action import NewEcoAccountActionView, EditEcoAccountActionView, \
|
||||||
|
RemoveEcoAccountActionView
|
||||||
|
from compensation.views.eco_account.deadline import NewEcoAccountDeadlineView, EditEcoAccountDeadlineView, \
|
||||||
|
RemoveEcoAccountDeadlineView
|
||||||
|
from compensation.views.eco_account.share import EcoAccountShareByTokenView, EcoAccountShareFormView
|
||||||
|
from compensation.views.eco_account.document import GetEcoAccountDocumentView, NewEcoAccountDocumentView, \
|
||||||
|
EditEcoAccountDocumentView, RemoveEcoAccountDocumentView
|
||||||
|
from compensation.views.eco_account.deduction import NewEcoAccountDeductionView, EditEcoAccountDeductionView, \
|
||||||
|
RemoveEcoAccountDeductionView
|
||||||
|
|
||||||
app_name = "acc"
|
app_name = "acc"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -14,37 +32,39 @@ urlpatterns = [
|
|||||||
path('new/', new_view, name='new'),
|
path('new/', new_view, name='new'),
|
||||||
path('new/id', new_id_view, name='new-id'),
|
path('new/id', new_id_view, name='new-id'),
|
||||||
path('<id>', detail_view, name='detail'),
|
path('<id>', detail_view, name='detail'),
|
||||||
path('<id>/log', log_view, name='log'),
|
path('<id>/log', EcoAccountLogView.as_view(), name='log'),
|
||||||
path('<id>/record', record_view, name='record'),
|
path('<id>/record', EcoAccountRecordView.as_view(), name='record'),
|
||||||
path('<id>/report', report_view, name='report'),
|
path('<id>/report', report_view, name='report'),
|
||||||
path('<id>/edit', edit_view, name='edit'),
|
path('<id>/edit', edit_view, name='edit'),
|
||||||
path('<id>/remove', remove_view, name='remove'),
|
path('<id>/remove', remove_view, name='remove'),
|
||||||
path('<id>/resub', create_resubmission_view, name='resubmission-create'),
|
path('<id>/resub', EcoAccountResubmissionView.as_view(), name='resubmission-create'),
|
||||||
|
|
||||||
path('<id>/state/new', state_new_view, name='new-state'),
|
path('<id>/state/new', NewEcoAccountStateView.as_view(), name='new-state'),
|
||||||
path('<id>/state/<state_id>/edit', state_edit_view, name='state-edit'),
|
path('<id>/state/<state_id>/edit', EditEcoAccountStateView.as_view(), name='state-edit'),
|
||||||
path('<id>/state/<state_id>/remove', state_remove_view, name='state-remove'),
|
path('<id>/state/<state_id>/remove', RemoveEcoAccountStateView.as_view(), name='state-remove'),
|
||||||
|
|
||||||
path('<id>/action/new', action_new_view, name='new-action'),
|
path('<id>/action/new', NewEcoAccountActionView.as_view(), name='new-action'),
|
||||||
path('<id>/action/<action_id>/edit', action_edit_view, name='action-edit'),
|
path('<id>/action/<action_id>/edit', EditEcoAccountActionView.as_view(), name='action-edit'),
|
||||||
path('<id>/action/<action_id>/remove', action_remove_view, name='action-remove'),
|
path('<id>/action/<action_id>/remove', RemoveEcoAccountActionView.as_view(), name='action-remove'),
|
||||||
|
|
||||||
path('<id>/deadline/new', deadline_new_view, name="new-deadline"),
|
path('<id>/deadline/new', NewEcoAccountDeadlineView.as_view(), name="new-deadline"),
|
||||||
path('<id>/deadline/<deadline_id>/edit', deadline_edit_view, name='deadline-edit'),
|
path('<id>/deadline/<deadline_id>/edit', EditEcoAccountDeadlineView.as_view(), name='deadline-edit'),
|
||||||
path('<id>/deadline/<deadline_id>/remove', deadline_remove_view, name='deadline-remove'),
|
path('<id>/deadline/<deadline_id>/remove', RemoveEcoAccountDeadlineView.as_view(), name='deadline-remove'),
|
||||||
|
|
||||||
path('<id>/share/<token>', share_view, name='share'),
|
path('<id>/share/<token>', EcoAccountShareByTokenView.as_view(), name='share-token'),
|
||||||
path('<id>/share', create_share_view, name='share-create'),
|
path('<id>/share', EcoAccountShareFormView.as_view(), name='share-form'),
|
||||||
|
|
||||||
# Documents
|
# Documents
|
||||||
path('<id>/document/new/', new_document_view, name='new-doc'),
|
path('<id>/document/new/', NewEcoAccountDocumentView.as_view(), name='new-doc'),
|
||||||
path('<id>/document/<doc_id>', get_document_view, name='get-doc'),
|
path('<id>/document/<doc_id>', GetEcoAccountDocumentView.as_view(), name='get-doc'),
|
||||||
path('<id>/document/<doc_id>/edit', edit_document_view, name='edit-doc'),
|
path('<id>/document/<doc_id>/edit', EditEcoAccountDocumentView.as_view(), name='edit-doc'),
|
||||||
path('<id>/document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
|
path('<id>/document/<doc_id>/remove/', RemoveEcoAccountDocumentView.as_view(), name='remove-doc'),
|
||||||
|
|
||||||
# Eco-account deductions
|
# Eco-account deductions
|
||||||
path('<id>/deduction/<deduction_id>/remove', deduction_remove_view, name='remove-deduction'),
|
path('<id>/deduction/<deduction_id>/remove', RemoveEcoAccountDeductionView.as_view(), name='remove-deduction'),
|
||||||
path('<id>/deduction/<deduction_id>/edit', deduction_edit_view, name='edit-deduction'),
|
path('<id>/deduction/<deduction_id>/edit', EditEcoAccountDeductionView.as_view(), name='edit-deduction'),
|
||||||
path('<id>/deduct/new', new_deduction_view, name='new-deduction'),
|
path('<id>/deduct/new', NewEcoAccountDeductionView.as_view(), name='new-deduction'),
|
||||||
|
|
||||||
|
# Autocomplete
|
||||||
|
path("atcmplt/eco-accounts", EcoAccountAutocomplete.as_view(), name="autocomplete"),
|
||||||
]
|
]
|
@ -5,6 +5,5 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 16.11.21
|
Created on: 16.11.21
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from .compensation import *
|
|
||||||
from .eco_account import *
|
from .eco_account import *
|
||||||
from .payment import *
|
from .payment import *
|
||||||
|
@ -1,683 +0,0 @@
|
|||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
from django.db.models import Sum
|
|
||||||
from django.http import HttpRequest, JsonResponse
|
|
||||||
from django.shortcuts import render
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from compensation.forms.forms import NewCompensationForm, EditCompensationForm
|
|
||||||
from compensation.forms.modalForms import NewStateModalForm, NewDeadlineModalForm, NewActionModalForm, \
|
|
||||||
NewCompensationDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, \
|
|
||||||
EditCompensationStateModalForm, EditCompensationActionModalForm, EditDeadlineModalForm
|
|
||||||
from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument
|
|
||||||
from compensation.tables import CompensationTable
|
|
||||||
from intervention.models import Intervention
|
|
||||||
from konova.contexts import BaseContext
|
|
||||||
from konova.decorators import *
|
|
||||||
from konova.forms.modals import RemoveModalForm,RemoveDeadlineModalForm, EditDocumentModalForm, \
|
|
||||||
ResubmissionModalForm
|
|
||||||
from konova.forms import SimpleGeomForm
|
|
||||||
from konova.models import Deadline
|
|
||||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
|
||||||
from konova.utils.documents import get_document, remove_document
|
|
||||||
from konova.utils.generators import generate_qr_code
|
|
||||||
from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION, \
|
|
||||||
CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \
|
|
||||||
COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, \
|
|
||||||
DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED, \
|
|
||||||
DEADLINE_EDITED, RECORDED_BLOCKS_EDIT, PARAMS_INVALID, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
|
||||||
from konova.utils.user_checks import in_group
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@any_group_check
|
|
||||||
def index_view(request: HttpRequest):
|
|
||||||
"""
|
|
||||||
Renders the index view for compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A rendered view
|
|
||||||
"""
|
|
||||||
template = "generic_index.html"
|
|
||||||
compensations = Compensation.objects.filter(
|
|
||||||
deleted=None, # only show those which are not deleted individually
|
|
||||||
intervention__deleted=None, # and don't show the ones whose intervention has been deleted
|
|
||||||
)
|
|
||||||
table = CompensationTable(
|
|
||||||
request=request,
|
|
||||||
queryset=compensations
|
|
||||||
)
|
|
||||||
context = {
|
|
||||||
"table": table,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("Compensations - Overview"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "intervention_id")
|
|
||||||
def new_view(request: HttpRequest, intervention_id: str = None):
|
|
||||||
"""
|
|
||||||
Renders a view for a new compensation creation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "compensation/form/view.html"
|
|
||||||
if intervention_id is not None:
|
|
||||||
try:
|
|
||||||
intervention = Intervention.objects.get(id=intervention_id)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
messages.error(request, PARAMS_INVALID)
|
|
||||||
return redirect("home")
|
|
||||||
if intervention.is_recorded:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
RECORDED_BLOCKS_EDIT
|
|
||||||
)
|
|
||||||
return redirect("intervention:detail", id=intervention_id)
|
|
||||||
|
|
||||||
data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
|
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
|
||||||
if request.method == "POST":
|
|
||||||
if data_form.is_valid() and geom_form.is_valid():
|
|
||||||
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
|
||||||
comp = data_form.save(request.user, geom_form)
|
|
||||||
if generated_identifier != comp.identifier:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
IDENTIFIER_REPLACED.format(
|
|
||||||
generated_identifier,
|
|
||||||
comp.identifier
|
|
||||||
)
|
|
||||||
)
|
|
||||||
messages.success(request, COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
|
|
||||||
return redirect("compensation:detail", id=comp.id)
|
|
||||||
else:
|
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
|
||||||
else:
|
|
||||||
# For clarification: nothing in this case
|
|
||||||
pass
|
|
||||||
context = {
|
|
||||||
"form": data_form,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("New compensation"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
def new_id_view(request: HttpRequest):
|
|
||||||
""" JSON endpoint
|
|
||||||
|
|
||||||
Provides fetching of free identifiers for e.g. AJAX calls
|
|
||||||
|
|
||||||
"""
|
|
||||||
tmp = Compensation()
|
|
||||||
identifier = tmp.generate_new_identifier()
|
|
||||||
while Compensation.objects.filter(identifier=identifier).exists():
|
|
||||||
identifier = tmp.generate_new_identifier()
|
|
||||||
return JsonResponse(
|
|
||||||
data={
|
|
||||||
"gen_data": identifier
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def edit_view(request: HttpRequest, id: str):
|
|
||||||
"""
|
|
||||||
Renders a view for editing compensations
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "compensation/form/view.html"
|
|
||||||
# Get object from db
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
if comp.is_recorded:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
RECORDED_BLOCKS_EDIT
|
|
||||||
)
|
|
||||||
return redirect("compensation:detail", id=id)
|
|
||||||
|
|
||||||
# Create forms, initialize with values from db/from POST request
|
|
||||||
data_form = EditCompensationForm(request.POST or None, instance=comp)
|
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
|
|
||||||
if request.method == "POST":
|
|
||||||
if data_form.is_valid() and geom_form.is_valid():
|
|
||||||
# Preserve state of intervention recorded/checked to determine whether the user must be informed or not
|
|
||||||
# about a change of the recorded/checked state
|
|
||||||
intervention_recorded = comp.intervention.recorded is not None
|
|
||||||
intervention_checked = comp.intervention.checked is not None
|
|
||||||
|
|
||||||
# The data form takes the geom form for processing, as well as the performing user
|
|
||||||
comp = data_form.save(request.user, geom_form)
|
|
||||||
if intervention_recorded or intervention_checked:
|
|
||||||
messages.info(request, CHECKED_RECORDED_RESET)
|
|
||||||
messages.success(request, _("Compensation {} edited").format(comp.identifier))
|
|
||||||
return redirect("compensation:detail", id=comp.id)
|
|
||||||
else:
|
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
|
||||||
else:
|
|
||||||
# For clarification: nothing in this case
|
|
||||||
pass
|
|
||||||
context = {
|
|
||||||
"form": data_form,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@any_group_check
|
|
||||||
def detail_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a detail view for a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "compensation/detail/compensation/view.html"
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
geom_form = SimpleGeomForm(instance=comp)
|
|
||||||
parcels = comp.get_underlying_parcels()
|
|
||||||
_user = request.user
|
|
||||||
is_data_shared = comp.intervention.is_shared_with(_user)
|
|
||||||
|
|
||||||
# Order states according to surface
|
|
||||||
before_states = comp.before_states.all().prefetch_related("biotope_type").order_by("-surface")
|
|
||||||
after_states = comp.after_states.all().prefetch_related("biotope_type").order_by("-surface")
|
|
||||||
actions = comp.actions.all().prefetch_related("action_type")
|
|
||||||
|
|
||||||
# Precalculate logical errors between before- and after-states
|
|
||||||
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
|
||||||
sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
|
||||||
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
|
||||||
diff_states = abs(sum_before_states - sum_after_states)
|
|
||||||
|
|
||||||
request = comp.set_status_messages(request)
|
|
||||||
|
|
||||||
last_checked = comp.intervention.get_last_checked_action()
|
|
||||||
last_checked_tooltip = ""
|
|
||||||
if last_checked:
|
|
||||||
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(last_checked.get_timestamp_str_formatted(), last_checked.user)
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"obj": comp,
|
|
||||||
"last_checked": last_checked,
|
|
||||||
"last_checked_tooltip": last_checked_tooltip,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
"parcels": parcels,
|
|
||||||
"has_access": is_data_shared,
|
|
||||||
"actions": actions,
|
|
||||||
"before_states": before_states,
|
|
||||||
"after_states": after_states,
|
|
||||||
"sum_before_states": sum_before_states,
|
|
||||||
"sum_after_states": sum_after_states,
|
|
||||||
"diff_states": diff_states,
|
|
||||||
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
|
||||||
"is_zb_member": in_group(_user, ZB_GROUP),
|
|
||||||
"is_ets_member": in_group(_user, ETS_GROUP),
|
|
||||||
"LANIS_LINK": comp.get_LANIS_link(),
|
|
||||||
TAB_TITLE_IDENTIFIER: f"{comp.identifier} - {comp.title}",
|
|
||||||
"has_finished_deadlines": comp.get_finished_deadlines().exists(),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def log_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a log view using modal
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
template = "modal/modal_generic.html"
|
|
||||||
body_template = "log.html"
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"modal_body_template": body_template,
|
|
||||||
"log": comp.log.all(),
|
|
||||||
"modal_title": _("Log"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def remove_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a modal view for removing the compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
form = RemoveModalForm(request.POST or None, instance=comp, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request=request,
|
|
||||||
msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier),
|
|
||||||
redirect_url=reverse("compensation:index"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def new_document_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for uploading new documents
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id to which the new document will be related
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
form = NewCompensationDocumentModalForm(request.POST or None, request.FILES or None, instance=comp, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DOCUMENT_ADDED,
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def get_document_view(request: HttpRequest, id: str, doc_id: str):
|
|
||||||
""" Returns the document as downloadable file
|
|
||||||
|
|
||||||
Wraps the generic document fetcher function from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
doc = get_object_or_404(CompensationDocument, id=doc_id)
|
|
||||||
return get_document(doc)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def remove_document_view(request: HttpRequest, id: str, doc_id: str):
|
|
||||||
""" Removes the document from the database and file system
|
|
||||||
|
|
||||||
Wraps the generic functionality from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
doc = get_object_or_404(CompensationDocument, id=doc_id)
|
|
||||||
return remove_document(
|
|
||||||
request,
|
|
||||||
doc
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def edit_document_view(request: HttpRequest, id: str, doc_id: str):
|
|
||||||
""" Removes the document from the database and file system
|
|
||||||
|
|
||||||
Wraps the generic functionality from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
doc = get_object_or_404(CompensationDocument, id=doc_id)
|
|
||||||
form = EditDocumentModalForm(request.POST or None, request.FILES or None, instance=comp, document=doc, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
DOCUMENT_EDITED,
|
|
||||||
reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def state_new_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for adding new states for a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id to which the new state will be related
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
form = NewStateModalForm(request.POST or None, instance=comp, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_STATE_ADDED,
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def action_new_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for adding new actions for a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id to which the new state will be related
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
form = NewActionModalForm(request.POST or None, instance=comp, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_ACTION_ADDED,
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def action_edit_view(request: HttpRequest, id: str, action_id: str):
|
|
||||||
""" Renders a form for editing actions for a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
action_id (str): The action's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
action = get_object_or_404(CompensationAction, id=action_id)
|
|
||||||
form = EditCompensationActionModalForm(request.POST or None, instance=comp, action=action, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_ACTION_EDITED,
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def deadline_new_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for adding new states for a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id to which the new state will be related
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
form = NewDeadlineModalForm(request.POST or None, instance=comp, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEADLINE_ADDED,
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def deadline_edit_view(request: HttpRequest, id: str, deadline_id: str):
|
|
||||||
""" Renders a form for editing deadlines from a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
deadline_id (str): The deadline's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
deadline = get_object_or_404(Deadline, id=deadline_id)
|
|
||||||
form = EditDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEADLINE_EDITED,
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str):
|
|
||||||
""" Renders a form for removing deadlines from a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
deadline_id (str): The deadline's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
deadline = get_object_or_404(Deadline, id=deadline_id)
|
|
||||||
form = RemoveDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEADLINE_REMOVED,
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def state_remove_view(request: HttpRequest, id: str, state_id: str):
|
|
||||||
""" Renders a form for removing a compensation state
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
state_id (str): The state's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
state = get_object_or_404(CompensationState, id=state_id)
|
|
||||||
form = RemoveCompensationStateModalForm(request.POST or None, instance=comp, state=state, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_STATE_REMOVED,
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def state_edit_view(request: HttpRequest, id: str, state_id: str):
|
|
||||||
""" Renders a form for editing a compensation state
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
state_id (str): The state's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
state = get_object_or_404(CompensationState, id=state_id)
|
|
||||||
form = EditCompensationStateModalForm(request.POST or None, instance=comp, state=state, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_STATE_EDITED,
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def action_remove_view(request: HttpRequest, id: str, action_id: str):
|
|
||||||
""" Renders a form for removing a compensation action
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
id (str): The action's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
action = get_object_or_404(CompensationAction, id=action_id)
|
|
||||||
form = RemoveCompensationActionModalForm(request.POST or None, instance=comp, action=action, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_ACTION_REMOVED,
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def report_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders the public report view
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The id of the intervention
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Reuse the compensation report template since compensations are structurally identical
|
|
||||||
template = "compensation/report/compensation/report.html"
|
|
||||||
comp = get_object_or_404(Compensation, id=id)
|
|
||||||
|
|
||||||
tab_title = _("Report {}").format(comp.identifier)
|
|
||||||
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
|
||||||
if not comp.is_ready_for_publish():
|
|
||||||
template = "report/unavailable.html"
|
|
||||||
context = {
|
|
||||||
TAB_TITLE_IDENTIFIER: tab_title,
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
# Prepare data for map viewer
|
|
||||||
geom_form = SimpleGeomForm(
|
|
||||||
instance=comp
|
|
||||||
)
|
|
||||||
parcels = comp.get_underlying_parcels()
|
|
||||||
|
|
||||||
qrcode_url = request.build_absolute_uri(reverse("compensation:report", args=(id,)))
|
|
||||||
qrcode_img = generate_qr_code(qrcode_url, 10)
|
|
||||||
qrcode_lanis_url = comp.get_LANIS_link()
|
|
||||||
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
|
||||||
|
|
||||||
# Order states by surface
|
|
||||||
before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
|
||||||
after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type")
|
|
||||||
actions = comp.actions.all().prefetch_related("action_type")
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"obj": comp,
|
|
||||||
"qrcode": {
|
|
||||||
"img": qrcode_img,
|
|
||||||
"url": qrcode_url,
|
|
||||||
},
|
|
||||||
"qrcode_lanis": {
|
|
||||||
"img": qrcode_img_lanis,
|
|
||||||
"url": qrcode_lanis_url,
|
|
||||||
},
|
|
||||||
"has_access": False, # disables action buttons during rendering
|
|
||||||
"before_states": before_states,
|
|
||||||
"after_states": after_states,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
"parcels": parcels,
|
|
||||||
"actions": actions,
|
|
||||||
TAB_TITLE_IDENTIFIER: tab_title,
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Compensation, "id")
|
|
||||||
def create_resubmission_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders resubmission form for a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): Compensation's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
com = get_object_or_404(Compensation, id=id)
|
|
||||||
form = ResubmissionModalForm(request.POST or None, instance=com, request=request)
|
|
||||||
form.action_url = reverse("compensation:resubmission-create", args=(id,))
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=_("Resubmission set"),
|
|
||||||
redirect_url=reverse("compensation:detail", args=(id,))
|
|
||||||
)
|
|
7
compensation/views/compensation/__init__.py
Normal file
7
compensation/views/compensation/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
54
compensation/views/compensation/action.py
Normal file
54
compensation/views/compensation/action.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.forms.modals.compensation_action import RemoveCompensationActionModalForm, \
|
||||||
|
EditCompensationActionModalForm, NewCompensationActionModalForm
|
||||||
|
from compensation.models import Compensation, CompensationAction
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.utils.message_templates import COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_EDITED, \
|
||||||
|
COMPENSATION_ACTION_ADDED
|
||||||
|
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
|
||||||
|
AbstractRemoveCompensationActionView
|
||||||
|
|
||||||
|
|
||||||
|
class NewCompensationActionView(AbstractNewCompensationActionView):
|
||||||
|
model = Compensation
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditCompensationActionView(AbstractEditCompensationActionView):
|
||||||
|
model = Compensation
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveCompensationActionView(AbstractRemoveCompensationActionView):
|
||||||
|
model = Compensation
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
272
compensation/views/compensation/compensation.py
Normal file
272
compensation/views/compensation/compensation.py
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.db.models import Sum
|
||||||
|
from django.http import HttpRequest, JsonResponse
|
||||||
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from compensation.forms.compensation import EditCompensationForm, NewCompensationForm
|
||||||
|
from compensation.models import Compensation
|
||||||
|
from compensation.tables.compensation import CompensationTable
|
||||||
|
from intervention.models import Intervention
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, any_group_check
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.forms.modals import RemoveModalForm
|
||||||
|
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \
|
||||||
|
RECORDED_BLOCKS_EDIT, CHECKED_RECORDED_RESET, FORM_INVALID, PARAMS_INVALID, IDENTIFIER_REPLACED, \
|
||||||
|
COMPENSATION_ADDED_TEMPLATE
|
||||||
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@any_group_check
|
||||||
|
def index_view(request: HttpRequest):
|
||||||
|
"""
|
||||||
|
Renders the index view for compensation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A rendered view
|
||||||
|
"""
|
||||||
|
template = "generic_index.html"
|
||||||
|
compensations = Compensation.objects.filter(
|
||||||
|
deleted=None, # only show those which are not deleted individually
|
||||||
|
intervention__deleted=None, # and don't show the ones whose intervention has been deleted
|
||||||
|
)
|
||||||
|
table = CompensationTable(
|
||||||
|
request=request,
|
||||||
|
queryset=compensations
|
||||||
|
)
|
||||||
|
context = {
|
||||||
|
"table": table,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Compensations - Overview"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
@shared_access_required(Intervention, "intervention_id")
|
||||||
|
def new_view(request: HttpRequest, intervention_id: str = None):
|
||||||
|
"""
|
||||||
|
Renders a view for a new compensation creation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "compensation/form/view.html"
|
||||||
|
if intervention_id is not None:
|
||||||
|
try:
|
||||||
|
intervention = Intervention.objects.get(id=intervention_id)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
messages.error(request, PARAMS_INVALID)
|
||||||
|
return redirect("home")
|
||||||
|
if intervention.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("intervention:detail", id=intervention_id)
|
||||||
|
|
||||||
|
data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
|
if request.method == "POST":
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
||||||
|
comp = data_form.save(request.user, geom_form)
|
||||||
|
if generated_identifier != comp.identifier:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
IDENTIFIER_REPLACED.format(
|
||||||
|
generated_identifier,
|
||||||
|
comp.identifier
|
||||||
|
)
|
||||||
|
)
|
||||||
|
messages.success(request, COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
|
||||||
|
return redirect("compensation:detail", id=comp.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||||
|
else:
|
||||||
|
# For clarification: nothing in this case
|
||||||
|
pass
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("New compensation"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
def new_id_view(request: HttpRequest):
|
||||||
|
""" JSON endpoint
|
||||||
|
|
||||||
|
Provides fetching of free identifiers for e.g. AJAX calls
|
||||||
|
|
||||||
|
"""
|
||||||
|
tmp = Compensation()
|
||||||
|
identifier = tmp.generate_new_identifier()
|
||||||
|
while Compensation.objects.filter(identifier=identifier).exists():
|
||||||
|
identifier = tmp.generate_new_identifier()
|
||||||
|
return JsonResponse(
|
||||||
|
data={
|
||||||
|
"gen_data": identifier
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
@shared_access_required(Compensation, "id")
|
||||||
|
def edit_view(request: HttpRequest, id: str):
|
||||||
|
"""
|
||||||
|
Renders a view for editing compensations
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "compensation/form/view.html"
|
||||||
|
# Get object from db
|
||||||
|
comp = get_object_or_404(Compensation, id=id)
|
||||||
|
if comp.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("compensation:detail", id=id)
|
||||||
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditCompensationForm(request.POST or None, instance=comp)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
|
||||||
|
if request.method == "POST":
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
# Preserve state of intervention recorded/checked to determine whether the user must be informed or not
|
||||||
|
# about a change of the recorded/checked state
|
||||||
|
intervention_recorded = comp.intervention.recorded is not None
|
||||||
|
intervention_checked = comp.intervention.checked is not None
|
||||||
|
|
||||||
|
# The data form takes the geom form for processing, as well as the performing user
|
||||||
|
comp = data_form.save(request.user, geom_form)
|
||||||
|
if intervention_recorded or intervention_checked:
|
||||||
|
messages.info(request, CHECKED_RECORDED_RESET)
|
||||||
|
messages.success(request, _("Compensation {} edited").format(comp.identifier))
|
||||||
|
return redirect("compensation:detail", id=comp.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||||
|
else:
|
||||||
|
# For clarification: nothing in this case
|
||||||
|
pass
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@any_group_check
|
||||||
|
def detail_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders a detail view for a compensation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The compensation's id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "compensation/detail/compensation/view.html"
|
||||||
|
comp = get_object_or_404(Compensation, id=id)
|
||||||
|
geom_form = SimpleGeomForm(instance=comp)
|
||||||
|
parcels = comp.get_underlying_parcels()
|
||||||
|
_user = request.user
|
||||||
|
is_data_shared = comp.intervention.is_shared_with(_user)
|
||||||
|
|
||||||
|
# Order states according to surface
|
||||||
|
before_states = comp.before_states.all().prefetch_related("biotope_type").order_by("-surface")
|
||||||
|
after_states = comp.after_states.all().prefetch_related("biotope_type").order_by("-surface")
|
||||||
|
actions = comp.actions.all().prefetch_related("action_type")
|
||||||
|
|
||||||
|
# Precalculate logical errors between before- and after-states
|
||||||
|
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
||||||
|
sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
|
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
|
diff_states = abs(sum_before_states - sum_after_states)
|
||||||
|
|
||||||
|
request = comp.set_status_messages(request)
|
||||||
|
|
||||||
|
last_checked = comp.intervention.get_last_checked_action()
|
||||||
|
last_checked_tooltip = ""
|
||||||
|
if last_checked:
|
||||||
|
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(last_checked.get_timestamp_str_formatted(), last_checked.user)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": comp,
|
||||||
|
"last_checked": last_checked,
|
||||||
|
"last_checked_tooltip": last_checked_tooltip,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"has_access": is_data_shared,
|
||||||
|
"actions": actions,
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"sum_before_states": sum_before_states,
|
||||||
|
"sum_after_states": sum_after_states,
|
||||||
|
"diff_states": diff_states,
|
||||||
|
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
||||||
|
"is_zb_member": in_group(_user, ZB_GROUP),
|
||||||
|
"is_ets_member": in_group(_user, ETS_GROUP),
|
||||||
|
"LANIS_LINK": comp.get_LANIS_link(),
|
||||||
|
TAB_TITLE_IDENTIFIER: f"{comp.identifier} - {comp.title}",
|
||||||
|
"has_finished_deadlines": comp.get_finished_deadlines().exists(),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
@shared_access_required(Compensation, "id")
|
||||||
|
def remove_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders a modal view for removing the compensation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The compensation's id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
comp = get_object_or_404(Compensation, id=id)
|
||||||
|
form = RemoveModalForm(request.POST or None, instance=comp, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request=request,
|
||||||
|
msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier),
|
||||||
|
redirect_url=reverse("compensation:index"),
|
||||||
|
)
|
||||||
|
|
46
compensation/views/compensation/deadline.py
Normal file
46
compensation/views/compensation/deadline.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import Compensation
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.deadline import AbstractRemoveDeadlineView, AbstractEditDeadlineView, AbstractNewDeadlineView
|
||||||
|
|
||||||
|
|
||||||
|
class NewCompensationDeadlineView(AbstractNewDeadlineView):
|
||||||
|
model = Compensation
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditCompensationDeadlineView(AbstractEditDeadlineView):
|
||||||
|
model = Compensation
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveCompensationDeadlineView(AbstractRemoveDeadlineView):
|
||||||
|
model = Compensation
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
63
compensation/views/compensation/document.py
Normal file
63
compensation/views/compensation/document.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.forms.modals.document import NewCompensationDocumentModalForm
|
||||||
|
from compensation.models import Compensation, CompensationDocument
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.forms.modals import EditDocumentModalForm
|
||||||
|
from konova.views.document import AbstractNewDocumentView, AbstractGetDocumentView, AbstractRemoveDocumentView, \
|
||||||
|
AbstractEditDocumentView
|
||||||
|
|
||||||
|
|
||||||
|
class NewCompensationDocumentView(AbstractNewDocumentView):
|
||||||
|
model = Compensation
|
||||||
|
form = NewCompensationDocumentModalForm
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class GetCompensationDocumentView(AbstractGetDocumentView):
|
||||||
|
model = Compensation
|
||||||
|
document_model = CompensationDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveCompensationDocumentView(AbstractRemoveDocumentView):
|
||||||
|
model = Compensation
|
||||||
|
document_model = CompensationDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditCompensationDocumentView(AbstractEditDocumentView):
|
||||||
|
model = Compensation
|
||||||
|
document_model = CompensationDocument
|
||||||
|
form = EditDocumentModalForm
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
23
compensation/views/compensation/log.py
Normal file
23
compensation/views/compensation/log.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import Compensation
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.log import AbstractLogView
|
||||||
|
|
||||||
|
|
||||||
|
class CompensationLogView(AbstractLogView):
|
||||||
|
model = Compensation
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
79
compensation/views/compensation/report.py
Normal file
79
compensation/views/compensation/report.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from compensation.models import Compensation
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.generators import generate_qr_code
|
||||||
|
|
||||||
|
|
||||||
|
def report_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders the public report view
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The id of the intervention
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Reuse the compensation report template since compensations are structurally identical
|
||||||
|
template = "compensation/report/compensation/report.html"
|
||||||
|
comp = get_object_or_404(Compensation, id=id)
|
||||||
|
|
||||||
|
tab_title = _("Report {}").format(comp.identifier)
|
||||||
|
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
||||||
|
if not comp.is_ready_for_publish():
|
||||||
|
template = "report/unavailable.html"
|
||||||
|
context = {
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
# Prepare data for map viewer
|
||||||
|
geom_form = SimpleGeomForm(
|
||||||
|
instance=comp
|
||||||
|
)
|
||||||
|
parcels = comp.get_underlying_parcels()
|
||||||
|
|
||||||
|
qrcode_url = request.build_absolute_uri(reverse("compensation:report", args=(id,)))
|
||||||
|
qrcode_img = generate_qr_code(qrcode_url, 10)
|
||||||
|
qrcode_lanis_url = comp.get_LANIS_link()
|
||||||
|
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
||||||
|
|
||||||
|
# Order states by surface
|
||||||
|
before_states = comp.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||||
|
after_states = comp.after_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||||
|
actions = comp.actions.all().prefetch_related("action_type")
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": comp,
|
||||||
|
"qrcode": {
|
||||||
|
"img": qrcode_img,
|
||||||
|
"url": qrcode_url,
|
||||||
|
},
|
||||||
|
"qrcode_lanis": {
|
||||||
|
"img": qrcode_img_lanis,
|
||||||
|
"url": qrcode_lanis_url,
|
||||||
|
},
|
||||||
|
"has_access": False, # disables action buttons during rendering
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"actions": actions,
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
25
compensation/views/compensation/resubmission.py
Normal file
25
compensation/views/compensation/resubmission.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import Compensation
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.resubmission import AbstractResubmissionView
|
||||||
|
|
||||||
|
|
||||||
|
class CompensationResubmissionView(AbstractResubmissionView):
|
||||||
|
model = Compensation
|
||||||
|
redirect_url_base = "compensation:detail"
|
||||||
|
form_action_url_base = "compensation:resubmission-create"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
47
compensation/views/compensation/state.py
Normal file
47
compensation/views/compensation/state.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import Compensation
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
|
||||||
|
AbstractRemoveCompensationStateView
|
||||||
|
|
||||||
|
|
||||||
|
class NewCompensationStateView(AbstractNewCompensationStateView):
|
||||||
|
model = Compensation
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditCompensationStateView(AbstractEditCompensationStateView):
|
||||||
|
model = Compensation
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveCompensationStateView(AbstractRemoveCompensationStateView):
|
||||||
|
model = Compensation
|
||||||
|
redirect_url = "compensation:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Compensation, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
@ -1,865 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
|
||||||
Created on: 09.08.21
|
|
||||||
|
|
||||||
"""
|
|
||||||
from django.contrib import messages
|
|
||||||
from django.db.models import Sum
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
from django.http import HttpRequest, Http404, JsonResponse
|
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
|
||||||
|
|
||||||
from compensation.forms.forms import NewEcoAccountForm, EditEcoAccountForm
|
|
||||||
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \
|
|
||||||
NewEcoAccountDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, \
|
|
||||||
EditCompensationStateModalForm, EditCompensationActionModalForm, EditDeadlineModalForm
|
|
||||||
from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction
|
|
||||||
from compensation.tables import EcoAccountTable
|
|
||||||
from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm, \
|
|
||||||
EditEcoAccountDeductionModalForm
|
|
||||||
from konova.contexts import BaseContext
|
|
||||||
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \
|
|
||||||
shared_access_required
|
|
||||||
from konova.forms.modals import RemoveModalForm, RecordModalForm, \
|
|
||||||
RemoveDeadlineModalForm, EditDocumentModalForm, ResubmissionModalForm
|
|
||||||
from konova.forms import SimpleGeomForm
|
|
||||||
from konova.models import Deadline
|
|
||||||
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
|
||||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
|
||||||
from konova.utils.documents import get_document, remove_document
|
|
||||||
from konova.utils.generators import generate_qr_code
|
|
||||||
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, \
|
|
||||||
CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \
|
|
||||||
COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, \
|
|
||||||
DEDUCTION_EDITED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED, DEADLINE_EDITED, \
|
|
||||||
RECORDED_BLOCKS_EDIT
|
|
||||||
from konova.utils.user_checks import in_group
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@any_group_check
|
|
||||||
def index_view(request: HttpRequest):
|
|
||||||
"""
|
|
||||||
Renders the index view for eco accounts
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A rendered view
|
|
||||||
"""
|
|
||||||
template = "generic_index.html"
|
|
||||||
eco_accounts = EcoAccount.objects.filter(
|
|
||||||
deleted=None,
|
|
||||||
)
|
|
||||||
table = EcoAccountTable(
|
|
||||||
request=request,
|
|
||||||
queryset=eco_accounts
|
|
||||||
)
|
|
||||||
context = {
|
|
||||||
"table": table,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("Eco-account - Overview"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
def new_view(request: HttpRequest):
|
|
||||||
"""
|
|
||||||
Renders a view for a new eco account creation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "compensation/form/view.html"
|
|
||||||
data_form = NewEcoAccountForm(request.POST or None)
|
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
|
||||||
if request.method == "POST":
|
|
||||||
if data_form.is_valid() and geom_form.is_valid():
|
|
||||||
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
|
||||||
acc = data_form.save(request.user, geom_form)
|
|
||||||
if generated_identifier != acc.identifier:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
IDENTIFIER_REPLACED.format(
|
|
||||||
generated_identifier,
|
|
||||||
acc.identifier
|
|
||||||
)
|
|
||||||
)
|
|
||||||
messages.success(request, _("Eco-Account {} added").format(acc.identifier))
|
|
||||||
return redirect("compensation:acc:detail", id=acc.id)
|
|
||||||
else:
|
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
|
||||||
else:
|
|
||||||
# For clarification: nothing in this case
|
|
||||||
pass
|
|
||||||
context = {
|
|
||||||
"form": data_form,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("New Eco-Account"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
def new_id_view(request: HttpRequest):
|
|
||||||
""" JSON endpoint
|
|
||||||
|
|
||||||
Provides fetching of free identifiers for e.g. AJAX calls
|
|
||||||
|
|
||||||
"""
|
|
||||||
tmp = EcoAccount()
|
|
||||||
identifier = tmp.generate_new_identifier()
|
|
||||||
while EcoAccount.objects.filter(identifier=identifier).exists():
|
|
||||||
identifier = tmp.generate_new_identifier()
|
|
||||||
return JsonResponse(
|
|
||||||
data={
|
|
||||||
"gen_data": identifier
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def edit_view(request: HttpRequest, id: str):
|
|
||||||
"""
|
|
||||||
Renders a view for editing compensations
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "compensation/form/view.html"
|
|
||||||
# Get object from db
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
if acc.is_recorded:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
RECORDED_BLOCKS_EDIT
|
|
||||||
)
|
|
||||||
return redirect("compensation:acc:detail", id=id)
|
|
||||||
|
|
||||||
# Create forms, initialize with values from db/from POST request
|
|
||||||
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
|
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
|
|
||||||
if request.method == "POST":
|
|
||||||
if data_form.is_valid() and geom_form.is_valid():
|
|
||||||
# The data form takes the geom form for processing, as well as the performing user
|
|
||||||
acc = data_form.save(request.user, geom_form)
|
|
||||||
messages.success(request, _("Eco-Account {} edited").format(acc.identifier))
|
|
||||||
return redirect("compensation:acc:detail", id=acc.id)
|
|
||||||
else:
|
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
|
||||||
else:
|
|
||||||
# For clarification: nothing in this case
|
|
||||||
pass
|
|
||||||
context = {
|
|
||||||
"form": data_form,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("Edit {}").format(acc.identifier),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@any_group_check
|
|
||||||
def detail_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a detail view for a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "compensation/detail/eco_account/view.html"
|
|
||||||
acc = get_object_or_404(
|
|
||||||
EcoAccount.objects.prefetch_related(
|
|
||||||
"deadlines",
|
|
||||||
).select_related(
|
|
||||||
'geometry',
|
|
||||||
'responsible',
|
|
||||||
),
|
|
||||||
id=id
|
|
||||||
)
|
|
||||||
geom_form = SimpleGeomForm(instance=acc)
|
|
||||||
parcels = acc.get_underlying_parcels()
|
|
||||||
_user = request.user
|
|
||||||
is_data_shared = acc.is_shared_with(_user)
|
|
||||||
|
|
||||||
# Order states according to surface
|
|
||||||
before_states = acc.before_states.order_by("-surface")
|
|
||||||
after_states = acc.after_states.order_by("-surface")
|
|
||||||
|
|
||||||
# Precalculate logical errors between before- and after-states
|
|
||||||
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
|
||||||
sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
|
||||||
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
|
||||||
diff_states = abs(sum_before_states - sum_after_states)
|
|
||||||
# Calculate rest of available surface for deductions
|
|
||||||
available_total, available_relative = acc.get_available_rest()
|
|
||||||
|
|
||||||
# Prefetch related data to decrease the amount of db connections
|
|
||||||
deductions = acc.deductions.filter(
|
|
||||||
intervention__deleted=None,
|
|
||||||
)
|
|
||||||
actions = acc.actions.all()
|
|
||||||
|
|
||||||
request = acc.set_status_messages(request)
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"obj": acc,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
"parcels": parcels,
|
|
||||||
"has_access": is_data_shared,
|
|
||||||
"before_states": before_states,
|
|
||||||
"after_states": after_states,
|
|
||||||
"sum_before_states": sum_before_states,
|
|
||||||
"sum_after_states": sum_after_states,
|
|
||||||
"diff_states": diff_states,
|
|
||||||
"available": available_relative,
|
|
||||||
"available_total": available_total,
|
|
||||||
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
|
||||||
"is_zb_member": in_group(_user, ZB_GROUP),
|
|
||||||
"is_ets_member": in_group(_user, ETS_GROUP),
|
|
||||||
"LANIS_LINK": acc.get_LANIS_link(),
|
|
||||||
"deductions": deductions,
|
|
||||||
"actions": actions,
|
|
||||||
TAB_TITLE_IDENTIFIER: f"{acc.identifier} - {acc.title}",
|
|
||||||
"has_finished_deadlines": acc.get_finished_deadlines().exists(),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def remove_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a modal view for removing the eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The account's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
|
|
||||||
# If the eco account has already been recorded OR there are already deductions, it can not be deleted by a regular
|
|
||||||
# default group user
|
|
||||||
if acc.recorded is not None or acc.deductions.exists():
|
|
||||||
user = request.user
|
|
||||||
if not in_group(user, ETS_GROUP):
|
|
||||||
messages.info(request, CANCEL_ACC_RECORDED_OR_DEDUCTED)
|
|
||||||
return redirect("compensation:acc:detail", id=id)
|
|
||||||
|
|
||||||
form = RemoveModalForm(request.POST or None, instance=acc, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request=request,
|
|
||||||
msg_success=_("Eco-account removed"),
|
|
||||||
redirect_url=reverse("compensation:acc:index"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
def deduction_remove_view(request: HttpRequest, id: str, deduction_id: str):
|
|
||||||
""" Renders a modal view for removing deductions
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The eco account's id
|
|
||||||
deduction_id (str): The deduction's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
try:
|
|
||||||
eco_deduction = acc.deductions.get(id=deduction_id)
|
|
||||||
if not eco_deduction.intervention.is_shared_with(request.user):
|
|
||||||
raise ObjectDoesNotExist()
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
raise Http404("Unknown deduction")
|
|
||||||
|
|
||||||
form = RemoveEcoAccountDeductionModalForm(request.POST or None, instance=acc, deduction=eco_deduction, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request=request,
|
|
||||||
msg_success=DEDUCTION_REMOVED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
def deduction_edit_view(request: HttpRequest, id: str, deduction_id: str):
|
|
||||||
""" Renders a modal view for editing deductions
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The eco account's id
|
|
||||||
deduction_id (str): The deduction's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
try:
|
|
||||||
eco_deduction = acc.deductions.get(id=deduction_id)
|
|
||||||
if not eco_deduction.intervention.is_shared_with(request.user):
|
|
||||||
raise ObjectDoesNotExist
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
raise Http404("Unknown deduction")
|
|
||||||
|
|
||||||
form = EditEcoAccountDeductionModalForm(request.POST or None, instance=acc, deduction=eco_deduction, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request=request,
|
|
||||||
msg_success=DEDUCTION_EDITED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def log_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a log view using modal
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The eco acount's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(EcoAccount, id=id)
|
|
||||||
template = "modal/modal_generic.html"
|
|
||||||
body_template = "log.html"
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"modal_body_template": body_template,
|
|
||||||
"log": comp.log.all(),
|
|
||||||
"modal_title": _("Log"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def record_view(request: HttpRequest, id:str):
|
|
||||||
""" Renders a modal form for recording an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The account's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
form = RecordModalForm(request.POST or None, instance=acc, request=request)
|
|
||||||
msg_succ = _("{} unrecorded") if acc.recorded else _("{} recorded")
|
|
||||||
msg_succ = msg_succ.format(acc.identifier)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_succ
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def state_new_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for adding new states for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The account's id to which the new state will be related
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
form = NewStateModalForm(request.POST or None, instance=acc, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_STATE_ADDED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def action_new_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for adding new actions for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The account's id to which the new state will be related
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
form = NewActionModalForm(request.POST or None, instance=acc, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_ACTION_ADDED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def state_remove_view(request: HttpRequest, id: str, state_id: str):
|
|
||||||
""" Renders a form for removing a compensation state
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
state_id (str): The state's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
state = get_object_or_404(CompensationState, id=state_id)
|
|
||||||
form = RemoveCompensationStateModalForm(request.POST or None, instance=acc, state=state, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_STATE_REMOVED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def state_edit_view(request: HttpRequest, id: str, state_id: str):
|
|
||||||
""" Renders a form for editing a compensation state
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
state_id (str): The state's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
state = get_object_or_404(CompensationState, id=state_id)
|
|
||||||
form = EditCompensationStateModalForm(request.POST or None, instance=acc, state=state, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_STATE_EDITED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def action_remove_view(request: HttpRequest, id: str, action_id: str):
|
|
||||||
""" Renders a form for removing a compensation action
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
id (str): The action's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
action = get_object_or_404(CompensationAction, id=action_id)
|
|
||||||
form = RemoveCompensationActionModalForm(request.POST or None, instance=acc, action=action, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_ACTION_REMOVED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def action_edit_view(request: HttpRequest, id: str, action_id: str):
|
|
||||||
""" Renders a form for editing a compensation action
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
id (str): The action's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
action = get_object_or_404(CompensationAction, id=action_id)
|
|
||||||
form = EditCompensationActionModalForm(request.POST or None, instance=acc, action=action, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_ACTION_EDITED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def deadline_edit_view(request: HttpRequest, id: str, deadline_id: str):
|
|
||||||
""" Renders a form for editing deadlines from a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
deadline_id (str): The deadline's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(EcoAccount, id=id)
|
|
||||||
deadline = get_object_or_404(Deadline, id=deadline_id)
|
|
||||||
form = EditDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEADLINE_EDITED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str):
|
|
||||||
""" Renders a form for removing deadlines from a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
deadline_id (str): The deadline's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comp = get_object_or_404(EcoAccount, id=id)
|
|
||||||
deadline = get_object_or_404(Deadline, id=deadline_id)
|
|
||||||
form = RemoveDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEADLINE_REMOVED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def deadline_new_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for adding new states for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The account's id to which the new state will be related
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
form = NewDeadlineModalForm(request.POST or None, instance=acc, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEADLINE_ADDED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def new_document_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for uploading new documents
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The account's id to which the new document will be related
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
form = NewEcoAccountDocumentModalForm(request.POST or None, request.FILES or None, instance=acc, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DOCUMENT_ADDED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def get_document_view(request: HttpRequest, id:str, doc_id: str):
|
|
||||||
""" Returns the document as downloadable file
|
|
||||||
|
|
||||||
Wraps the generic document fetcher function from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The account id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
doc = get_object_or_404(EcoAccountDocument, id=doc_id)
|
|
||||||
return get_document(doc)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def edit_document_view(request: HttpRequest, id: str, doc_id: str):
|
|
||||||
""" Removes the document from the database and file system
|
|
||||||
|
|
||||||
Wraps the generic functionality from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The account id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
doc = get_object_or_404(EcoAccountDocument, id=doc_id)
|
|
||||||
form = EditDocumentModalForm(request.POST or None, request.FILES or None, instance=acc, document=doc, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
DOCUMENT_EDITED,
|
|
||||||
reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def remove_document_view(request: HttpRequest, id: str, doc_id: str):
|
|
||||||
""" Removes the document from the database and file system
|
|
||||||
|
|
||||||
Wraps the generic functionality from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The account id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
doc = get_object_or_404(EcoAccountDocument, id=doc_id)
|
|
||||||
return remove_document(
|
|
||||||
request,
|
|
||||||
doc
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
def new_deduction_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a modal form view for creating deductions
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): THe incoming request
|
|
||||||
id (str): The eco account's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
if not acc.recorded:
|
|
||||||
raise Http404()
|
|
||||||
form = NewDeductionModalForm(request.POST or None, instance=acc, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEDUCTION_ADDED,
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def report_view(request:HttpRequest, id: str):
|
|
||||||
""" Renders the public report view
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The id of the intervention
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Reuse the compensation report template since EcoAccounts are structurally identical
|
|
||||||
template = "compensation/report/eco_account/report.html"
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
|
|
||||||
tab_title = _("Report {}").format(acc.identifier)
|
|
||||||
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
|
||||||
if not acc.is_ready_for_publish():
|
|
||||||
template = "report/unavailable.html"
|
|
||||||
context = {
|
|
||||||
TAB_TITLE_IDENTIFIER: tab_title,
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
# Prepare data for map viewer
|
|
||||||
geom_form = SimpleGeomForm(
|
|
||||||
instance=acc
|
|
||||||
)
|
|
||||||
parcels = acc.get_underlying_parcels()
|
|
||||||
|
|
||||||
qrcode_url = request.build_absolute_uri(reverse("ema:report", args=(id,)))
|
|
||||||
qrcode_img = generate_qr_code(qrcode_url, 10)
|
|
||||||
qrcode_lanis_url = acc.get_LANIS_link()
|
|
||||||
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
|
||||||
|
|
||||||
# Order states by surface
|
|
||||||
before_states = acc.before_states.all().order_by("-surface").select_related("biotope_type__parent")
|
|
||||||
after_states = acc.after_states.all().order_by("-surface").select_related("biotope_type__parent")
|
|
||||||
actions = acc.actions.all().prefetch_related("action_type__parent")
|
|
||||||
|
|
||||||
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
|
|
||||||
deductions = acc.deductions.all()\
|
|
||||||
.distinct("intervention")\
|
|
||||||
.select_related("intervention")\
|
|
||||||
.values_list("intervention__id", "intervention__identifier", "intervention__title", named=True)
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"obj": acc,
|
|
||||||
"qrcode": {
|
|
||||||
"img": qrcode_img,
|
|
||||||
"url": qrcode_url,
|
|
||||||
},
|
|
||||||
"qrcode_lanis": {
|
|
||||||
"img": qrcode_img_lanis,
|
|
||||||
"url": qrcode_lanis_url,
|
|
||||||
},
|
|
||||||
"has_access": False, # disables action buttons during rendering
|
|
||||||
"before_states": before_states,
|
|
||||||
"after_states": after_states,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
"parcels": parcels,
|
|
||||||
"actions": actions,
|
|
||||||
"deductions": deductions,
|
|
||||||
TAB_TITLE_IDENTIFIER: tab_title,
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def share_view(request: HttpRequest, id: str, token: str):
|
|
||||||
""" Performs sharing of an eco account
|
|
||||||
|
|
||||||
If token given in url is not valid, the user will be redirected to the dashboard
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): EcoAccount's id
|
|
||||||
token (str): Access token for EcoAccount
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
user = request.user
|
|
||||||
obj = get_object_or_404(EcoAccount, id=id)
|
|
||||||
# Check tokens
|
|
||||||
if obj.access_token == token:
|
|
||||||
# Send different messages in case user has already been added to list of sharing users
|
|
||||||
if obj.is_shared_with(user):
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
_("{} has already been shared with you").format(obj.identifier)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
messages.success(
|
|
||||||
request,
|
|
||||||
_("{} has been shared with you").format(obj.identifier)
|
|
||||||
)
|
|
||||||
obj.share_with_user(user)
|
|
||||||
return redirect("compensation:acc:detail", id=id)
|
|
||||||
else:
|
|
||||||
messages.error(
|
|
||||||
request,
|
|
||||||
_("Share link invalid"),
|
|
||||||
extra_tags="danger",
|
|
||||||
)
|
|
||||||
return redirect("home")
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def create_share_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders sharing form for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): EcoAccount's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
obj = get_object_or_404(EcoAccount, id=id)
|
|
||||||
form = ShareModalForm(request.POST or None, instance=obj, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=_("Share settings updated")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(EcoAccount, "id")
|
|
||||||
def create_resubmission_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders resubmission form for an eco account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): EcoAccount's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
acc = get_object_or_404(EcoAccount, id=id)
|
|
||||||
form = ResubmissionModalForm(request.POST or None, instance=acc, request=request)
|
|
||||||
form.action_url = reverse("compensation:acc:resubmission-create", args=(id,))
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=_("Resubmission set"),
|
|
||||||
redirect_url=reverse("compensation:acc:detail", args=(id,))
|
|
||||||
)
|
|
7
compensation/views/eco_account/__init__.py
Normal file
7
compensation/views/eco_account/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
47
compensation/views/eco_account/action.py
Normal file
47
compensation/views/eco_account/action.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
|
||||||
|
AbstractRemoveCompensationActionView
|
||||||
|
|
||||||
|
|
||||||
|
class NewEcoAccountActionView(AbstractNewCompensationActionView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditEcoAccountActionView(AbstractEditCompensationActionView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEcoAccountActionView(AbstractRemoveCompensationActionView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
46
compensation/views/eco_account/deadline.py
Normal file
46
compensation/views/eco_account/deadline.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.deadline import AbstractNewDeadlineView, AbstractEditDeadlineView, AbstractRemoveDeadlineView
|
||||||
|
|
||||||
|
|
||||||
|
class NewEcoAccountDeadlineView(AbstractNewDeadlineView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditEcoAccountDeadlineView(AbstractEditDeadlineView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEcoAccountDeadlineView(AbstractRemoveDeadlineView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
55
compensation/views/eco_account/deduction.py
Normal file
55
compensation/views/eco_account/deduction.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import Http404
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import default_group_required
|
||||||
|
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
|
||||||
|
|
||||||
|
|
||||||
|
class NewEcoAccountDeductionView(AbstractNewDeductionView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def _custom_check(self, obj):
|
||||||
|
if not obj.recorded:
|
||||||
|
raise Http404()
|
||||||
|
|
||||||
|
|
||||||
|
class EditEcoAccountDeductionView(AbstractEditDeductionView):
|
||||||
|
def _custom_check(self, obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEcoAccountDeductionView(AbstractRemoveDeductionView):
|
||||||
|
def _custom_check(self, obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
68
compensation/views/eco_account/document.py
Normal file
68
compensation/views/eco_account/document.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.forms.modals.document import NewEcoAccountDocumentModalForm
|
||||||
|
from compensation.models import EcoAccount, EcoAccountDocument
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.forms.modals import EditDocumentModalForm
|
||||||
|
from konova.utils.documents import remove_document, get_document
|
||||||
|
from konova.utils.message_templates import DOCUMENT_EDITED, DOCUMENT_ADDED
|
||||||
|
from konova.views.document import AbstractNewDocumentView, AbstractGetDocumentView, AbstractRemoveDocumentView, \
|
||||||
|
AbstractEditDocumentView
|
||||||
|
|
||||||
|
|
||||||
|
class NewEcoAccountDocumentView(AbstractNewDocumentView):
|
||||||
|
model = EcoAccount
|
||||||
|
form = NewEcoAccountDocumentModalForm
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class GetEcoAccountDocumentView(AbstractGetDocumentView):
|
||||||
|
model = EcoAccount
|
||||||
|
document_model = EcoAccountDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEcoAccountDocumentView(AbstractRemoveDocumentView):
|
||||||
|
model = EcoAccount
|
||||||
|
document_model = EcoAccountDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditEcoAccountDocumentView(AbstractEditDocumentView):
|
||||||
|
model = EcoAccount
|
||||||
|
document_model = EcoAccountDocument
|
||||||
|
form = EditDocumentModalForm
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
266
compensation/views/eco_account/eco_account.py
Normal file
266
compensation/views/eco_account/eco_account.py
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.db.models import Sum
|
||||||
|
from django.http import HttpRequest, JsonResponse
|
||||||
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from compensation.forms.eco_account import EditEcoAccountForm, NewEcoAccountForm
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from compensation.tables.eco_account import EcoAccountTable
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.decorators import shared_access_required, default_group_required, any_group_check
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.forms.modals import RemoveModalForm
|
||||||
|
from konova.settings import ETS_GROUP, DEFAULT_GROUP, ZB_GROUP
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.message_templates import CANCEL_ACC_RECORDED_OR_DEDUCTED, RECORDED_BLOCKS_EDIT, FORM_INVALID
|
||||||
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@any_group_check
|
||||||
|
def index_view(request: HttpRequest):
|
||||||
|
"""
|
||||||
|
Renders the index view for eco accounts
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A rendered view
|
||||||
|
"""
|
||||||
|
template = "generic_index.html"
|
||||||
|
eco_accounts = EcoAccount.objects.filter(
|
||||||
|
deleted=None,
|
||||||
|
)
|
||||||
|
table = EcoAccountTable(
|
||||||
|
request=request,
|
||||||
|
queryset=eco_accounts
|
||||||
|
)
|
||||||
|
context = {
|
||||||
|
"table": table,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Eco-account - Overview"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
def new_view(request: HttpRequest):
|
||||||
|
"""
|
||||||
|
Renders a view for a new eco account creation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "compensation/form/view.html"
|
||||||
|
data_form = NewEcoAccountForm(request.POST or None)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
|
if request.method == "POST":
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
||||||
|
acc = data_form.save(request.user, geom_form)
|
||||||
|
if generated_identifier != acc.identifier:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
IDENTIFIER_REPLACED.format(
|
||||||
|
generated_identifier,
|
||||||
|
acc.identifier
|
||||||
|
)
|
||||||
|
)
|
||||||
|
messages.success(request, _("Eco-Account {} added").format(acc.identifier))
|
||||||
|
return redirect("compensation:acc:detail", id=acc.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||||
|
else:
|
||||||
|
# For clarification: nothing in this case
|
||||||
|
pass
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("New Eco-Account"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
def new_id_view(request: HttpRequest):
|
||||||
|
""" JSON endpoint
|
||||||
|
|
||||||
|
Provides fetching of free identifiers for e.g. AJAX calls
|
||||||
|
|
||||||
|
"""
|
||||||
|
tmp = EcoAccount()
|
||||||
|
identifier = tmp.generate_new_identifier()
|
||||||
|
while EcoAccount.objects.filter(identifier=identifier).exists():
|
||||||
|
identifier = tmp.generate_new_identifier()
|
||||||
|
return JsonResponse(
|
||||||
|
data={
|
||||||
|
"gen_data": identifier
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
@shared_access_required(EcoAccount, "id")
|
||||||
|
def edit_view(request: HttpRequest, id: str):
|
||||||
|
"""
|
||||||
|
Renders a view for editing compensations
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "compensation/form/view.html"
|
||||||
|
# Get object from db
|
||||||
|
acc = get_object_or_404(EcoAccount, id=id)
|
||||||
|
if acc.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("compensation:acc:detail", id=id)
|
||||||
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditEcoAccountForm(request.POST or None, instance=acc)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=acc)
|
||||||
|
if request.method == "POST":
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
# The data form takes the geom form for processing, as well as the performing user
|
||||||
|
acc = data_form.save(request.user, geom_form)
|
||||||
|
messages.success(request, _("Eco-Account {} edited").format(acc.identifier))
|
||||||
|
return redirect("compensation:acc:detail", id=acc.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||||
|
else:
|
||||||
|
# For clarification: nothing in this case
|
||||||
|
pass
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(acc.identifier),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@any_group_check
|
||||||
|
def detail_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders a detail view for a compensation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The compensation's id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "compensation/detail/eco_account/view.html"
|
||||||
|
acc = get_object_or_404(
|
||||||
|
EcoAccount.objects.prefetch_related(
|
||||||
|
"deadlines",
|
||||||
|
).select_related(
|
||||||
|
'geometry',
|
||||||
|
'responsible',
|
||||||
|
),
|
||||||
|
id=id
|
||||||
|
)
|
||||||
|
geom_form = SimpleGeomForm(instance=acc)
|
||||||
|
parcels = acc.get_underlying_parcels()
|
||||||
|
_user = request.user
|
||||||
|
is_data_shared = acc.is_shared_with(_user)
|
||||||
|
|
||||||
|
# Order states according to surface
|
||||||
|
before_states = acc.before_states.order_by("-surface")
|
||||||
|
after_states = acc.after_states.order_by("-surface")
|
||||||
|
|
||||||
|
# Precalculate logical errors between before- and after-states
|
||||||
|
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
||||||
|
sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
|
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
|
diff_states = abs(sum_before_states - sum_after_states)
|
||||||
|
# Calculate rest of available surface for deductions
|
||||||
|
available_total, available_relative = acc.get_available_rest()
|
||||||
|
|
||||||
|
# Prefetch related data to decrease the amount of db connections
|
||||||
|
deductions = acc.deductions.filter(
|
||||||
|
intervention__deleted=None,
|
||||||
|
)
|
||||||
|
actions = acc.actions.all()
|
||||||
|
|
||||||
|
request = acc.set_status_messages(request)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": acc,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"has_access": is_data_shared,
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"sum_before_states": sum_before_states,
|
||||||
|
"sum_after_states": sum_after_states,
|
||||||
|
"diff_states": diff_states,
|
||||||
|
"available": available_relative,
|
||||||
|
"available_total": available_total,
|
||||||
|
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
||||||
|
"is_zb_member": in_group(_user, ZB_GROUP),
|
||||||
|
"is_ets_member": in_group(_user, ETS_GROUP),
|
||||||
|
"LANIS_LINK": acc.get_LANIS_link(),
|
||||||
|
"deductions": deductions,
|
||||||
|
"actions": actions,
|
||||||
|
TAB_TITLE_IDENTIFIER: f"{acc.identifier} - {acc.title}",
|
||||||
|
"has_finished_deadlines": acc.get_finished_deadlines().exists(),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
@shared_access_required(EcoAccount, "id")
|
||||||
|
def remove_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders a modal view for removing the eco account
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The account's id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
acc = get_object_or_404(EcoAccount, id=id)
|
||||||
|
|
||||||
|
# If the eco account has already been recorded OR there are already deductions, it can not be deleted by a regular
|
||||||
|
# default group user
|
||||||
|
if acc.recorded is not None or acc.deductions.exists():
|
||||||
|
user = request.user
|
||||||
|
if not in_group(user, ETS_GROUP):
|
||||||
|
messages.info(request, CANCEL_ACC_RECORDED_OR_DEDUCTED)
|
||||||
|
return redirect("compensation:acc:detail", id=id)
|
||||||
|
|
||||||
|
form = RemoveModalForm(request.POST or None, instance=acc, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request=request,
|
||||||
|
msg_success=_("Eco-account removed"),
|
||||||
|
redirect_url=reverse("compensation:acc:index"),
|
||||||
|
)
|
||||||
|
|
23
compensation/views/eco_account/log.py
Normal file
23
compensation/views/eco_account/log.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.log import AbstractLogView
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountLogView(AbstractLogView):
|
||||||
|
model = EcoAccount
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
23
compensation/views/eco_account/record.py
Normal file
23
compensation/views/eco_account/record.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required
|
||||||
|
from konova.views.record import AbstractRecordView
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountRecordView(AbstractRecordView):
|
||||||
|
model = EcoAccount
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
86
compensation/views/eco_account/report.py
Normal file
86
compensation/views/eco_account/report.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.generators import generate_qr_code
|
||||||
|
|
||||||
|
|
||||||
|
def report_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders the public report view
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The id of the intervention
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Reuse the compensation report template since EcoAccounts are structurally identical
|
||||||
|
template = "compensation/report/eco_account/report.html"
|
||||||
|
acc = get_object_or_404(EcoAccount, id=id)
|
||||||
|
|
||||||
|
tab_title = _("Report {}").format(acc.identifier)
|
||||||
|
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
||||||
|
if not acc.is_ready_for_publish():
|
||||||
|
template = "report/unavailable.html"
|
||||||
|
context = {
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
# Prepare data for map viewer
|
||||||
|
geom_form = SimpleGeomForm(
|
||||||
|
instance=acc
|
||||||
|
)
|
||||||
|
parcels = acc.get_underlying_parcels()
|
||||||
|
|
||||||
|
qrcode_url = request.build_absolute_uri(reverse("compensation:acc:report", args=(id,)))
|
||||||
|
qrcode_img = generate_qr_code(qrcode_url, 10)
|
||||||
|
qrcode_lanis_url = acc.get_LANIS_link()
|
||||||
|
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
||||||
|
|
||||||
|
# Order states by surface
|
||||||
|
before_states = acc.before_states.all().order_by("-surface").select_related("biotope_type__parent")
|
||||||
|
after_states = acc.after_states.all().order_by("-surface").select_related("biotope_type__parent")
|
||||||
|
actions = acc.actions.all().prefetch_related("action_type__parent")
|
||||||
|
|
||||||
|
# Reduce amount of db fetched data to the bare minimum we need in the template (deduction's intervention id and identifier)
|
||||||
|
deductions = acc.deductions.all()\
|
||||||
|
.distinct("intervention")\
|
||||||
|
.select_related("intervention")\
|
||||||
|
.values_list("intervention__id", "intervention__identifier", "intervention__title", named=True)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": acc,
|
||||||
|
"qrcode": {
|
||||||
|
"img": qrcode_img,
|
||||||
|
"url": qrcode_url,
|
||||||
|
},
|
||||||
|
"qrcode_lanis": {
|
||||||
|
"img": qrcode_img_lanis,
|
||||||
|
"url": qrcode_lanis_url,
|
||||||
|
},
|
||||||
|
"has_access": False, # disables action buttons during rendering
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"actions": actions,
|
||||||
|
"deductions": deductions,
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
25
compensation/views/eco_account/resubmission.py
Normal file
25
compensation/views/eco_account/resubmission.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.resubmission import AbstractResubmissionView
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountResubmissionView(AbstractResubmissionView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url_base = "compensation:acc:detail"
|
||||||
|
form_action_url_base = "compensation:acc:resubmission-create"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
32
compensation/views/eco_account/share.py
Normal file
32
compensation/views/eco_account/share.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.share import AbstractShareByTokenView, AbstractShareFormView
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountShareByTokenView(AbstractShareByTokenView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EcoAccountShareFormView(AbstractShareFormView):
|
||||||
|
model = EcoAccount
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
47
compensation/views/eco_account/state.py
Normal file
47
compensation/views/eco_account/state.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
|
||||||
|
AbstractRemoveCompensationStateView
|
||||||
|
|
||||||
|
|
||||||
|
class NewEcoAccountStateView(AbstractNewCompensationStateView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditEcoAccountStateView(AbstractEditCompensationStateView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEcoAccountStateView(AbstractRemoveCompensationStateView):
|
||||||
|
model = EcoAccount
|
||||||
|
redirect_url = "compensation:acc:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(EcoAccount, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
@ -6,12 +6,11 @@ Created on: 09.08.21
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from compensation.forms.modalForms import NewPaymentForm, RemovePaymentModalForm, EditPaymentModalForm
|
from compensation.forms.modals.payment import NewPaymentForm, RemovePaymentModalForm, EditPaymentModalForm
|
||||||
from compensation.models import Payment
|
from compensation.models import Payment
|
||||||
from intervention.models import Intervention
|
from intervention.models import Intervention
|
||||||
from konova.decorators import default_group_required, shared_access_required
|
from konova.decorators import default_group_required, shared_access_required
|
||||||
|
@ -5,7 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 19.08.21
|
Created on: 19.08.21
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from compensation.filters import EcoAccountTableFilter
|
from compensation.filters.eco_account import EcoAccountTableFilter
|
||||||
|
|
||||||
|
|
||||||
class EmaTableFilter(EcoAccountTableFilter):
|
class EmaTableFilter(EcoAccountTableFilter):
|
||||||
|
@ -10,8 +10,8 @@ from django.db import transaction
|
|||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from compensation.forms.forms import AbstractCompensationForm, CompensationResponsibleFormMixin, \
|
from compensation.forms.mixins import CompensationResponsibleFormMixin, PikCompensationFormMixin
|
||||||
PikCompensationFormMixin
|
from compensation.forms.compensation import AbstractCompensationForm
|
||||||
from ema.models import Ema, EmaDocument
|
from ema.models import Ema, EmaDocument
|
||||||
from intervention.models import Responsibility, Handler
|
from intervention.models import Responsibility, Handler
|
||||||
from konova.forms import SimpleGeomForm
|
from konova.forms import SimpleGeomForm
|
||||||
|
@ -103,7 +103,7 @@ class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin, Pik
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return reverse("ema:share", args=(self.id, self.access_token))
|
return reverse("ema:share-token", args=(self.id, self.access_token))
|
||||||
|
|
||||||
|
|
||||||
class EmaDocument(AbstractDocument):
|
class EmaDocument(AbstractDocument):
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Resubmission' %}" data-form-url="{% url 'ema:resubmission-create' obj.id %}">
|
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Resubmission' %}" data-form-url="{% url 'ema:resubmission-create' obj.id %}">
|
||||||
{% fa5_icon 'bell' %}
|
{% fa5_icon 'bell' %}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'ema:share-create' obj.id %}">
|
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'ema:share-form' obj.id %}">
|
||||||
{% fa5_icon 'share-alt' %}
|
{% fa5_icon 'share-alt' %}
|
||||||
</button>
|
</button>
|
||||||
{% if is_ets_member %}
|
{% if is_ets_member %}
|
||||||
|
@ -49,8 +49,8 @@ class EmaViewTestCase(CompensationViewTestCase):
|
|||||||
self.log_url = reverse("ema:log", args=(self.ema.id,))
|
self.log_url = reverse("ema:log", args=(self.ema.id,))
|
||||||
self.edit_url = reverse("ema:edit", args=(self.ema.id,))
|
self.edit_url = reverse("ema:edit", args=(self.ema.id,))
|
||||||
self.remove_url = reverse("ema:remove", args=(self.ema.id,))
|
self.remove_url = reverse("ema:remove", args=(self.ema.id,))
|
||||||
self.share_url = reverse("ema:share", args=(self.ema.id, self.ema.access_token,))
|
self.share_url = reverse("ema:share-token", args=(self.ema.id, self.ema.access_token,))
|
||||||
self.share_create_url = reverse("ema:share-create", args=(self.ema.id,))
|
self.share_create_url = reverse("ema:share-form", args=(self.ema.id,))
|
||||||
self.record_url = reverse("ema:record", args=(self.ema.id,))
|
self.record_url = reverse("ema:record", args=(self.ema.id,))
|
||||||
self.report_url = reverse("ema:report", args=(self.ema.id,))
|
self.report_url = reverse("ema:report", args=(self.ema.id,))
|
||||||
self.new_doc_url = reverse("ema:new-doc", args=(self.ema.id,))
|
self.new_doc_url = reverse("ema:new-doc", args=(self.ema.id,))
|
||||||
|
48
ema/urls.py
48
ema/urls.py
@ -6,7 +6,17 @@ Created on: 19.08.21
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from ema.views import *
|
|
||||||
|
from ema.views.action import NewEmaActionView, EditEmaActionView, RemoveEmaActionView
|
||||||
|
from ema.views.deadline import NewEmaDeadlineView, EditEmaDeadlineView, RemoveEmaDeadlineView
|
||||||
|
from ema.views.document import NewEmaDocumentView, EditEmaDocumentView, RemoveEmaDocumentView, GetEmaDocumentView
|
||||||
|
from ema.views.ema import index_view, new_view, new_id_view, detail_view, edit_view, remove_view
|
||||||
|
from ema.views.log import EmaLogView
|
||||||
|
from ema.views.record import EmaRecordView
|
||||||
|
from ema.views.report import report_view
|
||||||
|
from ema.views.resubmission import EmaResubmissionView
|
||||||
|
from ema.views.share import EmaShareFormView, EmaShareByTokenView
|
||||||
|
from ema.views.state import NewEmaStateView, EditEmaStateView, RemoveEmaStateView
|
||||||
|
|
||||||
app_name = "ema"
|
app_name = "ema"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -14,32 +24,32 @@ urlpatterns = [
|
|||||||
path("new/", new_view, name="new"),
|
path("new/", new_view, name="new"),
|
||||||
path("new/id", new_id_view, name="new-id"),
|
path("new/id", new_id_view, name="new-id"),
|
||||||
path("<id>", detail_view, name="detail"),
|
path("<id>", detail_view, name="detail"),
|
||||||
path('<id>/log', log_view, name='log'),
|
path('<id>/log', EmaLogView.as_view(), name='log'),
|
||||||
path('<id>/edit', edit_view, name='edit'),
|
path('<id>/edit', edit_view, name='edit'),
|
||||||
path('<id>/remove', remove_view, name='remove'),
|
path('<id>/remove', remove_view, name='remove'),
|
||||||
path('<id>/record', record_view, name='record'),
|
path('<id>/record', EmaRecordView.as_view(), name='record'),
|
||||||
path('<id>/report', report_view, name='report'),
|
path('<id>/report', report_view, name='report'),
|
||||||
path('<id>/resub', create_resubmission_view, name='resubmission-create'),
|
path('<id>/resub', EmaResubmissionView.as_view(), name='resubmission-create'),
|
||||||
|
|
||||||
path('<id>/state/new', state_new_view, name='new-state'),
|
path('<id>/state/new', NewEmaStateView.as_view(), name='new-state'),
|
||||||
path('<id>/state/<state_id>/remove', state_remove_view, name='state-remove'),
|
path('<id>/state/<state_id>/remove', RemoveEmaStateView.as_view(), name='state-remove'),
|
||||||
path('<id>/state/<state_id>/edit', state_edit_view, name='state-edit'),
|
path('<id>/state/<state_id>/edit', EditEmaStateView.as_view(), name='state-edit'),
|
||||||
|
|
||||||
path('<id>/action/new', action_new_view, name='new-action'),
|
path('<id>/action/new', NewEmaActionView.as_view(), name='new-action'),
|
||||||
path('<id>/action/<action_id>/edit', action_edit_view, name='action-edit'),
|
path('<id>/action/<action_id>/edit', EditEmaActionView.as_view(), name='action-edit'),
|
||||||
path('<id>/action/<action_id>/remove', action_remove_view, name='action-remove'),
|
path('<id>/action/<action_id>/remove', RemoveEmaActionView.as_view(), name='action-remove'),
|
||||||
|
|
||||||
path('<id>/deadline/new', deadline_new_view, name="new-deadline"),
|
path('<id>/deadline/new', NewEmaDeadlineView.as_view(), name="new-deadline"),
|
||||||
path('<id>/deadline/<deadline_id>/edit', deadline_edit_view, name='deadline-edit'),
|
path('<id>/deadline/<deadline_id>/edit', EditEmaDeadlineView.as_view(), name='deadline-edit'),
|
||||||
path('<id>/deadline/<deadline_id>/remove', deadline_remove_view, name='deadline-remove'),
|
path('<id>/deadline/<deadline_id>/remove', RemoveEmaDeadlineView.as_view(), name='deadline-remove'),
|
||||||
|
|
||||||
path('<id>/share/<token>', share_view, name='share'),
|
path('<id>/share/<token>', EmaShareByTokenView.as_view(), name='share-token'),
|
||||||
path('<id>/share', create_share_view, name='share-create'),
|
path('<id>/share', EmaShareFormView.as_view(), name='share-form'),
|
||||||
|
|
||||||
# Documents
|
# Documents
|
||||||
path('<id>/document/new/', document_new_view, name='new-doc'),
|
path('<id>/document/new/', NewEmaDocumentView.as_view(), name='new-doc'),
|
||||||
path('<id>/document/<doc_id>', get_document_view, name='get-doc'),
|
path('<id>/document/<doc_id>', GetEmaDocumentView.as_view(), name='get-doc'),
|
||||||
path('<id>/document/<doc_id>/edit/', edit_document_view, name='edit-doc'),
|
path('<id>/document/<doc_id>/edit/', EditEmaDocumentView.as_view(), name='edit-doc'),
|
||||||
path('<id>/document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
|
path('<id>/document/<doc_id>/remove/', RemoveEmaDocumentView.as_view(), name='remove-doc'),
|
||||||
|
|
||||||
]
|
]
|
737
ema/views.py
737
ema/views.py
@ -1,737 +0,0 @@
|
|||||||
from django.contrib import messages
|
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.db.models import Sum
|
|
||||||
from django.http import HttpRequest, JsonResponse
|
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \
|
|
||||||
RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, EditCompensationStateModalForm, \
|
|
||||||
EditCompensationActionModalForm, EditDeadlineModalForm
|
|
||||||
from compensation.models import CompensationAction, CompensationState
|
|
||||||
from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentModalForm
|
|
||||||
from ema.tables import EmaTable
|
|
||||||
from intervention.forms.modalForms import ShareModalForm
|
|
||||||
from konova.contexts import BaseContext
|
|
||||||
from konova.decorators import conservation_office_group_required, shared_access_required
|
|
||||||
from ema.models import Ema, EmaDocument
|
|
||||||
from konova.forms.modals import RemoveModalForm, RecordModalForm, RemoveDeadlineModalForm, \
|
|
||||||
EditDocumentModalForm, ResubmissionModalForm
|
|
||||||
from konova.forms import SimpleGeomForm
|
|
||||||
from konova.models import Deadline
|
|
||||||
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
|
||||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
|
||||||
from konova.utils.documents import get_document, remove_document
|
|
||||||
from konova.utils.generators import generate_qr_code
|
|
||||||
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \
|
|
||||||
DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, \
|
|
||||||
COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, \
|
|
||||||
COMPENSATION_ACTION_EDITED, DEADLINE_EDITED, RECORDED_BLOCKS_EDIT
|
|
||||||
from konova.utils.user_checks import in_group
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def index_view(request: HttpRequest):
|
|
||||||
""" Renders the index view for EMAs
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "generic_index.html"
|
|
||||||
emas = Ema.objects.filter(
|
|
||||||
deleted=None,
|
|
||||||
).order_by(
|
|
||||||
"-modified"
|
|
||||||
)
|
|
||||||
table = EmaTable(
|
|
||||||
request,
|
|
||||||
queryset=emas
|
|
||||||
)
|
|
||||||
context = {
|
|
||||||
"table": table,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("EMAs - Overview"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
def new_view(request: HttpRequest):
|
|
||||||
"""
|
|
||||||
Renders a view for a new eco account creation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "ema/form/view.html"
|
|
||||||
data_form = NewEmaForm(request.POST or None)
|
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
|
||||||
if request.method == "POST":
|
|
||||||
if data_form.is_valid() and geom_form.is_valid():
|
|
||||||
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
|
||||||
ema = data_form.save(request.user, geom_form)
|
|
||||||
if generated_identifier != ema.identifier:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
IDENTIFIER_REPLACED.format(
|
|
||||||
generated_identifier,
|
|
||||||
ema.identifier
|
|
||||||
)
|
|
||||||
)
|
|
||||||
messages.success(request, _("EMA {} added").format(ema.identifier))
|
|
||||||
return redirect("ema:detail", id=ema.id)
|
|
||||||
else:
|
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
|
||||||
else:
|
|
||||||
# For clarification: nothing in this case
|
|
||||||
pass
|
|
||||||
context = {
|
|
||||||
"form": data_form,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("New EMA"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
def new_id_view(request: HttpRequest):
|
|
||||||
""" JSON endpoint
|
|
||||||
|
|
||||||
Provides fetching of free identifiers for e.g. AJAX calls
|
|
||||||
|
|
||||||
"""
|
|
||||||
tmp = Ema()
|
|
||||||
identifier = tmp.generate_new_identifier()
|
|
||||||
while Ema.objects.filter(identifier=identifier).exists():
|
|
||||||
identifier = tmp.generate_new_identifier()
|
|
||||||
return JsonResponse(
|
|
||||||
data={
|
|
||||||
"gen_data": identifier
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def detail_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders the detail view of an EMA
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "ema/detail/view.html"
|
|
||||||
ema = get_object_or_404(Ema, id=id, deleted=None)
|
|
||||||
|
|
||||||
geom_form = SimpleGeomForm(instance=ema)
|
|
||||||
parcels = ema.get_underlying_parcels()
|
|
||||||
_user = request.user
|
|
||||||
is_data_shared = ema.is_shared_with(_user)
|
|
||||||
|
|
||||||
# Order states according to surface
|
|
||||||
before_states = ema.before_states.all().order_by("-surface")
|
|
||||||
after_states = ema.after_states.all().order_by("-surface")
|
|
||||||
|
|
||||||
# Precalculate logical errors between before- and after-states
|
|
||||||
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
|
||||||
sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
|
||||||
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
|
||||||
diff_states = abs(sum_before_states - sum_after_states)
|
|
||||||
|
|
||||||
ema.set_status_messages(request)
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"obj": ema,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
"parcels": parcels,
|
|
||||||
"has_access": is_data_shared,
|
|
||||||
"before_states": before_states,
|
|
||||||
"after_states": after_states,
|
|
||||||
"sum_before_states": sum_before_states,
|
|
||||||
"sum_after_states": sum_after_states,
|
|
||||||
"diff_states": diff_states,
|
|
||||||
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
|
||||||
"is_zb_member": in_group(_user, ZB_GROUP),
|
|
||||||
"is_ets_member": in_group(_user, ETS_GROUP),
|
|
||||||
"LANIS_LINK": ema.get_LANIS_link(),
|
|
||||||
TAB_TITLE_IDENTIFIER: f"{ema.identifier} - {ema.title}",
|
|
||||||
"has_finished_deadlines": ema.get_finished_deadlines().exists(),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def log_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a log view using modal
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
template = "modal/modal_generic.html"
|
|
||||||
body_template = "log.html"
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"modal_body_template": body_template,
|
|
||||||
"log": ema.log.all(),
|
|
||||||
"modal_title": _("Log"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def edit_view(request: HttpRequest, id: str):
|
|
||||||
"""
|
|
||||||
Renders a view for editing compensations
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "compensation/form/view.html"
|
|
||||||
# Get object from db
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
if ema.is_recorded:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
RECORDED_BLOCKS_EDIT
|
|
||||||
)
|
|
||||||
return redirect("ema:detail", id=id)
|
|
||||||
|
|
||||||
# Create forms, initialize with values from db/from POST request
|
|
||||||
data_form = EditEmaForm(request.POST or None, instance=ema)
|
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=ema)
|
|
||||||
if request.method == "POST":
|
|
||||||
if data_form.is_valid() and geom_form.is_valid():
|
|
||||||
# The data form takes the geom form for processing, as well as the performing user
|
|
||||||
ema = data_form.save(request.user, geom_form)
|
|
||||||
messages.success(request, _("EMA {} edited").format(ema.identifier))
|
|
||||||
return redirect("ema:detail", id=ema.id)
|
|
||||||
else:
|
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
|
||||||
else:
|
|
||||||
# For clarification: nothing in this case
|
|
||||||
pass
|
|
||||||
context = {
|
|
||||||
"form": data_form,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("Edit {}").format(ema.identifier),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def remove_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a modal view for removing the EMA
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
form = RemoveModalForm(request.POST or None, instance=ema, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request=request,
|
|
||||||
msg_success=_("EMA removed"),
|
|
||||||
redirect_url=reverse("ema:index"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def record_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a modal view for recording the EMA
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
msg_succ = _("{} unrecorded") if ema.recorded else _("{} recorded")
|
|
||||||
form = RecordModalForm(request.POST or None, instance=ema, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request=request,
|
|
||||||
msg_success=msg_succ.format("EMA"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def state_new_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for adding new states for an EMA
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA's id to which the new state will be related
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
form = NewStateModalForm(request.POST or None, instance=ema, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_STATE_ADDED,
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def action_new_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for adding new actions for an EMA
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA's id to which the new state will be related
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
form = NewActionModalForm(request.POST or None, instance=ema, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_ACTION_ADDED,
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def action_edit_view(request: HttpRequest, id: str, action_id: str):
|
|
||||||
""" Renders a form for editing an actions for an EMA
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA's id
|
|
||||||
action_id (str): The action id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
action = get_object_or_404(CompensationAction, id=action_id)
|
|
||||||
form = EditCompensationActionModalForm(request.POST or None, instance=ema, action=action, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_ACTION_EDITED,
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def deadline_new_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for adding new states for an EMA
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA's id to which the new state will be related
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
form = NewDeadlineModalForm(request.POST or None, instance=ema, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEADLINE_ADDED,
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def document_new_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for uploading new documents
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA's id to which the new document will be related
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
form = NewEmaDocumentModalForm(request.POST or None, request.FILES or None, instance=ema, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DOCUMENT_ADDED,
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def get_document_view(request: HttpRequest, id: str, doc_id: str):
|
|
||||||
""" Returns the document as downloadable file
|
|
||||||
|
|
||||||
Wraps the generic document fetcher function from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
doc = get_object_or_404(EmaDocument, id=doc_id)
|
|
||||||
return get_document(doc)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def edit_document_view(request: HttpRequest, id: str, doc_id: str):
|
|
||||||
""" Removes the document from the database and file system
|
|
||||||
|
|
||||||
Wraps the generic functionality from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
doc = get_object_or_404(EmaDocument, id=doc_id)
|
|
||||||
form = EditDocumentModalForm(request.POST or None, request.FILES or None, instance=ema, document=doc, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
DOCUMENT_EDITED,
|
|
||||||
reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def remove_document_view(request: HttpRequest, id:str, doc_id: str):
|
|
||||||
""" Removes the document from the database and file system
|
|
||||||
|
|
||||||
Wraps the generic functionality from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The EMA id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
doc = get_object_or_404(EmaDocument, id=doc_id)
|
|
||||||
return remove_document(
|
|
||||||
request,
|
|
||||||
doc
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def state_remove_view(request: HttpRequest, id: str, state_id: str):
|
|
||||||
""" Renders a form for removing an EMA state
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The ema id
|
|
||||||
state_id (str): The state's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
state = get_object_or_404(CompensationState, id=state_id)
|
|
||||||
form = RemoveCompensationStateModalForm(request.POST or None, instance=ema, state=state, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_STATE_REMOVED,
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def state_edit_view(request: HttpRequest, id: str, state_id: str):
|
|
||||||
""" Renders a form for editing an EMA state
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The ema id
|
|
||||||
state_id (str): The state's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
state = get_object_or_404(CompensationState, id=state_id)
|
|
||||||
form = EditCompensationStateModalForm(request.POST or None, instance=ema, state=state, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_STATE_EDITED,
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def action_remove_view(request: HttpRequest, id: str, action_id: str):
|
|
||||||
""" Renders a form for removing an EMA action
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The ema id
|
|
||||||
id (str): The action's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
action = get_object_or_404(CompensationAction, id=action_id)
|
|
||||||
form = RemoveCompensationActionModalForm(request.POST or None, instance=ema, action=action, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=COMPENSATION_ACTION_REMOVED,
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def report_view(request:HttpRequest, id: str):
|
|
||||||
""" Renders the public report view
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The id of the intervention
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Reuse the compensation report template since EMAs are structurally identical
|
|
||||||
template = "ema/report/report.html"
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
|
|
||||||
tab_title = _("Report {}").format(ema.identifier)
|
|
||||||
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
|
||||||
if not ema.is_ready_for_publish():
|
|
||||||
template = "report/unavailable.html"
|
|
||||||
context = {
|
|
||||||
TAB_TITLE_IDENTIFIER: tab_title,
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
# Prepare data for map viewer
|
|
||||||
geom_form = SimpleGeomForm(
|
|
||||||
instance=ema,
|
|
||||||
)
|
|
||||||
parcels = ema.get_underlying_parcels()
|
|
||||||
|
|
||||||
qrcode_url = request.build_absolute_uri(reverse("ema:report", args=(id,)))
|
|
||||||
qrcode_img = generate_qr_code(qrcode_url, 10)
|
|
||||||
qrcode_lanis_url = ema.get_LANIS_link()
|
|
||||||
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
|
||||||
|
|
||||||
# Order states by surface
|
|
||||||
before_states = ema.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
|
||||||
after_states = ema.after_states.all().order_by("-surface").prefetch_related("biotope_type")
|
|
||||||
actions = ema.actions.all().prefetch_related("action_type")
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"obj": ema,
|
|
||||||
"qrcode": {
|
|
||||||
"img": qrcode_img,
|
|
||||||
"url": qrcode_url
|
|
||||||
},
|
|
||||||
"qrcode_lanis": {
|
|
||||||
"img": qrcode_img_lanis,
|
|
||||||
"url": qrcode_lanis_url
|
|
||||||
},
|
|
||||||
"has_access": False, # disables action buttons during rendering
|
|
||||||
"before_states": before_states,
|
|
||||||
"after_states": after_states,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
"parcels": parcels,
|
|
||||||
"actions": actions,
|
|
||||||
TAB_TITLE_IDENTIFIER: tab_title,
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def share_view(request: HttpRequest, id: str, token: str):
|
|
||||||
""" Performs sharing of an ema
|
|
||||||
|
|
||||||
If token given in url is not valid, the user will be redirected to the dashboard
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): EMA's id
|
|
||||||
token (str): Access token for EMA
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
user = request.user
|
|
||||||
obj = get_object_or_404(Ema, id=id)
|
|
||||||
# Check tokens
|
|
||||||
if obj.access_token == token:
|
|
||||||
# Send different messages in case user has already been added to list of sharing users
|
|
||||||
if obj.is_shared_with(user):
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
_("{} has already been shared with you").format(obj.identifier)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
messages.success(
|
|
||||||
request,
|
|
||||||
_("{} has been shared with you").format(obj.identifier)
|
|
||||||
)
|
|
||||||
obj.share_with_user(user)
|
|
||||||
return redirect("ema:detail", id=id)
|
|
||||||
else:
|
|
||||||
messages.error(
|
|
||||||
request,
|
|
||||||
_("Share link invalid"),
|
|
||||||
extra_tags="danger",
|
|
||||||
)
|
|
||||||
return redirect("home")
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def create_share_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders sharing form for an Ema
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): Ema's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
obj = get_object_or_404(Ema, id=id)
|
|
||||||
form = ShareModalForm(request.POST or None, instance=obj, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=_("Share settings updated")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def deadline_edit_view(request: HttpRequest, id: str, deadline_id: str):
|
|
||||||
""" Renders a form for editing deadlines from a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
deadline_id (str): The deadline's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
deadline = get_object_or_404(Deadline, id=deadline_id)
|
|
||||||
form = EditDeadlineModalForm(request.POST or None, instance=ema, deadline=deadline, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEADLINE_EDITED,
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str):
|
|
||||||
""" Renders a form for removing deadlines from a compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
deadline_id (str): The deadline's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
deadline = get_object_or_404(Deadline, id=deadline_id)
|
|
||||||
form = RemoveDeadlineModalForm(request.POST or None, instance=ema, deadline=deadline, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEADLINE_REMOVED,
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Ema, "id")
|
|
||||||
def create_resubmission_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders resubmission form for an EMA
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): EMA's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
ema = get_object_or_404(Ema, id=id)
|
|
||||||
form = ResubmissionModalForm(request.POST or None, instance=ema, request=request)
|
|
||||||
form.action_url = reverse("ema:resubmission-create", args=(id,))
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=_("Resubmission set"),
|
|
||||||
redirect_url=reverse("ema:detail", args=(id,))
|
|
||||||
)
|
|
7
ema/views/__init__.py
Normal file
7
ema/views/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
47
ema/views/action.py
Normal file
47
ema/views/action.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required
|
||||||
|
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
|
||||||
|
AbstractRemoveCompensationActionView
|
||||||
|
|
||||||
|
|
||||||
|
class NewEmaActionView(AbstractNewCompensationActionView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditEmaActionView(AbstractEditCompensationActionView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEmaActionView(AbstractRemoveCompensationActionView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
47
ema/views/deadline.py
Normal file
47
ema/views/deadline.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required
|
||||||
|
from konova.views.deadline import AbstractNewDeadlineView, AbstractRemoveDeadlineView, AbstractEditDeadlineView
|
||||||
|
|
||||||
|
|
||||||
|
class NewEmaDeadlineView(AbstractNewDeadlineView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditEmaDeadlineView(AbstractEditDeadlineView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEmaDeadlineView(AbstractRemoveDeadlineView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
63
ema/views/document.py
Normal file
63
ema/views/document.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from ema.forms import NewEmaDocumentModalForm
|
||||||
|
from ema.models import Ema, EmaDocument
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required
|
||||||
|
from konova.forms.modals import EditDocumentModalForm
|
||||||
|
from konova.views.document import AbstractEditDocumentView, AbstractRemoveDocumentView, AbstractGetDocumentView, \
|
||||||
|
AbstractNewDocumentView
|
||||||
|
|
||||||
|
|
||||||
|
class NewEmaDocumentView(AbstractNewDocumentView):
|
||||||
|
model = Ema
|
||||||
|
form = NewEmaDocumentModalForm
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class GetEmaDocumentView(AbstractGetDocumentView):
|
||||||
|
model = Ema
|
||||||
|
document_model = EmaDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEmaDocumentView(AbstractRemoveDocumentView):
|
||||||
|
model = Ema
|
||||||
|
document_model = EmaDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditEmaDocumentView(AbstractEditDocumentView):
|
||||||
|
model = Ema
|
||||||
|
document_model = EmaDocument
|
||||||
|
form = EditDocumentModalForm
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
237
ema/views/ema.py
Normal file
237
ema/views/ema.py
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.db.models import Sum
|
||||||
|
from django.http import HttpRequest, JsonResponse
|
||||||
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from ema.forms import NewEmaForm, EditEmaForm
|
||||||
|
from ema.models import Ema
|
||||||
|
from ema.tables import EmaTable
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.forms.modals import RemoveModalForm
|
||||||
|
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.message_templates import RECORDED_BLOCKS_EDIT, IDENTIFIER_REPLACED, FORM_INVALID
|
||||||
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def index_view(request: HttpRequest):
|
||||||
|
""" Renders the index view for EMAs
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "generic_index.html"
|
||||||
|
emas = Ema.objects.filter(
|
||||||
|
deleted=None,
|
||||||
|
).order_by(
|
||||||
|
"-modified"
|
||||||
|
)
|
||||||
|
table = EmaTable(
|
||||||
|
request,
|
||||||
|
queryset=emas
|
||||||
|
)
|
||||||
|
context = {
|
||||||
|
"table": table,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("EMAs - Overview"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@conservation_office_group_required
|
||||||
|
def new_view(request: HttpRequest):
|
||||||
|
"""
|
||||||
|
Renders a view for a new eco account creation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "ema/form/view.html"
|
||||||
|
data_form = NewEmaForm(request.POST or None)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
|
if request.method == "POST":
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
||||||
|
ema = data_form.save(request.user, geom_form)
|
||||||
|
if generated_identifier != ema.identifier:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
IDENTIFIER_REPLACED.format(
|
||||||
|
generated_identifier,
|
||||||
|
ema.identifier
|
||||||
|
)
|
||||||
|
)
|
||||||
|
messages.success(request, _("EMA {} added").format(ema.identifier))
|
||||||
|
return redirect("ema:detail", id=ema.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||||
|
else:
|
||||||
|
# For clarification: nothing in this case
|
||||||
|
pass
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("New EMA"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@conservation_office_group_required
|
||||||
|
def new_id_view(request: HttpRequest):
|
||||||
|
""" JSON endpoint
|
||||||
|
|
||||||
|
Provides fetching of free identifiers for e.g. AJAX calls
|
||||||
|
|
||||||
|
"""
|
||||||
|
tmp = Ema()
|
||||||
|
identifier = tmp.generate_new_identifier()
|
||||||
|
while Ema.objects.filter(identifier=identifier).exists():
|
||||||
|
identifier = tmp.generate_new_identifier()
|
||||||
|
return JsonResponse(
|
||||||
|
data={
|
||||||
|
"gen_data": identifier
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def detail_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders the detail view of an EMA
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The EMA id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "ema/detail/view.html"
|
||||||
|
ema = get_object_or_404(Ema, id=id, deleted=None)
|
||||||
|
|
||||||
|
geom_form = SimpleGeomForm(instance=ema)
|
||||||
|
parcels = ema.get_underlying_parcels()
|
||||||
|
_user = request.user
|
||||||
|
is_data_shared = ema.is_shared_with(_user)
|
||||||
|
|
||||||
|
# Order states according to surface
|
||||||
|
before_states = ema.before_states.all().order_by("-surface")
|
||||||
|
after_states = ema.after_states.all().order_by("-surface")
|
||||||
|
|
||||||
|
# Precalculate logical errors between before- and after-states
|
||||||
|
# Sum() returns None in case of no states, so we catch that and replace it with 0 for easier handling
|
||||||
|
sum_before_states = before_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
|
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
|
||||||
|
diff_states = abs(sum_before_states - sum_after_states)
|
||||||
|
|
||||||
|
ema.set_status_messages(request)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": ema,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"has_access": is_data_shared,
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"sum_before_states": sum_before_states,
|
||||||
|
"sum_after_states": sum_after_states,
|
||||||
|
"diff_states": diff_states,
|
||||||
|
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
||||||
|
"is_zb_member": in_group(_user, ZB_GROUP),
|
||||||
|
"is_ets_member": in_group(_user, ETS_GROUP),
|
||||||
|
"LANIS_LINK": ema.get_LANIS_link(),
|
||||||
|
TAB_TITLE_IDENTIFIER: f"{ema.identifier} - {ema.title}",
|
||||||
|
"has_finished_deadlines": ema.get_finished_deadlines().exists(),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@conservation_office_group_required
|
||||||
|
@shared_access_required(Ema, "id")
|
||||||
|
def edit_view(request: HttpRequest, id: str):
|
||||||
|
"""
|
||||||
|
Renders a view for editing compensations
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "compensation/form/view.html"
|
||||||
|
# Get object from db
|
||||||
|
ema = get_object_or_404(Ema, id=id)
|
||||||
|
if ema.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("ema:detail", id=id)
|
||||||
|
|
||||||
|
# Create forms, initialize with values from db/from POST request
|
||||||
|
data_form = EditEmaForm(request.POST or None, instance=ema)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=ema)
|
||||||
|
if request.method == "POST":
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
# The data form takes the geom form for processing, as well as the performing user
|
||||||
|
ema = data_form.save(request.user, geom_form)
|
||||||
|
messages.success(request, _("EMA {} edited").format(ema.identifier))
|
||||||
|
return redirect("ema:detail", id=ema.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||||
|
else:
|
||||||
|
# For clarification: nothing in this case
|
||||||
|
pass
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(ema.identifier),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@conservation_office_group_required
|
||||||
|
@shared_access_required(Ema, "id")
|
||||||
|
def remove_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders a modal view for removing the EMA
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The EMA's id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
ema = get_object_or_404(Ema, id=id)
|
||||||
|
form = RemoveModalForm(request.POST or None, instance=ema, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request=request,
|
||||||
|
msg_success=_("EMA removed"),
|
||||||
|
redirect_url=reverse("ema:index"),
|
||||||
|
)
|
||||||
|
|
23
ema/views/log.py
Normal file
23
ema/views/log.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required
|
||||||
|
from konova.views.log import AbstractLogView
|
||||||
|
|
||||||
|
|
||||||
|
class EmaLogView(AbstractLogView):
|
||||||
|
model = Ema
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
23
ema/views/record.py
Normal file
23
ema/views/record.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required
|
||||||
|
from konova.views.record import AbstractRecordView
|
||||||
|
|
||||||
|
|
||||||
|
class EmaRecordView(AbstractRecordView):
|
||||||
|
model = Ema
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
79
ema/views/report.py
Normal file
79
ema/views/report.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from ema.models import Ema
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.generators import generate_qr_code
|
||||||
|
|
||||||
|
|
||||||
|
def report_view(request:HttpRequest, id: str):
|
||||||
|
""" Renders the public report view
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The id of the intervention
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Reuse the compensation report template since EMAs are structurally identical
|
||||||
|
template = "ema/report/report.html"
|
||||||
|
ema = get_object_or_404(Ema, id=id)
|
||||||
|
|
||||||
|
tab_title = _("Report {}").format(ema.identifier)
|
||||||
|
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
||||||
|
if not ema.is_ready_for_publish():
|
||||||
|
template = "report/unavailable.html"
|
||||||
|
context = {
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
# Prepare data for map viewer
|
||||||
|
geom_form = SimpleGeomForm(
|
||||||
|
instance=ema,
|
||||||
|
)
|
||||||
|
parcels = ema.get_underlying_parcels()
|
||||||
|
|
||||||
|
qrcode_url = request.build_absolute_uri(reverse("ema:report", args=(id,)))
|
||||||
|
qrcode_img = generate_qr_code(qrcode_url, 10)
|
||||||
|
qrcode_lanis_url = ema.get_LANIS_link()
|
||||||
|
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
||||||
|
|
||||||
|
# Order states by surface
|
||||||
|
before_states = ema.before_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||||
|
after_states = ema.after_states.all().order_by("-surface").prefetch_related("biotope_type")
|
||||||
|
actions = ema.actions.all().prefetch_related("action_type")
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": ema,
|
||||||
|
"qrcode": {
|
||||||
|
"img": qrcode_img,
|
||||||
|
"url": qrcode_url
|
||||||
|
},
|
||||||
|
"qrcode_lanis": {
|
||||||
|
"img": qrcode_img_lanis,
|
||||||
|
"url": qrcode_lanis_url
|
||||||
|
},
|
||||||
|
"has_access": False, # disables action buttons during rendering
|
||||||
|
"before_states": before_states,
|
||||||
|
"after_states": after_states,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"parcels": parcels,
|
||||||
|
"actions": actions,
|
||||||
|
TAB_TITLE_IDENTIFIER: tab_title,
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
25
ema/views/resubmission.py
Normal file
25
ema/views/resubmission.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from ema.models import Ema
|
||||||
|
from konova.decorators import shared_access_required, conservation_office_group_required
|
||||||
|
from konova.views.resubmission import AbstractResubmissionView
|
||||||
|
|
||||||
|
|
||||||
|
class EmaResubmissionView(AbstractResubmissionView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url_base = "ema:detail"
|
||||||
|
form_action_url_base = "ema:resubmission-create"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
32
ema/views/share.py
Normal file
32
ema/views/share.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from ema.models import Ema
|
||||||
|
from konova.decorators import conservation_office_group_required, shared_access_required
|
||||||
|
from konova.views.share import AbstractShareByTokenView, AbstractShareFormView
|
||||||
|
|
||||||
|
|
||||||
|
class EmaShareByTokenView(AbstractShareByTokenView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EmaShareFormView(AbstractShareFormView):
|
||||||
|
model = Ema
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
47
ema/views/state.py
Normal file
47
ema/views/state.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from ema.models import Ema
|
||||||
|
from konova.decorators import conservation_office_group_required, shared_access_required
|
||||||
|
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
|
||||||
|
AbstractRemoveCompensationStateView
|
||||||
|
|
||||||
|
|
||||||
|
class NewEmaStateView(AbstractNewCompensationStateView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditEmaStateView(AbstractEditCompensationStateView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEmaStateView(AbstractRemoveCompensationStateView):
|
||||||
|
model = Ema
|
||||||
|
redirect_url = "ema:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(conservation_office_group_required)
|
||||||
|
@method_decorator(shared_access_required(Ema, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
7
intervention/autocomplete/__init__.py
Normal file
7
intervention/autocomplete/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
36
intervention/autocomplete/intervention.py
Normal file
36
intervention/autocomplete/intervention.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from dal_select2.views import Select2QuerySetView
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from intervention.models import Intervention
|
||||||
|
|
||||||
|
|
||||||
|
class InterventionAutocomplete(Select2QuerySetView):
|
||||||
|
""" Autocomplete for intervention entries
|
||||||
|
|
||||||
|
Only returns entries that are accessible for the requesting user
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_queryset(self):
|
||||||
|
user = self.request.user
|
||||||
|
if user.is_anonymous:
|
||||||
|
return Intervention.objects.none()
|
||||||
|
qs = Intervention.objects.filter(
|
||||||
|
Q(deleted=None) &
|
||||||
|
Q(users__in=[user]) |
|
||||||
|
Q(teams__in=user.teams.all())
|
||||||
|
).order_by(
|
||||||
|
"identifier"
|
||||||
|
).distinct()
|
||||||
|
if self.q:
|
||||||
|
qs = qs.filter(
|
||||||
|
Q(identifier__icontains=self.q) |
|
||||||
|
Q(title__icontains=self.q)
|
||||||
|
).distinct()
|
||||||
|
return qs
|
@ -60,7 +60,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_PROCESS_TYPE_ID],
|
code_lists__in=[CODELIST_PROCESS_TYPE_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codes-process-type-autocomplete",
|
url="codelist:process-type-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_LAW_ID],
|
code_lists__in=[CODELIST_LAW_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
url="codes-law-autocomplete",
|
url="codelist:law-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
|
code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codes-registration-office-autocomplete",
|
url="codelist:registration-office-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
||||||
@ -109,7 +109,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codes-conservation-office-autocomplete",
|
url="codelist:conservation-office-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ class NewInterventionForm(BaseForm):
|
|||||||
code_lists__in=[CODELIST_HANDLER_ID],
|
code_lists__in=[CODELIST_HANDLER_ID],
|
||||||
),
|
),
|
||||||
widget=autocomplete.ModelSelect2(
|
widget=autocomplete.ModelSelect2(
|
||||||
url="codes-handler-autocomplete",
|
url="codelist:handler-autocomplete",
|
||||||
attrs={
|
attrs={
|
||||||
"data-placeholder": _("Click for selection"),
|
"data-placeholder": _("Click for selection"),
|
||||||
}
|
}
|
@ -1,574 +0,0 @@
|
|||||||
"""
|
|
||||||
Author: Michel Peltriaux
|
|
||||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
|
||||||
Contact: michel.peltriaux@sgdnord.rlp.de
|
|
||||||
Created on: 27.09.21
|
|
||||||
|
|
||||||
"""
|
|
||||||
from dal import autocomplete
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
|
|
||||||
from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED, DEDUCTION_REMOVED, DEDUCTION_EDITED, \
|
|
||||||
REVOCATION_EDITED, ENTRY_REMOVE_MISSING_PERMISSION
|
|
||||||
from user.models import User, Team
|
|
||||||
from user.models import UserActionLogEntry
|
|
||||||
from django.db import transaction
|
|
||||||
from django import forms
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from compensation.models import EcoAccount, EcoAccountDeduction
|
|
||||||
from intervention.inputs import TextToClipboardInput
|
|
||||||
from intervention.models import Intervention, InterventionDocument, RevocationDocument
|
|
||||||
from konova.forms.modals import BaseModalForm
|
|
||||||
from konova.forms.modals import NewDocumentModalForm, RemoveModalForm
|
|
||||||
from konova.utils.general import format_german_float
|
|
||||||
from konova.utils.user_checks import is_default_group_only
|
|
||||||
|
|
||||||
|
|
||||||
class ShareModalForm(BaseModalForm):
|
|
||||||
url = forms.CharField(
|
|
||||||
label=_("Share link"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Send this link to users who you want to have writing access on the data"),
|
|
||||||
required=False,
|
|
||||||
widget=TextToClipboardInput(
|
|
||||||
attrs={
|
|
||||||
"readonly": True,
|
|
||||||
"class": "form-control",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
teams = forms.ModelMultipleChoiceField(
|
|
||||||
label=_("Add team to share with"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Multiple selection possible - You can only select teams which do not already have access."),
|
|
||||||
required=False,
|
|
||||||
queryset=Team.objects.all(),
|
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
|
||||||
url="share-team-autocomplete",
|
|
||||||
attrs={
|
|
||||||
"data-placeholder": _("Click for selection"),
|
|
||||||
"data-minimum-input-length": 3,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
users = forms.ModelMultipleChoiceField(
|
|
||||||
label=_("Add user to share with"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Multiple selection possible - You can only select users which do not already have access. Enter the full username."),
|
|
||||||
required=False,
|
|
||||||
queryset=User.objects.all(),
|
|
||||||
widget=autocomplete.ModelSelect2Multiple(
|
|
||||||
url="share-user-autocomplete",
|
|
||||||
attrs={
|
|
||||||
"data-placeholder": _("Click for selection"),
|
|
||||||
"data-minimum-input-length": 3,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Share")
|
|
||||||
self.form_caption = _("Share settings for {}").format(self.instance.identifier)
|
|
||||||
self.template = "modal/modal_form.html"
|
|
||||||
|
|
||||||
# Make sure an access_token is set
|
|
||||||
if self.instance.access_token is None:
|
|
||||||
self.instance.generate_access_token()
|
|
||||||
|
|
||||||
self._init_fields()
|
|
||||||
|
|
||||||
def _user_team_valid(self):
|
|
||||||
""" Checks whether users and teams have been removed by the user and if the user is allowed to do so or not
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
users = self.cleaned_data.get("users", User.objects.none())
|
|
||||||
teams = self.cleaned_data.get("teams", Team.objects.none())
|
|
||||||
|
|
||||||
_is_valid = True
|
|
||||||
if is_default_group_only(self.user):
|
|
||||||
shared_users = self.instance.shared_users
|
|
||||||
shared_teams = self.instance.shared_teams
|
|
||||||
|
|
||||||
shared_users_are_removed = not set(shared_users).issubset(users)
|
|
||||||
shared_teams_are_removed = not set(shared_teams).issubset(teams)
|
|
||||||
|
|
||||||
if shared_users_are_removed:
|
|
||||||
self.add_error(
|
|
||||||
"users",
|
|
||||||
ENTRY_REMOVE_MISSING_PERMISSION
|
|
||||||
)
|
|
||||||
_is_valid = False
|
|
||||||
if shared_teams_are_removed:
|
|
||||||
self.add_error(
|
|
||||||
"teams",
|
|
||||||
ENTRY_REMOVE_MISSING_PERMISSION
|
|
||||||
)
|
|
||||||
_is_valid = False
|
|
||||||
return _is_valid
|
|
||||||
|
|
||||||
def is_valid(self):
|
|
||||||
""" Extended validity check
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
super_valid = super().is_valid()
|
|
||||||
user_team_valid = self._user_team_valid()
|
|
||||||
_is_valid = super_valid and user_team_valid
|
|
||||||
return _is_valid
|
|
||||||
|
|
||||||
def _init_fields(self):
|
|
||||||
""" Wraps initializing of fields
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Initialize share_link field
|
|
||||||
share_link = self.instance.get_share_link()
|
|
||||||
self.share_link = self.request.build_absolute_uri(share_link)
|
|
||||||
self.initialize_form_field(
|
|
||||||
"url",
|
|
||||||
self.share_link
|
|
||||||
)
|
|
||||||
|
|
||||||
form_data = {
|
|
||||||
"teams": self.instance.teams.all(),
|
|
||||||
"users": self.instance.users.all(),
|
|
||||||
}
|
|
||||||
self.load_initial_data(form_data)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
self.instance.update_shared_access(self)
|
|
||||||
|
|
||||||
|
|
||||||
class NewRevocationModalForm(BaseModalForm):
|
|
||||||
date = forms.DateField(
|
|
||||||
label=_("Date"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
help_text=_("Date of revocation"),
|
|
||||||
widget=forms.DateInput(
|
|
||||||
attrs={
|
|
||||||
"type": "date",
|
|
||||||
"data-provide": "datepicker",
|
|
||||||
"class": "form-control",
|
|
||||||
},
|
|
||||||
format="%d.%m.%Y"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
file = forms.FileField(
|
|
||||||
label=_("Document"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
required=False,
|
|
||||||
help_text=_("Must be smaller than 15 Mb"),
|
|
||||||
widget=forms.FileInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control-file"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
comment = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
max_length=200,
|
|
||||||
label=_("Comment"),
|
|
||||||
label_suffix=_(""),
|
|
||||||
help_text=_("Additional comment, maximum {} letters").format(200),
|
|
||||||
widget=forms.Textarea(
|
|
||||||
attrs={
|
|
||||||
"cols": 30,
|
|
||||||
"rows": 5,
|
|
||||||
"class": "form-control",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
document_model = RevocationDocument
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Add revocation")
|
|
||||||
self.form_caption = ""
|
|
||||||
self.form_attrs = {
|
|
||||||
"enctype": "multipart/form-data", # important for file upload
|
|
||||||
}
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
revocation = self.instance.add_revocation(self)
|
|
||||||
self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_ADDED)
|
|
||||||
return revocation
|
|
||||||
|
|
||||||
|
|
||||||
class EditRevocationModalForm(NewRevocationModalForm):
|
|
||||||
revocation = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.revocation = kwargs.pop("revocation", None)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Edit revocation")
|
|
||||||
try:
|
|
||||||
doc = self.revocation.document.file
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
doc = None
|
|
||||||
form_data = {
|
|
||||||
"date": str(self.revocation.date),
|
|
||||||
"file": doc,
|
|
||||||
"comment": self.revocation.comment,
|
|
||||||
}
|
|
||||||
self.load_initial_data(form_data)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
revocation = self.instance.edit_revocation(self)
|
|
||||||
self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_EDITED)
|
|
||||||
return revocation
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveRevocationModalForm(RemoveModalForm):
|
|
||||||
""" Removing modal form for Revocation
|
|
||||||
|
|
||||||
Can be used for anything, where removing shall be confirmed by the user a second time.
|
|
||||||
|
|
||||||
"""
|
|
||||||
revocation = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
revocation = kwargs.pop("revocation", None)
|
|
||||||
self.revocation = revocation
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
self.instance.remove_revocation(self)
|
|
||||||
|
|
||||||
|
|
||||||
class CheckModalForm(BaseModalForm):
|
|
||||||
""" The modal form for running a check on interventions and their compensations
|
|
||||||
|
|
||||||
"""
|
|
||||||
checked_intervention = forms.BooleanField(
|
|
||||||
label=_("Checked intervention data"),
|
|
||||||
label_suffix="",
|
|
||||||
widget=forms.CheckboxInput(),
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
checked_comps = forms.BooleanField(
|
|
||||||
label=_("Checked compensations data and payments"),
|
|
||||||
label_suffix="",
|
|
||||||
widget=forms.CheckboxInput(),
|
|
||||||
required=True
|
|
||||||
)
|
|
||||||
valid = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Run check")
|
|
||||||
self.form_caption = _("I, {} {}, confirm that all necessary control steps have been performed by myself.").format(self.user.first_name, self.user.last_name)
|
|
||||||
self.valid = False
|
|
||||||
|
|
||||||
def _are_deductions_valid(self):
|
|
||||||
""" Performs validity checks on deductions and their eco-account
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
deductions = self.instance.deductions.all()
|
|
||||||
for deduction in deductions:
|
|
||||||
checker = deduction.account.quality_check()
|
|
||||||
for msg in checker.messages:
|
|
||||||
self.add_error(
|
|
||||||
"checked_comps",
|
|
||||||
f"{deduction.account.identifier}: {msg}"
|
|
||||||
)
|
|
||||||
return checker.valid
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _are_comps_valid(self):
|
|
||||||
""" Performs validity checks on all types of compensations
|
|
||||||
|
|
||||||
Types of compensations are
|
|
||||||
* regular Compensations
|
|
||||||
* deductions from EcoAccounts
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
comps = self.instance.compensations.filter(
|
|
||||||
deleted=None,
|
|
||||||
)
|
|
||||||
comps_valid = True
|
|
||||||
for comp in comps:
|
|
||||||
checker = comp.quality_check()
|
|
||||||
for msg in checker.messages:
|
|
||||||
self.add_error(
|
|
||||||
"checked_comps",
|
|
||||||
f"{comp.identifier}: {msg}"
|
|
||||||
)
|
|
||||||
comps_valid = checker.valid
|
|
||||||
deductions_valid = self._are_deductions_valid()
|
|
||||||
return deductions_valid and comps_valid
|
|
||||||
|
|
||||||
def is_valid(self) -> bool:
|
|
||||||
""" Perform a validity check based on quality_check() logic
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
result (bool)
|
|
||||||
"""
|
|
||||||
super_valid = super().is_valid()
|
|
||||||
# Perform check
|
|
||||||
checker = self.instance.quality_check()
|
|
||||||
for msg in checker.messages:
|
|
||||||
self.add_error(
|
|
||||||
"checked_intervention",
|
|
||||||
msg
|
|
||||||
)
|
|
||||||
all_comps_valid = self._are_comps_valid()
|
|
||||||
intervention_valid = checker.valid
|
|
||||||
|
|
||||||
return super_valid and intervention_valid and all_comps_valid
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
""" Saving logic
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
with transaction.atomic():
|
|
||||||
self.instance.set_checked(self.user)
|
|
||||||
|
|
||||||
|
|
||||||
class NewDeductionModalForm(BaseModalForm):
|
|
||||||
""" Form for creating new deduction
|
|
||||||
|
|
||||||
Can be used for Intervention view as well as for EcoAccount views.
|
|
||||||
|
|
||||||
Parameter 'instance' can be an intervention, as well as an ecoAccount.
|
|
||||||
An instance check handles both workflows properly.
|
|
||||||
|
|
||||||
"""
|
|
||||||
account = forms.ModelChoiceField(
|
|
||||||
label=_("Eco-account"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Only recorded accounts can be selected for deductions"),
|
|
||||||
queryset=EcoAccount.objects.filter(deleted=None),
|
|
||||||
widget=autocomplete.ModelSelect2(
|
|
||||||
url="accounts-autocomplete",
|
|
||||||
attrs={
|
|
||||||
"data-placeholder": _("Eco-account"),
|
|
||||||
"data-minimum-input-length": 3,
|
|
||||||
"readonly": True,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
surface = forms.DecimalField(
|
|
||||||
min_value=0.00,
|
|
||||||
decimal_places=2,
|
|
||||||
label=_("Surface"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("in m²"),
|
|
||||||
widget=forms.NumberInput(
|
|
||||||
attrs={
|
|
||||||
"class": "form-control",
|
|
||||||
"placeholder": "0,00",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
intervention = forms.ModelChoiceField(
|
|
||||||
label=_("Intervention"),
|
|
||||||
label_suffix="",
|
|
||||||
help_text=_("Only shared interventions can be selected"),
|
|
||||||
queryset=Intervention.objects.filter(deleted=None),
|
|
||||||
widget=autocomplete.ModelSelect2(
|
|
||||||
url="interventions-autocomplete",
|
|
||||||
attrs={
|
|
||||||
"data-placeholder": _("Intervention"),
|
|
||||||
"data-minimum-input-length": 3,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("New Deduction")
|
|
||||||
self.form_caption = _("Enter the information for a new deduction from a chosen eco-account")
|
|
||||||
|
|
||||||
# Check for Intervention or EcoAccount
|
|
||||||
if isinstance(self.instance, Intervention):
|
|
||||||
# Form has been called with a given intervention
|
|
||||||
self.initialize_form_field("intervention", self.instance)
|
|
||||||
self.disable_form_field("intervention")
|
|
||||||
elif isinstance(self.instance, EcoAccount):
|
|
||||||
# Form has been called with a given account --> make it initial in the form and read-only
|
|
||||||
self.initialize_form_field("account", self.instance)
|
|
||||||
self.disable_form_field("account")
|
|
||||||
else:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def _get_available_surface(self, acc):
|
|
||||||
""" Calculates how much available surface is left on the account
|
|
||||||
|
|
||||||
Args:
|
|
||||||
acc (EcoAccount):
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
# Calculate valid surface
|
|
||||||
deductable_surface = acc.deductable_surface
|
|
||||||
sum_surface_deductions = acc.get_deductions_surface()
|
|
||||||
rest_surface = deductable_surface - sum_surface_deductions
|
|
||||||
return rest_surface
|
|
||||||
|
|
||||||
def is_valid(self):
|
|
||||||
""" Custom validity check
|
|
||||||
|
|
||||||
Makes sure the deduction can not contain more surface than the account still provides
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
is_valid (bool)
|
|
||||||
"""
|
|
||||||
super_result = super().is_valid()
|
|
||||||
acc = self.cleaned_data["account"]
|
|
||||||
intervention = self.cleaned_data["intervention"]
|
|
||||||
objects_valid = True
|
|
||||||
|
|
||||||
if not acc.recorded:
|
|
||||||
self.add_error(
|
|
||||||
"account",
|
|
||||||
_("Eco-account {} is not recorded yet. You can only deduct from recorded accounts.").format(acc.identifier)
|
|
||||||
)
|
|
||||||
objects_valid = False
|
|
||||||
|
|
||||||
if intervention.is_recorded:
|
|
||||||
self.add_error(
|
|
||||||
"intervention",
|
|
||||||
_("Intervention {} is currently recorded. To change any data on it, the entry must be unrecorded.").format(intervention.identifier)
|
|
||||||
)
|
|
||||||
objects_valid = False
|
|
||||||
|
|
||||||
rest_surface = self._get_available_surface(acc)
|
|
||||||
form_surface = float(self.cleaned_data["surface"])
|
|
||||||
is_valid_surface = form_surface <= rest_surface
|
|
||||||
if not is_valid_surface:
|
|
||||||
self.add_error(
|
|
||||||
"surface",
|
|
||||||
_("The account {} has not enough surface for a deduction of {} m². There are only {} m² left").format(
|
|
||||||
acc.identifier,
|
|
||||||
format_german_float(form_surface),
|
|
||||||
format_german_float(rest_surface),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return is_valid_surface and objects_valid and super_result
|
|
||||||
|
|
||||||
def __create_deduction(self):
|
|
||||||
""" Creates the deduction
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
with transaction.atomic():
|
|
||||||
user_action_create = UserActionLogEntry.get_created_action(self.user)
|
|
||||||
deduction = EcoAccountDeduction.objects.create(
|
|
||||||
intervention=self.cleaned_data["intervention"],
|
|
||||||
account=self.cleaned_data["account"],
|
|
||||||
surface=self.cleaned_data["surface"],
|
|
||||||
created=user_action_create,
|
|
||||||
)
|
|
||||||
return deduction
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
deduction = self.__create_deduction()
|
|
||||||
self.cleaned_data["intervention"].mark_as_edited(self.user, edit_comment=DEDUCTION_ADDED)
|
|
||||||
self.cleaned_data["account"].mark_as_edited(self.user, edit_comment=DEDUCTION_ADDED)
|
|
||||||
return deduction
|
|
||||||
|
|
||||||
|
|
||||||
class EditEcoAccountDeductionModalForm(NewDeductionModalForm):
|
|
||||||
deduction = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.deduction = kwargs.pop("deduction", None)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.form_title = _("Edit Deduction")
|
|
||||||
form_data = {
|
|
||||||
"account": self.deduction.account,
|
|
||||||
"intervention": self.deduction.intervention,
|
|
||||||
"surface": self.deduction.surface,
|
|
||||||
}
|
|
||||||
self.load_initial_data(form_data)
|
|
||||||
|
|
||||||
def _get_available_surface(self, acc):
|
|
||||||
rest_surface = super()._get_available_surface(acc)
|
|
||||||
# Increase available surface by the currently deducted surface, so we can 'deduct' the same amount again or
|
|
||||||
# increase the surface only a little, which will still be valid.
|
|
||||||
# Example: 200 m² left, 500 m² deducted. Entering 700 m² would fail if we would not add the 500 m² to the available
|
|
||||||
# surface again.
|
|
||||||
rest_surface += self.deduction.surface
|
|
||||||
return rest_surface
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
deduction = self.deduction
|
|
||||||
form_account = self.cleaned_data.get("account", None)
|
|
||||||
form_intervention = self.cleaned_data.get("intervention", None)
|
|
||||||
old_account = deduction.account
|
|
||||||
old_intervention = deduction.intervention
|
|
||||||
old_surface = deduction.surface
|
|
||||||
|
|
||||||
# If account or intervention has been changed, we put that change in the logs just as if the deduction has
|
|
||||||
# been removed for this entry. Act as if the deduction is newly created for the new entries
|
|
||||||
if old_account != form_account:
|
|
||||||
old_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_REMOVED)
|
|
||||||
form_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_ADDED)
|
|
||||||
else:
|
|
||||||
old_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_EDITED)
|
|
||||||
|
|
||||||
if old_intervention != form_intervention:
|
|
||||||
old_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_REMOVED)
|
|
||||||
form_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_ADDED)
|
|
||||||
else:
|
|
||||||
old_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_EDITED)
|
|
||||||
|
|
||||||
deduction.account = form_account
|
|
||||||
deduction.intervention = self.cleaned_data.get("intervention", None)
|
|
||||||
deduction.surface = self.cleaned_data.get("surface", None)
|
|
||||||
deduction.save()
|
|
||||||
|
|
||||||
data_changes = {
|
|
||||||
"surface": {
|
|
||||||
"old": old_surface,
|
|
||||||
"new": deduction.surface,
|
|
||||||
},
|
|
||||||
"intervention": {
|
|
||||||
"old": old_intervention.identifier,
|
|
||||||
"new": deduction.intervention.identifier,
|
|
||||||
},
|
|
||||||
"account": {
|
|
||||||
"old": old_account.identifier,
|
|
||||||
"new": deduction.account.identifier,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
old_account.send_notification_mail_on_deduction_change(data_changes)
|
|
||||||
return deduction
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveEcoAccountDeductionModalForm(RemoveModalForm):
|
|
||||||
""" Removing modal form for EcoAccountDeduction
|
|
||||||
|
|
||||||
Can be used for anything, where removing shall be confirmed by the user a second time.
|
|
||||||
|
|
||||||
"""
|
|
||||||
deduction = None
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
deduction = kwargs.pop("deduction", None)
|
|
||||||
self.deduction = deduction
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
with transaction.atomic():
|
|
||||||
self.deduction.intervention.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
|
|
||||||
self.deduction.account.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
|
|
||||||
self.deduction.delete()
|
|
||||||
|
|
||||||
|
|
||||||
class NewInterventionDocumentModalForm(NewDocumentModalForm):
|
|
||||||
document_model = InterventionDocument
|
|
7
intervention/forms/modals/__init__.py
Normal file
7
intervention/forms/modals/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
107
intervention/forms/modals/check.py
Normal file
107
intervention/forms/modals/check.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django import forms
|
||||||
|
from django.db import transaction
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from konova.forms.modals import BaseModalForm
|
||||||
|
|
||||||
|
|
||||||
|
class CheckModalForm(BaseModalForm):
|
||||||
|
""" The modal form for running a check on interventions and their compensations
|
||||||
|
|
||||||
|
"""
|
||||||
|
checked_intervention = forms.BooleanField(
|
||||||
|
label=_("Checked intervention data"),
|
||||||
|
label_suffix="",
|
||||||
|
widget=forms.CheckboxInput(),
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
checked_comps = forms.BooleanField(
|
||||||
|
label=_("Checked compensations data and payments"),
|
||||||
|
label_suffix="",
|
||||||
|
widget=forms.CheckboxInput(),
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
valid = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Run check")
|
||||||
|
self.form_caption = _("I, {} {}, confirm that all necessary control steps have been performed by myself.").format(self.user.first_name, self.user.last_name)
|
||||||
|
self.valid = False
|
||||||
|
|
||||||
|
def _are_deductions_valid(self):
|
||||||
|
""" Performs validity checks on deductions and their eco-account
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
deductions = self.instance.deductions.all()
|
||||||
|
for deduction in deductions:
|
||||||
|
checker = deduction.account.quality_check()
|
||||||
|
for msg in checker.messages:
|
||||||
|
self.add_error(
|
||||||
|
"checked_comps",
|
||||||
|
f"{deduction.account.identifier}: {msg}"
|
||||||
|
)
|
||||||
|
return checker.valid
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _are_comps_valid(self):
|
||||||
|
""" Performs validity checks on all types of compensations
|
||||||
|
|
||||||
|
Types of compensations are
|
||||||
|
* regular Compensations
|
||||||
|
* deductions from EcoAccounts
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
comps = self.instance.compensations.filter(
|
||||||
|
deleted=None,
|
||||||
|
)
|
||||||
|
comps_valid = True
|
||||||
|
for comp in comps:
|
||||||
|
checker = comp.quality_check()
|
||||||
|
for msg in checker.messages:
|
||||||
|
self.add_error(
|
||||||
|
"checked_comps",
|
||||||
|
f"{comp.identifier}: {msg}"
|
||||||
|
)
|
||||||
|
comps_valid = checker.valid
|
||||||
|
deductions_valid = self._are_deductions_valid()
|
||||||
|
return deductions_valid and comps_valid
|
||||||
|
|
||||||
|
def is_valid(self) -> bool:
|
||||||
|
""" Perform a validity check based on quality_check() logic
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
result (bool)
|
||||||
|
"""
|
||||||
|
super_valid = super().is_valid()
|
||||||
|
# Perform check
|
||||||
|
checker = self.instance.quality_check()
|
||||||
|
for msg in checker.messages:
|
||||||
|
self.add_error(
|
||||||
|
"checked_intervention",
|
||||||
|
msg
|
||||||
|
)
|
||||||
|
all_comps_valid = self._are_comps_valid()
|
||||||
|
intervention_valid = checker.valid
|
||||||
|
|
||||||
|
return super_valid and intervention_valid and all_comps_valid
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
""" Saving logic
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
with transaction.atomic():
|
||||||
|
self.instance.set_checked(self.user)
|
252
intervention/forms/modals/deduction.py
Normal file
252
intervention/forms/modals/deduction.py
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from dal import autocomplete
|
||||||
|
from django import forms
|
||||||
|
from django.db import transaction
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from compensation.models import EcoAccount, EcoAccountDeduction
|
||||||
|
from intervention.models import Intervention
|
||||||
|
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
||||||
|
from konova.utils.general import format_german_float
|
||||||
|
from konova.utils.message_templates import DEDUCTION_ADDED, DEDUCTION_REMOVED, DEDUCTION_EDITED
|
||||||
|
from user.models import UserActionLogEntry
|
||||||
|
|
||||||
|
|
||||||
|
class NewEcoAccountDeductionModalForm(BaseModalForm):
|
||||||
|
""" Form for creating new deduction
|
||||||
|
|
||||||
|
Can be used for Intervention view as well as for EcoAccount views.
|
||||||
|
|
||||||
|
Parameter 'instance' can be an intervention, as well as an ecoAccount.
|
||||||
|
An instance check handles both workflows properly.
|
||||||
|
|
||||||
|
"""
|
||||||
|
account = forms.ModelChoiceField(
|
||||||
|
label=_("Eco-account"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Only recorded accounts can be selected for deductions"),
|
||||||
|
queryset=EcoAccount.objects.filter(deleted=None),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url="compensation:acc:autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Eco-account"),
|
||||||
|
"data-minimum-input-length": 3,
|
||||||
|
"readonly": True,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
surface = forms.DecimalField(
|
||||||
|
min_value=0.00,
|
||||||
|
decimal_places=2,
|
||||||
|
label=_("Surface"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("in m²"),
|
||||||
|
widget=forms.NumberInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control",
|
||||||
|
"placeholder": "0,00",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
intervention = forms.ModelChoiceField(
|
||||||
|
label=_("Intervention"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Only shared interventions can be selected"),
|
||||||
|
queryset=Intervention.objects.filter(deleted=None),
|
||||||
|
widget=autocomplete.ModelSelect2(
|
||||||
|
url="intervention:autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Intervention"),
|
||||||
|
"data-minimum-input-length": 3,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("New Deduction")
|
||||||
|
self.form_caption = _("Enter the information for a new deduction from a chosen eco-account")
|
||||||
|
|
||||||
|
# Check for Intervention or EcoAccount
|
||||||
|
if isinstance(self.instance, Intervention):
|
||||||
|
# Form has been called with a given intervention
|
||||||
|
self.initialize_form_field("intervention", self.instance)
|
||||||
|
self.disable_form_field("intervention")
|
||||||
|
elif isinstance(self.instance, EcoAccount):
|
||||||
|
# Form has been called with a given account --> make it initial in the form and read-only
|
||||||
|
self.initialize_form_field("account", self.instance)
|
||||||
|
self.disable_form_field("account")
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def _get_available_surface(self, acc):
|
||||||
|
""" Calculates how much available surface is left on the account
|
||||||
|
|
||||||
|
Args:
|
||||||
|
acc (EcoAccount):
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Calculate valid surface
|
||||||
|
deductable_surface = acc.deductable_surface
|
||||||
|
sum_surface_deductions = acc.get_deductions_surface()
|
||||||
|
rest_surface = deductable_surface - sum_surface_deductions
|
||||||
|
return rest_surface
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
""" Custom validity check
|
||||||
|
|
||||||
|
Makes sure the deduction can not contain more surface than the account still provides
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
is_valid (bool)
|
||||||
|
"""
|
||||||
|
super_result = super().is_valid()
|
||||||
|
acc = self.cleaned_data["account"]
|
||||||
|
intervention = self.cleaned_data["intervention"]
|
||||||
|
objects_valid = True
|
||||||
|
|
||||||
|
if not acc.recorded:
|
||||||
|
self.add_error(
|
||||||
|
"account",
|
||||||
|
_("Eco-account {} is not recorded yet. You can only deduct from recorded accounts.").format(acc.identifier)
|
||||||
|
)
|
||||||
|
objects_valid = False
|
||||||
|
|
||||||
|
if intervention.is_recorded:
|
||||||
|
self.add_error(
|
||||||
|
"intervention",
|
||||||
|
_("Intervention {} is currently recorded. To change any data on it, the entry must be unrecorded.").format(intervention.identifier)
|
||||||
|
)
|
||||||
|
objects_valid = False
|
||||||
|
|
||||||
|
rest_surface = self._get_available_surface(acc)
|
||||||
|
form_surface = float(self.cleaned_data["surface"])
|
||||||
|
is_valid_surface = form_surface <= rest_surface
|
||||||
|
if not is_valid_surface:
|
||||||
|
self.add_error(
|
||||||
|
"surface",
|
||||||
|
_("The account {} has not enough surface for a deduction of {} m². There are only {} m² left").format(
|
||||||
|
acc.identifier,
|
||||||
|
format_german_float(form_surface),
|
||||||
|
format_german_float(rest_surface),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return is_valid_surface and objects_valid and super_result
|
||||||
|
|
||||||
|
def __create_deduction(self):
|
||||||
|
""" Creates the deduction
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
with transaction.atomic():
|
||||||
|
user_action_create = UserActionLogEntry.get_created_action(self.user)
|
||||||
|
deduction = EcoAccountDeduction.objects.create(
|
||||||
|
intervention=self.cleaned_data["intervention"],
|
||||||
|
account=self.cleaned_data["account"],
|
||||||
|
surface=self.cleaned_data["surface"],
|
||||||
|
created=user_action_create,
|
||||||
|
)
|
||||||
|
return deduction
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
deduction = self.__create_deduction()
|
||||||
|
self.cleaned_data["intervention"].mark_as_edited(self.user, edit_comment=DEDUCTION_ADDED)
|
||||||
|
self.cleaned_data["account"].mark_as_edited(self.user, edit_comment=DEDUCTION_ADDED)
|
||||||
|
return deduction
|
||||||
|
|
||||||
|
|
||||||
|
class EditEcoAccountDeductionModalForm(NewEcoAccountDeductionModalForm):
|
||||||
|
deduction = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.deduction = kwargs.pop("deduction", None)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Edit Deduction")
|
||||||
|
form_data = {
|
||||||
|
"account": self.deduction.account,
|
||||||
|
"intervention": self.deduction.intervention,
|
||||||
|
"surface": self.deduction.surface,
|
||||||
|
}
|
||||||
|
self.load_initial_data(form_data)
|
||||||
|
|
||||||
|
def _get_available_surface(self, acc):
|
||||||
|
rest_surface = super()._get_available_surface(acc)
|
||||||
|
# Increase available surface by the currently deducted surface, so we can 'deduct' the same amount again or
|
||||||
|
# increase the surface only a little, which will still be valid.
|
||||||
|
# Example: 200 m² left, 500 m² deducted. Entering 700 m² would fail if we would not add the 500 m² to the available
|
||||||
|
# surface again.
|
||||||
|
rest_surface += self.deduction.surface
|
||||||
|
return rest_surface
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
deduction = self.deduction
|
||||||
|
form_account = self.cleaned_data.get("account", None)
|
||||||
|
form_intervention = self.cleaned_data.get("intervention", None)
|
||||||
|
old_account = deduction.account
|
||||||
|
old_intervention = deduction.intervention
|
||||||
|
old_surface = deduction.surface
|
||||||
|
|
||||||
|
# If account or intervention has been changed, we put that change in the logs just as if the deduction has
|
||||||
|
# been removed for this entry. Act as if the deduction is newly created for the new entries
|
||||||
|
if old_account != form_account:
|
||||||
|
old_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_REMOVED)
|
||||||
|
form_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_ADDED)
|
||||||
|
else:
|
||||||
|
old_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_EDITED)
|
||||||
|
|
||||||
|
if old_intervention != form_intervention:
|
||||||
|
old_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_REMOVED)
|
||||||
|
form_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_ADDED)
|
||||||
|
else:
|
||||||
|
old_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_EDITED)
|
||||||
|
|
||||||
|
deduction.account = form_account
|
||||||
|
deduction.intervention = self.cleaned_data.get("intervention", None)
|
||||||
|
deduction.surface = self.cleaned_data.get("surface", None)
|
||||||
|
deduction.save()
|
||||||
|
|
||||||
|
data_changes = {
|
||||||
|
"surface": {
|
||||||
|
"old": old_surface,
|
||||||
|
"new": deduction.surface,
|
||||||
|
},
|
||||||
|
"intervention": {
|
||||||
|
"old": old_intervention.identifier,
|
||||||
|
"new": deduction.intervention.identifier,
|
||||||
|
},
|
||||||
|
"account": {
|
||||||
|
"old": old_account.identifier,
|
||||||
|
"new": deduction.account.identifier,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
old_account.send_notification_mail_on_deduction_change(data_changes)
|
||||||
|
return deduction
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveEcoAccountDeductionModalForm(RemoveModalForm):
|
||||||
|
""" Removing modal form for EcoAccountDeduction
|
||||||
|
|
||||||
|
Can be used for anything, where removing shall be confirmed by the user a second time.
|
||||||
|
|
||||||
|
"""
|
||||||
|
deduction = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
deduction = kwargs.pop("deduction", None)
|
||||||
|
self.deduction = deduction
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
with transaction.atomic():
|
||||||
|
self.deduction.intervention.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
|
||||||
|
self.deduction.account.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED)
|
||||||
|
self.deduction.delete()
|
13
intervention/forms/modals/document.py
Normal file
13
intervention/forms/modals/document.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from intervention.models import InterventionDocument
|
||||||
|
from konova.forms.modals import NewDocumentModalForm
|
||||||
|
|
||||||
|
|
||||||
|
class NewInterventionDocumentModalForm(NewDocumentModalForm):
|
||||||
|
document_model = InterventionDocument
|
110
intervention/forms/modals/revocation.py
Normal file
110
intervention/forms/modals/revocation.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django import forms
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from intervention.models import RevocationDocument
|
||||||
|
from konova.forms.modals import BaseModalForm, RemoveModalForm
|
||||||
|
from konova.utils.message_templates import REVOCATION_ADDED, REVOCATION_EDITED
|
||||||
|
|
||||||
|
|
||||||
|
class NewRevocationModalForm(BaseModalForm):
|
||||||
|
date = forms.DateField(
|
||||||
|
label=_("Date"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
help_text=_("Date of revocation"),
|
||||||
|
widget=forms.DateInput(
|
||||||
|
attrs={
|
||||||
|
"type": "date",
|
||||||
|
"data-provide": "datepicker",
|
||||||
|
"class": "form-control",
|
||||||
|
},
|
||||||
|
format="%d.%m.%Y"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
file = forms.FileField(
|
||||||
|
label=_("Document"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
required=False,
|
||||||
|
help_text=_("Must be smaller than 15 Mb"),
|
||||||
|
widget=forms.FileInput(
|
||||||
|
attrs={
|
||||||
|
"class": "form-control-file"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
comment = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
max_length=200,
|
||||||
|
label=_("Comment"),
|
||||||
|
label_suffix=_(""),
|
||||||
|
help_text=_("Additional comment, maximum {} letters").format(200),
|
||||||
|
widget=forms.Textarea(
|
||||||
|
attrs={
|
||||||
|
"cols": 30,
|
||||||
|
"rows": 5,
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
document_model = RevocationDocument
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Add revocation")
|
||||||
|
self.form_caption = ""
|
||||||
|
self.form_attrs = {
|
||||||
|
"enctype": "multipart/form-data", # important for file upload
|
||||||
|
}
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
revocation = self.instance.add_revocation(self)
|
||||||
|
self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_ADDED)
|
||||||
|
return revocation
|
||||||
|
|
||||||
|
|
||||||
|
class EditRevocationModalForm(NewRevocationModalForm):
|
||||||
|
revocation = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.revocation = kwargs.pop("revocation", None)
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Edit revocation")
|
||||||
|
try:
|
||||||
|
doc = self.revocation.document.file
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
doc = None
|
||||||
|
form_data = {
|
||||||
|
"date": str(self.revocation.date),
|
||||||
|
"file": doc,
|
||||||
|
"comment": self.revocation.comment,
|
||||||
|
}
|
||||||
|
self.load_initial_data(form_data)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
revocation = self.instance.edit_revocation(self)
|
||||||
|
self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_EDITED)
|
||||||
|
return revocation
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveRevocationModalForm(RemoveModalForm):
|
||||||
|
""" Removing modal form for Revocation
|
||||||
|
|
||||||
|
Can be used for anything, where removing shall be confirmed by the user a second time.
|
||||||
|
|
||||||
|
"""
|
||||||
|
revocation = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
revocation = kwargs.pop("revocation", None)
|
||||||
|
self.revocation = revocation
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.instance.remove_revocation(self)
|
136
intervention/forms/modals/share.py
Normal file
136
intervention/forms/modals/share.py
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 18.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from dal import autocomplete
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from intervention.inputs import TextToClipboardInput
|
||||||
|
from konova.forms.modals import BaseModalForm
|
||||||
|
from konova.utils.message_templates import ENTRY_REMOVE_MISSING_PERMISSION
|
||||||
|
from konova.utils.user_checks import is_default_group_only
|
||||||
|
from user.models import Team, User
|
||||||
|
|
||||||
|
|
||||||
|
class ShareModalForm(BaseModalForm):
|
||||||
|
url = forms.CharField(
|
||||||
|
label=_("Share link"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Send this link to users who you want to have writing access on the data"),
|
||||||
|
required=False,
|
||||||
|
widget=TextToClipboardInput(
|
||||||
|
attrs={
|
||||||
|
"readonly": True,
|
||||||
|
"class": "form-control",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
teams = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Add team to share with"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Multiple selection possible - You can only select teams which do not already have access."),
|
||||||
|
required=False,
|
||||||
|
queryset=Team.objects.all(),
|
||||||
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
|
url="user:share-team-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Click for selection"),
|
||||||
|
"data-minimum-input-length": 3,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
users = forms.ModelMultipleChoiceField(
|
||||||
|
label=_("Add user to share with"),
|
||||||
|
label_suffix="",
|
||||||
|
help_text=_("Multiple selection possible - You can only select users which do not already have access. Enter the full username."),
|
||||||
|
required=False,
|
||||||
|
queryset=User.objects.all(),
|
||||||
|
widget=autocomplete.ModelSelect2Multiple(
|
||||||
|
url="user:share-user-autocomplete",
|
||||||
|
attrs={
|
||||||
|
"data-placeholder": _("Click for selection"),
|
||||||
|
"data-minimum-input-length": 3,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.form_title = _("Share")
|
||||||
|
self.form_caption = _("Share settings for {}").format(self.instance.identifier)
|
||||||
|
self.template = "modal/modal_form.html"
|
||||||
|
|
||||||
|
# Make sure an access_token is set
|
||||||
|
if self.instance.access_token is None:
|
||||||
|
self.instance.generate_access_token()
|
||||||
|
|
||||||
|
self._init_fields()
|
||||||
|
|
||||||
|
def _user_team_valid(self):
|
||||||
|
""" Checks whether users and teams have been removed by the user and if the user is allowed to do so or not
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
users = self.cleaned_data.get("users", User.objects.none())
|
||||||
|
teams = self.cleaned_data.get("teams", Team.objects.none())
|
||||||
|
|
||||||
|
_is_valid = True
|
||||||
|
if is_default_group_only(self.user):
|
||||||
|
shared_users = self.instance.shared_users
|
||||||
|
shared_teams = self.instance.shared_teams
|
||||||
|
|
||||||
|
shared_users_are_removed = not set(shared_users).issubset(users)
|
||||||
|
shared_teams_are_removed = not set(shared_teams).issubset(teams)
|
||||||
|
|
||||||
|
if shared_users_are_removed:
|
||||||
|
self.add_error(
|
||||||
|
"users",
|
||||||
|
ENTRY_REMOVE_MISSING_PERMISSION
|
||||||
|
)
|
||||||
|
_is_valid = False
|
||||||
|
if shared_teams_are_removed:
|
||||||
|
self.add_error(
|
||||||
|
"teams",
|
||||||
|
ENTRY_REMOVE_MISSING_PERMISSION
|
||||||
|
)
|
||||||
|
_is_valid = False
|
||||||
|
return _is_valid
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
""" Extended validity check
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
super_valid = super().is_valid()
|
||||||
|
user_team_valid = self._user_team_valid()
|
||||||
|
_is_valid = super_valid and user_team_valid
|
||||||
|
return _is_valid
|
||||||
|
|
||||||
|
def _init_fields(self):
|
||||||
|
""" Wraps initializing of fields
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Initialize share_link field
|
||||||
|
share_link = self.instance.get_share_link()
|
||||||
|
self.share_link = self.request.build_absolute_uri(share_link)
|
||||||
|
self.initialize_form_field(
|
||||||
|
"url",
|
||||||
|
self.share_link
|
||||||
|
)
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
"teams": self.instance.teams.all(),
|
||||||
|
"users": self.instance.users.all(),
|
||||||
|
}
|
||||||
|
self.load_initial_data(form_data)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.instance.update_shared_access(self)
|
@ -337,7 +337,7 @@ class Intervention(BaseObject,
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return reverse("intervention:share", args=(self.id, self.access_token))
|
return reverse("intervention:share-token", args=(self.id, self.access_token))
|
||||||
|
|
||||||
def remove_payment(self, form):
|
def remove_payment(self, form):
|
||||||
""" Removes a Payment from the intervention
|
""" Removes a Payment from the intervention
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Resubmission' %}" data-form-url="{% url 'intervention:resubmission-create' obj.id %}">
|
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Resubmission' %}" data-form-url="{% url 'intervention:resubmission-create' obj.id %}">
|
||||||
{% fa5_icon 'bell' %}
|
{% fa5_icon 'bell' %}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'intervention:share-create' obj.id %}">
|
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'intervention:share-form' obj.id %}">
|
||||||
{% fa5_icon 'share-alt' %}
|
{% fa5_icon 'share-alt' %}
|
||||||
</button>
|
</button>
|
||||||
{% if is_zb_member %}
|
{% if is_zb_member %}
|
||||||
|
@ -31,8 +31,8 @@ class InterventionViewTestCase(BaseViewTestCase):
|
|||||||
self.log_url = reverse("intervention:log", args=(self.intervention.id,))
|
self.log_url = reverse("intervention:log", args=(self.intervention.id,))
|
||||||
self.edit_url = reverse("intervention:edit", args=(self.intervention.id,))
|
self.edit_url = reverse("intervention:edit", args=(self.intervention.id,))
|
||||||
self.remove_url = reverse("intervention:remove", args=(self.intervention.id,))
|
self.remove_url = reverse("intervention:remove", args=(self.intervention.id,))
|
||||||
self.share_url = reverse("intervention:share", args=(self.intervention.id, self.intervention.access_token,))
|
self.share_url = reverse("intervention:share-token", args=(self.intervention.id, self.intervention.access_token,))
|
||||||
self.share_create_url = reverse("intervention:share-create", args=(self.intervention.id,))
|
self.share_create_url = reverse("intervention:share-form", args=(self.intervention.id,))
|
||||||
self.run_check_url = reverse("intervention:check", args=(self.intervention.id,))
|
self.run_check_url = reverse("intervention:check", args=(self.intervention.id,))
|
||||||
self.record_url = reverse("intervention:record", args=(self.intervention.id,))
|
self.record_url = reverse("intervention:record", args=(self.intervention.id,))
|
||||||
self.report_url = reverse("intervention:report", args=(self.intervention.id,))
|
self.report_url = reverse("intervention:report", args=(self.intervention.id,))
|
||||||
|
@ -7,11 +7,21 @@ Created on: 30.11.20
|
|||||||
"""
|
"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from intervention.views import index_view, new_view, detail_view, edit_view, remove_view, new_document_view, share_view, \
|
from intervention.autocomplete.intervention import InterventionAutocomplete
|
||||||
create_share_view, remove_revocation_view, new_revocation_view, check_view, log_view, new_deduction_view, \
|
from intervention.views.check import check_view
|
||||||
record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view, report_view, \
|
from intervention.views.compensation import remove_compensation_view
|
||||||
remove_deduction_view, remove_compensation_view, edit_deduction_view, edit_revocation_view, edit_document_view, \
|
from intervention.views.deduction import NewInterventionDeductionView, EditInterventionDeductionView, \
|
||||||
create_resubmission_view
|
RemoveInterventionDeductionView
|
||||||
|
from intervention.views.document import NewInterventionDocumentView, GetInterventionDocumentView, \
|
||||||
|
RemoveInterventionDocumentView, EditInterventionDocumentView
|
||||||
|
from intervention.views.intervention import index_view, new_view, new_id_view, detail_view, edit_view, remove_view
|
||||||
|
from intervention.views.log import InterventionLogView
|
||||||
|
from intervention.views.record import InterventionRecordView
|
||||||
|
from intervention.views.report import report_view
|
||||||
|
from intervention.views.resubmission import InterventionResubmissionView
|
||||||
|
from intervention.views.revocation import new_revocation_view, edit_revocation_view, remove_revocation_view, \
|
||||||
|
get_revocation_view
|
||||||
|
from intervention.views.share import InterventionShareFormView, InterventionShareByTokenView
|
||||||
|
|
||||||
app_name = "intervention"
|
app_name = "intervention"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -19,33 +29,36 @@ urlpatterns = [
|
|||||||
path('new/', new_view, name='new'),
|
path('new/', new_view, name='new'),
|
||||||
path('new/id', new_id_view, name='new-id'),
|
path('new/id', new_id_view, name='new-id'),
|
||||||
path('<id>', detail_view, name='detail'),
|
path('<id>', detail_view, name='detail'),
|
||||||
path('<id>/log', log_view, name='log'),
|
path('<id>/log', InterventionLogView.as_view(), name='log'),
|
||||||
path('<id>/edit', edit_view, name='edit'),
|
path('<id>/edit', edit_view, name='edit'),
|
||||||
path('<id>/remove', remove_view, name='remove'),
|
path('<id>/remove', remove_view, name='remove'),
|
||||||
path('<id>/share/<token>', share_view, name='share'),
|
path('<id>/share/<token>', InterventionShareByTokenView.as_view(), name='share-token'),
|
||||||
path('<id>/share', create_share_view, name='share-create'),
|
path('<id>/share', InterventionShareFormView.as_view(), name='share-form'),
|
||||||
path('<id>/check', check_view, name='check'),
|
path('<id>/check', check_view, name='check'),
|
||||||
path('<id>/record', record_view, name='record'),
|
path('<id>/record', InterventionRecordView.as_view(), name='record'),
|
||||||
path('<id>/report', report_view, name='report'),
|
path('<id>/report', report_view, name='report'),
|
||||||
path('<id>/resub', create_resubmission_view, name='resubmission-create'),
|
path('<id>/resub', InterventionResubmissionView.as_view(), name='resubmission-create'),
|
||||||
|
|
||||||
# Compensations
|
# Compensations
|
||||||
path('<id>/compensation/<comp_id>/remove', remove_compensation_view, name='remove-compensation'),
|
path('<id>/compensation/<comp_id>/remove', remove_compensation_view, name='remove-compensation'),
|
||||||
|
|
||||||
# Documents
|
# Documents
|
||||||
path('<id>/document/new/', new_document_view, name='new-doc'),
|
path('<id>/document/new/', NewInterventionDocumentView.as_view(), name='new-doc'),
|
||||||
path('<id>/document/<doc_id>', get_document_view, name='get-doc'),
|
path('<id>/document/<doc_id>', GetInterventionDocumentView.as_view(), name='get-doc'),
|
||||||
path('<id>/document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
|
path('<id>/document/<doc_id>/remove/', RemoveInterventionDocumentView.as_view(), name='remove-doc'),
|
||||||
path('<id>/document/<doc_id>/edit/', edit_document_view, name='edit-doc'),
|
path('<id>/document/<doc_id>/edit/', EditInterventionDocumentView.as_view(), name='edit-doc'),
|
||||||
|
|
||||||
# Deductions
|
# Deductions
|
||||||
path('<id>/deduction/new', new_deduction_view, name='new-deduction'),
|
path('<id>/deduction/new', NewInterventionDeductionView.as_view(), name='new-deduction'),
|
||||||
path('<id>/deduction/<deduction_id>/edit', edit_deduction_view, name='edit-deduction'),
|
path('<id>/deduction/<deduction_id>/edit', EditInterventionDeductionView.as_view(), name='edit-deduction'),
|
||||||
path('<id>/deduction/<deduction_id>/remove', remove_deduction_view, name='remove-deduction'),
|
path('<id>/deduction/<deduction_id>/remove', RemoveInterventionDeductionView.as_view(), name='remove-deduction'),
|
||||||
|
|
||||||
# Revocation routes
|
# Revocation routes
|
||||||
path('<id>/revocation/new', new_revocation_view, name='new-revocation'),
|
path('<id>/revocation/new', new_revocation_view, name='new-revocation'),
|
||||||
path('<id>/revocation/<revocation_id>/edit', edit_revocation_view, name='edit-revocation'),
|
path('<id>/revocation/<revocation_id>/edit', edit_revocation_view, name='edit-revocation'),
|
||||||
path('<id>/revocation/<revocation_id>/remove', remove_revocation_view, name='remove-revocation'),
|
path('<id>/revocation/<revocation_id>/remove', remove_revocation_view, name='remove-revocation'),
|
||||||
path('revocation/<doc_id>', get_revocation_view, name='get-doc-revocation'),
|
path('revocation/<doc_id>', get_revocation_view, name='get-doc-revocation'),
|
||||||
|
|
||||||
|
# Autocomplete
|
||||||
|
path("atcmplt/interventions", InterventionAutocomplete.as_view(), name="autocomplete"),
|
||||||
]
|
]
|
@ -1,752 +0,0 @@
|
|||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.http import HttpRequest, JsonResponse, Http404
|
|
||||||
from django.shortcuts import render
|
|
||||||
|
|
||||||
from intervention.forms.forms import NewInterventionForm, EditInterventionForm
|
|
||||||
from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \
|
|
||||||
CheckModalForm, NewDeductionModalForm, NewInterventionDocumentModalForm, RemoveEcoAccountDeductionModalForm, \
|
|
||||||
RemoveRevocationModalForm, EditEcoAccountDeductionModalForm, EditRevocationModalForm
|
|
||||||
from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument
|
|
||||||
from intervention.tables import InterventionTable
|
|
||||||
from konova.contexts import BaseContext
|
|
||||||
from konova.decorators import *
|
|
||||||
from konova.forms import SimpleGeomForm
|
|
||||||
from konova.forms.modals import RemoveModalForm, RecordModalForm, EditDocumentModalForm, ResubmissionModalForm
|
|
||||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
|
||||||
from konova.utils.documents import remove_document, get_document
|
|
||||||
from konova.utils.generators import generate_qr_code
|
|
||||||
from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED, \
|
|
||||||
CHECKED_RECORDED_RESET, DEDUCTION_REMOVED, DEDUCTION_ADDED, REVOCATION_ADDED, REVOCATION_REMOVED, \
|
|
||||||
COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, DEDUCTION_EDITED, REVOCATION_EDITED, DOCUMENT_EDITED, \
|
|
||||||
RECORDED_BLOCKS_EDIT, DATA_CHECKED_PREVIOUSLY_TEMPLATE
|
|
||||||
from konova.utils.user_checks import in_group
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@any_group_check
|
|
||||||
def index_view(request: HttpRequest):
|
|
||||||
"""
|
|
||||||
Renders the index view for Interventions
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A rendered view
|
|
||||||
"""
|
|
||||||
template = "generic_index.html"
|
|
||||||
|
|
||||||
# Filtering by user access is performed in table filter inside of InterventionTableFilter class
|
|
||||||
interventions = Intervention.objects.filter(
|
|
||||||
deleted=None, # not deleted
|
|
||||||
).select_related(
|
|
||||||
"legal"
|
|
||||||
)
|
|
||||||
table = InterventionTable(
|
|
||||||
request=request,
|
|
||||||
queryset=interventions
|
|
||||||
)
|
|
||||||
context = {
|
|
||||||
"table": table,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("Interventions - Overview"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
def new_view(request: HttpRequest):
|
|
||||||
"""
|
|
||||||
Renders a view for a new intervention creation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "intervention/form/view.html"
|
|
||||||
data_form = NewInterventionForm(request.POST or None)
|
|
||||||
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
|
||||||
if request.method == "POST":
|
|
||||||
if data_form.is_valid() and geom_form.is_valid():
|
|
||||||
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
|
||||||
intervention = data_form.save(request.user, geom_form)
|
|
||||||
if generated_identifier != intervention.identifier:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
IDENTIFIER_REPLACED.format(
|
|
||||||
generated_identifier,
|
|
||||||
intervention.identifier
|
|
||||||
)
|
|
||||||
)
|
|
||||||
messages.success(request, _("Intervention {} added").format(intervention.identifier))
|
|
||||||
return redirect("intervention:detail", id=intervention.id)
|
|
||||||
else:
|
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
|
||||||
else:
|
|
||||||
# For clarification: nothing in this case
|
|
||||||
pass
|
|
||||||
context = {
|
|
||||||
"form": data_form,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("New intervention"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_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={
|
|
||||||
"gen_data": identifier
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def new_document_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a form for uploading new documents
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention's id to which the new document will be related
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
form = NewInterventionDocumentModalForm(request.POST or None, request.FILES or None, instance=intervention, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DOCUMENT_ADDED,
|
|
||||||
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
def get_revocation_view(request: HttpRequest, doc_id: str):
|
|
||||||
""" Returns the revocation document as downloadable file
|
|
||||||
|
|
||||||
Wraps the generic document fetcher function from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
doc = get_object_or_404(RevocationDocument, id=doc_id)
|
|
||||||
# File download only possible if related instance is shared with user
|
|
||||||
if not doc.instance.legal.intervention.users.filter(id=request.user.id):
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
DATA_UNSHARED
|
|
||||||
)
|
|
||||||
return redirect("intervention:detail", id=doc.instance.id)
|
|
||||||
return get_document(doc)
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def get_document_view(request: HttpRequest, id: str, doc_id: str):
|
|
||||||
""" Returns the document as downloadable file
|
|
||||||
|
|
||||||
Wraps the generic document fetcher function from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
doc = get_object_or_404(InterventionDocument, id=doc_id)
|
|
||||||
return get_document(doc)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def remove_document_view(request: HttpRequest, id: str, doc_id: str):
|
|
||||||
""" Removes the document from the database and file system
|
|
||||||
|
|
||||||
Wraps the generic functionality from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
doc = get_object_or_404(InterventionDocument, id=doc_id)
|
|
||||||
return remove_document(
|
|
||||||
request,
|
|
||||||
doc
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def edit_document_view(request: HttpRequest, id: str, doc_id: str):
|
|
||||||
""" Removes the document from the database and file system
|
|
||||||
|
|
||||||
Wraps the generic functionality from konova.utils.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention id
|
|
||||||
doc_id (str): The document id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
doc = get_object_or_404(InterventionDocument, id=doc_id)
|
|
||||||
form = EditDocumentModalForm(request.POST or None, request.FILES or None, instance=intervention, document=doc, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
DOCUMENT_EDITED,
|
|
||||||
redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@any_group_check
|
|
||||||
def detail_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a detail view for viewing an intervention's data
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "intervention/detail/view.html"
|
|
||||||
|
|
||||||
# Fetch data, filter out deleted related data
|
|
||||||
intervention = get_object_or_404(
|
|
||||||
Intervention.objects.select_related(
|
|
||||||
"geometry",
|
|
||||||
"legal",
|
|
||||||
"responsible",
|
|
||||||
),
|
|
||||||
id=id
|
|
||||||
)
|
|
||||||
compensations = intervention.compensations.filter(
|
|
||||||
deleted=None,
|
|
||||||
)
|
|
||||||
_user = request.user
|
|
||||||
is_data_shared = intervention.is_shared_with(user=_user)
|
|
||||||
|
|
||||||
geom_form = SimpleGeomForm(
|
|
||||||
instance=intervention,
|
|
||||||
)
|
|
||||||
last_checked = intervention.get_last_checked_action()
|
|
||||||
last_checked_tooltip = ""
|
|
||||||
if last_checked:
|
|
||||||
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(last_checked.get_timestamp_str_formatted(), last_checked.user)
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"obj": intervention,
|
|
||||||
"last_checked": last_checked,
|
|
||||||
"last_checked_tooltip": last_checked_tooltip,
|
|
||||||
"compensations": compensations,
|
|
||||||
"has_access": is_data_shared,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
|
||||||
"is_zb_member": in_group(_user, ZB_GROUP),
|
|
||||||
"is_ets_member": in_group(_user, ETS_GROUP),
|
|
||||||
"LANIS_LINK": intervention.get_LANIS_link(),
|
|
||||||
TAB_TITLE_IDENTIFIER: f"{intervention.identifier} - {intervention.title}",
|
|
||||||
}
|
|
||||||
|
|
||||||
request = intervention.set_status_messages(request)
|
|
||||||
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def edit_view(request: HttpRequest, id: str):
|
|
||||||
"""
|
|
||||||
Renders a view for editing interventions
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "intervention/form/view.html"
|
|
||||||
# Get object from db
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
if intervention.is_recorded:
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
RECORDED_BLOCKS_EDIT
|
|
||||||
)
|
|
||||||
return redirect("intervention:detail", 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():
|
|
||||||
# The data form takes the geom form for processing, as well as the performing user
|
|
||||||
# Save the current state of recorded|checked to inform the user in case of a status reset due to editing
|
|
||||||
i_rec = intervention.recorded is not None
|
|
||||||
i_check = intervention.checked is not None
|
|
||||||
intervention = data_form.save(request.user, geom_form)
|
|
||||||
messages.success(request, _("Intervention {} edited").format(intervention.identifier))
|
|
||||||
if i_check or i_rec:
|
|
||||||
messages.info(request, CHECKED_RECORDED_RESET)
|
|
||||||
return redirect("intervention:detail", id=intervention.id)
|
|
||||||
else:
|
|
||||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
|
||||||
else:
|
|
||||||
# For clarification: nothing in this case
|
|
||||||
pass
|
|
||||||
context = {
|
|
||||||
"form": data_form,
|
|
||||||
"geom_form": geom_form,
|
|
||||||
TAB_TITLE_IDENTIFIER: _("Edit {}").format(intervention.identifier),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def remove_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a remove view for this intervention
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The uuid id as string
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
obj = Intervention.objects.get(id=id)
|
|
||||||
identifier = obj.identifier
|
|
||||||
form = RemoveModalForm(request.POST or None, instance=obj, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
_("{} removed").format(identifier),
|
|
||||||
redirect_url=reverse("intervention:index")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def edit_revocation_view(request: HttpRequest, id: str, revocation_id: str):
|
|
||||||
""" Renders a edit view for a revocation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention's id as string
|
|
||||||
revocation_id (str): The revocation's id as string
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
revocation = get_object_or_404(Revocation, id=revocation_id)
|
|
||||||
|
|
||||||
form = EditRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention, revocation=revocation, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
REVOCATION_EDITED,
|
|
||||||
redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def remove_revocation_view(request: HttpRequest, id: str, revocation_id: str):
|
|
||||||
""" Renders a remove view for a revocation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention's id as string
|
|
||||||
revocation_id (str): The revocation's id as string
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
revocation = get_object_or_404(Revocation, id=revocation_id)
|
|
||||||
|
|
||||||
form = RemoveRevocationModalForm(request.POST or None, instance=intervention, revocation=revocation, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
REVOCATION_REMOVED,
|
|
||||||
redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def share_view(request: HttpRequest, id: str, token: str):
|
|
||||||
""" Performs sharing of an intervention
|
|
||||||
|
|
||||||
If token given in url is not valid, the user will be redirected to the dashboard
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): Intervention's id
|
|
||||||
token (str): Access token for intervention
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
user = request.user
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
# Check tokens
|
|
||||||
if intervention.access_token == token:
|
|
||||||
# Send different messages in case user has already been added to list of sharing users
|
|
||||||
if intervention.is_shared_with(user):
|
|
||||||
messages.info(
|
|
||||||
request,
|
|
||||||
_("{} has already been shared with you").format(intervention.identifier)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
messages.success(
|
|
||||||
request,
|
|
||||||
_("{} has been shared with you").format(intervention.identifier)
|
|
||||||
)
|
|
||||||
intervention.share_with_user(user)
|
|
||||||
return redirect("intervention:detail", id=id)
|
|
||||||
else:
|
|
||||||
messages.error(
|
|
||||||
request,
|
|
||||||
_("Share link invalid"),
|
|
||||||
extra_tags="danger",
|
|
||||||
)
|
|
||||||
return redirect("home")
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def create_share_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders sharing form for an intervention
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): Intervention's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
form = ShareModalForm(request.POST or None, instance=intervention, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=_("Share settings updated")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def create_resubmission_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders resubmission form for an intervention
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): Intervention's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
form = ResubmissionModalForm(request.POST or None, instance=intervention, request=request)
|
|
||||||
form.action_url = reverse("intervention:resubmission-create", args=(id,))
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=_("Resubmission set"),
|
|
||||||
redirect_url=reverse("intervention:detail", args=(id,))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@registration_office_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def check_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders check form for an intervention
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): Intervention's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
form = CheckModalForm(request.POST or None, instance=intervention, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=_("Check performed"),
|
|
||||||
msg_error=INTERVENTION_INVALID
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def new_revocation_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders sharing form for an intervention
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): Intervention's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
form = NewRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=REVOCATION_ADDED,
|
|
||||||
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def log_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a log view using modal
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
template = "modal/modal_generic.html"
|
|
||||||
body_template = "log.html"
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"modal_body_template": body_template,
|
|
||||||
"log": intervention.log.all(),
|
|
||||||
"modal_title": _("Log"),
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def new_deduction_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a modal form view for creating deductions
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention's id which shall benefit from this deduction
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
form = NewDeductionModalForm(request.POST or None, instance=intervention, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_success=DEDUCTION_ADDED,
|
|
||||||
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def remove_deduction_view(request: HttpRequest, id: str, deduction_id: str):
|
|
||||||
""" Renders a modal view for removing deductions
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention's id
|
|
||||||
deduction_id (str): The deduction's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
try:
|
|
||||||
eco_deduction = intervention.deductions.get(id=deduction_id)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
raise Http404("Unknown deduction")
|
|
||||||
|
|
||||||
form = RemoveEcoAccountDeductionModalForm(request.POST or None, instance=intervention, deduction=eco_deduction, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request=request,
|
|
||||||
msg_success=DEDUCTION_REMOVED,
|
|
||||||
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@default_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def edit_deduction_view(request: HttpRequest, id: str, deduction_id: str):
|
|
||||||
""" Renders a modal view for removing deductions
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention's id
|
|
||||||
deduction_id (str): The deduction's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
try:
|
|
||||||
eco_deduction = intervention.deductions.get(id=deduction_id)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
raise Http404("Unknown deduction")
|
|
||||||
|
|
||||||
form = EditEcoAccountDeductionModalForm(request.POST or None, instance=intervention, deduction=eco_deduction, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request=request,
|
|
||||||
msg_success=DEDUCTION_EDITED,
|
|
||||||
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
@conservation_office_group_required
|
|
||||||
@shared_access_required(Intervention, "id")
|
|
||||||
def record_view(request: HttpRequest, id: str):
|
|
||||||
""" Renders a modal form for recording an intervention
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The intervention's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
form = RecordModalForm(request.POST or None, instance=intervention, request=request)
|
|
||||||
msg_succ = _("{} unrecorded") if intervention.recorded else _("{} recorded")
|
|
||||||
msg_succ = msg_succ.format(intervention.identifier)
|
|
||||||
return form.process_request(
|
|
||||||
request,
|
|
||||||
msg_succ,
|
|
||||||
msg_error=_("There are errors on this intervention:")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def remove_compensation_view(request:HttpRequest, id: str, comp_id: str):
|
|
||||||
""" Renders a modal view for removing the compensation
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The compensation's id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
try:
|
|
||||||
comp = intervention.compensations.get(
|
|
||||||
id=comp_id
|
|
||||||
)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
raise Http404("Unknown compensation")
|
|
||||||
form = RemoveModalForm(request.POST or None, instance=comp, request=request)
|
|
||||||
return form.process_request(
|
|
||||||
request=request,
|
|
||||||
msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier),
|
|
||||||
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def report_view(request:HttpRequest, id: str):
|
|
||||||
""" Renders the public report view
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request (HttpRequest): The incoming request
|
|
||||||
id (str): The id of the intervention
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
template = "intervention/report/report.html"
|
|
||||||
intervention = get_object_or_404(Intervention, id=id)
|
|
||||||
|
|
||||||
tab_title = _("Report {}").format(intervention.identifier)
|
|
||||||
# If intervention is not recorded (yet or currently) we need to render another template without any data
|
|
||||||
if not intervention.is_ready_for_publish():
|
|
||||||
template = "report/unavailable.html"
|
|
||||||
context = {
|
|
||||||
TAB_TITLE_IDENTIFIER: tab_title,
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
||||||
|
|
||||||
# Prepare data for map viewer
|
|
||||||
geom_form = SimpleGeomForm(
|
|
||||||
instance=intervention
|
|
||||||
)
|
|
||||||
parcels = intervention.get_underlying_parcels()
|
|
||||||
|
|
||||||
distinct_deductions = intervention.deductions.all().distinct(
|
|
||||||
"account"
|
|
||||||
)
|
|
||||||
qrcode_url = request.build_absolute_uri(reverse("intervention:report", args=(id,)))
|
|
||||||
qrcode_img = generate_qr_code(qrcode_url, 10)
|
|
||||||
qrcode_lanis_url = intervention.get_LANIS_link()
|
|
||||||
qrcode_img_lanis = generate_qr_code(qrcode_lanis_url, 7)
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"obj": intervention,
|
|
||||||
"deductions": distinct_deductions,
|
|
||||||
"qrcode": {
|
|
||||||
"img": qrcode_img,
|
|
||||||
"url": qrcode_url,
|
|
||||||
},
|
|
||||||
"qrcode_lanis": {
|
|
||||||
"img": qrcode_img_lanis,
|
|
||||||
"url": qrcode_lanis_url,
|
|
||||||
},
|
|
||||||
"geom_form": geom_form,
|
|
||||||
"parcels": parcels,
|
|
||||||
TAB_TITLE_IDENTIFIER: tab_title,
|
|
||||||
}
|
|
||||||
context = BaseContext(request, context).context
|
|
||||||
return render(request, template, context)
|
|
7
intervention/views/__init__.py
Normal file
7
intervention/views/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
39
intervention/views/check.py
Normal file
39
intervention/views/check.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from intervention.forms.modals.check import CheckModalForm
|
||||||
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import registration_office_group_required, shared_access_required
|
||||||
|
from konova.utils.message_templates import INTERVENTION_INVALID
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@registration_office_group_required
|
||||||
|
@shared_access_required(Intervention, "id")
|
||||||
|
def check_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders check form for an intervention
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): Intervention's id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
|
form = CheckModalForm(request.POST or None, instance=intervention, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request,
|
||||||
|
msg_success=_("Check performed"),
|
||||||
|
msg_error=INTERVENTION_INVALID
|
||||||
|
)
|
||||||
|
|
45
intervention/views/compensation.py
Normal file
45
intervention/views/compensation.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.http import HttpRequest, Http404
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import shared_access_required
|
||||||
|
from konova.forms.modals import RemoveModalForm
|
||||||
|
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@shared_access_required(Intervention, "id")
|
||||||
|
def remove_compensation_view(request: HttpRequest, id: str, comp_id: str):
|
||||||
|
""" Renders a modal view for removing the compensation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The compensation's id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
|
try:
|
||||||
|
comp = intervention.compensations.get(
|
||||||
|
id=comp_id
|
||||||
|
)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
raise Http404("Unknown compensation")
|
||||||
|
form = RemoveModalForm(request.POST or None, instance=comp, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request=request,
|
||||||
|
msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier),
|
||||||
|
redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data",
|
||||||
|
)
|
||||||
|
|
55
intervention/views/deduction.py
Normal file
55
intervention/views/deduction.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import default_group_required, shared_access_required
|
||||||
|
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
|
||||||
|
|
||||||
|
|
||||||
|
class NewInterventionDeductionView(AbstractNewDeductionView):
|
||||||
|
def _custom_check(self, obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
model = Intervention
|
||||||
|
redirect_url = "intervention:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditInterventionDeductionView(AbstractEditDeductionView):
|
||||||
|
def _custom_check(self, obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
model = Intervention
|
||||||
|
redirect_url = "intervention:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveInterventionDeductionView(AbstractRemoveDeductionView):
|
||||||
|
def _custom_check(self, obj):
|
||||||
|
pass
|
||||||
|
|
||||||
|
model = Intervention
|
||||||
|
redirect_url = "intervention:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
63
intervention/views/document.py
Normal file
63
intervention/views/document.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from intervention.forms.modals.document import NewInterventionDocumentModalForm
|
||||||
|
from intervention.models import Intervention, InterventionDocument
|
||||||
|
from konova.decorators import default_group_required, shared_access_required
|
||||||
|
from konova.forms.modals import EditDocumentModalForm
|
||||||
|
from konova.views.document import AbstractNewDocumentView, AbstractGetDocumentView, AbstractRemoveDocumentView, \
|
||||||
|
AbstractEditDocumentView
|
||||||
|
|
||||||
|
|
||||||
|
class NewInterventionDocumentView(AbstractNewDocumentView):
|
||||||
|
model = Intervention
|
||||||
|
form = NewInterventionDocumentModalForm
|
||||||
|
redirect_url = "intervention:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class GetInterventionDocumentView(AbstractGetDocumentView):
|
||||||
|
model = Intervention
|
||||||
|
document_model = InterventionDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveInterventionDocumentView(AbstractRemoveDocumentView):
|
||||||
|
model = Intervention
|
||||||
|
document_model = InterventionDocument
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class EditInterventionDocumentView(AbstractEditDocumentView):
|
||||||
|
model = Intervention
|
||||||
|
document_model = InterventionDocument
|
||||||
|
form = EditDocumentModalForm
|
||||||
|
redirect_url = "intervention:detail"
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
251
intervention/views/intervention.py
Normal file
251
intervention/views/intervention.py
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import JsonResponse, HttpRequest
|
||||||
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from intervention.forms.intervention import EditInterventionForm, NewInterventionForm
|
||||||
|
from intervention.models import Intervention
|
||||||
|
from intervention.tables import InterventionTable
|
||||||
|
from konova.contexts import BaseContext
|
||||||
|
from konova.decorators import default_group_required, shared_access_required, any_group_check
|
||||||
|
from konova.forms import SimpleGeomForm
|
||||||
|
from konova.forms.modals import RemoveModalForm
|
||||||
|
from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP
|
||||||
|
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||||
|
from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, RECORDED_BLOCKS_EDIT, \
|
||||||
|
CHECKED_RECORDED_RESET, FORM_INVALID, IDENTIFIER_REPLACED
|
||||||
|
from konova.utils.user_checks import in_group
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@any_group_check
|
||||||
|
def index_view(request: HttpRequest):
|
||||||
|
"""
|
||||||
|
Renders the index view for Interventions
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A rendered view
|
||||||
|
"""
|
||||||
|
template = "generic_index.html"
|
||||||
|
|
||||||
|
# Filtering by user access is performed in table filter inside of InterventionTableFilter class
|
||||||
|
interventions = Intervention.objects.filter(
|
||||||
|
deleted=None, # not deleted
|
||||||
|
).select_related(
|
||||||
|
"legal"
|
||||||
|
)
|
||||||
|
table = InterventionTable(
|
||||||
|
request=request,
|
||||||
|
queryset=interventions
|
||||||
|
)
|
||||||
|
context = {
|
||||||
|
"table": table,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Interventions - Overview"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
def new_view(request: HttpRequest):
|
||||||
|
"""
|
||||||
|
Renders a view for a new intervention creation
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "intervention/form/view.html"
|
||||||
|
data_form = NewInterventionForm(request.POST or None)
|
||||||
|
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
|
||||||
|
if request.method == "POST":
|
||||||
|
if data_form.is_valid() and geom_form.is_valid():
|
||||||
|
generated_identifier = data_form.cleaned_data.get("identifier", None)
|
||||||
|
intervention = data_form.save(request.user, geom_form)
|
||||||
|
if generated_identifier != intervention.identifier:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
IDENTIFIER_REPLACED.format(
|
||||||
|
generated_identifier,
|
||||||
|
intervention.identifier
|
||||||
|
)
|
||||||
|
)
|
||||||
|
messages.success(request, _("Intervention {} added").format(intervention.identifier))
|
||||||
|
return redirect("intervention:detail", id=intervention.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||||
|
else:
|
||||||
|
# For clarification: nothing in this case
|
||||||
|
pass
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("New intervention"),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_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={
|
||||||
|
"gen_data": identifier
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@any_group_check
|
||||||
|
def detail_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders a detail view for viewing an intervention's data
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The intervention's id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "intervention/detail/view.html"
|
||||||
|
|
||||||
|
# Fetch data, filter out deleted related data
|
||||||
|
intervention = get_object_or_404(
|
||||||
|
Intervention.objects.select_related(
|
||||||
|
"geometry",
|
||||||
|
"legal",
|
||||||
|
"responsible",
|
||||||
|
),
|
||||||
|
id=id
|
||||||
|
)
|
||||||
|
compensations = intervention.compensations.filter(
|
||||||
|
deleted=None,
|
||||||
|
)
|
||||||
|
_user = request.user
|
||||||
|
is_data_shared = intervention.is_shared_with(user=_user)
|
||||||
|
|
||||||
|
geom_form = SimpleGeomForm(
|
||||||
|
instance=intervention,
|
||||||
|
)
|
||||||
|
last_checked = intervention.get_last_checked_action()
|
||||||
|
last_checked_tooltip = ""
|
||||||
|
if last_checked:
|
||||||
|
last_checked_tooltip = DATA_CHECKED_PREVIOUSLY_TEMPLATE.format(last_checked.get_timestamp_str_formatted(), last_checked.user)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"obj": intervention,
|
||||||
|
"last_checked": last_checked,
|
||||||
|
"last_checked_tooltip": last_checked_tooltip,
|
||||||
|
"compensations": compensations,
|
||||||
|
"has_access": is_data_shared,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
"is_default_member": in_group(_user, DEFAULT_GROUP),
|
||||||
|
"is_zb_member": in_group(_user, ZB_GROUP),
|
||||||
|
"is_ets_member": in_group(_user, ETS_GROUP),
|
||||||
|
"LANIS_LINK": intervention.get_LANIS_link(),
|
||||||
|
TAB_TITLE_IDENTIFIER: f"{intervention.identifier} - {intervention.title}",
|
||||||
|
}
|
||||||
|
|
||||||
|
request = intervention.set_status_messages(request)
|
||||||
|
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
@shared_access_required(Intervention, "id")
|
||||||
|
def edit_view(request: HttpRequest, id: str):
|
||||||
|
"""
|
||||||
|
Renders a view for editing interventions
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
template = "intervention/form/view.html"
|
||||||
|
# Get object from db
|
||||||
|
intervention = get_object_or_404(Intervention, id=id)
|
||||||
|
if intervention.is_recorded:
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
RECORDED_BLOCKS_EDIT
|
||||||
|
)
|
||||||
|
return redirect("intervention:detail", 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():
|
||||||
|
# The data form takes the geom form for processing, as well as the performing user
|
||||||
|
# Save the current state of recorded|checked to inform the user in case of a status reset due to editing
|
||||||
|
i_rec = intervention.recorded is not None
|
||||||
|
i_check = intervention.checked is not None
|
||||||
|
intervention = data_form.save(request.user, geom_form)
|
||||||
|
messages.success(request, _("Intervention {} edited").format(intervention.identifier))
|
||||||
|
if i_check or i_rec:
|
||||||
|
messages.info(request, CHECKED_RECORDED_RESET)
|
||||||
|
return redirect("intervention:detail", id=intervention.id)
|
||||||
|
else:
|
||||||
|
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||||
|
else:
|
||||||
|
# For clarification: nothing in this case
|
||||||
|
pass
|
||||||
|
context = {
|
||||||
|
"form": data_form,
|
||||||
|
"geom_form": geom_form,
|
||||||
|
TAB_TITLE_IDENTIFIER: _("Edit {}").format(intervention.identifier),
|
||||||
|
}
|
||||||
|
context = BaseContext(request, context).context
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@default_group_required
|
||||||
|
@shared_access_required(Intervention, "id")
|
||||||
|
def remove_view(request: HttpRequest, id: str):
|
||||||
|
""" Renders a remove view for this intervention
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (HttpRequest): The incoming request
|
||||||
|
id (str): The uuid id as string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
obj = Intervention.objects.get(id=id)
|
||||||
|
identifier = obj.identifier
|
||||||
|
form = RemoveModalForm(request.POST or None, instance=obj, request=request)
|
||||||
|
return form.process_request(
|
||||||
|
request,
|
||||||
|
_("{} removed").format(identifier),
|
||||||
|
redirect_url=reverse("intervention:index")
|
||||||
|
)
|
23
intervention/views/log.py
Normal file
23
intervention/views/log.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
Author: Michel Peltriaux
|
||||||
|
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
|
||||||
|
Contact: ksp-servicestelle@sgdnord.rlp.de
|
||||||
|
Created on: 19.08.22
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
|
||||||
|
from intervention.models import Intervention
|
||||||
|
from konova.decorators import shared_access_required, default_group_required
|
||||||
|
from konova.views.log import AbstractLogView
|
||||||
|
|
||||||
|
|
||||||
|
class InterventionLogView(AbstractLogView):
|
||||||
|
model = Intervention
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
@method_decorator(default_group_required)
|
||||||
|
@method_decorator(shared_access_required(Intervention, "id"))
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user