#7 New forms
* refactors large forms.py into forms/modalForms.py and forms/forms.py * refactors custom input fields into intervention/inputs.py
This commit is contained in:
		
							parent
							
								
									58eefbdbe6
								
							
						
					
					
						commit
						fdb6f2d105
					
				@ -16,7 +16,7 @@ from django.shortcuts import render, get_object_or_404
 | 
			
		||||
from compensation.forms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm
 | 
			
		||||
from compensation.models import EcoAccount, EcoAccountDocument
 | 
			
		||||
from compensation.tables import EcoAccountTable
 | 
			
		||||
from intervention.forms import NewDeductionForm
 | 
			
		||||
from intervention.forms.modalForms import NewDeductionModalForm
 | 
			
		||||
from konova.contexts import BaseContext
 | 
			
		||||
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required
 | 
			
		||||
from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm
 | 
			
		||||
@ -340,7 +340,7 @@ def new_deduction_view(request: HttpRequest, id: str):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    acc = get_object_or_404(EcoAccount, id=id)
 | 
			
		||||
    form = NewDeductionForm(request.POST or None, instance=acc, user=request.user)
 | 
			
		||||
    form = NewDeductionModalForm(request.POST or None, instance=acc, user=request.user)
 | 
			
		||||
    return form.process_request(
 | 
			
		||||
        request,
 | 
			
		||||
        msg_success=_("Deduction added")
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ from django.db.models import QuerySet, Q
 | 
			
		||||
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from intervention.forms import DummyFilterInput
 | 
			
		||||
from intervention.inputs import DummyFilterInput
 | 
			
		||||
from intervention.models import Intervention
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,744 +0,0 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 02.12.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from dal import autocomplete
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.contrib.gis.geos import Polygon
 | 
			
		||||
from django.db import transaction
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from codelist.models import KonovaCode
 | 
			
		||||
from codelist.settings import CODELIST_PROCESS_TYPE_ID, CODELIST_LAW_ID, \
 | 
			
		||||
    CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID
 | 
			
		||||
from compensation.models import EcoAccountDeduction, EcoAccount
 | 
			
		||||
from intervention.models import Intervention, Revocation, RevocationDocument, LegalData, ResponsibilityData
 | 
			
		||||
from konova.forms import BaseForm, BaseModalForm, SimpleGeomForm
 | 
			
		||||
from konova.settings import ZB_GROUP, ETS_GROUP
 | 
			
		||||
from konova.utils.general import format_german_float
 | 
			
		||||
from konova.utils.messenger import Messenger
 | 
			
		||||
from konova.utils.user_checks import in_group
 | 
			
		||||
from user.models import UserActionLogEntry, UserAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewInterventionForm(BaseForm):
 | 
			
		||||
    identifier = forms.CharField(
 | 
			
		||||
        label=_("Identifier"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        help_text=_("Generated automatically"),
 | 
			
		||||
    )
 | 
			
		||||
    title = forms.CharField(
 | 
			
		||||
        label=_("Title"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("An explanatory name"),
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        widget=forms.TextInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "placeholder": _("Construction XY; Location ABC")
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    type = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Process type"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_(""),
 | 
			
		||||
        required=False,
 | 
			
		||||
        queryset=KonovaCode.objects.filter(
 | 
			
		||||
            is_archived=False,
 | 
			
		||||
            is_leaf=True,
 | 
			
		||||
            code_lists__in=[CODELIST_PROCESS_TYPE_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-process-type-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    laws = forms.ModelMultipleChoiceField(
 | 
			
		||||
        label=_("Law"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("Multiple selection possible"),
 | 
			
		||||
        required=False,
 | 
			
		||||
        queryset=KonovaCode.objects.filter(
 | 
			
		||||
            is_archived=False,
 | 
			
		||||
            is_leaf=True,
 | 
			
		||||
            code_lists__in=[CODELIST_LAW_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2Multiple(
 | 
			
		||||
            url="codes-law-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    registration_office = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Registration office"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        required=False,
 | 
			
		||||
        queryset=KonovaCode.objects.filter(
 | 
			
		||||
            is_archived=False,
 | 
			
		||||
            is_leaf=True,
 | 
			
		||||
            code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-registration-office-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    conservation_office = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Conservation office"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        required=False,
 | 
			
		||||
        queryset=KonovaCode.objects.filter(
 | 
			
		||||
            is_archived=False,
 | 
			
		||||
            is_leaf=True,
 | 
			
		||||
            code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-conservation-office-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    registration_file_number = forms.CharField(
 | 
			
		||||
        label=_("Registration office file number"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        required=False,
 | 
			
		||||
        widget=forms.TextInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "placeholder": _("ZB-123/ABC.456")
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    conservation_file_number = forms.CharField(
 | 
			
		||||
        label=_("Conservation office file number"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        required=False,
 | 
			
		||||
        widget=forms.TextInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "placeholder": _("ETS-123/ABC.456")
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    handler = forms.CharField(
 | 
			
		||||
        label=_("Intervention handler"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        required=False,
 | 
			
		||||
        help_text=_("Who performs the intervention"),
 | 
			
		||||
        widget=forms.TextInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "placeholder": _("Company Mustermann")
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    registration_date = forms.DateField(
 | 
			
		||||
        label=_("Registration date"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=False,
 | 
			
		||||
        widget=forms.DateInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "type": "date",
 | 
			
		||||
            },
 | 
			
		||||
            format="%d.%m.%Y"
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    binding_date = forms.DateField(
 | 
			
		||||
        label=_("Binding on"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=False,
 | 
			
		||||
        widget=forms.DateInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "type": "date",
 | 
			
		||||
            },
 | 
			
		||||
            format="%d.%m.%Y"
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    comment = forms.CharField(
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        label=_("Comment"),
 | 
			
		||||
        required=False,
 | 
			
		||||
        help_text=_("Additional comment"),
 | 
			
		||||
        widget=forms.Textarea(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "rows": 5,
 | 
			
		||||
                "class": "w-100"
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Define w-100 for all form fields
 | 
			
		||||
    full_width_fields = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("New intervention")
 | 
			
		||||
        self.action_url = reverse("intervention:new")
 | 
			
		||||
        self.cancel_redirect = reverse("intervention:index")
 | 
			
		||||
 | 
			
		||||
        tmp_intervention = Intervention()
 | 
			
		||||
        identifier = tmp_intervention._generate_new_identifier()
 | 
			
		||||
        self.initialize_form_field("identifier", identifier)
 | 
			
		||||
 | 
			
		||||
    def save(self, user: User, geom_form: SimpleGeomForm):
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            # Fetch data from cleaned POST values
 | 
			
		||||
            identifier = self.cleaned_data.get("identifier", None)
 | 
			
		||||
            title = self.cleaned_data.get("title", None)
 | 
			
		||||
            _type = self.cleaned_data.get("type", None)
 | 
			
		||||
            laws = self.cleaned_data.get("laws", None)
 | 
			
		||||
            handler = self.cleaned_data.get("handler", None)
 | 
			
		||||
            registration_office = self.cleaned_data.get("registration_office", None)
 | 
			
		||||
            conservation_office = self.cleaned_data.get("conservation_office", None)
 | 
			
		||||
            conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
 | 
			
		||||
            registration_file_number = self.cleaned_data.get("registration_file_number", None)
 | 
			
		||||
            binding_date = self.cleaned_data.get("binding_date", None)
 | 
			
		||||
            registration_date = self.cleaned_data.get("registration_date", None)
 | 
			
		||||
            comment = self.cleaned_data.get("comment", None)
 | 
			
		||||
 | 
			
		||||
            # Create log entry
 | 
			
		||||
            action = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=user,
 | 
			
		||||
                action=UserAction.CREATED,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # Create legal data object (without M2M laws first)
 | 
			
		||||
            legal_data = LegalData.objects.create(
 | 
			
		||||
                registration_date=registration_date,
 | 
			
		||||
                binding_date=binding_date,
 | 
			
		||||
                process_type=_type,
 | 
			
		||||
            )
 | 
			
		||||
            # Then add the M2M laws to the object
 | 
			
		||||
            legal_data.laws.set(laws)
 | 
			
		||||
 | 
			
		||||
            # Create responsible data object
 | 
			
		||||
            responsibility_data = ResponsibilityData.objects.create(
 | 
			
		||||
                registration_office=registration_office,
 | 
			
		||||
                conservation_office=conservation_office,
 | 
			
		||||
                registration_file_number=registration_file_number,
 | 
			
		||||
                conservation_file_number=conservation_file_number,
 | 
			
		||||
                handler=handler,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # Process the geometry form
 | 
			
		||||
            geometry = geom_form.save(action)
 | 
			
		||||
 | 
			
		||||
            # Finally create main object, holding the other objects
 | 
			
		||||
            intervention = Intervention.objects.create(
 | 
			
		||||
                identifier=identifier,
 | 
			
		||||
                title=title,
 | 
			
		||||
                responsible=responsibility_data,
 | 
			
		||||
                legal=legal_data,
 | 
			
		||||
                created=action,
 | 
			
		||||
                geometry=geometry,
 | 
			
		||||
                comment=comment,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # Add the log entry to the main objects log list
 | 
			
		||||
            intervention.log.add(action)
 | 
			
		||||
 | 
			
		||||
            # Add the performing user as the first user having access to the data
 | 
			
		||||
            intervention.users.add(user)
 | 
			
		||||
        return intervention
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditInterventionForm(NewInterventionForm):
 | 
			
		||||
    """ Subclasses NewInterventionForm
 | 
			
		||||
 | 
			
		||||
    Simply adds initializing of a provided self.instance object into the form fields
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        if self.instance is not None:
 | 
			
		||||
            self.action_url = reverse("intervention:edit", args=(self.instance.id,))
 | 
			
		||||
        self.cancel_redirect = reverse("intervention:open", args=(self.instance.id,))
 | 
			
		||||
        self.form_title = _("Edit intervention")
 | 
			
		||||
        self.form_caption = ""
 | 
			
		||||
 | 
			
		||||
        reg_date = self.instance.legal.registration_date
 | 
			
		||||
        bind_date = self.instance.legal.binding_date
 | 
			
		||||
        if reg_date is not None:
 | 
			
		||||
            reg_date = reg_date.isoformat()
 | 
			
		||||
        if bind_date is not None:
 | 
			
		||||
            bind_date = bind_date.isoformat()
 | 
			
		||||
 | 
			
		||||
        # Initialize form data
 | 
			
		||||
        form_data = {
 | 
			
		||||
            "identifier": self.instance.identifier,
 | 
			
		||||
            "title": self.instance.title,
 | 
			
		||||
            "type": self.instance.legal.process_type,
 | 
			
		||||
            "laws": list(self.instance.legal.laws.values_list("id", flat=True)),
 | 
			
		||||
            "handler": self.instance.responsible.handler,
 | 
			
		||||
            "registration_office": self.instance.responsible.registration_office,
 | 
			
		||||
            "registration_file_number": self.instance.responsible.registration_file_number,
 | 
			
		||||
            "conservation_office": self.instance.responsible.conservation_office,
 | 
			
		||||
            "conservation_file_number": self.instance.responsible.conservation_file_number,
 | 
			
		||||
            "registration_date": reg_date,
 | 
			
		||||
            "binding_date": bind_date,
 | 
			
		||||
            "comment": self.instance.comment,
 | 
			
		||||
        }
 | 
			
		||||
        disabled_fields = [
 | 
			
		||||
            "identifier",
 | 
			
		||||
        ]
 | 
			
		||||
        self.load_initial_data(
 | 
			
		||||
            form_data,
 | 
			
		||||
            disabled_fields,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def save(self, user: User, geom_form: SimpleGeomForm):
 | 
			
		||||
        """ Overwrite instance with new form data
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            user ():
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            identifier = self.cleaned_data.get("identifier", None)
 | 
			
		||||
            title = self.cleaned_data.get("title", None)
 | 
			
		||||
            process_type = self.cleaned_data.get("type", None)
 | 
			
		||||
            laws = self.cleaned_data.get("laws", None)
 | 
			
		||||
            handler = self.cleaned_data.get("handler", None)
 | 
			
		||||
            registration_office = self.cleaned_data.get("registration_office", None)
 | 
			
		||||
            registration_file_number = self.cleaned_data.get("registration_file_number", None)
 | 
			
		||||
            conservation_office = self.cleaned_data.get("conservation_office", None)
 | 
			
		||||
            conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
 | 
			
		||||
            registration_date = self.cleaned_data.get("registration_date", None)
 | 
			
		||||
            binding_date = self.cleaned_data.get("binding_date", None)
 | 
			
		||||
            comment = self.cleaned_data.get("comment", None)
 | 
			
		||||
 | 
			
		||||
            self.instance.legal.process_type = process_type
 | 
			
		||||
            self.instance.legal.registration_date = registration_date
 | 
			
		||||
            self.instance.legal.binding_date = binding_date
 | 
			
		||||
            self.instance.legal.laws.set(laws)
 | 
			
		||||
            self.instance.legal.save()
 | 
			
		||||
 | 
			
		||||
            self.instance.responsible.handler = handler
 | 
			
		||||
            self.instance.responsible.registration_office = registration_office
 | 
			
		||||
            self.instance.responsible.registration_file_number = registration_file_number
 | 
			
		||||
            self.instance.responsible.conservation_office = conservation_office
 | 
			
		||||
            self.instance.responsible.conservation_file_number = conservation_file_number
 | 
			
		||||
            self.instance.responsible.save()
 | 
			
		||||
 | 
			
		||||
            user_action = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=user,
 | 
			
		||||
                timestamp=timezone.now(),
 | 
			
		||||
                action=UserAction.EDITED,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            geometry = geom_form.save(user_action)
 | 
			
		||||
            self.instance.geometry = geometry
 | 
			
		||||
            self.instance.geometry.save()
 | 
			
		||||
 | 
			
		||||
            self.instance.log.add(user_action)
 | 
			
		||||
 | 
			
		||||
            self.instance.identifier = identifier
 | 
			
		||||
            self.instance.title = title
 | 
			
		||||
            self.instance.comment = comment
 | 
			
		||||
            self.instance.modified = user_action
 | 
			
		||||
            self.instance.save()
 | 
			
		||||
 | 
			
		||||
        return self.instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OpenInterventionForm(EditInterventionForm):
 | 
			
		||||
    """
 | 
			
		||||
    This form is not intended to be used as data-input form. It's used to simplify the rendering of intervention:open
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        # Resize map
 | 
			
		||||
        self.fields["geometry"].widget.attrs["map_width"] = 500
 | 
			
		||||
        self.fields["geometry"].widget.attrs["map_height"] = 300
 | 
			
		||||
 | 
			
		||||
        # Disable all form fields
 | 
			
		||||
        for field in self.fields:
 | 
			
		||||
            self.disable_form_field(field)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DummyFilterInput(forms.HiddenInput):
 | 
			
		||||
    """ A dummy input widget
 | 
			
		||||
 | 
			
		||||
    Does not render anything. Can be used to keep filter logic using django_filter without having a pre defined
 | 
			
		||||
    filter widget being rendered to the template.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    template_name = "konova/custom_widgets/dummy-filter-input.html"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TextToClipboardInput(forms.TextInput):
 | 
			
		||||
    template_name = "konova/custom_widgets/text-to-clipboard-input.html"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShareInterventionForm(BaseModalForm):
 | 
			
		||||
    url = forms.CharField(
 | 
			
		||||
        label=_("Share link"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("Send this link to users who you want to have writing access on the data"),
 | 
			
		||||
        required=False,
 | 
			
		||||
        widget=TextToClipboardInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "readonly": True
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    users = forms.MultipleChoiceField(
 | 
			
		||||
        label=_("Shared with"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        required=True,
 | 
			
		||||
        help_text=_("Remove check to remove access for this user"),
 | 
			
		||||
        widget=forms.CheckboxSelectMultiple(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "class": "list-unstyled",
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        choices=[]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("Share")
 | 
			
		||||
        self.form_caption = _("Share settings for {}").format(self.instance.identifier)
 | 
			
		||||
        self.template = "modal/modal_form.html"
 | 
			
		||||
 | 
			
		||||
        # Make sure an access_token is set
 | 
			
		||||
        if self.instance.access_token is None:
 | 
			
		||||
            self.instance.generate_access_token()
 | 
			
		||||
 | 
			
		||||
        self._init_fields()
 | 
			
		||||
 | 
			
		||||
    def _init_fields(self):
 | 
			
		||||
        """ Wraps initializing of fields
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        # Initialize share_link field
 | 
			
		||||
        self.share_link = self.request.build_absolute_uri(
 | 
			
		||||
            reverse("intervention:share", args=(self.instance.id, self.instance.access_token,))
 | 
			
		||||
        )
 | 
			
		||||
        self.initialize_form_field(
 | 
			
		||||
            "url",
 | 
			
		||||
            self.share_link
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Initialize users field
 | 
			
		||||
        # Remove field if user is not in registration or conservation group
 | 
			
		||||
        if not in_group(self.request.user, ZB_GROUP) and not in_group(self.request.user, ETS_GROUP):
 | 
			
		||||
            del self.fields["users"]
 | 
			
		||||
        else:
 | 
			
		||||
            users = self.instance.users.all()
 | 
			
		||||
            choices = []
 | 
			
		||||
            for n in users:
 | 
			
		||||
                choices.append(
 | 
			
		||||
                    (n.id, n.username)
 | 
			
		||||
                )
 | 
			
		||||
            self.fields["users"].choices = choices
 | 
			
		||||
            u_ids = list(users.values_list("id", flat=True))
 | 
			
		||||
            self.initialize_form_field(
 | 
			
		||||
                "users",
 | 
			
		||||
                u_ids
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def save(self):
 | 
			
		||||
        accessing_users = User.objects.filter(
 | 
			
		||||
            id__in=self.cleaned_data["users"]
 | 
			
		||||
        )
 | 
			
		||||
        self.instance.users.set(accessing_users)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewRevocationForm(BaseModalForm):
 | 
			
		||||
    date = forms.DateField(
 | 
			
		||||
        label=_("Date"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        help_text=_("Date of revocation"),
 | 
			
		||||
        widget=forms.DateInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "type": "date",
 | 
			
		||||
                "data-provide": "datepicker",
 | 
			
		||||
            },
 | 
			
		||||
            format="%d.%m.%Y"
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    file = forms.FileField(
 | 
			
		||||
        label=_("Document"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=False,
 | 
			
		||||
        help_text=_("Must be smaller than 15 Mb"),
 | 
			
		||||
        widget=forms.FileInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "class": "w-75"
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    comment = forms.CharField(
 | 
			
		||||
        required=False,
 | 
			
		||||
        max_length=200,
 | 
			
		||||
        label=_("Comment"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        help_text=_("Additional comment, maximum {} letters").format(200),
 | 
			
		||||
        widget=forms.Textarea(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "cols": 30,
 | 
			
		||||
                "rows": 5,
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Define w-100 for all form fields
 | 
			
		||||
    full_width_fields = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("Add revocation")
 | 
			
		||||
        self.form_caption = ""
 | 
			
		||||
        self.form_attrs = {
 | 
			
		||||
            "enctype": "multipart/form-data",  # important for file upload
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def save(self):
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            created_action = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                action=UserAction.CREATED
 | 
			
		||||
            )
 | 
			
		||||
            edited_action = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                action=UserAction.EDITED
 | 
			
		||||
            )
 | 
			
		||||
            revocation = Revocation.objects.create(
 | 
			
		||||
                date=self.cleaned_data["date"],
 | 
			
		||||
                comment=self.cleaned_data["comment"],
 | 
			
		||||
                created=created_action,
 | 
			
		||||
            )
 | 
			
		||||
            self.instance.modified = edited_action
 | 
			
		||||
            self.instance.save()
 | 
			
		||||
            self.instance.log.add(edited_action)
 | 
			
		||||
            self.instance.legal.revocation = revocation
 | 
			
		||||
            self.instance.legal.save()
 | 
			
		||||
 | 
			
		||||
            if self.cleaned_data["file"]:
 | 
			
		||||
                RevocationDocument.objects.create(
 | 
			
		||||
                    title="revocation_of_{}".format(self.instance.identifier),
 | 
			
		||||
                    date_of_creation=self.cleaned_data["date"],
 | 
			
		||||
                    comment=self.cleaned_data["comment"],
 | 
			
		||||
                    file=self.cleaned_data["file"],
 | 
			
		||||
                    instance=revocation
 | 
			
		||||
                )
 | 
			
		||||
        return revocation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RunCheckForm(BaseModalForm):
 | 
			
		||||
    checked_intervention = forms.BooleanField(
 | 
			
		||||
        label=_("Checked intervention data"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        widget=forms.CheckboxInput(),
 | 
			
		||||
        required=True,
 | 
			
		||||
    )
 | 
			
		||||
    checked_comps = forms.BooleanField(
 | 
			
		||||
        label=_("Checked compensations data and payments"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        widget=forms.CheckboxInput(),
 | 
			
		||||
        required=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("Run check")
 | 
			
		||||
        self.form_caption = _("I, {} {}, confirm that all necessary control steps have been performed by myself.").format(self.user.first_name, self.user.last_name)
 | 
			
		||||
 | 
			
		||||
    def is_valid(self) -> bool:
 | 
			
		||||
        """ Perform a validity check based on quality_check() logic
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            result (bool)
 | 
			
		||||
        """
 | 
			
		||||
        super_result = super().is_valid()
 | 
			
		||||
        # Perform check
 | 
			
		||||
        msgs = self.instance.quality_check()
 | 
			
		||||
        for msg in msgs:
 | 
			
		||||
            self.add_error(
 | 
			
		||||
                "checked_intervention",
 | 
			
		||||
                msg
 | 
			
		||||
            )
 | 
			
		||||
        return super_result and (len(msgs) == 0)
 | 
			
		||||
 | 
			
		||||
    def save(self):
 | 
			
		||||
        """ Saving logic
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            user_action = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                action=UserAction.CHECKED
 | 
			
		||||
            )
 | 
			
		||||
            # Replace old checked
 | 
			
		||||
            if self.instance.checked:
 | 
			
		||||
                self.instance.checked.delete()
 | 
			
		||||
            self.instance.checked = user_action
 | 
			
		||||
            self.instance.log.add(user_action)
 | 
			
		||||
            self.instance.save()
 | 
			
		||||
 | 
			
		||||
        # Send message to the SSO server
 | 
			
		||||
        messenger = Messenger(
 | 
			
		||||
            self.instance.users.all(),
 | 
			
		||||
            type="INFO",
 | 
			
		||||
        )
 | 
			
		||||
        messenger.send_object_checked(
 | 
			
		||||
            self.instance.identifier,
 | 
			
		||||
            self.user,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewDeductionForm(BaseModalForm):
 | 
			
		||||
    """ Form for creating new deduction
 | 
			
		||||
 | 
			
		||||
    Can be used for Intervention view as well as for EcoAccount views.
 | 
			
		||||
 | 
			
		||||
    Parameter 'instance' can be an intervention, as well as an ecoAccount.
 | 
			
		||||
    An instance check handles both workflows properly.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    account = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Eco-account"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("Only recorded accounts can be selected for deductions"),
 | 
			
		||||
        queryset=EcoAccount.objects.filter(deleted=None),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="accounts-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Eco-account"),
 | 
			
		||||
                "data-minimum-input-length": 3,
 | 
			
		||||
                "readonly": True,
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    surface = forms.DecimalField(
 | 
			
		||||
        min_value=0.00,
 | 
			
		||||
        decimal_places=2,
 | 
			
		||||
        label=_("Surface"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("in m²"),
 | 
			
		||||
    )
 | 
			
		||||
    intervention = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Intervention"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("Only shared interventions can be selected"),
 | 
			
		||||
        queryset=Intervention.objects.filter(deleted=None),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="interventions-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Intervention"),
 | 
			
		||||
                "data-minimum-input-length": 3,
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Define w-100 for all form fields
 | 
			
		||||
    full_width_fields = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("New Deduction")
 | 
			
		||||
        self.form_caption = _("Enter the information for a new deduction from a chosen eco-account")
 | 
			
		||||
        self.is_intervention_initially = False
 | 
			
		||||
 | 
			
		||||
        # Add a placeholder for field 'surface' without having to define the whole widget above
 | 
			
		||||
        self.add_placeholder_for_field("surface", "0,00")
 | 
			
		||||
 | 
			
		||||
        # Check for Intervention or EcoAccount
 | 
			
		||||
        if isinstance(self.instance, Intervention):
 | 
			
		||||
            # Form has been called with a given intervention
 | 
			
		||||
            self.initialize_form_field("intervention", self.instance)
 | 
			
		||||
            self.disable_form_field("intervention")
 | 
			
		||||
            self.is_intervention_initially = True
 | 
			
		||||
        elif isinstance(self.instance, EcoAccount):
 | 
			
		||||
            # Form has been called with a given account --> make it initial in the form and read-only
 | 
			
		||||
            self.initialize_form_field("account", self.instance)
 | 
			
		||||
            self.disable_form_field("account")
 | 
			
		||||
        else:
 | 
			
		||||
            raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
    def is_valid(self):
 | 
			
		||||
        """ Custom validity check
 | 
			
		||||
 | 
			
		||||
        Makes sure the deduction can not contain more surface than the account still provides
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            is_valid (bool)
 | 
			
		||||
        """
 | 
			
		||||
        super_result = super().is_valid()
 | 
			
		||||
        if self.is_intervention_initially:
 | 
			
		||||
            acc = self.cleaned_data["account"]
 | 
			
		||||
        else:
 | 
			
		||||
            acc = self.instance
 | 
			
		||||
 | 
			
		||||
        if not acc.recorded:
 | 
			
		||||
            self.add_error(
 | 
			
		||||
                "account",
 | 
			
		||||
                _("Eco-account {} is not recorded yet. You can only deduct from recorded accounts.").format(acc.identifier)
 | 
			
		||||
            )
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        # Calculate valid surface
 | 
			
		||||
        sum_surface = acc.get_surface()
 | 
			
		||||
        sum_surface_deductions = acc.get_deductions_surface()
 | 
			
		||||
        rest_surface = sum_surface - sum_surface_deductions
 | 
			
		||||
        form_surface = float(self.cleaned_data["surface"])
 | 
			
		||||
        is_valid_surface = form_surface < rest_surface
 | 
			
		||||
        if not is_valid_surface:
 | 
			
		||||
            self.add_error(
 | 
			
		||||
                "surface",
 | 
			
		||||
                _("The account {} has not enough surface for a deduction of {} m². There are only {} m² left").format(
 | 
			
		||||
                    acc.identifier,
 | 
			
		||||
                    format_german_float(form_surface),
 | 
			
		||||
                    format_german_float(rest_surface),
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
        return is_valid_surface and super_result
 | 
			
		||||
 | 
			
		||||
    def save(self):
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            # Create log entry
 | 
			
		||||
            user_action_edit = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                action=UserAction.EDITED
 | 
			
		||||
            )
 | 
			
		||||
            user_action_create = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                action=UserAction.CREATED
 | 
			
		||||
            )
 | 
			
		||||
            self.instance.log.add(user_action_edit)
 | 
			
		||||
            self.instance.modified = user_action_edit
 | 
			
		||||
            self.instance.save()
 | 
			
		||||
 | 
			
		||||
            # Create deductions depending on Intervention or EcoAccount as the initial instance
 | 
			
		||||
            if self.is_intervention_initially:
 | 
			
		||||
                deduction = EcoAccountDeduction.objects.create(
 | 
			
		||||
                    intervention=self.instance,
 | 
			
		||||
                    account=self.cleaned_data["account"],
 | 
			
		||||
                    surface=self.cleaned_data["surface"],
 | 
			
		||||
                    created=user_action_create,
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                deduction = EcoAccountDeduction.objects.create(
 | 
			
		||||
                    intervention=self.cleaned_data["intervention"],
 | 
			
		||||
                    account=self.instance,
 | 
			
		||||
                    surface=self.cleaned_data["surface"],
 | 
			
		||||
                    created=user_action_create,
 | 
			
		||||
                )
 | 
			
		||||
        return deduction
 | 
			
		||||
							
								
								
									
										362
									
								
								intervention/forms/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								intervention/forms/forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,362 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 02.12.20
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from dal import autocomplete
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.db import transaction
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from codelist.models import KonovaCode
 | 
			
		||||
from codelist.settings import CODELIST_PROCESS_TYPE_ID, CODELIST_LAW_ID, \
 | 
			
		||||
    CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID
 | 
			
		||||
from intervention.models import Intervention, LegalData, ResponsibilityData
 | 
			
		||||
from konova.forms import BaseForm, SimpleGeomForm
 | 
			
		||||
from user.models import UserActionLogEntry, UserAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewInterventionForm(BaseForm):
 | 
			
		||||
    identifier = forms.CharField(
 | 
			
		||||
        label=_("Identifier"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        help_text=_("Generated automatically"),
 | 
			
		||||
    )
 | 
			
		||||
    title = forms.CharField(
 | 
			
		||||
        label=_("Title"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("An explanatory name"),
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        widget=forms.TextInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "placeholder": _("Construction XY; Location ABC")
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    type = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Process type"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_(""),
 | 
			
		||||
        required=False,
 | 
			
		||||
        queryset=KonovaCode.objects.filter(
 | 
			
		||||
            is_archived=False,
 | 
			
		||||
            is_leaf=True,
 | 
			
		||||
            code_lists__in=[CODELIST_PROCESS_TYPE_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-process-type-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    laws = forms.ModelMultipleChoiceField(
 | 
			
		||||
        label=_("Law"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("Multiple selection possible"),
 | 
			
		||||
        required=False,
 | 
			
		||||
        queryset=KonovaCode.objects.filter(
 | 
			
		||||
            is_archived=False,
 | 
			
		||||
            is_leaf=True,
 | 
			
		||||
            code_lists__in=[CODELIST_LAW_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2Multiple(
 | 
			
		||||
            url="codes-law-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    registration_office = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Registration office"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        required=False,
 | 
			
		||||
        queryset=KonovaCode.objects.filter(
 | 
			
		||||
            is_archived=False,
 | 
			
		||||
            is_leaf=True,
 | 
			
		||||
            code_lists__in=[CODELIST_REGISTRATION_OFFICE_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-registration-office-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    conservation_office = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Conservation office"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        required=False,
 | 
			
		||||
        queryset=KonovaCode.objects.filter(
 | 
			
		||||
            is_archived=False,
 | 
			
		||||
            is_leaf=True,
 | 
			
		||||
            code_lists__in=[CODELIST_CONSERVATION_OFFICE_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-conservation-office-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    registration_file_number = forms.CharField(
 | 
			
		||||
        label=_("Registration office file number"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        required=False,
 | 
			
		||||
        widget=forms.TextInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "placeholder": _("ZB-123/ABC.456")
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    conservation_file_number = forms.CharField(
 | 
			
		||||
        label=_("Conservation office file number"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        required=False,
 | 
			
		||||
        widget=forms.TextInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "placeholder": _("ETS-123/ABC.456")
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    handler = forms.CharField(
 | 
			
		||||
        label=_("Intervention handler"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        max_length=255,
 | 
			
		||||
        required=False,
 | 
			
		||||
        help_text=_("Who performs the intervention"),
 | 
			
		||||
        widget=forms.TextInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "placeholder": _("Company Mustermann")
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    registration_date = forms.DateField(
 | 
			
		||||
        label=_("Registration date"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=False,
 | 
			
		||||
        widget=forms.DateInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "type": "date",
 | 
			
		||||
            },
 | 
			
		||||
            format="%d.%m.%Y"
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    binding_date = forms.DateField(
 | 
			
		||||
        label=_("Binding on"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=False,
 | 
			
		||||
        widget=forms.DateInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "type": "date",
 | 
			
		||||
            },
 | 
			
		||||
            format="%d.%m.%Y"
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    comment = forms.CharField(
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        label=_("Comment"),
 | 
			
		||||
        required=False,
 | 
			
		||||
        help_text=_("Additional comment"),
 | 
			
		||||
        widget=forms.Textarea(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "rows": 5,
 | 
			
		||||
                "class": "w-100"
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Define w-100 for all form fields
 | 
			
		||||
    full_width_fields = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("New intervention")
 | 
			
		||||
        self.action_url = reverse("intervention:new")
 | 
			
		||||
        self.cancel_redirect = reverse("intervention:index")
 | 
			
		||||
 | 
			
		||||
        tmp_intervention = Intervention()
 | 
			
		||||
        identifier = tmp_intervention._generate_new_identifier()
 | 
			
		||||
        self.initialize_form_field("identifier", identifier)
 | 
			
		||||
 | 
			
		||||
    def save(self, user: User, geom_form: SimpleGeomForm):
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            # Fetch data from cleaned POST values
 | 
			
		||||
            identifier = self.cleaned_data.get("identifier", None)
 | 
			
		||||
            title = self.cleaned_data.get("title", None)
 | 
			
		||||
            _type = self.cleaned_data.get("type", None)
 | 
			
		||||
            laws = self.cleaned_data.get("laws", None)
 | 
			
		||||
            handler = self.cleaned_data.get("handler", None)
 | 
			
		||||
            registration_office = self.cleaned_data.get("registration_office", None)
 | 
			
		||||
            conservation_office = self.cleaned_data.get("conservation_office", None)
 | 
			
		||||
            conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
 | 
			
		||||
            registration_file_number = self.cleaned_data.get("registration_file_number", None)
 | 
			
		||||
            binding_date = self.cleaned_data.get("binding_date", None)
 | 
			
		||||
            registration_date = self.cleaned_data.get("registration_date", None)
 | 
			
		||||
            comment = self.cleaned_data.get("comment", None)
 | 
			
		||||
 | 
			
		||||
            # Create log entry
 | 
			
		||||
            action = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=user,
 | 
			
		||||
                action=UserAction.CREATED,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # Create legal data object (without M2M laws first)
 | 
			
		||||
            legal_data = LegalData.objects.create(
 | 
			
		||||
                registration_date=registration_date,
 | 
			
		||||
                binding_date=binding_date,
 | 
			
		||||
                process_type=_type,
 | 
			
		||||
            )
 | 
			
		||||
            # Then add the M2M laws to the object
 | 
			
		||||
            legal_data.laws.set(laws)
 | 
			
		||||
 | 
			
		||||
            # Create responsible data object
 | 
			
		||||
            responsibility_data = ResponsibilityData.objects.create(
 | 
			
		||||
                registration_office=registration_office,
 | 
			
		||||
                conservation_office=conservation_office,
 | 
			
		||||
                registration_file_number=registration_file_number,
 | 
			
		||||
                conservation_file_number=conservation_file_number,
 | 
			
		||||
                handler=handler,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # Process the geometry form
 | 
			
		||||
            geometry = geom_form.save(action)
 | 
			
		||||
 | 
			
		||||
            # Finally create main object, holding the other objects
 | 
			
		||||
            intervention = Intervention.objects.create(
 | 
			
		||||
                identifier=identifier,
 | 
			
		||||
                title=title,
 | 
			
		||||
                responsible=responsibility_data,
 | 
			
		||||
                legal=legal_data,
 | 
			
		||||
                created=action,
 | 
			
		||||
                geometry=geometry,
 | 
			
		||||
                comment=comment,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            # Add the log entry to the main objects log list
 | 
			
		||||
            intervention.log.add(action)
 | 
			
		||||
 | 
			
		||||
            # Add the performing user as the first user having access to the data
 | 
			
		||||
            intervention.users.add(user)
 | 
			
		||||
        return intervention
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EditInterventionForm(NewInterventionForm):
 | 
			
		||||
    """ Subclasses NewInterventionForm
 | 
			
		||||
 | 
			
		||||
    Simply adds initializing of a provided self.instance object into the form fields
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        if self.instance is not None:
 | 
			
		||||
            self.action_url = reverse("intervention:edit", args=(self.instance.id,))
 | 
			
		||||
        self.cancel_redirect = reverse("intervention:open", args=(self.instance.id,))
 | 
			
		||||
        self.form_title = _("Edit intervention")
 | 
			
		||||
        self.form_caption = ""
 | 
			
		||||
 | 
			
		||||
        reg_date = self.instance.legal.registration_date
 | 
			
		||||
        bind_date = self.instance.legal.binding_date
 | 
			
		||||
        if reg_date is not None:
 | 
			
		||||
            reg_date = reg_date.isoformat()
 | 
			
		||||
        if bind_date is not None:
 | 
			
		||||
            bind_date = bind_date.isoformat()
 | 
			
		||||
 | 
			
		||||
        # Initialize form data
 | 
			
		||||
        form_data = {
 | 
			
		||||
            "identifier": self.instance.identifier,
 | 
			
		||||
            "title": self.instance.title,
 | 
			
		||||
            "type": self.instance.legal.process_type,
 | 
			
		||||
            "laws": list(self.instance.legal.laws.values_list("id", flat=True)),
 | 
			
		||||
            "handler": self.instance.responsible.handler,
 | 
			
		||||
            "registration_office": self.instance.responsible.registration_office,
 | 
			
		||||
            "registration_file_number": self.instance.responsible.registration_file_number,
 | 
			
		||||
            "conservation_office": self.instance.responsible.conservation_office,
 | 
			
		||||
            "conservation_file_number": self.instance.responsible.conservation_file_number,
 | 
			
		||||
            "registration_date": reg_date,
 | 
			
		||||
            "binding_date": bind_date,
 | 
			
		||||
            "comment": self.instance.comment,
 | 
			
		||||
        }
 | 
			
		||||
        disabled_fields = [
 | 
			
		||||
            "identifier",
 | 
			
		||||
        ]
 | 
			
		||||
        self.load_initial_data(
 | 
			
		||||
            form_data,
 | 
			
		||||
            disabled_fields,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def save(self, user: User, geom_form: SimpleGeomForm):
 | 
			
		||||
        """ Overwrite instance with new form data
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            user ():
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            identifier = self.cleaned_data.get("identifier", None)
 | 
			
		||||
            title = self.cleaned_data.get("title", None)
 | 
			
		||||
            process_type = self.cleaned_data.get("type", None)
 | 
			
		||||
            laws = self.cleaned_data.get("laws", None)
 | 
			
		||||
            handler = self.cleaned_data.get("handler", None)
 | 
			
		||||
            registration_office = self.cleaned_data.get("registration_office", None)
 | 
			
		||||
            registration_file_number = self.cleaned_data.get("registration_file_number", None)
 | 
			
		||||
            conservation_office = self.cleaned_data.get("conservation_office", None)
 | 
			
		||||
            conservation_file_number = self.cleaned_data.get("conservation_file_number", None)
 | 
			
		||||
            registration_date = self.cleaned_data.get("registration_date", None)
 | 
			
		||||
            binding_date = self.cleaned_data.get("binding_date", None)
 | 
			
		||||
            comment = self.cleaned_data.get("comment", None)
 | 
			
		||||
 | 
			
		||||
            self.instance.legal.process_type = process_type
 | 
			
		||||
            self.instance.legal.registration_date = registration_date
 | 
			
		||||
            self.instance.legal.binding_date = binding_date
 | 
			
		||||
            self.instance.legal.laws.set(laws)
 | 
			
		||||
            self.instance.legal.save()
 | 
			
		||||
 | 
			
		||||
            self.instance.responsible.handler = handler
 | 
			
		||||
            self.instance.responsible.registration_office = registration_office
 | 
			
		||||
            self.instance.responsible.registration_file_number = registration_file_number
 | 
			
		||||
            self.instance.responsible.conservation_office = conservation_office
 | 
			
		||||
            self.instance.responsible.conservation_file_number = conservation_file_number
 | 
			
		||||
            self.instance.responsible.save()
 | 
			
		||||
 | 
			
		||||
            user_action = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=user,
 | 
			
		||||
                timestamp=timezone.now(),
 | 
			
		||||
                action=UserAction.EDITED,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            geometry = geom_form.save(user_action)
 | 
			
		||||
            self.instance.geometry = geometry
 | 
			
		||||
            self.instance.geometry.save()
 | 
			
		||||
 | 
			
		||||
            self.instance.log.add(user_action)
 | 
			
		||||
 | 
			
		||||
            self.instance.identifier = identifier
 | 
			
		||||
            self.instance.title = title
 | 
			
		||||
            self.instance.comment = comment
 | 
			
		||||
            self.instance.modified = user_action
 | 
			
		||||
            self.instance.save()
 | 
			
		||||
 | 
			
		||||
        return self.instance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OpenInterventionForm(EditInterventionForm):
 | 
			
		||||
    """
 | 
			
		||||
    This form is not intended to be used as data-input form. It's used to simplify the rendering of intervention:open
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        # Resize map
 | 
			
		||||
        self.fields["geometry"].widget.attrs["map_width"] = 500
 | 
			
		||||
        self.fields["geometry"].widget.attrs["map_height"] = 300
 | 
			
		||||
 | 
			
		||||
        # Disable all form fields
 | 
			
		||||
        for field in self.fields:
 | 
			
		||||
            self.disable_form_field(field)
 | 
			
		||||
							
								
								
									
										385
									
								
								intervention/forms/modalForms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										385
									
								
								intervention/forms/modalForms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,385 @@
 | 
			
		||||
"""
 | 
			
		||||
Author: Michel Peltriaux
 | 
			
		||||
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
 | 
			
		||||
Contact: michel.peltriaux@sgdnord.rlp.de
 | 
			
		||||
Created on: 27.09.21
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
from dal import autocomplete
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.db import transaction
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.urls import reverse
 | 
			
		||||
from django.utils.translation import  gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from compensation.models import EcoAccount, EcoAccountDeduction
 | 
			
		||||
from intervention.inputs import TextToClipboardInput
 | 
			
		||||
from intervention.models import Revocation, RevocationDocument, Intervention
 | 
			
		||||
from konova.forms import BaseModalForm
 | 
			
		||||
from konova.settings import ZB_GROUP, ETS_GROUP
 | 
			
		||||
from konova.utils.general import format_german_float
 | 
			
		||||
from konova.utils.messenger import Messenger
 | 
			
		||||
from konova.utils.user_checks import in_group
 | 
			
		||||
from user.models import UserActionLogEntry, UserAction
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ShareInterventionModalForm(BaseModalForm):
 | 
			
		||||
    url = forms.CharField(
 | 
			
		||||
        label=_("Share link"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("Send this link to users who you want to have writing access on the data"),
 | 
			
		||||
        required=False,
 | 
			
		||||
        widget=TextToClipboardInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "readonly": True
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    users = forms.MultipleChoiceField(
 | 
			
		||||
        label=_("Shared with"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        required=True,
 | 
			
		||||
        help_text=_("Remove check to remove access for this user"),
 | 
			
		||||
        widget=forms.CheckboxSelectMultiple(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "class": "list-unstyled",
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        choices=[]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("Share")
 | 
			
		||||
        self.form_caption = _("Share settings for {}").format(self.instance.identifier)
 | 
			
		||||
        self.template = "modal/modal_form.html"
 | 
			
		||||
 | 
			
		||||
        # Make sure an access_token is set
 | 
			
		||||
        if self.instance.access_token is None:
 | 
			
		||||
            self.instance.generate_access_token()
 | 
			
		||||
 | 
			
		||||
        self._init_fields()
 | 
			
		||||
 | 
			
		||||
    def _init_fields(self):
 | 
			
		||||
        """ Wraps initializing of fields
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        # Initialize share_link field
 | 
			
		||||
        self.share_link = self.request.build_absolute_uri(
 | 
			
		||||
            reverse("intervention:share", args=(self.instance.id, self.instance.access_token,))
 | 
			
		||||
        )
 | 
			
		||||
        self.initialize_form_field(
 | 
			
		||||
            "url",
 | 
			
		||||
            self.share_link
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Initialize users field
 | 
			
		||||
        # Remove field if user is not in registration or conservation group
 | 
			
		||||
        if not in_group(self.request.user, ZB_GROUP) and not in_group(self.request.user, ETS_GROUP):
 | 
			
		||||
            del self.fields["users"]
 | 
			
		||||
        else:
 | 
			
		||||
            users = self.instance.users.all()
 | 
			
		||||
            choices = []
 | 
			
		||||
            for n in users:
 | 
			
		||||
                choices.append(
 | 
			
		||||
                    (n.id, n.username)
 | 
			
		||||
                )
 | 
			
		||||
            self.fields["users"].choices = choices
 | 
			
		||||
            u_ids = list(users.values_list("id", flat=True))
 | 
			
		||||
            self.initialize_form_field(
 | 
			
		||||
                "users",
 | 
			
		||||
                u_ids
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def save(self):
 | 
			
		||||
        accessing_users = User.objects.filter(
 | 
			
		||||
            id__in=self.cleaned_data["users"]
 | 
			
		||||
        )
 | 
			
		||||
        self.instance.users.set(accessing_users)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewRevocationModalForm(BaseModalForm):
 | 
			
		||||
    date = forms.DateField(
 | 
			
		||||
        label=_("Date"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        help_text=_("Date of revocation"),
 | 
			
		||||
        widget=forms.DateInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "type": "date",
 | 
			
		||||
                "data-provide": "datepicker",
 | 
			
		||||
            },
 | 
			
		||||
            format="%d.%m.%Y"
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    file = forms.FileField(
 | 
			
		||||
        label=_("Document"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        required=False,
 | 
			
		||||
        help_text=_("Must be smaller than 15 Mb"),
 | 
			
		||||
        widget=forms.FileInput(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "class": "w-75"
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    comment = forms.CharField(
 | 
			
		||||
        required=False,
 | 
			
		||||
        max_length=200,
 | 
			
		||||
        label=_("Comment"),
 | 
			
		||||
        label_suffix=_(""),
 | 
			
		||||
        help_text=_("Additional comment, maximum {} letters").format(200),
 | 
			
		||||
        widget=forms.Textarea(
 | 
			
		||||
            attrs={
 | 
			
		||||
                "cols": 30,
 | 
			
		||||
                "rows": 5,
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Define w-100 for all form fields
 | 
			
		||||
    full_width_fields = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("Add revocation")
 | 
			
		||||
        self.form_caption = ""
 | 
			
		||||
        self.form_attrs = {
 | 
			
		||||
            "enctype": "multipart/form-data",  # important for file upload
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def save(self):
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            created_action = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                action=UserAction.CREATED
 | 
			
		||||
            )
 | 
			
		||||
            edited_action = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                action=UserAction.EDITED
 | 
			
		||||
            )
 | 
			
		||||
            revocation = Revocation.objects.create(
 | 
			
		||||
                date=self.cleaned_data["date"],
 | 
			
		||||
                comment=self.cleaned_data["comment"],
 | 
			
		||||
                created=created_action,
 | 
			
		||||
            )
 | 
			
		||||
            self.instance.modified = edited_action
 | 
			
		||||
            self.instance.save()
 | 
			
		||||
            self.instance.log.add(edited_action)
 | 
			
		||||
            self.instance.legal.revocation = revocation
 | 
			
		||||
            self.instance.legal.save()
 | 
			
		||||
 | 
			
		||||
            if self.cleaned_data["file"]:
 | 
			
		||||
                RevocationDocument.objects.create(
 | 
			
		||||
                    title="revocation_of_{}".format(self.instance.identifier),
 | 
			
		||||
                    date_of_creation=self.cleaned_data["date"],
 | 
			
		||||
                    comment=self.cleaned_data["comment"],
 | 
			
		||||
                    file=self.cleaned_data["file"],
 | 
			
		||||
                    instance=revocation
 | 
			
		||||
                )
 | 
			
		||||
        return revocation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RunCheckModalForm(BaseModalForm):
 | 
			
		||||
    checked_intervention = forms.BooleanField(
 | 
			
		||||
        label=_("Checked intervention data"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        widget=forms.CheckboxInput(),
 | 
			
		||||
        required=True,
 | 
			
		||||
    )
 | 
			
		||||
    checked_comps = forms.BooleanField(
 | 
			
		||||
        label=_("Checked compensations data and payments"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        widget=forms.CheckboxInput(),
 | 
			
		||||
        required=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("Run check")
 | 
			
		||||
        self.form_caption = _("I, {} {}, confirm that all necessary control steps have been performed by myself.").format(self.user.first_name, self.user.last_name)
 | 
			
		||||
 | 
			
		||||
    def is_valid(self) -> bool:
 | 
			
		||||
        """ Perform a validity check based on quality_check() logic
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            result (bool)
 | 
			
		||||
        """
 | 
			
		||||
        super_result = super().is_valid()
 | 
			
		||||
        # Perform check
 | 
			
		||||
        msgs = self.instance.quality_check()
 | 
			
		||||
        for msg in msgs:
 | 
			
		||||
            self.add_error(
 | 
			
		||||
                "checked_intervention",
 | 
			
		||||
                msg
 | 
			
		||||
            )
 | 
			
		||||
        return super_result and (len(msgs) == 0)
 | 
			
		||||
 | 
			
		||||
    def save(self):
 | 
			
		||||
        """ Saving logic
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            user_action = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                action=UserAction.CHECKED
 | 
			
		||||
            )
 | 
			
		||||
            # Replace old checked
 | 
			
		||||
            if self.instance.checked:
 | 
			
		||||
                self.instance.checked.delete()
 | 
			
		||||
            self.instance.checked = user_action
 | 
			
		||||
            self.instance.log.add(user_action)
 | 
			
		||||
            self.instance.save()
 | 
			
		||||
 | 
			
		||||
        # Send message to the SSO server
 | 
			
		||||
        messenger = Messenger(
 | 
			
		||||
            self.instance.users.all(),
 | 
			
		||||
            type="INFO",
 | 
			
		||||
        )
 | 
			
		||||
        messenger.send_object_checked(
 | 
			
		||||
            self.instance.identifier,
 | 
			
		||||
            self.user,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NewDeductionModalForm(BaseModalForm):
 | 
			
		||||
    """ Form for creating new deduction
 | 
			
		||||
 | 
			
		||||
    Can be used for Intervention view as well as for EcoAccount views.
 | 
			
		||||
 | 
			
		||||
    Parameter 'instance' can be an intervention, as well as an ecoAccount.
 | 
			
		||||
    An instance check handles both workflows properly.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    account = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Eco-account"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("Only recorded accounts can be selected for deductions"),
 | 
			
		||||
        queryset=EcoAccount.objects.filter(deleted=None),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="accounts-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Eco-account"),
 | 
			
		||||
                "data-minimum-input-length": 3,
 | 
			
		||||
                "readonly": True,
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
    surface = forms.DecimalField(
 | 
			
		||||
        min_value=0.00,
 | 
			
		||||
        decimal_places=2,
 | 
			
		||||
        label=_("Surface"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("in m²"),
 | 
			
		||||
    )
 | 
			
		||||
    intervention = forms.ModelChoiceField(
 | 
			
		||||
        label=_("Intervention"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        help_text=_("Only shared interventions can be selected"),
 | 
			
		||||
        queryset=Intervention.objects.filter(deleted=None),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="interventions-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Intervention"),
 | 
			
		||||
                "data-minimum-input-length": 3,
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Define w-100 for all form fields
 | 
			
		||||
    full_width_fields = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("New Deduction")
 | 
			
		||||
        self.form_caption = _("Enter the information for a new deduction from a chosen eco-account")
 | 
			
		||||
        self.is_intervention_initially = False
 | 
			
		||||
 | 
			
		||||
        # Add a placeholder for field 'surface' without having to define the whole widget above
 | 
			
		||||
        self.add_placeholder_for_field("surface", "0,00")
 | 
			
		||||
 | 
			
		||||
        # Check for Intervention or EcoAccount
 | 
			
		||||
        if isinstance(self.instance, Intervention):
 | 
			
		||||
            # Form has been called with a given intervention
 | 
			
		||||
            self.initialize_form_field("intervention", self.instance)
 | 
			
		||||
            self.disable_form_field("intervention")
 | 
			
		||||
            self.is_intervention_initially = True
 | 
			
		||||
        elif isinstance(self.instance, EcoAccount):
 | 
			
		||||
            # Form has been called with a given account --> make it initial in the form and read-only
 | 
			
		||||
            self.initialize_form_field("account", self.instance)
 | 
			
		||||
            self.disable_form_field("account")
 | 
			
		||||
        else:
 | 
			
		||||
            raise NotImplementedError
 | 
			
		||||
 | 
			
		||||
    def is_valid(self):
 | 
			
		||||
        """ Custom validity check
 | 
			
		||||
 | 
			
		||||
        Makes sure the deduction can not contain more surface than the account still provides
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            is_valid (bool)
 | 
			
		||||
        """
 | 
			
		||||
        super_result = super().is_valid()
 | 
			
		||||
        if self.is_intervention_initially:
 | 
			
		||||
            acc = self.cleaned_data["account"]
 | 
			
		||||
        else:
 | 
			
		||||
            acc = self.instance
 | 
			
		||||
 | 
			
		||||
        if not acc.recorded:
 | 
			
		||||
            self.add_error(
 | 
			
		||||
                "account",
 | 
			
		||||
                _("Eco-account {} is not recorded yet. You can only deduct from recorded accounts.").format(acc.identifier)
 | 
			
		||||
            )
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        # Calculate valid surface
 | 
			
		||||
        sum_surface = acc.get_surface()
 | 
			
		||||
        sum_surface_deductions = acc.get_deductions_surface()
 | 
			
		||||
        rest_surface = sum_surface - sum_surface_deductions
 | 
			
		||||
        form_surface = float(self.cleaned_data["surface"])
 | 
			
		||||
        is_valid_surface = form_surface < rest_surface
 | 
			
		||||
        if not is_valid_surface:
 | 
			
		||||
            self.add_error(
 | 
			
		||||
                "surface",
 | 
			
		||||
                _("The account {} has not enough surface for a deduction of {} m². There are only {} m² left").format(
 | 
			
		||||
                    acc.identifier,
 | 
			
		||||
                    format_german_float(form_surface),
 | 
			
		||||
                    format_german_float(rest_surface),
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
        return is_valid_surface and super_result
 | 
			
		||||
 | 
			
		||||
    def save(self):
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            # Create log entry
 | 
			
		||||
            user_action_edit = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                action=UserAction.EDITED
 | 
			
		||||
            )
 | 
			
		||||
            user_action_create = UserActionLogEntry.objects.create(
 | 
			
		||||
                user=self.user,
 | 
			
		||||
                action=UserAction.CREATED
 | 
			
		||||
            )
 | 
			
		||||
            self.instance.log.add(user_action_edit)
 | 
			
		||||
            self.instance.modified = user_action_edit
 | 
			
		||||
            self.instance.save()
 | 
			
		||||
 | 
			
		||||
            # Create deductions depending on Intervention or EcoAccount as the initial instance
 | 
			
		||||
            if self.is_intervention_initially:
 | 
			
		||||
                deduction = EcoAccountDeduction.objects.create(
 | 
			
		||||
                    intervention=self.instance,
 | 
			
		||||
                    account=self.cleaned_data["account"],
 | 
			
		||||
                    surface=self.cleaned_data["surface"],
 | 
			
		||||
                    created=user_action_create,
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                deduction = EcoAccountDeduction.objects.create(
 | 
			
		||||
                    intervention=self.cleaned_data["intervention"],
 | 
			
		||||
                    account=self.instance,
 | 
			
		||||
                    surface=self.cleaned_data["surface"],
 | 
			
		||||
                    created=user_action_create,
 | 
			
		||||
                )
 | 
			
		||||
        return deduction
 | 
			
		||||
							
								
								
									
										15
									
								
								intervention/inputs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								intervention/inputs.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
from django import forms
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DummyFilterInput(forms.HiddenInput):
 | 
			
		||||
    """ A dummy input widget
 | 
			
		||||
 | 
			
		||||
    Does not render anything. Can be used to keep filter logic using django_filter without having a pre defined
 | 
			
		||||
    filter widget being rendered to the template.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    template_name = "konova/custom_widgets/dummy-filter-input.html"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TextToClipboardInput(forms.TextInput):
 | 
			
		||||
    template_name = "konova/custom_widgets/text-to-clipboard-input.html"
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.http import HttpRequest
 | 
			
		||||
from django.shortcuts import render, get_object_or_404
 | 
			
		||||
 | 
			
		||||
from intervention.forms import NewInterventionForm, EditInterventionForm, ShareInterventionForm, NewRevocationForm, \
 | 
			
		||||
    RunCheckForm, NewDeductionForm
 | 
			
		||||
from intervention.forms.forms import NewInterventionForm, EditInterventionForm
 | 
			
		||||
from intervention.forms.modalForms import ShareInterventionModalForm, NewRevocationModalForm, \
 | 
			
		||||
    RunCheckModalForm, NewDeductionModalForm
 | 
			
		||||
from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument
 | 
			
		||||
from intervention.tables import InterventionTable
 | 
			
		||||
from konova.contexts import BaseContext
 | 
			
		||||
@ -342,7 +342,7 @@ def create_share_view(request: HttpRequest, id: str):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    intervention = get_object_or_404(Intervention, id=id)
 | 
			
		||||
    form = ShareInterventionForm(request.POST or None, instance=intervention, request=request)
 | 
			
		||||
    form = ShareInterventionModalForm(request.POST or None, instance=intervention, request=request)
 | 
			
		||||
    return form.process_request(
 | 
			
		||||
        request,
 | 
			
		||||
        msg_success=_("Share settings updated")
 | 
			
		||||
@ -361,7 +361,7 @@ def run_check_view(request: HttpRequest, id: str):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    intervention = get_object_or_404(Intervention, id=id)
 | 
			
		||||
    form = RunCheckForm(request.POST or None, instance=intervention, user=request.user)
 | 
			
		||||
    form = RunCheckModalForm(request.POST or None, instance=intervention, user=request.user)
 | 
			
		||||
    return form.process_request(
 | 
			
		||||
        request,
 | 
			
		||||
        msg_success=_("Check performed"),
 | 
			
		||||
@ -381,7 +381,7 @@ def new_revocation_view(request: HttpRequest, id: str):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    intervention = get_object_or_404(Intervention, id=id)
 | 
			
		||||
    form = NewRevocationForm(request.POST or None, request.FILES or None, instance=intervention, user=request.user)
 | 
			
		||||
    form = NewRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention, user=request.user)
 | 
			
		||||
    return form.process_request(
 | 
			
		||||
        request,
 | 
			
		||||
        msg_success=_("Revocation added")
 | 
			
		||||
@ -425,7 +425,7 @@ def new_deduction_view(request: HttpRequest, id: str):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    intervention = get_object_or_404(Intervention, id=id)
 | 
			
		||||
    form = NewDeductionForm(request.POST or None, instance=intervention, user=request.user)
 | 
			
		||||
    form = NewDeductionModalForm(request.POST or None, instance=intervention, user=request.user)
 | 
			
		||||
    return form.process_request(
 | 
			
		||||
        request,
 | 
			
		||||
        msg_success=_("Deduction added")
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user