Intervention revocation

* adds Revocation model to interventions/models.py
* adds revocations to interventions detail view
* fixes duplicated ids in html includes
* refactors controls for detail view into included template files (controls.html)
* reduces max length for payment transfer notes from 1000 to 200
* adds RevocationAdmin to intervention/admin.py
* adds new form for adding a Revocation to an intervention's legal_data
  * only one revocation per intervention possible
  * removes add button in case of an existing revocation
* adds revocation routes to intervention app
* renames document field in Document model into file for more clarity
* adds/updates translations
This commit is contained in:
mipel
2021-08-04 13:32:35 +02:00
parent d7c95c9e70
commit 6818ef290e
25 changed files with 495 additions and 197 deletions

View File

@@ -1,6 +1,6 @@
from django.contrib import admin
from intervention.models import Intervention, ResponsibilityData, LegalData
from intervention.models import Intervention, ResponsibilityData, LegalData, Revocation
class InterventionAdmin(admin.ModelAdmin):
@@ -33,6 +33,16 @@ class LegalAdmin(admin.ModelAdmin):
]
class RevocationAdmin(admin.ModelAdmin):
list_display = [
"id",
"date",
"comment",
"created",
]
admin.site.register(Intervention, InterventionAdmin)
admin.site.register(ResponsibilityData, ResponsibilityAdmin)
admin.site.register(LegalData, LegalAdmin)
admin.site.register(Revocation, RevocationAdmin)

View File

@@ -14,7 +14,7 @@ from django.db import transaction
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from intervention.models import Intervention
from intervention.models import Intervention, Revocation
from konova.forms import BaseForm, BaseModalForm
from konova.models import Document
from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM, ZB_GROUP, ETS_GROUP
@@ -315,3 +315,71 @@ class ShareInterventionForm(BaseModalForm):
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=_(""),
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,
}
)
)
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():
user_action = UserActionLogEntry.objects.create(
user=self.user,
action=UserAction.CREATED
)
document = Document.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"],
)
revocation = Revocation.objects.create(
date=self.cleaned_data["date"],
comment=self.cleaned_data["comment"],
document=document,
created=user_action,
)
self.instance.legal.revocation = revocation
self.instance.legal.save()
return revocation

View File

@@ -12,7 +12,7 @@ from django.utils import timezone
from django.utils.timezone import now
from intervention.settings import INTERVENTION_IDENTIFIER_LENGTH, INTERVENTION_IDENTIFIER_TEMPLATE
from konova.models import BaseObject, Geometry, UuidModel
from konova.models import BaseObject, Geometry, UuidModel, BaseResource
from konova.utils import generators
from konova.utils.generators import generate_random_string
from organisation.models import Organisation
@@ -38,6 +38,20 @@ class ResponsibilityData(UuidModel):
)
class Revocation(BaseResource):
"""
Holds revocation data e.g. for intervention objects
"""
date = models.DateField(null=True, blank=True, help_text="Revocation from")
comment = models.TextField(null=True, blank=True)
document = models.ForeignKey("konova.Document", blank=True, null=True, on_delete=models.SET_NULL)
def delete(self):
# Make sure related objects are being removed as well
self.document.delete()
super().delete()
class LegalData(UuidModel):
"""
Holds intervention legal data such as important dates, laws or responsible handler
@@ -51,6 +65,8 @@ class LegalData(UuidModel):
process_type = models.CharField(max_length=500, null=True, blank=True)
law = models.CharField(max_length=500, null=True, blank=True)
revocation = models.ForeignKey(Revocation, null=True, blank=True, help_text="Refers to 'Widerspruch am'", on_delete=models.SET_NULL)
def __str__(self):
return "{} | {} | {}".format(
self.process_type,

View File

@@ -1,5 +1,5 @@
{% load i18n l10n fontawesome_5 %}
<div id="related-compensations" class="card">
<div id="compensations" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">

View File

@@ -0,0 +1,43 @@
{% load i18n l10n fontawesome_5 %}
<div class="d-flex justify-content-end">
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Open in LANIS' %}">
LANIS
</button>
</a>
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Public report' %}">
{% fa5_icon 'file-alt' %}
</button>
</a>
{% if has_access %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'intervention:share-create' intervention.id %}">
{% fa5_icon 'share-alt' %}
</button>
{% if is_zb_member %}
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Run check' %}">
{% fa5_icon 'star' %}
</button>
</a>
{% endif %}
{% if is_ets_member %}
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Record' %}">
{% fa5_icon 'bookmark' %}
</button>
</a>
{% endif %}
{% if is_default_member %}
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Edit' %}">
{% fa5_icon 'edit' %}
</button>
</a>
<button class="btn btn-default btn-modal" data-form-url="{% url 'intervention:remove' intervention.id %}" title="{% trans 'Delete' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
{% endif %}
</div>

View File

@@ -1,5 +1,5 @@
{% load i18n l10n fontawesome_5 %}
<div id="related-documents" class="card">
<div id="documents" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">

View File

@@ -1,5 +1,5 @@
{% load i18n l10n fontawesome_5 %}
<div id="related-eco-account-withdraws" class="card">
<div id="eco-account-withdraws" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">

View File

@@ -1,5 +1,5 @@
{% load i18n l10n fontawesome_5 %}
<div id="related-payments" class="card">
<div id="payments" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">

View File

@@ -0,0 +1,70 @@
{% load i18n l10n fontawesome_5 %}
<div id="revocation" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">
<h5>
<span class="badge badge-light">{% if intervention.legal.revocation %}1{% else %}0{% endif %}</span>
{% trans 'Revocation' %}
</h5>
</div>
<div class="col-sm-6">
<div class="d-flex justify-content-end">
{% comment %}
Only show add-button if no revocation exists, yet.
{% endcomment %}
{% if is_default_member and has_access and not intervention.legal.revocation %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:revocation-new' intervention.id %}" title="{% trans 'Add revocation' %}">
{% fa5_icon 'plus' %}
{% fa5_icon 'ban' %}
</button>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card-body scroll-300">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">
{% trans 'From' context 'Revocation' %}
</th>
<th scope="col">
{% trans 'Comment' %}
</th>
<th scope="col">
{% trans 'Document' %}
</th>
<th scope="col">
{% trans 'Action' %}
</th>
</tr>
</thead>
<tbody>
{% if intervention.legal.revocation %}
{% with intervention.legal.revocation as rev %}
<tr>
<td class="align-middle">
{{ rev.date }}
</td>
<td class="align-middle">{{ rev.comment }}</td>
<td class="align-middle">
<a href="{% url 'doc-open' rev.document.id %}">
{{ rev.document.file }}
</a>
</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'intervention:revocation-remove' rev.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove revocation' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
</td>
</tr>
{% endwith %}
{% endif %}
</tbody>
</table>
</div>
</div>

View File

@@ -12,47 +12,7 @@
<h3>{% trans 'Intervention' %} {{intervention.identifier}}</h3>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="d-flex justify-content-end">
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Open in LANIS' %}">
LANIS
</button>
</a>
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Public report' %}">
{% fa5_icon 'file-alt' %}
</button>
</a>
{% if has_access %}
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'intervention:share-create' intervention.id %}">
{% fa5_icon 'share-alt' %}
</button>
{% if is_zb_member %}
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Run check' %}">
{% fa5_icon 'star' %}
</button>
</a>
{% endif %}
{% if is_ets_member %}
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Record' %}">
{% fa5_icon 'bookmark' %}
</button>
</a>
{% endif %}
{% if is_default_member %}
<a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Edit' %}">
{% fa5_icon 'edit' %}
</button>
</a>
<button class="btn btn-default btn-modal" data-form-url="{% url 'intervention:remove' intervention.id %}" title="{% trans 'Delete' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
{% endif %}
</div>
{% include 'intervention/detail/includes/controls.html' %}
</div>
</div>
<hr>
@@ -128,6 +88,10 @@
<th scope="row">{% trans 'Binding on' %}</th>
<td class="align-middle">{{intervention.legal.binding_date|default_if_none:""}}</td>
</tr>
<tr {% if intervention.legal.revocation %}class="alert alert-danger"{% endif %}>
<th scope="row">{% trans 'Revocation' %}</th>
<td class="align-middle">{{intervention.legal.revocation.date|naturalday|default_if_none:""}}</td>
</tr>
<tr>
<th scope="row">{% trans 'Last modified' %}</th>
<td class="align-middle">
@@ -171,9 +135,16 @@
<div class="col-sm-12 col-md-12 col-lg-6">
{% include 'intervention/detail/includes/eco-account-withdraws.html' %}
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
{% include 'intervention/detail/includes/revocation.html' %}
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
{% include 'intervention/detail/includes/documents.html' %}
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
</div>
</div>

View File

@@ -8,7 +8,7 @@ Created on: 30.11.20
from django.urls import path
from intervention.views import index_view, new_view, open_view, edit_view, remove_view, new_document_view, share_view, \
create_share_view
create_share_view, remove_revocation_view, new_revocation_view
app_name = "intervention"
urlpatterns = [
@@ -20,4 +20,8 @@ urlpatterns = [
path('<id>/remove', remove_view, name='remove'),
path('<id>/share/<token>', share_view, name='share'),
path('<id>/share', create_share_view, name='share-create'),
# Revocation routes
path('<id>/revocation/new', new_revocation_view, name='revocation-new'),
path('revocation/<id>/remove', remove_revocation_view, name='revocation-remove'),
]

View File

@@ -4,12 +4,14 @@ 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
from intervention.models import Intervention
from intervention.forms import NewInterventionForm, EditInterventionForm, ShareInterventionForm, NewRevocationForm
from intervention.models import Intervention, Revocation
from intervention.tables import InterventionTable
from konova.contexts import BaseContext
from konova.decorators import *
from konova.forms import SimpleGeomForm, NewDocumentForm, RemoveModalForm
from konova.sub_settings.django_settings import DEFAULT_DATE_TIME_FORMAT, DEFAULT_DATE_FORMAT
from konova.utils.message_templates import FORM_INVALID
from konova.utils.user_checks import in_group
@@ -117,6 +119,14 @@ def open_view(request: HttpRequest, id: str):
instance=intervention
)
# Inform user about revocation
if intervention.legal.revocation:
messages.error(
request,
_("This intervention has a revocation from {}").format(intervention.legal.revocation.date.strftime(DEFAULT_DATE_FORMAT)),
extra_tags="danger",
)
context = {
"intervention": intervention,
"compensations": compensations,
@@ -185,6 +195,26 @@ def remove_view(request: HttpRequest, id: str):
)
@login_required
@default_group_required
def remove_revocation_view(request: HttpRequest, id: str):
""" Renders a remove view for a revocation
Args:
request (HttpRequest): The incoming request
id (str): The revocation's id as string
Returns:
"""
obj = Revocation.objects.get(id=id)
form = RemoveModalForm(request.POST or None, instance=obj, user=request.user)
return form.process_request(
request,
_("Revocation removed"),
)
@login_required
def share_view(request: HttpRequest, id: str, token: str):
""" Performs sharing of an intervention
@@ -256,3 +286,40 @@ def create_share_view(request: HttpRequest, id: str):
raise NotImplementedError
@login_required
def new_revocation_view(request: HttpRequest, id: str):
""" Renders sharing form for an intervention
Args:
request (HttpRequest): The incoming request
id (str): Intervention's id
Returns:
"""
intervention = get_object_or_404(Intervention, id=id)
form = NewRevocationForm(request.POST or None, request.FILES or None, instance=intervention, user=request.user)
if request.method == "POST":
if form.is_valid():
form.save()
messages.info(
request,
_("Revocation added")
)
else:
messages.error(
request,
FORM_INVALID,
extra_tags="danger",
)
return redirect(request.META.get("HTTP_REFERER", "home"))
elif request.method == "GET":
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, form.template, context)
else:
raise NotImplementedError