master #205
@ -55,7 +55,7 @@ class TimespanReportForm(BaseForm):
 | 
			
		||||
            code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-conservation-office-autocomplete",
 | 
			
		||||
            url="codelist:conservation-office-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "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):
 | 
			
		||||
        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.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 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"
 | 
			
		||||
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
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 29.07.21
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
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 konova.filters.mixins import ConservationOfficeTableFilterMixin
 | 
			
		||||
from konova.filters.table_filters import QueryTableFilter, CheckboxTableFilter, SelectionTableFilter, AbstractTableFilter
 | 
			
		||||
from konova.filters.table_filters import AbstractTableFilter, CheckboxTableFilter, QueryTableFilter, \
 | 
			
		||||
    SelectionTableFilter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SelectionCompensationTableFilter(SelectionTableFilter):
 | 
			
		||||
@ -114,71 +111,3 @@ class CompensationTableFilter(AbstractTableFilter):
 | 
			
		||||
        )
 | 
			
		||||
        # Overwrite final queryset as well
 | 
			
		||||
        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:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        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):
 | 
			
		||||
        """ 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
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 01.12.20
 | 
			
		||||
Contact: ksp-servicestelle@sgdnord.rlp.de
 | 
			
		||||
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.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 import CompensationTableFilter, EcoAccountTableFilter
 | 
			
		||||
from compensation.models import Compensation, EcoAccount
 | 
			
		||||
from compensation.filters.compensation import CompensationTableFilter
 | 
			
		||||
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
 | 
			
		||||
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",
 | 
			
		||||
        )
 | 
			
		||||
        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 %}">
 | 
			
		||||
            {% fa5_icon 'bell' %}
 | 
			
		||||
        </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' %}
 | 
			
		||||
        </button>
 | 
			
		||||
        {% if is_ets_member %}
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,20 @@ Created on: 24.08.21
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
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 = [
 | 
			
		||||
    # Main compensation
 | 
			
		||||
@ -15,28 +28,28 @@ urlpatterns = [
 | 
			
		||||
    path('new/<intervention_id>', new_view, name='new'),
 | 
			
		||||
    path('new', new_view, name='new'),
 | 
			
		||||
    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>/remove', remove_view, name='remove'),
 | 
			
		||||
 | 
			
		||||
    path('<id>/state/new', state_new_view, name='new-state'),
 | 
			
		||||
    path('<id>/state/<state_id>/edit', state_edit_view, name='state-edit'),
 | 
			
		||||
    path('<id>/state/<state_id>/remove', state_remove_view, name='state-remove'),
 | 
			
		||||
    path('<id>/state/new', NewCompensationStateView.as_view(), name='new-state'),
 | 
			
		||||
    path('<id>/state/<state_id>/edit', EditCompensationStateView.as_view(), name='state-edit'),
 | 
			
		||||
    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/<action_id>/edit', action_edit_view, name='action-edit'),
 | 
			
		||||
    path('<id>/action/<action_id>/remove', action_remove_view, name='action-remove'),
 | 
			
		||||
    path('<id>/action/new', NewCompensationActionView.as_view(), name='new-action'),
 | 
			
		||||
    path('<id>/action/<action_id>/edit', EditCompensationActionView.as_view(), name='action-edit'),
 | 
			
		||||
    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/<deadline_id>/edit', deadline_edit_view, name='deadline-edit'),
 | 
			
		||||
    path('<id>/deadline/<deadline_id>/remove', deadline_remove_view, name='deadline-remove'),
 | 
			
		||||
    path('<id>/deadline/new', NewCompensationDeadlineView.as_view(), name="new-deadline"),
 | 
			
		||||
    path('<id>/deadline/<deadline_id>/edit', EditCompensationDeadlineView.as_view(), name='deadline-edit'),
 | 
			
		||||
    path('<id>/deadline/<deadline_id>/remove', RemoveCompensationDeadlineView.as_view(), name='deadline-remove'),
 | 
			
		||||
    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
 | 
			
		||||
    path('<id>/document/new/', new_document_view, name='new-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>', get_document_view, name='get-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/edit/', edit_document_view, name='edit-doc'),
 | 
			
		||||
    path('<id>/document/new/', NewCompensationDocumentView.as_view(), name='new-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>', GetCompensationDocumentView.as_view(), name='get-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/remove/', RemoveCompensationDocumentView.as_view(), name='remove-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 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"
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
@ -14,37 +32,39 @@ urlpatterns = [
 | 
			
		||||
    path('new/', new_view, name='new'),
 | 
			
		||||
    path('new/id', new_id_view, name='new-id'),
 | 
			
		||||
    path('<id>', detail_view, name='detail'),
 | 
			
		||||
    path('<id>/log', log_view, name='log'),
 | 
			
		||||
    path('<id>/record', record_view, name='record'),
 | 
			
		||||
    path('<id>/log', EcoAccountLogView.as_view(), name='log'),
 | 
			
		||||
    path('<id>/record', EcoAccountRecordView.as_view(), name='record'),
 | 
			
		||||
    path('<id>/report', report_view, name='report'),
 | 
			
		||||
    path('<id>/edit', edit_view, name='edit'),
 | 
			
		||||
    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/<state_id>/edit', state_edit_view, name='state-edit'),
 | 
			
		||||
    path('<id>/state/<state_id>/remove', state_remove_view, name='state-remove'),
 | 
			
		||||
    path('<id>/state/new', NewEcoAccountStateView.as_view(), name='new-state'),
 | 
			
		||||
    path('<id>/state/<state_id>/edit', EditEcoAccountStateView.as_view(), name='state-edit'),
 | 
			
		||||
    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/<action_id>/edit', action_edit_view, name='action-edit'),
 | 
			
		||||
    path('<id>/action/<action_id>/remove', action_remove_view, name='action-remove'),
 | 
			
		||||
    path('<id>/action/new', NewEcoAccountActionView.as_view(), name='new-action'),
 | 
			
		||||
    path('<id>/action/<action_id>/edit', EditEcoAccountActionView.as_view(), name='action-edit'),
 | 
			
		||||
    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/<deadline_id>/edit', deadline_edit_view, name='deadline-edit'),
 | 
			
		||||
    path('<id>/deadline/<deadline_id>/remove', deadline_remove_view, name='deadline-remove'),
 | 
			
		||||
    path('<id>/deadline/new', NewEcoAccountDeadlineView.as_view(), name="new-deadline"),
 | 
			
		||||
    path('<id>/deadline/<deadline_id>/edit', EditEcoAccountDeadlineView.as_view(), name='deadline-edit'),
 | 
			
		||||
    path('<id>/deadline/<deadline_id>/remove', RemoveEcoAccountDeadlineView.as_view(), name='deadline-remove'),
 | 
			
		||||
 | 
			
		||||
    path('<id>/share/<token>', share_view, name='share'),
 | 
			
		||||
    path('<id>/share', create_share_view, name='share-create'),
 | 
			
		||||
    path('<id>/share/<token>', EcoAccountShareByTokenView.as_view(), name='share-token'),
 | 
			
		||||
    path('<id>/share', EcoAccountShareFormView.as_view(), name='share-form'),
 | 
			
		||||
 | 
			
		||||
    # Documents
 | 
			
		||||
    path('<id>/document/new/', new_document_view, name='new-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>', get_document_view, name='get-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/edit', edit_document_view, name='edit-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
 | 
			
		||||
    path('<id>/document/new/', NewEcoAccountDocumentView.as_view(), name='new-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>', GetEcoAccountDocumentView.as_view(), name='get-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/edit', EditEcoAccountDocumentView.as_view(), name='edit-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/remove/', RemoveEcoAccountDocumentView.as_view(), name='remove-doc'),
 | 
			
		||||
 | 
			
		||||
    # Eco-account deductions
 | 
			
		||||
    path('<id>/deduction/<deduction_id>/remove', deduction_remove_view, name='remove-deduction'),
 | 
			
		||||
    path('<id>/deduction/<deduction_id>/edit', deduction_edit_view, name='edit-deduction'),
 | 
			
		||||
    path('<id>/deduct/new', new_deduction_view, name='new-deduction'),
 | 
			
		||||
    path('<id>/deduction/<deduction_id>/remove', RemoveEcoAccountDeductionView.as_view(), name='remove-deduction'),
 | 
			
		||||
    path('<id>/deduction/<deduction_id>/edit', EditEcoAccountDeductionView.as_view(), name='edit-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
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from .compensation import *
 | 
			
		||||
from .eco_account 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
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										57
									
								
								compensation/views/compensation/action.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								compensation/views/compensation/action.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										273
									
								
								compensation/views/compensation/compensation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								compensation/views/compensation/compensation.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,273 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
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_modal
 | 
			
		||||
@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"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										49
									
								
								compensation/views/compensation/deadline.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								compensation/views/compensation/deadline.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
from konova.views.deadline import AbstractRemoveDeadlineView, AbstractEditDeadlineView, AbstractNewDeadlineView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewCompensationDeadlineView(AbstractNewDeadlineView):
 | 
			
		||||
    model = Compensation
 | 
			
		||||
    redirect_url = "compensation:detail"
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										66
									
								
								compensation/views/compensation/document.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								compensation/views/compensation/document.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										24
									
								
								compensation/views/compensation/log.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								compensation/views/compensation/log.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: 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, login_required_modal
 | 
			
		||||
from konova.views.log import AbstractLogView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CompensationLogView(AbstractLogView):
 | 
			
		||||
    model = Compensation
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										26
									
								
								compensation/views/compensation/resubmission.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								compensation/views/compensation/resubmission.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										50
									
								
								compensation/views/compensation/state.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								compensation/views/compensation/state.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
 | 
			
		||||
    AbstractRemoveCompensationStateView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewCompensationStateView(AbstractNewCompensationStateView):
 | 
			
		||||
    model = Compensation
 | 
			
		||||
    redirect_url = "compensation:detail"
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										50
									
								
								compensation/views/eco_account/action.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								compensation/views/eco_account/action.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
 | 
			
		||||
    AbstractRemoveCompensationActionView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewEcoAccountActionView(AbstractNewCompensationActionView):
 | 
			
		||||
    model = EcoAccount
 | 
			
		||||
    redirect_url = "compensation:acc:detail"
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										49
									
								
								compensation/views/eco_account/deadline.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								compensation/views/eco_account/deadline.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
from konova.views.deadline import AbstractNewDeadlineView, AbstractEditDeadlineView, AbstractRemoveDeadlineView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewEcoAccountDeadlineView(AbstractNewDeadlineView):
 | 
			
		||||
    model = EcoAccount
 | 
			
		||||
    redirect_url = "compensation:acc:detail"
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										58
									
								
								compensation/views/eco_account/deduction.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								compensation/views/eco_account/deduction.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
from konova.views.deduction import AbstractNewDeductionView, AbstractEditDeductionView, AbstractRemoveDeductionView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewEcoAccountDeductionView(AbstractNewDeductionView):
 | 
			
		||||
    model = EcoAccount
 | 
			
		||||
    redirect_url = "compensation:acc:detail"
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @method_decorator(login_required)
 | 
			
		||||
    @method_decorator(default_group_required)
 | 
			
		||||
    def dispatch(self, request, *args, **kwargs):
 | 
			
		||||
        return super().dispatch(request, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										69
									
								
								compensation/views/eco_account/document.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								compensation/views/eco_account/document.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,69 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
from konova.forms.modals import EditDocumentModalForm
 | 
			
		||||
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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										268
									
								
								compensation/views/eco_account/eco_account.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								compensation/views/eco_account/eco_account.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,268 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
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, \
 | 
			
		||||
    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 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_modal
 | 
			
		||||
@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"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								compensation/views/eco_account/log.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								compensation/views/eco_account/log.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: 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, login_required_modal
 | 
			
		||||
from konova.views.log import AbstractLogView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EcoAccountLogView(AbstractLogView):
 | 
			
		||||
    model = EcoAccount
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										24
									
								
								compensation/views/eco_account/record.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								compensation/views/eco_account/record.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: 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, login_required_modal
 | 
			
		||||
from konova.views.record import AbstractRecordView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EcoAccountRecordView(AbstractRecordView):
 | 
			
		||||
    model = EcoAccount
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										26
									
								
								compensation/views/eco_account/resubmission.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								compensation/views/eco_account/resubmission.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										33
									
								
								compensation/views/eco_account/share.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								compensation/views/eco_account/share.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										50
									
								
								compensation/views/eco_account/state.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								compensation/views/eco_account/state.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
 | 
			
		||||
    AbstractRemoveCompensationStateView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewEcoAccountStateView(AbstractNewCompensationStateView):
 | 
			
		||||
    model = EcoAccount
 | 
			
		||||
    redirect_url = "compensation:acc:detail"
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.http import HttpRequest
 | 
			
		||||
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 intervention.models import Intervention
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from compensation.filters import EcoAccountTableFilter
 | 
			
		||||
from compensation.filters.eco_account import EcoAccountTableFilter
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EmaTableFilter(EcoAccountTableFilter):
 | 
			
		||||
 | 
			
		||||
@ -10,8 +10,8 @@ from django.db import transaction
 | 
			
		||||
from django.urls import reverse, reverse_lazy
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from compensation.forms.forms import AbstractCompensationForm, CompensationResponsibleFormMixin, \
 | 
			
		||||
    PikCompensationFormMixin
 | 
			
		||||
from compensation.forms.mixins import CompensationResponsibleFormMixin, PikCompensationFormMixin
 | 
			
		||||
from compensation.forms.compensation import AbstractCompensationForm
 | 
			
		||||
from ema.models import Ema, EmaDocument
 | 
			
		||||
from intervention.models import Responsibility, Handler
 | 
			
		||||
from konova.forms import SimpleGeomForm
 | 
			
		||||
 | 
			
		||||
@ -103,7 +103,7 @@ class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin, Pik
 | 
			
		||||
        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):
 | 
			
		||||
 | 
			
		||||
@ -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 %}">
 | 
			
		||||
            {% fa5_icon 'bell' %}
 | 
			
		||||
        </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' %}
 | 
			
		||||
        </button>
 | 
			
		||||
        {% if is_ets_member %}
 | 
			
		||||
 | 
			
		||||
@ -49,8 +49,8 @@ class EmaViewTestCase(CompensationViewTestCase):
 | 
			
		||||
        self.log_url = reverse("ema:log", 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.share_url = reverse("ema:share", args=(self.ema.id, self.ema.access_token,))
 | 
			
		||||
        self.share_create_url = reverse("ema:share-create", args=(self.ema.id,))
 | 
			
		||||
        self.share_url = reverse("ema:share-token", args=(self.ema.id, self.ema.access_token,))
 | 
			
		||||
        self.share_create_url = reverse("ema:share-form", 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.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 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"
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
@ -14,32 +24,32 @@ urlpatterns = [
 | 
			
		||||
    path("new/", new_view, name="new"),
 | 
			
		||||
    path("new/id", new_id_view, name="new-id"),
 | 
			
		||||
    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>/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>/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/<state_id>/remove', state_remove_view, name='state-remove'),
 | 
			
		||||
    path('<id>/state/<state_id>/edit', state_edit_view, name='state-edit'),
 | 
			
		||||
    path('<id>/state/new', NewEmaStateView.as_view(), name='new-state'),
 | 
			
		||||
    path('<id>/state/<state_id>/remove', RemoveEmaStateView.as_view(), name='state-remove'),
 | 
			
		||||
    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/<action_id>/edit', action_edit_view, name='action-edit'),
 | 
			
		||||
    path('<id>/action/<action_id>/remove', action_remove_view, name='action-remove'),
 | 
			
		||||
    path('<id>/action/new', NewEmaActionView.as_view(), name='new-action'),
 | 
			
		||||
    path('<id>/action/<action_id>/edit', EditEmaActionView.as_view(), name='action-edit'),
 | 
			
		||||
    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/<deadline_id>/edit', deadline_edit_view, name='deadline-edit'),
 | 
			
		||||
    path('<id>/deadline/<deadline_id>/remove', deadline_remove_view, name='deadline-remove'),
 | 
			
		||||
    path('<id>/deadline/new', NewEmaDeadlineView.as_view(), name="new-deadline"),
 | 
			
		||||
    path('<id>/deadline/<deadline_id>/edit', EditEmaDeadlineView.as_view(), name='deadline-edit'),
 | 
			
		||||
    path('<id>/deadline/<deadline_id>/remove', RemoveEmaDeadlineView.as_view(), name='deadline-remove'),
 | 
			
		||||
 | 
			
		||||
    path('<id>/share/<token>', share_view, name='share'),
 | 
			
		||||
    path('<id>/share', create_share_view, name='share-create'),
 | 
			
		||||
    path('<id>/share/<token>', EmaShareByTokenView.as_view(), name='share-token'),
 | 
			
		||||
    path('<id>/share', EmaShareFormView.as_view(), name='share-form'),
 | 
			
		||||
 | 
			
		||||
    # Documents
 | 
			
		||||
    path('<id>/document/new/', document_new_view, name='new-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>', get_document_view, name='get-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/edit/', edit_document_view, name='edit-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
 | 
			
		||||
    path('<id>/document/new/', NewEmaDocumentView.as_view(), name='new-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>', GetEmaDocumentView.as_view(), name='get-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/edit/', EditEmaDocumentView.as_view(), name='edit-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
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
							
								
								
									
										50
									
								
								ema/views/action.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								ema/views/action.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
from konova.views.action import AbstractNewCompensationActionView, AbstractEditCompensationActionView, \
 | 
			
		||||
    AbstractRemoveCompensationActionView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewEmaActionView(AbstractNewCompensationActionView):
 | 
			
		||||
    model = Ema
 | 
			
		||||
    redirect_url = "ema:detail"
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										50
									
								
								ema/views/deadline.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								ema/views/deadline.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
from konova.views.deadline import AbstractNewDeadlineView, AbstractRemoveDeadlineView, AbstractEditDeadlineView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewEmaDeadlineView(AbstractNewDeadlineView):
 | 
			
		||||
    model = Ema
 | 
			
		||||
    redirect_url = "ema:detail"
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								ema/views/document.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								ema/views/document.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										238
									
								
								ema/views/ema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								ema/views/ema.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: 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, login_required_modal
 | 
			
		||||
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_modal
 | 
			
		||||
@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"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								ema/views/log.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								ema/views/log.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: 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, login_required_modal
 | 
			
		||||
from konova.views.log import AbstractLogView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EmaLogView(AbstractLogView):
 | 
			
		||||
    model = Ema
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										24
									
								
								ema/views/record.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								ema/views/record.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: 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, login_required_modal
 | 
			
		||||
from konova.views.record import AbstractRecordView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EmaRecordView(AbstractRecordView):
 | 
			
		||||
    model = Ema
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										26
									
								
								ema/views/resubmission.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								ema/views/resubmission.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										33
									
								
								ema/views/share.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								ema/views/share.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
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_modal)
 | 
			
		||||
    @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)
 | 
			
		||||
							
								
								
									
										50
									
								
								ema/views/state.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								ema/views/state.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
"""
 | 
			
		||||
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, login_required_modal
 | 
			
		||||
from konova.views.state import AbstractNewCompensationStateView, AbstractEditCompensationStateView, \
 | 
			
		||||
    AbstractRemoveCompensationStateView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewEmaStateView(AbstractNewCompensationStateView):
 | 
			
		||||
    model = Ema
 | 
			
		||||
    redirect_url = "ema:detail"
 | 
			
		||||
 | 
			
		||||
    @method_decorator(login_required_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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_modal)
 | 
			
		||||
    @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],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-process-type-autocomplete",
 | 
			
		||||
            url="codelist:process-type-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Click for selection"),
 | 
			
		||||
            }
 | 
			
		||||
@ -77,7 +77,7 @@ class NewInterventionForm(BaseForm):
 | 
			
		||||
            code_lists__in=[CODELIST_LAW_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2Multiple(
 | 
			
		||||
            url="codes-law-autocomplete",
 | 
			
		||||
            url="codelist:law-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Click for selection"),
 | 
			
		||||
            }
 | 
			
		||||
@ -93,7 +93,7 @@ class NewInterventionForm(BaseForm):
 | 
			
		||||
            code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-registration-office-autocomplete",
 | 
			
		||||
            url="codelist:registration-office-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Click for selection"),
 | 
			
		||||
            }
 | 
			
		||||
@ -109,7 +109,7 @@ class NewInterventionForm(BaseForm):
 | 
			
		||||
            code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-conservation-office-autocomplete",
 | 
			
		||||
            url="codelist:conservation-office-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Click for selection"),
 | 
			
		||||
            }
 | 
			
		||||
@ -150,7 +150,7 @@ class NewInterventionForm(BaseForm):
 | 
			
		||||
            code_lists__in=[CODELIST_HANDLER_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-handler-autocomplete",
 | 
			
		||||
            url="codelist:handler-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Click for selection"),
 | 
			
		||||
            }
 | 
			
		||||
@ -372,7 +372,6 @@ class EditInterventionForm(NewInterventionForm):
 | 
			
		||||
 | 
			
		||||
            geometry = geom_form.save(user_action)
 | 
			
		||||
            self.instance.geometry = geometry
 | 
			
		||||
            self.instance.geometry.save()
 | 
			
		||||
 | 
			
		||||
            self.instance.log.add(user_action)
 | 
			
		||||
 | 
			
		||||
@ -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:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        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):
 | 
			
		||||
        """ 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 %}">
 | 
			
		||||
            {% fa5_icon 'bell' %}
 | 
			
		||||
        </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' %}
 | 
			
		||||
        </button>
 | 
			
		||||
        {% if is_zb_member %}
 | 
			
		||||
 | 
			
		||||
@ -31,8 +31,8 @@ class InterventionViewTestCase(BaseViewTestCase):
 | 
			
		||||
        self.log_url = reverse("intervention:log", 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.share_url = reverse("intervention:share", args=(self.intervention.id, self.intervention.access_token,))
 | 
			
		||||
        self.share_create_url = reverse("intervention:share-create", args=(self.intervention.id,))
 | 
			
		||||
        self.share_url = reverse("intervention:share-token", args=(self.intervention.id, self.intervention.access_token,))
 | 
			
		||||
        self.share_create_url = reverse("intervention:share-form", 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.report_url = reverse("intervention:report", args=(self.intervention.id,))
 | 
			
		||||
 | 
			
		||||
@ -7,11 +7,21 @@ Created on: 30.11.20
 | 
			
		||||
"""
 | 
			
		||||
from django.urls import path
 | 
			
		||||
 | 
			
		||||
from intervention.views import index_view, new_view, detail_view, edit_view, remove_view, new_document_view, share_view, \
 | 
			
		||||
    create_share_view, remove_revocation_view, new_revocation_view, check_view, log_view, new_deduction_view, \
 | 
			
		||||
    record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view, report_view, \
 | 
			
		||||
    remove_deduction_view, remove_compensation_view, edit_deduction_view, edit_revocation_view, edit_document_view, \
 | 
			
		||||
    create_resubmission_view
 | 
			
		||||
from intervention.autocomplete.intervention import InterventionAutocomplete
 | 
			
		||||
from intervention.views.check import check_view
 | 
			
		||||
from intervention.views.compensation import remove_compensation_view
 | 
			
		||||
from intervention.views.deduction import NewInterventionDeductionView, EditInterventionDeductionView, \
 | 
			
		||||
    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"
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
@ -19,33 +29,36 @@ urlpatterns = [
 | 
			
		||||
    path('new/', new_view, name='new'),
 | 
			
		||||
    path('new/id', new_id_view, name='new-id'),
 | 
			
		||||
    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>/remove', remove_view, name='remove'),
 | 
			
		||||
    path('<id>/share/<token>', share_view, name='share'),
 | 
			
		||||
    path('<id>/share', create_share_view, name='share-create'),
 | 
			
		||||
    path('<id>/share/<token>', InterventionShareByTokenView.as_view(), name='share-token'),
 | 
			
		||||
    path('<id>/share', InterventionShareFormView.as_view(), name='share-form'),
 | 
			
		||||
    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>/resub', create_resubmission_view, name='resubmission-create'),
 | 
			
		||||
    path('<id>/resub', InterventionResubmissionView.as_view(), name='resubmission-create'),
 | 
			
		||||
 | 
			
		||||
    # Compensations
 | 
			
		||||
    path('<id>/compensation/<comp_id>/remove', remove_compensation_view, name='remove-compensation'),
 | 
			
		||||
 | 
			
		||||
    # Documents
 | 
			
		||||
    path('<id>/document/new/', new_document_view, name='new-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>', get_document_view, name='get-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/edit/', edit_document_view, name='edit-doc'),
 | 
			
		||||
    path('<id>/document/new/', NewInterventionDocumentView.as_view(), name='new-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>', GetInterventionDocumentView.as_view(), name='get-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/remove/', RemoveInterventionDocumentView.as_view(), name='remove-doc'),
 | 
			
		||||
    path('<id>/document/<doc_id>/edit/', EditInterventionDocumentView.as_view(), name='edit-doc'),
 | 
			
		||||
 | 
			
		||||
    # Deductions
 | 
			
		||||
    path('<id>/deduction/new', new_deduction_view, name='new-deduction'),
 | 
			
		||||
    path('<id>/deduction/<deduction_id>/edit', edit_deduction_view, name='edit-deduction'),
 | 
			
		||||
    path('<id>/deduction/<deduction_id>/remove', remove_deduction_view, name='remove-deduction'),
 | 
			
		||||
    path('<id>/deduction/new', NewInterventionDeductionView.as_view(), name='new-deduction'),
 | 
			
		||||
    path('<id>/deduction/<deduction_id>/edit', EditInterventionDeductionView.as_view(), name='edit-deduction'),
 | 
			
		||||
    path('<id>/deduction/<deduction_id>/remove', RemoveInterventionDeductionView.as_view(), name='remove-deduction'),
 | 
			
		||||
 | 
			
		||||
    # Revocation routes
 | 
			
		||||
    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>/remove', remove_revocation_view, name='remove-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
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										46
									
								
								intervention/views/compensation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								intervention/views/compensation.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.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, login_required_modal
 | 
			
		||||
from konova.forms.modals import RemoveModalForm
 | 
			
		||||
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@login_required_modal
 | 
			
		||||
@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)
 | 
			
		||||
							
								
								
									
										252
									
								
								intervention/views/intervention.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								intervention/views/intervention.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: 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, login_required_modal
 | 
			
		||||
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_modal
 | 
			
		||||
@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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user