""" Author: Michel Peltriaux Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany Contact: michel.peltriaux@sgdnord.rlp.de Created on: 04.12.20 """ from bootstrap_modal_forms.utils import is_ajax from dal import autocomplete from django import forms from django.contrib import messages from django.db import transaction from django.http import HttpRequest, HttpResponseRedirect from django.shortcuts import render from django.utils.translation import gettext_lazy as _ from django.utils.translation import pgettext_lazy as _con from codelist.models import KonovaCode from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION_ID from compensation.models import Payment, CompensationState, CompensationAction, UnitChoices from konova.contexts import BaseContext from konova.forms import BaseForm, BaseModalForm from konova.models import Deadline, DeadlineType from konova.utils.message_templates import FORM_INVALID from user.models import UserActionLogEntry, UserAction class NewCompensationForm(BaseForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def save(self): with transaction.atomic(): user_action = UserActionLogEntry.objects.create( user=self.user, action=UserAction.CREATED ) # Save action to log class NewPaymentForm(BaseModalForm): amount = forms.DecimalField( min_value=0.00, decimal_places=2, label=_con("money", "Amount"), # contextual translation label_suffix=_(""), help_text=_("in Euro"), ) 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", }, 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": "w-100" } ) ) 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) self.add_placeholder_for_field("amount", "0,00") 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): 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, comment=_("Added payment"), ) pay = Payment.objects.create( created=created_action, amount=self.cleaned_data.get("amount", -1), due_on=self.cleaned_data.get("due", None), comment=self.cleaned_data.get("comment", None), intervention=self.intervention, ) self.intervention.log.add(edited_action) self.intervention.modified = edited_action self.intervention.save() return pay class NewStateModalForm(BaseModalForm): biotope_type = forms.ModelChoiceField( label=_("Biotope Type"), label_suffix="", required=True, help_text=_("Select the biotope type"), queryset=KonovaCode.objects.filter( is_archived=False, is_leaf=True, code_lists__in=[CODELIST_BIOTOPES_ID], ), widget=autocomplete.ModelSelect2( url="codes-biotope-autocomplete", attrs={ "data-placeholder": _("Biotope Type"), } ), ) surface = forms.DecimalField( min_value=0.00, decimal_places=2, label=_("Surface"), label_suffix="", required=True, help_text=_("in m²") ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.form_title = _("New state") self.form_caption = _("Insert data for the new state") self.add_placeholder_for_field("surface", "0,00") def save(self, is_before_state: bool = False): with transaction.atomic(): user_action = UserActionLogEntry.objects.create( user=self.user, action=UserAction.EDITED, comment=_("Added state") ) self.instance.log.add(user_action) self.instance.modified = user_action self.instance.save() state = CompensationState.objects.create( biotope_type=self.cleaned_data["biotope_type"], surface=self.cleaned_data["surface"], ) if is_before_state: self.instance.before_states.add(state) else: self.instance.after_states.add(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 NewDeadlineModalForm(BaseModalForm): type = forms.ChoiceField( label=_("Deadline Type"), label_suffix="", required=True, help_text=_("Select the deadline type"), choices=DeadlineType.choices, widget=forms.Select( attrs={ "class": "custom-select" } ) ) date = forms.DateField( label=_("Date"), label_suffix="", required=True, help_text=_("Select date"), widget=forms.DateInput( attrs={ "type": "date", "data-provide": "datepicker", }, 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, } ) ) 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): with transaction.atomic(): created_action = UserActionLogEntry.objects.create( user=self.user, action=UserAction.CREATED ) deadline = Deadline.objects.create( type=self.cleaned_data["type"], date=self.cleaned_data["date"], comment=self.cleaned_data["comment"], created=created_action, ) edited_action = UserActionLogEntry.objects.create( user=self.user, action=UserAction.EDITED, comment=_("Added deadline") ) self.instance.modified = edited_action self.instance.save() self.instance.log.add(edited_action) self.instance.deadlines.add(deadline) return deadline class NewActionModalForm(BaseModalForm): action_type = forms.ModelChoiceField( label=_("Action Type"), label_suffix="", required=True, help_text=_("Select the action type"), queryset=KonovaCode.objects.filter( is_archived=False, is_leaf=True, code_lists__in=[CODELIST_COMPENSATION_ACTION_ID], ), widget=autocomplete.ModelSelect2( url="codes-compensation-action-autocomplete", attrs={ "data-placeholder": _("Action"), } ), ) unit = forms.ChoiceField( label=_("Unit"), label_suffix="", required=True, help_text=_("Select the unit"), choices=UnitChoices.choices, widget=forms.Select( attrs={ "class": "custom-select" } ) ) amount = forms.DecimalField( label=_("Amount"), label_suffix="", required=True, help_text=_("Insert the amount"), decimal_places=2, min_value=0.00, ) comment = forms.CharField( required=False, max_length=200, label=_("Comment"), label_suffix=_(""), help_text=_("Additional comment, maximum {} letters").format(200), widget=forms.Textarea( attrs={ "rows": 5, "class": "w-100" } ) ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.form_title = _("New action") self.form_caption = _("Insert data for the new action") self.add_placeholder_for_field("amount", "0,00") def save(self): with transaction.atomic(): user_action = UserActionLogEntry.objects.create( user=self.user, action=UserAction.CREATED, ) comp_action = CompensationAction.objects.create( action_type=self.cleaned_data["action_type"], amount=self.cleaned_data["amount"], unit=self.cleaned_data["unit"], comment=self.cleaned_data["comment"], created=user_action, ) edited_action = UserActionLogEntry.objects.create( user=self.user, action=UserAction.EDITED, comment=_("Added action"), ) self.instance.modified = edited_action self.instance.save() self.instance.log.add(edited_action) self.instance.actions.add(comp_action) return comp_action