* refactors deduction views on interventions and eco accounts from function to class based * introduces basic checks on shared access and permission on BaseView on dispatching --> checks shall be overwritten on inheriting classes
		
			
				
	
	
		
			278 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""
 | 
						|
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.contrib.auth.mixins import LoginRequiredMixin
 | 
						|
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, RemoveEcoAccountModalForm
 | 
						|
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, \
 | 
						|
    uuid_required
 | 
						|
from konova.forms import SimpleGeomForm
 | 
						|
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, DO_NOT_FORGET_TO_SHARE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE
 | 
						|
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView
 | 
						|
 | 
						|
 | 
						|
class EcoAccountIndexView(LoginRequiredMixin, BaseIndexView):
 | 
						|
    _INDEX_TABLE_CLS = EcoAccountTable
 | 
						|
    _TAB_TITLE = _("Eco-account - Overview")
 | 
						|
 | 
						|
    def _get_queryset(self):
 | 
						|
        qs = EcoAccount.objects.filter(
 | 
						|
            deleted=None,
 | 
						|
        ).order_by(
 | 
						|
            "-modified__timestamp"
 | 
						|
        )
 | 
						|
        return qs
 | 
						|
 | 
						|
 | 
						|
@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))
 | 
						|
            if geom_form.has_geometry_simplified():
 | 
						|
                messages.info(
 | 
						|
                    request,
 | 
						|
                    GEOMETRY_SIMPLIFIED
 | 
						|
                )
 | 
						|
 | 
						|
            num_ignored_geometries = geom_form.get_num_geometries_ignored()
 | 
						|
            if num_ignored_geometries > 0:
 | 
						|
                messages.info(
 | 
						|
                    request,
 | 
						|
                    GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
 | 
						|
                )
 | 
						|
 | 
						|
            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)
 | 
						|
 | 
						|
 | 
						|
class EcoAccountIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
 | 
						|
    _MODEL_CLS = EcoAccount
 | 
						|
    _REDIRECT_URL = "compensation:acc:index"
 | 
						|
 | 
						|
 | 
						|
@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":
 | 
						|
        data_form_valid = data_form.is_valid()
 | 
						|
        geom_form_valid = geom_form.is_valid()
 | 
						|
        if data_form_valid and geom_form_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))
 | 
						|
            if geom_form.has_geometry_simplified():
 | 
						|
                messages.info(
 | 
						|
                    request,
 | 
						|
                    GEOMETRY_SIMPLIFIED
 | 
						|
                )
 | 
						|
 | 
						|
            num_ignored_geometries = geom_form.get_num_geometries_ignored()
 | 
						|
            if num_ignored_geometries > 0:
 | 
						|
                messages.info(
 | 
						|
                    request,
 | 
						|
                    GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
 | 
						|
                )
 | 
						|
 | 
						|
            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
 | 
						|
@uuid_required
 | 
						|
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,
 | 
						|
        deleted=None,
 | 
						|
    )
 | 
						|
    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 = acc.get_surface_before_states()
 | 
						|
    sum_after_states = acc.get_surface_after_states()
 | 
						|
    diff_states = abs(sum_before_states - sum_after_states)
 | 
						|
    # Calculate rest of available surface for deductions
 | 
						|
    available_total = acc.deductable_rest
 | 
						|
    available_relative = acc.get_deductable_rest_relative()
 | 
						|
 | 
						|
    # 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)
 | 
						|
 | 
						|
    requesting_user_is_only_shared_user = acc.is_only_shared_with(_user)
 | 
						|
    if requesting_user_is_only_shared_user:
 | 
						|
        messages.info(
 | 
						|
            request,
 | 
						|
            DO_NOT_FORGET_TO_SHARE
 | 
						|
        )
 | 
						|
 | 
						|
    context = {
 | 
						|
        "obj": acc,
 | 
						|
        "geom_form": geom_form,
 | 
						|
        "parcels": parcels,
 | 
						|
        "is_entry_shared": 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": _user.in_group(DEFAULT_GROUP),
 | 
						|
        "is_zb_member": _user.in_group(ZB_GROUP),
 | 
						|
        "is_ets_member": _user.in_group(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 user.in_group(ETS_GROUP):
 | 
						|
            messages.info(request, CANCEL_ACC_RECORDED_OR_DEDUCTED)
 | 
						|
            return redirect("compensation:acc:detail", id=id)
 | 
						|
 | 
						|
    form = RemoveEcoAccountModalForm(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"),
 | 
						|
    )
 | 
						|
 |