2021-09-27 11:45:13 +02:00
"""
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
2022-01-12 12:56:22 +01:00
from user . models import User
2021-09-27 11:45:13 +02:00
from django . db import transaction
from django import forms
from django . urls import reverse
2021-11-16 12:51:10 +01:00
from django . utils . translation import gettext_lazy as _
2021-09-27 11:45:13 +02:00
2021-11-16 12:51:10 +01:00
from compensation . models import EcoAccount
2021-09-27 11:45:13 +02:00
from intervention . inputs import TextToClipboardInput
2021-11-16 12:51:10 +01:00
from intervention . models import Intervention , InterventionDocument
2021-11-15 17:09:17 +01:00
from konova . forms import BaseModalForm , NewDocumentForm
2021-09-27 11:45:13 +02:00
from konova . utils . general import format_german_float
2021-11-15 14:00:08 +01:00
from konova . utils . user_checks import is_default_group_only
2021-09-27 11:45:13 +02:00
2021-11-16 12:54:28 +01:00
class ShareModalForm ( BaseModalForm ) :
2021-09-27 11:45:13 +02:00
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 = {
2021-09-27 13:57:56 +02:00
" readonly " : True ,
" class " : " form-control " ,
2021-09-27 11:45:13 +02:00
}
)
)
2021-11-15 14:00:08 +01:00
user_select = forms . ModelMultipleChoiceField (
label = _ ( " Add user to share with " ) ,
label_suffix = " " ,
2021-12-14 13:55:08 +01:00
help_text = _ ( " Multiple selection possible - You can only select users which do not already have access. Enter the full username. " ) ,
2021-11-15 14:00:08 +01:00
required = False ,
queryset = User . objects . all ( ) ,
widget = autocomplete . ModelSelect2Multiple (
url = " share-user-autocomplete " ,
attrs = {
" data-placeholder " : _ ( " Click for selection " ) ,
" data-minimum-input-length " : 3 ,
} ,
forward = [ " users " ]
) ,
)
2021-09-27 11:45:13 +02:00
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
2021-10-26 15:09:30 +02:00
url_name = f " { self . instance . _meta . app_label } :share "
2021-09-27 11:45:13 +02:00
self . share_link = self . request . build_absolute_uri (
2021-10-26 15:09:30 +02:00
reverse ( url_name , args = ( self . instance . id , self . instance . access_token , ) )
2021-09-27 11:45:13 +02:00
)
self . initialize_form_field (
" url " ,
self . share_link
)
# Initialize users field
2021-11-15 14:00:08 +01:00
# Disable field if user is not in registration or conservation group
if is_default_group_only ( self . request . user ) :
self . disable_form_field ( " users " )
self . _add_user_choices_to_field ( )
def _add_user_choices_to_field ( self ) :
""" Transforms the instance ' s sharing users into a list for the form field
Returns :
"""
users = self . instance . users . all ( )
choices = [ ]
for n in users :
choices . append (
( n . id , n . username )
2021-09-27 11:45:13 +02:00
)
2021-11-15 14:00:08 +01:00
self . fields [ " users " ] . choices = choices
u_ids = list ( users . values_list ( " id " , flat = True ) )
self . initialize_form_field (
" users " ,
u_ids
)
2021-09-27 11:45:13 +02:00
def save ( self ) :
2021-11-16 12:51:10 +01:00
self . instance . update_sharing_user ( self )
2021-09-27 11:45:13 +02:00
class NewRevocationModalForm ( BaseModalForm ) :
date = forms . DateField (
label = _ ( " Date " ) ,
label_suffix = _ ( " " ) ,
help_text = _ ( " Date of revocation " ) ,
widget = forms . DateInput (
attrs = {
" type " : " date " ,
" data-provide " : " datepicker " ,
2021-09-27 13:57:56 +02:00
" class " : " form-control " ,
2021-09-27 11:45:13 +02:00
} ,
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 = {
2021-09-27 13:57:56 +02:00
" class " : " form-control-file "
2021-09-27 11:45:13 +02: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 = {
" cols " : 30 ,
" rows " : 5 ,
2021-09-27 13:57:56 +02:00
" class " : " form-control " ,
2021-09-27 11:45:13 +02:00
}
)
)
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 ) :
2021-11-16 12:26:50 +01:00
revocation = self . instance . add_revocation ( self )
2021-11-17 14:33:05 +01:00
self . instance . mark_as_edited ( self . user , self . request )
2021-09-27 11:45:13 +02:00
return revocation
2021-11-11 10:37:22 +01:00
class CheckModalForm ( BaseModalForm ) :
""" The modal form for running a check on interventions and their compensations
"""
2021-09-27 11:45:13 +02:00
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
)
2022-01-07 15:41:40 +01:00
valid = None
2021-09-27 11:45:13 +02:00
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 )
2022-01-07 15:41:40 +01:00
self . valid = False
def _are_deductions_valid ( self ) :
""" Performs validity checks on deductions and their eco-account
Returns :
"""
deductions = self . instance . deductions . all ( )
for deduction in deductions :
checker = deduction . account . quality_check ( )
for msg in checker . messages :
self . add_error (
" checked_comps " ,
f " { deduction . account . identifier } : { msg } "
)
return checker . valid
return True
def _are_comps_valid ( self ) :
""" Performs validity checks on all types of compensations
Types of compensations are
* regular Compensations
* deductions from EcoAccounts
Returns :
"""
comps = self . instance . compensations . all ( )
comps_valid = True
for comp in comps :
checker = comp . quality_check ( )
for msg in checker . messages :
self . add_error (
" checked_comps " ,
f " { comp . identifier } : { msg } "
)
comps_valid = checker . valid
deductions_valid = self . _are_deductions_valid ( )
return deductions_valid and comps_valid
2021-09-27 11:45:13 +02:00
def is_valid ( self ) - > bool :
""" Perform a validity check based on quality_check() logic
Returns :
result ( bool )
"""
2022-01-07 15:41:40 +01:00
super_valid = super ( ) . is_valid ( )
2021-09-27 11:45:13 +02:00
# Perform check
2021-10-25 13:06:54 +02:00
checker = self . instance . quality_check ( )
for msg in checker . messages :
2021-09-27 11:45:13 +02:00
self . add_error (
" checked_intervention " ,
msg
)
2022-01-07 15:41:40 +01:00
all_comps_valid = self . _are_comps_valid ( )
intervention_valid = checker . valid
return super_valid and intervention_valid and all_comps_valid
2021-09-27 11:45:13 +02:00
def save ( self ) :
""" Saving logic
Returns :
"""
with transaction . atomic ( ) :
2021-11-17 12:09:49 +01:00
self . instance . set_checked ( self . user )
2021-09-27 11:45:13 +02:00
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² " ) ,
2021-09-27 13:57:56 +02:00
widget = forms . NumberInput (
attrs = {
" class " : " form-control " ,
2021-10-06 13:10:10 +02:00
" placeholder " : " 0,00 " ,
2021-09-27 13:57:56 +02:00
}
)
2021-09-27 11:45:13 +02:00
)
intervention = forms . ModelChoiceField (
label = _ ( " Intervention " ) ,
label_suffix = " " ,
help_text = _ ( " Only shared interventions can be selected " ) ,
queryset = Intervention . objects . filter ( deleted = None ) ,
widget = autocomplete . ModelSelect2 (
url = " interventions-autocomplete " ,
attrs = {
" data-placeholder " : _ ( " Intervention " ) ,
" data-minimum-input-length " : 3 ,
}
) ,
)
def __init__ ( self , * args , * * kwargs ) :
super ( ) . __init__ ( * args , * * kwargs )
self . form_title = _ ( " New Deduction " )
self . form_caption = _ ( " Enter the information for a new deduction from a chosen eco-account " )
# Check for Intervention or EcoAccount
if isinstance ( self . instance , Intervention ) :
# Form has been called with a given intervention
self . initialize_form_field ( " intervention " , self . instance )
self . disable_form_field ( " intervention " )
elif isinstance ( self . instance , EcoAccount ) :
# Form has been called with a given account --> make it initial in the form and read-only
self . initialize_form_field ( " account " , self . instance )
self . disable_form_field ( " account " )
else :
raise NotImplementedError
def 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 ( )
2021-11-16 12:43:13 +01:00
acc = self . cleaned_data [ " account " ]
2021-09-27 11:45:13 +02:00
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
2021-10-28 17:21:07 +02:00
deductable_surface = acc . deductable_surface
2021-09-27 11:45:13 +02:00
sum_surface_deductions = acc . get_deductions_surface ( )
2021-10-28 17:21:07 +02:00
rest_surface = deductable_surface - sum_surface_deductions
2021-09-27 11:45:13 +02:00
form_surface = float ( self . cleaned_data [ " surface " ] )
2021-11-16 12:43:13 +01:00
is_valid_surface = form_surface < = rest_surface
2021-09-27 11:45:13 +02:00
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 ) :
2021-11-16 12:43:13 +01:00
deduction = self . instance . add_deduction ( self )
2022-01-31 10:14:46 +01:00
self . instance . mark_as_edited ( self . user , self . request , reset_recorded = False )
2021-11-15 17:09:17 +01:00
return deduction
class NewInterventionDocumentForm ( NewDocumentForm ) :
document_model = InterventionDocument