From 7535f008b79ca7a2f9e618c293d0fbeb4e360a35 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Fri, 4 Feb 2022 16:56:08 +0100 Subject: [PATCH 01/31] #86 Logs * adds log detail support for compensation state and action --- compensation/models/action.py | 11 ++++++++++ compensation/models/compensation.py | 9 ++++---- compensation/models/eco_account.py | 2 +- compensation/models/state.py | 13 +++++++++++ compensation/views/compensation.py | 11 +++++----- compensation/views/eco_account.py | 11 +++++----- ema/views.py | 11 +++++----- intervention/forms/modalForms.py | 2 +- intervention/models/intervention.py | 4 +++- konova/models/object.py | 34 +++++++++++++++++++---------- konova/utils/message_templates.py | 13 +++++++++-- 11 files changed, 84 insertions(+), 37 deletions(-) diff --git a/compensation/models/action.py b/compensation/models/action.py index 087f48be..bd2400e7 100644 --- a/compensation/models/action.py +++ b/compensation/models/action.py @@ -12,6 +12,7 @@ from codelist.models import KonovaCode from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_DETAIL_ID from compensation.managers import CompensationActionManager from konova.models import BaseResource +from konova.utils.message_templates import COMPENSATION_ACTION_REMOVED class UnitChoices(models.TextChoices): @@ -75,3 +76,13 @@ class CompensationAction(BaseResource): if choice[0] == self.unit: return choice[1] return None + + def delete(self, user=None, *args, **kwargs): + from compensation.models import Compensation + if user: + comps = Compensation.objects.filter( + actions__id__in=[self.id] + ).distinct() + for comp in comps: + comp.mark_as_edited(user, edit_comment=COMPENSATION_ACTION_REMOVED) + super().delete(*args, **kwargs) \ No newline at end of file diff --git a/compensation/models/compensation.py b/compensation/models/compensation.py index f50553f0..ef8e20fe 100644 --- a/compensation/models/compensation.py +++ b/compensation/models/compensation.py @@ -21,7 +21,7 @@ from konova.models import BaseObject, AbstractDocument, Deadline, generate_docum GeoReferencedMixin from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, COMPENSATION_REMOVED_TEMPLATE, \ - DOCUMENT_REMOVED_TEMPLATE + DOCUMENT_REMOVED_TEMPLATE, COMPENSATION_EDITED_TEMPLATE from user.models import UserActionLogEntry @@ -61,7 +61,6 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin): user = form.user with transaction.atomic(): created_action = UserActionLogEntry.get_created_action(user) - edited_action = UserActionLogEntry.get_edited_action(user, _("Added deadline")) deadline = Deadline.objects.create( type=form_data["type"], @@ -70,9 +69,7 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin): created=created_action, ) - self.modified = edited_action self.save() - self.log.add(edited_action) self.deadlines.add(deadline) return deadline @@ -332,7 +329,9 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin): Returns: """ - return self.intervention.mark_as_edited(user, request, edit_comment, reset_recorded) + self.intervention.unrecord(user, request) + action = super().mark_as_edited(user, edit_comment) + return action def is_ready_for_publish(self) -> bool: """ Not inherited by RecordableObjectMixin diff --git a/compensation/models/eco_account.py b/compensation/models/eco_account.py index af660615..895d537f 100644 --- a/compensation/models/eco_account.py +++ b/compensation/models/eco_account.py @@ -273,5 +273,5 @@ class EcoAccountDeduction(BaseResource): def delete(self, user=None, *args, **kwargs): if user is not None: self.intervention.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED) - self.account.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED, reset_recorded=False) + self.account.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED) super().delete(*args, **kwargs) \ No newline at end of file diff --git a/compensation/models/state.py b/compensation/models/state.py index ce0fc699..02249145 100644 --- a/compensation/models/state.py +++ b/compensation/models/state.py @@ -6,11 +6,13 @@ Created on: 16.11.21 """ from django.db import models +from django.db.models import Q from codelist.models import KonovaCode from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID from compensation.managers import CompensationStateManager from konova.models import UuidModel +from konova.utils.message_templates import COMPENSATION_STATE_REMOVED class CompensationState(UuidModel): @@ -45,3 +47,14 @@ class CompensationState(UuidModel): def __str__(self): return f"{self.biotope_type} | {self.surface} m²" + + def delete(self, user=None, *args, **kwargs): + from compensation.models import Compensation + if user: + comps = Compensation.objects.filter( + Q(before_states__id__in=[self.id]) | + Q(after_states__id__in=[self.id]) + ).distinct() + for comp in comps: + comp.mark_as_edited(user, edit_comment=COMPENSATION_STATE_REMOVED) + super().delete(*args, **kwargs) diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py index f6176e03..26e53fd2 100644 --- a/compensation/views/compensation.py +++ b/compensation/views/compensation.py @@ -18,7 +18,8 @@ from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION, \ - CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED + CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \ + COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED from konova.utils.user_checks import in_group @@ -347,7 +348,7 @@ def state_new_view(request: HttpRequest, id: str): form = NewStateModalForm(request.POST or None, instance=comp, request=request) return form.process_request( request, - msg_success=_("State added"), + msg_success=COMPENSATION_STATE_ADDED, redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" ) @@ -369,7 +370,7 @@ def action_new_view(request: HttpRequest, id: str): form = NewActionModalForm(request.POST or None, instance=comp, request=request) return form.process_request( request, - msg_success=_("Action added"), + msg_success=COMPENSATION_ACTION_ADDED, redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" ) @@ -437,7 +438,7 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): form = RemoveModalForm(request.POST or None, instance=state, request=request) return form.process_request( request, - msg_success=_("State removed"), + msg_success=COMPENSATION_STATE_REMOVED, redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" ) @@ -460,7 +461,7 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str): form = RemoveModalForm(request.POST or None, instance=action, request=request) return form.process_request( request, - msg_success=_("Action removed"), + msg_success=COMPENSATION_ACTION_REMOVED, redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" ) diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index d04359ef..27a113cb 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -30,7 +30,8 @@ from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ - CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED + CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \ + COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED from konova.utils.user_checks import in_group @@ -359,7 +360,7 @@ def state_new_view(request: HttpRequest, id: str): form = NewStateModalForm(request.POST or None, instance=acc, request=request) return form.process_request( request, - msg_success=_("State added"), + msg_success=COMPENSATION_STATE_ADDED, redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" ) @@ -381,7 +382,7 @@ def action_new_view(request: HttpRequest, id: str): form = NewActionModalForm(request.POST or None, instance=acc, request=request) return form.process_request( request, - msg_success=_("Action added"), + msg_success=COMPENSATION_ACTION_ADDED, redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" ) @@ -404,7 +405,7 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): form = RemoveModalForm(request.POST or None, instance=state, request=request) return form.process_request( request, - msg_success=_("State removed"), + msg_success=COMPENSATION_STATE_REMOVED, redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" ) @@ -427,7 +428,7 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str): form = RemoveModalForm(request.POST or None, instance=action, request=request) return form.process_request( request, - msg_success=_("Action removed"), + msg_success=COMPENSATION_ACTION_REMOVED, redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" ) diff --git a/ema/views.py b/ema/views.py index ff372170..44a92c93 100644 --- a/ema/views.py +++ b/ema/views.py @@ -21,7 +21,8 @@ from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ - DOCUMENT_ADDED + DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, \ + COMPENSATION_ACTION_ADDED from konova.utils.user_checks import in_group @@ -292,7 +293,7 @@ def state_new_view(request: HttpRequest, id: str): form = NewStateModalForm(request.POST or None, instance=ema, request=request) return form.process_request( request, - msg_success=_("State added"), + msg_success=COMPENSATION_STATE_ADDED, redirect_url=reverse("ema:detail", args=(id,)) + "#related_data" ) @@ -314,7 +315,7 @@ def action_new_view(request: HttpRequest, id: str): form = NewActionModalForm(request.POST or None, instance=ema, request=request) return form.process_request( request, - msg_success=_("Action added"), + msg_success=COMPENSATION_ACTION_ADDED, redirect_url=reverse("ema:detail", args=(id,)) + "#related_data" ) @@ -428,7 +429,7 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): form = RemoveModalForm(request.POST or None, instance=state, request=request) return form.process_request( request, - msg_success=_("State removed"), + msg_success=COMPENSATION_STATE_REMOVED, redirect_url=reverse("ema:detail", args=(id,)) + "#related_data" ) @@ -451,7 +452,7 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str): form = RemoveModalForm(request.POST or None, instance=action, request=request) return form.process_request( request, - msg_success=_("Action removed"), + msg_success=COMPENSATION_ACTION_REMOVED, redirect_url=reverse("ema:detail", args=(id,)) + "#related_data" ) diff --git a/intervention/forms/modalForms.py b/intervention/forms/modalForms.py index f8a419d2..e44649da 100644 --- a/intervention/forms/modalForms.py +++ b/intervention/forms/modalForms.py @@ -386,7 +386,7 @@ class NewDeductionModalForm(BaseModalForm): def save(self): deduction = self.__create_deduction() self.cleaned_data["intervention"].mark_as_edited(self.user, edit_comment=DEDUCTION_ADDED) - self.cleaned_data["account"].mark_as_edited(self.user, edit_comment=DEDUCTION_ADDED, reset_recorded=False) + self.cleaned_data["account"].mark_as_edited(self.user, edit_comment=DEDUCTION_ADDED) return deduction diff --git a/intervention/models/intervention.py b/intervention/models/intervention.py index 06b9174d..068594cb 100644 --- a/intervention/models/intervention.py +++ b/intervention/models/intervention.py @@ -242,7 +242,9 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec Returns: """ - action = super().mark_as_edited(performing_user, request, edit_comment, reset_recorded) + action = super().mark_as_edited(performing_user, edit_comment) + if reset_recorded: + self.unrecord(performing_user, request) if self.checked: self.set_unchecked() return action diff --git a/konova/models/object.py b/konova/models/object.py index 9eba8c1d..dfd5fbcc 100644 --- a/konova/models/object.py +++ b/konova/models/object.py @@ -132,6 +132,23 @@ class BaseObject(BaseResource): self.save() + def mark_as_edited(self, performing_user: User, edit_comment: str = None): + """ In case the object or a related object changed the log history needs to be updated + + Args: + performing_user (User): The user which performed the editing action + request (HttpRequest): The used request for this action + edit_comment (str): Additional comment for the log entry + + Returns: + + """ + edit_action = UserActionLogEntry.get_edited_action(performing_user, edit_comment) + self.modified = edit_action + self.log.add(edit_action) + self.save() + return edit_action + def add_log_entry(self, action: UserAction, user: User, comment: str): """ Wraps adding of UserActionLogEntry to log @@ -262,25 +279,18 @@ class RecordableObjectMixin(models.Model): return action - def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None, reset_recorded: bool = True): - """ In case the object or a related object changed, internal processes need to be started, such as - unrecord and uncheck + def unrecord(self, performing_user: User, request: HttpRequest = None): + """ Unrecords a dataset Args: performing_user (User): The user which performed the editing action request (HttpRequest): The used request for this action - edit_comment (str): Additional comment for the log entry - reset_recorded (bool): Whether the record-state of the object should be reset Returns: """ - edit_action = UserActionLogEntry.get_edited_action(performing_user, edit_comment) - self.modified = edit_action - self.log.add(edit_action) - self.save() - - if self.recorded and reset_recorded: + action = None + if self.recorded: action = self.set_unrecorded(performing_user) self.log.add(action) if request: @@ -288,7 +298,7 @@ class RecordableObjectMixin(models.Model): request, CHECKED_RECORDED_RESET ) - return edit_action + return action @abstractmethod def is_ready_for_publish(self) -> bool: diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index 79a190ca..c38b2e86 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -24,6 +24,17 @@ CANCEL_ACC_RECORDED_OR_DEDUCTED = _("Action canceled. Eco account is recorded or # COMPENSATION COMPENSATION_ADDED_TEMPLATE = _("Compensation {} added") COMPENSATION_REMOVED_TEMPLATE = _("Compensation {} removed") +COMPENSATION_EDITED_TEMPLATE = _("Compensation {} edited") +ADDED_COMPENSATION_ACTION = _("Added compensation action") +ADDED_COMPENSATION_STATE = _("Added compensation state") + +# COMPENSATION STATE +COMPENSATION_STATE_REMOVED = _("State removed") +COMPENSATION_STATE_ADDED = _("State added") + +# COMPENSATION ACTION +COMPENSATION_ACTION_ADDED = _("Action added") +COMPENSATION_ACTION_REMOVED = _("Action removed") # DEDUCTIONS DEDUCTION_ADDED = _("Deduction added") @@ -43,9 +54,7 @@ DOCUMENT_ADDED = _("Document added") # Edited EDITED_GENERAL_DATA = _("Edited general data") -ADDED_COMPENSATION_STATE = _("Added compensation state") ADDED_DEADLINE = _("Added deadline") -ADDED_COMPENSATION_ACTION = _("Added compensation action") # Geometry conflicts GEOMETRY_CONFLICT_WITH_TEMPLATE = _("Geometry conflict detected with {}") From 7d3c3f030b6c5b7d6791bba40630558bf5a95d2f Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Mon, 7 Feb 2022 09:56:37 +0100 Subject: [PATCH 02/31] # 86 Deadline removal log entry * adds log entries if deadline is removed --- compensation/forms/modalForms.py | 1 - compensation/models/compensation.py | 18 +++++++++++++++++- compensation/views/compensation.py | 12 +++++++----- compensation/views/eco_account.py | 11 ++++++----- ema/views.py | 11 ++++++----- konova/forms.py | 17 +++++++++++++++++ konova/utils/message_templates.py | 4 ++++ locale/de/LC_MESSAGES/django.mo | Bin 36530 -> 36537 bytes locale/de/LC_MESSAGES/django.po | 2 +- 9 files changed, 58 insertions(+), 18 deletions(-) diff --git a/compensation/forms/modalForms.py b/compensation/forms/modalForms.py index 9331f60a..c1761736 100644 --- a/compensation/forms/modalForms.py +++ b/compensation/forms/modalForms.py @@ -271,7 +271,6 @@ class NewDeadlineModalForm(BaseModalForm): def save(self): deadline = self.instance.add_deadline(self) - self.instance.mark_as_edited(self.user, self.request, ADDED_DEADLINE) return deadline diff --git a/compensation/models/compensation.py b/compensation/models/compensation.py index ef8e20fe..e2dc9c89 100644 --- a/compensation/models/compensation.py +++ b/compensation/models/compensation.py @@ -21,7 +21,7 @@ from konova.models import BaseObject, AbstractDocument, Deadline, generate_docum GeoReferencedMixin from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, COMPENSATION_REMOVED_TEMPLATE, \ - DOCUMENT_REMOVED_TEMPLATE, COMPENSATION_EDITED_TEMPLATE + DOCUMENT_REMOVED_TEMPLATE, COMPENSATION_EDITED_TEMPLATE, DEADLINE_REMOVED, ADDED_DEADLINE from user.models import UserActionLogEntry @@ -71,8 +71,24 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin): self.save() self.deadlines.add(deadline) + self.mark_as_edited(user, edit_comment=ADDED_DEADLINE) return deadline + def remove_deadline(self, form): + """ Removes a deadline from the abstract compensation + + Args: + form (DeadlineRemoveModalForm): The form holding all relevant data + + Returns: + + """ + deadline = form.deadline + user = form.user + with transaction.atomic(): + deadline.delete() + self.mark_as_edited(user, edit_comment=DEADLINE_REMOVED) + def add_action(self, form) -> CompensationAction: """ Adds a new action to the compensation diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py index 26e53fd2..e2456ecd 100644 --- a/compensation/views/compensation.py +++ b/compensation/views/compensation.py @@ -12,14 +12,15 @@ from compensation.tables import CompensationTable from intervention.models import Intervention from konova.contexts import BaseContext from konova.decorators import * -from konova.forms import RemoveModalForm, SimpleGeomForm +from konova.forms import RemoveModalForm, SimpleGeomForm, DeadlineRemoveModalForm from konova.models import Deadline from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION, \ CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \ - COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED + COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, \ + DEADLINE_ADDED, DEADLINE_REMOVED from konova.utils.user_checks import in_group @@ -392,7 +393,7 @@ def deadline_new_view(request: HttpRequest, id: str): form = NewDeadlineModalForm(request.POST or None, instance=comp, request=request) return form.process_request( request, - msg_success=_("Deadline added"), + msg_success=DEADLINE_ADDED, redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" ) @@ -411,11 +412,12 @@ def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str): Returns: """ + comp = get_object_or_404(Compensation, id=id) deadline = get_object_or_404(Deadline, id=deadline_id) - form = RemoveModalForm(request.POST or None, instance=deadline, request=request) + form = DeadlineRemoveModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) return form.process_request( request, - msg_success=_("Deadline removed"), + msg_success=DEADLINE_REMOVED, redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" ) diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index 27a113cb..34f979f0 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -23,7 +23,7 @@ from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm from konova.contexts import BaseContext from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \ shared_access_required -from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm +from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm, DeadlineRemoveModalForm from konova.models import Deadline from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER @@ -31,7 +31,7 @@ from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \ - COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED + COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED from konova.utils.user_checks import in_group @@ -447,11 +447,12 @@ def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str): Returns: """ + comp = get_object_or_404(EcoAccount, id=id) deadline = get_object_or_404(Deadline, id=deadline_id) - form = RemoveModalForm(request.POST or None, instance=deadline, request=request) + form = DeadlineRemoveModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) return form.process_request( request, - msg_success=_("Deadline removed"), + msg_success=DEADLINE_REMOVED, redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" ) @@ -473,7 +474,7 @@ def deadline_new_view(request: HttpRequest, id: str): form = NewDeadlineModalForm(request.POST or None, instance=acc, request=request) return form.process_request( request, - msg_success=_("Deadline added"), + msg_success=DEADLINE_ADDED, redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" ) diff --git a/ema/views.py b/ema/views.py index 44a92c93..82bfcdc0 100644 --- a/ema/views.py +++ b/ema/views.py @@ -14,7 +14,7 @@ from intervention.forms.modalForms import ShareModalForm from konova.contexts import BaseContext from konova.decorators import conservation_office_group_required, shared_access_required from ema.models import Ema, EmaDocument -from konova.forms import RemoveModalForm, SimpleGeomForm, RecordModalForm +from konova.forms import RemoveModalForm, SimpleGeomForm, RecordModalForm, DeadlineRemoveModalForm from konova.models import Deadline from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER @@ -22,7 +22,7 @@ from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, \ - COMPENSATION_ACTION_ADDED + COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED from konova.utils.user_checks import in_group @@ -337,7 +337,7 @@ def deadline_new_view(request: HttpRequest, id: str): form = NewDeadlineModalForm(request.POST or None, instance=ema, request=request) return form.process_request( request, - msg_success=_("Deadline added"), + msg_success=DEADLINE_ADDED, redirect_url=reverse("ema:detail", args=(id,)) + "#related_data" ) @@ -590,10 +590,11 @@ def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str): Returns: """ + ema = get_object_or_404(Ema, id=id) deadline = get_object_or_404(Deadline, id=deadline_id) - form = RemoveModalForm(request.POST or None, instance=deadline, request=request) + form = DeadlineRemoveModalForm(request.POST or None, instance=ema, deadline=deadline, request=request) return form.process_request( request, - msg_success=_("Deadline removed"), + msg_success=DEADLINE_REMOVED, redirect_url=reverse("ema:detail", args=(id,)) + "#related_data" ) \ No newline at end of file diff --git a/konova/forms.py b/konova/forms.py index ab55cb75..8b7539b3 100644 --- a/konova/forms.py +++ b/konova/forms.py @@ -330,6 +330,23 @@ class RemoveModalForm(BaseModalForm): self.instance.delete(self.user) +class DeadlineRemoveModalForm(RemoveModalForm): + """ Removing modal form for deadlines + + Can be used for anything, where removing shall be confirmed by the user a second time. + + """ + deadline = None + + def __init__(self, *args, **kwargs): + deadline = kwargs.pop("deadline", None) + self.deadline = deadline + super().__init__(*args, **kwargs) + + def save(self): + self.instance.remove_deadline(self) + + class NewDocumentForm(BaseModalForm): """ Modal form for new documents diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index c38b2e86..e13a9cae 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -40,6 +40,10 @@ COMPENSATION_ACTION_REMOVED = _("Action removed") DEDUCTION_ADDED = _("Deduction added") DEDUCTION_REMOVED = _("Deduction removed") +# DEADLINE +DEADLINE_ADDED = _("Deadline added") +DEADLINE_REMOVED = _("Deadline removed") + # PAYMENTS PAYMENT_ADDED = _("Payment added") PAYMENT_REMOVED = _("Payment removed") diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 23892cf84d1776aa8f508d127e5ea2d93a2b6cac..c25c2f113b0cf7f234e21d82feaf2dcc8f869568 100644 GIT binary patch delta 3137 zcmXZedra3=7{~D=D*ogq-athKZwPV|$}*8qDs|p6BsZFiIR}UeqCy5rUjrmH)LC=k z1#OW_agnCrr3up1*-W?6a)mk9#mbzy(v_yJ_lI--(ARmsmvf%;oO8ZEKCSmVUhj8s zS069T7?YN3Okd1GA1=ZyT<$!Jb;J>Q#_0JY*ay#JAYQ{zyorPGKU6YvPeqe!3 zI1C38C!ofgin^au;MvR;yBj4K%Z*x8$1RwQ=TI|#faG9;7uya>F_?HQ_QTDn3N@fA zdIE>wMO5OqP!+q60odJJLRo2q;xibJ8YmBYVwtlFdk}9z&2Ss4lm}29AHoo9MJ03r z{jdY|-ZkusH}Gk^iR#}QzSOQ=JgS2>iy$5hVD!|jSMc_!gZKbWXv&a#yBimX3S{Zfr>vzC3+iu_z1Q6 z{EO`pgyR6>c+^t7fSSO37w4gtrUH92zS-K@V9hX!8_mvdQ8WApHKWJQuFLIAeW(FK zP!${H9FKZ$25O*8)Qs~m6pK(x;h~T5O_jS*iz;EgvjO$sehkEq-Sy+B3bbJmUUYU~ zFXG>j(wcuU5l5`Bdm$5**vqI1%|}nAT0r9+EJtm|;FWgZSkyoXsNFpgHNZ>m`U31t zT#VYyl^Beh-2I&{ZbWUmW2lMTK_&2TCH0?8qf3cBpL0+pT8!EgtC3@GTCp#la`!Kz z68i_Bba2=8^<7>W9HCZI|)4OQA%s19;a zl_@}FUXJ~+62tL59EzWy-n)pJ>2*|pcTj784`cBmu0b!l%$Qv?8n6l{m9xHh8nu>T ztL=ASBx+_8P?eg68YtVvOHd_V<*dO-;%yj>&8YXUy6b=91)cw1q@hy$fLh}l&IhPc zg;dxzo{btH7d3E+vj+PU*SWX}RiRU;O?eL0|0NuZS8){H$IkN~`G%cQGU~Xc;Q*YB z+5@kkO1&Ock$QLk0BT03QJ>l?sDZynE!mH#lHWoN{5LA`Q1+bqAC80gXHxl3Yda5z zU_NR_>rgN5#G!Z$Rmv+k2_K_=)za76#IjNEO->+^bZPp4R$7is%T3k>#qUM zb3vuNj2h@yjKI678HKVl6-T3%Bm?zav9kiz;YQSq_n* zP-_-hZ5@rA43mT@xDV^F19fal_=PIO-57&GHP&$$Mf@tpp@&M~fV+PbrxIVsnHcky zG0$VEM`J0C6R3N#5cPf&`tT6y`4iX!TT%U- zMOEl)7kfX`P-*X=)+%U&-L-M3wMs#i`Z?4R%|Y#rg{b4T+}&S;dVT|Hz#XWBK0qJt zMeVgF)ZYcyt)7Y4Xm|ZQ)C(mTg{v?I>o67DF&ZCXHyru4HP)Hvd;&G|@h+Z-N?3y-<+nAh=1W&99C=p7R*G&wWx$nqK;=fW}?|-KO}Qer(`KcVI>a31{{HB zoWElraqwpQ1so{#tCmhfn`s$_;1<+_yItIPc>mDtQ+gz2<*q0!C_0>xTO9j8`gyy} delta 3129 zcmXZedra0<9LMn^a(VEEA}Wc%qXvQ~iJ>W$St@FHS(uACm!v6|gb0K%#9v-OGjn;F z;zF0PTA@8Q(`fD`)z zj@5>HVaAxpRv0r7=b;au$7e9#c?N5UBUTzS7>{B&p2OaF1^eSQ9EyLS`U%UiMq()O za16pEcReM?cxDI}(zq}PGu?x^*o(Lrkf0 zQ59{%XlzF%ejQb@+t>^L^=L%V2z}9*$ry(kXeowbv2!hk5LcsSxE)o>L#U3AU_Wd| zC3GGGumknp73_&u@e#a+>fei6W!Eka)xktm0y9wqWuYD{#EDpr>aY=mu^BbsX;eZN zFc81MG`xZfF}%Q-3|xtN{{)U^eA7zfNiJN+DoiLerUe@?33FaDW)$v1#UG*)y@5Ww zhuVDqp_ZV3kui}NhgyoMs0l1`@lw>%l;HrzH(R?KtQq>b(ct_XHN)RfGrI5m2Q||k zFWUh^P!)@Drl8)Nff^_SHRGk&A9GMkQI0;wH*4LEDpbdNoVBP2-^Jeep1XbmRe=`l zgYC`^3?u#lDXsY(lW|zF-3uA0#O9+Wv~_!*AHzcCpT z%ZzylvoHm>U^=$qcLuFoq5maPp6_O~z*@8VkYV%MEahqB`$J$7)!hzroXth1!&7QT<=QVR#wi@HTdz|HyTAMx#;3 zZ6Zcu8fp(LM3s6ysv>*b{X?i3wV^(>mrw(LiCVI+Q6;~Q8u(XK;-Tz0^&f>p`Dar3 zPiwmXqcIybqjjhkcVi45N0sstPQ&}CQ#0u`n^-35y=-S0YQSx%{tuu|RU;0@+vugx z2wiVC%M_eSoPnC@R#fR~P#>Cus23Yh34M$s@h2DiHrVgSC{*IpQO{*NJ?u^dRnf)` zT7MenxS&#AL=E&kM&V7=j6&I&iephrG6nTqp0fefjTw?{6ZDrUQEECYHKq3iJ!$JEJr19$lX7Nvxqx! zE)L(!?*kTkG*;1QLS^LNVt4IG)Fw(sbuh-mga=9zi|dgdy0B z>gNoqLZ7bVvi%lM{^hJIvz!ErcroBdlb0~J@H5;}=Go~@XPf1*AlbKkV5WCi+(D{wg0 u;z&I0{1JN-2XD7uz;LNwwMjIznR2loZb3b`*Tr>Bhhz4oH_cg5l=wf&skEB_ diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 65808d43..eb1b60a4 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -1117,7 +1117,7 @@ msgstr "Frist/Termin hinzugefügt" #: compensation/views/compensation.py:417 compensation/views/eco_account.py:453 #: ema/views.py:595 msgid "Deadline removed" -msgstr "Frist gelöscht" +msgstr "Frist/Termin gelöscht" #: compensation/views/compensation.py:440 compensation/views/eco_account.py:407 #: ema/views.py:430 From 98df0f93c3c117aec351cf62ec75a7f7adeaae07 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 09:27:28 +0100 Subject: [PATCH 03/31] Further tests * restructures compensation/tests into subtests for ecoaccount and compensation * adds tests for ema workflow * improved test data setup --- compensation/tests/compensation/__init__.py | 7 + .../tests/{ => compensation}/test_views.py | 185 +---------------- .../tests/{ => compensation}/test_workflow.py | 87 +++----- compensation/tests/ecoaccount/__init__.py | 7 + compensation/tests/ecoaccount/test_views.py | 194 ++++++++++++++++++ .../tests/ecoaccount/test_workflow.py | 84 ++++++++ ema/tests/test_views.py | 3 +- ema/tests/test_workflow.py | 165 +++++++++++++++ konova/models/object.py | 2 +- konova/tests/test_views.py | 42 +++- 10 files changed, 526 insertions(+), 250 deletions(-) create mode 100644 compensation/tests/compensation/__init__.py rename compensation/tests/{ => compensation}/test_views.py (53%) rename compensation/tests/{ => compensation}/test_workflow.py (79%) create mode 100644 compensation/tests/ecoaccount/__init__.py create mode 100644 compensation/tests/ecoaccount/test_views.py create mode 100644 compensation/tests/ecoaccount/test_workflow.py create mode 100644 ema/tests/test_workflow.py diff --git a/compensation/tests/compensation/__init__.py b/compensation/tests/compensation/__init__.py new file mode 100644 index 00000000..7cf7973a --- /dev/null +++ b/compensation/tests/compensation/__init__.py @@ -0,0 +1,7 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 07.02.22 + +""" diff --git a/compensation/tests/test_views.py b/compensation/tests/compensation/test_views.py similarity index 53% rename from compensation/tests/test_views.py rename to compensation/tests/compensation/test_views.py index 8d668220..4039496a 100644 --- a/compensation/tests/test_views.py +++ b/compensation/tests/compensation/test_views.py @@ -2,11 +2,11 @@ Author: Michel Peltriaux Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany Contact: michel.peltriaux@sgdnord.rlp.de -Created on: 27.10.21 +Created on: 07.02.22 """ +from django.test.client import Client from django.urls import reverse -from django.test import Client from konova.settings import DEFAULT_GROUP from konova.tests.test_views import BaseViewTestCase @@ -223,184 +223,3 @@ class CompensationViewTestCase(BaseViewTestCase): self.assert_url_fail(client, fail_urls) self.assert_url_success(client, success_urls) - -class EcoAccountViewTestCase(CompensationViewTestCase): - """ - These tests focus on proper returned views depending on the user's groups privileges and login status - - EcoAccounts can inherit the same tests used for compensations. - - """ - comp_state = None - comp_action = None - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - state = cls.create_dummy_states() - cls.eco_account.before_states.set([state]) - cls.eco_account.after_states.set([state]) - - action = cls.create_dummy_action() - cls.eco_account.actions.set([action]) - - # Prepare urls - cls.index_url = reverse("compensation:acc:index", args=()) - cls.new_url = reverse("compensation:acc:new", args=()) - cls.new_id_url = reverse("compensation:acc:new-id", args=()) - cls.detail_url = reverse("compensation:acc:detail", args=(cls.eco_account.id,)) - cls.log_url = reverse("compensation:acc:log", args=(cls.eco_account.id,)) - cls.edit_url = reverse("compensation:acc:edit", args=(cls.eco_account.id,)) - cls.remove_url = reverse("compensation:acc:remove", args=(cls.eco_account.id,)) - cls.report_url = reverse("compensation:acc:report", args=(cls.eco_account.id,)) - cls.state_new_url = reverse("compensation:acc:new-state", args=(cls.eco_account.id,)) - cls.action_new_url = reverse("compensation:acc:new-action", args=(cls.eco_account.id,)) - cls.deadline_new_url = reverse("compensation:acc:new-deadline", args=(cls.eco_account.id,)) - cls.new_doc_url = reverse("compensation:acc:new-doc", args=(cls.eco_account.id,)) - cls.state_remove_url = reverse("compensation:acc:state-remove", args=(cls.eco_account.id, cls.comp_state.id,)) - cls.action_remove_url = reverse("compensation:acc:action-remove", args=(cls.eco_account.id, cls.comp_action.id,)) - - def test_logged_in_no_groups_shared(self): - """ Check correct status code for all requests - - Assumption: User logged in and has no groups and data is shared - - Returns: - - """ - client = Client() - client.login(username=self.superuser.username, password=self.superuser_pw) - self.superuser.groups.set([]) - self.eco_account.share_with_list([self.superuser]) - - # Since the user has no groups, it does not matter that data has been shared. There SHOULD not be any difference - # to a user without access, since the important permissions are missing - success_urls = [ - self.index_url, - self.detail_url, - self.report_url, - ] - fail_urls = [ - self.new_url, - self.new_id_url, - self.log_url, - self.edit_url, - self.remove_url, - self.state_new_url, - self.action_new_url, - self.deadline_new_url, - self.state_remove_url, - self.action_remove_url, - self.new_doc_url, - ] - - self.assert_url_success(client, success_urls) - self.assert_url_fail(client, fail_urls) - - def test_logged_in_no_groups_unshared(self): - """ Check correct status code for all requests - - Assumption: User logged in and has no groups and data is shared - - Returns: - - """ - client = Client() - client.login(username=self.superuser.username, password=self.superuser_pw) - self.superuser.groups.set([]) - self.eco_account.share_with_list([]) - - # Since the user has no groups, it does not matter that data is unshared. There SHOULD not be any difference - # to a user having shared access, since all important permissions are missing - success_urls = [ - self.index_url, - self.detail_url, - self.report_url, - ] - fail_urls = [ - self.new_url, - self.new_id_url, - self.log_url, - self.edit_url, - self.remove_url, - self.state_new_url, - self.action_new_url, - self.deadline_new_url, - self.state_remove_url, - self.action_remove_url, - self.new_doc_url, - ] - - self.assert_url_success(client, success_urls) - self.assert_url_fail(client, fail_urls) - - def test_logged_in_default_group_shared(self): - """ Check correct status code for all requests - - Assumption: User logged in, is default group member and data is shared - --> Default group necessary since all base functionalities depend on this group membership - - Returns: - - """ - client = Client() - client.login(username=self.superuser.username, password=self.superuser_pw) - group = self.groups.get(name=DEFAULT_GROUP) - self.superuser.groups.set([group]) - # Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state - self.eco_account.share_with_list([self.superuser]) - - success_urls = [ - self.index_url, - self.detail_url, - self.report_url, - self.new_url, - self.new_id_url, - self.edit_url, - self.state_new_url, - self.action_new_url, - self.deadline_new_url, - self.state_remove_url, - self.action_remove_url, - self.new_doc_url, - self.log_url, - self.remove_url, - ] - self.assert_url_success(client, success_urls) - - def test_logged_in_default_group_unshared(self): - """ Check correct status code for all requests - - Assumption: User logged in, is default group member and data is NOT shared - --> Default group necessary since all base functionalities depend on this group membership - - Returns: - - """ - client = Client() - client.login(username=self.superuser.username, password=self.superuser_pw) - group = self.groups.get(name=DEFAULT_GROUP) - self.superuser.groups.set([group]) - self.eco_account.share_with_list([]) - - success_urls = [ - self.index_url, - self.detail_url, - self.report_url, - self.new_id_url, - self.new_url, - ] - fail_urls = [ - self.edit_url, - self.state_new_url, - self.action_new_url, - self.deadline_new_url, - self.state_remove_url, - self.action_remove_url, - self.new_doc_url, - self.log_url, - self.remove_url, - ] - self.assert_url_fail(client, fail_urls) - self.assert_url_success(client, success_urls) - diff --git a/compensation/tests/test_workflow.py b/compensation/tests/compensation/test_workflow.py similarity index 79% rename from compensation/tests/test_workflow.py rename to compensation/tests/compensation/test_workflow.py index 7f4864d6..d1f13782 100644 --- a/compensation/tests/test_workflow.py +++ b/compensation/tests/compensation/test_workflow.py @@ -2,7 +2,7 @@ Author: Michel Peltriaux Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany Contact: michel.peltriaux@sgdnord.rlp.de -Created on: 11.11.21 +Created on: 07.02.22 """ import datetime @@ -11,7 +11,7 @@ from django.contrib.gis.geos import MultiPolygon from django.urls import reverse from compensation.models import Compensation -from konova.settings import ETS_GROUP, ZB_GROUP +from konova.settings import ZB_GROUP, ETS_GROUP from konova.tests.test_views import BaseWorkflowTestCase from user.models import UserAction @@ -55,6 +55,7 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): "geom": test_geom.geojson, "intervention": self.intervention.id, } + pre_creation_intervention_log_count = self.intervention.log.count() # Preserve the current number of intervention's compensations num_compensations = self.intervention.compensations.count() @@ -66,6 +67,13 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): self.assertEqual(new_compensation.identifier, test_id) self.assertEqual(new_compensation.title, test_title) self.assert_equal_geometries(new_compensation.geometry.geom, test_geom) + self.assertEqual(new_compensation.log.count(), 1) + + # Expect logs to be set + self.assertEqual(pre_creation_intervention_log_count + 1, self.intervention.log.count()) + self.assertEqual(new_compensation.log.count(), 1) + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + self.assertEqual(new_compensation.log.first().action, UserAction.CREATED) def test_new_from_intervention(self): """ Test the creation of a compensation from a given intervention @@ -83,6 +91,7 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): "title": test_title, "geom": test_geom.geojson, } + pre_creation_intervention_log_count = self.intervention.log.count() # Preserve the current number of intervention's compensations num_compensations = self.intervention.compensations.count() @@ -95,6 +104,12 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): self.assertEqual(new_compensation.title, test_title) self.assert_equal_geometries(new_compensation.geometry.geom, test_geom) + # Expect logs to be set + self.assertEqual(new_compensation.log.count(), 1) + self.assertEqual(new_compensation.log.first().action, UserAction.CREATED) + self.assertEqual(pre_creation_intervention_log_count + 1, self.intervention.log.count()) + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + def test_edit(self): """ Checks that the editing of a compensation works @@ -103,6 +118,7 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): """ url = reverse("compensation:edit", args=(self.compensation.id,)) self.compensation = self.fill_out_compensation(self.compensation) + pre_edit_log_count = self.compensation.log.count() new_title = self.create_dummy_string() new_identifier = self.create_dummy_string() @@ -138,6 +154,10 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): self.assert_equal_geometries(self.compensation.geometry.geom, new_geometry) + # Expect logs to be set + self.assertEqual(pre_edit_log_count + 1, self.compensation.log.count()) + self.assertEqual(self.compensation.log.first().action, UserAction.EDITED) + def test_checkability(self): """ This tests if the checkability of the compensation (which is defined by the linked intervention's checked @@ -152,6 +172,8 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): # Add proper privilege for the user self.superuser.groups.add(self.groups.get(name=ZB_GROUP)) + pre_check_log_count = self.compensation.log.count() + # Prepare url and form data url = reverse("intervention:check", args=(self.intervention.id,)) post_data = { @@ -186,6 +208,7 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): # Expect the user action to be in the log self.assertIn(checked, self.compensation.log.all()) + self.assertEqual(pre_check_log_count + 1, self.compensation.log.count()) def test_recordability(self): """ @@ -200,6 +223,7 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): """ # Add proper privilege for the user self.superuser.groups.add(self.groups.get(name=ETS_GROUP)) + pre_record_log_count = self.compensation.log.count() # Prepare url and form data record_url = reverse("intervention:record", args=(self.intervention.id,)) @@ -234,62 +258,5 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): # Expect the user action to be in the log self.assertIn(recorded, self.compensation.log.all()) - - -class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): - @classmethod - def setUpTestData(cls): - super().setUpTestData() - - # Add user to conservation office group and give shared access to the account - cls.superuser.groups.add(cls.groups.get(name=ETS_GROUP)) - cls.eco_account.share_with_list([cls.superuser]) - - def test_deductability(self): - """ - This tests the deductability of an eco account. - - An eco account should only be deductible if it is recorded. - - Returns: - - """ - # Give user shared access to the dummy intervention, which will be needed here - self.intervention.share_with(self.superuser) - - # Prepare data for deduction creation - deduct_url = reverse("compensation:acc:new-deduction", args=(self.eco_account.id,)) - test_surface = 10.00 - post_data = { - "surface": test_surface, - "account": self.id, - "intervention": self.intervention.id, - } - # Perform request --> expect to fail - self.client_user.post(deduct_url, post_data) - - # Expect that no deduction has been created - self.assertEqual(0, self.eco_account.deductions.count()) - self.assertEqual(0, self.intervention.deductions.count()) - - # Now mock the eco account as it would be recorded (with invalid data) - # Make sure the deductible surface is high enough for the request - self.eco_account.set_recorded(self.superuser) - self.eco_account.refresh_from_db() - self.eco_account.deductable_surface = test_surface + 1.00 - self.eco_account.save() - self.assertIsNotNone(self.eco_account.recorded) - self.assertGreater(self.eco_account.deductable_surface, test_surface) - - # Rerun the request - self.client_user.post(deduct_url, post_data) - - # Expect that the deduction has been created - self.assertEqual(1, self.eco_account.deductions.count()) - self.assertEqual(1, self.intervention.deductions.count()) - deduction = self.eco_account.deductions.first() - self.assertEqual(deduction.surface, test_surface) - self.assertEqual(deduction.account, self.eco_account) - self.assertEqual(deduction.intervention, self.intervention) - + self.assertEqual(pre_record_log_count + 1, self.compensation.log.count()) diff --git a/compensation/tests/ecoaccount/__init__.py b/compensation/tests/ecoaccount/__init__.py new file mode 100644 index 00000000..7cf7973a --- /dev/null +++ b/compensation/tests/ecoaccount/__init__.py @@ -0,0 +1,7 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 07.02.22 + +""" diff --git a/compensation/tests/ecoaccount/test_views.py b/compensation/tests/ecoaccount/test_views.py new file mode 100644 index 00000000..c4e742fe --- /dev/null +++ b/compensation/tests/ecoaccount/test_views.py @@ -0,0 +1,194 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 27.10.21 + +""" +from django.urls import reverse +from django.test import Client + +from compensation.tests.compensation.test_views import CompensationViewTestCase +from konova.settings import DEFAULT_GROUP + + +class EcoAccountViewTestCase(CompensationViewTestCase): + """ + These tests focus on proper returned views depending on the user's groups privileges and login status + + EcoAccounts can inherit the same tests used for compensations. + + """ + comp_state = None + comp_action = None + + @classmethod + def setUpTestData(cls) -> None: + super().setUpTestData() + state = cls.create_dummy_states() + cls.eco_account.before_states.set([state]) + cls.eco_account.after_states.set([state]) + + action = cls.create_dummy_action() + cls.eco_account.actions.set([action]) + + # Prepare urls + cls.index_url = reverse("compensation:acc:index", args=()) + cls.new_url = reverse("compensation:acc:new", args=()) + cls.new_id_url = reverse("compensation:acc:new-id", args=()) + cls.detail_url = reverse("compensation:acc:detail", args=(cls.eco_account.id,)) + cls.log_url = reverse("compensation:acc:log", args=(cls.eco_account.id,)) + cls.edit_url = reverse("compensation:acc:edit", args=(cls.eco_account.id,)) + cls.remove_url = reverse("compensation:acc:remove", args=(cls.eco_account.id,)) + cls.report_url = reverse("compensation:acc:report", args=(cls.eco_account.id,)) + cls.state_new_url = reverse("compensation:acc:new-state", args=(cls.eco_account.id,)) + cls.action_new_url = reverse("compensation:acc:new-action", args=(cls.eco_account.id,)) + cls.deadline_new_url = reverse("compensation:acc:new-deadline", args=(cls.eco_account.id,)) + cls.new_doc_url = reverse("compensation:acc:new-doc", args=(cls.eco_account.id,)) + cls.state_remove_url = reverse("compensation:acc:state-remove", args=(cls.eco_account.id, cls.comp_state.id,)) + cls.action_remove_url = reverse("compensation:acc:action-remove", args=(cls.eco_account.id, cls.comp_action.id,)) + + def test_logged_in_no_groups_shared(self): + """ Check correct status code for all requests + + Assumption: User logged in and has no groups and data is shared + + Returns: + + """ + client = Client() + client.login(username=self.superuser.username, password=self.superuser_pw) + self.superuser.groups.set([]) + self.eco_account.share_with_list([self.superuser]) + + # Since the user has no groups, it does not matter that data has been shared. There SHOULD not be any difference + # to a user without access, since the important permissions are missing + success_urls = [ + self.index_url, + self.detail_url, + self.report_url, + ] + fail_urls = [ + self.new_url, + self.new_id_url, + self.log_url, + self.edit_url, + self.remove_url, + self.state_new_url, + self.action_new_url, + self.deadline_new_url, + self.state_remove_url, + self.action_remove_url, + self.new_doc_url, + ] + + self.assert_url_success(client, success_urls) + self.assert_url_fail(client, fail_urls) + + def test_logged_in_no_groups_unshared(self): + """ Check correct status code for all requests + + Assumption: User logged in and has no groups and data is shared + + Returns: + + """ + client = Client() + client.login(username=self.superuser.username, password=self.superuser_pw) + self.superuser.groups.set([]) + self.eco_account.share_with_list([]) + + # Since the user has no groups, it does not matter that data is unshared. There SHOULD not be any difference + # to a user having shared access, since all important permissions are missing + success_urls = [ + self.index_url, + self.detail_url, + self.report_url, + ] + fail_urls = [ + self.new_url, + self.new_id_url, + self.log_url, + self.edit_url, + self.remove_url, + self.state_new_url, + self.action_new_url, + self.deadline_new_url, + self.state_remove_url, + self.action_remove_url, + self.new_doc_url, + ] + + self.assert_url_success(client, success_urls) + self.assert_url_fail(client, fail_urls) + + def test_logged_in_default_group_shared(self): + """ Check correct status code for all requests + + Assumption: User logged in, is default group member and data is shared + --> Default group necessary since all base functionalities depend on this group membership + + Returns: + + """ + client = Client() + client.login(username=self.superuser.username, password=self.superuser_pw) + group = self.groups.get(name=DEFAULT_GROUP) + self.superuser.groups.set([group]) + # Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state + self.eco_account.share_with_list([self.superuser]) + + success_urls = [ + self.index_url, + self.detail_url, + self.report_url, + self.new_url, + self.new_id_url, + self.edit_url, + self.state_new_url, + self.action_new_url, + self.deadline_new_url, + self.state_remove_url, + self.action_remove_url, + self.new_doc_url, + self.log_url, + self.remove_url, + ] + self.assert_url_success(client, success_urls) + + def test_logged_in_default_group_unshared(self): + """ Check correct status code for all requests + + Assumption: User logged in, is default group member and data is NOT shared + --> Default group necessary since all base functionalities depend on this group membership + + Returns: + + """ + client = Client() + client.login(username=self.superuser.username, password=self.superuser_pw) + group = self.groups.get(name=DEFAULT_GROUP) + self.superuser.groups.set([group]) + self.eco_account.share_with_list([]) + + success_urls = [ + self.index_url, + self.detail_url, + self.report_url, + self.new_id_url, + self.new_url, + ] + fail_urls = [ + self.edit_url, + self.state_new_url, + self.action_new_url, + self.deadline_new_url, + self.state_remove_url, + self.action_remove_url, + self.new_doc_url, + self.log_url, + self.remove_url, + ] + self.assert_url_fail(client, fail_urls) + self.assert_url_success(client, success_urls) + diff --git a/compensation/tests/ecoaccount/test_workflow.py b/compensation/tests/ecoaccount/test_workflow.py new file mode 100644 index 00000000..92079207 --- /dev/null +++ b/compensation/tests/ecoaccount/test_workflow.py @@ -0,0 +1,84 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 11.11.21 + +""" +from django.urls import reverse + +from konova.settings import ETS_GROUP +from konova.tests.test_views import BaseWorkflowTestCase +from user.models import UserAction + + +class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): + @classmethod + def setUpTestData(cls): + super().setUpTestData() + + # Add user to conservation office group and give shared access to the account + cls.superuser.groups.add(cls.groups.get(name=ETS_GROUP)) + cls.eco_account.share_with_list([cls.superuser]) + + def test_deductability(self): + """ + This tests the deductability of an eco account. + + An eco account should only be deductible if it is recorded. + + Returns: + + """ + # Give user shared access to the dummy intervention, which will be needed here + self.intervention.share_with(self.superuser) + pre_deduction_acc_log_count = self.eco_account.log.count() + pre_deduction_int_log_count = self.intervention.log.count() + + # Prepare data for deduction creation + deduct_url = reverse("compensation:acc:new-deduction", args=(self.eco_account.id,)) + test_surface = 10.00 + post_data = { + "surface": test_surface, + "account": self.id, + "intervention": self.intervention.id, + } + # Perform request --> expect to fail + self.client_user.post(deduct_url, post_data) + + # Expect that no deduction has been created + self.assertEqual(0, self.eco_account.deductions.count()) + self.assertEqual(0, self.intervention.deductions.count()) + self.assertEqual(pre_deduction_acc_log_count, 0) + self.assertEqual(pre_deduction_int_log_count, 0) + + # Now mock the eco account as it would be recorded (with invalid data) + # Make sure the deductible surface is high enough for the request + self.eco_account.set_recorded(self.superuser) + self.eco_account.refresh_from_db() + self.eco_account.deductable_surface = test_surface + 1.00 + self.eco_account.save() + self.assertIsNotNone(self.eco_account.recorded) + self.assertGreater(self.eco_account.deductable_surface, test_surface) + # Expect the recorded entry in the log + self.assertEqual(pre_deduction_acc_log_count + 1, self.eco_account.log.count()) + self.assertTrue(self.eco_account.log.first().action == UserAction.RECORDED) + + # Rerun the request + self.client_user.post(deduct_url, post_data) + + # Expect that the deduction has been created + self.assertEqual(1, self.eco_account.deductions.count()) + self.assertEqual(1, self.intervention.deductions.count()) + deduction = self.eco_account.deductions.first() + self.assertEqual(deduction.surface, test_surface) + self.assertEqual(deduction.account, self.eco_account) + self.assertEqual(deduction.intervention, self.intervention) + + # Expect entries in the log + self.assertEqual(pre_deduction_acc_log_count + 2, self.eco_account.log.count()) + self.assertTrue(self.eco_account.log.first().action == UserAction.EDITED) + self.assertEqual(pre_deduction_int_log_count + 1, self.intervention.log.count()) + self.assertTrue(self.intervention.log.first().action == UserAction.EDITED) + + diff --git a/ema/tests/test_views.py b/ema/tests/test_views.py index 3d853e7b..dd37fced 100644 --- a/ema/tests/test_views.py +++ b/ema/tests/test_views.py @@ -5,11 +5,12 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 26.10.21 """ + from django.db.models import Q from django.urls import reverse from django.test.client import Client -from compensation.tests.test_views import CompensationViewTestCase +from compensation.tests.compensation.test_views import CompensationViewTestCase from ema.models import Ema from intervention.models import Responsibility from konova.models import Geometry diff --git a/ema/tests/test_workflow.py b/ema/tests/test_workflow.py new file mode 100644 index 00000000..3306a21f --- /dev/null +++ b/ema/tests/test_workflow.py @@ -0,0 +1,165 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 08.02.22 + +""" +import datetime + +from django.contrib.gis.geos import MultiPolygon +from django.core.exceptions import ObjectDoesNotExist +from django.urls import reverse + +from ema.models import Ema +from konova.settings import ETS_GROUP +from konova.tests.test_views import BaseWorkflowTestCase +from user.models import UserAction + + +class EmaWorkflowTestCase(BaseWorkflowTestCase): + ema = None + + @classmethod + def setUpTestData(cls) -> None: + super().setUpTestData() + + def setUp(self) -> None: + super().setUp() + # Create a fresh dummy (non-valid) compensation before each test + self.ema = self.create_dummy_ema() + + def test_new(self): + """ Test the creation of an Ema + + Returns: + + """ + self.superuser.groups.add(self.groups.get(name=ETS_GROUP)) + # Prepare url and form data to be posted + new_url = reverse("ema:new") + test_id = self.create_dummy_string() + test_title = self.create_dummy_string() + test_geom = self.create_dummy_geometry() + test_conservation_office = self.get_conservation_office_code() + post_data = { + "identifier": test_id, + "title": test_title, + "geom": test_geom.geojson, + "conservation_office": test_conservation_office.id + } + self.client_user.post(new_url, post_data) + + try: + ema = Ema.objects.get( + identifier=test_id + ) + except ObjectDoesNotExist: + self.fail(msg="Ema not created") + + self.assertEqual(ema.identifier, test_id) + self.assertEqual(ema.title, test_title) + self.assert_equal_geometries(ema.geometry.geom, test_geom) + self.assertEqual(ema.log.count(), 1) + + # Expect logs to be set + self.assertEqual(ema.log.count(), 1) + self.assertEqual(ema.log.first().action, UserAction.CREATED) + + def test_edit(self): + """ Checks that the editing of an Ema works + + Returns: + + """ + self.superuser.groups.add(self.groups.get(name=ETS_GROUP)) + self.ema.users.add(self.superuser) + url = reverse("ema:edit", args=(self.ema.id,)) + self.ema = self.fill_out_ema(self.ema) + pre_edit_log_count = self.ema.log.count() + + new_title = self.create_dummy_string() + new_identifier = self.create_dummy_string() + new_comment = self.create_dummy_string() + new_geometry = MultiPolygon(srid=4326) # Create an empty geometry + test_conservation_office = self.get_conservation_office_code() + + check_on_elements = { + self.ema.title: new_title, + self.ema.identifier: new_identifier, + self.ema.comment: new_comment, + } + for k, v in check_on_elements.items(): + self.assertNotEqual(k, v) + + post_data = { + "identifier": new_identifier, + "title": new_title, + "comment": new_comment, + "geom": new_geometry.geojson, + "conservation_office": test_conservation_office.id + } + self.client_user.post(url, post_data) + self.ema.refresh_from_db() + + check_on_elements = { + self.ema.title: new_title, + self.ema.identifier: new_identifier, + self.ema.comment: new_comment, + } + + for k, v in check_on_elements.items(): + self.assertEqual(k, v) + + self.assert_equal_geometries(self.ema.geometry.geom, new_geometry) + + # Expect logs to be set + self.assertEqual(pre_edit_log_count + 1, self.ema.log.count()) + self.assertEqual(self.ema.log.first().action, UserAction.EDITED) + + def test_recordability(self): + """ + This tests if the recordability of the Ema is triggered by the quality of it's data (e.g. not all fields filled) + + Returns: + + """ + # Add proper privilege for the user + self.superuser.groups.add(self.groups.get(name=ETS_GROUP)) + self.ema.users.add(self.superuser) + pre_record_log_count = self.ema.log.count() + + # Prepare url and form data + record_url = reverse("ema:record", args=(self.ema.id,)) + post_data = { + "confirm": True, + } + + # Make sure the ema is not recorded + self.assertIsNone(self.ema.recorded) + + # Run the request --> expect fail, since the Ema is not valid, yet + self.client_user.post(record_url, post_data) + + # Check that the Ema is still not recorded + self.assertIsNone(self.ema.recorded) + + # Now fill out the data for a compensation + self.ema = self.fill_out_ema(self.ema) + + # Rerun the request + self.client_user.post(record_url, post_data) + + # Expect the Ema now to be recorded + # Attention: We can only test the date part of the timestamp, + # since the delay in microseconds would lead to fail + self.ema.refresh_from_db() + recorded = self.ema.recorded + self.assertIsNotNone(recorded) + self.assertEqual(self.superuser, recorded.user) + self.assertEqual(UserAction.RECORDED, recorded.action) + self.assertEqual(datetime.date.today(), recorded.timestamp.date()) + + # Expect the user action to be in the log + self.assertIn(recorded, self.ema.log.all()) + self.assertEqual(pre_record_log_count + 1, self.ema.log.count()) diff --git a/konova/models/object.py b/konova/models/object.py index dfd5fbcc..0a802ed5 100644 --- a/konova/models/object.py +++ b/konova/models/object.py @@ -132,7 +132,7 @@ class BaseObject(BaseResource): self.save() - def mark_as_edited(self, performing_user: User, edit_comment: str = None): + def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None): """ In case the object or a related object changed the log history needs to be updated Args: diff --git a/konova/tests/test_views.py b/konova/tests/test_views.py index 1bf33325..a10acf16 100644 --- a/konova/tests/test_views.py +++ b/konova/tests/test_views.py @@ -7,6 +7,7 @@ Created on: 26.10.21 """ import datetime +from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID from ema.models import Ema from user.models import User from django.contrib.auth.models import Group @@ -15,7 +16,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.test import TestCase, Client from django.urls import reverse -from codelist.models import KonovaCode +from codelist.models import KonovaCode, KonovaCodeList from compensation.models import Compensation, CompensationState, CompensationAction, EcoAccount, EcoAccountDeduction from intervention.models import Legal, Responsibility, Intervention from konova.management.commands.setup_data import GROUPS_DATA @@ -236,10 +237,10 @@ class BaseTestCase(TestCase): """ codes = KonovaCode.objects.bulk_create([ - KonovaCode(id=1, is_selectable=True, long_name="Test1"), - KonovaCode(id=2, is_selectable=True, long_name="Test2"), - KonovaCode(id=3, is_selectable=True, long_name="Test3"), - KonovaCode(id=4, is_selectable=True, long_name="Test4"), + KonovaCode(id=1, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test1"), + KonovaCode(id=2, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test2"), + KonovaCode(id=3, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test3"), + KonovaCode(id=4, is_selectable=True, is_archived=False, is_leaf=True, long_name="Test4"), ]) return codes @@ -298,6 +299,37 @@ class BaseTestCase(TestCase): compensation.geometry.save() return compensation + @classmethod + def get_conservation_office_code(cls): + """ Returns a dummy KonovaCode as conservation office code + + Returns: + + """ + codelist = KonovaCodeList.objects.get_or_create( + id=CODELIST_CONSERVATION_OFFICE_ID + )[0] + code = KonovaCode.objects.get(id=2) + codelist.codes.add(code) + return code + + @classmethod + def fill_out_ema(cls, ema): + """ Adds all required (dummy) data to an Ema + + Returns: + """ + ema.responsible.conservation_office = cls.get_conservation_office_code() + ema.responsible.conservation_file_number = "test" + ema.responsible.handler = "handler" + ema.responsible.save() + ema.after_states.add(cls.comp_state) + ema.before_states.add(cls.comp_state) + ema.actions.add(cls.comp_action) + ema.geometry.geom = cls.create_dummy_geometry() + ema.geometry.save() + return ema + def assert_equal_geometries(self, geom1: MultiPolygon, geom2: MultiPolygon): """ Assert for geometries to be equal From 5eebd42c3c9d0ec879fa0d82b4611c6c11d37626 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 11:58:43 +0100 Subject: [PATCH 04/31] Further tests ecoaccount * adds ecoaccount workflow tests --- .../tests/ecoaccount/test_workflow.py | 153 +++++++++++++++++- konova/tests/test_views.py | 22 +++ 2 files changed, 172 insertions(+), 3 deletions(-) diff --git a/compensation/tests/ecoaccount/test_workflow.py b/compensation/tests/ecoaccount/test_workflow.py index 92079207..f394ec7c 100644 --- a/compensation/tests/ecoaccount/test_workflow.py +++ b/compensation/tests/ecoaccount/test_workflow.py @@ -5,9 +5,14 @@ Contact: michel.peltriaux@sgdnord.rlp.de Created on: 11.11.21 """ +import datetime + +from django.contrib.gis.geos import MultiPolygon +from django.core.exceptions import ObjectDoesNotExist from django.urls import reverse -from konova.settings import ETS_GROUP +from compensation.models import EcoAccount +from konova.settings import ETS_GROUP, DEFAULT_GROUP from konova.tests.test_views import BaseWorkflowTestCase from user.models import UserAction @@ -17,9 +22,151 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): def setUpTestData(cls): super().setUpTestData() + def setUp(self) -> None: + super().setUp() # Add user to conservation office group and give shared access to the account - cls.superuser.groups.add(cls.groups.get(name=ETS_GROUP)) - cls.eco_account.share_with_list([cls.superuser]) + self.superuser.groups.add(self.groups.get(name=DEFAULT_GROUP)) + self.superuser.groups.add(self.groups.get(name=ETS_GROUP)) + self.eco_account.share_with_list([self.superuser]) + + def test_new(self): + """ Test the creation of an EcoAccount + + Returns: + + """ + # Prepare url and form data to be posted + new_url = reverse("compensation:acc:new") + test_id = self.create_dummy_string() + test_title = self.create_dummy_string() + test_geom = self.create_dummy_geometry() + test_deductable_surface = 1000 + test_conservation_office = self.get_conservation_office_code() + post_data = { + "identifier": test_id, + "title": test_title, + "geom": test_geom.geojson, + "deductable_surface": test_deductable_surface, + "conservation_office": test_conservation_office.id + } + self.client_user.post(new_url, post_data) + + try: + acc = EcoAccount.objects.get( + identifier=test_id + ) + except ObjectDoesNotExist: + self.fail(msg="EcoAccount not created") + + self.assertEqual(acc.identifier, test_id) + self.assertEqual(acc.title, test_title) + self.assert_equal_geometries(acc.geometry.geom, test_geom) + self.assertEqual(acc.log.count(), 1) + + # Expect logs to be set + self.assertEqual(acc.log.count(), 1) + self.assertEqual(acc.log.first().action, UserAction.CREATED) + + def test_edit(self): + """ Checks that the editing of an EcoAccount works + + Returns: + + """ + self.eco_account.share_with(self.superuser) + + url = reverse("compensation:acc:edit", args=(self.eco_account.id,)) + pre_edit_log_count = self.eco_account.log.count() + + new_title = self.create_dummy_string() + new_identifier = self.create_dummy_string() + new_comment = self.create_dummy_string() + new_geometry = MultiPolygon(srid=4326) # Create an empty geometry + test_conservation_office = self.get_conservation_office_code() + test_deductable_surface = 10005 + + check_on_elements = { + self.eco_account.title: new_title, + self.eco_account.identifier: new_identifier, + self.eco_account.comment: new_comment, + self.eco_account.deductable_surface: test_deductable_surface, + } + for k, v in check_on_elements.items(): + self.assertNotEqual(k, v) + + post_data = { + "identifier": new_identifier, + "title": new_title, + "comment": new_comment, + "geom": new_geometry.geojson, + "surface": test_deductable_surface, + "conservation_office": test_conservation_office.id + } + self.client_user.post(url, post_data) + self.eco_account.refresh_from_db() + + check_on_elements = { + self.eco_account.title: new_title, + self.eco_account.identifier: new_identifier, + self.eco_account.deductable_surface: test_deductable_surface, + self.eco_account.comment: new_comment, + } + + for k, v in check_on_elements.items(): + self.assertEqual(k, v) + + self.assert_equal_geometries(self.eco_account.geometry.geom, new_geometry) + + # Expect logs to be set + self.assertEqual(pre_edit_log_count + 1, self.eco_account.log.count()) + self.assertEqual(self.eco_account.log.first().action, UserAction.EDITED) + + def test_recordability(self): + """ + This tests if the recordability of the EcoAccount is triggered by the quality of it's data (e.g. not all fields filled) + + Returns: + + """ + # Add proper privilege for the user + self.eco_account.share_with(self.superuser) + pre_record_log_count = self.eco_account.log.count() + + # Prepare url and form data + record_url = reverse("compensation:acc:record", args=(self.eco_account.id,)) + post_data = { + "confirm": True, + } + self.eco_account.refresh_from_db() + + # Make sure the account is not recorded + self.assertIsNone(self.eco_account.recorded) + + # Run the request --> expect fail, since the account is not valid, yet + self.client_user.post(record_url, post_data) + + # Check that the account is still not recorded + self.assertIsNone(self.eco_account.recorded) + + # Now fill out the data for an ecoaccount + self.eco_account = self.fill_out_eco_account(self.eco_account) + + # Rerun the request + self.client_user.post(record_url, post_data) + + # Expect the EcoAccount now to be recorded + # Attention: We can only test the date part of the timestamp, + # since the delay in microseconds would lead to fail + self.eco_account.refresh_from_db() + recorded = self.eco_account.recorded + self.assertIsNotNone(recorded) + self.assertEqual(self.superuser, recorded.user) + self.assertEqual(UserAction.RECORDED, recorded.action) + self.assertEqual(datetime.date.today(), recorded.timestamp.date()) + + # Expect the user action to be in the log + self.assertIn(recorded, self.eco_account.log.all()) + self.assertEqual(pre_record_log_count + 1, self.eco_account.log.count()) def test_deductability(self): """ diff --git a/konova/tests/test_views.py b/konova/tests/test_views.py index a10acf16..536a7868 100644 --- a/konova/tests/test_views.py +++ b/konova/tests/test_views.py @@ -330,6 +330,27 @@ class BaseTestCase(TestCase): ema.geometry.save() return ema + @classmethod + def fill_out_eco_account(cls, eco_account): + """ Adds all required (dummy) data to an EcoAccount + + Returns: + """ + eco_account.legal.registration_date = "2022-01-01" + eco_account.legal.save() + eco_account.responsible.conservation_office = cls.get_conservation_office_code() + eco_account.responsible.conservation_file_number = "test" + eco_account.responsible.handler = "handler" + eco_account.responsible.save() + eco_account.after_states.add(cls.comp_state) + eco_account.before_states.add(cls.comp_state) + eco_account.actions.add(cls.comp_action) + eco_account.geometry.geom = cls.create_dummy_geometry() + eco_account.geometry.save() + eco_account.deductable_surface = eco_account.get_state_after_surface_sum() + eco_account.save() + return eco_account + def assert_equal_geometries(self, geom1: MultiPolygon, geom2: MultiPolygon): """ Assert for geometries to be equal @@ -534,6 +555,7 @@ class BaseWorkflowTestCase(BaseTestCase): Returns: """ + super().setUp() # Set the default group as only group for the user default_group = self.groups.get(name=DEFAULT_GROUP) self.superuser.groups.set([default_group]) From 43bc3517ff5ca0468b918de66a5d42f82f8867fb Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 12:07:49 +0100 Subject: [PATCH 05/31] Further tests * adds tests for intervention workflow --- intervention/tests/test_workflow.py | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/intervention/tests/test_workflow.py b/intervention/tests/test_workflow.py index fbc2f81f..7644051b 100644 --- a/intervention/tests/test_workflow.py +++ b/intervention/tests/test_workflow.py @@ -74,6 +74,9 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): self.assertEqual(obj.identifier, test_id) self.assertEqual(obj.title, test_title) self.assert_equal_geometries(obj.geometry.geom, test_geom) + self.assertEqual(1, obj.log.count()) + self.assertEqual(obj.log.first().action, UserAction.CREATED) + self.assertEqual(obj.log.first().user, self.superuser) except ObjectDoesNotExist: # Fail if there is no such object self.fail() @@ -215,6 +218,8 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): # Make sure there are no payments on the intervention, yet self.assertEqual(0, self.intervention.payments.count()) + pre_payment_logs_count = self.intervention.log.count() + # Create form data to be sent to the url test_amount = 10.00 test_due = "2021-01-01" @@ -239,6 +244,10 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): self.assertEqual(payment.amount, test_amount) self.assertEqual(payment.due_on, datetime.date.fromisoformat(test_due)) self.assertEqual(payment.comment, test_comment) + + # Make sure a log entry has been created + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + self.assertEqual(pre_payment_logs_count + 1, self.intervention.log.count()) return payment def subtest_delete_payment(self, payment: Payment): @@ -250,6 +259,8 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): Returns: """ + pre_payment_logs_count = self.intervention.log.count() + # Create removing url for the payment remove_url = reverse("compensation:pay:remove", args=(payment.id,)) post_data = { @@ -266,6 +277,11 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): # Now make sure the intervention has no payments anymore self.assertEqual(0, self.intervention.payments.count()) + # Make sure a log entry has been created + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + self.assertEqual(self.intervention.log.first().user, self.superuser) + self.assertEqual(pre_payment_logs_count + 1, self.intervention.log.count()) + def test_payments(self): """ Checks a 'normal' case of adding a payment. @@ -353,6 +369,8 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): Returns: """ + pre_deduction_logs_count = self.intervention.log.count() + # Prepare the account for a working situation (enough deductable surface, recorded and shared) self.eco_account.deductable_surface = 10000.00 if self.eco_account.recorded is None: @@ -376,6 +394,11 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): ) self.assertEqual(deduction.surface, test_surface) + # Make sure a log entry has been created + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + self.assertEqual(self.intervention.log.first().user, self.superuser) + self.assertEqual(pre_deduction_logs_count + 1, self.intervention.log.count()) + # Return deduction for further usage in tests return deduction @@ -414,6 +437,8 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): Returns: """ + pre_delete_logs_count = self.intervention.log.count() + # Prepare url for deleting of this deduction delete_url = reverse("compensation:acc:remove-deduction", args=(self.eco_account.id, deduction.id,)) post_data = { @@ -433,6 +458,11 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): # Expect the deduction to be totally gone self.assert_object_is_deleted(deduction) + # Make sure a log entry has been created + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + self.assertEqual(self.intervention.log.first().user, self.superuser) + self.assertEqual(pre_delete_logs_count + 1, self.intervention.log.count()) + def test_deduction(self): """ Checks a 'normal case of adding a deduction. From 5ebb3f833a7d192743985c0e03ae4288e229829e Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 13:16:20 +0100 Subject: [PATCH 06/31] #86 Log detail enhancements * restructures removing of related data into separate sub-delete forms for easier logic handling --- compensation/forms/modalForms.py | 54 +++++++++++++++++- compensation/models/action.py | 10 ---- compensation/models/compensation.py | 35 +++++++++++- compensation/models/payment.py | 5 -- compensation/models/state.py | 11 ---- compensation/urls/payment.py | 4 +- compensation/views/compensation.py | 8 ++- compensation/views/eco_account.py | 12 ++-- compensation/views/payment.py | 20 ++++--- ema/views.py | 9 ++- intervention/forms/modalForms.py | 41 ++++++++++++- intervention/models/intervention.py | 37 +++++++++++- intervention/models/revocation.py | 5 +- .../detail/includes/payments.html | 2 +- .../detail/includes/revocation.html | 2 +- intervention/tests/test_workflow.py | 2 +- intervention/urls.py | 6 +- intervention/views.py | 14 +++-- konova/forms.py | 2 +- locale/de/LC_MESSAGES/django.mo | Bin 36537 -> 36538 bytes locale/de/LC_MESSAGES/django.po | 2 +- 21 files changed, 206 insertions(+), 75 deletions(-) diff --git a/compensation/forms/modalForms.py b/compensation/forms/modalForms.py index c1761736..a31dda0c 100644 --- a/compensation/forms/modalForms.py +++ b/compensation/forms/modalForms.py @@ -18,7 +18,7 @@ from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION CODELIST_COMPENSATION_ACTION_DETAIL_ID from compensation.models import CompensationDocument, EcoAccountDocument from konova.contexts import BaseContext -from konova.forms import BaseModalForm, NewDocumentForm +from konova.forms import BaseModalForm, NewDocumentForm, RemoveModalForm from konova.models import DeadlineType from konova.utils.message_templates import FORM_INVALID, ADDED_COMPENSATION_STATE, ADDED_DEADLINE, \ ADDED_COMPENSATION_ACTION, PAYMENT_ADDED @@ -100,10 +100,26 @@ class NewPaymentForm(BaseModalForm): def save(self): pay = self.instance.add_payment(self) - self.instance.mark_as_edited(self.user, self.request, edit_comment=PAYMENT_ADDED) return pay +class PaymentRemoveModalForm(RemoveModalForm): + """ Removing modal form for Payment + + Can be used for anything, where removing shall be confirmed by the user a second time. + + """ + payment = None + + def __init__(self, *args, **kwargs): + payment = kwargs.pop("payment", None) + self.payment = payment + super().__init__(*args, **kwargs) + + def save(self): + self.instance.remove_payment(self) + + class NewStateModalForm(BaseModalForm): """ Form handling state related input @@ -219,6 +235,40 @@ class NewStateModalForm(BaseModalForm): raise NotImplementedError +class CompensationStateRemoveModalForm(RemoveModalForm): + """ Removing modal form for CompensationState + + Can be used for anything, where removing shall be confirmed by the user a second time. + + """ + state = None + + def __init__(self, *args, **kwargs): + state = kwargs.pop("state", None) + self.state = state + super().__init__(*args, **kwargs) + + def save(self): + self.instance.remove_state(self) + + +class CompensationActionRemoveModalForm(RemoveModalForm): + """ Removing modal form for CompensationAction + + Can be used for anything, where removing shall be confirmed by the user a second time. + + """ + action = None + + def __init__(self, *args, **kwargs): + action = kwargs.pop("action", None) + self.action = action + super().__init__(*args, **kwargs) + + def save(self): + self.instance.remove_action(self) + + class NewDeadlineModalForm(BaseModalForm): """ Form handling deadline related input diff --git a/compensation/models/action.py b/compensation/models/action.py index bd2400e7..a5579159 100644 --- a/compensation/models/action.py +++ b/compensation/models/action.py @@ -76,13 +76,3 @@ class CompensationAction(BaseResource): if choice[0] == self.unit: return choice[1] return None - - def delete(self, user=None, *args, **kwargs): - from compensation.models import Compensation - if user: - comps = Compensation.objects.filter( - actions__id__in=[self.id] - ).distinct() - for comp in comps: - comp.mark_as_edited(user, edit_comment=COMPENSATION_ACTION_REMOVED) - super().delete(*args, **kwargs) \ No newline at end of file diff --git a/compensation/models/compensation.py b/compensation/models/compensation.py index e2dc9c89..46476a67 100644 --- a/compensation/models/compensation.py +++ b/compensation/models/compensation.py @@ -21,7 +21,8 @@ from konova.models import BaseObject, AbstractDocument, Deadline, generate_docum GeoReferencedMixin from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, COMPENSATION_REMOVED_TEMPLATE, \ - DOCUMENT_REMOVED_TEMPLATE, COMPENSATION_EDITED_TEMPLATE, DEADLINE_REMOVED, ADDED_DEADLINE + DOCUMENT_REMOVED_TEMPLATE, COMPENSATION_EDITED_TEMPLATE, DEADLINE_REMOVED, ADDED_DEADLINE, \ + COMPENSATION_ACTION_REMOVED, COMPENSATION_STATE_REMOVED from user.models import UserActionLogEntry @@ -114,6 +115,21 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin): self.actions.add(comp_action) return comp_action + def remove_action(self, form): + """ Removes a CompensationAction from the abstract compensation + + Args: + form (CompensationActionRemoveModalForm): The form holding all relevant data + + Returns: + + """ + action = form.action + user = form.user + with transaction.atomic(): + action.delete() + self.mark_as_edited(user, edit_comment=COMPENSATION_ACTION_REMOVED) + def add_state(self, form, is_before_state: bool) -> CompensationState: """ Adds a new compensation state to the compensation @@ -138,6 +154,21 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin): self.after_states.add(state) return state + def remove_state(self, form): + """ Removes a CompensationState from the abstract compensation + + Args: + form (CompensationStateRemoveModalForm): The form holding all relevant data + + Returns: + + """ + state = form.state + user = form.user + with transaction.atomic(): + state.delete() + self.mark_as_edited(user, edit_comment=COMPENSATION_STATE_REMOVED) + def get_surface_after_states(self) -> float: """ Calculates the compensation's/account's surface @@ -346,7 +377,7 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin): """ self.intervention.unrecord(user, request) - action = super().mark_as_edited(user, edit_comment) + action = super().mark_as_edited(user, edit_comment=edit_comment) return action def is_ready_for_publish(self) -> bool: diff --git a/compensation/models/payment.py b/compensation/models/payment.py index 48eec3fc..6f3f4c24 100644 --- a/compensation/models/payment.py +++ b/compensation/models/payment.py @@ -37,8 +37,3 @@ class Payment(BaseResource): ordering = [ "-amount", ] - - def delete(self, user=None, *args, **kwargs): - if user is not None: - self.intervention.mark_as_edited(user, edit_comment=PAYMENT_REMOVED) - super().delete(*args, **kwargs) diff --git a/compensation/models/state.py b/compensation/models/state.py index 02249145..5cb8376a 100644 --- a/compensation/models/state.py +++ b/compensation/models/state.py @@ -47,14 +47,3 @@ class CompensationState(UuidModel): def __str__(self): return f"{self.biotope_type} | {self.surface} m²" - - def delete(self, user=None, *args, **kwargs): - from compensation.models import Compensation - if user: - comps = Compensation.objects.filter( - Q(before_states__id__in=[self.id]) | - Q(after_states__id__in=[self.id]) - ).distinct() - for comp in comps: - comp.mark_as_edited(user, edit_comment=COMPENSATION_STATE_REMOVED) - super().delete(*args, **kwargs) diff --git a/compensation/urls/payment.py b/compensation/urls/payment.py index 2c8e5d4b..a400c636 100644 --- a/compensation/urls/payment.py +++ b/compensation/urls/payment.py @@ -10,6 +10,6 @@ from compensation.views.payment import * app_name = "pay" urlpatterns = [ - path('/new', new_payment_view, name='new'), - path('/remove', payment_remove_view, name='remove'), + path('/new', new_payment_view, name='new'), + path('/remove/', payment_remove_view, name='remove'), ] diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py index e2456ecd..eb4dd371 100644 --- a/compensation/views/compensation.py +++ b/compensation/views/compensation.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _ from compensation.forms.forms import NewCompensationForm, EditCompensationForm from compensation.forms.modalForms import NewStateModalForm, NewDeadlineModalForm, NewActionModalForm, \ - NewCompensationDocumentForm + NewCompensationDocumentForm, CompensationActionRemoveModalForm, CompensationStateRemoveModalForm from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument from compensation.tables import CompensationTable from intervention.models import Intervention @@ -436,8 +436,9 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): Returns: """ + comp = get_object_or_404(Compensation, id=id) state = get_object_or_404(CompensationState, id=state_id) - form = RemoveModalForm(request.POST or None, instance=state, request=request) + form = CompensationStateRemoveModalForm(request.POST or None, instance=comp, state=state, request=request) return form.process_request( request, msg_success=COMPENSATION_STATE_REMOVED, @@ -459,8 +460,9 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str): Returns: """ + comp = get_object_or_404(Compensation, id=id) action = get_object_or_404(CompensationAction, id=action_id) - form = RemoveModalForm(request.POST or None, instance=action, request=request) + form = CompensationActionRemoveModalForm(request.POST or None, instance=comp, action=action, request=request) return form.process_request( request, msg_success=COMPENSATION_ACTION_REMOVED, diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index 34f979f0..f6cced09 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -16,10 +16,10 @@ from django.shortcuts import render, get_object_or_404, redirect from compensation.forms.forms import NewEcoAccountForm, EditEcoAccountForm from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ - NewEcoAccountDocumentForm + NewEcoAccountDocumentForm, CompensationActionRemoveModalForm, CompensationStateRemoveModalForm from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction from compensation.tables import EcoAccountTable -from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm +from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, DeductionRemoveModalForm from konova.contexts import BaseContext from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \ shared_access_required @@ -286,7 +286,7 @@ def deduction_remove_view(request: HttpRequest, id: str, deduction_id: str): except ObjectDoesNotExist: raise Http404("Unknown deduction") - form = RemoveModalForm(request.POST or None, instance=eco_deduction, request=request) + form = DeductionRemoveModalForm(request.POST or None, instance=acc, deduction=eco_deduction, request=request) return form.process_request( request=request, msg_success=DEDUCTION_REMOVED, @@ -401,8 +401,9 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): Returns: """ + acc = get_object_or_404(EcoAccount, id=id) state = get_object_or_404(CompensationState, id=state_id) - form = RemoveModalForm(request.POST or None, instance=state, request=request) + form = CompensationStateRemoveModalForm(request.POST or None, instance=acc, state=state, request=request) return form.process_request( request, msg_success=COMPENSATION_STATE_REMOVED, @@ -424,8 +425,9 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str): Returns: """ + acc = get_object_or_404(EcoAccount, id=id) action = get_object_or_404(CompensationAction, id=action_id) - form = RemoveModalForm(request.POST or None, instance=action, request=request) + form = CompensationActionRemoveModalForm(request.POST or None, instance=acc, action=action, request=request) return form.process_request( request, msg_success=COMPENSATION_ACTION_REMOVED, diff --git a/compensation/views/payment.py b/compensation/views/payment.py index b715a3ae..d1fa91b2 100644 --- a/compensation/views/payment.py +++ b/compensation/views/payment.py @@ -11,7 +11,7 @@ from django.contrib.auth.decorators import login_required from django.http import HttpRequest from django.shortcuts import get_object_or_404 -from compensation.forms.modalForms import NewPaymentForm +from compensation.forms.modalForms import NewPaymentForm, PaymentRemoveModalForm from compensation.models import Payment from intervention.models import Intervention from konova.decorators import default_group_required @@ -21,39 +21,41 @@ from konova.utils.message_templates import PAYMENT_ADDED, PAYMENT_REMOVED @login_required @default_group_required -def new_payment_view(request: HttpRequest, intervention_id: str): +def new_payment_view(request: HttpRequest, id: str): """ Renders a modal view for adding new payments Args: request (HttpRequest): The incoming request - intervention_id (str): The intervention's id for which a new payment shall be added + id (str): The intervention's id for which a new payment shall be added Returns: """ - intervention = get_object_or_404(Intervention, id=intervention_id) + intervention = get_object_or_404(Intervention, id=id) form = NewPaymentForm(request.POST or None, instance=intervention, request=request) return form.process_request( request, msg_success=PAYMENT_ADDED, - redirect_url=reverse("intervention:detail", args=(intervention_id,)) + "#related_data" + redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data" ) @login_required @default_group_required -def payment_remove_view(request: HttpRequest, id: str): +def payment_remove_view(request: HttpRequest, id: str, payment_id: str): """ Renders a modal view for removing payments Args: request (HttpRequest): The incoming request - id (str): The payment's id + id (str): The intervention's id + payment_id (str): The payment's id Returns: """ - payment = get_object_or_404(Payment, id=id) - form = RemoveModalForm(request.POST or None, instance=payment, request=request) + intervention = get_object_or_404(Intervention, id=id) + payment = get_object_or_404(Payment, id=payment_id) + form = PaymentRemoveModalForm(request.POST or None, instance=intervention, payment=payment, request=request) return form.process_request( request=request, msg_success=PAYMENT_REMOVED, diff --git a/ema/views.py b/ema/views.py index 82bfcdc0..5f60c7a2 100644 --- a/ema/views.py +++ b/ema/views.py @@ -6,7 +6,8 @@ from django.shortcuts import render, get_object_or_404, redirect from django.urls import reverse from django.utils.translation import gettext_lazy as _ -from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm +from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ + CompensationActionRemoveModalForm, CompensationStateRemoveModalForm from compensation.models import CompensationAction, CompensationState from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentForm from ema.tables import EmaTable @@ -425,8 +426,9 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): Returns: """ + ema = get_object_or_404(Ema, id=id) state = get_object_or_404(CompensationState, id=state_id) - form = RemoveModalForm(request.POST or None, instance=state, request=request) + form = CompensationStateRemoveModalForm(request.POST or None, instance=ema, state=state, request=request) return form.process_request( request, msg_success=COMPENSATION_STATE_REMOVED, @@ -448,8 +450,9 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str): Returns: """ + ema = get_object_or_404(Ema, id=id) action = get_object_or_404(CompensationAction, id=action_id) - form = RemoveModalForm(request.POST or None, instance=action, request=request) + form = CompensationActionRemoveModalForm(request.POST or None, instance=ema, action=action, request=request) return form.process_request( request, msg_success=COMPENSATION_ACTION_REMOVED, diff --git a/intervention/forms/modalForms.py b/intervention/forms/modalForms.py index e44649da..d9112b1e 100644 --- a/intervention/forms/modalForms.py +++ b/intervention/forms/modalForms.py @@ -7,7 +7,7 @@ Created on: 27.09.21 """ from dal import autocomplete -from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED +from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED, DEDUCTION_REMOVED from user.models import User, UserActionLogEntry from django.db import transaction from django import forms @@ -16,7 +16,7 @@ from django.utils.translation import gettext_lazy as _ from compensation.models import EcoAccount, EcoAccountDeduction from intervention.inputs import TextToClipboardInput from intervention.models import Intervention, InterventionDocument -from konova.forms import BaseModalForm, NewDocumentForm +from konova.forms import BaseModalForm, NewDocumentForm, RemoveModalForm from konova.utils.general import format_german_float from konova.utils.user_checks import is_default_group_only @@ -172,6 +172,23 @@ class NewRevocationModalForm(BaseModalForm): return revocation +class RevocationRemoveModalForm(RemoveModalForm): + """ Removing modal form for Revocation + + Can be used for anything, where removing shall be confirmed by the user a second time. + + """ + revocation = None + + def __init__(self, *args, **kwargs): + revocation = kwargs.pop("revocation", None) + self.revocation = revocation + super().__init__(*args, **kwargs) + + def save(self): + self.instance.remove_revocation(self) + + class CheckModalForm(BaseModalForm): """ The modal form for running a check on interventions and their compensations @@ -390,5 +407,25 @@ class NewDeductionModalForm(BaseModalForm): return deduction +class DeductionRemoveModalForm(RemoveModalForm): + """ Removing modal form for EcoAccountDeduction + + Can be used for anything, where removing shall be confirmed by the user a second time. + + """ + deduction = None + + def __init__(self, *args, **kwargs): + deduction = kwargs.pop("deduction", None) + self.deduction = deduction + super().__init__(*args, **kwargs) + + def save(self): + with transaction.atomic(): + self.deduction.intervention.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED) + self.deduction.account.mark_as_edited(self.user, edit_comment=DEDUCTION_REMOVED) + self.deduction.delete() + + class NewInterventionDocumentForm(NewDocumentForm): document_model = InterventionDocument diff --git a/intervention/models/intervention.py b/intervention/models/intervention.py index 068594cb..79277339 100644 --- a/intervention/models/intervention.py +++ b/intervention/models/intervention.py @@ -16,7 +16,6 @@ from django.db import models, transaction from django.db.models import QuerySet from django.http import HttpRequest -from compensation.models import EcoAccountDeduction from intervention.managers import InterventionManager from intervention.models.legal import Legal from intervention.models.responsibility import Responsibility @@ -26,7 +25,8 @@ from konova.models import generate_document_file_upload_path, AbstractDocument, ShareableObjectMixin, \ RecordableObjectMixin, CheckableObjectMixin, GeoReferencedMixin from konova.settings import LANIS_LINK_TEMPLATE, LANIS_ZOOM_LUT, DEFAULT_SRID_RLP -from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, DEDUCTION_ADDED, DOCUMENT_REMOVED_TEMPLATE +from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, DOCUMENT_REMOVED_TEMPLATE, \ + PAYMENT_REMOVED, PAYMENT_ADDED, REVOCATION_REMOVED from user.models import UserActionLogEntry @@ -196,6 +196,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec comment=form_data.get("comment", None), intervention=self, ) + self.mark_as_edited(user, form.request, edit_comment=PAYMENT_ADDED) return pay def add_revocation(self, form): @@ -229,6 +230,21 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec ) return revocation + def remove_revocation(self, form): + """ Removes a revocation from the intervention + + Args: + form (RevocationRemoveModalForm): The form holding all relevant data + + Returns: + + """ + revocation = form.revocation + user = form.user + with transaction.atomic(): + revocation.delete() + self.mark_as_edited(user, request=form.request, edit_comment=REVOCATION_REMOVED) + def mark_as_edited(self, performing_user: User, request: HttpRequest = None, edit_comment: str = None, reset_recorded: bool = True): """ In case the object or a related object changed, internal processes need to be started, such as unrecord and uncheck @@ -242,7 +258,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec Returns: """ - action = super().mark_as_edited(performing_user, edit_comment) + action = super().mark_as_edited(performing_user, edit_comment=edit_comment) if reset_recorded: self.unrecord(performing_user, request) if self.checked: @@ -289,6 +305,21 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec """ return reverse("intervention:share", args=(self.id, self.access_token)) + def remove_payment(self, form): + """ Removes a Payment from the intervention + + Args: + form (PaymentRemoveModalForm): The form holding all relevant data + + Returns: + + """ + payment = form.payment + user = form.user + with transaction.atomic(): + payment.delete() + self.mark_as_edited(user, request=form.request, edit_comment=PAYMENT_REMOVED) + class InterventionDocument(AbstractDocument): """ diff --git a/intervention/models/revocation.py b/intervention/models/revocation.py index da39b001..3f55bbf4 100644 --- a/intervention/models/revocation.py +++ b/intervention/models/revocation.py @@ -23,7 +23,7 @@ class Revocation(BaseResource): legal = models.ForeignKey("Legal", null=False, blank=False, on_delete=models.CASCADE, help_text="Refers to 'Widerspruch am'", related_name="revocations") comment = models.TextField(null=True, blank=True) - def delete(self, user=None, *args, **kwargs): + def delete(self, *args, **kwargs): # Make sure related objects are being removed as well try: self.document.delete(*args, **kwargs) @@ -31,9 +31,6 @@ class Revocation(BaseResource): # No file to delete pass - if user is not None: - self.legal.intervention.mark_as_edited(user, edit_comment=REVOCATION_REMOVED) - super().delete() @property diff --git a/intervention/templates/intervention/detail/includes/payments.html b/intervention/templates/intervention/detail/includes/payments.html index 53a17f8f..205755bc 100644 --- a/intervention/templates/intervention/detail/includes/payments.html +++ b/intervention/templates/intervention/detail/includes/payments.html @@ -56,7 +56,7 @@ {% if is_default_member and has_access %} - {% endif %} diff --git a/intervention/templates/intervention/detail/includes/revocation.html b/intervention/templates/intervention/detail/includes/revocation.html index 9fb2989d..d6b07b72 100644 --- a/intervention/templates/intervention/detail/includes/revocation.html +++ b/intervention/templates/intervention/detail/includes/revocation.html @@ -65,7 +65,7 @@ {% if is_default_member and has_access %} - {% endif %} diff --git a/intervention/tests/test_workflow.py b/intervention/tests/test_workflow.py index 7644051b..69f606f0 100644 --- a/intervention/tests/test_workflow.py +++ b/intervention/tests/test_workflow.py @@ -262,7 +262,7 @@ class InterventionWorkflowTestCase(BaseWorkflowTestCase): pre_payment_logs_count = self.intervention.log.count() # Create removing url for the payment - remove_url = reverse("compensation:pay:remove", args=(payment.id,)) + remove_url = reverse("compensation:pay:remove", args=(self.intervention.id, payment.id,)) post_data = { "confirm": True, } diff --git a/intervention/urls.py b/intervention/urls.py index 1c663124..7d6ff32f 100644 --- a/intervention/urls.py +++ b/intervention/urls.py @@ -28,7 +28,7 @@ urlpatterns = [ path('/report', report_view, name='report'), # Compensations - path('/remove/', remove_compensation_view, name='remove-compensation'), + path('/compensation//remove', remove_compensation_view, name='remove-compensation'), # Documents path('/document/new/', new_document_view, name='new-doc'), @@ -37,10 +37,10 @@ urlpatterns = [ # Deductions path('/deduction/new', new_deduction_view, name='new-deduction'), - path('/remove/', remove_deduction_view, name='remove-deduction'), + path('/deduction//remove', remove_deduction_view, name='remove-deduction'), # Revocation routes path('/revocation/new', new_revocation_view, name='new-revocation'), - path('revocation//remove', remove_revocation_view, name='remove-revocation'), + path('/revocation//remove', remove_revocation_view, name='remove-revocation'), path('revocation/', get_revocation_view, name='get-doc-revocation'), ] \ No newline at end of file diff --git a/intervention/views.py b/intervention/views.py index a518a9da..259f3e0f 100644 --- a/intervention/views.py +++ b/intervention/views.py @@ -6,7 +6,8 @@ from django.shortcuts import render from intervention.forms.forms import NewInterventionForm, EditInterventionForm from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \ - CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm + CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm, DeductionRemoveModalForm, \ + RevocationRemoveModalForm from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument from intervention.tables import InterventionTable from konova.contexts import BaseContext @@ -340,7 +341,7 @@ def remove_view(request: HttpRequest, id: str): @login_required @default_group_required -def remove_revocation_view(request: HttpRequest, id: str): +def remove_revocation_view(request: HttpRequest, id: str, revocation_id: str): """ Renders a remove view for a revocation Args: @@ -350,13 +351,14 @@ def remove_revocation_view(request: HttpRequest, id: str): Returns: """ - obj = Revocation.objects.get(id=id) + intervention = get_object_or_404(Intervention, id=id) + revocation = get_object_or_404(Revocation, id=revocation_id) - form = RemoveModalForm(request.POST or None, instance=obj, request=request) + form = RevocationRemoveModalForm(request.POST or None, instance=intervention, revocation=revocation, request=request) return form.process_request( request, REVOCATION_REMOVED, - redirect_url=reverse("intervention:detail", args=(obj.intervention.id,)) + "#related_data" + redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data" ) @@ -533,7 +535,7 @@ def remove_deduction_view(request: HttpRequest, id: str, deduction_id: str): except ObjectDoesNotExist: raise Http404("Unknown deduction") - form = RemoveModalForm(request.POST or None, instance=eco_deduction, request=request) + form = DeductionRemoveModalForm(request.POST or None, instance=intervention, deduction=eco_deduction, request=request) return form.process_request( request=request, msg_success=DEDUCTION_REMOVED, diff --git a/konova/forms.py b/konova/forms.py index 8b7539b3..66aeea70 100644 --- a/konova/forms.py +++ b/konova/forms.py @@ -327,7 +327,7 @@ class RemoveModalForm(BaseModalForm): self.instance.mark_as_deleted(self.user) else: # If the class does not provide restorable delete functionality, we must delete the entry finally - self.instance.delete(self.user) + self.instance.delete() class DeadlineRemoveModalForm(RemoveModalForm): diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index c25c2f113b0cf7f234e21d82feaf2dcc8f869568..7fe3a4ee71d0b17b766706edd33f285e920fe645 100644 GIT binary patch delta 3692 zcmXZedvJ|M7{~FIgj^(%5TX&JjYJ|u;?}x0LRCj-Bh-wJiWG@^ToP#_4o%zzlXf!H zwbRIym{NwgbOargL1tQ7)u=J0T8f&MX$kuM+5PLYyJz2ho_%)TbIzg7UWYe(?U@(i zX=RKF&om|s6Y(V+ge`F#hT~jJz*X2BYtRSJVl38SV|;{~-+;|9Y=SW%*Z~!%C$`2k z48TbfjK{Q~k;{NLmSPYtM~zoFccMT2qZo?cy8adC4Q#{sJyb#gSvFuY`q3YXF_?}@ za6Sg&+ANPTku)|jpumUF7f+)C)}eOr6Dsg+)PoJE9r%A>cOHhSTm))<57fGEU<78N zc3zCZSb<7-yN8ArI*!Whobv`MU<1Zu@I+(U;cKXc#-I|-#U@yQO3Z^wWHl;q6?)@w z)DfISRixhOxk*E3bq{rh&rlP6CfSK?Pz!cIjSoN_&1m=e6dXl=j`K9Gryn`l&fkx^ zGv}}=UPD#r4w9h9G|*5+;n~(GRO#cfF?M(3y-^iPMP1IZsCl`l3Kn7vF2Tl)jHyOd z;TlZFGpHRu zL2@v`Q|U`zG*r?f*cR(hncqc~>@fyllWF$Sgko>{T~L9t(H{$) zrRYn44XRQVs7mfat-BX>32V`#j4sgd!g|z$*HD+{7QTviPywQ++q3I}T3`Swf#Ik? znW%aB*b5h-)~mrLSc{5x3YE~s>D1qc#uWzo;5E#^h#AHV!GmtfLN zV-Dg0Ou$*QjOmCQQT=aFiQY#)e1^JwzPa`YTIW)Ky-r;i(Cr?A+QB&2&qf{1Lev?T zJ1a4Y{sHHYs2%=`+R$^S&uqI>f7JTTP!)@H_V&=wgK4Nh8K@m+V+hVd9mOKl4ocm4 z8LEU^oRz3~JFzK#>BbMEDsT(~vCdhK&FFjnprO+Khl$u>j=c*RsKnky?PwgTQdwAz zMW}DE;JG$%Csd$B)a~w%3NX@*XJIJ)*{I80f z7pNmSh`JlcP>G$z&iDg%LGOH9!DQ@8KNFL29S*@0*bV*X+r;}}bG`q=X{2B#D!^x$ zgw^;u)}ziovcMXH`fg7|Rpu>JX-A-*&qP&bDk||J48sx($MvY+l0E3rgLO2t)9a`O zAE3_u5ys=Pe@{@`uk+U@6MfQ6j2Ak9Gi>lNJRGhJ{KOI%!f+Fg# z#xe%lV+F?I0n`Fl-S}U4o_+{fs1m=R&iIz|393@fitQP{jaqjyDsY~28MdUq!S#0+ zQ-77{I0L$sXHWqyVkBP07<`OcC~BeIQ8(1{t0XFCSlVh(CYOHt2l!ghEFRmsbkg3nPu zYAK6sVq;Oyd2(E1Au8Y}r~p-%iZ$2@A7e6xF1B}N5DuW9f!b*~s&d;S<~Wz(i$qW*tw9AihbrYIRG{Cn4L(HeD3qJ2en-@i z3_{JD?OcdjZxx2>{oh7Iw{$P+EjWr%cmcJ*ee}Y%rS{CCoN>sDVUn;L?!fg}k9uqJ z_=TE*TQLrUmRfsZ6#em-Kzy@^h7#E2CLF{!>0ie*j9+HVV9dwqcm$PD+;V$q6Hs@k z7iygp^u{#of$v~@T!Nan9rgTf^!U-(OG6JFL0_yzEp!@nC%$+6o2b%0Kpj=k3VUfg zqmHUOs?>u~N0g4b8xv7)*BtkGF>3zG71Un=H!`4%K1V;SLS44q*axpWBUjp6KL#~F z52LUE<8TA^#S<8d&(H^BR#`hayE=QUqW;=>Z#U2%l|U*gv0>N*Gu-E6o#RnIzY|f< zPesk2>H7H?K)(=m*_OERPf_!>pc1R{xDTpP3+_e*-0#M}Mom11x{N0=5&y;{d}X!$ zFPMSqm!T3mih4ay;7Dv#X1_;9q27`i7=`I2G+w4riP3nsRmF(XM!4`TKq!Z!PiL*E6#;@qdia14{q^ delta 3692 zcmXZee^k#`9LMpGQc6FGNGMWDN`)~$hW3Nw*N8dCjwAU|ew47%LOxr5Qykmrtg+Zx zIwNPsL1-)5FplY*jm<1`oG}~2oQ<;)&Ymy#{`I)`e(&eL-}im*=To&69>*#?_U8q= z?H$MIFvD@$VFHF>Dr$ThcEBYVj~lTq)?iCKi!s=UUic6-zZrutB*SrnF$zPmA9lns z*cxYNIIa^yBbNbBT!jI+4mDn8-GjdL>#z-eWBZq_*D;*&yQqYGXPSUX=tF-bM&krj zf=kgKH_vn(r!$T13@GqXY=x&$0UJ>}_z4yG7V5!f)DC=RnVpBADi?;D-xsy+E7%D$ zP&;3Nfmnu0c$Z5<3!Ok^cFuYo6|foOFkrUhbj1Ovg{Ghq%|&l4Kqa;kmB=Pk;41XQ z6R0CNiKzu0;j%jt(_n)!!N zcjg@W;Z;K$Sigy)ea&4@6aH1nP25Ma|1aRj?4FaW#6iaGYvX zMfT4%3D==2`knPUwkN*xkcJlUn`bf(MeR5N6>uc#_jL5d`F1=HndzTzZ6xeGE^n^qSiftx`g%UDx>o>Jg^D%;8oP6xrqbtHYz~(Y;$(KQ40)3B`_8h zC>=E~A74NhwO$Q+V?8R~DO5rivZ;Sd8kZRugjaDAhUM^)z#P;9$FLVRU@G3hHP~k% zuPh$McwCt4I6ZM2s{a)#(R=8FPf?f8dyzSU4vVP2UZ>s+=ytz~+QBs2&qN(f5$cS$ zS}QS<{$cBns2%=`+R!tr=VG%{AJqDRsES2f2f8%$;22b(G}MkWF&G!3jzaUagH?9C z1XaQcYb9#l9`wV{?D#QM1&*UXHd>o7h`##=4VCskOvI=q<}Re65_c%02A$a2DYKU2z8l@F@X3^shzOh3^>)OOLr8tll!Oyp5SC& z@1xHCA;#eoT!Ar#j`I;#;wl`zjPu2l$XPm}%guM72ezd@2vw9$W*zv#cJpCZDP$hmro$*cUV^pOAi_961N3A;t6*$kj7TeP=xBY!Z z)L$h!!GLb%8B~A^*cq>2G(JKt6tU9mC>iy-4aJT)0(A%8M3s6ysv;Hk_r0hMokV?U zFQMZ8?9$Mg{fa929aP|dPyvHUPyxE5&U`THY^Pus%tGyG4eGh=*cFeWDtQTq<1^GB zwPEj=#HOO2bF*xt2o>;sRDdefYgL1x_z06R_+4{XQt>7FX{eoUMOAJm>O->|_1s}p zLf>K({%QL`#m~PVu9HYZnU6+I%(AY<=ZT<7T7wF34pqvFs6fACI6gq_D43h6ehlhJ zQc?33S&LBXZNxTu|98^REj@sG3+gZu&!ZN&haT8@wK=m0Yft3GaQa{h?#6O#LcKM4 z{DsQF9ToW7DYnEOn~ZVRL~Gwo)L%OvXa|O%5*UF>Y&3dfn*DvMbvo+L?<~~w z^HB41Y(F1c(=S9_w$*n0L)5$qRAN=G{h=DQ;67BqLw5WN)WqYc%XktK@o!ARZYAb_ z!8BCA1eH)7>h)~EG;~VMcVrUkEy>16oLEew8;weg!c*2;=tn diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index eb1b60a4..f7a36715 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -1896,7 +1896,7 @@ msgstr "Zustand hinzugefügt" #: konova/utils/message_templates.py:44 msgid "Added compensation action" -msgstr "Maßnahme hinzufügen" +msgstr "Maßnahme hinzugefügt" #: konova/utils/message_templates.py:47 msgid "Geometry conflict detected with {}" From 07c6f19d5c186d1b4863ff4b1bad4c4fef93ee30 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 13:27:42 +0100 Subject: [PATCH 07/31] Test enhancements * adds more view tests to intervention tests --- intervention/tests/test_views.py | 44 ++++++++++++++++++++++++++++++++ intervention/views.py | 1 + 2 files changed, 45 insertions(+) diff --git a/intervention/tests/test_views.py b/intervention/tests/test_views.py index 23da913a..2cdb299d 100644 --- a/intervention/tests/test_views.py +++ b/intervention/tests/test_views.py @@ -10,6 +10,7 @@ from django.test import Client from django.contrib.auth.models import Group from django.urls import reverse +from intervention.models import Revocation from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.tests.test_views import BaseViewTestCase @@ -34,6 +35,17 @@ class InterventionViewTestCase(BaseViewTestCase): cls.record_url = reverse("intervention:record", args=(cls.intervention.id,)) cls.report_url = reverse("intervention:report", args=(cls.intervention.id,)) + cls.deduction.intervention = cls.intervention + cls.deduction.save() + cls.deduction_new_url = reverse("intervention:new-deduction", args=(cls.intervention.id,)) + cls.deduction_remove_url = reverse("intervention:remove-deduction", args=(cls.intervention.id, cls.deduction.id)) + + cls.revocation = Revocation.objects.create( + legal=cls.intervention.legal + ) + cls.revocation_new_url = reverse("intervention:new-revocation", args=(cls.intervention.id,)) + cls.revocation_remove_url = reverse("intervention:remove-revocation", args=(cls.intervention.id, cls.revocation.id)) + def test_views_anonymous_user(self): """ Check correct status code for all requests @@ -61,6 +73,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.share_create_url: f"{login_redirect_base}{self.share_create_url}", self.run_check_url: f"{login_redirect_base}{self.run_check_url}", self.record_url: f"{login_redirect_base}{self.record_url}", + self.deduction_new_url: f"{login_redirect_base}{self.deduction_new_url}", + self.deduction_remove_url: f"{login_redirect_base}{self.deduction_remove_url}", + self.revocation_new_url: f"{login_redirect_base}{self.revocation_new_url}", + self.revocation_remove_url: f"{login_redirect_base}{self.revocation_remove_url}", } self.assert_url_success(client, success_urls) @@ -96,6 +112,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.share_create_url, self.run_check_url, self.record_url, + self.revocation_new_url, + self.revocation_remove_url, + self.deduction_new_url, + self.deduction_remove_url, ] self.assert_url_success(client, success_urls) @@ -128,6 +148,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.edit_url, self.remove_url, self.share_create_url, + self.revocation_new_url, + self.revocation_remove_url, + self.deduction_new_url, + self.deduction_remove_url, ] fail_urls = [ self.run_check_url, @@ -172,6 +196,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.remove_url, self.share_create_url, self.log_url, + self.revocation_new_url, + self.revocation_remove_url, + self.deduction_new_url, + self.deduction_remove_url, ] success_urls_redirect = { self.share_url: self.detail_url @@ -212,6 +240,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.remove_url, self.share_create_url, self.record_url, + self.revocation_new_url, + self.revocation_remove_url, + self.deduction_new_url, + self.deduction_remove_url, ] success_urls_redirect = { self.share_url: self.detail_url @@ -252,6 +284,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.share_create_url, self.record_url, self.run_check_url, + self.revocation_new_url, + self.revocation_remove_url, + self.deduction_new_url, + self.deduction_remove_url, ] success_urls_redirect = { self.share_url: self.detail_url @@ -292,6 +328,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.remove_url, self.share_create_url, self.run_check_url, + self.revocation_new_url, + self.revocation_remove_url, + self.deduction_new_url, + self.deduction_remove_url, ] success_urls_redirect = { self.share_url: self.detail_url @@ -332,6 +372,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.remove_url, self.share_create_url, self.run_check_url, + self.revocation_new_url, + self.revocation_remove_url, + self.deduction_new_url, + self.deduction_remove_url, ] # Define urls where a redirect to a specific location is the proper response success_urls_redirect = { diff --git a/intervention/views.py b/intervention/views.py index 259f3e0f..41f484c5 100644 --- a/intervention/views.py +++ b/intervention/views.py @@ -341,6 +341,7 @@ def remove_view(request: HttpRequest, id: str): @login_required @default_group_required +@shared_access_required(Intervention, "id") def remove_revocation_view(request: HttpRequest, id: str, revocation_id: str): """ Renders a remove view for a revocation From 02ccb7808032113b83c22c8f615dd3e42d1b50f0 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 13:31:40 +0100 Subject: [PATCH 08/31] Remove form renaming * renames new remove modal forms to match a more coherent style --- compensation/forms/modalForms.py | 6 +++--- compensation/models/compensation.py | 6 +++--- compensation/views/compensation.py | 10 +++++----- compensation/views/eco_account.py | 14 +++++++------- compensation/views/payment.py | 4 ++-- ema/views.py | 10 +++++----- intervention/forms/modalForms.py | 4 ++-- intervention/models/intervention.py | 4 ++-- intervention/views.py | 8 ++++---- konova/forms.py | 2 +- 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/compensation/forms/modalForms.py b/compensation/forms/modalForms.py index a31dda0c..ae5a6dad 100644 --- a/compensation/forms/modalForms.py +++ b/compensation/forms/modalForms.py @@ -103,7 +103,7 @@ class NewPaymentForm(BaseModalForm): return pay -class PaymentRemoveModalForm(RemoveModalForm): +class RemovePaymentModalForm(RemoveModalForm): """ Removing modal form for Payment Can be used for anything, where removing shall be confirmed by the user a second time. @@ -235,7 +235,7 @@ class NewStateModalForm(BaseModalForm): raise NotImplementedError -class CompensationStateRemoveModalForm(RemoveModalForm): +class RemoveCompensationStateModalForm(RemoveModalForm): """ Removing modal form for CompensationState Can be used for anything, where removing shall be confirmed by the user a second time. @@ -252,7 +252,7 @@ class CompensationStateRemoveModalForm(RemoveModalForm): self.instance.remove_state(self) -class CompensationActionRemoveModalForm(RemoveModalForm): +class RemoveCompensationActionModalForm(RemoveModalForm): """ Removing modal form for CompensationAction Can be used for anything, where removing shall be confirmed by the user a second time. diff --git a/compensation/models/compensation.py b/compensation/models/compensation.py index 46476a67..e90e1735 100644 --- a/compensation/models/compensation.py +++ b/compensation/models/compensation.py @@ -79,7 +79,7 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin): """ Removes a deadline from the abstract compensation Args: - form (DeadlineRemoveModalForm): The form holding all relevant data + form (RemoveDeadlineModalForm): The form holding all relevant data Returns: @@ -119,7 +119,7 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin): """ Removes a CompensationAction from the abstract compensation Args: - form (CompensationActionRemoveModalForm): The form holding all relevant data + form (RemoveCompensationActionModalForm): The form holding all relevant data Returns: @@ -158,7 +158,7 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin): """ Removes a CompensationState from the abstract compensation Args: - form (CompensationStateRemoveModalForm): The form holding all relevant data + form (RemoveCompensationStateModalForm): The form holding all relevant data Returns: diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py index eb4dd371..c4b5c2f5 100644 --- a/compensation/views/compensation.py +++ b/compensation/views/compensation.py @@ -6,13 +6,13 @@ from django.utils.translation import gettext_lazy as _ from compensation.forms.forms import NewCompensationForm, EditCompensationForm from compensation.forms.modalForms import NewStateModalForm, NewDeadlineModalForm, NewActionModalForm, \ - NewCompensationDocumentForm, CompensationActionRemoveModalForm, CompensationStateRemoveModalForm + NewCompensationDocumentForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument from compensation.tables import CompensationTable from intervention.models import Intervention from konova.contexts import BaseContext from konova.decorators import * -from konova.forms import RemoveModalForm, SimpleGeomForm, DeadlineRemoveModalForm +from konova.forms import RemoveModalForm, SimpleGeomForm, RemoveDeadlineModalForm from konova.models import Deadline from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.utils.documents import get_document, remove_document @@ -414,7 +414,7 @@ def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str): """ comp = get_object_or_404(Compensation, id=id) deadline = get_object_or_404(Deadline, id=deadline_id) - form = DeadlineRemoveModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) + form = RemoveDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) return form.process_request( request, msg_success=DEADLINE_REMOVED, @@ -438,7 +438,7 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): """ comp = get_object_or_404(Compensation, id=id) state = get_object_or_404(CompensationState, id=state_id) - form = CompensationStateRemoveModalForm(request.POST or None, instance=comp, state=state, request=request) + form = RemoveCompensationStateModalForm(request.POST or None, instance=comp, state=state, request=request) return form.process_request( request, msg_success=COMPENSATION_STATE_REMOVED, @@ -462,7 +462,7 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str): """ comp = get_object_or_404(Compensation, id=id) action = get_object_or_404(CompensationAction, id=action_id) - form = CompensationActionRemoveModalForm(request.POST or None, instance=comp, action=action, request=request) + form = RemoveCompensationActionModalForm(request.POST or None, instance=comp, action=action, request=request) return form.process_request( request, msg_success=COMPENSATION_ACTION_REMOVED, diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index f6cced09..7d1e560f 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -16,14 +16,14 @@ from django.shortcuts import render, get_object_or_404, redirect from compensation.forms.forms import NewEcoAccountForm, EditEcoAccountForm from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ - NewEcoAccountDocumentForm, CompensationActionRemoveModalForm, CompensationStateRemoveModalForm + NewEcoAccountDocumentForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction from compensation.tables import EcoAccountTable -from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, DeductionRemoveModalForm +from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm from konova.contexts import BaseContext from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \ shared_access_required -from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm, DeadlineRemoveModalForm +from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm, RemoveDeadlineModalForm from konova.models import Deadline from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER @@ -286,7 +286,7 @@ def deduction_remove_view(request: HttpRequest, id: str, deduction_id: str): except ObjectDoesNotExist: raise Http404("Unknown deduction") - form = DeductionRemoveModalForm(request.POST or None, instance=acc, deduction=eco_deduction, request=request) + form = RemoveEcoAccountDeductionModalForm(request.POST or None, instance=acc, deduction=eco_deduction, request=request) return form.process_request( request=request, msg_success=DEDUCTION_REMOVED, @@ -403,7 +403,7 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): """ acc = get_object_or_404(EcoAccount, id=id) state = get_object_or_404(CompensationState, id=state_id) - form = CompensationStateRemoveModalForm(request.POST or None, instance=acc, state=state, request=request) + form = RemoveCompensationStateModalForm(request.POST or None, instance=acc, state=state, request=request) return form.process_request( request, msg_success=COMPENSATION_STATE_REMOVED, @@ -427,7 +427,7 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str): """ acc = get_object_or_404(EcoAccount, id=id) action = get_object_or_404(CompensationAction, id=action_id) - form = CompensationActionRemoveModalForm(request.POST or None, instance=acc, action=action, request=request) + form = RemoveCompensationActionModalForm(request.POST or None, instance=acc, action=action, request=request) return form.process_request( request, msg_success=COMPENSATION_ACTION_REMOVED, @@ -451,7 +451,7 @@ def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str): """ comp = get_object_or_404(EcoAccount, id=id) deadline = get_object_or_404(Deadline, id=deadline_id) - form = DeadlineRemoveModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) + form = RemoveDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) return form.process_request( request, msg_success=DEADLINE_REMOVED, diff --git a/compensation/views/payment.py b/compensation/views/payment.py index d1fa91b2..7be9bee8 100644 --- a/compensation/views/payment.py +++ b/compensation/views/payment.py @@ -11,7 +11,7 @@ from django.contrib.auth.decorators import login_required from django.http import HttpRequest from django.shortcuts import get_object_or_404 -from compensation.forms.modalForms import NewPaymentForm, PaymentRemoveModalForm +from compensation.forms.modalForms import NewPaymentForm, RemovePaymentModalForm from compensation.models import Payment from intervention.models import Intervention from konova.decorators import default_group_required @@ -55,7 +55,7 @@ def payment_remove_view(request: HttpRequest, id: str, payment_id: str): """ intervention = get_object_or_404(Intervention, id=id) payment = get_object_or_404(Payment, id=payment_id) - form = PaymentRemoveModalForm(request.POST or None, instance=intervention, payment=payment, request=request) + form = RemovePaymentModalForm(request.POST or None, instance=intervention, payment=payment, request=request) return form.process_request( request=request, msg_success=PAYMENT_REMOVED, diff --git a/ema/views.py b/ema/views.py index 5f60c7a2..900dde7c 100644 --- a/ema/views.py +++ b/ema/views.py @@ -7,7 +7,7 @@ from django.urls import reverse from django.utils.translation import gettext_lazy as _ from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ - CompensationActionRemoveModalForm, CompensationStateRemoveModalForm + RemoveCompensationActionModalForm, RemoveCompensationStateModalForm from compensation.models import CompensationAction, CompensationState from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentForm from ema.tables import EmaTable @@ -15,7 +15,7 @@ from intervention.forms.modalForms import ShareModalForm from konova.contexts import BaseContext from konova.decorators import conservation_office_group_required, shared_access_required from ema.models import Ema, EmaDocument -from konova.forms import RemoveModalForm, SimpleGeomForm, RecordModalForm, DeadlineRemoveModalForm +from konova.forms import RemoveModalForm, SimpleGeomForm, RecordModalForm, RemoveDeadlineModalForm from konova.models import Deadline from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER @@ -428,7 +428,7 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): """ ema = get_object_or_404(Ema, id=id) state = get_object_or_404(CompensationState, id=state_id) - form = CompensationStateRemoveModalForm(request.POST or None, instance=ema, state=state, request=request) + form = RemoveCompensationStateModalForm(request.POST or None, instance=ema, state=state, request=request) return form.process_request( request, msg_success=COMPENSATION_STATE_REMOVED, @@ -452,7 +452,7 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str): """ ema = get_object_or_404(Ema, id=id) action = get_object_or_404(CompensationAction, id=action_id) - form = CompensationActionRemoveModalForm(request.POST or None, instance=ema, action=action, request=request) + form = RemoveCompensationActionModalForm(request.POST or None, instance=ema, action=action, request=request) return form.process_request( request, msg_success=COMPENSATION_ACTION_REMOVED, @@ -595,7 +595,7 @@ def deadline_remove_view(request: HttpRequest, id: str, deadline_id: str): """ ema = get_object_or_404(Ema, id=id) deadline = get_object_or_404(Deadline, id=deadline_id) - form = DeadlineRemoveModalForm(request.POST or None, instance=ema, deadline=deadline, request=request) + form = RemoveDeadlineModalForm(request.POST or None, instance=ema, deadline=deadline, request=request) return form.process_request( request, msg_success=DEADLINE_REMOVED, diff --git a/intervention/forms/modalForms.py b/intervention/forms/modalForms.py index d9112b1e..6e2a2a39 100644 --- a/intervention/forms/modalForms.py +++ b/intervention/forms/modalForms.py @@ -172,7 +172,7 @@ class NewRevocationModalForm(BaseModalForm): return revocation -class RevocationRemoveModalForm(RemoveModalForm): +class RemoveRevocationModalForm(RemoveModalForm): """ Removing modal form for Revocation Can be used for anything, where removing shall be confirmed by the user a second time. @@ -407,7 +407,7 @@ class NewDeductionModalForm(BaseModalForm): return deduction -class DeductionRemoveModalForm(RemoveModalForm): +class RemoveEcoAccountDeductionModalForm(RemoveModalForm): """ Removing modal form for EcoAccountDeduction Can be used for anything, where removing shall be confirmed by the user a second time. diff --git a/intervention/models/intervention.py b/intervention/models/intervention.py index 79277339..821a835f 100644 --- a/intervention/models/intervention.py +++ b/intervention/models/intervention.py @@ -234,7 +234,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec """ Removes a revocation from the intervention Args: - form (RevocationRemoveModalForm): The form holding all relevant data + form (RemoveRevocationModalForm): The form holding all relevant data Returns: @@ -309,7 +309,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec """ Removes a Payment from the intervention Args: - form (PaymentRemoveModalForm): The form holding all relevant data + form (RemovePaymentModalForm): The form holding all relevant data Returns: diff --git a/intervention/views.py b/intervention/views.py index 41f484c5..c0317035 100644 --- a/intervention/views.py +++ b/intervention/views.py @@ -6,8 +6,8 @@ from django.shortcuts import render from intervention.forms.forms import NewInterventionForm, EditInterventionForm from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \ - CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm, DeductionRemoveModalForm, \ - RevocationRemoveModalForm + CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm, RemoveEcoAccountDeductionModalForm, \ + RemoveRevocationModalForm from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument from intervention.tables import InterventionTable from konova.contexts import BaseContext @@ -355,7 +355,7 @@ def remove_revocation_view(request: HttpRequest, id: str, revocation_id: str): intervention = get_object_or_404(Intervention, id=id) revocation = get_object_or_404(Revocation, id=revocation_id) - form = RevocationRemoveModalForm(request.POST or None, instance=intervention, revocation=revocation, request=request) + form = RemoveRevocationModalForm(request.POST or None, instance=intervention, revocation=revocation, request=request) return form.process_request( request, REVOCATION_REMOVED, @@ -536,7 +536,7 @@ def remove_deduction_view(request: HttpRequest, id: str, deduction_id: str): except ObjectDoesNotExist: raise Http404("Unknown deduction") - form = DeductionRemoveModalForm(request.POST or None, instance=intervention, deduction=eco_deduction, request=request) + form = RemoveEcoAccountDeductionModalForm(request.POST or None, instance=intervention, deduction=eco_deduction, request=request) return form.process_request( request=request, msg_success=DEDUCTION_REMOVED, diff --git a/konova/forms.py b/konova/forms.py index 66aeea70..ca7ac0e1 100644 --- a/konova/forms.py +++ b/konova/forms.py @@ -330,7 +330,7 @@ class RemoveModalForm(BaseModalForm): self.instance.delete() -class DeadlineRemoveModalForm(RemoveModalForm): +class RemoveDeadlineModalForm(RemoveModalForm): """ Removing modal form for deadlines Can be used for anything, where removing shall be confirmed by the user a second time. From d5a3c707883f63e6c5215d7c8ac7b3562ace9be4 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 14:31:11 +0100 Subject: [PATCH 09/31] #86 Email enhancement * adds object titles to email sending --- konova/models/object.py | 16 +++++++----- konova/tasks.py | 24 +++++++++--------- konova/utils/mailer.py | 19 +++++++++----- .../email/checking/shared_data_checked.html | 2 ++ .../email/deleting/shared_data_deleted.html | 2 ++ .../email/recording/shared_data_recorded.html | 2 ++ .../recording/shared_data_unrecorded.html | 2 ++ .../email/sharing/shared_access_given.html | 2 ++ .../email/sharing/shared_access_removed.html | 2 ++ user/models/user.py | 25 ++++++++++--------- 10 files changed, 60 insertions(+), 36 deletions(-) diff --git a/konova/models/object.py b/konova/models/object.py index 0a802ed5..06256abb 100644 --- a/konova/models/object.py +++ b/konova/models/object.py @@ -128,7 +128,7 @@ class BaseObject(BaseResource): # Send mail shared_users = self.shared_users.values_list("id", flat=True) for user_id in shared_users: - celery_send_mail_shared_data_deleted.delay(self.identifier, user_id) + celery_send_mail_shared_data_deleted.delay(self.identifier, self.title, user_id) self.save() @@ -217,6 +217,10 @@ class BaseObject(BaseResource): _str = "{}{}-{}".format(curr_month, curr_year, rand_str) return definitions[self.__class__]["template"].format(_str) + @abstractmethod + def get_detail_url(self): + raise NotImplementedError() + class RecordableObjectMixin(models.Model): """ Wraps record related fields and functionality @@ -253,7 +257,7 @@ class RecordableObjectMixin(models.Model): shared_users = self.users.all().values_list("id", flat=True) for user_id in shared_users: - celery_send_mail_shared_data_unrecorded.delay(self.identifier, user_id) + celery_send_mail_shared_data_unrecorded.delay(self.identifier, self.title, user_id) return action @@ -275,7 +279,7 @@ class RecordableObjectMixin(models.Model): shared_users = self.users.all().values_list("id", flat=True) for user_id in shared_users: - celery_send_mail_shared_data_recorded.delay(self.identifier, user_id) + celery_send_mail_shared_data_recorded.delay(self.identifier, self.title, user_id) return action @@ -360,7 +364,7 @@ class CheckableObjectMixin(models.Model): # Send mail shared_users = self.users.all().values_list("id", flat=True) for user_id in shared_users: - celery_send_mail_shared_data_checked.delay(self.identifier, user_id) + celery_send_mail_shared_data_checked.delay(self.identifier, self.title, user_id) self.log.add(action) return action @@ -474,9 +478,9 @@ class ShareableObjectMixin(models.Model): # Send mails for user in removed_users: - celery_send_mail_shared_access_removed.delay(self.identifier, user["id"]) + celery_send_mail_shared_access_removed.delay(self.identifier, self.title, user["id"]) for user in new_accessing_users: - celery_send_mail_shared_access_given.delay(self.identifier, user) + celery_send_mail_shared_access_given.delay(self.identifier, self.title, user) # Set new shared users self.share_with_list(users) diff --git a/konova/tasks.py b/konova/tasks.py index a463374c..4c528038 100644 --- a/konova/tasks.py +++ b/konova/tasks.py @@ -19,42 +19,42 @@ def celery_update_parcels(geometry_id: str, recheck: bool = True): @shared_task -def celery_send_mail_shared_access_removed(obj_identifier, user_id): +def celery_send_mail_shared_access_removed(obj_identifier, obj_title=None, user_id=None): from user.models import User user = User.objects.get(id=user_id) - user.send_mail_shared_access_removed(obj_identifier) + user.send_mail_shared_access_removed(obj_identifier, obj_title) @shared_task -def celery_send_mail_shared_access_given(obj_identifier, user_id): +def celery_send_mail_shared_access_given(obj_identifier, obj_title=None, user_id=None): from user.models import User user = User.objects.get(id=user_id) - user.send_mail_shared_access_given(obj_identifier) + user.send_mail_shared_access_given(obj_identifier, obj_title) @shared_task -def celery_send_mail_shared_data_recorded(obj_identifier, user_id): +def celery_send_mail_shared_data_recorded(obj_identifier, obj_title=None, user_id=None): from user.models import User user = User.objects.get(id=user_id) - user.send_mail_shared_data_recorded(obj_identifier) + user.send_mail_shared_data_recorded(obj_identifier, obj_title) @shared_task -def celery_send_mail_shared_data_unrecorded(obj_identifier, user_id): +def celery_send_mail_shared_data_unrecorded(obj_identifier, obj_title=None, user_id=None): from user.models import User user = User.objects.get(id=user_id) - user.send_mail_shared_data_unrecorded(obj_identifier) + user.send_mail_shared_data_unrecorded(obj_identifier, obj_title) @shared_task -def celery_send_mail_shared_data_deleted(obj_identifier, user_id): +def celery_send_mail_shared_data_deleted(obj_identifier, obj_title=None, user_id=None): from user.models import User user = User.objects.get(id=user_id) - user.send_mail_shared_data_deleted(obj_identifier) + user.send_mail_shared_data_deleted(obj_identifier, obj_title) @shared_task -def celery_send_mail_shared_data_checked(obj_identifier, user_id): +def celery_send_mail_shared_data_checked(obj_identifier, obj_title=None, user_id=None): from user.models import User user = User.objects.get(id=user_id) - user.send_mail_shared_data_checked(obj_identifier) + user.send_mail_shared_data_checked(obj_identifier, obj_title) diff --git a/konova/utils/mailer.py b/konova/utils/mailer.py index e174ae56..33907f3c 100644 --- a/konova/utils/mailer.py +++ b/konova/utils/mailer.py @@ -45,11 +45,12 @@ class Mailer: auth_password=self.auth_password ) - def send_mail_shared_access_removed(self, obj_identifier, user): + def send_mail_shared_access_removed(self, obj_identifier, obj_title, user): """ Send a mail if user has no access to the object anymore Args: obj_identifier (str): The object identifier + obj_title (str): The object title Returns: @@ -57,6 +58,7 @@ class Mailer: context = { "user": user, "obj_identifier": obj_identifier, + "obj_title": obj_title, "EMAIL_REPLY_TO": EMAIL_REPLY_TO, } msg = render_to_string("email/sharing/shared_access_removed.html", context) @@ -67,7 +69,7 @@ class Mailer: msg ) - def send_mail_shared_access_given(self, obj_identifier, user): + def send_mail_shared_access_given(self, obj_identifier, obj_title, user): """ Send a mail if user just got access to the object Args: @@ -79,6 +81,7 @@ class Mailer: context = { "user": user, "obj_identifier": obj_identifier, + "obj_title": obj_title, "EMAIL_REPLY_TO": EMAIL_REPLY_TO, } msg = render_to_string("email/sharing/shared_access_given.html", context) @@ -89,7 +92,7 @@ class Mailer: msg ) - def send_mail_shared_data_recorded(self, obj_identifier, user): + def send_mail_shared_data_recorded(self, obj_identifier, obj_title, user): """ Send a mail if the user's shared data has just been unrecorded Args: @@ -101,6 +104,7 @@ class Mailer: context = { "user": user, "obj_identifier": obj_identifier, + "obj_title": obj_title, "EMAIL_REPLY_TO": EMAIL_REPLY_TO, } msg = render_to_string("email/recording/shared_data_recorded.html", context) @@ -111,7 +115,7 @@ class Mailer: msg ) - def send_mail_shared_data_unrecorded(self, obj_identifier, user): + def send_mail_shared_data_unrecorded(self, obj_identifier, obj_title, user): """ Send a mail if the user's shared data has just been unrecorded Args: @@ -123,6 +127,7 @@ class Mailer: context = { "user": user, "obj_identifier": obj_identifier, + "obj_title": obj_title, "EMAIL_REPLY_TO": EMAIL_REPLY_TO, } msg = render_to_string("email/recording/shared_data_unrecorded.html", context) @@ -133,7 +138,7 @@ class Mailer: msg ) - def send_mail_shared_data_deleted(self, obj_identifier, user): + def send_mail_shared_data_deleted(self, obj_identifier, obj_title, user): """ Send a mail if shared data has just been deleted Args: @@ -145,6 +150,7 @@ class Mailer: context = { "user": user, "obj_identifier": obj_identifier, + "obj_title": obj_title, "EMAIL_REPLY_TO": EMAIL_REPLY_TO, } msg = render_to_string("email/deleting/shared_data_deleted.html", context) @@ -155,7 +161,7 @@ class Mailer: msg ) - def send_mail_shared_data_checked(self, obj_identifier, user): + def send_mail_shared_data_checked(self, obj_identifier, obj_title, user): """ Send a mail if shared data just has been checked Args: @@ -167,6 +173,7 @@ class Mailer: context = { "user": user, "obj_identifier": obj_identifier, + "obj_title": obj_title, "EMAIL_REPLY_TO": EMAIL_REPLY_TO, } msg = render_to_string("email/checking/shared_data_checked.html", context) diff --git a/templates/email/checking/shared_data_checked.html b/templates/email/checking/shared_data_checked.html index 0707cfbc..0b67ecc7 100644 --- a/templates/email/checking/shared_data_checked.html +++ b/templates/email/checking/shared_data_checked.html @@ -11,6 +11,8 @@
{{obj_identifier}}
+ {{obj_title}} +
{% trans 'This means, the responsible registration office just confirmed the correctness of this dataset.' %}

diff --git a/templates/email/deleting/shared_data_deleted.html b/templates/email/deleting/shared_data_deleted.html index b920f1ec..272b0fde 100644 --- a/templates/email/deleting/shared_data_deleted.html +++ b/templates/email/deleting/shared_data_deleted.html @@ -11,6 +11,8 @@
{{obj_identifier}}
+ "{{obj_title}}" +
{% trans 'If this should not have been happened, please contact us. See the signature for details.' %}

diff --git a/templates/email/recording/shared_data_recorded.html b/templates/email/recording/shared_data_recorded.html index f8db9182..6805c928 100644 --- a/templates/email/recording/shared_data_recorded.html +++ b/templates/email/recording/shared_data_recorded.html @@ -11,6 +11,8 @@
{{obj_identifier}}
+ "{{obj_title}}" +
{% trans 'This means the data is now publicly available, e.g. in LANIS' %}

diff --git a/templates/email/recording/shared_data_unrecorded.html b/templates/email/recording/shared_data_unrecorded.html index d4639c9e..1e0310ae 100644 --- a/templates/email/recording/shared_data_unrecorded.html +++ b/templates/email/recording/shared_data_unrecorded.html @@ -11,6 +11,8 @@
{{obj_identifier}}
+ "{{obj_title}}" +
{% trans 'This means the data is no longer publicly available.' %}

diff --git a/templates/email/sharing/shared_access_given.html b/templates/email/sharing/shared_access_given.html index b8b26b7a..140e7a88 100644 --- a/templates/email/sharing/shared_access_given.html +++ b/templates/email/sharing/shared_access_given.html @@ -11,6 +11,8 @@
{{obj_identifier}}
+ "{{obj_title}}" +
{% trans 'This means you can now edit this dataset.' %} {% trans 'The shared dataset appears now by default on your overview for this dataset type.' %}
diff --git a/templates/email/sharing/shared_access_removed.html b/templates/email/sharing/shared_access_removed.html index 86d4fddd..d1cbc5bf 100644 --- a/templates/email/sharing/shared_access_removed.html +++ b/templates/email/sharing/shared_access_removed.html @@ -11,6 +11,8 @@
{{obj_identifier}}
+ "{{obj_title}}" +
{% trans 'However, you are still able to view the dataset content.' %} {% trans 'Please use the provided search filter on the dataset`s overview pages to find them.' %}
diff --git a/user/models/user.py b/user/models/user.py index 6370f2d5..df63dd76 100644 --- a/user/models/user.py +++ b/user/models/user.py @@ -60,11 +60,12 @@ class User(AbstractUser): name=ETS_GROUP ).exists() - def send_mail_shared_access_removed(self, obj_identifier): + def send_mail_shared_access_removed(self, obj_identifier, obj_title): """ Sends a mail to the user in case of removed shared access Args: obj_identifier (): + obj_title (): Returns: @@ -72,9 +73,9 @@ class User(AbstractUser): notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_ACCESS_REMOVED) if notification_set: mailer = Mailer() - mailer.send_mail_shared_access_removed(obj_identifier, self) + mailer.send_mail_shared_access_removed(obj_identifier, obj_title, self) - def send_mail_shared_access_given(self, obj_identifier): + def send_mail_shared_access_given(self, obj_identifier, obj_title): """ Sends a mail to the user in case of given shared access Args: @@ -86,9 +87,9 @@ class User(AbstractUser): notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_ACCESS_GAINED) if notification_set: mailer = Mailer() - mailer.send_mail_shared_access_given(obj_identifier, self) + mailer.send_mail_shared_access_given(obj_identifier, obj_title, self) - def send_mail_shared_data_recorded(self, obj_identifier): + def send_mail_shared_data_recorded(self, obj_identifier, obj_title): """ Sends a mail to the user in case of shared data has been recorded Args: @@ -100,9 +101,9 @@ class User(AbstractUser): notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_RECORDED) if notification_set: mailer = Mailer() - mailer.send_mail_shared_data_recorded(obj_identifier, self) + mailer.send_mail_shared_data_recorded(obj_identifier, obj_title, self) - def send_mail_shared_data_unrecorded(self, obj_identifier): + def send_mail_shared_data_unrecorded(self, obj_identifier, obj_title): """ Sends a mail to the user in case of shared data has been unrecorded Args: @@ -114,9 +115,9 @@ class User(AbstractUser): notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_RECORDED) if notification_set: mailer = Mailer() - mailer.send_mail_shared_data_unrecorded(obj_identifier, self) + mailer.send_mail_shared_data_unrecorded(obj_identifier, obj_title, self) - def send_mail_shared_data_deleted(self, obj_identifier): + def send_mail_shared_data_deleted(self, obj_identifier, obj_title): """ Sends a mail to the user in case of shared data has been deleted Args: @@ -128,9 +129,9 @@ class User(AbstractUser): notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_DELETED) if notification_set: mailer = Mailer() - mailer.send_mail_shared_data_deleted(obj_identifier, self) + mailer.send_mail_shared_data_deleted(obj_identifier, obj_title, self) - def send_mail_shared_data_checked(self, obj_identifier): + def send_mail_shared_data_checked(self, obj_identifier, obj_title): """ Sends a mail to the user in case of shared data has been deleted Args: @@ -142,7 +143,7 @@ class User(AbstractUser): notification_set = self.is_notification_setting_set(UserNotificationEnum.NOTIFY_ON_SHARED_DATA_CHECKED) if notification_set: mailer = Mailer() - mailer.send_mail_shared_data_checked(obj_identifier, self) + mailer.send_mail_shared_data_checked(obj_identifier, obj_title, self) def get_API_token(self): """ Getter for an API token From 23c04c88834b4c481e7f9105cfbc6e43607d4d5b Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 14:51:53 +0100 Subject: [PATCH 10/31] #86 Parcel districts instead of revocation * drops revocation column in favour of a parcel district column --- intervention/tables.py | 60 ++-- locale/de/LC_MESSAGES/django.mo | Bin 36538 -> 36677 bytes locale/de/LC_MESSAGES/django.po | 467 ++++++++++++++++---------------- templates/table/gmrkng_col.html | 9 + 4 files changed, 272 insertions(+), 264 deletions(-) create mode 100644 templates/table/gmrkng_col.html diff --git a/intervention/tables.py b/intervention/tables.py index 0ed003fa..223cd8a4 100644 --- a/intervention/tables.py +++ b/intervention/tables.py @@ -6,6 +6,7 @@ Created on: 01.12.20 """ from django.http import HttpRequest +from django.template.loader import render_to_string from django.urls import reverse from django.utils.html import format_html from django.utils.timezone import localtime @@ -29,6 +30,11 @@ class InterventionTable(BaseTable, TableRenderMixin): orderable=True, accessor="title", ) + d = tables.Column( + verbose_name=_("Parcel gmrkng"), + orderable=True, + accessor="geometry", + ) c = tables.Column( verbose_name=_("Checked"), orderable=True, @@ -41,12 +47,6 @@ class InterventionTable(BaseTable, TableRenderMixin): empty_values=[], accessor="recorded", ) - rev = tables.Column( - verbose_name=_("Revocation"), - orderable=True, - empty_values=[], - accessor="legal__revocation", - ) e = tables.Column( verbose_name=_("Editable"), orderable=True, @@ -117,6 +117,29 @@ class InterventionTable(BaseTable, TableRenderMixin): ) return format_html(html) + def render_d(self, value, record: Intervention): + """ Renders the parcel district column for an intervention + + Args: + value (str): The intervention geometry + record (Intervention): The intervention record + + Returns: + + """ + parcels = value.parcels.all().values_list( + "gmrkng", + flat=True + ).distinct() + html = render_to_string( + "table/gmrkng_col.html", + { + "entries": parcels + } + ) + return html + + def render_r(self, value, record: Intervention): """ Renders the recorded column for an intervention @@ -162,28 +185,3 @@ class InterventionTable(BaseTable, TableRenderMixin): ) return format_html(html) - def render_rev(self, value, record: Intervention): - """ Renders the revocation column for an intervention - - Args: - value (str): The revocation value - record (Intervention): The intervention record - - Returns: - - """ - html = "" - exists = value is not None - tooltip = _("No revocation") - if exists: - _date = value.date - added_ts = localtime(value.created.timestamp) - added_ts = added_ts.strftime(DEFAULT_DATE_TIME_FORMAT) - on = _date.strftime(DEFAULT_DATE_FORMAT) - tooltip = _("Revocation from {}, added on {} by {}").format(on, added_ts, value.created.user) - html += self.render_stop( - tooltip=tooltip, - icn_filled=exists, - ) - return format_html(html) - diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 7fe3a4ee71d0b17b766706edd33f285e920fe645..c9d4293ad8de176ef64a6005d1b1cb3428ecc471 100644 GIT binary patch delta 10483 zcmYk>2YgTG9>?($AqlZEZ4yKVLI^6Pg4nh9XiM#u2u0QU$EukcLB)2hYKszkRE?lD zY8P!&S~~R7-m9)^wY{J3|NOk}J+Id<@8@~WbI!9*qCK!GU+(IBp1k1vOB}8rd>p4N z_9^B#nfV-NWqFl4&V?AqDaO5vm_`0}tmC|eW8xfVDn7+-=&9;BX?P9WVY6zEGZGi$ zbo8t4IOT9I*2OJYn8%!3B!Vfpk44alck@Njk8C6cVvLn1A`dz(a4dGgqIkxDVCUaE%Ugp!ZJ;+Tvix8v4<`vzmk&+=v?4e$-5yL^XH?OW;pf5PcKWbLyaGr~>M_ zDrRjgL_WpxZBWnmuzWh|xk>0@)}84jD&kHohF8%a?_xCGM~yhFmOHgIP#tbyw#NYS z{ZY>i!(beZnz@;%4lYH_=z7%i`)V=&+7xFg2*P_9hEGu=53cR@JQg)0jZstH9@Wqg zR0HW)96cC<3$Qe9zzTQ-)y~(b=l(=>xIiNFuY#h9?t|f|j>Ms+s3GdbO;Po_pr*DD zszZY@KaRm*9B<{BsE(~bJ^vnR=?*X&{}Sr?(Ws70Lp|roB%y{DqZ;0XIzD?)YjzAZ($DPv zR~SzIKI(pcdav(B5Qbtrs==nH`#n+b8-#jp6h`B04AJ@DL82N3xo&~;Z){KA?>TpB zyP-zZ8#R>!P#qp)&O}XpHflybK-J%c8sTB2A?E}(#p|f&%hqT8b^hZ?sA3}O!3J0c zQ&DT%7d3)`s3{zYs+WN}&$Cg_XQJvaMm3m?n(D2nnb>QdK+W_;ETQv%n}lBY1a%Da zCAlw*GOMAMpf;+3#%6P5Fisonh|`hnOJRB4PHcb=sK3cuW%GT z!O}Rakz0SJhlEBn7u9e!YICeZjbsO^BZp8uKZlx$n^u0u@{f>Dh4VM6;W{boBTT|n zOhzr?RMgTfN7eIuNJ0(nMm3O&YTy#8;V)5}=RWGe%8lI)C!yA`C2DP7MDOW94I~3K zQ?pR-TaN0$1}uq(kiFw^uDS{5pH{)Qi93=q7)!;Ds2Q4o>ga4M&qg(L9NB-)8ElOK zP2KX&s6EshOJP6M(o8^gbSf6n`CsTwu!g7?ZngYw)Y=`#viLQs15Z(Vr7-iV??M>r z#gQ0^G1viH7NLD7*9tD9UUUof;_p!nJ+%B&)Pn)d-Kh^l zO>rD*2^wMr?0~B=9sRIa3wJ5QQ8O8hRk03w^k6>{8rc|g25O3ypc>eKYIqOog~w5A z`YHP371R`eiP|&&G5uS*o46$E{gqJnYooposV$j*jkLQJ^hdpTEc#*w2H%)id#XbNdhtUn zhlSW-+Ql)bU7UiAuqWz%Hu8;e_M%4o2$>A0KpS@;ZLt9PZdeHWct~g}2BVJGcvOQ6 zEWa5w!o%i8)UmsZs`onvV$ch2gXK^&RUI`0wJ-ovQO~tS?SWTJPdbTM3MQi-coz%f z0o036p&qzw<+m;W0JTS+VigQ*>n>R>)DqQ2e@w>S*bL)v9ZtftSW)M{bvyUB*KpK} zj+ozJ1ob?h)YmQumx00{ET|RBh*Nqpr+QRqpKh4#erA^Ls4s55sP7U)RHEnp6iT7u?Gg>U<{^x z=S>oN!93Ipm!qbBy_IiAb>J|nL%FDte1etmGS!Ww`SiYIr%F5efVd{0Y@>eh)`9W9}hnh=J13Zeg@d3tT#jbAs zj$N65ZJI6=w8Y7%j+{b$7j9Yp4>Poz`$4IWn(7v)HSdpFiZrZ&i|qa})Kcc5>fJ*v z^&@QJ!(ct#`Qw0s<~Wx6Yv>qpugwFML)VKN+sspz%6n{gN7wP97r)X6D zWYk)BMRjZn>X1N28A6EYu7wvit^f8{jA*#V_)Ckt28s3F^?j&lHT}F*O4>falQ1$;py|>r^cWJ7jMdTx zFPe+$*iv&Fs=3(o zZlOR^v>VmKlc*QnHt(UP{vj5?Kdsz%h&w}tF^uxcsCr4L8BE0}Y>x$e_zMd)BNK+Y z9iHVOp($NyZbQB3W7G?-p?Z89HR9h;OA`F5dp`~flCOg*Z-lYf9rfNRn1ETRQ<97H z-MNm1(3A3-yUE(1j$2PugK4N!Favd7vr#?Yi-C9o^@0niJ@W!E}rxKM$MWUYvoy z<37w7;eMd%r@Mb&48*FGXJS#@j{!RWr%4pYi>M{|26ZeSqn4uhNOuI$sC)uyXyjAH%^lgOq(ZbF^=9Msev$9Q~z+6(1gcRLn=8c{TA zrmEp0Y=+v5*HI1sifZUL)Xe&icHdtXRbFj0^B+W_0R`I4ZLtXUunL1LKL)kwrlCf1 z5Y>TG*bdL4j%S%M?p~;a9Cv3NhTwSA%)O0jcnxaiw|PjkB$123SahswBVV%k_Ya6t zs0MmrO&oxYa0zP7ubO$NFXs2C8OisiJEQ)n=S!j9UkTOmL=44-SVHH&BMJQy8H!qp zS*VdNN4@Yp)GpqRvA72(;w@Z>FOFyQ_&avQ$rIcqyntck^H2l(6*W`-Jgjy?Fi__| ziiD;(!A!vl3}*- z{n4Wa2a(WPrJ<&J9I8PNs-cyr7i>eV^(oYveuL%kA!@3FGTi5)u{`-y)C~5=6r6*l z@C>SBcQcrOJ@}9US#XNmU-{EDgcybJ{gDA0(n*^P&0FgwfJ5!946K`lvl)KvCGHS{{_JsGHhthD@2 ztWN$ss@_wx;B@zW;T{s2;;N{%Oh%2Y6-HoB)Cr8TCMO^vBky7j;6-&`Xw2M@{ua)S50s zKirF2s*g}p{TXVBzC`VfpHUtC)9we%QGM-P5^AstYDDp<4ke&ATM{PWFmp3%*MEbm zum5io0lpZ6m9YV~#VR-(^W%r+Zu5Yoedh=Xjr@e&IF0JSIaJ54p+=Zz_wSnbP`_?} zMm_%sRsV_Qedf6%E`Zv+#ZmR5Q1xQbqn;($jk>58CZQTkvGNwEdTmgfu>raNNxz8)@Dwr2?rD#7C0^xTHa5gV#0}!vb=_l`*5+&G=cxZcaF?2K zL=Vy@2(9BHds;KnjIvdD5yunl2+d$k)HTV)xlR5WX?=cslTN{DM8bSBMM%uTFNt16 zb%Lezt`T@ofgfeL#PeA`5g{J7zV}qO#p#3&p02Ibcy@h3T8q)q3ckf1L?-2Zi3F?L zgtF75ALDb_4|S!IjwSv<`V-V=PSdqlUyZ(Gbj_te*D<0e=|o}$={;7idkaZ-QG)9Q z((UbD29Boeb3(gnqm_R`S_k(o`RlkGKOiQPUW-%p)%b~wuG7RlZ_53VO$EL%PFs9- z6|(dotWIH5q6_I6c$HX2TIWJvnQp{iL{-X`;wYj!@gk8<=sK@y*g!nH3X$MXT<=$- zD!xqY;C>yVwAH(Yal~ciiIv0)Bz3(e@ieBma$VFCi046sBN3kwP3KzXEmDb8$Wg zz9%t~cy6VuMr!rh4G(BUjaE>_{FIf7lw;F$*Szg)ah_A@+#z|KG z8R=-!z42T82Qh*0Cyo#uiK&DxeXVs>ChyrwqB*gOOpsORMg9zNo6t3oIL^J7?7p{< zABTy3lx5;O#9HFn^&N>|?!9jXSIieFdxiLo{AWHk|2xSXps*Bim>6W0MJMvQa!jSq zkiMt{S7$4qj_b$|B`OdfSvg5(q@~N^+e917FI4*xWQMC2*AUYAiEX334~OR@UD98^gHY%JMBp>w1f5MY;qo#(`E|$y6Qh_)lOtD?CJ6f0)cC{{Q-v`y+@V zL`!bexB9Ac)zafh>ks70#8KiTc)*5 zmd>*my^Oz7wvy;a`YE2n2k3*Ba1s81+wr@spy-g{XG!@}@;V+PW|F>13?RLim`J=u zBvbb6`htWnWxC!m{YY;n+7ZhLUB$S!j@V69BflkUVRVRRg7@D4evBpAj3`5`^Hy9D zL&+Dw3q&N*g7|~jOu4Ss#5bfz5wDUiVdcf~74qSPuE9h@BA)WO`c2-S%oH+5iMm+{ zF(EzcQxZc?S3UDn(*GeA6SJ+1r1whY=f}h_qRRi>iy;3J(Sfqk_;uE*m=aO0lz*%{o^|r;*-IOv||ubH>-FP*zyg^qlq8mWAZBO4;e36WKC0G^<0el{u~ZM3v0R O82x?0oF$W^ivJgx^UF8@ delta 10561 zcmYk?3w+P@9>?(?W;S*;w;0;zTC* z+=``K%B7TB3ZKSQ3k!PQh6<|nyl)ddFPw9iFo*KZB+r|Jv#WaEvlx=>dEIa^j>fCl9=lfa zyji#!%VSb?&#Q;6F$AY!anJL4^GQOf$aNLo1`MRU7mMK`t3QFd@d{?+?-+)|Qk;`e z*U!aDm}B+Zumt5p7=dRo60iHqKF_;H!l1qI8lD%0F{m5rU>K%jDeR69;b<&_^X&X8 za}zQmZ!cEC6ITBh7NQ(f)ALBZ5Yzx0VHE9qElD)8?ih-hs0PPjX?zZYaTTiP>roH* z5Ov?j<`-C$@&zkjL*0Md%3-zKedUnJ^D3gRB1uP*a2$_Ca3&_;eAI~ZP*ZypHA7#U zH!z5Df!glAqNo`tj+(i6R0mU0Guj+=e>c>g7+#z8k0hB#MM-=WHS$fUp68=x=xC!;=ciyP%JWg@-$Z?0Y{U|H1l8arJO6Lg^8y%VDU84bOu#7YfYtQ=544IG zumdMnqo(#2YD9mbrm|oI*W)NN9yRs#Q8V%w>iW*85%xkgGyq#-HfqE>u`nLNK-%|C zkm$y*u^fJf>cBnJ2nwaSDGWzlR{`}tC!l{SQP+K84yd>ri`T3$hB{aeM*;8@i9^ zzNk<4W9W+|xj~}WB&?B}qH3s~H$nBZEozMiqSkm4YE2iRI=BYQU><5oj-Uo|5j7)M zF%Ivbo)g{JEm^I`%)dsKMulvGTAP-rwQ7T!%3i332BD^Yywy)c-8Ta@@;P??MN|h~ zK`q%z)PUBYp1&FOob8R7e_gPf3T>i;sLgiNynuRrenoZY9>IdxWrp#7i>h0C?AvXA5?=0EnG+I zqUs+-HI#+yD{mq`hFh%uK5DItv~*u$p{ONFKrLwshU)!q>`&O`s0a42a(~nsW??kW zN6pkbs6DeC_4%+L_28ozho`V3-p29Rnsw-cyD$SI9(6OFiJ`RbjkO9N>Ol)o56(d~ z^tzSbK`qf1)YR`sP4SngCHN6z@Gj5VKHgnvF$pIzEK+Pz}9l=Akz8$EcY*ZuJ*2p7L+4nSXs?luCCaPC}LI zq8doYLf9FDup4UE_C<9h6D!~d)Igp`eK6%(`2*Ad4x>7F7Pa~QG{f33|9U_|8~1*; zLT$eOsI^>*T8bU02Oq@pcn-Cj@1P!7oSoMclTqhKARiUpbEuK$BZKz7!XS)k>o#Yi zkEAFSDX5XBp8hCz5ioL3Q{o<6L2zWPrT{BfY%K5fPBH&RF4;+b_`UzG)1J!}Ws17YfjbsH@!ZlbA52HS~ zyv}YjMx(BO*vd^%Gxj9WeJbjC4KNuS znnO?n%)vUi4^!|~7v^6#RO;$BO%+V1+!580<*4uN%~n2YUPXPN+(%9IL*3k(*F`Nw zGpvD`sPiwQmU0tnZ|p`bb-s_JMFGz{jpwN-^MvQUjkocA%b^UnM8qY^{Y#Zve{1o;1aU8X$ZlkWhi`tYW`n%0o8B6K?uS=pHw?nPX zAkQK=EZeX=gQ=g9UwC{ByQ4c4g zcJpG?FB~gT4evm`_xY#>kE33vuTc&Egu3r8Y5>6l-N;LzX09yi`c%|&A4M(A0Q70( zQ%Uq*E!OXwj%ZpTKihf4*@Gh!DrH43UQB$9Ug|Uv+r=e!31!{BlL|r!qHG`8e9%p0W z0{qhrYDV54>N>o~N1`b`VO~Z(=r-yBA;VmcOQS}df@&}wb-o`4;|QytfJu}WqMo}A zYvEzki2p|V;Drr$_xrL*w8>^*30#bt($%Ob-GzEzkE42i12toJuqXzOaC;^a(EQg@Kg+gBs9%GhnP6XfXQJKsbq}tcsb2y0IOqp$ycBhhPL| zp_XDg2H`BLUx=E)Tyr(*x_7Y{zGwA2Q8Tb_EbAXaa-0fz4#V*pYHI(&6s$bX?S%|f z$GV|L)EhNZ1MnrBg8Bdp8}AyfifSkYwYeLkp5NK(2aMi!qd!U=`eodcb+B{|%2)j-VHsi3_MT{=xhkHB;eJ-4b^| zJ%2E2fDHo%^RWCwmX)`p{N1P zLEX0!E8q^)OrF7Jc;CnT>szhabl0<2N=s}) zIRiD)mryhJ2I})=9qPWVs16;SK~E}?T(^pdXWa)#4OGwDqAnb2&P9JmP*b`M)xZ(d zOrAzH^dpwTKT!jUWM`_JfLfB4sO!f1ESZLS;C$4G-$3os&8W9v560p#)B}FSf>?f* zTe4U)5qUAZnphjx;W9jjdTS=|4K)g1$3*mn&T&aSjHRM4CgXHe2R7LG?bw>~C2WUD zbKO5MX5&c8yHFiUoaZ)eGHMUiLp`S%7Q%LziXAaV@BeHPUAPAIfQ=Z0n^8CH!XmgA z^`Lyz44t&{71Y$;L@ib5b8gdCLoHPu)Kou$TB1&549N&Vha9&HL>CX_q$*Qs=N^OygjJb^8j|n0t=aceUNln=w6di7)yC3 z#^GwLgon(Zuo&gAMeY-@Eb3dW8M2wYF&Ks~psst}%ImQJ<%ayYq=X~S|C9NzL&bCo z!FEFRuUr1EIRT$0F6t!55F(87hq#=`C3FN)*9<4)4m?E6C3NWXqzf^Eb9yBk>3$zS zz9AkQUt8YR9BH0G{X4;BE=(eNkRK%Uh4Z4_Tp1swZY7?;DMSaNBITN>;~9td9p%&H z%{Bc`QfP|L5-*U4;B$DM=tWc~SX%$40soo?Qg?`GoO3fK%EznV|A&=!IFryvmX7zh z;=yr_Jd{F5tN0E#5sRqrPt>w&n^Sj~{5CegeyHOS@+4vv`KQP)Al_fZR(*=}CDE~f z6FT-2J;_svSIM_oz0SQvo}q-}aq^CKZU&C0?n@$tSZnp4kjE2ODSw44@LvR9zuqdG zp-+*YDCo#1uK6?f<5e!0N7^1A97Qc3g4L*OL3Aabg{O$+bR5?%T0=ZIf=R{^`qZdO^dUBLzAjPLuKNnB5~oxqUL)F)>R3p1iU-^ri3KWmi6WplCv$t0{{7wW`3@}0!nIdK)Ee0t|k5I+*{Q~M(^mN-l3sEDia zCE{0Nl-1{%2dR65yeV;#c#ZO7!~vo>F^%X>wB+1I)bSRvkn$z%l9wqY5+PKqCYljD zDX&Bw^&H-Q%BkeLaRc#z)i1H@_E5e>{BHG4uov+?@ut;%z_sfw7y7>r{-ZiSuM!tr z(f`kWarWRTR<{}dL$soPG|`0kh4K@`7~;V(kfbr^q6i&VOy%j8|EvPXXth7lD(9Qp zIbT@*ng24rKdh``ec}S;EjZ2UkC7*k_r%Ni7V!*GgxE)PCT0>k^nIhFGG*Tel1GVL z3Xyg}56Yhs-x4}9iH|wg*UtMZ`LUbWLEQpeLcC5qIIfTs=iIwiankHV-9X~sls_+E z^Z!1DT~t0q>>&o(#Ug{UjxFYNJVJc#s=Y2&KO5hmZYWWK*lYEq-Z;ynaWT=($}gz> zSPCO`6~{300ODWd#W2iY%RlRruOWY%(C-;K+B>}aSd#PUR&GtMV>Z!-ycE8IgRQ=j zscZb>{|L)l$fuq!Xa6raFOq*u zev_zY*B2xA)!@fDLdO%FcnZ%F+L+xa|AU|493qCurmSNzv5q)GyiENa)Nze`BcWrP z!y9ha!rzE^&aWfNx{pnFc9LDxofBs)zi1EYhksDFf*3%47mwi$EP!8N4*r0fWKMyE zs8L7Bics?ueniY6|C-1m-%d;=<`RvldvJV1QiwVo%gjLXwZ!AZQbI>K=iVf?63LX` z$(fN5<(uq3_rD(#NFO1}an(_)j>8g^3*&JjmS{!%O}sCt3?0~2Fnb$JCZi&1Sz2nP-#C9K+ jIcVJYG2<4%0=GbiiZFzydrV;-I6#Dc_ diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index f7a36715..f280137d 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ # #: compensation/filters.py:122 compensation/forms/modalForms.py:35 #: compensation/forms/modalForms.py:46 compensation/forms/modalForms.py:62 -#: compensation/forms/modalForms.py:256 compensation/forms/modalForms.py:350 +#: compensation/forms/modalForms.py:306 compensation/forms/modalForms.py:399 #: intervention/forms/forms.py:54 intervention/forms/forms.py:156 #: intervention/forms/forms.py:168 intervention/forms/modalForms.py:124 #: intervention/forms/modalForms.py:137 intervention/forms/modalForms.py:150 @@ -19,14 +19,14 @@ #: konova/filters/mixins.py:353 konova/filters/mixins.py:354 #: konova/filters/mixins.py:385 konova/filters/mixins.py:386 #: konova/forms.py:140 konova/forms.py:241 konova/forms.py:312 -#: konova/forms.py:339 konova/forms.py:349 konova/forms.py:362 -#: konova/forms.py:374 konova/forms.py:392 user/forms.py:42 +#: konova/forms.py:356 konova/forms.py:366 konova/forms.py:379 +#: konova/forms.py:391 konova/forms.py:409 user/forms.py:42 #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-02-03 13:37+0100\n" +"POT-Creation-Date: 2022-02-08 14:48+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -95,7 +95,7 @@ msgstr "" #: analysis/templates/analysis/reports/includes/eco_account/amount.html:3 #: analysis/templates/analysis/reports/includes/intervention/amount.html:3 #: analysis/templates/analysis/reports/includes/old_data/amount.html:3 -#: compensation/forms/modalForms.py:334 +#: compensation/forms/modalForms.py:383 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:34 #: intervention/templates/intervention/detail/includes/deductions.html:31 msgid "Amount" @@ -138,7 +138,7 @@ msgstr "Zuständigkeitsbereich" #: analysis/templates/analysis/reports/includes/intervention/laws.html:17 #: compensation/tables.py:35 #: compensation/templates/compensation/detail/compensation/view.html:63 -#: intervention/tables.py:33 +#: intervention/tables.py:39 #: intervention/templates/intervention/detail/view.html:68 #: user/models/user_action.py:20 msgid "Checked" @@ -157,7 +157,7 @@ msgstr "Geprüft" #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:31 #: compensation/templates/compensation/detail/eco_account/view.html:44 #: ema/tables.py:38 ema/templates/ema/detail/view.html:35 -#: intervention/tables.py:39 +#: intervention/tables.py:45 #: intervention/templates/intervention/detail/view.html:82 #: user/models/user_action.py:21 msgid "Recorded" @@ -213,14 +213,14 @@ msgstr "Abbuchungen" #: analysis/templates/analysis/reports/includes/eco_account/deductions.html:9 #: analysis/templates/analysis/reports/includes/eco_account/deductions.html:11 -#: compensation/forms/modalForms.py:151 +#: compensation/forms/modalForms.py:167 #: compensation/templates/compensation/detail/compensation/includes/states-after.html:36 #: compensation/templates/compensation/detail/compensation/includes/states-before.html:36 #: compensation/templates/compensation/detail/eco_account/includes/states-after.html:36 #: compensation/templates/compensation/detail/eco_account/includes/states-before.html:36 #: ema/templates/ema/detail/includes/states-after.html:36 #: ema/templates/ema/detail/includes/states-before.html:36 -#: intervention/forms/modalForms.py:294 +#: intervention/forms/modalForms.py:311 msgid "Surface" msgstr "Fläche" @@ -284,7 +284,7 @@ msgid "Type" msgstr "Typ" #: analysis/templates/analysis/reports/includes/old_data/amount.html:24 -#: intervention/forms/modalForms.py:305 intervention/forms/modalForms.py:312 +#: intervention/forms/modalForms.py:322 intervention/forms/modalForms.py:329 #: intervention/tables.py:89 #: intervention/templates/intervention/detail/view.html:19 #: konova/templates/konova/includes/quickstart/interventions.html:4 @@ -295,7 +295,7 @@ msgstr "Eingriff" #: analysis/templates/analysis/reports/includes/old_data/amount.html:34 #: compensation/tables.py:226 #: compensation/templates/compensation/detail/eco_account/view.html:19 -#: intervention/forms/modalForms.py:278 intervention/forms/modalForms.py:285 +#: intervention/forms/modalForms.py:295 intervention/forms/modalForms.py:302 #: konova/templates/konova/includes/quickstart/ecoaccounts.html:4 #: templates/navbars/navbar.html:34 msgid "Eco-account" @@ -315,7 +315,7 @@ msgstr "Nur unverzeichnete anzeigen" #: compensation/forms/forms.py:32 compensation/tables.py:25 #: compensation/tables.py:167 ema/tables.py:28 intervention/forms/forms.py:28 -#: intervention/tables.py:23 +#: intervention/tables.py:24 #: intervention/templates/intervention/detail/includes/compensations.html:30 msgid "Identifier" msgstr "Kennung" @@ -336,12 +336,12 @@ msgstr "Automatisch generiert" #: ema/tables.py:33 ema/templates/ema/detail/includes/documents.html:28 #: ema/templates/ema/detail/view.html:31 #: ema/templates/ema/report/report.html:12 intervention/forms/forms.py:40 -#: intervention/tables.py:28 +#: intervention/tables.py:29 #: intervention/templates/intervention/detail/includes/compensations.html:33 #: intervention/templates/intervention/detail/includes/documents.html:28 #: intervention/templates/intervention/detail/view.html:31 #: intervention/templates/intervention/report/report.html:12 -#: konova/forms.py:338 +#: konova/forms.py:355 msgid "Title" msgstr "Bezeichnung" @@ -354,7 +354,7 @@ msgid "Compensation XY; Location ABC" msgstr "Kompensation XY; Flur ABC" #: compensation/forms/forms.py:57 compensation/forms/modalForms.py:61 -#: compensation/forms/modalForms.py:255 compensation/forms/modalForms.py:349 +#: compensation/forms/modalForms.py:305 compensation/forms/modalForms.py:398 #: compensation/templates/compensation/detail/compensation/includes/actions.html:35 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:34 #: compensation/templates/compensation/detail/compensation/includes/documents.html:31 @@ -368,11 +368,11 @@ msgstr "Kompensation XY; Flur ABC" #: intervention/templates/intervention/detail/includes/documents.html:31 #: intervention/templates/intervention/detail/includes/payments.html:34 #: intervention/templates/intervention/detail/includes/revocation.html:38 -#: konova/forms.py:373 konova/templates/konova/includes/comment_card.html:16 +#: konova/forms.py:390 konova/templates/konova/includes/comment_card.html:16 msgid "Comment" msgstr "Kommentar" -#: compensation/forms/forms.py:59 compensation/forms/modalForms.py:351 +#: compensation/forms/forms.py:59 compensation/forms/modalForms.py:400 #: intervention/forms/forms.py:182 msgid "Additional comment" msgstr "Zusätzlicher Kommentar" @@ -432,7 +432,7 @@ msgstr "kompensiert Eingriff" msgid "Select the intervention for which this compensation compensates" msgstr "Wählen Sie den Eingriff, für den diese Kompensation bestimmt ist" -#: compensation/forms/forms.py:184 compensation/views/compensation.py:92 +#: compensation/forms/forms.py:184 compensation/views/compensation.py:94 msgid "New compensation" msgstr "Neue Kompensation" @@ -458,7 +458,7 @@ msgstr "Vereinbarungsdatum" msgid "When did the parties agree on this?" msgstr "Wann wurde dieses Ökokonto offiziell vereinbart?" -#: compensation/forms/forms.py:354 compensation/views/eco_account.py:102 +#: compensation/forms/forms.py:354 compensation/views/eco_account.py:103 msgid "New Eco-Account" msgstr "Neues Ökokonto" @@ -483,8 +483,8 @@ msgstr "Fällig am" msgid "Due on which date" msgstr "Zahlung wird an diesem Datum erwartet" -#: compensation/forms/modalForms.py:63 compensation/forms/modalForms.py:257 -#: intervention/forms/modalForms.py:151 konova/forms.py:375 +#: compensation/forms/modalForms.py:63 compensation/forms/modalForms.py:307 +#: intervention/forms/modalForms.py:151 konova/forms.py:392 msgid "Additional comment, maximum {} letters" msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" @@ -496,47 +496,47 @@ msgstr "Neue Ersatzzahlung zu Eingriff '{}' hinzufügen" msgid "If there is no date you can enter, please explain why." msgstr "Falls Sie kein Datum angeben können, erklären Sie bitte weshalb." -#: compensation/forms/modalForms.py:115 compensation/forms/modalForms.py:127 +#: compensation/forms/modalForms.py:131 compensation/forms/modalForms.py:143 msgid "Biotope Type" msgstr "Biotoptyp" -#: compensation/forms/modalForms.py:118 +#: compensation/forms/modalForms.py:134 msgid "Select the biotope type" msgstr "Biotoptyp wählen" -#: compensation/forms/modalForms.py:132 compensation/forms/modalForms.py:144 +#: compensation/forms/modalForms.py:148 compensation/forms/modalForms.py:160 msgid "Biotope additional type" msgstr "Zusatzbezeichnung" -#: compensation/forms/modalForms.py:135 +#: compensation/forms/modalForms.py:151 msgid "Select an additional biotope type" msgstr "Zusatzbezeichnung wählen" -#: compensation/forms/modalForms.py:154 intervention/forms/modalForms.py:296 +#: compensation/forms/modalForms.py:170 intervention/forms/modalForms.py:313 msgid "in m²" msgstr "" -#: compensation/forms/modalForms.py:165 +#: compensation/forms/modalForms.py:181 msgid "New state" msgstr "Neuer Zustand" -#: compensation/forms/modalForms.py:166 +#: compensation/forms/modalForms.py:182 msgid "Insert data for the new state" msgstr "Geben Sie die Daten des neuen Zustandes ein" -#: compensation/forms/modalForms.py:173 konova/forms.py:190 +#: compensation/forms/modalForms.py:189 konova/forms.py:190 msgid "Object removed" msgstr "Objekt entfernt" -#: compensation/forms/modalForms.py:227 +#: compensation/forms/modalForms.py:277 msgid "Deadline Type" msgstr "Fristart" -#: compensation/forms/modalForms.py:230 +#: compensation/forms/modalForms.py:280 msgid "Select the deadline type" msgstr "Fristart wählen" -#: compensation/forms/modalForms.py:239 +#: compensation/forms/modalForms.py:289 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:31 #: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31 #: ema/templates/ema/detail/includes/deadlines.html:31 @@ -544,27 +544,27 @@ msgstr "Fristart wählen" msgid "Date" msgstr "Datum" -#: compensation/forms/modalForms.py:242 +#: compensation/forms/modalForms.py:292 msgid "Select date" msgstr "Datum wählen" -#: compensation/forms/modalForms.py:269 +#: compensation/forms/modalForms.py:319 msgid "New deadline" msgstr "Neue Frist" -#: compensation/forms/modalForms.py:270 +#: compensation/forms/modalForms.py:320 msgid "Insert data for the new deadline" msgstr "Geben Sie die Daten der neuen Frist ein" -#: compensation/forms/modalForms.py:288 +#: compensation/forms/modalForms.py:337 msgid "Action Type" msgstr "Maßnahmentyp" -#: compensation/forms/modalForms.py:291 +#: compensation/forms/modalForms.py:340 msgid "Select the action type" msgstr "Maßnahmentyp wählen" -#: compensation/forms/modalForms.py:300 +#: compensation/forms/modalForms.py:349 #: compensation/templates/compensation/detail/compensation/includes/actions.html:40 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:39 #: compensation/templates/compensation/detail/compensation/includes/documents.html:36 @@ -590,62 +590,58 @@ msgstr "Maßnahmentyp wählen" msgid "Action" msgstr "Aktionen" -#: compensation/forms/modalForms.py:305 compensation/forms/modalForms.py:317 +#: compensation/forms/modalForms.py:354 compensation/forms/modalForms.py:366 msgid "Action Type detail" msgstr "Zusatzmerkmal" -#: compensation/forms/modalForms.py:308 +#: compensation/forms/modalForms.py:357 msgid "Select the action type detail" msgstr "Zusatzmerkmal wählen" -#: compensation/forms/modalForms.py:322 +#: compensation/forms/modalForms.py:371 msgid "Unit" msgstr "Einheit" -#: compensation/forms/modalForms.py:325 +#: compensation/forms/modalForms.py:374 msgid "Select the unit" msgstr "Einheit wählen" -#: compensation/forms/modalForms.py:337 +#: compensation/forms/modalForms.py:386 msgid "Insert the amount" msgstr "Menge eingeben" -#: compensation/forms/modalForms.py:362 +#: compensation/forms/modalForms.py:411 msgid "New action" msgstr "Neue Maßnahme" -#: compensation/forms/modalForms.py:363 +#: compensation/forms/modalForms.py:412 msgid "Insert data for the new action" msgstr "Geben Sie die Daten der neuen Maßnahme ein" -#: compensation/models/action.py:21 +#: compensation/models/action.py:22 msgid "cm" msgstr "" -#: compensation/models/action.py:22 +#: compensation/models/action.py:23 msgid "m" msgstr "" -#: compensation/models/action.py:23 +#: compensation/models/action.py:24 msgid "km" msgstr "" -#: compensation/models/action.py:24 +#: compensation/models/action.py:25 msgid "m²" msgstr "" -#: compensation/models/action.py:25 +#: compensation/models/action.py:26 msgid "ha" msgstr "" -#: compensation/models/action.py:26 +#: compensation/models/action.py:27 msgid "Pieces" msgstr "Stück" -#: compensation/models/compensation.py:63 konova/utils/message_templates.py:43 -msgid "Added deadline" -msgstr "Frist/Termin hinzugefügt" - #: compensation/models/eco_account.py:56 msgid "" "Deductable surface can not be larger than existing surfaces in after states" @@ -689,23 +685,23 @@ msgstr "Am {} von {} geprüft worden" #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:58 #: compensation/templates/compensation/detail/eco_account/view.html:47 #: ema/tables.py:102 ema/templates/ema/detail/view.html:38 -#: intervention/tables.py:132 +#: intervention/tables.py:146 #: intervention/templates/intervention/detail/view.html:85 msgid "Not recorded yet" msgstr "Noch nicht verzeichnet" #: compensation/tables.py:135 compensation/tables.py:264 ema/tables.py:107 -#: intervention/tables.py:137 +#: intervention/tables.py:151 msgid "Recorded on {} by {}" msgstr "Am {} von {} verzeichnet worden" #: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:130 -#: intervention/tables.py:160 +#: intervention/tables.py:174 msgid "Full access granted" msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden" #: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:130 -#: intervention/tables.py:160 +#: intervention/tables.py:174 msgid "Access not granted" msgstr "Nicht freigegeben - Datensatz nur lesbar" @@ -821,7 +817,7 @@ msgstr "Dokumente" #: compensation/templates/compensation/detail/eco_account/includes/documents.html:14 #: ema/templates/ema/detail/includes/documents.html:14 #: intervention/templates/intervention/detail/includes/documents.html:14 -#: konova/forms.py:391 +#: konova/forms.py:408 msgid "Add new document" msgstr "Neues Dokument hinzufügen" @@ -1076,111 +1072,76 @@ msgstr "" msgid "Responsible data" msgstr "Daten zu den verantwortlichen Stellen" -#: compensation/views/compensation.py:48 +#: compensation/views/compensation.py:50 msgid "Compensations - Overview" msgstr "Kompensationen - Übersicht" -#: compensation/views/compensation.py:147 +#: compensation/views/compensation.py:149 konova/utils/message_templates.py:27 msgid "Compensation {} edited" msgstr "Kompensation {} bearbeitet" -#: compensation/views/compensation.py:157 compensation/views/eco_account.py:160 -#: ema/views.py:227 intervention/views.py:311 +#: compensation/views/compensation.py:159 compensation/views/eco_account.py:161 +#: ema/views.py:230 intervention/views.py:313 msgid "Edit {}" msgstr "Bearbeite {}" -#: compensation/views/compensation.py:236 compensation/views/eco_account.py:316 -#: ema/views.py:188 intervention/views.py:487 +#: compensation/views/compensation.py:238 compensation/views/eco_account.py:317 +#: ema/views.py:191 intervention/views.py:491 msgid "Log" msgstr "Log" -#: compensation/views/compensation.py:280 compensation/views/eco_account.py:496 -#: ema/views.py:359 intervention/views.py:133 -msgid "Document added" -msgstr "Dokument hinzugefügt" - -#: compensation/views/compensation.py:350 compensation/views/eco_account.py:362 -#: ema/views.py:294 -msgid "State added" -msgstr "Zustand hinzugefügt" - -#: compensation/views/compensation.py:372 compensation/views/eco_account.py:384 -#: ema/views.py:316 -msgid "Action added" -msgstr "Maßnahme hinzugefügt" - -#: compensation/views/compensation.py:394 compensation/views/eco_account.py:475 -#: ema/views.py:338 -msgid "Deadline added" -msgstr "Frist/Termin hinzugefügt" - -#: compensation/views/compensation.py:417 compensation/views/eco_account.py:453 -#: ema/views.py:595 -msgid "Deadline removed" -msgstr "Frist/Termin gelöscht" - -#: compensation/views/compensation.py:440 compensation/views/eco_account.py:407 -#: ema/views.py:430 -msgid "State removed" -msgstr "Zustand gelöscht" - -#: compensation/views/compensation.py:463 compensation/views/eco_account.py:430 -#: ema/views.py:453 -msgid "Action removed" -msgstr "Maßnahme entfernt" - -#: compensation/views/compensation.py:482 compensation/views/eco_account.py:586 -#: ema/views.py:472 intervention/views.py:580 +#: compensation/views/compensation.py:487 compensation/views/eco_account.py:590 +#: ema/views.py:477 intervention/views.py:609 msgid "Report {}" msgstr "Bericht {}" -#: compensation/views/eco_account.py:59 +#: compensation/views/eco_account.py:60 msgid "Eco-account - Overview" msgstr "Ökokonten - Übersicht" -#: compensation/views/eco_account.py:92 +#: compensation/views/eco_account.py:93 msgid "Eco-Account {} added" msgstr "Ökokonto {} hinzugefügt" -#: compensation/views/eco_account.py:150 +#: compensation/views/eco_account.py:151 msgid "Eco-Account {} edited" msgstr "Ökokonto {} bearbeitet" -#: compensation/views/eco_account.py:263 +#: compensation/views/eco_account.py:264 msgid "Eco-account removed" msgstr "Ökokonto entfernt" -#: compensation/views/eco_account.py:337 ema/views.py:269 -#: intervention/views.py:558 +#: compensation/views/eco_account.py:338 ema/views.py:272 +#: intervention/views.py:562 msgid "{} unrecorded" msgstr "{} entzeichnet" -#: compensation/views/eco_account.py:337 ema/views.py:269 -#: intervention/views.py:558 +#: compensation/views/eco_account.py:338 ema/views.py:272 +#: intervention/views.py:562 msgid "{} recorded" msgstr "{} verzeichnet" -#: compensation/views/eco_account.py:659 ema/views.py:538 -#: intervention/views.py:384 +#: compensation/views/eco_account.py:663 ema/views.py:543 +#: intervention/views.py:388 msgid "{} has already been shared with you" msgstr "{} wurde bereits für Sie freigegeben" -#: compensation/views/eco_account.py:664 ema/views.py:543 -#: intervention/views.py:389 +#: compensation/views/eco_account.py:668 ema/views.py:548 +#: intervention/views.py:393 msgid "{} has been shared with you" msgstr "{} ist nun für Sie freigegeben" -#: compensation/views/eco_account.py:671 ema/views.py:550 -#: intervention/views.py:396 +#: compensation/views/eco_account.py:675 ema/views.py:555 +#: intervention/views.py:400 msgid "Share link invalid" msgstr "Freigabelink ungültig" -#: compensation/views/eco_account.py:694 ema/views.py:573 -#: intervention/views.py:419 +#: compensation/views/eco_account.py:698 ema/views.py:578 +#: intervention/views.py:423 msgid "Share settings updated" msgstr "Freigabe Einstellungen aktualisiert" -#: ema/forms.py:40 ema/views.py:92 +#: ema/forms.py:40 ema/views.py:95 msgid "New EMA" msgstr "Neue EMA hinzufügen" @@ -1208,19 +1169,19 @@ msgstr "" msgid "Payment funded compensation" msgstr "Ersatzzahlungsmaßnahme" -#: ema/views.py:49 +#: ema/views.py:52 msgid "EMAs - Overview" msgstr "EMAs - Übersicht" -#: ema/views.py:82 +#: ema/views.py:85 msgid "EMA {} added" msgstr "EMA {} hinzugefügt" -#: ema/views.py:217 +#: ema/views.py:220 msgid "EMA {} edited" msgstr "EMA {} bearbeitet" -#: ema/views.py:250 +#: ema/views.py:253 msgid "EMA removed" msgstr "EMA entfernt" @@ -1281,7 +1242,7 @@ msgstr "Datum Zulassung bzw. Satzungsbeschluss" msgid "Binding on" msgstr "Datum Bestandskraft" -#: intervention/forms/forms.py:193 intervention/views.py:92 +#: intervention/forms/forms.py:193 intervention/views.py:94 msgid "New intervention" msgstr "Neuer Eingriff" @@ -1335,20 +1296,20 @@ msgstr "Muss kleiner als 15 Mb sein" msgid "Add revocation" msgstr "Widerspruch hinzufügen" -#: intervention/forms/modalForms.py:180 +#: intervention/forms/modalForms.py:197 msgid "Checked intervention data" msgstr "Eingriffsdaten geprüft" -#: intervention/forms/modalForms.py:186 +#: intervention/forms/modalForms.py:203 msgid "Checked compensations data and payments" msgstr "Kompensationen und Zahlungen geprüft" -#: intervention/forms/modalForms.py:195 +#: intervention/forms/modalForms.py:212 #: intervention/templates/intervention/detail/includes/controls.html:19 msgid "Run check" msgstr "Prüfung vornehmen" -#: intervention/forms/modalForms.py:196 konova/forms.py:457 +#: intervention/forms/modalForms.py:213 konova/forms.py:474 msgid "" "I, {} {}, confirm that all necessary control steps have been performed by " "myself." @@ -1356,23 +1317,23 @@ msgstr "" "Ich, {} {}, bestätige, dass die notwendigen Kontrollschritte durchgeführt " "wurden:" -#: intervention/forms/modalForms.py:280 +#: intervention/forms/modalForms.py:297 msgid "Only recorded accounts can be selected for deductions" msgstr "Nur verzeichnete Ökokonten können für Abbuchungen verwendet werden." -#: intervention/forms/modalForms.py:307 +#: intervention/forms/modalForms.py:324 msgid "Only shared interventions can be selected" msgstr "Nur freigegebene Eingriffe können gewählt werden" -#: intervention/forms/modalForms.py:320 +#: intervention/forms/modalForms.py:337 msgid "New Deduction" msgstr "Neue Abbuchung" -#: intervention/forms/modalForms.py:321 +#: intervention/forms/modalForms.py:338 msgid "Enter the information for a new deduction from a chosen eco-account" msgstr "Geben Sie die Informationen für eine neue Abbuchung ein." -#: intervention/forms/modalForms.py:349 +#: intervention/forms/modalForms.py:366 msgid "" "Eco-account {} is not recorded yet. You can only deduct from recorded " "accounts." @@ -1380,7 +1341,7 @@ msgstr "" "Ökokonto {} ist noch nicht verzeichnet. Abbuchungen können nur von " "verzeichneten Ökokonten erfolgen." -#: intervention/forms/modalForms.py:362 +#: intervention/forms/modalForms.py:379 msgid "" "The account {} has not enough surface for a deduction of {} m². There are " "only {} m² left" @@ -1388,18 +1349,9 @@ msgstr "" "Das Ökokonto {} hat für eine Abbuchung von {} m² nicht ausreichend " "Restfläche. Es stehen noch {} m² zur Verfügung." -#: intervention/tables.py:45 -#: intervention/templates/intervention/detail/includes/revocation.html:58 -msgid "Revocation" -msgstr "Widerspruch" - -#: intervention/tables.py:177 -msgid "No revocation" -msgstr "Kein Widerspruch" - -#: intervention/tables.py:183 -msgid "Revocation from {}, added on {} by {}" -msgstr "Widerspruch vom {}, am {} von {} hinzugefügt" +#: intervention/tables.py:34 konova/filters/mixins.py:98 +msgid "Parcel gmrkng" +msgstr "Gemarkung" #: intervention/templates/intervention/detail/includes/compensations.html:14 msgid "Add new compensation" @@ -1449,6 +1401,10 @@ msgctxt "Revocation" msgid "From" msgstr "Vom" +#: intervention/templates/intervention/detail/includes/revocation.html:58 +msgid "Revocation" +msgstr "Widerspruch" + #: intervention/templates/intervention/detail/includes/revocation.html:69 msgid "Remove revocation" msgstr "Widerspruch entfernen" @@ -1465,6 +1421,17 @@ msgstr "Abbuchungen von Ökokonten" msgid "Exist" msgstr "Vorhanden" +#: intervention/templates/intervention/table/gmrkng_col.html:6 +msgid "" +"\n" +"If the geometry is not empty, the parcels are currently recalculated. Please " +"refresh this page in a few moments." +msgstr "" +"\n" +"Falls die Geometrie nicht leer ist, werden die Flurstücke aktuell berechnet. " +"Bitte laden Sie diese Seite in ein paar Augenblicken erneut... \n" +" " + #: intervention/utils/quality.py:70 msgid "Revocations exists" msgstr "Widersprüche liegen vor" @@ -1477,37 +1444,37 @@ msgstr "Datum Bestandskraft" msgid "Laws" msgstr "Gesetze" -#: intervention/utils/quality.py:98 +#: intervention/utils/quality.py:101 msgid "No compensation of any type found (Compensation, Payment, Deduction)" msgstr "" "Kein Ausgleich jeglicher Art gefunden (Kompensation, Ersatzzahlung, " "Abbuchung)" -#: intervention/views.py:49 +#: intervention/views.py:51 msgid "Interventions - Overview" msgstr "Eingriffe - Übersicht" -#: intervention/views.py:82 +#: intervention/views.py:84 msgid "Intervention {} added" msgstr "Eingriff {} hinzugefügt" -#: intervention/views.py:250 +#: intervention/views.py:252 msgid "This intervention has {} revocations" msgstr "Dem Eingriff liegen {} Widersprüche vor" -#: intervention/views.py:299 +#: intervention/views.py:301 msgid "Intervention {} edited" msgstr "Eingriff {} bearbeitet" -#: intervention/views.py:335 +#: intervention/views.py:337 msgid "{} removed" msgstr "{} entfernt" -#: intervention/views.py:440 +#: intervention/views.py:444 msgid "Check performed" msgstr "Prüfung durchgeführt" -#: intervention/views.py:563 +#: intervention/views.py:567 msgid "There are errors on this intervention:" msgstr "Es liegen Fehler in diesem Eingriff vor:" @@ -1544,10 +1511,6 @@ msgstr "Kreis" msgid "Search for district" msgstr "Nach Kreis suchen" -#: konova/filters/mixins.py:98 -msgid "Parcel gmrkng" -msgstr "Gemarkung" - #: konova/filters/mixins.py:99 msgid "Search for parcel gmrkng" msgstr "Nach Gemarkung suchen" @@ -1624,52 +1587,52 @@ msgstr "Geometrie" msgid "Are you sure?" msgstr "Sind Sie sicher?" -#: konova/forms.py:348 +#: konova/forms.py:365 msgid "Created on" msgstr "Erstellt" -#: konova/forms.py:350 +#: konova/forms.py:367 msgid "When has this file been created? Important for photos." msgstr "Wann wurde diese Datei erstellt oder das Foto aufgenommen?" -#: konova/forms.py:361 +#: konova/forms.py:378 #: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231 msgid "File" msgstr "Datei" -#: konova/forms.py:363 +#: konova/forms.py:380 msgid "Allowed formats: pdf, jpg, png. Max size 15 MB." msgstr "Formate: pdf, jpg, png. Maximal 15 MB." -#: konova/forms.py:409 +#: konova/forms.py:426 msgid "Unsupported file type" msgstr "Dateiformat nicht unterstützt" -#: konova/forms.py:416 +#: konova/forms.py:433 msgid "File too large" msgstr "Datei zu groß" -#: konova/forms.py:425 +#: konova/forms.py:442 msgid "Added document" msgstr "Dokument hinzugefügt" -#: konova/forms.py:448 +#: konova/forms.py:465 msgid "Confirm record" msgstr "Verzeichnen bestätigen" -#: konova/forms.py:456 +#: konova/forms.py:473 msgid "Record data" msgstr "Daten verzeichnen" -#: konova/forms.py:463 +#: konova/forms.py:480 msgid "Confirm unrecord" msgstr "Entzeichnen bestätigen" -#: konova/forms.py:464 +#: konova/forms.py:481 msgid "Unrecord data" msgstr "Daten entzeichnen" -#: konova/forms.py:465 +#: konova/forms.py:482 msgid "I, {} {}, confirm that this data must be unrecorded." msgstr "" "Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen." @@ -1772,35 +1735,31 @@ msgstr "In Zwischenablage kopieren" msgid "Copied to clipboard" msgstr "In Zwischenablage kopiert" -#: konova/utils/documents.py:52 -msgid "Document '{}' deleted" -msgstr "Dokument '{}' gelöscht" - -#: konova/utils/mailer.py:66 +#: konova/utils/mailer.py:68 msgid "{} - Shared access removed" msgstr "{} - Zugriff entzogen" -#: konova/utils/mailer.py:88 +#: konova/utils/mailer.py:91 msgid "{} - Shared access given" msgstr "{} - Zugriff freigegeben" -#: konova/utils/mailer.py:110 +#: konova/utils/mailer.py:114 msgid "{} - Shared data recorded" msgstr "{} - Freigegebene Daten verzeichnet" -#: konova/utils/mailer.py:132 +#: konova/utils/mailer.py:137 msgid "{} - Shared data unrecorded" msgstr "{} - Freigegebene Daten entzeichnet" -#: konova/utils/mailer.py:154 +#: konova/utils/mailer.py:160 msgid "{} - Shared data deleted" msgstr "{} - Freigegebene Daten gelöscht" -#: konova/utils/mailer.py:176 +#: konova/utils/mailer.py:183 msgid "{} - Shared data checked" msgstr "{} - Freigegebene Daten geprüft" -#: konova/utils/mailer.py:197 templates/email/api/verify_token.html:4 +#: konova/utils/mailer.py:204 templates/email/api/verify_token.html:4 msgid "Request for new API token" msgstr "Anfrage für neuen API Token" @@ -1862,43 +1821,79 @@ msgstr "Kompensation {} hinzugefügt" msgid "Compensation {} removed" msgstr "Kompensation {} entfernt" -#: konova/utils/message_templates.py:29 -msgid "Deduction added" -msgstr "Abbuchung hinzugefügt" - -#: konova/utils/message_templates.py:30 -msgid "Deduction removed" -msgstr "Abbuchung entfernt" - -#: konova/utils/message_templates.py:33 -msgid "Payment added" -msgstr "Zahlung hinzugefügt" - -#: konova/utils/message_templates.py:34 -msgid "Payment removed" -msgstr "Zahlung gelöscht" - -#: konova/utils/message_templates.py:37 -msgid "Revocation added" -msgstr "Widerspruch hinzugefügt" - -#: konova/utils/message_templates.py:38 -msgid "Revocation removed" -msgstr "Widerspruch entfernt" - -#: konova/utils/message_templates.py:41 -msgid "Edited general data" -msgstr "Allgemeine Daten bearbeitet" - -#: konova/utils/message_templates.py:42 -msgid "Added compensation state" -msgstr "Zustand hinzugefügt" - -#: konova/utils/message_templates.py:44 +#: konova/utils/message_templates.py:28 msgid "Added compensation action" msgstr "Maßnahme hinzugefügt" -#: konova/utils/message_templates.py:47 +#: konova/utils/message_templates.py:29 +msgid "Added compensation state" +msgstr "Zustand hinzugefügt" + +#: konova/utils/message_templates.py:32 +msgid "State removed" +msgstr "Zustand gelöscht" + +#: konova/utils/message_templates.py:33 +msgid "State added" +msgstr "Zustand hinzugefügt" + +#: konova/utils/message_templates.py:36 +msgid "Action added" +msgstr "Maßnahme hinzugefügt" + +#: konova/utils/message_templates.py:37 +msgid "Action removed" +msgstr "Maßnahme entfernt" + +#: konova/utils/message_templates.py:40 +msgid "Deduction added" +msgstr "Abbuchung hinzugefügt" + +#: konova/utils/message_templates.py:41 +msgid "Deduction removed" +msgstr "Abbuchung entfernt" + +#: konova/utils/message_templates.py:44 +msgid "Deadline added" +msgstr "Frist/Termin hinzugefügt" + +#: konova/utils/message_templates.py:45 +msgid "Deadline removed" +msgstr "Frist/Termin gelöscht" + +#: konova/utils/message_templates.py:48 +msgid "Payment added" +msgstr "Zahlung hinzugefügt" + +#: konova/utils/message_templates.py:49 +msgid "Payment removed" +msgstr "Zahlung gelöscht" + +#: konova/utils/message_templates.py:52 +msgid "Revocation added" +msgstr "Widerspruch hinzugefügt" + +#: konova/utils/message_templates.py:53 +msgid "Revocation removed" +msgstr "Widerspruch entfernt" + +#: konova/utils/message_templates.py:56 +msgid "Document '{}' deleted" +msgstr "Dokument '{}' gelöscht" + +#: konova/utils/message_templates.py:57 +msgid "Document added" +msgstr "Dokument hinzugefügt" + +#: konova/utils/message_templates.py:60 +msgid "Edited general data" +msgstr "Allgemeine Daten bearbeitet" + +#: konova/utils/message_templates.py:61 +msgid "Added deadline" +msgstr "Frist/Termin hinzugefügt" + +#: konova/utils/message_templates.py:64 msgid "Geometry conflict detected with {}" msgstr "Geometriekonflikt mit folgenden Einträgen erkannt: {}" @@ -1984,12 +1979,12 @@ msgstr "" "Admin Backend aktiviert worden ist." #: templates/email/api/verify_token.html:18 -#: templates/email/checking/shared_data_checked.html:17 -#: templates/email/deleting/shared_data_deleted.html:17 -#: templates/email/recording/shared_data_recorded.html:17 -#: templates/email/recording/shared_data_unrecorded.html:17 -#: templates/email/sharing/shared_access_given.html:18 -#: templates/email/sharing/shared_access_removed.html:18 +#: templates/email/checking/shared_data_checked.html:19 +#: templates/email/deleting/shared_data_deleted.html:19 +#: templates/email/recording/shared_data_recorded.html:19 +#: templates/email/recording/shared_data_unrecorded.html:19 +#: templates/email/sharing/shared_access_given.html:20 +#: templates/email/sharing/shared_access_removed.html:20 msgid "Best regards" msgstr "Beste Grüße" @@ -2010,7 +2005,7 @@ msgstr "Hallo " msgid "the following dataset has just been checked" msgstr "der folgende Datensatz wurde soeben geprüft " -#: templates/email/checking/shared_data_checked.html:14 +#: templates/email/checking/shared_data_checked.html:16 msgid "" "This means, the responsible registration office just confirmed the " "correctness of this dataset." @@ -2026,7 +2021,7 @@ msgstr "Freigegebene Daten gelöscht" msgid "the following dataset has just been deleted" msgstr "der folgende Datensatz wurde soeben gelöscht " -#: templates/email/deleting/shared_data_deleted.html:14 +#: templates/email/deleting/shared_data_deleted.html:16 msgid "" "If this should not have been happened, please contact us. See the signature " "for details." @@ -2042,12 +2037,12 @@ msgstr "Freigegebene Daten verzeichnet" msgid "the following dataset has just been recorded" msgstr "der folgende Datensatz wurde soeben verzeichnet " -#: templates/email/recording/shared_data_recorded.html:14 +#: templates/email/recording/shared_data_recorded.html:16 msgid "This means the data is now publicly available, e.g. in LANIS" msgstr "" "Das bedeutet, dass die Daten nun öffentlich verfügbar sind, z.B. im LANIS." -#: templates/email/recording/shared_data_recorded.html:24 +#: templates/email/recording/shared_data_recorded.html:26 msgid "" "Please note: Recorded intervention means the compensations are recorded as " "well." @@ -2063,11 +2058,11 @@ msgstr "Freigegebene Daten entzeichnet" msgid "the following dataset has just been unrecorded" msgstr "der folgende Datensatz wurde soeben entzeichnet " -#: templates/email/recording/shared_data_unrecorded.html:14 +#: templates/email/recording/shared_data_unrecorded.html:16 msgid "This means the data is no longer publicly available." msgstr "Das bedeutet, dass die Daten nicht länger öffentlich verfügbar sind." -#: templates/email/recording/shared_data_unrecorded.html:24 +#: templates/email/recording/shared_data_unrecorded.html:26 msgid "" "Please note: Unrecorded intervention means the compensations are unrecorded " "as well." @@ -2083,11 +2078,11 @@ msgstr "Zugriff freigegeben" msgid "the following dataset has just been shared with you" msgstr "der folgende Datensatz wurde soeben für Sie freigegeben " -#: templates/email/sharing/shared_access_given.html:14 +#: templates/email/sharing/shared_access_given.html:16 msgid "This means you can now edit this dataset." msgstr "Das bedeutet, dass Sie diesen Datensatz nun auch bearbeiten können." -#: templates/email/sharing/shared_access_given.html:15 +#: templates/email/sharing/shared_access_given.html:17 msgid "" "The shared dataset appears now by default on your overview for this dataset " "type." @@ -2095,7 +2090,7 @@ msgstr "" "Der freigegebene Datensatz ist nun standardmäßig in Ihrer Übersicht für den " "Datensatztyp im KSP gelistet." -#: templates/email/sharing/shared_access_given.html:25 +#: templates/email/sharing/shared_access_given.html:27 msgid "" "Please note: Shared access on an intervention means you automatically have " "editing access to related compensations." @@ -2115,11 +2110,11 @@ msgstr "" "Ihnen wurde soeben der bearbeitende Zugriff auf den folgenden Datensatz " "entzogen: " -#: templates/email/sharing/shared_access_removed.html:14 +#: templates/email/sharing/shared_access_removed.html:16 msgid "However, you are still able to view the dataset content." msgstr "Sie können den Datensatz aber immer noch im KSP einsehen." -#: templates/email/sharing/shared_access_removed.html:15 +#: templates/email/sharing/shared_access_removed.html:17 msgid "" "Please use the provided search filter on the dataset`s overview pages to " "find them." @@ -3918,6 +3913,12 @@ msgstr "" msgid "Unable to connect to qpid with SASL mechanism %s" msgstr "" +#~ msgid "No revocation" +#~ msgstr "Kein Widerspruch" + +#~ msgid "Revocation from {}, added on {} by {}" +#~ msgstr "Widerspruch vom {}, am {} von {} hinzugefügt" + #~ msgid "General data edited" #~ msgstr "Allgemeine Daten bearbeitet" diff --git a/templates/table/gmrkng_col.html b/templates/table/gmrkng_col.html new file mode 100644 index 00000000..bd8878de --- /dev/null +++ b/templates/table/gmrkng_col.html @@ -0,0 +1,9 @@ +{% load i18n fontawesome_5 %} + +{% for entry in entries %} + {{entry}} +{% empty %} + + {% fa5_icon 'hourglass-half' %} + +{% endfor %} \ No newline at end of file From 5e79f16e1e1befd7766420c910c3f26bdb7587f6 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 15:07:05 +0100 Subject: [PATCH 11/31] #86 Revocation rendering if needed * renders revocation warning on the index view if a revocation exists --- intervention/tables.py | 20 +++++++++++--------- templates/table/revocation_warning_col.html | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 templates/table/revocation_warning_col.html diff --git a/intervention/tables.py b/intervention/tables.py index 223cd8a4..fd39ddcf 100644 --- a/intervention/tables.py +++ b/intervention/tables.py @@ -84,14 +84,17 @@ class InterventionTable(BaseTable, TableRenderMixin): Returns: """ - html = "" - html += self.render_link( - tooltip=_("Open {}").format(_("Intervention")), - href=reverse("intervention:detail", args=(record.id,)), - txt=value, - new_tab=False, + context = { + "tooltip": _("Open {}").format(_("Intervention")), + "content": value, + "url": reverse("intervention:detail", args=(record.id,)), + "has_revocations": record.legal.revocations.exists() + } + html = render_to_string( + "table/revocation_warning_col.html", + context ) - return format_html(html) + return html def render_c(self, value, record: Intervention): """ Renders the checked column for an intervention @@ -127,7 +130,7 @@ class InterventionTable(BaseTable, TableRenderMixin): Returns: """ - parcels = value.parcels.all().values_list( + parcels = value.parcels.values_list( "gmrkng", flat=True ).distinct() @@ -139,7 +142,6 @@ class InterventionTable(BaseTable, TableRenderMixin): ) return html - def render_r(self, value, record: Intervention): """ Renders the recorded column for an intervention diff --git a/templates/table/revocation_warning_col.html b/templates/table/revocation_warning_col.html new file mode 100644 index 00000000..2ed0922a --- /dev/null +++ b/templates/table/revocation_warning_col.html @@ -0,0 +1,14 @@ +{% load i18n fontawesome_5 %} + +{% if has_revocations %} + + + {% fa5_icon 'ban' %} + {{content}} + + +{% else %} + + {{content}} + +{% endif %} \ No newline at end of file From 402bc2d6f3bc1b2c7d227062701e38dd8b59a228 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 15:25:44 +0100 Subject: [PATCH 12/31] #86 Parcel district column for all * adds parcel district column for all major data objects * adds warning about intervention-revocation on index view of compensations * adds warning about intervention-revocation on detail view of related compensations --- compensation/forms/modalForms.py | 2 +- compensation/models/compensation.py | 22 ++++++- compensation/tables.py | 76 ++++++++++++++++++++--- ema/tables.py | 29 +++++++++ intervention/models/intervention.py | 9 ++- intervention/views.py | 8 --- konova/static/css/konova.css | 7 +++ konova/utils/message_templates.py | 3 + locale/de/LC_MESSAGES/django.mo | Bin 36677 -> 36661 bytes locale/de/LC_MESSAGES/django.po | 93 ++++++++++++++-------------- 10 files changed, 184 insertions(+), 65 deletions(-) diff --git a/compensation/forms/modalForms.py b/compensation/forms/modalForms.py index ae5a6dad..18e60611 100644 --- a/compensation/forms/modalForms.py +++ b/compensation/forms/modalForms.py @@ -21,7 +21,7 @@ from konova.contexts import BaseContext from konova.forms import BaseModalForm, NewDocumentForm, RemoveModalForm from konova.models import DeadlineType from konova.utils.message_templates import FORM_INVALID, ADDED_COMPENSATION_STATE, ADDED_DEADLINE, \ - ADDED_COMPENSATION_ACTION, PAYMENT_ADDED + ADDED_COMPENSATION_ACTION class NewPaymentForm(BaseModalForm): diff --git a/compensation/models/compensation.py b/compensation/models/compensation.py index e90e1735..491a8208 100644 --- a/compensation/models/compensation.py +++ b/compensation/models/compensation.py @@ -22,7 +22,7 @@ from konova.models import BaseObject, AbstractDocument, Deadline, generate_docum from konova.settings import DEFAULT_SRID_RLP, LANIS_LINK_TEMPLATE from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, COMPENSATION_REMOVED_TEMPLATE, \ DOCUMENT_REMOVED_TEMPLATE, COMPENSATION_EDITED_TEMPLATE, DEADLINE_REMOVED, ADDED_DEADLINE, \ - COMPENSATION_ACTION_REMOVED, COMPENSATION_STATE_REMOVED + COMPENSATION_ACTION_REMOVED, COMPENSATION_STATE_REMOVED, INTERVENTION_HAS_REVOCATIONS_TEMPLATE from user.models import UserActionLogEntry @@ -390,6 +390,26 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin): """ return self.intervention.is_ready_for_publish() + def set_status_messages(self, request: HttpRequest): + """ Setter for different information that need to be rendered + + Adds messages to the given HttpRequest + + Args: + request (HttpRequest): The incoming request + + Returns: + request (HttpRequest): The modified request + """ + if self.intervention.legal.revocations.exists(): + messages.error( + request, + INTERVENTION_HAS_REVOCATIONS_TEMPLATE.format(self.intervention.legal.revocations.count()), + extra_tags="danger", + ) + super().set_status_messages(request) + return request + class CompensationDocument(AbstractDocument): """ diff --git a/compensation/tables.py b/compensation/tables.py index b78b5aa9..6f29e0dd 100644 --- a/compensation/tables.py +++ b/compensation/tables.py @@ -31,6 +31,11 @@ class CompensationTable(BaseTable, TableRenderMixin): orderable=True, accessor="title", ) + d = tables.Column( + verbose_name=_("Parcel gmrkng"), + orderable=True, + accessor="geometry", + ) c = tables.Column( verbose_name=_("Checked"), orderable=True, @@ -80,14 +85,17 @@ class CompensationTable(BaseTable, TableRenderMixin): Returns: """ - html = "" - html += self.render_link( - tooltip=_("Open {}").format(_("Compensation")), - href=reverse("compensation:detail", args=(record.id,)), - txt=value, - new_tab=False, + context = { + "tooltip": _("Open {}").format(_("Intervention")), + "content": value, + "url": reverse("compensation:detail", args=(record.id,)), + "has_revocations": record.intervention.legal.revocations.exists() + } + html = render_to_string( + "table/revocation_warning_col.html", + context ) - return format_html(html) + return html def render_c(self, value, record: Compensation): """ Renders the checked column for a compensation @@ -115,6 +123,28 @@ class CompensationTable(BaseTable, TableRenderMixin): ) return format_html(html) + def render_d(self, value, record: Compensation): + """ Renders the parcel district column for a compensation + + Args: + value (str): The geometry + record (Compensation): The compensation record + + Returns: + + """ + parcels = value.parcels.values_list( + "gmrkng", + flat=True + ).distinct() + html = render_to_string( + "table/gmrkng_col.html", + { + "entries": parcels + } + ) + return html + def render_r(self, value, record: Compensation): """ Renders the registered column for a compensation @@ -173,10 +203,20 @@ class EcoAccountTable(BaseTable, TableRenderMixin): orderable=True, accessor="title", ) + d = tables.Column( + verbose_name=_("Parcel gmrkng"), + orderable=True, + accessor="geometry", + ) av = tables.Column( verbose_name=_("Available"), orderable=True, empty_values=[], + attrs={ + "th": { + "class": "w-20", + } + } ) r = tables.Column( verbose_name=_("Recorded"), @@ -244,6 +284,28 @@ class EcoAccountTable(BaseTable, TableRenderMixin): html = render_to_string("konova/widgets/progressbar.html", {"value": value_relative}) return format_html(html) + def render_d(self, value, record: Compensation): + """ Renders the parcel district column for a compensation + + Args: + value (str): The geometry + record (Compensation): The compensation record + + Returns: + + """ + parcels = value.parcels.values_list( + "gmrkng", + flat=True + ).distinct() + html = render_to_string( + "table/gmrkng_col.html", + { + "entries": parcels + } + ) + return html + def render_r(self, value, record: EcoAccount): """ Renders the recorded column for an eco account diff --git a/ema/tables.py b/ema/tables.py index d30f3e36..20ceb456 100644 --- a/ema/tables.py +++ b/ema/tables.py @@ -6,6 +6,7 @@ Created on: 19.08.21 """ from django.http import HttpRequest +from django.template.loader import render_to_string from django.utils.html import format_html from django.utils.timezone import localtime from django.utils.translation import gettext_lazy as _ @@ -34,6 +35,11 @@ class EmaTable(BaseTable, TableRenderMixin): orderable=True, accessor="title", ) + d = tables.Column( + verbose_name=_("Parcel gmrkng"), + orderable=True, + accessor="geometry", + ) r = tables.Column( verbose_name=_("Recorded"), orderable=True, @@ -87,6 +93,29 @@ class EmaTable(BaseTable, TableRenderMixin): ) return format_html(html) + def render_d(self, value, record: Ema): + """ Renders the parcel district column for a ema + + Args: + value (str): The geometry + record (Ema): The ema record + + Returns: + + """ + parcels = value.parcels.values_list( + "gmrkng", + flat=True + ).distinct() + html = render_to_string( + "table/gmrkng_col.html", + { + "entries": parcels + } + ) + return html + + def render_r(self, value, record: Ema): """ Renders the registered column for a EMA diff --git a/intervention/models/intervention.py b/intervention/models/intervention.py index 821a835f..8f54a2ae 100644 --- a/intervention/models/intervention.py +++ b/intervention/models/intervention.py @@ -26,7 +26,7 @@ from konova.models import generate_document_file_upload_path, AbstractDocument, RecordableObjectMixin, CheckableObjectMixin, GeoReferencedMixin from konova.settings import LANIS_LINK_TEMPLATE, LANIS_ZOOM_LUT, DEFAULT_SRID_RLP from konova.utils.message_templates import DATA_UNSHARED_EXPLANATION, DOCUMENT_REMOVED_TEMPLATE, \ - PAYMENT_REMOVED, PAYMENT_ADDED, REVOCATION_REMOVED + PAYMENT_REMOVED, PAYMENT_ADDED, REVOCATION_REMOVED, INTERVENTION_HAS_REVOCATIONS_TEMPLATE from user.models import UserActionLogEntry @@ -276,6 +276,13 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec Returns: request (HttpRequest): The modified request """ + # Inform user about revocation + if self.legal.revocations.exists(): + messages.error( + request, + INTERVENTION_HAS_REVOCATIONS_TEMPLATE.format(self.legal.revocations.count()), + extra_tags="danger", + ) if not self.is_shared_with(request.user): messages.info(request, DATA_UNSHARED_EXPLANATION) request = self.set_geometry_conflict_message(request) diff --git a/intervention/views.py b/intervention/views.py index c0317035..8a59d9a9 100644 --- a/intervention/views.py +++ b/intervention/views.py @@ -245,14 +245,6 @@ def detail_view(request: HttpRequest, id: str): parcels = intervention.get_underlying_parcels() - # Inform user about revocation - if intervention.legal.revocations.exists(): - messages.error( - request, - _("This intervention has {} revocations").format(intervention.legal.revocations.count()), - extra_tags="danger", - ) - context = { "obj": intervention, "compensations": compensations, diff --git a/konova/static/css/konova.css b/konova/static/css/konova.css index a10e124b..709d3eb3 100644 --- a/konova/static/css/konova.css +++ b/konova/static/css/konova.css @@ -219,6 +219,13 @@ Overwrites bootstrap .btn:focus box shadow color overflow: auto; } +.w-20{ + width: 20%; +} +.w-10{ + width: 20%; +} + /* Extends css for django autocomplete light (dal) No other approach worked to get the autocomplete fields to full width of parent containers diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index e13a9cae..87c22af2 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -62,3 +62,6 @@ ADDED_DEADLINE = _("Added deadline") # Geometry conflicts GEOMETRY_CONFLICT_WITH_TEMPLATE = _("Geometry conflict detected with {}") + +# INTERVENTION +INTERVENTION_HAS_REVOCATIONS_TEMPLATE = _("This intervention has {} revocations") diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index c9d4293ad8de176ef64a6005d1b1cb3428ecc471..74f794dec1833275cf54dc769ab7dab41978bbd3 100644 GIT binary patch delta 9547 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 D)!mGV delta 9549 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\n" "Language-Team: LANGUAGE \n" @@ -136,7 +136,7 @@ msgstr "Zuständigkeitsbereich" #: analysis/templates/analysis/reports/includes/intervention/amount.html:17 #: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:8 #: analysis/templates/analysis/reports/includes/intervention/laws.html:17 -#: compensation/tables.py:35 +#: compensation/tables.py:40 #: compensation/templates/compensation/detail/compensation/view.html:63 #: intervention/tables.py:39 #: intervention/templates/intervention/detail/view.html:68 @@ -152,11 +152,11 @@ msgstr "Geprüft" #: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:9 #: analysis/templates/analysis/reports/includes/intervention/laws.html:20 #: analysis/templates/analysis/reports/includes/old_data/amount.html:18 -#: compensation/tables.py:41 compensation/tables.py:182 +#: compensation/tables.py:46 compensation/tables.py:219 #: compensation/templates/compensation/detail/compensation/view.html:77 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:31 #: compensation/templates/compensation/detail/eco_account/view.html:44 -#: ema/tables.py:38 ema/templates/ema/detail/view.html:35 +#: ema/tables.py:44 ema/templates/ema/detail/view.html:35 #: intervention/tables.py:45 #: intervention/templates/intervention/detail/view.html:82 #: user/models/user_action.py:21 @@ -196,7 +196,7 @@ msgid "Other registration office" msgstr "Andere Zulassungsbehörden" #: analysis/templates/analysis/reports/includes/compensation/card_compensation.html:11 -#: compensation/tables.py:62 +#: compensation/tables.py:67 #: intervention/templates/intervention/detail/includes/compensations.html:8 #: intervention/templates/intervention/report/report.html:49 msgid "Compensations" @@ -239,7 +239,7 @@ msgstr "Kompensationsart" #: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:15 #: analysis/templates/analysis/reports/includes/old_data/amount.html:29 -#: compensation/tables.py:85 +#: compensation/tables.py:90 #: compensation/templates/compensation/detail/compensation/view.html:19 #: konova/templates/konova/includes/quickstart/compensations.html:4 #: templates/navbars/navbar.html:28 @@ -285,7 +285,7 @@ msgstr "Typ" #: analysis/templates/analysis/reports/includes/old_data/amount.html:24 #: intervention/forms/modalForms.py:322 intervention/forms/modalForms.py:329 -#: intervention/tables.py:89 +#: intervention/tables.py:88 #: intervention/templates/intervention/detail/view.html:19 #: konova/templates/konova/includes/quickstart/interventions.html:4 #: templates/navbars/navbar.html:22 @@ -293,7 +293,7 @@ msgid "Intervention" msgstr "Eingriff" #: analysis/templates/analysis/reports/includes/old_data/amount.html:34 -#: compensation/tables.py:226 +#: compensation/tables.py:263 #: compensation/templates/compensation/detail/eco_account/view.html:19 #: intervention/forms/modalForms.py:295 intervention/forms/modalForms.py:302 #: konova/templates/konova/includes/quickstart/ecoaccounts.html:4 @@ -314,7 +314,7 @@ msgid "Show only unrecorded" msgstr "Nur unverzeichnete anzeigen" #: compensation/forms/forms.py:32 compensation/tables.py:25 -#: compensation/tables.py:167 ema/tables.py:28 intervention/forms/forms.py:28 +#: compensation/tables.py:194 ema/tables.py:29 intervention/forms/forms.py:28 #: intervention/tables.py:24 #: intervention/templates/intervention/detail/includes/compensations.html:30 msgid "Identifier" @@ -326,14 +326,14 @@ msgid "Generated automatically" msgstr "Automatisch generiert" #: compensation/forms/forms.py:44 compensation/tables.py:30 -#: compensation/tables.py:172 +#: compensation/tables.py:199 #: compensation/templates/compensation/detail/compensation/includes/documents.html:28 #: compensation/templates/compensation/detail/compensation/view.html:31 #: compensation/templates/compensation/detail/eco_account/includes/documents.html:28 #: compensation/templates/compensation/detail/eco_account/view.html:31 #: compensation/templates/compensation/report/compensation/report.html:12 #: compensation/templates/compensation/report/eco_account/report.html:12 -#: ema/tables.py:33 ema/templates/ema/detail/includes/documents.html:28 +#: ema/tables.py:34 ema/templates/ema/detail/includes/documents.html:28 #: ema/templates/ema/detail/view.html:31 #: ema/templates/ema/report/report.html:12 intervention/forms/forms.py:40 #: intervention/tables.py:29 @@ -657,65 +657,70 @@ msgstr "" "Es wurde bereits mehr Fläche abgebucht, als Sie nun als abbuchbar einstellen " "wollen. Kontaktieren Sie die für die Abbuchungen verantwortlichen Nutzer!" -#: compensation/tables.py:47 compensation/tables.py:188 ema/tables.py:44 +#: compensation/tables.py:35 compensation/tables.py:204 ema/tables.py:39 +#: intervention/tables.py:34 konova/filters/mixins.py:98 +msgid "Parcel gmrkng" +msgstr "Gemarkung" + +#: compensation/tables.py:52 compensation/tables.py:225 ema/tables.py:50 #: intervention/tables.py:51 msgid "Editable" msgstr "Freigegeben" -#: compensation/tables.py:53 compensation/tables.py:194 ema/tables.py:50 +#: compensation/tables.py:58 compensation/tables.py:231 ema/tables.py:56 #: intervention/tables.py:57 msgid "Last edit" msgstr "Zuletzt bearbeitet" -#: compensation/tables.py:85 compensation/tables.py:226 ema/tables.py:83 -#: intervention/tables.py:89 +#: compensation/tables.py:90 compensation/tables.py:263 ema/tables.py:89 +#: intervention/tables.py:88 msgid "Open {}" msgstr "Öffne {}" -#: compensation/tables.py:106 intervention/tables.py:108 +#: compensation/tables.py:111 intervention/tables.py:111 msgid "Not checked yet" msgstr "Noch nicht geprüft" -#: compensation/tables.py:111 intervention/tables.py:113 +#: compensation/tables.py:116 intervention/tables.py:116 msgid "Checked on {} by {}" msgstr "Am {} von {} geprüft worden" -#: compensation/tables.py:130 +#: compensation/tables.py:157 #: compensation/templates/compensation/detail/compensation/view.html:80 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:58 #: compensation/templates/compensation/detail/eco_account/view.html:47 -#: ema/tables.py:102 ema/templates/ema/detail/view.html:38 -#: intervention/tables.py:146 +#: ema/tables.py:131 ema/templates/ema/detail/view.html:38 +#: intervention/tables.py:157 #: intervention/templates/intervention/detail/view.html:85 msgid "Not recorded yet" msgstr "Noch nicht verzeichnet" -#: compensation/tables.py:135 compensation/tables.py:264 ema/tables.py:107 -#: intervention/tables.py:151 +#: compensation/tables.py:162 compensation/tables.py:323 ema/tables.py:136 +#: intervention/tables.py:162 msgid "Recorded on {} by {}" msgstr "Am {} von {} verzeichnet worden" -#: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:130 -#: intervention/tables.py:174 +#: compensation/tables.py:186 compensation/tables.py:345 ema/tables.py:159 +#: intervention/tables.py:185 msgid "Full access granted" msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden" -#: compensation/tables.py:159 compensation/tables.py:286 ema/tables.py:130 -#: intervention/tables.py:174 +#: compensation/tables.py:186 compensation/tables.py:345 ema/tables.py:159 +#: intervention/tables.py:185 msgid "Access not granted" msgstr "Nicht freigegeben - Datensatz nur lesbar" -#: compensation/tables.py:177 +#: compensation/tables.py:209 #: compensation/templates/compensation/detail/eco_account/view.html:35 #: konova/templates/konova/widgets/progressbar.html:3 msgid "Available" msgstr "Verfügbar" -#: compensation/tables.py:203 +#: compensation/tables.py:240 msgid "Eco Accounts" msgstr "Ökokonten" -#: compensation/tables.py:259 +#: compensation/tables.py:318 msgid "Not recorded yet. Can not be used for deductions, yet." msgstr "" "Noch nicht verzeichnet. Kann noch nicht für Abbuchungen genutzt werden." @@ -1149,11 +1154,11 @@ msgstr "Neue EMA hinzufügen" msgid "Edit EMA" msgstr "Bearbeite EMA" -#: ema/tables.py:59 templates/navbars/navbar.html:43 +#: ema/tables.py:65 templates/navbars/navbar.html:43 msgid "Payment funded compensations" msgstr "Ersatzzahlungsmaßnahmen (EMA)" -#: ema/tables.py:60 +#: ema/tables.py:66 msgid "EMA explanation" msgstr "" "EMA sind Kompensationen, die durch Ersatzzahlungen finanziert wurden. " @@ -1161,7 +1166,7 @@ msgstr "" "Maßnahmen aus Ersatzzahlungen, die nach 2015 rechtskräftig wurden, werden " "durch die Stiftung Natur und Umwelt verwaltet." -#: ema/tables.py:83 templates/navbars/navbar.html:43 +#: ema/tables.py:89 templates/navbars/navbar.html:43 msgid "EMA" msgstr "" @@ -1349,10 +1354,6 @@ msgstr "" "Das Ökokonto {} hat für eine Abbuchung von {} m² nicht ausreichend " "Restfläche. Es stehen noch {} m² zur Verfügung." -#: intervention/tables.py:34 konova/filters/mixins.py:98 -msgid "Parcel gmrkng" -msgstr "Gemarkung" - #: intervention/templates/intervention/detail/includes/compensations.html:14 msgid "Add new compensation" msgstr "Neue Kompensation hinzufügen" @@ -1421,18 +1422,8 @@ msgstr "Abbuchungen von Ökokonten" msgid "Exist" msgstr "Vorhanden" -#: intervention/templates/intervention/table/gmrkng_col.html:6 -msgid "" -"\n" -"If the geometry is not empty, the parcels are currently recalculated. Please " -"refresh this page in a few moments." -msgstr "" -"\n" -"Falls die Geometrie nicht leer ist, werden die Flurstücke aktuell berechnet. " -"Bitte laden Sie diese Seite in ein paar Augenblicken erneut... \n" -" " - #: intervention/utils/quality.py:70 +#: templates/table/revocation_warning_col.html:5 msgid "Revocations exists" msgstr "Widersprüche liegen vor" @@ -2265,6 +2256,14 @@ msgstr "" "wieder vorbei. \n" " " +#: templates/table/gmrkng_col.html:6 +msgid "" +"If the geometry is not empty, the parcels are currently recalculated. Please " +"refresh this page in a few moments." +msgstr "" +"Falls die Geometrie nicht leer ist, werden die Flurstücke aktuell berechnet. " +"Bitte laden Sie diese Seite in ein paar Augenblicken erneut..." + #: user/forms.py:27 msgid "Notifications" msgstr "Benachrichtigungen" From 6ea66180d15ff71b81dec37d7d982b22f098951f Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 17:08:03 +0100 Subject: [PATCH 13/31] #86 Autocomplete enhancement * adds support for title lookup on EcoAccounts * adds support for title lookup on Interventions * adds support for email lookup on User --- konova/autocompletes.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/konova/autocompletes.py b/konova/autocompletes.py index 3c12b36d..3a79ab68 100644 --- a/konova/autocompletes.py +++ b/konova/autocompletes.py @@ -35,8 +35,9 @@ class EcoAccountAutocomplete(Select2QuerySetView): ) if self.q: qs = qs.filter( - identifier__icontains=self.q - ) + Q(identifier__icontains=self.q) | + Q(title__icontains=self.q) + ).distinct() return qs @@ -57,8 +58,9 @@ class InterventionAutocomplete(Select2QuerySetView): ) if self.q: qs = qs.filter( - identifier__icontains=self.q - ) + Q(identifier__icontains=self.q) | + Q(title__icontains=self.q) + ).distinct() return qs @@ -81,8 +83,9 @@ class ShareUserAutocomplete(Select2QuerySetView): if self.q: # Due to privacy concerns only a full username match will return the proper user entry qs = qs.filter( - username=self.q - ) + Q(username=self.q) | + Q(email=self.q) + ).distinct() return qs From a096b2a413ddfc8537105dd90e7d487edfe1679c Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Tue, 8 Feb 2022 17:14:23 +0100 Subject: [PATCH 14/31] # 86 LANIS link fix * simplifies creation of LANIS link by refactoring into super class --- compensation/models/compensation.py | 22 ---------------------- compensation/models/eco_account.py | 22 ---------------------- ema/models/ema.py | 22 ---------------------- intervention/models/intervention.py | 28 ---------------------------- konova/models/object.py | 29 +++++++++++++++++++++++++++++ 5 files changed, 29 insertions(+), 94 deletions(-) diff --git a/compensation/models/compensation.py b/compensation/models/compensation.py index 491a8208..4dd2b4c2 100644 --- a/compensation/models/compensation.py +++ b/compensation/models/compensation.py @@ -331,28 +331,6 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin): """ return self.intervention.users.all() - def get_LANIS_link(self) -> str: - """ Generates a link for LANIS depending on the geometry - - Returns: - - """ - try: - geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True) - x = geom.centroid.x - y = geom.centroid.y - zoom_lvl = 16 - except AttributeError: - # If no geometry has been added, yet. - x = 1 - y = 1 - zoom_lvl = 6 - return LANIS_LINK_TEMPLATE.format( - zoom_lvl, - x, - y, - ) - def get_documents(self) -> QuerySet: """ Getter for all documents of a compensation diff --git a/compensation/models/eco_account.py b/compensation/models/eco_account.py index 895d537f..6d95b399 100644 --- a/compensation/models/eco_account.py +++ b/compensation/models/eco_account.py @@ -123,28 +123,6 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix return ret_val_total, ret_val_relative - def get_LANIS_link(self) -> str: - """ Generates a link for LANIS depending on the geometry - - Returns: - - """ - try: - geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True) - x = geom.centroid.x - y = geom.centroid.y - zoom_lvl = 16 - except AttributeError: - # If no geometry has been added, yet. - x = 1 - y = 1 - zoom_lvl = 6 - return LANIS_LINK_TEMPLATE.format( - zoom_lvl, - x, - y, - ) - def quality_check(self) -> EcoAccountQualityChecker: """ Quality check diff --git a/ema/models/ema.py b/ema/models/ema.py index b145acba..983bdbd7 100644 --- a/ema/models/ema.py +++ b/ema/models/ema.py @@ -51,28 +51,6 @@ class Ema(AbstractCompensation, ShareableObjectMixin, RecordableObjectMixin): self.identifier = new_id super().save(*args, **kwargs) - def get_LANIS_link(self) -> str: - """ Generates a link for LANIS depending on the geometry - - Returns: - - """ - try: - geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True) - x = geom.centroid.x - y = geom.centroid.y - zoom_lvl = 16 - except AttributeError: - # If no geometry has been added, yet. - x = 1 - y = 1 - zoom_lvl = 6 - return LANIS_LINK_TEMPLATE.format( - zoom_lvl, - x, - y, - ) - def quality_check(self) -> EmaQualityChecker: """ Quality check diff --git a/intervention/models/intervention.py b/intervention/models/intervention.py index 8f54a2ae..e2e736d7 100644 --- a/intervention/models/intervention.py +++ b/intervention/models/intervention.py @@ -100,34 +100,6 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec checker.run_check() return checker - def get_LANIS_link(self) -> str: - """ Generates a link for LANIS depending on the geometry - - Returns: - - """ - try: - geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True) - x = geom.centroid.x - y = geom.centroid.y - area = int(geom.envelope.area) - z_l = 16 - for k_area, v_zoom in LANIS_ZOOM_LUT.items(): - if k_area < area: - z_l = v_zoom - break - zoom_lvl = z_l - except (AttributeError, IndexError) as e: - # If no geometry has been added, yet. - x = 1 - y = 1 - zoom_lvl = 6 - return LANIS_LINK_TEMPLATE.format( - zoom_lvl, - x, - y, - ) - def get_documents(self) -> (QuerySet, QuerySet): """ Getter for all documents of an intervention diff --git a/konova/models/object.py b/konova/models/object.py index 06256abb..a6164f5a 100644 --- a/konova/models/object.py +++ b/konova/models/object.py @@ -12,6 +12,7 @@ from abc import abstractmethod from django.contrib import messages from django.db.models import QuerySet +from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP, LANIS_ZOOM_LUT, LANIS_LINK_TEMPLATE from konova.tasks import celery_send_mail_shared_access_removed, celery_send_mail_shared_access_given, \ celery_send_mail_shared_data_recorded, celery_send_mail_shared_data_unrecorded, \ celery_send_mail_shared_data_deleted, celery_send_mail_shared_data_checked @@ -544,3 +545,31 @@ class GeoReferencedMixin(models.Model): message_str = GEOMETRY_CONFLICT_WITH_TEMPLATE.format(instance_identifiers) messages.info(request, message_str) return request + + def get_LANIS_link(self) -> str: + """ Generates a link for LANIS depending on the geometry + + Returns: + + """ + try: + geom = self.geometry.geom.transform(DEFAULT_SRID_RLP, clone=True) + x = geom.centroid.x + y = geom.centroid.y + area = int(geom.envelope.area) + z_l = 16 + for k_area, v_zoom in LANIS_ZOOM_LUT.items(): + if k_area < area: + z_l = v_zoom + break + zoom_lvl = z_l + except (AttributeError, IndexError) as e: + # If no geometry has been added, yet. + x = 1 + y = 1 + zoom_lvl = 6 + return LANIS_LINK_TEMPLATE.format( + zoom_lvl, + x, + y, + ) \ No newline at end of file From aa338e5519d62dbb4831a71a907048a8aba9f3e1 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 9 Feb 2022 09:18:35 +0100 Subject: [PATCH 15/31] #86 Parcel-Geometry improvement * improves the way parcel-geometry relations are stored on the DB * instead of a numerical sequence we switched to UUID, so no sequence will run out at anytime (new model: ParcelIntersection) * instead of dropping all M2M relations between parcel and geometry on each calculation, we keep the ones that still exist, drop the ones that do not exist and add new ones (if new ones exist) --- compensation/tables.py | 8 ++- ema/tables.py | 4 +- intervention/tables.py | 4 +- konova/migrations/0003_auto_20220208_1801.py | 54 ++++++++++++++++++++ konova/migrations/0004_auto_20220209_0839.py | 17 ++++++ konova/models/geometry.py | 23 +++++++-- konova/models/parcel.py | 21 +++++++- konova/tasks.py | 12 +++-- 8 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 konova/migrations/0003_auto_20220208_1801.py create mode 100644 konova/migrations/0004_auto_20220209_0839.py diff --git a/compensation/tables.py b/compensation/tables.py index 6f29e0dd..5a3da24b 100644 --- a/compensation/tables.py +++ b/compensation/tables.py @@ -133,7 +133,9 @@ class CompensationTable(BaseTable, TableRenderMixin): Returns: """ - parcels = value.parcels.values_list( + parcels = value.parcels.filter( + parcelintersection__calculated_on__isnull=False, + ).values_list( "gmrkng", flat=True ).distinct() @@ -294,7 +296,9 @@ class EcoAccountTable(BaseTable, TableRenderMixin): Returns: """ - parcels = value.parcels.values_list( + parcels = value.parcels.filter( + parcelintersection__calculated_on__isnull=False, + ).values_list( "gmrkng", flat=True ).distinct() diff --git a/ema/tables.py b/ema/tables.py index 20ceb456..bf3709de 100644 --- a/ema/tables.py +++ b/ema/tables.py @@ -103,7 +103,9 @@ class EmaTable(BaseTable, TableRenderMixin): Returns: """ - parcels = value.parcels.values_list( + parcels = value.parcels.filter( + parcelintersection__calculated_on__isnull=False, + ).values_list( "gmrkng", flat=True ).distinct() diff --git a/intervention/tables.py b/intervention/tables.py index fd39ddcf..96d17c59 100644 --- a/intervention/tables.py +++ b/intervention/tables.py @@ -130,7 +130,9 @@ class InterventionTable(BaseTable, TableRenderMixin): Returns: """ - parcels = value.parcels.values_list( + parcels = value.parcels.filter( + parcelintersection__calculated_on__isnull=False, + ).values_list( "gmrkng", flat=True ).distinct() diff --git a/konova/migrations/0003_auto_20220208_1801.py b/konova/migrations/0003_auto_20220208_1801.py new file mode 100644 index 00000000..d1d9b5a0 --- /dev/null +++ b/konova/migrations/0003_auto_20220208_1801.py @@ -0,0 +1,54 @@ +# Generated by Django 3.1.3 on 2022-02-08 17:01 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +def migrate_parcels(apps, schema_editor): + Geometry = apps.get_model('konova', 'Geometry') + SpatialIntersection = apps.get_model('konova', 'SpatialIntersection') + + all_geoms = Geometry.objects.all() + for geom in all_geoms: + SpatialIntersection.objects.bulk_create([ + SpatialIntersection(geometry=geom, parcel=parcel) + for parcel in geom.parcels.all() + ]) + + +class Migration(migrations.Migration): + + dependencies = [ + ('konova', '0002_auto_20220114_0936'), + ] + + operations = [ + migrations.CreateModel( + name='SpatialIntersection', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('calculated_on', models.DateTimeField(auto_now_add=True, null=True)), + ('geometry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='konova.geometry')), + ('parcel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='konova.parcel')), + ], + options={ + 'abstract': False, + }, + ), + migrations.RunPython(migrate_parcels), + migrations.AddField( + model_name='parcel', + name='geometries_tmp', + field=models.ManyToManyField(blank=True, related_name='parcels', through='konova.SpatialIntersection', to='konova.Geometry'), + ), + migrations.RemoveField( + model_name='parcel', + name='geometries', + ), + migrations.RenameField( + model_name='parcel', + old_name='geometries_tmp', + new_name='geometries', + ), + ] diff --git a/konova/migrations/0004_auto_20220209_0839.py b/konova/migrations/0004_auto_20220209_0839.py new file mode 100644 index 00000000..fe41eada --- /dev/null +++ b/konova/migrations/0004_auto_20220209_0839.py @@ -0,0 +1,17 @@ +# Generated by Django 3.1.3 on 2022-02-09 07:39 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('konova', '0003_auto_20220208_1801'), + ] + + operations = [ + migrations.RenameModel( + old_name='SpatialIntersection', + new_name='ParcelIntersection', + ), + ] diff --git a/konova/models/geometry.py b/konova/models/geometry.py index 0a380b48..bec89c39 100644 --- a/konova/models/geometry.py +++ b/konova/models/geometry.py @@ -99,7 +99,7 @@ class Geometry(BaseResource): Returns: """ - from konova.models import Parcel, District + from konova.models import Parcel, District, ParcelIntersection parcel_fetcher = ParcelWFSFetcher( geometry_id=self.id, ) @@ -107,6 +107,7 @@ class Geometry(BaseResource): fetched_parcels = parcel_fetcher.get_features( typename ) + _now = timezone.now() underlying_parcels = [] for result in fetched_parcels: fetched_parcel = result[typename] @@ -125,19 +126,35 @@ class Geometry(BaseResource): krs=fetched_parcel["ave:kreis"], )[0] parcel_obj.district = district - parcel_obj.updated_on = timezone.now() + parcel_obj.updated_on = _now parcel_obj.save() underlying_parcels.append(parcel_obj) + # Update the linked parcels self.parcels.set(underlying_parcels) + # Set the calculated_on intermediate field, so this related data will be found on lookups + intersections_without_ts = self.parcelintersection_set.filter( + parcel__in=self.parcels.all(), + calculated_on__isnull=True, + ) + for entry in intersections_without_ts: + entry.calculated_on = _now + ParcelIntersection.objects.bulk_update( + intersections_without_ts, + ["calculated_on"] + ) + def get_underlying_parcels(self): """ Getter for related parcels and their districts Returns: parcels (QuerySet): The related parcels as queryset """ - parcels = self.parcels.all().prefetch_related( + + parcels = self.parcels.filter( + parcelintersection__calculated_on__isnull=False, + ).prefetch_related( "district" ).order_by( "gmrkng", diff --git a/konova/models/parcel.py b/konova/models/parcel.py index 487225e6..9c887f1a 100644 --- a/konova/models/parcel.py +++ b/konova/models/parcel.py @@ -22,7 +22,7 @@ class Parcel(UuidModel): To avoid conflicts due to german Umlaute, the field names are shortened and vocals are dropped. """ - geometries = models.ManyToManyField("konova.Geometry", related_name="parcels", blank=True) + geometries = models.ManyToManyField("konova.Geometry", blank=True, related_name="parcels", through='ParcelIntersection') district = models.ForeignKey("konova.District", on_delete=models.SET_NULL, null=True, blank=True, related_name="parcels") gmrkng = models.CharField( max_length=1000, @@ -77,3 +77,22 @@ class District(UuidModel): def __str__(self): return f"{self.gmnd} | {self.krs}" + + +class ParcelIntersection(UuidModel): + """ ParcelIntersection is an intermediary model, which is used to configure the + M2M relation between Parcel and Geometry. + + Based on uuids, we will not have (practically) any problems on outrunning primary keys + and extending the model with calculated_on timestamp, we can 'hide' entries while they + are being recalculated and keep track on the last time they have been calculated this + way. + + Please note: The calculated_on describes when the relation between the Parcel and the Geometry + has been established. The updated_on field of Parcel describes when this Parcel has been + changed the last time. + + """ + parcel = models.ForeignKey(Parcel, on_delete=models.CASCADE) + geometry = models.ForeignKey("konova.Geometry", on_delete=models.CASCADE) + calculated_on = models.DateTimeField(auto_now_add=True, null=True, blank=True) diff --git a/konova/tasks.py b/konova/tasks.py index 4c528038..c74a2bd7 100644 --- a/konova/tasks.py +++ b/konova/tasks.py @@ -4,13 +4,19 @@ from celery import shared_task from django.core.exceptions import ObjectDoesNotExist - @shared_task def celery_update_parcels(geometry_id: str, recheck: bool = True): - from konova.models import Geometry + from konova.models import Geometry, ParcelIntersection try: geom = Geometry.objects.get(id=geometry_id) - geom.parcels.clear() + objs = geom.parcelintersection_set.all() + for obj in objs: + obj.calculated_on = None + ParcelIntersection.objects.bulk_update( + objs, + ["calculated_on"] + ) + geom.update_parcels() except ObjectDoesNotExist: if recheck: From c5e3800c344e9b7d6184f42e5af3684aa55afa83 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 9 Feb 2022 09:30:37 +0100 Subject: [PATCH 16/31] #86 District column simplification * simplifies the fetching of districts for district column --- compensation/tables.py | 8 ++------ ema/tables.py | 4 +--- intervention/tables.py | 4 +--- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/compensation/tables.py b/compensation/tables.py index 5a3da24b..96888cc1 100644 --- a/compensation/tables.py +++ b/compensation/tables.py @@ -133,9 +133,7 @@ class CompensationTable(BaseTable, TableRenderMixin): Returns: """ - parcels = value.parcels.filter( - parcelintersection__calculated_on__isnull=False, - ).values_list( + parcels = value.get_underlying_parcels().values_list( "gmrkng", flat=True ).distinct() @@ -296,9 +294,7 @@ class EcoAccountTable(BaseTable, TableRenderMixin): Returns: """ - parcels = value.parcels.filter( - parcelintersection__calculated_on__isnull=False, - ).values_list( + parcels = value.get_underlying_parcels().values_list( "gmrkng", flat=True ).distinct() diff --git a/ema/tables.py b/ema/tables.py index bf3709de..f9689517 100644 --- a/ema/tables.py +++ b/ema/tables.py @@ -103,9 +103,7 @@ class EmaTable(BaseTable, TableRenderMixin): Returns: """ - parcels = value.parcels.filter( - parcelintersection__calculated_on__isnull=False, - ).values_list( + parcels = value.get_underlying_parcels().values_list( "gmrkng", flat=True ).distinct() diff --git a/intervention/tables.py b/intervention/tables.py index 96d17c59..f535039d 100644 --- a/intervention/tables.py +++ b/intervention/tables.py @@ -130,9 +130,7 @@ class InterventionTable(BaseTable, TableRenderMixin): Returns: """ - parcels = value.parcels.filter( - parcelintersection__calculated_on__isnull=False, - ).values_list( + parcels = value.get_underlying_parcels().values_list( "gmrkng", flat=True ).distinct() From ce9143e4b2d5adfaaecda65c2f1c49a011d08f22 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 9 Feb 2022 10:29:34 +0100 Subject: [PATCH 17/31] #86 Edit payment * adds button for payment editing * adds new edit form payment editing * adds tests for views and workflow --- compensation/forms/modalForms.py | 28 ++- compensation/tests/compensation/test_views.py | 2 +- compensation/tests/payment/__init__.py | 7 + compensation/tests/payment/test_views.py | 154 +++++++++++++ compensation/tests/payment/test_workflow.py | 125 +++++++++++ compensation/urls/payment.py | 1 + compensation/views/payment.py | 32 ++- .../detail/includes/payments.html | 7 +- konova/forms.py | 7 +- konova/utils/message_templates.py | 1 + locale/de/LC_MESSAGES/django.mo | Bin 36661 -> 36759 bytes locale/de/LC_MESSAGES/django.po | 212 +++++++++--------- 12 files changed, 462 insertions(+), 114 deletions(-) create mode 100644 compensation/tests/payment/__init__.py create mode 100644 compensation/tests/payment/test_views.py create mode 100644 compensation/tests/payment/test_workflow.py diff --git a/compensation/forms/modalForms.py b/compensation/forms/modalForms.py index 18e60611..a6e7df26 100644 --- a/compensation/forms/modalForms.py +++ b/compensation/forms/modalForms.py @@ -21,7 +21,7 @@ from konova.contexts import BaseContext from konova.forms import BaseModalForm, NewDocumentForm, RemoveModalForm from konova.models import DeadlineType from konova.utils.message_templates import FORM_INVALID, ADDED_COMPENSATION_STATE, ADDED_DEADLINE, \ - ADDED_COMPENSATION_ACTION + ADDED_COMPENSATION_ACTION, PAYMENT_EDITED class NewPaymentForm(BaseModalForm): @@ -103,6 +103,32 @@ class NewPaymentForm(BaseModalForm): return pay +class EditPaymentModalForm(NewPaymentForm): + """ Form handling edit for Payment + + """ + payment = None + + def __init__(self, *args, **kwargs): + self.payment = kwargs.pop("payment", None) + super().__init__(*args, **kwargs) + form_date = { + "amount": self.payment.amount, + "due": str(self.payment.due_on), + "comment": self.payment.comment, + } + self.load_initial_data(form_date, disabled_fields=[]) + + def save(self): + payment = self.payment + payment.amount = self.cleaned_data.get("amount", None) + payment.due_on = self.cleaned_data.get("due", None) + payment.comment = self.cleaned_data.get("comment", None) + payment.save() + self.instance.mark_as_edited(self.user, self.request, edit_comment=PAYMENT_EDITED) + return payment + + class RemovePaymentModalForm(RemoveModalForm): """ Removing modal form for Payment diff --git a/compensation/tests/compensation/test_views.py b/compensation/tests/compensation/test_views.py index 4039496a..465a1026 100644 --- a/compensation/tests/compensation/test_views.py +++ b/compensation/tests/compensation/test_views.py @@ -117,7 +117,7 @@ class CompensationViewTestCase(BaseViewTestCase): def test_logged_in_no_groups_unshared(self): """ Check correct status code for all requests - Assumption: User logged in and has no groups and data is shared + Assumption: User logged in and has no groups and data is not shared Returns: diff --git a/compensation/tests/payment/__init__.py b/compensation/tests/payment/__init__.py new file mode 100644 index 00000000..b90ce206 --- /dev/null +++ b/compensation/tests/payment/__init__.py @@ -0,0 +1,7 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 09.02.22 + +""" diff --git a/compensation/tests/payment/test_views.py b/compensation/tests/payment/test_views.py new file mode 100644 index 00000000..69130e86 --- /dev/null +++ b/compensation/tests/payment/test_views.py @@ -0,0 +1,154 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 09.02.22 + +""" +from django.urls import reverse +from django.test.client import Client + +from compensation.models import Payment +from konova.settings import DEFAULT_GROUP +from konova.tests.test_views import BaseViewTestCase + + +class PaymentViewTestCase(BaseViewTestCase): + + @classmethod + def setUpTestData(cls) -> None: + super().setUpTestData() + + cls.payment = Payment.objects.get_or_create( + intervention=cls.intervention, + amount=1, + due_on="2020-01-01", + comment="Testcomment" + )[0] + + cls.new_url = reverse("compensation:pay:new", args=(cls.intervention.id,)) + cls.edit_url = reverse("compensation:pay:edit", args=(cls.intervention.id, cls.payment.id)) + cls.remove_url = reverse("compensation:pay:remove", args=(cls.intervention.id, cls.payment.id)) + + def test_anonymous_user(self): + """ Check correct status code for all requests + + Assumption: User not logged in + + Returns: + + """ + client = Client() + + success_urls = [ + ] + fail_urls = [ + self.new_url, + self.edit_url, + self.remove_url, + ] + + self.assert_url_success(client, success_urls) + self.assert_url_fail(client, fail_urls) + + def test_logged_in_no_groups_shared(self): + """ Check correct status code for all requests + + Assumption: User logged in and has no groups and data is shared + + Returns: + + """ + client = Client() + client.login(username=self.superuser.username, password=self.superuser_pw) + self.superuser.groups.set([]) + self.intervention.share_with_list([self.superuser]) + + # Since the user has no groups, it does not matter that data has been shared. There SHOULD not be any difference + # to a user without access, since the important permissions are missing + success_urls = [ + ] + fail_urls = [ + self.new_url, + self.edit_url, + self.remove_url, + ] + + self.assert_url_success(client, success_urls) + self.assert_url_fail(client, fail_urls) + + def test_logged_in_no_groups_unshared(self): + """ Check correct status code for all requests + + Assumption: User logged in and has no groups and data is not shared + + Returns: + + """ + client = Client() + client.login(username=self.superuser.username, password=self.superuser_pw) + self.superuser.groups.set([]) + # Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state + self.intervention.share_with_list([]) + + # Since the user has no groups, it does not matter that data is unshared. There SHOULD not be any difference + # to a user having shared access, since all important permissions are missing + success_urls = [ + ] + fail_urls = [ + self.new_url, + self.edit_url, + self.remove_url, + ] + + self.assert_url_success(client, success_urls) + self.assert_url_fail(client, fail_urls) + + def test_logged_in_default_group_shared(self): + """ Check correct status code for all requests + + Assumption: User logged in, is default group member and data is shared + --> Default group necessary since all base functionalities depend on this group membership + + Returns: + + """ + client = Client() + client.login(username=self.superuser.username, password=self.superuser_pw) + group = self.groups.get(name=DEFAULT_GROUP) + self.superuser.groups.set([group]) + # Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state + self.intervention.share_with_list([self.superuser]) + + success_urls = [ + self.new_url, + self.edit_url, + self.remove_url, + ] + self.assert_url_success(client, success_urls) + + def test_logged_in_default_group_unshared(self): + """ Check correct status code for all requests + + Assumption: User logged in, is default group member and data is NOT shared + --> Default group necessary since all base functionalities depend on this group membership + + Returns: + + """ + client = Client() + client.login(username=self.superuser.username, password=self.superuser_pw) + group = self.groups.get(name=DEFAULT_GROUP) + self.superuser.groups.set([group]) + # Sharing is inherited by base intervention for compensation. Therefore configure the interventions share state + self.intervention.share_with_list([]) + + success_urls = [ + ] + fail_urls = [ + self.new_url, + self.edit_url, + self.remove_url, + ] + self.assert_url_fail(client, fail_urls) + self.assert_url_success(client, success_urls) diff --git a/compensation/tests/payment/test_workflow.py b/compensation/tests/payment/test_workflow.py new file mode 100644 index 00000000..09ff0e69 --- /dev/null +++ b/compensation/tests/payment/test_workflow.py @@ -0,0 +1,125 @@ +""" +Author: Michel Peltriaux +Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany +Contact: michel.peltriaux@sgdnord.rlp.de +Created on: 09.02.22 + +""" +from django.core.exceptions import ObjectDoesNotExist +from django.urls import reverse + +from compensation.models import Payment +from konova.tests.test_views import BaseWorkflowTestCase +from user.models import UserAction + + +class PaymentWorkflowTestCase(BaseWorkflowTestCase): + @classmethod + def setUpTestData(cls): + super().setUpTestData() + + # Give the user shared access to the dummy intervention + cls.intervention.share_with(cls.superuser) + + cls.payment = Payment.objects.get_or_create( + intervention=cls.intervention, + amount=1, + due_on="2020-01-01", + comment="Testcomment" + )[0] + + def test_new(self): + """ Test the creation of a payment + + Returns: + + """ + # Prepare url and form data to be posted + new_url = reverse("compensation:pay:new", args=(self.intervention.id,)) + test_amount = 12345 + test_due_on = "1970-01-01" + test_comment = self.create_dummy_string() + post_data = { + "amount": test_amount, + "due": test_due_on, + "comment": test_comment, + } + pre_creation_intervention_log_count = self.intervention.log.count() + num_payments = self.intervention.payments.count() + + self.client_user.post(new_url, post_data) + + self.intervention.refresh_from_db() + + self.assertEqual(num_payments + 1, self.intervention.payments.count()) + new_payment = self.intervention.payments.get(amount=test_amount) + self.assertEqual(new_payment.amount, test_amount) + self.assertEqual(str(new_payment.due_on), test_due_on) + self.assertEqual(new_payment.comment, test_comment) + + # Expect logs to be set + self.assertEqual(pre_creation_intervention_log_count + 1, self.intervention.log.count()) + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + + def test_edit(self): + """ Test edit of a payment + + Returns: + + """ + # Prepare url and form data to be posted + new_url = reverse("compensation:pay:edit", args=(self.intervention.id, self.payment.id)) + test_amount = self.payment.amount * 2 + test_due_on = "1970-01-01" + test_comment = self.create_dummy_string() + post_data = { + "amount": test_amount, + "due": test_due_on, + "comment": test_comment, + } + pre_edit_intervention_log_count = self.intervention.log.count() + num_payments = self.intervention.payments.count() + + self.client_user.post(new_url, post_data) + + self.intervention.refresh_from_db() + self.payment.refresh_from_db() + + self.assertEqual(num_payments, self.intervention.payments.count()) + self.assertEqual(self.payment.amount, test_amount) + self.assertEqual(str(self.payment.due_on), test_due_on) + self.assertEqual(self.payment.comment, test_comment) + + # Expect logs to be set + self.assertEqual(pre_edit_intervention_log_count + 1, self.intervention.log.count()) + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + + def test_remove(self): + """ Test remove of a payment + + Returns: + + """ + # Prepare url and form data to be posted + new_url = reverse("compensation:pay:remove", args=(self.intervention.id, self.payment.id)) + post_data = { + "confirm": True, + } + pre_remove_intervention_log_count = self.intervention.log.count() + num_payments = self.intervention.payments.count() + + self.client_user.post(new_url, post_data) + + self.intervention.refresh_from_db() + try: + self.payment.refresh_from_db() + self.fail(msg="Payment still exists after delete") + except ObjectDoesNotExist: + pass + + self.assertEqual(num_payments - 1, self.intervention.payments.count()) + + # Expect logs to be set + self.assertEqual(pre_remove_intervention_log_count + 1, self.intervention.log.count()) + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + diff --git a/compensation/urls/payment.py b/compensation/urls/payment.py index a400c636..b51384dd 100644 --- a/compensation/urls/payment.py +++ b/compensation/urls/payment.py @@ -12,4 +12,5 @@ app_name = "pay" urlpatterns = [ path('/new', new_payment_view, name='new'), path('/remove/', payment_remove_view, name='remove'), + path('/edit/', payment_edit_view, name='edit'), ] diff --git a/compensation/views/payment.py b/compensation/views/payment.py index 7be9bee8..2be5455e 100644 --- a/compensation/views/payment.py +++ b/compensation/views/payment.py @@ -11,16 +11,17 @@ from django.contrib.auth.decorators import login_required from django.http import HttpRequest from django.shortcuts import get_object_or_404 -from compensation.forms.modalForms import NewPaymentForm, RemovePaymentModalForm +from compensation.forms.modalForms import NewPaymentForm, RemovePaymentModalForm, EditPaymentModalForm from compensation.models import Payment from intervention.models import Intervention -from konova.decorators import default_group_required +from konova.decorators import default_group_required, shared_access_required from konova.forms import RemoveModalForm -from konova.utils.message_templates import PAYMENT_ADDED, PAYMENT_REMOVED +from konova.utils.message_templates import PAYMENT_ADDED, PAYMENT_REMOVED, PAYMENT_EDITED @login_required @default_group_required +@shared_access_required(Intervention, "id") def new_payment_view(request: HttpRequest, id: str): """ Renders a modal view for adding new payments @@ -42,6 +43,7 @@ def new_payment_view(request: HttpRequest, id: str): @login_required @default_group_required +@shared_access_required(Intervention, "id") def payment_remove_view(request: HttpRequest, id: str, payment_id: str): """ Renders a modal view for removing payments @@ -62,3 +64,27 @@ def payment_remove_view(request: HttpRequest, id: str, payment_id: str): redirect_url=reverse("intervention:detail", args=(payment.intervention_id,)) + "#related_data" ) + +@login_required +@default_group_required +@shared_access_required(Intervention, "id") +def payment_edit_view(request: HttpRequest, id: str, payment_id: str): + """ Renders a modal view for editing payments + + Args: + request (HttpRequest): The incoming request + id (str): The intervention's id + payment_id (str): The payment's id + + Returns: + + """ + intervention = get_object_or_404(Intervention, id=id) + payment = get_object_or_404(Payment, id=payment_id) + form = EditPaymentModalForm(request.POST or None, instance=intervention, payment=payment, request=request) + return form.process_request( + request=request, + msg_success=PAYMENT_EDITED, + redirect_url=reverse("intervention:detail", args=(payment.intervention_id,)) + "#related_data" + ) + diff --git a/intervention/templates/intervention/detail/includes/payments.html b/intervention/templates/intervention/detail/includes/payments.html index 205755bc..4bf00cd8 100644 --- a/intervention/templates/intervention/detail/includes/payments.html +++ b/intervention/templates/intervention/detail/includes/payments.html @@ -54,9 +54,12 @@ {{ pay.comment }} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/konova/forms.py b/konova/forms.py index ca7ac0e1..c05eb422 100644 --- a/konova/forms.py +++ b/konova/forms.py @@ -87,7 +87,7 @@ class BaseForm(forms.Form): """ self.fields[field].widget.attrs["placeholder"] = val - def load_initial_data(self, form_data: dict, disabled_fields: list): + def load_initial_data(self, form_data: dict, disabled_fields: list = None): """ Initializes form data from instance Inserts instance data into form and disables form fields @@ -99,8 +99,9 @@ class BaseForm(forms.Form): return for k, v in form_data.items(): self.initialize_form_field(k, v) - for field in disabled_fields: - self.disable_form_field(field) + if disabled_fields: + for field in disabled_fields: + self.disable_form_field(field) def add_widget_html_class(self, field: str, cls: str): """ Adds a HTML class string to the widget of a field diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index 87c22af2..b7eb5253 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -46,6 +46,7 @@ DEADLINE_REMOVED = _("Deadline removed") # PAYMENTS PAYMENT_ADDED = _("Payment added") +PAYMENT_EDITED = _("Payment edited") PAYMENT_REMOVED = _("Payment removed") # REVOCATIONS diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 74f794dec1833275cf54dc769ab7dab41978bbd3..85b75fdda447f5409f7dac392a4a7ff964a0e7fe 100644 GIT binary patch delta 10501 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 delta 10423 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 diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index b97e8414..3feeb6db 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ # #: compensation/filters.py:122 compensation/forms/modalForms.py:35 #: compensation/forms/modalForms.py:46 compensation/forms/modalForms.py:62 -#: compensation/forms/modalForms.py:306 compensation/forms/modalForms.py:399 +#: compensation/forms/modalForms.py:329 compensation/forms/modalForms.py:422 #: intervention/forms/forms.py:54 intervention/forms/forms.py:156 #: intervention/forms/forms.py:168 intervention/forms/modalForms.py:124 #: intervention/forms/modalForms.py:137 intervention/forms/modalForms.py:150 @@ -18,15 +18,15 @@ #: konova/filters/mixins.py:270 konova/filters/mixins.py:315 #: konova/filters/mixins.py:353 konova/filters/mixins.py:354 #: konova/filters/mixins.py:385 konova/filters/mixins.py:386 -#: konova/forms.py:140 konova/forms.py:241 konova/forms.py:312 -#: konova/forms.py:356 konova/forms.py:366 konova/forms.py:379 -#: konova/forms.py:391 konova/forms.py:409 user/forms.py:42 +#: konova/forms.py:141 konova/forms.py:242 konova/forms.py:313 +#: konova/forms.py:357 konova/forms.py:367 konova/forms.py:380 +#: konova/forms.py:392 konova/forms.py:410 user/forms.py:42 #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-02-08 15:16+0100\n" +"POT-Creation-Date: 2022-02-09 09:50+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -75,7 +75,7 @@ msgstr "Bericht generieren" msgid "Select a timespan and the desired conservation office" msgstr "Wählen Sie die Zeitspanne und die gewünschte Eintragungsstelle" -#: analysis/forms.py:69 konova/forms.py:188 +#: analysis/forms.py:69 konova/forms.py:189 msgid "Continue" msgstr "Weiter" @@ -95,7 +95,7 @@ msgstr "" #: analysis/templates/analysis/reports/includes/eco_account/amount.html:3 #: analysis/templates/analysis/reports/includes/intervention/amount.html:3 #: analysis/templates/analysis/reports/includes/old_data/amount.html:3 -#: compensation/forms/modalForms.py:383 +#: compensation/forms/modalForms.py:406 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:34 #: intervention/templates/intervention/detail/includes/deductions.html:31 msgid "Amount" @@ -152,7 +152,7 @@ msgstr "Geprüft" #: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:9 #: analysis/templates/analysis/reports/includes/intervention/laws.html:20 #: analysis/templates/analysis/reports/includes/old_data/amount.html:18 -#: compensation/tables.py:46 compensation/tables.py:219 +#: compensation/tables.py:46 compensation/tables.py:222 #: compensation/templates/compensation/detail/compensation/view.html:77 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:31 #: compensation/templates/compensation/detail/eco_account/view.html:44 @@ -213,7 +213,7 @@ msgstr "Abbuchungen" #: analysis/templates/analysis/reports/includes/eco_account/deductions.html:9 #: analysis/templates/analysis/reports/includes/eco_account/deductions.html:11 -#: compensation/forms/modalForms.py:167 +#: compensation/forms/modalForms.py:190 #: compensation/templates/compensation/detail/compensation/includes/states-after.html:36 #: compensation/templates/compensation/detail/compensation/includes/states-before.html:36 #: compensation/templates/compensation/detail/eco_account/includes/states-after.html:36 @@ -239,7 +239,6 @@ msgstr "Kompensationsart" #: analysis/templates/analysis/reports/includes/intervention/compensated_by.html:15 #: analysis/templates/analysis/reports/includes/old_data/amount.html:29 -#: compensation/tables.py:90 #: compensation/templates/compensation/detail/compensation/view.html:19 #: konova/templates/konova/includes/quickstart/compensations.html:4 #: templates/navbars/navbar.html:28 @@ -284,8 +283,8 @@ msgid "Type" msgstr "Typ" #: analysis/templates/analysis/reports/includes/old_data/amount.html:24 -#: intervention/forms/modalForms.py:322 intervention/forms/modalForms.py:329 -#: intervention/tables.py:88 +#: compensation/tables.py:89 intervention/forms/modalForms.py:322 +#: intervention/forms/modalForms.py:329 intervention/tables.py:88 #: intervention/templates/intervention/detail/view.html:19 #: konova/templates/konova/includes/quickstart/interventions.html:4 #: templates/navbars/navbar.html:22 @@ -293,7 +292,7 @@ msgid "Intervention" msgstr "Eingriff" #: analysis/templates/analysis/reports/includes/old_data/amount.html:34 -#: compensation/tables.py:263 +#: compensation/tables.py:266 #: compensation/templates/compensation/detail/eco_account/view.html:19 #: intervention/forms/modalForms.py:295 intervention/forms/modalForms.py:302 #: konova/templates/konova/includes/quickstart/ecoaccounts.html:4 @@ -314,7 +313,7 @@ msgid "Show only unrecorded" msgstr "Nur unverzeichnete anzeigen" #: compensation/forms/forms.py:32 compensation/tables.py:25 -#: compensation/tables.py:194 ema/tables.py:29 intervention/forms/forms.py:28 +#: compensation/tables.py:197 ema/tables.py:29 intervention/forms/forms.py:28 #: intervention/tables.py:24 #: intervention/templates/intervention/detail/includes/compensations.html:30 msgid "Identifier" @@ -326,7 +325,7 @@ msgid "Generated automatically" msgstr "Automatisch generiert" #: compensation/forms/forms.py:44 compensation/tables.py:30 -#: compensation/tables.py:199 +#: compensation/tables.py:202 #: compensation/templates/compensation/detail/compensation/includes/documents.html:28 #: compensation/templates/compensation/detail/compensation/view.html:31 #: compensation/templates/compensation/detail/eco_account/includes/documents.html:28 @@ -341,7 +340,7 @@ msgstr "Automatisch generiert" #: intervention/templates/intervention/detail/includes/documents.html:28 #: intervention/templates/intervention/detail/view.html:31 #: intervention/templates/intervention/report/report.html:12 -#: konova/forms.py:355 +#: konova/forms.py:356 msgid "Title" msgstr "Bezeichnung" @@ -354,7 +353,7 @@ msgid "Compensation XY; Location ABC" msgstr "Kompensation XY; Flur ABC" #: compensation/forms/forms.py:57 compensation/forms/modalForms.py:61 -#: compensation/forms/modalForms.py:305 compensation/forms/modalForms.py:398 +#: compensation/forms/modalForms.py:328 compensation/forms/modalForms.py:421 #: compensation/templates/compensation/detail/compensation/includes/actions.html:35 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:34 #: compensation/templates/compensation/detail/compensation/includes/documents.html:31 @@ -368,11 +367,11 @@ msgstr "Kompensation XY; Flur ABC" #: intervention/templates/intervention/detail/includes/documents.html:31 #: intervention/templates/intervention/detail/includes/payments.html:34 #: intervention/templates/intervention/detail/includes/revocation.html:38 -#: konova/forms.py:390 konova/templates/konova/includes/comment_card.html:16 +#: konova/forms.py:391 konova/templates/konova/includes/comment_card.html:16 msgid "Comment" msgstr "Kommentar" -#: compensation/forms/forms.py:59 compensation/forms/modalForms.py:400 +#: compensation/forms/forms.py:59 compensation/forms/modalForms.py:423 #: intervention/forms/forms.py:182 msgid "Additional comment" msgstr "Zusätzlicher Kommentar" @@ -483,8 +482,8 @@ msgstr "Fällig am" msgid "Due on which date" msgstr "Zahlung wird an diesem Datum erwartet" -#: compensation/forms/modalForms.py:63 compensation/forms/modalForms.py:307 -#: intervention/forms/modalForms.py:151 konova/forms.py:392 +#: compensation/forms/modalForms.py:63 compensation/forms/modalForms.py:330 +#: intervention/forms/modalForms.py:151 konova/forms.py:393 msgid "Additional comment, maximum {} letters" msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" @@ -496,47 +495,47 @@ msgstr "Neue Ersatzzahlung zu Eingriff '{}' hinzufügen" msgid "If there is no date you can enter, please explain why." msgstr "Falls Sie kein Datum angeben können, erklären Sie bitte weshalb." -#: compensation/forms/modalForms.py:131 compensation/forms/modalForms.py:143 +#: compensation/forms/modalForms.py:154 compensation/forms/modalForms.py:166 msgid "Biotope Type" msgstr "Biotoptyp" -#: compensation/forms/modalForms.py:134 +#: compensation/forms/modalForms.py:157 msgid "Select the biotope type" msgstr "Biotoptyp wählen" -#: compensation/forms/modalForms.py:148 compensation/forms/modalForms.py:160 +#: compensation/forms/modalForms.py:171 compensation/forms/modalForms.py:183 msgid "Biotope additional type" msgstr "Zusatzbezeichnung" -#: compensation/forms/modalForms.py:151 +#: compensation/forms/modalForms.py:174 msgid "Select an additional biotope type" msgstr "Zusatzbezeichnung wählen" -#: compensation/forms/modalForms.py:170 intervention/forms/modalForms.py:313 +#: compensation/forms/modalForms.py:193 intervention/forms/modalForms.py:313 msgid "in m²" msgstr "" -#: compensation/forms/modalForms.py:181 +#: compensation/forms/modalForms.py:204 msgid "New state" msgstr "Neuer Zustand" -#: compensation/forms/modalForms.py:182 +#: compensation/forms/modalForms.py:205 msgid "Insert data for the new state" msgstr "Geben Sie die Daten des neuen Zustandes ein" -#: compensation/forms/modalForms.py:189 konova/forms.py:190 +#: compensation/forms/modalForms.py:212 konova/forms.py:191 msgid "Object removed" msgstr "Objekt entfernt" -#: compensation/forms/modalForms.py:277 +#: compensation/forms/modalForms.py:300 msgid "Deadline Type" msgstr "Fristart" -#: compensation/forms/modalForms.py:280 +#: compensation/forms/modalForms.py:303 msgid "Select the deadline type" msgstr "Fristart wählen" -#: compensation/forms/modalForms.py:289 +#: compensation/forms/modalForms.py:312 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:31 #: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31 #: ema/templates/ema/detail/includes/deadlines.html:31 @@ -544,27 +543,27 @@ msgstr "Fristart wählen" msgid "Date" msgstr "Datum" -#: compensation/forms/modalForms.py:292 +#: compensation/forms/modalForms.py:315 msgid "Select date" msgstr "Datum wählen" -#: compensation/forms/modalForms.py:319 +#: compensation/forms/modalForms.py:342 msgid "New deadline" msgstr "Neue Frist" -#: compensation/forms/modalForms.py:320 +#: compensation/forms/modalForms.py:343 msgid "Insert data for the new deadline" msgstr "Geben Sie die Daten der neuen Frist ein" -#: compensation/forms/modalForms.py:337 +#: compensation/forms/modalForms.py:360 msgid "Action Type" msgstr "Maßnahmentyp" -#: compensation/forms/modalForms.py:340 +#: compensation/forms/modalForms.py:363 msgid "Select the action type" msgstr "Maßnahmentyp wählen" -#: compensation/forms/modalForms.py:349 +#: compensation/forms/modalForms.py:372 #: compensation/templates/compensation/detail/compensation/includes/actions.html:40 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:39 #: compensation/templates/compensation/detail/compensation/includes/documents.html:36 @@ -590,31 +589,31 @@ msgstr "Maßnahmentyp wählen" msgid "Action" msgstr "Aktionen" -#: compensation/forms/modalForms.py:354 compensation/forms/modalForms.py:366 +#: compensation/forms/modalForms.py:377 compensation/forms/modalForms.py:389 msgid "Action Type detail" msgstr "Zusatzmerkmal" -#: compensation/forms/modalForms.py:357 +#: compensation/forms/modalForms.py:380 msgid "Select the action type detail" msgstr "Zusatzmerkmal wählen" -#: compensation/forms/modalForms.py:371 +#: compensation/forms/modalForms.py:394 msgid "Unit" msgstr "Einheit" -#: compensation/forms/modalForms.py:374 +#: compensation/forms/modalForms.py:397 msgid "Select the unit" msgstr "Einheit wählen" -#: compensation/forms/modalForms.py:386 +#: compensation/forms/modalForms.py:409 msgid "Insert the amount" msgstr "Menge eingeben" -#: compensation/forms/modalForms.py:411 +#: compensation/forms/modalForms.py:434 msgid "New action" msgstr "Neue Maßnahme" -#: compensation/forms/modalForms.py:412 +#: compensation/forms/modalForms.py:435 msgid "Insert data for the new action" msgstr "Geben Sie die Daten der neuen Maßnahme ein" @@ -657,35 +656,35 @@ msgstr "" "Es wurde bereits mehr Fläche abgebucht, als Sie nun als abbuchbar einstellen " "wollen. Kontaktieren Sie die für die Abbuchungen verantwortlichen Nutzer!" -#: compensation/tables.py:35 compensation/tables.py:204 ema/tables.py:39 +#: compensation/tables.py:35 compensation/tables.py:207 ema/tables.py:39 #: intervention/tables.py:34 konova/filters/mixins.py:98 msgid "Parcel gmrkng" msgstr "Gemarkung" -#: compensation/tables.py:52 compensation/tables.py:225 ema/tables.py:50 +#: compensation/tables.py:52 compensation/tables.py:228 ema/tables.py:50 #: intervention/tables.py:51 msgid "Editable" msgstr "Freigegeben" -#: compensation/tables.py:58 compensation/tables.py:231 ema/tables.py:56 +#: compensation/tables.py:58 compensation/tables.py:234 ema/tables.py:56 #: intervention/tables.py:57 msgid "Last edit" msgstr "Zuletzt bearbeitet" -#: compensation/tables.py:90 compensation/tables.py:263 ema/tables.py:89 +#: compensation/tables.py:89 compensation/tables.py:266 ema/tables.py:89 #: intervention/tables.py:88 msgid "Open {}" msgstr "Öffne {}" -#: compensation/tables.py:111 intervention/tables.py:111 +#: compensation/tables.py:114 intervention/tables.py:111 msgid "Not checked yet" msgstr "Noch nicht geprüft" -#: compensation/tables.py:116 intervention/tables.py:116 +#: compensation/tables.py:119 intervention/tables.py:116 msgid "Checked on {} by {}" msgstr "Am {} von {} geprüft worden" -#: compensation/tables.py:157 +#: compensation/tables.py:160 #: compensation/templates/compensation/detail/compensation/view.html:80 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:58 #: compensation/templates/compensation/detail/eco_account/view.html:47 @@ -695,32 +694,32 @@ msgstr "Am {} von {} geprüft worden" msgid "Not recorded yet" msgstr "Noch nicht verzeichnet" -#: compensation/tables.py:162 compensation/tables.py:323 ema/tables.py:136 +#: compensation/tables.py:165 compensation/tables.py:326 ema/tables.py:136 #: intervention/tables.py:162 msgid "Recorded on {} by {}" msgstr "Am {} von {} verzeichnet worden" -#: compensation/tables.py:186 compensation/tables.py:345 ema/tables.py:159 +#: compensation/tables.py:189 compensation/tables.py:348 ema/tables.py:159 #: intervention/tables.py:185 msgid "Full access granted" msgstr "Für Sie freigegeben - Datensatz kann bearbeitet werden" -#: compensation/tables.py:186 compensation/tables.py:345 ema/tables.py:159 +#: compensation/tables.py:189 compensation/tables.py:348 ema/tables.py:159 #: intervention/tables.py:185 msgid "Access not granted" msgstr "Nicht freigegeben - Datensatz nur lesbar" -#: compensation/tables.py:209 +#: compensation/tables.py:212 #: compensation/templates/compensation/detail/eco_account/view.html:35 #: konova/templates/konova/widgets/progressbar.html:3 msgid "Available" msgstr "Verfügbar" -#: compensation/tables.py:240 +#: compensation/tables.py:243 msgid "Eco Accounts" msgstr "Ökokonten" -#: compensation/tables.py:318 +#: compensation/tables.py:321 msgid "Not recorded yet. Can not be used for deductions, yet." msgstr "" "Noch nicht verzeichnet. Kann noch nicht für Abbuchungen genutzt werden." @@ -822,7 +821,7 @@ msgstr "Dokumente" #: compensation/templates/compensation/detail/eco_account/includes/documents.html:14 #: ema/templates/ema/detail/includes/documents.html:14 #: intervention/templates/intervention/detail/includes/documents.html:14 -#: konova/forms.py:408 +#: konova/forms.py:409 msgid "Add new document" msgstr "Neues Dokument hinzufügen" @@ -1086,17 +1085,17 @@ msgid "Compensation {} edited" msgstr "Kompensation {} bearbeitet" #: compensation/views/compensation.py:159 compensation/views/eco_account.py:161 -#: ema/views.py:230 intervention/views.py:313 +#: ema/views.py:230 intervention/views.py:305 msgid "Edit {}" msgstr "Bearbeite {}" #: compensation/views/compensation.py:238 compensation/views/eco_account.py:317 -#: ema/views.py:191 intervention/views.py:491 +#: ema/views.py:191 intervention/views.py:483 msgid "Log" msgstr "Log" #: compensation/views/compensation.py:487 compensation/views/eco_account.py:590 -#: ema/views.py:477 intervention/views.py:609 +#: ema/views.py:477 intervention/views.py:601 msgid "Report {}" msgstr "Bericht {}" @@ -1117,32 +1116,32 @@ msgid "Eco-account removed" msgstr "Ökokonto entfernt" #: compensation/views/eco_account.py:338 ema/views.py:272 -#: intervention/views.py:562 +#: intervention/views.py:554 msgid "{} unrecorded" msgstr "{} entzeichnet" #: compensation/views/eco_account.py:338 ema/views.py:272 -#: intervention/views.py:562 +#: intervention/views.py:554 msgid "{} recorded" msgstr "{} verzeichnet" #: compensation/views/eco_account.py:663 ema/views.py:543 -#: intervention/views.py:388 +#: intervention/views.py:380 msgid "{} has already been shared with you" msgstr "{} wurde bereits für Sie freigegeben" #: compensation/views/eco_account.py:668 ema/views.py:548 -#: intervention/views.py:393 +#: intervention/views.py:385 msgid "{} has been shared with you" msgstr "{} ist nun für Sie freigegeben" #: compensation/views/eco_account.py:675 ema/views.py:555 -#: intervention/views.py:400 +#: intervention/views.py:392 msgid "Share link invalid" msgstr "Freigabelink ungültig" #: compensation/views/eco_account.py:698 ema/views.py:578 -#: intervention/views.py:423 +#: intervention/views.py:415 msgid "Share settings updated" msgstr "Freigabe Einstellungen aktualisiert" @@ -1314,7 +1313,7 @@ msgstr "Kompensationen und Zahlungen geprüft" msgid "Run check" msgstr "Prüfung vornehmen" -#: intervention/forms/modalForms.py:213 konova/forms.py:474 +#: intervention/forms/modalForms.py:213 konova/forms.py:475 msgid "" "I, {} {}, confirm that all necessary control steps have been performed by " "myself." @@ -1389,6 +1388,10 @@ msgid "Amount" msgstr "Betrag" #: intervention/templates/intervention/detail/includes/payments.html:59 +msgid "Edit payment" +msgstr "Zahlung bearbeitet" + +#: intervention/templates/intervention/detail/includes/payments.html:62 msgid "Remove payment" msgstr "Zahlung entfernen" @@ -1449,23 +1452,19 @@ msgstr "Eingriffe - Übersicht" msgid "Intervention {} added" msgstr "Eingriff {} hinzugefügt" -#: intervention/views.py:252 -msgid "This intervention has {} revocations" -msgstr "Dem Eingriff liegen {} Widersprüche vor" - -#: intervention/views.py:301 +#: intervention/views.py:293 msgid "Intervention {} edited" msgstr "Eingriff {} bearbeitet" -#: intervention/views.py:337 +#: intervention/views.py:329 msgid "{} removed" msgstr "{} entfernt" -#: intervention/views.py:444 +#: intervention/views.py:436 msgid "Check performed" msgstr "Prüfung durchgeführt" -#: intervention/views.py:567 +#: intervention/views.py:559 msgid "There are errors on this intervention:" msgstr "Es liegen Fehler in diesem Eingriff vor:" @@ -1557,73 +1556,73 @@ msgstr "Speichern" msgid "Not editable" msgstr "Nicht editierbar" -#: konova/forms.py:139 konova/forms.py:311 +#: konova/forms.py:140 konova/forms.py:312 msgid "Confirm" msgstr "Bestätige" -#: konova/forms.py:151 konova/forms.py:320 +#: konova/forms.py:152 konova/forms.py:321 msgid "Remove" msgstr "Löschen" -#: konova/forms.py:153 +#: konova/forms.py:154 msgid "You are about to remove {} {}" msgstr "Sie sind dabei {} {} zu löschen" -#: konova/forms.py:240 konova/utils/quality.py:44 konova/utils/quality.py:46 +#: konova/forms.py:241 konova/utils/quality.py:44 konova/utils/quality.py:46 #: templates/form/collapsable/form.html:45 msgid "Geometry" msgstr "Geometrie" -#: konova/forms.py:321 +#: konova/forms.py:322 msgid "Are you sure?" msgstr "Sind Sie sicher?" -#: konova/forms.py:365 +#: konova/forms.py:366 msgid "Created on" msgstr "Erstellt" -#: konova/forms.py:367 +#: konova/forms.py:368 msgid "When has this file been created? Important for photos." msgstr "Wann wurde diese Datei erstellt oder das Foto aufgenommen?" -#: konova/forms.py:378 +#: konova/forms.py:379 #: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231 msgid "File" msgstr "Datei" -#: konova/forms.py:380 +#: konova/forms.py:381 msgid "Allowed formats: pdf, jpg, png. Max size 15 MB." msgstr "Formate: pdf, jpg, png. Maximal 15 MB." -#: konova/forms.py:426 +#: konova/forms.py:427 msgid "Unsupported file type" msgstr "Dateiformat nicht unterstützt" -#: konova/forms.py:433 +#: konova/forms.py:434 msgid "File too large" msgstr "Datei zu groß" -#: konova/forms.py:442 +#: konova/forms.py:443 msgid "Added document" msgstr "Dokument hinzugefügt" -#: konova/forms.py:465 +#: konova/forms.py:466 msgid "Confirm record" msgstr "Verzeichnen bestätigen" -#: konova/forms.py:473 +#: konova/forms.py:474 msgid "Record data" msgstr "Daten verzeichnen" -#: konova/forms.py:480 +#: konova/forms.py:481 msgid "Confirm unrecord" msgstr "Entzeichnen bestätigen" -#: konova/forms.py:481 +#: konova/forms.py:482 msgid "Unrecord data" msgstr "Daten entzeichnen" -#: konova/forms.py:482 +#: konova/forms.py:483 msgid "I, {} {}, confirm that this data must be unrecorded." msgstr "" "Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen." @@ -1857,37 +1856,45 @@ msgid "Payment added" msgstr "Zahlung hinzugefügt" #: konova/utils/message_templates.py:49 +msgid "Payment edited" +msgstr "Zahlung bearbeitet" + +#: konova/utils/message_templates.py:50 msgid "Payment removed" msgstr "Zahlung gelöscht" -#: konova/utils/message_templates.py:52 +#: konova/utils/message_templates.py:53 msgid "Revocation added" msgstr "Widerspruch hinzugefügt" -#: konova/utils/message_templates.py:53 +#: konova/utils/message_templates.py:54 msgid "Revocation removed" msgstr "Widerspruch entfernt" -#: konova/utils/message_templates.py:56 +#: konova/utils/message_templates.py:57 msgid "Document '{}' deleted" msgstr "Dokument '{}' gelöscht" -#: konova/utils/message_templates.py:57 +#: konova/utils/message_templates.py:58 msgid "Document added" msgstr "Dokument hinzugefügt" -#: konova/utils/message_templates.py:60 +#: konova/utils/message_templates.py:61 msgid "Edited general data" msgstr "Allgemeine Daten bearbeitet" -#: konova/utils/message_templates.py:61 +#: konova/utils/message_templates.py:62 msgid "Added deadline" msgstr "Frist/Termin hinzugefügt" -#: konova/utils/message_templates.py:64 +#: konova/utils/message_templates.py:65 msgid "Geometry conflict detected with {}" msgstr "Geometriekonflikt mit folgenden Einträgen erkannt: {}" +#: konova/utils/message_templates.py:68 +msgid "This intervention has {} revocations" +msgstr "Dem Eingriff liegen {} Widersprüche vor" + #: konova/utils/messenger.py:70 msgid "{} checked" msgstr "{} geprüft" @@ -3942,9 +3949,6 @@ msgstr "" #~ msgid "No file given!" #~ msgstr "Keine Datei angegeben!" -#~ msgid "Added payment" -#~ msgstr "Zahlung hinzufügen" - #~ msgid "Added state" #~ msgstr "Zustand hinzugefügt" From d106977c34b753114b3cd7d6573d797488e68bcc Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 9 Feb 2022 14:49:56 +0100 Subject: [PATCH 18/31] #86 Edit deductions * adds support for editing deductions * adds tests * improves major base test logic --- api/tests/v1/share/test_api_sharing.py | 18 +- .../eco_account/includes/deductions.html | 7 +- compensation/tests/compensation/test_views.py | 41 ++-- .../tests/compensation/test_workflow.py | 19 +- compensation/tests/ecoaccount/test_views.py | 41 ++-- .../tests/ecoaccount/test_workflow.py | 77 +++++++- compensation/tests/payment/test_views.py | 12 +- compensation/tests/payment/test_workflow.py | 8 +- compensation/urls/eco_account.py | 3 +- compensation/views/eco_account.py | 34 +++- ema/tests/test_views.py | 55 +++--- intervention/forms/modalForms.py | 73 +++++++- .../detail/includes/deductions.html | 7 +- intervention/tests/test_views.py | 42 +++-- intervention/urls.py | 3 +- intervention/views.py | 32 +++- konova/tests/test_views.py | 175 ++++++++---------- konova/utils/message_templates.py | 5 + locale/de/LC_MESSAGES/django.mo | Bin 36759 -> 37109 bytes locale/de/LC_MESSAGES/django.po | 162 +++++++++------- 20 files changed, 524 insertions(+), 290 deletions(-) diff --git a/api/tests/v1/share/test_api_sharing.py b/api/tests/v1/share/test_api_sharing.py index 9e7c9eec..1da0ce1b 100644 --- a/api/tests/v1/share/test_api_sharing.py +++ b/api/tests/v1/share/test_api_sharing.py @@ -12,15 +12,17 @@ class BaseAPIV1TestCase(BaseTestCase): def setUpTestData(cls): super().setUpTestData() - cls.superuser.get_API_token() - cls.superuser.api_token.is_active = True - cls.superuser.api_token.save() - default_group = cls.groups.get(name=DEFAULT_GROUP) - cls.superuser.groups.add(default_group) + def setUp(self) -> None: + super().setUp() + self.superuser.get_API_token() + self.superuser.api_token.is_active = True + self.superuser.api_token.save() + default_group = self.groups.get(name=DEFAULT_GROUP) + self.superuser.groups.add(default_group) - cls.header_data = { - "HTTP_ksptoken": cls.superuser.api_token.token, - "HTTP_kspuser": cls.superuser.username, + self.header_data = { + "HTTP_ksptoken": self.superuser.api_token.token, + "HTTP_kspuser": self.superuser.username, } diff --git a/compensation/templates/compensation/detail/eco_account/includes/deductions.html b/compensation/templates/compensation/detail/eco_account/includes/deductions.html index e72ab2ab..76c116b2 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/deductions.html +++ b/compensation/templates/compensation/detail/eco_account/includes/deductions.html @@ -60,9 +60,12 @@ {{ deduction.surface|floatformat:2|intcomma }} m² {{ deduction.created.timestamp|default_if_none:""|naturalday}} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/tests/compensation/test_views.py b/compensation/tests/compensation/test_views.py index 465a1026..5844e264 100644 --- a/compensation/tests/compensation/test_views.py +++ b/compensation/tests/compensation/test_views.py @@ -21,29 +21,32 @@ class CompensationViewTestCase(BaseViewTestCase): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() - state = cls.create_dummy_states() - cls.compensation.before_states.set([state]) - cls.compensation.after_states.set([state]) + + def setUp(self) -> None: + super().setUp() + state = self.create_dummy_states() + self.compensation.before_states.set([state]) + self.compensation.after_states.set([state]) - action = cls.create_dummy_action() - cls.compensation.actions.set([action]) + action = self.create_dummy_action() + self.compensation.actions.set([action]) # Prepare urls - cls.index_url = reverse("compensation:index", args=()) - cls.new_url = reverse("compensation:new", args=(cls.intervention.id,)) - cls.new_id_url = reverse("compensation:new-id", args=()) - cls.detail_url = reverse("compensation:detail", args=(cls.compensation.id,)) - cls.log_url = reverse("compensation:log", args=(cls.compensation.id,)) - cls.edit_url = reverse("compensation:edit", args=(cls.compensation.id,)) - cls.remove_url = reverse("compensation:remove", args=(cls.compensation.id,)) - cls.report_url = reverse("compensation:report", args=(cls.compensation.id,)) - cls.state_new_url = reverse("compensation:new-state", args=(cls.compensation.id,)) - cls.action_new_url = reverse("compensation:new-action", args=(cls.compensation.id,)) - cls.deadline_new_url = reverse("compensation:new-deadline", args=(cls.compensation.id,)) - cls.new_doc_url = reverse("compensation:new-doc", args=(cls.compensation.id,)) + self.index_url = reverse("compensation:index", args=()) + self.new_url = reverse("compensation:new", args=(self.intervention.id,)) + self.new_id_url = reverse("compensation:new-id", args=()) + self.detail_url = reverse("compensation:detail", args=(self.compensation.id,)) + self.log_url = reverse("compensation:log", args=(self.compensation.id,)) + self.edit_url = reverse("compensation:edit", args=(self.compensation.id,)) + self.remove_url = reverse("compensation:remove", args=(self.compensation.id,)) + self.report_url = reverse("compensation:report", args=(self.compensation.id,)) + self.state_new_url = reverse("compensation:new-state", args=(self.compensation.id,)) + self.action_new_url = reverse("compensation:new-action", args=(self.compensation.id,)) + self.deadline_new_url = reverse("compensation:new-deadline", args=(self.compensation.id,)) + self.new_doc_url = reverse("compensation:new-doc", args=(self.compensation.id,)) - cls.state_remove_url = reverse("compensation:state-remove", args=(cls.compensation.id, cls.comp_state.id,)) - cls.action_remove_url = reverse("compensation:action-remove", args=(cls.compensation.id, cls.comp_action.id,)) + self.state_remove_url = reverse("compensation:state-remove", args=(self.compensation.id, self.comp_state.id,)) + self.action_remove_url = reverse("compensation:action-remove", args=(self.compensation.id, self.comp_action.id,)) def test_anonymous_user(self): """ Check correct status code for all requests diff --git a/compensation/tests/compensation/test_workflow.py b/compensation/tests/compensation/test_workflow.py index d1f13782..7b73be89 100644 --- a/compensation/tests/compensation/test_workflow.py +++ b/compensation/tests/compensation/test_workflow.py @@ -21,17 +21,18 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): def setUpTestData(cls): super().setUpTestData() - # Give the user shared access to the dummy intervention -> inherits the access to the compensation - cls.intervention.share_with(cls.superuser) - - # Make sure the intervention itself would be fine with valid data - cls.intervention = cls.fill_out_intervention(cls.intervention) - - # Make sure the compensation is linked to the intervention - cls.intervention.compensations.set([cls.compensation]) - def setUp(self) -> None: super().setUp() + + # Give the user shared access to the dummy intervention -> inherits the access to the compensation + self.intervention.share_with(self.superuser) + + # Make sure the intervention itself would be fine with valid data + self.intervention = self.fill_out_intervention(self.intervention) + + # Make sure the compensation is linked to the intervention + self.intervention.compensations.set([self.compensation]) + # Delete all existing compensations, which might be created by tests Compensation.objects.all().delete() diff --git a/compensation/tests/ecoaccount/test_views.py b/compensation/tests/ecoaccount/test_views.py index c4e742fe..670f4f0d 100644 --- a/compensation/tests/ecoaccount/test_views.py +++ b/compensation/tests/ecoaccount/test_views.py @@ -25,28 +25,31 @@ class EcoAccountViewTestCase(CompensationViewTestCase): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() - state = cls.create_dummy_states() - cls.eco_account.before_states.set([state]) - cls.eco_account.after_states.set([state]) + + def setUp(self) -> None: + super().setUp() + state = self.create_dummy_states() + self.eco_account.before_states.set([state]) + self.eco_account.after_states.set([state]) - action = cls.create_dummy_action() - cls.eco_account.actions.set([action]) + action = self.create_dummy_action() + self.eco_account.actions.set([action]) # Prepare urls - cls.index_url = reverse("compensation:acc:index", args=()) - cls.new_url = reverse("compensation:acc:new", args=()) - cls.new_id_url = reverse("compensation:acc:new-id", args=()) - cls.detail_url = reverse("compensation:acc:detail", args=(cls.eco_account.id,)) - cls.log_url = reverse("compensation:acc:log", args=(cls.eco_account.id,)) - cls.edit_url = reverse("compensation:acc:edit", args=(cls.eco_account.id,)) - cls.remove_url = reverse("compensation:acc:remove", args=(cls.eco_account.id,)) - cls.report_url = reverse("compensation:acc:report", args=(cls.eco_account.id,)) - cls.state_new_url = reverse("compensation:acc:new-state", args=(cls.eco_account.id,)) - cls.action_new_url = reverse("compensation:acc:new-action", args=(cls.eco_account.id,)) - cls.deadline_new_url = reverse("compensation:acc:new-deadline", args=(cls.eco_account.id,)) - cls.new_doc_url = reverse("compensation:acc:new-doc", args=(cls.eco_account.id,)) - cls.state_remove_url = reverse("compensation:acc:state-remove", args=(cls.eco_account.id, cls.comp_state.id,)) - cls.action_remove_url = reverse("compensation:acc:action-remove", args=(cls.eco_account.id, cls.comp_action.id,)) + self.index_url = reverse("compensation:acc:index", args=()) + self.new_url = reverse("compensation:acc:new", args=()) + self.new_id_url = reverse("compensation:acc:new-id", args=()) + self.detail_url = reverse("compensation:acc:detail", args=(self.eco_account.id,)) + self.log_url = reverse("compensation:acc:log", args=(self.eco_account.id,)) + self.edit_url = reverse("compensation:acc:edit", args=(self.eco_account.id,)) + self.remove_url = reverse("compensation:acc:remove", args=(self.eco_account.id,)) + self.report_url = reverse("compensation:acc:report", args=(self.eco_account.id,)) + self.state_new_url = reverse("compensation:acc:new-state", args=(self.eco_account.id,)) + self.action_new_url = reverse("compensation:acc:new-action", args=(self.eco_account.id,)) + self.deadline_new_url = reverse("compensation:acc:new-deadline", args=(self.eco_account.id,)) + self.new_doc_url = reverse("compensation:acc:new-doc", args=(self.eco_account.id,)) + self.state_remove_url = reverse("compensation:acc:state-remove", args=(self.eco_account.id, self.comp_state.id,)) + self.action_remove_url = reverse("compensation:acc:action-remove", args=(self.eco_account.id, self.comp_action.id,)) def test_logged_in_no_groups_shared(self): """ Check correct status code for all requests diff --git a/compensation/tests/ecoaccount/test_workflow.py b/compensation/tests/ecoaccount/test_workflow.py index f394ec7c..1cdb0308 100644 --- a/compensation/tests/ecoaccount/test_workflow.py +++ b/compensation/tests/ecoaccount/test_workflow.py @@ -11,7 +11,7 @@ from django.contrib.gis.geos import MultiPolygon from django.core.exceptions import ObjectDoesNotExist from django.urls import reverse -from compensation.models import EcoAccount +from compensation.models import EcoAccount, EcoAccountDeduction from konova.settings import ETS_GROUP, DEFAULT_GROUP from konova.tests.test_views import BaseWorkflowTestCase from user.models import UserAction @@ -168,7 +168,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): self.assertIn(recorded, self.eco_account.log.all()) self.assertEqual(pre_record_log_count + 1, self.eco_account.log.count()) - def test_deductability(self): + def test_new_deduction(self): """ This tests the deductability of an eco account. @@ -187,7 +187,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): test_surface = 10.00 post_data = { "surface": test_surface, - "account": self.id, + "account": self.eco_account.id, "intervention": self.intervention.id, } # Perform request --> expect to fail @@ -228,4 +228,75 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): self.assertEqual(pre_deduction_int_log_count + 1, self.intervention.log.count()) self.assertTrue(self.intervention.log.first().action == UserAction.EDITED) + def test_edit_deduction(self): + test_surface = self.eco_account.get_available_rest()[0] + self.eco_account.set_recorded(self.superuser) + self.eco_account.refresh_from_db() + deduction = EcoAccountDeduction.objects.create( + intervention=self.intervention, + account=self.eco_account, + surface=0 + ) + self.assertEqual(1, self.intervention.deductions.count()) + self.assertEqual(1, self.eco_account.deductions.count()) + + # Prepare url and form data to be posted + new_url = reverse("compensation:acc:edit-deduction", args=(self.eco_account.id, deduction.id)) + post_data = { + "intervention": deduction.intervention.id, + "account": deduction.account.id, + "surface": test_surface, + } + pre_edit_intervention_log_count = self.intervention.log.count() + pre_edit_account_log_count = self.eco_account.log.count() + num_deductions_intervention = self.intervention.deductions.count() + num_deductions_account = self.eco_account.deductions.count() + + self.client_user.post(new_url, post_data) + + self.intervention.refresh_from_db() + self.eco_account.refresh_from_db() + deduction.refresh_from_db() + + self.assertEqual(num_deductions_intervention, self.intervention.deductions.count()) + self.assertEqual(num_deductions_account, self.eco_account.deductions.count()) + self.assertEqual(deduction.surface, test_surface) + + # Expect logs to be set + self.assertEqual(pre_edit_intervention_log_count + 1, self.intervention.log.count()) + self.assertEqual(pre_edit_account_log_count + 1, self.eco_account.log.count()) + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + self.assertEqual(self.eco_account.log.first().action, UserAction.EDITED) + + def test_remove_deduction(self): + intervention = self.deduction.intervention + account = self.deduction.account + + # Prepare url and form data to be posted + new_url = reverse("compensation:acc:remove-deduction", args=(account.id, self.deduction.id)) + post_data = { + "confirm": True, + } + + intervention.share_with(self.superuser) + account.share_with(self.superuser) + + pre_edit_intervention_log_count = intervention.log.count() + pre_edit_account_log_count = account.log.count() + num_deductions_intervention = intervention.deductions.count() + num_deductions_account = account.deductions.count() + + self.client_user.post(new_url, post_data) + + intervention.refresh_from_db() + account.refresh_from_db() + + self.assertEqual(num_deductions_intervention - 1, intervention.deductions.count()) + self.assertEqual(num_deductions_account - 1, account.deductions.count()) + + # Expect logs to be set + self.assertEqual(pre_edit_intervention_log_count + 1, intervention.log.count()) + self.assertEqual(pre_edit_account_log_count + 1, account.log.count()) + self.assertEqual(intervention.log.first().action, UserAction.EDITED) + self.assertEqual(account.log.first().action, UserAction.EDITED) diff --git a/compensation/tests/payment/test_views.py b/compensation/tests/payment/test_views.py index 69130e86..b1eca5ae 100644 --- a/compensation/tests/payment/test_views.py +++ b/compensation/tests/payment/test_views.py @@ -19,16 +19,18 @@ class PaymentViewTestCase(BaseViewTestCase): def setUpTestData(cls) -> None: super().setUpTestData() - cls.payment = Payment.objects.get_or_create( - intervention=cls.intervention, + def setUp(self) -> None: + super().setUp() + self.payment = Payment.objects.get_or_create( + intervention=self.intervention, amount=1, due_on="2020-01-01", comment="Testcomment" )[0] - cls.new_url = reverse("compensation:pay:new", args=(cls.intervention.id,)) - cls.edit_url = reverse("compensation:pay:edit", args=(cls.intervention.id, cls.payment.id)) - cls.remove_url = reverse("compensation:pay:remove", args=(cls.intervention.id, cls.payment.id)) + self.new_url = reverse("compensation:pay:new", args=(self.intervention.id,)) + self.edit_url = reverse("compensation:pay:edit", args=(self.intervention.id, self.payment.id)) + self.remove_url = reverse("compensation:pay:remove", args=(self.intervention.id, self.payment.id)) def test_anonymous_user(self): """ Check correct status code for all requests diff --git a/compensation/tests/payment/test_workflow.py b/compensation/tests/payment/test_workflow.py index 09ff0e69..790fb619 100644 --- a/compensation/tests/payment/test_workflow.py +++ b/compensation/tests/payment/test_workflow.py @@ -18,11 +18,13 @@ class PaymentWorkflowTestCase(BaseWorkflowTestCase): def setUpTestData(cls): super().setUpTestData() + def setUp(self) -> None: + super().setUp() # Give the user shared access to the dummy intervention - cls.intervention.share_with(cls.superuser) + self.intervention.share_with(self.superuser) - cls.payment = Payment.objects.get_or_create( - intervention=cls.intervention, + self.payment = Payment.objects.get_or_create( + intervention=self.intervention, amount=1, due_on="2020-01-01", comment="Testcomment" diff --git a/compensation/urls/eco_account.py b/compensation/urls/eco_account.py index 7deb48f7..0c3a0293 100644 --- a/compensation/urls/eco_account.py +++ b/compensation/urls/eco_account.py @@ -34,7 +34,8 @@ urlpatterns = [ path('document//remove/', remove_document_view, name='remove-doc'), # Eco-account deductions - path('/remove/', deduction_remove_view, name='remove-deduction'), + path('/deduction//remove', deduction_remove_view, name='remove-deduction'), + path('/deduction//edit', deduction_edit_view, name='edit-deduction'), path('/deduct/new', new_deduction_view, name='new-deduction'), ] \ No newline at end of file diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index 7d1e560f..08e01a61 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -19,7 +19,8 @@ from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewEcoAccountDocumentForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction from compensation.tables import EcoAccountTable -from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm +from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm, \ + EditEcoAccountDeductionModalForm from konova.contexts import BaseContext from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \ shared_access_required @@ -31,7 +32,8 @@ from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \ - COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED + COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, \ + DEDUCTION_EDITED from konova.utils.user_checks import in_group @@ -294,6 +296,34 @@ def deduction_remove_view(request: HttpRequest, id: str, deduction_id: str): ) +@login_required +@default_group_required +@shared_access_required(EcoAccount, "id") +def deduction_edit_view(request: HttpRequest, id: str, deduction_id: str): + """ Renders a modal view for editing deductions + + Args: + request (HttpRequest): The incoming request + id (str): The eco account's id + deduction_id (str): The deduction's id + + Returns: + + """ + acc = get_object_or_404(EcoAccount, id=id) + try: + eco_deduction = acc.deductions.get(id=deduction_id) + except ObjectDoesNotExist: + raise Http404("Unknown deduction") + + form = EditEcoAccountDeductionModalForm(request.POST or None, instance=acc, deduction=eco_deduction, request=request) + return form.process_request( + request=request, + msg_success=DEDUCTION_EDITED, + redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" + ) + + @login_required @default_group_required @shared_access_required(EcoAccount, "id") diff --git a/ema/tests/test_views.py b/ema/tests/test_views.py index dd37fced..65f4d351 100644 --- a/ema/tests/test_views.py +++ b/ema/tests/test_views.py @@ -31,42 +31,43 @@ class EmaViewTestCase(CompensationViewTestCase): def setUpTestData(cls) -> None: super().setUpTestData() + def setUp(self) -> None: + super().setUp() # Create dummy data and related objects, like states or actions - cls.create_dummy_data() - state = cls.create_dummy_states() - action = cls.create_dummy_action() - cls.ema.before_states.set([state]) - cls.ema.after_states.set([state]) - cls.ema.actions.set([action]) + self.create_dummy_data() + state = self.create_dummy_states() + action = self.create_dummy_action() + self.ema.before_states.set([state]) + self.ema.after_states.set([state]) + self.ema.actions.set([action]) # Prepare urls - cls.index_url = reverse("ema:index", args=()) - cls.new_url = reverse("ema:new", args=()) - cls.new_id_url = reverse("ema:new-id", args=()) - cls.detail_url = reverse("ema:detail", args=(cls.ema.id,)) - cls.log_url = reverse("ema:log", args=(cls.ema.id,)) - cls.edit_url = reverse("ema:edit", args=(cls.ema.id,)) - cls.remove_url = reverse("ema:remove", args=(cls.ema.id,)) - cls.share_url = reverse("ema:share", args=(cls.ema.id, cls.ema.access_token,)) - cls.share_create_url = reverse("ema:share-create", args=(cls.ema.id,)) - cls.record_url = reverse("ema:record", args=(cls.ema.id,)) - cls.report_url = reverse("ema:report", args=(cls.ema.id,)) - cls.new_doc_url = reverse("ema:new-doc", args=(cls.ema.id,)) - cls.state_new_url = reverse("ema:new-state", args=(cls.ema.id,)) - cls.action_new_url = reverse("ema:new-action", args=(cls.ema.id,)) - cls.deadline_new_url = reverse("ema:new-deadline", args=(cls.ema.id,)) - cls.state_remove_url = reverse("ema:state-remove", args=(cls.ema.id, state.id,)) - cls.action_remove_url = reverse("ema:action-remove", args=(cls.ema.id, action.id,)) + self.index_url = reverse("ema:index", args=()) + self.new_url = reverse("ema:new", args=()) + self.new_id_url = reverse("ema:new-id", args=()) + self.detail_url = reverse("ema:detail", args=(self.ema.id,)) + self.log_url = reverse("ema:log", args=(self.ema.id,)) + self.edit_url = reverse("ema:edit", args=(self.ema.id,)) + self.remove_url = reverse("ema:remove", args=(self.ema.id,)) + self.share_url = reverse("ema:share", args=(self.ema.id, self.ema.access_token,)) + self.share_create_url = reverse("ema:share-create", args=(self.ema.id,)) + self.record_url = reverse("ema:record", args=(self.ema.id,)) + self.report_url = reverse("ema:report", args=(self.ema.id,)) + self.new_doc_url = reverse("ema:new-doc", args=(self.ema.id,)) + self.state_new_url = reverse("ema:new-state", args=(self.ema.id,)) + self.action_new_url = reverse("ema:new-action", args=(self.ema.id,)) + self.deadline_new_url = reverse("ema:new-deadline", args=(self.ema.id,)) + self.state_remove_url = reverse("ema:state-remove", args=(self.ema.id, state.id,)) + self.action_remove_url = reverse("ema:action-remove", args=(self.ema.id, action.id,)) - @classmethod - def create_dummy_data(cls): + def create_dummy_data(self): # Create dummy data # Create log entry - action = UserActionLogEntry.get_created_action(cls.superuser) + action = UserActionLogEntry.get_created_action(self.superuser) # Create responsible data object responsibility_data = Responsibility.objects.create() geometry = Geometry.objects.create() - cls.ema = Ema.objects.create( + self.ema = Ema.objects.create( identifier="TEST", title="Test_title", created=action, diff --git a/intervention/forms/modalForms.py b/intervention/forms/modalForms.py index 6e2a2a39..6a044c7c 100644 --- a/intervention/forms/modalForms.py +++ b/intervention/forms/modalForms.py @@ -7,7 +7,7 @@ Created on: 27.09.21 """ from dal import autocomplete -from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED, DEDUCTION_REMOVED +from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED, DEDUCTION_REMOVED, DEDUCTION_EDITED from user.models import User, UserActionLogEntry from django.db import transaction from django import forms @@ -349,6 +349,21 @@ class NewDeductionModalForm(BaseModalForm): else: raise NotImplementedError + def _get_available_surface(self, acc): + """ Calculates how much available surface is left on the account + + Args: + acc (EcoAccount): + + Returns: + + """ + # Calculate valid surface + deductable_surface = acc.deductable_surface + sum_surface_deductions = acc.get_deductions_surface() + rest_surface = deductable_surface - sum_surface_deductions + return rest_surface + def is_valid(self): """ Custom validity check @@ -367,10 +382,7 @@ class NewDeductionModalForm(BaseModalForm): ) return False - # Calculate valid surface - deductable_surface = acc.deductable_surface - sum_surface_deductions = acc.get_deductions_surface() - rest_surface = deductable_surface - sum_surface_deductions + rest_surface = self._get_available_surface(acc) form_surface = float(self.cleaned_data["surface"]) is_valid_surface = form_surface <= rest_surface if not is_valid_surface: @@ -407,6 +419,57 @@ class NewDeductionModalForm(BaseModalForm): return deduction +class EditEcoAccountDeductionModalForm(NewDeductionModalForm): + deduction = None + + def __init__(self, *args, **kwargs): + self.deduction = kwargs.pop("deduction", None) + super().__init__(*args, **kwargs) + form_data = { + "account": self.deduction.account, + "intervention": self.deduction.intervention, + "surface": self.deduction.surface, + } + self.load_initial_data(form_data) + + def _get_available_surface(self, acc): + rest_surface = super()._get_available_surface(acc) + # Increase available surface by the currently deducted surface, so we can 'deduct' the same amount again or + # increase the surface only a little, which will still be valid. + # Example: 200 m² left, 500 m² deducted. Entering 700 m² would fail if we would not add the 500 m² to the available + # surface again. + rest_surface += self.deduction.surface + return rest_surface + + def save(self): + deduction = self.deduction + form_account = self.cleaned_data.get("account", None) + form_intervention = self.cleaned_data.get("intervention", None) + current_account = deduction.account + current_intervention = deduction.intervention + + + # If account or intervention has been changed, we put that change in the logs just as if the deduction has + # been removed for this entry. Act as if the deduction is newly created for the new entries + if current_account != form_account: + current_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_REMOVED) + form_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_ADDED) + else: + current_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_EDITED) + + if current_intervention != form_intervention: + current_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_REMOVED) + form_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_ADDED) + else: + current_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_EDITED) + + deduction.account = form_account + deduction.intervention = self.cleaned_data.get("intervention", None) + deduction.surface = self.cleaned_data.get("surface", None) + deduction.save() + return deduction + + class RemoveEcoAccountDeductionModalForm(RemoveModalForm): """ Removing modal form for EcoAccountDeduction diff --git a/intervention/templates/intervention/detail/includes/deductions.html b/intervention/templates/intervention/detail/includes/deductions.html index 99f11cb8..b11817d1 100644 --- a/intervention/templates/intervention/detail/includes/deductions.html +++ b/intervention/templates/intervention/detail/includes/deductions.html @@ -55,9 +55,12 @@ {{ deduction.surface|floatformat:2|intcomma }} m² {{ deduction.created.timestamp|default_if_none:""|naturalday}} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/intervention/tests/test_views.py b/intervention/tests/test_views.py index 2cdb299d..68e4b562 100644 --- a/intervention/tests/test_views.py +++ b/intervention/tests/test_views.py @@ -21,30 +21,32 @@ class InterventionViewTestCase(BaseViewTestCase): def setUpTestData(cls) -> None: super().setUpTestData() + def setUp(self) -> None: + super().setUp() # Prepare urls - cls.index_url = reverse("intervention:index", args=()) - cls.new_url = reverse("intervention:new", args=()) - cls.new_id_url = reverse("intervention:new-id", args=()) - cls.detail_url = reverse("intervention:detail", args=(cls.intervention.id,)) - cls.log_url = reverse("intervention:log", args=(cls.intervention.id,)) - cls.edit_url = reverse("intervention:edit", args=(cls.intervention.id,)) - cls.remove_url = reverse("intervention:remove", args=(cls.intervention.id,)) - cls.share_url = reverse("intervention:share", args=(cls.intervention.id, cls.intervention.access_token,)) - cls.share_create_url = reverse("intervention:share-create", args=(cls.intervention.id,)) - cls.run_check_url = reverse("intervention:check", args=(cls.intervention.id,)) - cls.record_url = reverse("intervention:record", args=(cls.intervention.id,)) - cls.report_url = reverse("intervention:report", args=(cls.intervention.id,)) + self.index_url = reverse("intervention:index", args=()) + self.new_url = reverse("intervention:new", args=()) + self.new_id_url = reverse("intervention:new-id", args=()) + self.detail_url = reverse("intervention:detail", args=(self.intervention.id,)) + self.log_url = reverse("intervention:log", args=(self.intervention.id,)) + self.edit_url = reverse("intervention:edit", args=(self.intervention.id,)) + self.remove_url = reverse("intervention:remove", args=(self.intervention.id,)) + self.share_url = reverse("intervention:share", args=(self.intervention.id, self.intervention.access_token,)) + self.share_create_url = reverse("intervention:share-create", args=(self.intervention.id,)) + self.run_check_url = reverse("intervention:check", args=(self.intervention.id,)) + self.record_url = reverse("intervention:record", args=(self.intervention.id,)) + self.report_url = reverse("intervention:report", args=(self.intervention.id,)) - cls.deduction.intervention = cls.intervention - cls.deduction.save() - cls.deduction_new_url = reverse("intervention:new-deduction", args=(cls.intervention.id,)) - cls.deduction_remove_url = reverse("intervention:remove-deduction", args=(cls.intervention.id, cls.deduction.id)) + self.deduction.intervention = self.intervention + self.deduction.save() + self.deduction_new_url = reverse("intervention:new-deduction", args=(self.intervention.id,)) + self.deduction_remove_url = reverse("intervention:remove-deduction", args=(self.intervention.id, self.deduction.id)) - cls.revocation = Revocation.objects.create( - legal=cls.intervention.legal + self.revocation = Revocation.objects.create( + legal=self.intervention.legal ) - cls.revocation_new_url = reverse("intervention:new-revocation", args=(cls.intervention.id,)) - cls.revocation_remove_url = reverse("intervention:remove-revocation", args=(cls.intervention.id, cls.revocation.id)) + self.revocation_new_url = reverse("intervention:new-revocation", args=(self.intervention.id,)) + self.revocation_remove_url = reverse("intervention:remove-revocation", args=(self.intervention.id, self.revocation.id)) def test_views_anonymous_user(self): """ Check correct status code for all requests diff --git a/intervention/urls.py b/intervention/urls.py index 7d6ff32f..8ad7f31e 100644 --- a/intervention/urls.py +++ b/intervention/urls.py @@ -10,7 +10,7 @@ from django.urls import path from intervention.views import index_view, new_view, detail_view, edit_view, remove_view, new_document_view, share_view, \ create_share_view, remove_revocation_view, new_revocation_view, check_view, log_view, new_deduction_view, \ record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view, report_view, \ - remove_deduction_view, remove_compensation_view + remove_deduction_view, remove_compensation_view, edit_deduction_view app_name = "intervention" urlpatterns = [ @@ -37,6 +37,7 @@ urlpatterns = [ # Deductions path('/deduction/new', new_deduction_view, name='new-deduction'), + path('/deduction//edit', edit_deduction_view, name='edit-deduction'), path('/deduction//remove', remove_deduction_view, name='remove-deduction'), # Revocation routes diff --git a/intervention/views.py b/intervention/views.py index 8a59d9a9..6db35474 100644 --- a/intervention/views.py +++ b/intervention/views.py @@ -7,7 +7,7 @@ from django.shortcuts import render from intervention.forms.forms import NewInterventionForm, EditInterventionForm from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \ CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm, RemoveEcoAccountDeductionModalForm, \ - RemoveRevocationModalForm + RemoveRevocationModalForm, EditEcoAccountDeductionModalForm from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument from intervention.tables import InterventionTable from konova.contexts import BaseContext @@ -18,7 +18,7 @@ from konova.utils.documents import remove_document, get_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED, \ CHECKED_RECORDED_RESET, DEDUCTION_REMOVED, DEDUCTION_ADDED, REVOCATION_ADDED, REVOCATION_REMOVED, \ - COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED + COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, DEDUCTION_EDITED from konova.utils.user_checks import in_group @@ -536,6 +536,34 @@ def remove_deduction_view(request: HttpRequest, id: str, deduction_id: str): ) +@login_required +@default_group_required +@shared_access_required(Intervention, "id") +def edit_deduction_view(request: HttpRequest, id: str, deduction_id: str): + """ Renders a modal view for removing deductions + + Args: + request (HttpRequest): The incoming request + id (str): The intervention's id + deduction_id (str): The deduction's id + + Returns: + + """ + intervention = get_object_or_404(Intervention, id=id) + try: + eco_deduction = intervention.deductions.get(id=deduction_id) + except ObjectDoesNotExist: + raise Http404("Unknown deduction") + + form = EditEcoAccountDeductionModalForm(request.POST or None, instance=intervention, deduction=eco_deduction, request=request) + return form.process_request( + request=request, + msg_success=DEDUCTION_EDITED, + redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data" + ) + + @login_required @conservation_office_group_required @shared_access_required(Intervention, "id") diff --git a/konova/tests/test_views.py b/konova/tests/test_views.py index 536a7868..6218a6c0 100644 --- a/konova/tests/test_views.py +++ b/konova/tests/test_views.py @@ -47,43 +47,58 @@ class BaseTestCase(TestCase): class Meta: abstract = True - @classmethod - def setUpTestData(cls): - cls.create_users() - cls.create_groups() - cls.intervention = cls.create_dummy_intervention() - cls.compensation = cls.create_dummy_compensation() - cls.eco_account = cls.create_dummy_eco_account() - cls.ema = cls.create_dummy_ema() - cls.deduction = cls.create_dummy_deduction() - cls.create_dummy_states() - cls.create_dummy_action() - cls.codes = cls.create_dummy_codes() + def setUp(self) -> None: + """ Setup data before each test run - @classmethod - def create_users(cls): + Returns: + + """ + super().setUp() + + self.create_users() + self.create_groups() + self.intervention = self.create_dummy_intervention() + self.compensation = self.create_dummy_compensation() + self.eco_account = self.create_dummy_eco_account() + self.ema = self.create_dummy_ema() + self.deduction = self.create_dummy_deduction() + self.create_dummy_states() + self.create_dummy_action() + self.codes = self.create_dummy_codes() + + # Set the default group as only group for the user + default_group = self.groups.get(name=DEFAULT_GROUP) + self.superuser.groups.set([default_group]) + + # Create fresh logged in client and a non-logged in client (anon) for each test + self.client_user = Client() + self.client_user.login(username=self.superuser.username, password=self.superuser_pw) + self.client_anon = Client() + + + def create_users(self): # Create superuser and regular user - cls.superuser = User.objects.create_superuser( + self.superuser = User.objects.create_superuser( username="root", email="root@root.com", - password=cls.superuser_pw, + password=self.superuser_pw, ) - cls.user = User.objects.create_user( + self.user = User.objects.create_user( username="user1", email="user@root.com", - password=cls.user_pw + password=self.user_pw ) - cls.users = User.objects.all() + self.users = User.objects.all() - @classmethod - def create_groups(cls): + + def create_groups(self): # Create groups for group_data in GROUPS_DATA: name = group_data.get("name") Group.objects.get_or_create( name=name, ) - cls.groups = Group.objects.all() + self.groups = Group.objects.all() @staticmethod def create_dummy_string(prefix: str = ""): @@ -94,8 +109,7 @@ class BaseTestCase(TestCase): """ return f"{prefix}{generate_random_string(3, True)}" - @classmethod - def create_dummy_intervention(cls): + def create_dummy_intervention(self): """ Creates an intervention which can be used for tests Returns: @@ -103,7 +117,7 @@ class BaseTestCase(TestCase): """ # Create dummy data # Create log entry - action = UserActionLogEntry.get_created_action(cls.superuser) + action = UserActionLogEntry.get_created_action(self.superuser) # Create legal data object (without M2M laws first) legal_data = Legal.objects.create() # Create responsible data object @@ -122,32 +136,30 @@ class BaseTestCase(TestCase): intervention.generate_access_token(make_unique=True) return intervention - @classmethod - def create_dummy_compensation(cls): + def create_dummy_compensation(self): """ Creates a compensation which can be used for tests Returns: """ - if cls.intervention is None: - cls.intervention = cls.create_dummy_intervention() + if self.intervention is None: + self.intervention = self.create_dummy_intervention() # Create dummy data # Create log entry - action = UserActionLogEntry.get_created_action(cls.superuser) + action = UserActionLogEntry.get_created_action(self.superuser) geometry = Geometry.objects.create() # Finally create main object, holding the other objects compensation = Compensation.objects.create( identifier="TEST", title="Test_title", - intervention=cls.intervention, + intervention=self.intervention, created=action, geometry=geometry, comment="Test", ) return compensation - @classmethod - def create_dummy_eco_account(cls): + def create_dummy_eco_account(self): """ Creates an eco account which can be used for tests Returns: @@ -155,7 +167,7 @@ class BaseTestCase(TestCase): """ # Create dummy data # Create log entry - action = UserActionLogEntry.get_created_action(cls.superuser) + action = UserActionLogEntry.get_created_action(self.superuser) geometry = Geometry.objects.create() # Create responsible data object lega_data = Legal.objects.create() @@ -172,8 +184,7 @@ class BaseTestCase(TestCase): ) return eco_account - @classmethod - def create_dummy_ema(cls): + def create_dummy_ema(self): """ Creates an ema which can be used for tests Returns: @@ -181,7 +192,7 @@ class BaseTestCase(TestCase): """ # Create dummy data # Create log entry - action = UserActionLogEntry.get_created_action(cls.superuser) + action = UserActionLogEntry.get_created_action(self.superuser) geometry = Geometry.objects.create() # Create responsible data object responsible_data = Responsibility.objects.create() @@ -196,41 +207,37 @@ class BaseTestCase(TestCase): ) return ema - @classmethod - def create_dummy_deduction(cls): + def create_dummy_deduction(self): return EcoAccountDeduction.objects.create( - account=cls.create_dummy_eco_account(), - intervention=cls.create_dummy_intervention(), + account=self.create_dummy_eco_account(), + intervention=self.create_dummy_intervention(), surface=100, ) - @classmethod - def create_dummy_states(cls): + def create_dummy_states(self): """ Creates an intervention which can be used for tests Returns: """ - cls.comp_state = CompensationState.objects.create( + self.comp_state = CompensationState.objects.create( surface=10.00, biotope_type=None, ) - return cls.comp_state + return self.comp_state - @classmethod - def create_dummy_action(cls): + def create_dummy_action(self): """ Creates an intervention which can be used for tests Returns: """ - cls.comp_action = CompensationAction.objects.create( + self.comp_action = CompensationAction.objects.create( amount=10 ) - return cls.comp_action + return self.comp_action - @classmethod - def create_dummy_codes(cls): + def create_dummy_codes(self): """ Creates some dummy KonovaCodes which can be used for testing Returns: @@ -256,8 +263,7 @@ class BaseTestCase(TestCase): polygon = polygon.transform(3857, clone=True) return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form - @classmethod - def fill_out_intervention(cls, intervention: Intervention) -> Intervention: + def fill_out_intervention(self, intervention: Intervention) -> Intervention: """ Adds all required (dummy) data to an intervention Args: @@ -277,13 +283,12 @@ class BaseTestCase(TestCase): intervention.legal.process_type = KonovaCode.objects.get(id=3) intervention.legal.save() intervention.legal.laws.set([KonovaCode.objects.get(id=(4))]) - intervention.geometry.geom = cls.create_dummy_geometry() + intervention.geometry.geom = self.create_dummy_geometry() intervention.geometry.save() intervention.save() return intervention - @classmethod - def fill_out_compensation(cls, compensation: Compensation) -> Compensation: + def fill_out_compensation(self, compensation: Compensation) -> Compensation: """ Adds all required (dummy) data to a compensation Args: @@ -292,15 +297,14 @@ class BaseTestCase(TestCase): Returns: compensation (Compensation): The modified compensation """ - compensation.after_states.add(cls.comp_state) - compensation.before_states.add(cls.comp_state) - compensation.actions.add(cls.comp_action) - compensation.geometry.geom = cls.create_dummy_geometry() + compensation.after_states.add(self.comp_state) + compensation.before_states.add(self.comp_state) + compensation.actions.add(self.comp_action) + compensation.geometry.geom = self.create_dummy_geometry() compensation.geometry.save() return compensation - @classmethod - def get_conservation_office_code(cls): + def get_conservation_office_code(self): """ Returns a dummy KonovaCode as conservation office code Returns: @@ -313,39 +317,37 @@ class BaseTestCase(TestCase): codelist.codes.add(code) return code - @classmethod - def fill_out_ema(cls, ema): + def fill_out_ema(self, ema): """ Adds all required (dummy) data to an Ema Returns: """ - ema.responsible.conservation_office = cls.get_conservation_office_code() + ema.responsible.conservation_office = self.get_conservation_office_code() ema.responsible.conservation_file_number = "test" ema.responsible.handler = "handler" ema.responsible.save() - ema.after_states.add(cls.comp_state) - ema.before_states.add(cls.comp_state) - ema.actions.add(cls.comp_action) - ema.geometry.geom = cls.create_dummy_geometry() + ema.after_states.add(self.comp_state) + ema.before_states.add(self.comp_state) + ema.actions.add(self.comp_action) + ema.geometry.geom = self.create_dummy_geometry() ema.geometry.save() return ema - @classmethod - def fill_out_eco_account(cls, eco_account): + def fill_out_eco_account(self, eco_account): """ Adds all required (dummy) data to an EcoAccount Returns: """ eco_account.legal.registration_date = "2022-01-01" eco_account.legal.save() - eco_account.responsible.conservation_office = cls.get_conservation_office_code() + eco_account.responsible.conservation_office = self.get_conservation_office_code() eco_account.responsible.conservation_file_number = "test" eco_account.responsible.handler = "handler" eco_account.responsible.save() - eco_account.after_states.add(cls.comp_state) - eco_account.before_states.add(cls.comp_state) - eco_account.actions.add(cls.comp_action) - eco_account.geometry.geom = cls.create_dummy_geometry() + eco_account.after_states.add(self.comp_state) + eco_account.before_states.add(self.comp_state) + eco_account.actions.add(self.comp_action) + eco_account.geometry.geom = self.create_dummy_geometry() eco_account.geometry.save() eco_account.deductable_surface = eco_account.get_state_after_surface_sum() eco_account.save() @@ -390,7 +392,10 @@ class BaseViewTestCase(BaseTestCase): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() - cls.login_url = reverse("simple-sso-login") + + def setUp(self) -> None: + super().setUp() + self.login_url = reverse("simple-sso-login") def assert_url_success(self, client: Client, urls: list): """ Assert for all given urls a direct 200 response @@ -549,22 +554,6 @@ class BaseWorkflowTestCase(BaseTestCase): def setUpTestData(cls): super().setUpTestData() - def setUp(self) -> None: - """ Setup data before each test run - - Returns: - - """ - super().setUp() - # Set the default group as only group for the user - default_group = self.groups.get(name=DEFAULT_GROUP) - self.superuser.groups.set([default_group]) - - # Create fresh logged in client and a non-logged in client (anon) for each test - self.client_user = Client() - self.client_user.login(username=self.superuser.username, password=self.superuser_pw) - self.client_anon = Client() - def assert_object_is_deleted(self, obj): """ Provides a quick check whether an object has been removed from the database or not diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index b7eb5253..ea778ea2 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -30,18 +30,22 @@ ADDED_COMPENSATION_STATE = _("Added compensation state") # COMPENSATION STATE COMPENSATION_STATE_REMOVED = _("State removed") +COMPENSATION_STATE_EDITED = _("State edited") COMPENSATION_STATE_ADDED = _("State added") # COMPENSATION ACTION COMPENSATION_ACTION_ADDED = _("Action added") +COMPENSATION_ACTION_EDITED = _("Action edited") COMPENSATION_ACTION_REMOVED = _("Action removed") # DEDUCTIONS DEDUCTION_ADDED = _("Deduction added") +DEDUCTION_EDITED = _("Deduction edited") DEDUCTION_REMOVED = _("Deduction removed") # DEADLINE DEADLINE_ADDED = _("Deadline added") +DEADLINE_EDITED = _("Deadline edited") DEADLINE_REMOVED = _("Deadline removed") # PAYMENTS @@ -51,6 +55,7 @@ PAYMENT_REMOVED = _("Payment removed") # REVOCATIONS REVOCATION_ADDED = _("Revocation added") +REVOCATION_EDITED = _("Revocation edited") REVOCATION_REMOVED = _("Revocation removed") # DOCUMENTS diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 85b75fdda447f5409f7dac392a4a7ff964a0e7fe..86cf2739bfdda76a4b610efe974b54aee72f0ad0 100644 GIT binary patch delta 10863 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 RAE}Y*M)*9E&B_id^M7fg6Dj}z delta 10631 zcmZA72Y63+`^WK<$VMWGJtC+PGa`iAd&VBASVe+X?4l+8#SRjC)~eAeTBV^%tyWP* zt9^7(+tU^;9{NyK>i_=y&Yl0&>p9nTd)?pf8Q*zMLi=F7@A7rNo=XvaOC63|K8{lo zhle>%wy)#tk5aAU{HK!RgmLa3E~e~L*>Ps#Qk;q*mw@2q(h zLnz;|@}H>t18Tb0MWODif=r?lhn}(|14zPf8V2HGtca^nBR+zf+KZ?ex@kVeAj;u! z?tR5kGf);abG1<&Y=N55E~xucQG4RGIMzR$WCaz4a4Tx$hfzKM9yKF(QB(dD)xpBG z+yEZOay#mQ zdr>3%9Mz%gsE+<><@=}x9-DdUxHFm$b$?k@M{1((tB-2O(~LxG-yOBagHdZX3N_Mn zJ3ki-Q(lES{{iZIaR>|I1uTU3?0g`@)AI^qVT{I#n1GSkAFJy9A8QqFVkb`QLQSn- zU3WymsHu!V^|+i_8#VQ$WmVL5^-=G23)KB>P}g@tHP{_B&9AkP=GoFP-^!~3V(F1p( zUdIEd2VOCMK`q4{R0B^;pXVKiJ~{!|1sfyV$ytcnJIAm%evPB>H)M63zV+FdxCA{T zNN$tpbKj={>x7d~ugz-IRP95J;1m|YbEq}Hhg$Q1hVB}dM0KlUL0hTN+8siT@VI#j{V9Kq-Zeu_=}lBazoVAGr;%IlkGek$H3J1v=S!nH zP!2WIl~Dt#)rk3515ND0R(3%lblA4TaH5s2e+@I-H7H!f~jz zosF8IHK>8Si<+rjsOKF+b>IxLM?B6A5`7?inz8J)bqdL0V>W`ut zx{K^b=MQX$@y*?O4{C{LVKK}`EzM>O)%(AlL=Qe-o0Pjzm-bJE|j2E4LhqdQb_}gUg~C zs&3^3)P2oSOVAlL)hVc@n1H2mF0RFG7=Z0syGz;yJ!)_uNfjK0x^XpXWIN2ms2Mne zYTz<@_Xz5Nf1}pczl}R{VW_Dtg4#PZ&1R_0+zs{oer=e4T`+|TiyFZ1s67?f)-6Y&Iv$7WSPRq#s$W~?U&#b2^neAZ_ws$z z=KBJ*hT2iyB|ts6I+ntgsLeVE^_)rA7?;@jv&c8fxsMugYCM zZ5Ts&FY1Qtm=7PI9_*XwULT68FKOi%)E-H|3fKy@WND}+8jXQC5tA_kE7QJnfh3)Z zz>e-uzZuwt@;j&pJvPg9a(_6rF^8e1ehxl|8!#S^qrL-APy?yQyVoBFp*nN~H3P>n zSnvNC5-q`b^u;S!5wD~6L|%HM^OaE#h((Pg9yPTM%%-RZx5E6`3ALnsF${;ImUJTO zzIhl*`_5vLaNK}e`(3C997jFyTh!EFvie)74m?72$m#07C4N|rVkp+ZIMfGg7;3Z5 zKwZDl%9~NYe>z7=SPSPdR>kt&+>UfLQ_P7N$@#e$j_a&`r`3OE{$TaLp+^494D0TG z4@zSw^|9TV{|J)CRA}nEqNchB>UBy+^>`e5H;Vq;AHPus5YrYn>6kD(c zp0@K(QA=5@mwR1#4~f<~7MuC-HyFG~c}fyLp|Jl8eEl%6H@|x0UgU*vdhiZ?h?|gG zoa8?I0K**A2Wxa+J~Fr%wNz)Z68?;l7}n3N_mm^iCaaC=NF%cY7N*=ASKw&WgF<+h zv}7ew*VjOeup#QpnTDk>6E#zBTm2#PywyKIuJbsd1Kb{0#Uk9$5jCZ$s19UcL0pci ze-{hkG1T=JP;2~ORL7!UbYHJpsP9Kz)Sl{(x;_QNFdYl%{m&**Lu*kz-i=zrFHuu< z4z&qyqegts>YtgB1KroJB5HfsgCZhnON zMZ^`V6s$;dyL{x(*sJCSTM&WwYa}J|AdJgsNzl!SE4OB-SpxO;fVgB=wR8DcH zq&jLdwlq7U)~Xl!;}F!;rJ=5yj(T9W)o(^E*+D!14f3ixKbo3$mc?5OcIn+{I$MSdw{e9TbL){rE z?iuFxxFTvw>zj$F2lYoi;1yJlGf*R5j%skHoj-xWl+RlIC5)l`5cS;Xm)MvXhZ^u; zq%V$V0!awTWz_Dzg?il{p&HEpvfIJuQ15kJ)B}2=)^r%=#qp@;OhxUTY^;Y{P}g6_ zV0>sg!@YaN5r zhhZ~(h%>O>NXPjI4`CIYIEvph^!{%mQN<|?#Yd=37dYA-NdeT7ltH~-bx=#w2{n>b zE003${#h7~tIYQ>n(`s@3hK4=8N>Q(WI-e{KWfAgs0NB+1lBN{p>FJfYN!ut^WhKXPpI#}eXNYWW8ELy)lhq) z52|DRQ3FXu&D6`d2&bbqWsz}i!?jTDB#dMJHMOm%Py@+!;$;k{{2FRkXJLL^Zs*^! z@-Eco`xG^h$EXejyyE_t&Wn0od!zQkIOO$r_FyFL^^j=lPNRBs2{rY%u?_l-cb6d1 z9DsTa$D*!Zi0b&8sHNC}+7ln4I`k=4#V@fM{)N>sW`g@;+ml36lZs4ih5IlLeI~jc zi^mAcEwKqEp&D3;vA7W%<5|?27kt&V6zU6F0W~8{Q8U^Kb$=2vaE~*PL_Hsi1#uFp z!39_ZH==&He2jX)dDMu1Lp}HjdUx|{?iVizCsQAXtMPrDh8-rkf3G-$TFSCqR7mfC zEQuOyg_^P#Pz|M6c>-#xrRdy@+@k~FJno(f#uPkhw3?1Py=d+dfjsWvYSX* zGo6awzyD7nDN4m`)JQg`!aJXJ=cwksZfu$VmUlv<=dzaM?Q9tdR_^2T}yK?dOL!e(j}+{ zwxDM615`stQP24XHK03I_VY}4-^*y!g&oag)B}g2Mx2gX%lW90t-@&BhI+40p)Wp0 zE!k7kpPxLunNEI;!h^4l93(n(8l6OLYqaFnEr;R0U8oT?Qlc{#Pf_=4gr`*a>w(Z`9fjK{YrL z)u9Yjho+%6TNXZ#2hBfGyFPxddwo}orrZN7;drc%>#zdtJ3o^6p>LLJpqbAMM~%F& zm5ZY~P#V>-N~jUW+W7>t0s2$l6m@@V)b;JH+zmY;RP-XzW*cZHCZH~ygzDHdtItF| zFbmb-BCB7Cx^4|>Gj2frkU4{~_zde|Y_?l|3Du!h+04J*&vjIE#Y?CUNzHlgYtjy* zDG$IhI0eh$M)Nr4rTi9j$JNJZOR$s zAy%*IH!a_0j>p%Ct2)UsgwTH_KZI`(>j)je)HT6L_%VJ?%p!E?d(vI&Kb(^*u@N35 zek62UBhFji!AvvHVGH7Cu8ARflOH4WgJvP2<2j-=b!+fnIGN~7l%*VpIy{qIo$FM5 zPp;|jOWqi#5(~-m;T*g~^dV{x>{;)g0q+_IQg@tasEYqNcmuuv+|mK35n*=zeILDl zc3kA<{N$aj{BPVz%%^@JQH#*gl=y=D5k8LtP)95BD#QlzlgRHe&J^5({Rth}lyw{- zlF936{Z~==$WG|wMDlJ*I69Jdv2#;#EOkE+)rrkk{{^`=-!GKU;cDDYq?2#JDfk7)%@=ln0eiDtCC(CSDYqj&BO-{`iC)@&EjhV^f{snZJj&N`2~mm2Px&pP z8L^-88q`t8#rc$SJoy29kND8)m)muRDL*9sYxVlzB@;gp8)@Hre8|P`S}x)&uZDYx zD^|XY?bcy93oP@weBC??V8=DYEP4YW7qVs`kA<%x}iiAaoFm0+1T7^Dn;c2BGFDR z;AC0y;kt_BC31gaJ9$3TPclYGqoa%WM_ggfx3O|tavd{>_T+`pvy5bj zohWbWA{|GGD62a_o=QGXC;oAqqHZ)1O0=fFKIR_ZlP@M-rJ@8;j`)=LoVskRp#8Uq z!Z8ZJ6LraTGME(@N!n^2;|H37B6L;bb zBKOEc9;EgEk_e>s75t2tN&X`-ihM8eI*~~<$h{LU5PsC@SZM~3Zzd9n6@-ogoZCq3 zA*xW`Lgdj2j_TBT-~ZR#^8f$&3Rw%HBo};TwPmmnWqu5{-n|vHG zjJ&AThvSQs3lTa}iTXsW-v2oy1Bq!|bcCo)UIcTG0P?yNDqCGW^CWeD5Q~Yq|G1u_ z_y6o&&xpwTf1o\n" "Language-Team: LANGUAGE \n" @@ -95,7 +95,7 @@ msgstr "" #: analysis/templates/analysis/reports/includes/eco_account/amount.html:3 #: analysis/templates/analysis/reports/includes/intervention/amount.html:3 #: analysis/templates/analysis/reports/includes/old_data/amount.html:3 -#: compensation/forms/modalForms.py:406 +#: compensation/forms/modalForms.py:409 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:34 #: intervention/templates/intervention/detail/includes/deductions.html:31 msgid "Amount" @@ -213,7 +213,7 @@ msgstr "Abbuchungen" #: analysis/templates/analysis/reports/includes/eco_account/deductions.html:9 #: analysis/templates/analysis/reports/includes/eco_account/deductions.html:11 -#: compensation/forms/modalForms.py:190 +#: compensation/forms/modalForms.py:193 #: compensation/templates/compensation/detail/compensation/includes/states-after.html:36 #: compensation/templates/compensation/detail/compensation/includes/states-before.html:36 #: compensation/templates/compensation/detail/eco_account/includes/states-after.html:36 @@ -353,7 +353,7 @@ msgid "Compensation XY; Location ABC" msgstr "Kompensation XY; Flur ABC" #: compensation/forms/forms.py:57 compensation/forms/modalForms.py:61 -#: compensation/forms/modalForms.py:328 compensation/forms/modalForms.py:421 +#: compensation/forms/modalForms.py:331 compensation/forms/modalForms.py:424 #: compensation/templates/compensation/detail/compensation/includes/actions.html:35 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:34 #: compensation/templates/compensation/detail/compensation/includes/documents.html:31 @@ -371,7 +371,7 @@ msgstr "Kompensation XY; Flur ABC" msgid "Comment" msgstr "Kommentar" -#: compensation/forms/forms.py:59 compensation/forms/modalForms.py:423 +#: compensation/forms/forms.py:59 compensation/forms/modalForms.py:426 #: intervention/forms/forms.py:182 msgid "Additional comment" msgstr "Zusätzlicher Kommentar" @@ -457,7 +457,7 @@ msgstr "Vereinbarungsdatum" msgid "When did the parties agree on this?" msgstr "Wann wurde dieses Ökokonto offiziell vereinbart?" -#: compensation/forms/forms.py:354 compensation/views/eco_account.py:103 +#: compensation/forms/forms.py:354 compensation/views/eco_account.py:105 msgid "New Eco-Account" msgstr "Neues Ökokonto" @@ -482,7 +482,7 @@ msgstr "Fällig am" msgid "Due on which date" msgstr "Zahlung wird an diesem Datum erwartet" -#: compensation/forms/modalForms.py:63 compensation/forms/modalForms.py:330 +#: compensation/forms/modalForms.py:63 compensation/forms/modalForms.py:333 #: intervention/forms/modalForms.py:151 konova/forms.py:393 msgid "Additional comment, maximum {} letters" msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" @@ -495,47 +495,47 @@ msgstr "Neue Ersatzzahlung zu Eingriff '{}' hinzufügen" msgid "If there is no date you can enter, please explain why." msgstr "Falls Sie kein Datum angeben können, erklären Sie bitte weshalb." -#: compensation/forms/modalForms.py:154 compensation/forms/modalForms.py:166 +#: compensation/forms/modalForms.py:157 compensation/forms/modalForms.py:169 msgid "Biotope Type" msgstr "Biotoptyp" -#: compensation/forms/modalForms.py:157 +#: compensation/forms/modalForms.py:160 msgid "Select the biotope type" msgstr "Biotoptyp wählen" -#: compensation/forms/modalForms.py:171 compensation/forms/modalForms.py:183 +#: compensation/forms/modalForms.py:174 compensation/forms/modalForms.py:186 msgid "Biotope additional type" msgstr "Zusatzbezeichnung" -#: compensation/forms/modalForms.py:174 +#: compensation/forms/modalForms.py:177 msgid "Select an additional biotope type" msgstr "Zusatzbezeichnung wählen" -#: compensation/forms/modalForms.py:193 intervention/forms/modalForms.py:313 +#: compensation/forms/modalForms.py:196 intervention/forms/modalForms.py:313 msgid "in m²" msgstr "" -#: compensation/forms/modalForms.py:204 +#: compensation/forms/modalForms.py:207 msgid "New state" msgstr "Neuer Zustand" -#: compensation/forms/modalForms.py:205 +#: compensation/forms/modalForms.py:208 msgid "Insert data for the new state" msgstr "Geben Sie die Daten des neuen Zustandes ein" -#: compensation/forms/modalForms.py:212 konova/forms.py:191 +#: compensation/forms/modalForms.py:215 konova/forms.py:191 msgid "Object removed" msgstr "Objekt entfernt" -#: compensation/forms/modalForms.py:300 +#: compensation/forms/modalForms.py:303 msgid "Deadline Type" msgstr "Fristart" -#: compensation/forms/modalForms.py:303 +#: compensation/forms/modalForms.py:306 msgid "Select the deadline type" msgstr "Fristart wählen" -#: compensation/forms/modalForms.py:312 +#: compensation/forms/modalForms.py:315 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:31 #: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31 #: ema/templates/ema/detail/includes/deadlines.html:31 @@ -543,27 +543,27 @@ msgstr "Fristart wählen" msgid "Date" msgstr "Datum" -#: compensation/forms/modalForms.py:315 +#: compensation/forms/modalForms.py:318 msgid "Select date" msgstr "Datum wählen" -#: compensation/forms/modalForms.py:342 +#: compensation/forms/modalForms.py:345 msgid "New deadline" msgstr "Neue Frist" -#: compensation/forms/modalForms.py:343 +#: compensation/forms/modalForms.py:346 msgid "Insert data for the new deadline" msgstr "Geben Sie die Daten der neuen Frist ein" -#: compensation/forms/modalForms.py:360 +#: compensation/forms/modalForms.py:363 msgid "Action Type" msgstr "Maßnahmentyp" -#: compensation/forms/modalForms.py:363 +#: compensation/forms/modalForms.py:366 msgid "Select the action type" msgstr "Maßnahmentyp wählen" -#: compensation/forms/modalForms.py:372 +#: compensation/forms/modalForms.py:375 #: compensation/templates/compensation/detail/compensation/includes/actions.html:40 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:39 #: compensation/templates/compensation/detail/compensation/includes/documents.html:36 @@ -589,31 +589,31 @@ msgstr "Maßnahmentyp wählen" msgid "Action" msgstr "Aktionen" -#: compensation/forms/modalForms.py:377 compensation/forms/modalForms.py:389 +#: compensation/forms/modalForms.py:380 compensation/forms/modalForms.py:392 msgid "Action Type detail" msgstr "Zusatzmerkmal" -#: compensation/forms/modalForms.py:380 +#: compensation/forms/modalForms.py:383 msgid "Select the action type detail" msgstr "Zusatzmerkmal wählen" -#: compensation/forms/modalForms.py:394 +#: compensation/forms/modalForms.py:397 msgid "Unit" msgstr "Einheit" -#: compensation/forms/modalForms.py:397 +#: compensation/forms/modalForms.py:400 msgid "Select the unit" msgstr "Einheit wählen" -#: compensation/forms/modalForms.py:409 +#: compensation/forms/modalForms.py:412 msgid "Insert the amount" msgstr "Menge eingeben" -#: compensation/forms/modalForms.py:434 +#: compensation/forms/modalForms.py:437 msgid "New action" msgstr "Neue Maßnahme" -#: compensation/forms/modalForms.py:435 +#: compensation/forms/modalForms.py:438 msgid "Insert data for the new action" msgstr "Geben Sie die Daten der neuen Maßnahme ein" @@ -992,7 +992,7 @@ msgid "Recorded on" msgstr "Verzeichnet am" #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:65 -#: intervention/templates/intervention/detail/includes/deductions.html:60 +#: intervention/templates/intervention/detail/includes/deductions.html:63 msgid "Remove Deduction" msgstr "Abbuchung entfernen" @@ -1084,63 +1084,63 @@ msgstr "Kompensationen - Übersicht" msgid "Compensation {} edited" msgstr "Kompensation {} bearbeitet" -#: compensation/views/compensation.py:159 compensation/views/eco_account.py:161 +#: compensation/views/compensation.py:159 compensation/views/eco_account.py:163 #: ema/views.py:230 intervention/views.py:305 msgid "Edit {}" msgstr "Bearbeite {}" -#: compensation/views/compensation.py:238 compensation/views/eco_account.py:317 +#: compensation/views/compensation.py:238 compensation/views/eco_account.py:347 #: ema/views.py:191 intervention/views.py:483 msgid "Log" msgstr "Log" -#: compensation/views/compensation.py:487 compensation/views/eco_account.py:590 -#: ema/views.py:477 intervention/views.py:601 +#: compensation/views/compensation.py:487 compensation/views/eco_account.py:620 +#: ema/views.py:477 intervention/views.py:629 msgid "Report {}" msgstr "Bericht {}" -#: compensation/views/eco_account.py:60 +#: compensation/views/eco_account.py:62 msgid "Eco-account - Overview" msgstr "Ökokonten - Übersicht" -#: compensation/views/eco_account.py:93 +#: compensation/views/eco_account.py:95 msgid "Eco-Account {} added" msgstr "Ökokonto {} hinzugefügt" -#: compensation/views/eco_account.py:151 +#: compensation/views/eco_account.py:153 msgid "Eco-Account {} edited" msgstr "Ökokonto {} bearbeitet" -#: compensation/views/eco_account.py:264 +#: compensation/views/eco_account.py:266 msgid "Eco-account removed" msgstr "Ökokonto entfernt" -#: compensation/views/eco_account.py:338 ema/views.py:272 -#: intervention/views.py:554 +#: compensation/views/eco_account.py:368 ema/views.py:272 +#: intervention/views.py:582 msgid "{} unrecorded" msgstr "{} entzeichnet" -#: compensation/views/eco_account.py:338 ema/views.py:272 -#: intervention/views.py:554 +#: compensation/views/eco_account.py:368 ema/views.py:272 +#: intervention/views.py:582 msgid "{} recorded" msgstr "{} verzeichnet" -#: compensation/views/eco_account.py:663 ema/views.py:543 +#: compensation/views/eco_account.py:693 ema/views.py:543 #: intervention/views.py:380 msgid "{} has already been shared with you" msgstr "{} wurde bereits für Sie freigegeben" -#: compensation/views/eco_account.py:668 ema/views.py:548 +#: compensation/views/eco_account.py:698 ema/views.py:548 #: intervention/views.py:385 msgid "{} has been shared with you" msgstr "{} ist nun für Sie freigegeben" -#: compensation/views/eco_account.py:675 ema/views.py:555 +#: compensation/views/eco_account.py:705 ema/views.py:555 #: intervention/views.py:392 msgid "Share link invalid" msgstr "Freigabelink ungültig" -#: compensation/views/eco_account.py:698 ema/views.py:578 +#: compensation/views/eco_account.py:728 ema/views.py:578 #: intervention/views.py:415 msgid "Share settings updated" msgstr "Freigabe Einstellungen aktualisiert" @@ -1337,7 +1337,7 @@ msgstr "Neue Abbuchung" msgid "Enter the information for a new deduction from a chosen eco-account" msgstr "Geben Sie die Informationen für eine neue Abbuchung ein." -#: intervention/forms/modalForms.py:366 +#: intervention/forms/modalForms.py:381 msgid "" "Eco-account {} is not recorded yet. You can only deduct from recorded " "accounts." @@ -1345,7 +1345,7 @@ msgstr "" "Ökokonto {} ist noch nicht verzeichnet. Abbuchungen können nur von " "verzeichneten Ökokonten erfolgen." -#: intervention/forms/modalForms.py:379 +#: intervention/forms/modalForms.py:391 msgid "" "The account {} has not enough surface for a deduction of {} m². There are " "only {} m² left" @@ -1373,6 +1373,10 @@ msgstr "Ökokonto gelöscht! Abbuchung ungültig!" msgid "Eco-account not recorded! Deduction invalid!" msgstr "Ökokonto nicht verzeichnet! Abbuchung ungültig!" +#: intervention/templates/intervention/detail/includes/deductions.html:60 +msgid "Edit Deduction" +msgstr "Abbuchung bearbeiten" + #: intervention/templates/intervention/detail/includes/payments.html:8 #: intervention/templates/intervention/report/report.html:73 msgid "Payments" @@ -1464,7 +1468,7 @@ msgstr "{} entfernt" msgid "Check performed" msgstr "Prüfung durchgeführt" -#: intervention/views.py:559 +#: intervention/views.py:587 msgid "There are errors on this intervention:" msgstr "Es liegen Fehler in diesem Eingriff vor:" @@ -1824,74 +1828,94 @@ msgid "State removed" msgstr "Zustand gelöscht" #: konova/utils/message_templates.py:33 +msgid "State edited" +msgstr "Zustand bearbeitet" + +#: konova/utils/message_templates.py:34 msgid "State added" msgstr "Zustand hinzugefügt" -#: konova/utils/message_templates.py:36 +#: konova/utils/message_templates.py:37 msgid "Action added" msgstr "Maßnahme hinzugefügt" -#: konova/utils/message_templates.py:37 +#: konova/utils/message_templates.py:38 +msgid "Action edited" +msgstr "Maßnahme bearbeitet" + +#: konova/utils/message_templates.py:39 msgid "Action removed" msgstr "Maßnahme entfernt" -#: konova/utils/message_templates.py:40 +#: konova/utils/message_templates.py:42 msgid "Deduction added" msgstr "Abbuchung hinzugefügt" -#: konova/utils/message_templates.py:41 +#: konova/utils/message_templates.py:43 +msgid "Deduction edited" +msgstr "Abbuchung bearbeitet" + +#: konova/utils/message_templates.py:44 msgid "Deduction removed" msgstr "Abbuchung entfernt" -#: konova/utils/message_templates.py:44 +#: konova/utils/message_templates.py:47 msgid "Deadline added" msgstr "Frist/Termin hinzugefügt" -#: konova/utils/message_templates.py:45 +#: konova/utils/message_templates.py:48 +msgid "Deadline edited" +msgstr "Frist/Termin bearbeitet" + +#: konova/utils/message_templates.py:49 msgid "Deadline removed" msgstr "Frist/Termin gelöscht" -#: konova/utils/message_templates.py:48 +#: konova/utils/message_templates.py:52 msgid "Payment added" msgstr "Zahlung hinzugefügt" -#: konova/utils/message_templates.py:49 +#: konova/utils/message_templates.py:53 msgid "Payment edited" msgstr "Zahlung bearbeitet" -#: konova/utils/message_templates.py:50 +#: konova/utils/message_templates.py:54 msgid "Payment removed" msgstr "Zahlung gelöscht" -#: konova/utils/message_templates.py:53 +#: konova/utils/message_templates.py:57 msgid "Revocation added" msgstr "Widerspruch hinzugefügt" -#: konova/utils/message_templates.py:54 +#: konova/utils/message_templates.py:58 +msgid "Revocation edited" +msgstr "Widerspruch bearbeitet" + +#: konova/utils/message_templates.py:59 msgid "Revocation removed" msgstr "Widerspruch entfernt" -#: konova/utils/message_templates.py:57 +#: konova/utils/message_templates.py:62 msgid "Document '{}' deleted" msgstr "Dokument '{}' gelöscht" -#: konova/utils/message_templates.py:58 +#: konova/utils/message_templates.py:63 msgid "Document added" msgstr "Dokument hinzugefügt" -#: konova/utils/message_templates.py:61 +#: konova/utils/message_templates.py:66 msgid "Edited general data" msgstr "Allgemeine Daten bearbeitet" -#: konova/utils/message_templates.py:62 +#: konova/utils/message_templates.py:67 msgid "Added deadline" msgstr "Frist/Termin hinzugefügt" -#: konova/utils/message_templates.py:65 +#: konova/utils/message_templates.py:70 msgid "Geometry conflict detected with {}" msgstr "Geometriekonflikt mit folgenden Einträgen erkannt: {}" -#: konova/utils/message_templates.py:68 +#: konova/utils/message_templates.py:73 msgid "This intervention has {} revocations" msgstr "Dem Eingriff liegen {} Widersprüche vor" From 1b5cda648e8273dc36ab7ce47f46daed631a78e7 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Wed, 9 Feb 2022 16:02:28 +0100 Subject: [PATCH 19/31] #86 Revocation edit * adds support for revocation edit * revocation document files will be replaced on an edit --- intervention/forms/modalForms.py | 57 ++++++++++++++++++- intervention/models/intervention.py | 37 ++++++++++++ .../detail/includes/revocation.html | 7 ++- intervention/urls.py | 3 +- intervention/views.py | 32 ++++++++++- konova/forms.py | 6 +- konova/models/document.py | 16 ++++++ konova/utils/message_templates.py | 4 ++ 8 files changed, 151 insertions(+), 11 deletions(-) diff --git a/intervention/forms/modalForms.py b/intervention/forms/modalForms.py index 6a044c7c..8039b3ae 100644 --- a/intervention/forms/modalForms.py +++ b/intervention/forms/modalForms.py @@ -6,8 +6,11 @@ Created on: 27.09.21 """ from dal import autocomplete +from django.core.exceptions import ObjectDoesNotExist +from django.db.models.fields.files import FieldFile -from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED, DEDUCTION_REMOVED, DEDUCTION_EDITED +from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED, DEDUCTION_REMOVED, DEDUCTION_EDITED, \ + REVOCATION_EDITED, FILE_TYPE_UNSUPPORTED, FILE_SIZE_TOO_LARGE from user.models import User, UserActionLogEntry from django.db import transaction from django import forms @@ -15,7 +18,7 @@ from django.utils.translation import gettext_lazy as _ from compensation.models import EcoAccount, EcoAccountDeduction from intervention.inputs import TextToClipboardInput -from intervention.models import Intervention, InterventionDocument +from intervention.models import Intervention, InterventionDocument, RevocationDocument from konova.forms import BaseModalForm, NewDocumentForm, RemoveModalForm from konova.utils.general import format_german_float from konova.utils.user_checks import is_default_group_only @@ -157,6 +160,7 @@ class NewRevocationModalForm(BaseModalForm): } ) ) + document_model = RevocationDocument def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -166,12 +170,61 @@ class NewRevocationModalForm(BaseModalForm): "enctype": "multipart/form-data", # important for file upload } + def is_valid(self): + super_valid = super().is_valid() + + _file = self.cleaned_data.get("file", None) + + if isinstance(_file, FieldFile): + # FieldFile declares that no new file has been uploaded and we do not need to check on the file again + return super_valid + + mime_type_valid = self.document_model.is_mime_type_valid(_file) + if not mime_type_valid: + self.add_error( + "file", + FILE_TYPE_UNSUPPORTED + ) + + file_size_valid = self.document_model.is_file_size_valid(_file) + if not file_size_valid: + self.add_error( + "file", + FILE_SIZE_TOO_LARGE + ) + + file_valid = mime_type_valid and file_size_valid + return super_valid and file_valid + def save(self): revocation = self.instance.add_revocation(self) self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_ADDED) return revocation +class EditRevocationModalForm(NewRevocationModalForm): + revocation = None + + def __init__(self, *args, **kwargs): + self.revocation = kwargs.pop("revocation", None) + super().__init__(*args, **kwargs) + try: + doc = self.revocation.document.file + except ObjectDoesNotExist: + doc = None + form_data = { + "date": str(self.revocation.date), + "file": doc, + "comment": self.revocation.comment, + } + self.load_initial_data(form_data) + + def save(self): + revocation = self.instance.edit_revocation(self) + self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_EDITED) + return revocation + + class RemoveRevocationModalForm(RemoveModalForm): """ Removing modal form for Revocation diff --git a/intervention/models/intervention.py b/intervention/models/intervention.py index e2e736d7..167c27af 100644 --- a/intervention/models/intervention.py +++ b/intervention/models/intervention.py @@ -8,6 +8,8 @@ Created on: 15.11.21 import shutil from django.contrib import messages +from django.core.exceptions import ObjectDoesNotExist +from django.db.models.fields.files import FieldFile from django.urls import reverse from django.utils import timezone @@ -202,6 +204,41 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec ) return revocation + def edit_revocation(self, form): + """ Updates a revocation of the intervention + + Args: + form (EditRevocationModalForm): The form holding the data + + Returns: + + """ + form_data = form.cleaned_data + file = form_data.get("file", None) + + revocation = form.revocation + revocation.date = form_data.get("date", None) + revocation.comment = form_data.get("comment", None) + + with transaction.atomic(): + try: + revocation.document.date_of_creation = revocation.date + revocation.document.comment = revocation.comment + if not isinstance(file, FieldFile): + revocation.document.replace_file(file) + revocation.document.save() + except ObjectDoesNotExist: + revocation.document = RevocationDocument.objects.create( + title="revocation_of_{}".format(self.identifier), + date_of_creation=revocation.date, + comment=revocation.comment, + file=file, + instance=revocation + ) + revocation.save() + + return revocation + def remove_revocation(self, form): """ Removes a revocation from the intervention diff --git a/intervention/templates/intervention/detail/includes/revocation.html b/intervention/templates/intervention/detail/includes/revocation.html index d6b07b72..6eb68f99 100644 --- a/intervention/templates/intervention/detail/includes/revocation.html +++ b/intervention/templates/intervention/detail/includes/revocation.html @@ -63,9 +63,12 @@ {{ rev.comment }} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/intervention/urls.py b/intervention/urls.py index 8ad7f31e..45985587 100644 --- a/intervention/urls.py +++ b/intervention/urls.py @@ -10,7 +10,7 @@ from django.urls import path from intervention.views import index_view, new_view, detail_view, edit_view, remove_view, new_document_view, share_view, \ create_share_view, remove_revocation_view, new_revocation_view, check_view, log_view, new_deduction_view, \ record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view, report_view, \ - remove_deduction_view, remove_compensation_view, edit_deduction_view + remove_deduction_view, remove_compensation_view, edit_deduction_view, edit_revocation_view app_name = "intervention" urlpatterns = [ @@ -42,6 +42,7 @@ urlpatterns = [ # Revocation routes path('/revocation/new', new_revocation_view, name='new-revocation'), + path('/revocation//edit', edit_revocation_view, name='edit-revocation'), path('/revocation//remove', remove_revocation_view, name='remove-revocation'), path('revocation/', get_revocation_view, name='get-doc-revocation'), ] \ No newline at end of file diff --git a/intervention/views.py b/intervention/views.py index 6db35474..90447c91 100644 --- a/intervention/views.py +++ b/intervention/views.py @@ -7,7 +7,7 @@ from django.shortcuts import render from intervention.forms.forms import NewInterventionForm, EditInterventionForm from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \ CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm, RemoveEcoAccountDeductionModalForm, \ - RemoveRevocationModalForm, EditEcoAccountDeductionModalForm + RemoveRevocationModalForm, EditEcoAccountDeductionModalForm, EditRevocationModalForm from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument from intervention.tables import InterventionTable from konova.contexts import BaseContext @@ -18,7 +18,7 @@ from konova.utils.documents import remove_document, get_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED, \ CHECKED_RECORDED_RESET, DEDUCTION_REMOVED, DEDUCTION_ADDED, REVOCATION_ADDED, REVOCATION_REMOVED, \ - COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, DEDUCTION_EDITED + COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, DEDUCTION_EDITED, REVOCATION_EDITED from konova.utils.user_checks import in_group @@ -331,6 +331,31 @@ def remove_view(request: HttpRequest, id: str): ) +@login_required +@default_group_required +@shared_access_required(Intervention, "id") +def edit_revocation_view(request: HttpRequest, id: str, revocation_id: str): + """ Renders a edit view for a revocation + + Args: + request (HttpRequest): The incoming request + id (str): The intervention's id as string + revocation_id (str): The revocation's id as string + + Returns: + + """ + intervention = get_object_or_404(Intervention, id=id) + revocation = get_object_or_404(Revocation, id=revocation_id) + + form = EditRevocationModalForm(request.POST or None, request.FILES or None, instance=intervention, revocation=revocation, request=request) + return form.process_request( + request, + REVOCATION_EDITED, + redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data" + ) + + @login_required @default_group_required @shared_access_required(Intervention, "id") @@ -339,7 +364,8 @@ def remove_revocation_view(request: HttpRequest, id: str, revocation_id: str): Args: request (HttpRequest): The incoming request - id (str): The revocation's id as string + id (str): The intervention's id as string + revocation_id (str): The revocation's id as string Returns: diff --git a/konova/forms.py b/konova/forms.py index c05eb422..a842e411 100644 --- a/konova/forms.py +++ b/konova/forms.py @@ -24,7 +24,7 @@ from konova.contexts import BaseContext from konova.models import BaseObject, Geometry, RecordableObjectMixin from konova.settings import DEFAULT_SRID from konova.tasks import celery_update_parcels -from konova.utils.message_templates import FORM_INVALID +from konova.utils.message_templates import FORM_INVALID, FILE_TYPE_UNSUPPORTED, FILE_SIZE_TOO_LARGE from user.models import UserActionLogEntry @@ -424,14 +424,14 @@ class NewDocumentForm(BaseModalForm): if not mime_type_valid: self.add_error( "file", - _("Unsupported file type") + FILE_TYPE_UNSUPPORTED ) file_size_valid = self.document_model.is_file_size_valid(_file) if not file_size_valid: self.add_error( "file", - _("File too large") + FILE_SIZE_TOO_LARGE ) file_valid = mime_type_valid and file_size_valid diff --git a/konova/models/document.py b/konova/models/document.py index 72b124d3..b465d70c 100644 --- a/konova/models/document.py +++ b/konova/models/document.py @@ -101,3 +101,19 @@ class AbstractDocument(BaseResource): def is_file_size_valid(cls, _file): max_size = cls._maximum_file_size * pow(1000, 2) return _file.size <= max_size + + def replace_file(self, new_file): + """ Replaces the old file on the hard drive with the new one + + Args: + new_file (File): The new file + + Returns: + + """ + try: + os.remove(self.file.file.name) + except FileNotFoundError: + pass + self.file = new_file + self.save() \ No newline at end of file diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index ea778ea2..2196dd9c 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -18,6 +18,10 @@ MISSING_GROUP_PERMISSION = _("You need to be part of another user group.") CHECKED_RECORDED_RESET = _("Status of Checked and Recorded reseted") +# FILES +FILE_TYPE_UNSUPPORTED = _("Unsupported file type") +FILE_SIZE_TOO_LARGE = _("File too large") + # ECO ACCOUNT CANCEL_ACC_RECORDED_OR_DEDUCTED = _("Action canceled. Eco account is recorded or deductions exist. Only conservation office member can perform this action.") From c5534bcd552d67d4e095df8efcdadd9894330f4d Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 10:21:18 +0100 Subject: [PATCH 20/31] #86 Edit document * adds support for editing of documents * adds buttons for intervention --- compensation/forms/modalForms.py | 6 +- compensation/views/compensation.py | 4 +- compensation/views/eco_account.py | 6 +- ema/forms.py | 4 +- ema/views.py | 4 +- intervention/forms/modalForms.py | 30 +-- .../detail/includes/documents.html | 19 +- intervention/urls.py | 7 +- intervention/views.py | 44 ++- konova/forms.py | 45 +++- konova/utils/documents.py | 2 +- konova/utils/message_templates.py | 1 + locale/de/LC_MESSAGES/django.mo | Bin 37109 -> 37314 bytes locale/de/LC_MESSAGES/django.po | 250 +++++++++--------- 14 files changed, 247 insertions(+), 175 deletions(-) diff --git a/compensation/forms/modalForms.py b/compensation/forms/modalForms.py index a6e7df26..5368077c 100644 --- a/compensation/forms/modalForms.py +++ b/compensation/forms/modalForms.py @@ -18,7 +18,7 @@ from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION CODELIST_COMPENSATION_ACTION_DETAIL_ID from compensation.models import CompensationDocument, EcoAccountDocument from konova.contexts import BaseContext -from konova.forms import BaseModalForm, NewDocumentForm, RemoveModalForm +from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm from konova.models import DeadlineType from konova.utils.message_templates import FORM_INVALID, ADDED_COMPENSATION_STATE, ADDED_DEADLINE, \ ADDED_COMPENSATION_ACTION, PAYMENT_EDITED @@ -443,9 +443,9 @@ class NewActionModalForm(BaseModalForm): return action -class NewCompensationDocumentForm(NewDocumentForm): +class NewCompensationDocumentModalForm(NewDocumentModalForm): document_model = CompensationDocument -class NewEcoAccountDocumentForm(NewDocumentForm): +class NewEcoAccountDocumentModalForm(NewDocumentModalForm): document_model = EcoAccountDocument \ No newline at end of file diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py index c4b5c2f5..95f4ac0b 100644 --- a/compensation/views/compensation.py +++ b/compensation/views/compensation.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _ from compensation.forms.forms import NewCompensationForm, EditCompensationForm from compensation.forms.modalForms import NewStateModalForm, NewDeadlineModalForm, NewActionModalForm, \ - NewCompensationDocumentForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm + NewCompensationDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument from compensation.tables import CompensationTable from intervention.models import Intervention @@ -276,7 +276,7 @@ def new_document_view(request: HttpRequest, id: str): """ comp = get_object_or_404(Compensation, id=id) - form = NewCompensationDocumentForm(request.POST or None, request.FILES or None, instance=comp, request=request) + form = NewCompensationDocumentModalForm(request.POST or None, request.FILES or None, instance=comp, request=request) return form.process_request( request, msg_success=DOCUMENT_ADDED, diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index 08e01a61..d433d551 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -16,7 +16,7 @@ from django.shortcuts import render, get_object_or_404, redirect from compensation.forms.forms import NewEcoAccountForm, EditEcoAccountForm from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ - NewEcoAccountDocumentForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm + NewEcoAccountDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction from compensation.tables import EcoAccountTable from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm, \ @@ -24,7 +24,7 @@ from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, from konova.contexts import BaseContext from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \ shared_access_required -from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentForm, RecordModalForm, RemoveDeadlineModalForm +from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentModalForm, RecordModalForm, RemoveDeadlineModalForm from konova.models import Deadline from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER @@ -524,7 +524,7 @@ def new_document_view(request: HttpRequest, id: str): """ acc = get_object_or_404(EcoAccount, id=id) - form = NewEcoAccountDocumentForm(request.POST or None, request.FILES or None, instance=acc, request=request) + form = NewEcoAccountDocumentModalForm(request.POST or None, request.FILES or None, instance=acc, request=request) return form.process_request( request, msg_success=DOCUMENT_ADDED, diff --git a/ema/forms.py b/ema/forms.py index 5dc3bcfb..2f193605 100644 --- a/ema/forms.py +++ b/ema/forms.py @@ -15,7 +15,7 @@ from django.utils.translation import gettext_lazy as _ from compensation.forms.forms import AbstractCompensationForm, CompensationResponsibleFormMixin from ema.models import Ema, EmaDocument from intervention.models import Responsibility -from konova.forms import SimpleGeomForm, NewDocumentForm +from konova.forms import SimpleGeomForm, NewDocumentModalForm from user.models import UserActionLogEntry @@ -150,5 +150,5 @@ class EditEmaForm(NewEmaForm): return self.instance -class NewEmaDocumentForm(NewDocumentForm): +class NewEmaDocumentModalForm(NewDocumentModalForm): document_model = EmaDocument \ No newline at end of file diff --git a/ema/views.py b/ema/views.py index 900dde7c..99c80be3 100644 --- a/ema/views.py +++ b/ema/views.py @@ -9,7 +9,7 @@ from django.utils.translation import gettext_lazy as _ from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ RemoveCompensationActionModalForm, RemoveCompensationStateModalForm from compensation.models import CompensationAction, CompensationState -from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentForm +from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentModalForm from ema.tables import EmaTable from intervention.forms.modalForms import ShareModalForm from konova.contexts import BaseContext @@ -356,7 +356,7 @@ def document_new_view(request: HttpRequest, id: str): """ ema = get_object_or_404(Ema, id=id) - form = NewEmaDocumentForm(request.POST or None, request.FILES or None, instance=ema, request=request) + form = NewEmaDocumentModalForm(request.POST or None, request.FILES or None, instance=ema, request=request) return form.process_request( request, msg_success=DOCUMENT_ADDED, diff --git a/intervention/forms/modalForms.py b/intervention/forms/modalForms.py index 8039b3ae..a8fa98de 100644 --- a/intervention/forms/modalForms.py +++ b/intervention/forms/modalForms.py @@ -19,7 +19,7 @@ from django.utils.translation import gettext_lazy as _ from compensation.models import EcoAccount, EcoAccountDeduction from intervention.inputs import TextToClipboardInput from intervention.models import Intervention, InterventionDocument, RevocationDocument -from konova.forms import BaseModalForm, NewDocumentForm, RemoveModalForm +from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm from konova.utils.general import format_german_float from konova.utils.user_checks import is_default_group_only @@ -170,32 +170,6 @@ class NewRevocationModalForm(BaseModalForm): "enctype": "multipart/form-data", # important for file upload } - def is_valid(self): - super_valid = super().is_valid() - - _file = self.cleaned_data.get("file", None) - - if isinstance(_file, FieldFile): - # FieldFile declares that no new file has been uploaded and we do not need to check on the file again - return super_valid - - mime_type_valid = self.document_model.is_mime_type_valid(_file) - if not mime_type_valid: - self.add_error( - "file", - FILE_TYPE_UNSUPPORTED - ) - - file_size_valid = self.document_model.is_file_size_valid(_file) - if not file_size_valid: - self.add_error( - "file", - FILE_SIZE_TOO_LARGE - ) - - file_valid = mime_type_valid and file_size_valid - return super_valid and file_valid - def save(self): revocation = self.instance.add_revocation(self) self.instance.mark_as_edited(self.user, self.request, edit_comment=REVOCATION_ADDED) @@ -543,5 +517,5 @@ class RemoveEcoAccountDeductionModalForm(RemoveModalForm): self.deduction.delete() -class NewInterventionDocumentForm(NewDocumentForm): +class NewInterventionDocumentModalForm(NewDocumentModalForm): document_model = InterventionDocument diff --git a/intervention/templates/intervention/detail/includes/documents.html b/intervention/templates/intervention/detail/includes/documents.html index f536f154..93309f5c 100644 --- a/intervention/templates/intervention/detail/includes/documents.html +++ b/intervention/templates/intervention/detail/includes/documents.html @@ -27,7 +27,10 @@ {% trans 'Title' %} - + + {% trans 'Created on' %} + + {% trans 'Comment' %} {% if is_default_member and has_access %} @@ -43,18 +46,26 @@ {% for doc in obj.documents.all %} - + {{ doc.title }} + +
+ {{ doc.date_of_creation }} +
+
{{ doc.comment }}
- + {% if is_default_member and has_access %} - + {% endif %} diff --git a/intervention/urls.py b/intervention/urls.py index 45985587..2a5e6d38 100644 --- a/intervention/urls.py +++ b/intervention/urls.py @@ -10,7 +10,7 @@ from django.urls import path from intervention.views import index_view, new_view, detail_view, edit_view, remove_view, new_document_view, share_view, \ create_share_view, remove_revocation_view, new_revocation_view, check_view, log_view, new_deduction_view, \ record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view, report_view, \ - remove_deduction_view, remove_compensation_view, edit_deduction_view, edit_revocation_view + remove_deduction_view, remove_compensation_view, edit_deduction_view, edit_revocation_view, edit_document_view app_name = "intervention" urlpatterns = [ @@ -32,8 +32,9 @@ urlpatterns = [ # Documents path('/document/new/', new_document_view, name='new-doc'), - path('document/', get_document_view, name='get-doc'), - path('document//remove/', remove_document_view, name='remove-doc'), + path('/document/', get_document_view, name='get-doc'), + path('/document//remove/', remove_document_view, name='remove-doc'), + path('/document//edit/', edit_document_view, name='edit-doc'), # Deductions path('/deduction/new', new_deduction_view, name='new-deduction'), diff --git a/intervention/views.py b/intervention/views.py index 90447c91..2945c337 100644 --- a/intervention/views.py +++ b/intervention/views.py @@ -6,19 +6,19 @@ from django.shortcuts import render from intervention.forms.forms import NewInterventionForm, EditInterventionForm from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \ - CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm, RemoveEcoAccountDeductionModalForm, \ + CheckModalForm, NewDeductionModalForm, NewInterventionDocumentModalForm, RemoveEcoAccountDeductionModalForm, \ RemoveRevocationModalForm, EditEcoAccountDeductionModalForm, EditRevocationModalForm from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument from intervention.tables import InterventionTable from konova.contexts import BaseContext from konova.decorators import * -from konova.forms import SimpleGeomForm, RemoveModalForm, RecordModalForm +from konova.forms import SimpleGeomForm, RemoveModalForm, RecordModalForm, EditDocumentModalForm from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.utils.documents import remove_document, get_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED, \ CHECKED_RECORDED_RESET, DEDUCTION_REMOVED, DEDUCTION_ADDED, REVOCATION_ADDED, REVOCATION_REMOVED, \ - COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, DEDUCTION_EDITED, REVOCATION_EDITED + COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, DEDUCTION_EDITED, REVOCATION_EDITED, DOCUMENT_EDITED from konova.utils.user_checks import in_group @@ -129,7 +129,7 @@ def new_document_view(request: HttpRequest, id: str): """ intervention = get_object_or_404(Intervention, id=id) - form = NewInterventionDocumentForm(request.POST or None, request.FILES or None, instance=intervention, request=request) + form = NewInterventionDocumentModalForm(request.POST or None, request.FILES or None, instance=intervention, request=request) return form.process_request( request, msg_success=DOCUMENT_ADDED, @@ -164,18 +164,21 @@ def get_revocation_view(request: HttpRequest, doc_id: str): @login_required @default_group_required -def get_document_view(request: HttpRequest, doc_id: str): +@shared_access_required(Intervention, "id") +def get_document_view(request: HttpRequest, id: str, doc_id: str): """ Returns the document as downloadable file Wraps the generic document fetcher function from konova.utils. Args: request (HttpRequest): The incoming request + id (str): The intervention id doc_id (str): The document id Returns: """ + intervention = get_object_or_404(Intervention, id=id) doc = get_object_or_404(InterventionDocument, id=doc_id) user = request.user instance = doc.instance @@ -191,18 +194,21 @@ def get_document_view(request: HttpRequest, doc_id: str): @login_required @default_group_required -def remove_document_view(request: HttpRequest, doc_id: str): +@shared_access_required(Intervention, "id") +def remove_document_view(request: HttpRequest, id: str, doc_id: str): """ Removes the document from the database and file system Wraps the generic functionality from konova.utils. Args: request (HttpRequest): The incoming request + id (str): The intervention id doc_id (str): The document id Returns: """ + intervention = get_object_or_404(Intervention, id=id) doc = get_object_or_404(InterventionDocument, id=doc_id) return remove_document( request, @@ -210,6 +216,32 @@ def remove_document_view(request: HttpRequest, doc_id: str): ) +@login_required +@default_group_required +@shared_access_required(Intervention, "id") +def edit_document_view(request: HttpRequest, id: str, doc_id: str): + """ Removes the document from the database and file system + + Wraps the generic functionality from konova.utils. + + Args: + request (HttpRequest): The incoming request + id (str): The intervention id + doc_id (str): The document id + + Returns: + + """ + intervention = get_object_or_404(Intervention, id=id) + doc = get_object_or_404(InterventionDocument, id=doc_id) + form = EditDocumentModalForm(request.POST or None, request.FILES or None, instance=intervention, document=doc, request=request) + return form.process_request( + request, + DOCUMENT_EDITED, + redirect_url=reverse("intervention:detail", args=(intervention.id,)) + "#related_data" + ) + + @login_required @any_group_check def detail_view(request: HttpRequest, id: str): diff --git a/konova/forms.py b/konova/forms.py index a842e411..57562f71 100644 --- a/konova/forms.py +++ b/konova/forms.py @@ -12,6 +12,8 @@ from bootstrap_modal_forms.forms import BSModalForm from bootstrap_modal_forms.utils import is_ajax from django import forms from django.contrib import messages +from django.db.models.fields.files import FieldFile + from user.models import User from django.contrib.gis.forms import OSMWidget, MultiPolygonField from django.contrib.gis.geos import MultiPolygon @@ -21,10 +23,10 @@ from django.shortcuts import render from django.utils.translation import gettext_lazy as _ from konova.contexts import BaseContext -from konova.models import BaseObject, Geometry, RecordableObjectMixin +from konova.models import BaseObject, Geometry, RecordableObjectMixin, AbstractDocument from konova.settings import DEFAULT_SRID from konova.tasks import celery_update_parcels -from konova.utils.message_templates import FORM_INVALID, FILE_TYPE_UNSUPPORTED, FILE_SIZE_TOO_LARGE +from konova.utils.message_templates import FORM_INVALID, FILE_TYPE_UNSUPPORTED, FILE_SIZE_TOO_LARGE, DOCUMENT_EDITED from user.models import UserActionLogEntry @@ -348,7 +350,7 @@ class RemoveDeadlineModalForm(RemoveModalForm): self.instance.remove_deadline(self) -class NewDocumentForm(BaseModalForm): +class NewDocumentModalForm(BaseModalForm): """ Modal form for new documents """ @@ -420,6 +422,10 @@ class NewDocumentForm(BaseModalForm): _file = self.cleaned_data.get("file", None) + if _file is None or isinstance(_file, FieldFile): + # FieldFile declares that no new file has been uploaded and we do not need to check on the file again + return super_valid + mime_type_valid = self.document_model.is_mime_type_valid(_file) if not mime_type_valid: self.add_error( @@ -458,6 +464,39 @@ class NewDocumentForm(BaseModalForm): return doc +class EditDocumentModalForm(NewDocumentModalForm): + document = None + document_model = AbstractDocument + + def __init__(self, *args, **kwargs): + self.document = kwargs.pop("document", None) + super().__init__(*args, **kwargs) + form_data = { + "title": self.document.title, + "comment": self.document.comment, + "creation_date": str(self.document.date_of_creation), + "file": self.document.file, + } + self.load_initial_data(form_data) + + + def save(self): + with transaction.atomic(): + document = self.document + file = self.cleaned_data.get("file", None) + + document.title = self.cleaned_data.get("title", None) + document.comment = self.cleaned_data.get("comment", None) + document.date_of_creation = self.cleaned_data.get("creation_date", None) + if not isinstance(file, FieldFile): + document.replace_file(file) + document.save() + + self.instance.mark_as_edited(self.user, self.request, edit_comment=DOCUMENT_EDITED) + + return document + + class RecordModalForm(BaseModalForm): """ Modal form for recording data diff --git a/konova/utils/documents.py b/konova/utils/documents.py index 63a7d328..f9b15160 100644 --- a/konova/utils/documents.py +++ b/konova/utils/documents.py @@ -50,5 +50,5 @@ def remove_document(request: HttpRequest, doc: AbstractDocument): form = RemoveModalForm(request.POST or None, instance=doc, request=request) return form.process_request( request=request, - msg_success=DOCUMENT_REMOVED_TEMPLATE.format(title) + msg_success=DOCUMENT_REMOVED_TEMPLATE.format(title), ) \ No newline at end of file diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index 2196dd9c..c7a2be00 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -65,6 +65,7 @@ REVOCATION_REMOVED = _("Revocation removed") # DOCUMENTS DOCUMENT_REMOVED_TEMPLATE = _("Document '{}' deleted") DOCUMENT_ADDED = _("Document added") +DOCUMENT_EDITED = _("Document edited") # Edited EDITED_GENERAL_DATA = _("Edited general data") diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 86cf2739bfdda76a4b610efe974b54aee72f0ad0..f2e51b0088fba3200fe8ff206ddd3043ff1e8a06 100644 GIT binary patch delta 10906 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

hSy;z79n2|b7KOAVQbV%bwNGX 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 diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 67ae2338..488b3d4c 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ #: compensation/forms/modalForms.py:46 compensation/forms/modalForms.py:62 #: compensation/forms/modalForms.py:332 compensation/forms/modalForms.py:425 #: intervention/forms/forms.py:54 intervention/forms/forms.py:156 -#: intervention/forms/forms.py:168 intervention/forms/modalForms.py:124 -#: intervention/forms/modalForms.py:137 intervention/forms/modalForms.py:150 +#: intervention/forms/forms.py:168 intervention/forms/modalForms.py:127 +#: intervention/forms/modalForms.py:140 intervention/forms/modalForms.py:153 #: konova/filters/mixins.py:53 konova/filters/mixins.py:54 #: konova/filters/mixins.py:81 konova/filters/mixins.py:82 #: konova/filters/mixins.py:94 konova/filters/mixins.py:95 @@ -18,15 +18,15 @@ #: konova/filters/mixins.py:270 konova/filters/mixins.py:315 #: konova/filters/mixins.py:353 konova/filters/mixins.py:354 #: konova/filters/mixins.py:385 konova/filters/mixins.py:386 -#: konova/forms.py:141 konova/forms.py:242 konova/forms.py:313 -#: konova/forms.py:357 konova/forms.py:367 konova/forms.py:380 -#: konova/forms.py:392 konova/forms.py:410 user/forms.py:42 +#: konova/forms.py:143 konova/forms.py:244 konova/forms.py:315 +#: konova/forms.py:359 konova/forms.py:369 konova/forms.py:382 +#: konova/forms.py:394 konova/forms.py:412 user/forms.py:42 #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-02-09 12:52+0100\n" +"POT-Creation-Date: 2022-02-10 10:17+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -63,7 +63,7 @@ msgstr "Verantwortliche Stelle" #: analysis/forms.py:58 compensation/forms/forms.py:88 #: compensation/forms/forms.py:165 intervention/forms/forms.py:64 #: intervention/forms/forms.py:81 intervention/forms/forms.py:97 -#: intervention/forms/forms.py:113 intervention/forms/modalForms.py:46 +#: intervention/forms/forms.py:113 intervention/forms/modalForms.py:49 msgid "Click for selection" msgstr "Auswählen..." @@ -75,7 +75,7 @@ msgstr "Bericht generieren" msgid "Select a timespan and the desired conservation office" msgstr "Wählen Sie die Zeitspanne und die gewünschte Eintragungsstelle" -#: analysis/forms.py:69 konova/forms.py:189 +#: analysis/forms.py:69 konova/forms.py:191 msgid "Continue" msgstr "Weiter" @@ -220,7 +220,7 @@ msgstr "Abbuchungen" #: compensation/templates/compensation/detail/eco_account/includes/states-before.html:36 #: ema/templates/ema/detail/includes/states-after.html:36 #: ema/templates/ema/detail/includes/states-before.html:36 -#: intervention/forms/modalForms.py:311 +#: intervention/forms/modalForms.py:338 msgid "Surface" msgstr "Fläche" @@ -283,8 +283,8 @@ msgid "Type" msgstr "Typ" #: analysis/templates/analysis/reports/includes/old_data/amount.html:24 -#: compensation/tables.py:89 intervention/forms/modalForms.py:322 -#: intervention/forms/modalForms.py:329 intervention/tables.py:88 +#: compensation/tables.py:89 intervention/forms/modalForms.py:349 +#: intervention/forms/modalForms.py:356 intervention/tables.py:88 #: intervention/templates/intervention/detail/view.html:19 #: konova/templates/konova/includes/quickstart/interventions.html:4 #: templates/navbars/navbar.html:22 @@ -294,7 +294,7 @@ msgstr "Eingriff" #: analysis/templates/analysis/reports/includes/old_data/amount.html:34 #: compensation/tables.py:266 #: compensation/templates/compensation/detail/eco_account/view.html:19 -#: intervention/forms/modalForms.py:295 intervention/forms/modalForms.py:302 +#: intervention/forms/modalForms.py:322 intervention/forms/modalForms.py:329 #: konova/templates/konova/includes/quickstart/ecoaccounts.html:4 #: templates/navbars/navbar.html:34 msgid "Eco-account" @@ -340,7 +340,7 @@ msgstr "Automatisch generiert" #: intervention/templates/intervention/detail/includes/documents.html:28 #: intervention/templates/intervention/detail/view.html:31 #: intervention/templates/intervention/report/report.html:12 -#: konova/forms.py:356 +#: konova/forms.py:358 msgid "Title" msgstr "Bezeichnung" @@ -363,11 +363,11 @@ msgstr "Kompensation XY; Flur ABC" #: ema/templates/ema/detail/includes/actions.html:34 #: ema/templates/ema/detail/includes/deadlines.html:34 #: ema/templates/ema/detail/includes/documents.html:31 -#: intervention/forms/forms.py:180 intervention/forms/modalForms.py:149 -#: intervention/templates/intervention/detail/includes/documents.html:31 +#: intervention/forms/forms.py:180 intervention/forms/modalForms.py:152 +#: intervention/templates/intervention/detail/includes/documents.html:34 #: intervention/templates/intervention/detail/includes/payments.html:34 #: intervention/templates/intervention/detail/includes/revocation.html:38 -#: konova/forms.py:391 konova/templates/konova/includes/comment_card.html:16 +#: konova/forms.py:393 konova/templates/konova/includes/comment_card.html:16 msgid "Comment" msgstr "Kommentar" @@ -483,7 +483,7 @@ msgid "Due on which date" msgstr "Zahlung wird an diesem Datum erwartet" #: compensation/forms/modalForms.py:63 compensation/forms/modalForms.py:333 -#: intervention/forms/modalForms.py:151 konova/forms.py:393 +#: intervention/forms/modalForms.py:154 konova/forms.py:395 msgid "Additional comment, maximum {} letters" msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" @@ -511,7 +511,7 @@ msgstr "Zusatzbezeichnung" msgid "Select an additional biotope type" msgstr "Zusatzbezeichnung wählen" -#: compensation/forms/modalForms.py:196 intervention/forms/modalForms.py:313 +#: compensation/forms/modalForms.py:196 intervention/forms/modalForms.py:340 msgid "in m²" msgstr "" @@ -523,7 +523,7 @@ msgstr "Neuer Zustand" msgid "Insert data for the new state" msgstr "Geben Sie die Daten des neuen Zustandes ein" -#: compensation/forms/modalForms.py:215 konova/forms.py:191 +#: compensation/forms/modalForms.py:215 konova/forms.py:193 msgid "Object removed" msgstr "Objekt entfernt" @@ -539,7 +539,7 @@ msgstr "Fristart wählen" #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:31 #: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31 #: ema/templates/ema/detail/includes/deadlines.html:31 -#: intervention/forms/modalForms.py:123 +#: intervention/forms/modalForms.py:126 msgid "Date" msgstr "Datum" @@ -582,7 +582,7 @@ msgstr "Maßnahmentyp wählen" #: ema/templates/ema/detail/includes/states-before.html:40 #: intervention/templates/intervention/detail/includes/compensations.html:38 #: intervention/templates/intervention/detail/includes/deductions.html:39 -#: intervention/templates/intervention/detail/includes/documents.html:36 +#: intervention/templates/intervention/detail/includes/documents.html:39 #: intervention/templates/intervention/detail/includes/payments.html:39 #: intervention/templates/intervention/detail/includes/revocation.html:43 #: templates/log.html:10 @@ -821,14 +821,14 @@ msgstr "Dokumente" #: compensation/templates/compensation/detail/eco_account/includes/documents.html:14 #: ema/templates/ema/detail/includes/documents.html:14 #: intervention/templates/intervention/detail/includes/documents.html:14 -#: konova/forms.py:409 +#: konova/forms.py:411 msgid "Add new document" msgstr "Neues Dokument hinzufügen" #: compensation/templates/compensation/detail/compensation/includes/documents.html:57 #: compensation/templates/compensation/detail/eco_account/includes/documents.html:55 #: ema/templates/ema/detail/includes/documents.html:55 -#: intervention/templates/intervention/detail/includes/documents.html:57 +#: intervention/templates/intervention/detail/includes/documents.html:68 msgid "Remove document" msgstr "Dokument löschen" @@ -943,14 +943,14 @@ msgstr "Zuletzt bearbeitet" #: compensation/templates/compensation/detail/compensation/view.html:99 #: compensation/templates/compensation/detail/eco_account/view.html:82 -#: ema/templates/ema/detail/view.html:76 intervention/forms/modalForms.py:53 +#: ema/templates/ema/detail/view.html:76 intervention/forms/modalForms.py:56 #: intervention/templates/intervention/detail/view.html:116 msgid "Shared with" msgstr "Freigegeben für" #: compensation/templates/compensation/detail/eco_account/includes/controls.html:15 #: ema/templates/ema/detail/includes/controls.html:15 -#: intervention/forms/modalForms.py:67 +#: intervention/forms/modalForms.py:70 #: intervention/templates/intervention/detail/includes/controls.html:15 msgid "Share" msgstr "Freigabe" @@ -992,6 +992,11 @@ msgid "Recorded on" msgstr "Verzeichnet am" #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:65 +#: intervention/templates/intervention/detail/includes/deductions.html:60 +msgid "Edit Deduction" +msgstr "Abbuchung bearbeiten" + +#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:68 #: intervention/templates/intervention/detail/includes/deductions.html:63 msgid "Remove Deduction" msgstr "Abbuchung entfernen" @@ -1080,22 +1085,22 @@ msgstr "Daten zu den verantwortlichen Stellen" msgid "Compensations - Overview" msgstr "Kompensationen - Übersicht" -#: compensation/views/compensation.py:149 konova/utils/message_templates.py:27 +#: compensation/views/compensation.py:149 konova/utils/message_templates.py:31 msgid "Compensation {} edited" msgstr "Kompensation {} bearbeitet" #: compensation/views/compensation.py:159 compensation/views/eco_account.py:163 -#: ema/views.py:230 intervention/views.py:305 +#: ema/views.py:230 intervention/views.py:337 msgid "Edit {}" msgstr "Bearbeite {}" #: compensation/views/compensation.py:238 compensation/views/eco_account.py:347 -#: ema/views.py:191 intervention/views.py:483 +#: ema/views.py:191 intervention/views.py:541 msgid "Log" msgstr "Log" #: compensation/views/compensation.py:487 compensation/views/eco_account.py:620 -#: ema/views.py:477 intervention/views.py:629 +#: ema/views.py:477 intervention/views.py:687 msgid "Report {}" msgstr "Bericht {}" @@ -1116,32 +1121,32 @@ msgid "Eco-account removed" msgstr "Ökokonto entfernt" #: compensation/views/eco_account.py:368 ema/views.py:272 -#: intervention/views.py:582 +#: intervention/views.py:640 msgid "{} unrecorded" msgstr "{} entzeichnet" #: compensation/views/eco_account.py:368 ema/views.py:272 -#: intervention/views.py:582 +#: intervention/views.py:640 msgid "{} recorded" msgstr "{} verzeichnet" #: compensation/views/eco_account.py:693 ema/views.py:543 -#: intervention/views.py:380 +#: intervention/views.py:438 msgid "{} has already been shared with you" msgstr "{} wurde bereits für Sie freigegeben" #: compensation/views/eco_account.py:698 ema/views.py:548 -#: intervention/views.py:385 +#: intervention/views.py:443 msgid "{} has been shared with you" msgstr "{} ist nun für Sie freigegeben" #: compensation/views/eco_account.py:705 ema/views.py:555 -#: intervention/views.py:392 +#: intervention/views.py:450 msgid "Share link invalid" msgstr "Freigabelink ungültig" #: compensation/views/eco_account.py:728 ema/views.py:578 -#: intervention/views.py:415 +#: intervention/views.py:473 msgid "Share settings updated" msgstr "Freigabe Einstellungen aktualisiert" @@ -1254,19 +1259,19 @@ msgstr "Neuer Eingriff" msgid "Edit intervention" msgstr "Eingriff bearbeiten" -#: intervention/forms/modalForms.py:26 +#: intervention/forms/modalForms.py:29 msgid "Share link" msgstr "Freigabelink" -#: intervention/forms/modalForms.py:28 +#: intervention/forms/modalForms.py:31 msgid "Send this link to users who you want to have writing access on the data" msgstr "Andere Nutzer erhalten über diesen Link Zugriff auf die Daten" -#: intervention/forms/modalForms.py:38 +#: intervention/forms/modalForms.py:41 msgid "Add user to share with" msgstr "Nutzer direkt hinzufügen" -#: intervention/forms/modalForms.py:40 +#: intervention/forms/modalForms.py:43 msgid "" "Multiple selection possible - You can only select users which do not already " "have access. Enter the full username." @@ -1274,46 +1279,46 @@ msgstr "" "Mehrfachauswahl möglich - Sie können nur Nutzer wählen, für die der Eintrag " "noch nicht freigegeben wurde. Geben Sie den ganzen Nutzernamen an." -#: intervention/forms/modalForms.py:56 +#: intervention/forms/modalForms.py:59 msgid "Remove check to remove access for this user" msgstr "Wählen Sie die Nutzer ab, die keinen Zugriff mehr haben sollen" -#: intervention/forms/modalForms.py:68 +#: intervention/forms/modalForms.py:71 msgid "Share settings for {}" msgstr "Freigabe Einstellungen für {}" -#: intervention/forms/modalForms.py:125 +#: intervention/forms/modalForms.py:128 msgid "Date of revocation" msgstr "Datum des Widerspruchs" -#: intervention/forms/modalForms.py:136 +#: intervention/forms/modalForms.py:139 #: intervention/templates/intervention/detail/includes/revocation.html:35 msgid "Document" msgstr "Dokument" -#: intervention/forms/modalForms.py:139 +#: intervention/forms/modalForms.py:142 msgid "Must be smaller than 15 Mb" msgstr "Muss kleiner als 15 Mb sein" -#: intervention/forms/modalForms.py:163 +#: intervention/forms/modalForms.py:167 #: intervention/templates/intervention/detail/includes/revocation.html:18 msgid "Add revocation" msgstr "Widerspruch hinzufügen" -#: intervention/forms/modalForms.py:197 +#: intervention/forms/modalForms.py:224 msgid "Checked intervention data" msgstr "Eingriffsdaten geprüft" -#: intervention/forms/modalForms.py:203 +#: intervention/forms/modalForms.py:230 msgid "Checked compensations data and payments" msgstr "Kompensationen und Zahlungen geprüft" -#: intervention/forms/modalForms.py:212 +#: intervention/forms/modalForms.py:239 #: intervention/templates/intervention/detail/includes/controls.html:19 msgid "Run check" msgstr "Prüfung vornehmen" -#: intervention/forms/modalForms.py:213 konova/forms.py:475 +#: intervention/forms/modalForms.py:240 konova/forms.py:514 msgid "" "I, {} {}, confirm that all necessary control steps have been performed by " "myself." @@ -1321,23 +1326,23 @@ msgstr "" "Ich, {} {}, bestätige, dass die notwendigen Kontrollschritte durchgeführt " "wurden:" -#: intervention/forms/modalForms.py:297 +#: intervention/forms/modalForms.py:324 msgid "Only recorded accounts can be selected for deductions" msgstr "Nur verzeichnete Ökokonten können für Abbuchungen verwendet werden." -#: intervention/forms/modalForms.py:324 +#: intervention/forms/modalForms.py:351 msgid "Only shared interventions can be selected" msgstr "Nur freigegebene Eingriffe können gewählt werden" -#: intervention/forms/modalForms.py:337 +#: intervention/forms/modalForms.py:364 msgid "New Deduction" msgstr "Neue Abbuchung" -#: intervention/forms/modalForms.py:338 +#: intervention/forms/modalForms.py:365 msgid "Enter the information for a new deduction from a chosen eco-account" msgstr "Geben Sie die Informationen für eine neue Abbuchung ein." -#: intervention/forms/modalForms.py:381 +#: intervention/forms/modalForms.py:408 msgid "" "Eco-account {} is not recorded yet. You can only deduct from recorded " "accounts." @@ -1345,7 +1350,7 @@ msgstr "" "Ökokonto {} ist noch nicht verzeichnet. Abbuchungen können nur von " "verzeichneten Ökokonten erfolgen." -#: intervention/forms/modalForms.py:391 +#: intervention/forms/modalForms.py:418 msgid "" "The account {} has not enough surface for a deduction of {} m². There are " "only {} m² left" @@ -1373,9 +1378,14 @@ msgstr "Ökokonto gelöscht! Abbuchung ungültig!" msgid "Eco-account not recorded! Deduction invalid!" msgstr "Ökokonto nicht verzeichnet! Abbuchung ungültig!" -#: intervention/templates/intervention/detail/includes/deductions.html:60 -msgid "Edit Deduction" -msgstr "Abbuchung bearbeiten" +#: intervention/templates/intervention/detail/includes/documents.html:31 +#: konova/forms.py:368 +msgid "Created on" +msgstr "Erstellt" + +#: intervention/templates/intervention/detail/includes/documents.html:65 +msgid "Edit document" +msgstr "Dokument bearbeitet" #: intervention/templates/intervention/detail/includes/payments.html:8 #: intervention/templates/intervention/report/report.html:73 @@ -1414,6 +1424,10 @@ msgid "Revocation" msgstr "Widerspruch" #: intervention/templates/intervention/detail/includes/revocation.html:69 +msgid "Edit revocation" +msgstr "Widerspruch bearbeiten" + +#: intervention/templates/intervention/detail/includes/revocation.html:72 msgid "Remove revocation" msgstr "Widerspruch entfernen" @@ -1456,19 +1470,19 @@ msgstr "Eingriffe - Übersicht" msgid "Intervention {} added" msgstr "Eingriff {} hinzugefügt" -#: intervention/views.py:293 +#: intervention/views.py:325 msgid "Intervention {} edited" msgstr "Eingriff {} bearbeitet" -#: intervention/views.py:329 +#: intervention/views.py:361 msgid "{} removed" msgstr "{} entfernt" -#: intervention/views.py:436 +#: intervention/views.py:494 msgid "Check performed" msgstr "Prüfung durchgeführt" -#: intervention/views.py:587 +#: intervention/views.py:645 msgid "There are errors on this intervention:" msgstr "Es liegen Fehler in diesem Eingriff vor:" @@ -1552,81 +1566,69 @@ msgstr "Nach Zulassungsbehörde suchen" msgid "Search for conservation office" msgstr "Nch Eintragungsstelle suchen" -#: konova/forms.py:37 templates/form/collapsable/form.html:62 +#: konova/forms.py:39 templates/form/collapsable/form.html:62 msgid "Save" msgstr "Speichern" -#: konova/forms.py:69 +#: konova/forms.py:71 msgid "Not editable" msgstr "Nicht editierbar" -#: konova/forms.py:140 konova/forms.py:312 +#: konova/forms.py:142 konova/forms.py:314 msgid "Confirm" msgstr "Bestätige" -#: konova/forms.py:152 konova/forms.py:321 +#: konova/forms.py:154 konova/forms.py:323 msgid "Remove" msgstr "Löschen" -#: konova/forms.py:154 +#: konova/forms.py:156 msgid "You are about to remove {} {}" msgstr "Sie sind dabei {} {} zu löschen" -#: konova/forms.py:241 konova/utils/quality.py:44 konova/utils/quality.py:46 +#: konova/forms.py:243 konova/utils/quality.py:44 konova/utils/quality.py:46 #: templates/form/collapsable/form.html:45 msgid "Geometry" msgstr "Geometrie" -#: konova/forms.py:322 +#: konova/forms.py:324 msgid "Are you sure?" msgstr "Sind Sie sicher?" -#: konova/forms.py:366 -msgid "Created on" -msgstr "Erstellt" - -#: konova/forms.py:368 +#: konova/forms.py:370 msgid "When has this file been created? Important for photos." msgstr "Wann wurde diese Datei erstellt oder das Foto aufgenommen?" -#: konova/forms.py:379 +#: konova/forms.py:381 #: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231 msgid "File" msgstr "Datei" -#: konova/forms.py:381 +#: konova/forms.py:383 msgid "Allowed formats: pdf, jpg, png. Max size 15 MB." msgstr "Formate: pdf, jpg, png. Maximal 15 MB." -#: konova/forms.py:427 -msgid "Unsupported file type" -msgstr "Dateiformat nicht unterstützt" - -#: konova/forms.py:434 -msgid "File too large" -msgstr "Datei zu groß" - -#: konova/forms.py:443 +#: konova/forms.py:449 msgid "Added document" msgstr "Dokument hinzugefügt" -#: konova/forms.py:466 +#: konova/forms.py:505 msgid "Confirm record" msgstr "Verzeichnen bestätigen" -#: konova/forms.py:474 +#: konova/forms.py:513 msgid "Record data" msgstr "Daten verzeichnen" -#: konova/forms.py:481 +#: konova/forms.py:520 msgid "Confirm unrecord" msgstr "Entzeichnen bestätigen" -#: konova/forms.py:482 +#: konova/forms.py:521 msgid "Unrecord data" msgstr "Daten entzeichnen" -#: konova/forms.py:483 +#: konova/forms.py:522 msgid "I, {} {}, confirm that this data must be unrecorded." msgstr "" "Ich, {} {}, bestätige, dass diese Daten wieder entzeichnet werden müssen." @@ -1800,6 +1802,14 @@ msgid "Status of Checked and Recorded reseted" msgstr "'Geprüft'/'Verzeichnet' wurde zurückgesetzt" #: konova/utils/message_templates.py:22 +msgid "Unsupported file type" +msgstr "Dateiformat nicht unterstützt" + +#: konova/utils/message_templates.py:23 +msgid "File too large" +msgstr "Datei zu groß" + +#: konova/utils/message_templates.py:26 msgid "" "Action canceled. Eco account is recorded or deductions exist. Only " "conservation office member can perform this action." @@ -1807,115 +1817,119 @@ msgstr "" "Aktion abgebrochen. Ökokonto ist bereits verzeichnet oder Abbuchungen liegen " "vor. Nur Eintragungsstellennutzer können diese Aktion jetzt durchführen." -#: konova/utils/message_templates.py:25 +#: konova/utils/message_templates.py:29 msgid "Compensation {} added" msgstr "Kompensation {} hinzugefügt" -#: konova/utils/message_templates.py:26 +#: konova/utils/message_templates.py:30 msgid "Compensation {} removed" msgstr "Kompensation {} entfernt" -#: konova/utils/message_templates.py:28 +#: konova/utils/message_templates.py:32 msgid "Added compensation action" msgstr "Maßnahme hinzugefügt" -#: konova/utils/message_templates.py:29 +#: konova/utils/message_templates.py:33 msgid "Added compensation state" msgstr "Zustand hinzugefügt" -#: konova/utils/message_templates.py:32 +#: konova/utils/message_templates.py:36 msgid "State removed" msgstr "Zustand gelöscht" -#: konova/utils/message_templates.py:33 +#: konova/utils/message_templates.py:37 msgid "State edited" msgstr "Zustand bearbeitet" -#: konova/utils/message_templates.py:34 +#: konova/utils/message_templates.py:38 msgid "State added" msgstr "Zustand hinzugefügt" -#: konova/utils/message_templates.py:37 +#: konova/utils/message_templates.py:41 msgid "Action added" msgstr "Maßnahme hinzugefügt" -#: konova/utils/message_templates.py:38 +#: konova/utils/message_templates.py:42 msgid "Action edited" msgstr "Maßnahme bearbeitet" -#: konova/utils/message_templates.py:39 +#: konova/utils/message_templates.py:43 msgid "Action removed" msgstr "Maßnahme entfernt" -#: konova/utils/message_templates.py:42 +#: konova/utils/message_templates.py:46 msgid "Deduction added" msgstr "Abbuchung hinzugefügt" -#: konova/utils/message_templates.py:43 +#: konova/utils/message_templates.py:47 msgid "Deduction edited" msgstr "Abbuchung bearbeitet" -#: konova/utils/message_templates.py:44 +#: konova/utils/message_templates.py:48 msgid "Deduction removed" msgstr "Abbuchung entfernt" -#: konova/utils/message_templates.py:47 +#: konova/utils/message_templates.py:51 msgid "Deadline added" msgstr "Frist/Termin hinzugefügt" -#: konova/utils/message_templates.py:48 +#: konova/utils/message_templates.py:52 msgid "Deadline edited" msgstr "Frist/Termin bearbeitet" -#: konova/utils/message_templates.py:49 +#: konova/utils/message_templates.py:53 msgid "Deadline removed" msgstr "Frist/Termin gelöscht" -#: konova/utils/message_templates.py:52 +#: konova/utils/message_templates.py:56 msgid "Payment added" msgstr "Zahlung hinzugefügt" -#: konova/utils/message_templates.py:53 +#: konova/utils/message_templates.py:57 msgid "Payment edited" msgstr "Zahlung bearbeitet" -#: konova/utils/message_templates.py:54 +#: konova/utils/message_templates.py:58 msgid "Payment removed" msgstr "Zahlung gelöscht" -#: konova/utils/message_templates.py:57 +#: konova/utils/message_templates.py:61 msgid "Revocation added" msgstr "Widerspruch hinzugefügt" -#: konova/utils/message_templates.py:58 +#: konova/utils/message_templates.py:62 msgid "Revocation edited" msgstr "Widerspruch bearbeitet" -#: konova/utils/message_templates.py:59 +#: konova/utils/message_templates.py:63 msgid "Revocation removed" msgstr "Widerspruch entfernt" -#: konova/utils/message_templates.py:62 +#: konova/utils/message_templates.py:66 msgid "Document '{}' deleted" msgstr "Dokument '{}' gelöscht" -#: konova/utils/message_templates.py:63 +#: konova/utils/message_templates.py:67 msgid "Document added" msgstr "Dokument hinzugefügt" -#: konova/utils/message_templates.py:66 +#: konova/utils/message_templates.py:68 +msgid "Document edited" +msgstr "Dokument bearbeitet" + +#: konova/utils/message_templates.py:71 msgid "Edited general data" msgstr "Allgemeine Daten bearbeitet" -#: konova/utils/message_templates.py:67 +#: konova/utils/message_templates.py:72 msgid "Added deadline" msgstr "Frist/Termin hinzugefügt" -#: konova/utils/message_templates.py:70 +#: konova/utils/message_templates.py:75 msgid "Geometry conflict detected with {}" msgstr "Geometriekonflikt mit folgenden Einträgen erkannt: {}" -#: konova/utils/message_templates.py:73 +#: konova/utils/message_templates.py:78 msgid "This intervention has {} revocations" msgstr "Dem Eingriff liegen {} Widersprüche vor" From 4a777e4b01c1f877ff4f86384eee296068afac67 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 10:28:41 +0100 Subject: [PATCH 21/31] #86 Edit document EMA * adds buttons and urls for ema --- .../ema/detail/includes/documents.html | 15 +++++-- ema/urls.py | 5 ++- ema/views.py | 41 +++++++++++++++++-- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/ema/templates/ema/detail/includes/documents.html b/ema/templates/ema/detail/includes/documents.html index dbc8927b..7be632d3 100644 --- a/ema/templates/ema/detail/includes/documents.html +++ b/ema/templates/ema/detail/includes/documents.html @@ -27,6 +27,9 @@ {% trans 'Title' %} + + {% trans 'Created on' %} + {% trans 'Comment' %} @@ -41,18 +44,24 @@ {% for doc in obj.documents.all %} - + {{ doc.title }} + + {{ doc.date_of_creation }} +

{{ doc.comment }}
- + {% if is_default_member and has_access %} - + {% endif %} diff --git a/ema/urls.py b/ema/urls.py index 1aa52780..a2ee618f 100644 --- a/ema/urls.py +++ b/ema/urls.py @@ -30,7 +30,8 @@ urlpatterns = [ # Documents path('/document/new/', document_new_view, name='new-doc'), - path('document/', get_document_view, name='get-doc'), - path('document//remove/', remove_document_view, name='remove-doc'), + path('/document/', get_document_view, name='get-doc'), + path('/document//edit/', edit_document_view, name='edit-doc'), + path('/document//remove/', remove_document_view, name='remove-doc'), ] \ No newline at end of file diff --git a/ema/views.py b/ema/views.py index 99c80be3..343953f3 100644 --- a/ema/views.py +++ b/ema/views.py @@ -15,7 +15,8 @@ from intervention.forms.modalForms import ShareModalForm from konova.contexts import BaseContext from konova.decorators import conservation_office_group_required, shared_access_required from ema.models import Ema, EmaDocument -from konova.forms import RemoveModalForm, SimpleGeomForm, RecordModalForm, RemoveDeadlineModalForm +from konova.forms import RemoveModalForm, SimpleGeomForm, RecordModalForm, RemoveDeadlineModalForm, \ + EditDocumentModalForm from konova.models import Deadline from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER @@ -23,7 +24,7 @@ from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, \ - COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED + COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED from konova.utils.user_checks import in_group @@ -366,18 +367,21 @@ def document_new_view(request: HttpRequest, id: str): @login_required @conservation_office_group_required -def get_document_view(request: HttpRequest, doc_id: str): +@shared_access_required(Ema, "id") +def get_document_view(request: HttpRequest, id: str, doc_id: str): """ Returns the document as downloadable file Wraps the generic document fetcher function from konova.utils. Args: request (HttpRequest): The incoming request + id (str): The EMA id doc_id (str): The document id Returns: """ + ema = get_object_or_404(Ema, id=id) doc = get_object_or_404(EmaDocument, id=doc_id) user = request.user instance = doc.instance @@ -393,18 +397,47 @@ def get_document_view(request: HttpRequest, doc_id: str): @login_required @conservation_office_group_required -def remove_document_view(request: HttpRequest, doc_id: str): +@shared_access_required(Ema, "id") +def edit_document_view(request: HttpRequest, id: str, doc_id: str): """ Removes the document from the database and file system Wraps the generic functionality from konova.utils. Args: request (HttpRequest): The incoming request + id (str): The EMA id doc_id (str): The document id Returns: """ + ema = get_object_or_404(Ema, id=id) + doc = get_object_or_404(EmaDocument, id=doc_id) + form = EditDocumentModalForm(request.POST or None, request.FILES or None, instance=ema, document=doc, request=request) + return form.process_request( + request, + DOCUMENT_EDITED, + reverse("ema:detail", args=(id,)) + "#related_data" + ) + + +@login_required +@conservation_office_group_required +@shared_access_required(Ema, "id") +def remove_document_view(request: HttpRequest, id:str, doc_id: str): + """ Removes the document from the database and file system + + Wraps the generic functionality from konova.utils. + + Args: + request (HttpRequest): The incoming request + id (str): The EMA id + doc_id (str): The document id + + Returns: + + """ + ema = get_object_or_404(Ema, id=id) doc = get_object_or_404(EmaDocument, id=doc_id) return remove_document( request, From 3ded54b80f2b977ecdfb37d0c3d74d8ac7b8963c Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 10:44:44 +0100 Subject: [PATCH 22/31] #86 Edit document Compensation * adds support for editing of documents * adds buttons and urls for compensation * simplifies getter for all documents --- .../compensation/includes/documents.html | 15 ++++-- compensation/urls/compensation.py | 5 +- compensation/views/compensation.py | 49 ++++++++++++++----- ema/views.py | 9 ---- intervention/views.py | 10 ---- 5 files changed, 51 insertions(+), 37 deletions(-) diff --git a/compensation/templates/compensation/detail/compensation/includes/documents.html b/compensation/templates/compensation/detail/compensation/includes/documents.html index 0615d4be..e12de4e9 100644 --- a/compensation/templates/compensation/detail/compensation/includes/documents.html +++ b/compensation/templates/compensation/detail/compensation/includes/documents.html @@ -27,6 +27,9 @@ {% trans 'Title' %} + + {% trans 'Created on' %} + {% trans 'Comment' %} @@ -43,18 +46,24 @@ {% for doc in obj.documents.all %} - + {{ doc.title }} + + {{ doc.date_of_creation }} +
{{ doc.comment }}
- + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/urls/compensation.py b/compensation/urls/compensation.py index 9c23117b..ea50b011 100644 --- a/compensation/urls/compensation.py +++ b/compensation/urls/compensation.py @@ -28,7 +28,8 @@ urlpatterns = [ # Documents path('/document/new/', new_document_view, name='new-doc'), - path('document/', get_document_view, name='get-doc'), - path('document//remove/', remove_document_view, name='remove-doc'), + path('/document/', get_document_view, name='get-doc'), + path('/document//remove/', remove_document_view, name='remove-doc'), + path('/document//edit/', edit_document_view, name='edit-doc'), ] \ No newline at end of file diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py index 95f4ac0b..40ea9335 100644 --- a/compensation/views/compensation.py +++ b/compensation/views/compensation.py @@ -12,7 +12,7 @@ from compensation.tables import CompensationTable from intervention.models import Intervention from konova.contexts import BaseContext from konova.decorators import * -from konova.forms import RemoveModalForm, SimpleGeomForm, RemoveDeadlineModalForm +from konova.forms import RemoveModalForm, SimpleGeomForm, RemoveDeadlineModalForm, EditDocumentModalForm from konova.models import Deadline from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.utils.documents import get_document, remove_document @@ -20,7 +20,7 @@ from konova.utils.generators import generate_qr_code from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION, \ CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \ COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, \ - DEADLINE_ADDED, DEADLINE_REMOVED + DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED from konova.utils.user_checks import in_group @@ -286,45 +286,42 @@ def new_document_view(request: HttpRequest, id: str): @login_required @default_group_required -def get_document_view(request: HttpRequest, doc_id: str): +@shared_access_required(Compensation, "id") +def get_document_view(request: HttpRequest, id: str, doc_id: str): """ Returns the document as downloadable file Wraps the generic document fetcher function from konova.utils. Args: request (HttpRequest): The incoming request + id (str): The compensation id doc_id (str): The document id Returns: """ + comp = get_object_or_404(Compensation, id=id) doc = get_object_or_404(CompensationDocument, id=doc_id) - user = request.user - instance = doc.instance - # File download only possible if related instance is shared with user - if not instance.users.filter(id=user.id): - messages.info( - request, - DATA_UNSHARED - ) - return redirect("compensation:detail", id=instance.id) return get_document(doc) @login_required @default_group_required -def remove_document_view(request: HttpRequest, doc_id: str): +@shared_access_required(Compensation, "id") +def remove_document_view(request: HttpRequest, id: str, doc_id: str): """ Removes the document from the database and file system Wraps the generic functionality from konova.utils. Args: request (HttpRequest): The incoming request + id (str): The compensation id doc_id (str): The document id Returns: """ + comp = get_object_or_404(Compensation, id=id) doc = get_object_or_404(CompensationDocument, id=doc_id) return remove_document( request, @@ -332,6 +329,32 @@ def remove_document_view(request: HttpRequest, doc_id: str): ) +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def edit_document_view(request: HttpRequest, id: str, doc_id: str): + """ Removes the document from the database and file system + + Wraps the generic functionality from konova.utils. + + Args: + request (HttpRequest): The incoming request + id (str): The compensation id + doc_id (str): The document id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + doc = get_object_or_404(CompensationDocument, id=doc_id) + form = EditDocumentModalForm(request.POST or None, request.FILES or None, instance=comp, document=doc, request=request) + return form.process_request( + request, + DOCUMENT_EDITED, + reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + @login_required @default_group_required @shared_access_required(Compensation, "id") diff --git a/ema/views.py b/ema/views.py index 343953f3..0650f4c9 100644 --- a/ema/views.py +++ b/ema/views.py @@ -383,15 +383,6 @@ def get_document_view(request: HttpRequest, id: str, doc_id: str): """ ema = get_object_or_404(Ema, id=id) doc = get_object_or_404(EmaDocument, id=doc_id) - user = request.user - instance = doc.instance - # File download only possible if related instance is shared with user - if not instance.users.filter(id=user.id): - messages.info( - request, - DATA_UNSHARED - ) - return redirect("ema:detail", id=instance.id) return get_document(doc) diff --git a/intervention/views.py b/intervention/views.py index 2945c337..00440bb8 100644 --- a/intervention/views.py +++ b/intervention/views.py @@ -161,7 +161,6 @@ def get_revocation_view(request: HttpRequest, doc_id: str): return redirect("intervention:detail", id=doc.instance.id) return get_document(doc) - @login_required @default_group_required @shared_access_required(Intervention, "id") @@ -180,15 +179,6 @@ def get_document_view(request: HttpRequest, id: str, doc_id: str): """ intervention = get_object_or_404(Intervention, id=id) doc = get_object_or_404(InterventionDocument, id=doc_id) - user = request.user - instance = doc.instance - # File download only possible if related instance is shared with user - if not instance.users.filter(id=user.id): - messages.info( - request, - DATA_UNSHARED - ) - return redirect("intervention:detail", id=instance.id) return get_document(doc) From fe842cb9bcdc32e2c068c2d69b375938b612c542 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 10:51:52 +0100 Subject: [PATCH 23/31] #86 Edit document EcoAccount * adds support for editing of documents * adds buttons and urls for ecoaccount --- .../eco_account/includes/documents.html | 15 ++++-- compensation/urls/eco_account.py | 5 +- compensation/views/eco_account.py | 49 ++++++++++++++----- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/compensation/templates/compensation/detail/eco_account/includes/documents.html b/compensation/templates/compensation/detail/eco_account/includes/documents.html index b3895eac..e852842b 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/documents.html +++ b/compensation/templates/compensation/detail/eco_account/includes/documents.html @@ -27,6 +27,9 @@ {% trans 'Title' %} + + {% trans 'Created on' %} + {% trans 'Comment' %} @@ -41,18 +44,24 @@ {% for doc in obj.documents.all %} - + {{ doc.title }} + + {{ doc.date_of_creation }} +
{{ doc.comment }}
- + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/urls/eco_account.py b/compensation/urls/eco_account.py index 0c3a0293..e390d4fb 100644 --- a/compensation/urls/eco_account.py +++ b/compensation/urls/eco_account.py @@ -30,8 +30,9 @@ urlpatterns = [ # Documents path('/document/new/', new_document_view, name='new-doc'), - path('document/', get_document_view, name='get-doc'), - path('document//remove/', remove_document_view, name='remove-doc'), + path('/document/', get_document_view, name='get-doc'), + path('/document//edit', edit_document_view, name='edit-doc'), + path('/document//remove/', remove_document_view, name='remove-doc'), # Eco-account deductions path('/deduction//remove', deduction_remove_view, name='remove-deduction'), diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index d433d551..19959f81 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -24,7 +24,8 @@ from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, from konova.contexts import BaseContext from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \ shared_access_required -from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentModalForm, RecordModalForm, RemoveDeadlineModalForm +from konova.forms import RemoveModalForm, SimpleGeomForm, NewDocumentModalForm, RecordModalForm, \ + RemoveDeadlineModalForm, EditDocumentModalForm from konova.models import Deadline from konova.settings import DEFAULT_GROUP, ZB_GROUP, ETS_GROUP from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER @@ -33,7 +34,7 @@ from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \ COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, \ - DEDUCTION_EDITED + DEDUCTION_EDITED, DOCUMENT_EDITED from konova.utils.user_checks import in_group @@ -534,46 +535,68 @@ def new_document_view(request: HttpRequest, id: str): @login_required @default_group_required -def get_document_view(request: HttpRequest, doc_id: str): +@shared_access_required(EcoAccount, "id") +def get_document_view(request: HttpRequest, id:str, doc_id: str): """ Returns the document as downloadable file Wraps the generic document fetcher function from konova.utils. Args: request (HttpRequest): The incoming request + id (str): The account id doc_id (str): The document id Returns: """ + acc = get_object_or_404(EcoAccount, id=id) doc = get_object_or_404(EcoAccountDocument, id=doc_id) - user = request.user - instance = doc.instance - # File download only possible if related instance is shared with user - if not instance.users.filter(id=user.id): - messages.info( - request, - DATA_UNSHARED - ) - return redirect("compensation:acc:detail", id=instance.id) return get_document(doc) @login_required @default_group_required @shared_access_required(EcoAccount, "id") -def remove_document_view(request: HttpRequest, doc_id: str): +def edit_document_view(request: HttpRequest, id: str, doc_id: str): """ Removes the document from the database and file system Wraps the generic functionality from konova.utils. Args: request (HttpRequest): The incoming request + id (str): The account id doc_id (str): The document id Returns: """ + acc = get_object_or_404(EcoAccount, id=id) + doc = get_object_or_404(EcoAccountDocument, id=doc_id) + form = EditDocumentModalForm(request.POST or None, request.FILES or None, instance=acc, document=doc, request=request) + return form.process_request( + request, + DOCUMENT_EDITED, + reverse("compensation:acc:detail", args=(id,)) + "#related_data" + ) + + +@login_required +@default_group_required +@shared_access_required(EcoAccount, "id") +def remove_document_view(request: HttpRequest, id: str, doc_id: str): + """ Removes the document from the database and file system + + Wraps the generic functionality from konova.utils. + + Args: + request (HttpRequest): The incoming request + id (str): The account id + doc_id (str): The document id + + Returns: + + """ + acc = get_object_or_404(EcoAccount, id=id) doc = get_object_or_404(EcoAccountDocument, id=doc_id) return remove_document( request, From 0c261196a4dfc217df3c779021de537c6002070b Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 11:02:30 +0100 Subject: [PATCH 24/31] #86 Edit states compensation * adds support for editing of states * adds buttons and urls for compensation --- compensation/forms/modalForms.py | 25 +++++++++++++++- .../compensation/includes/states-after.html | 7 +++-- .../compensation/includes/states-before.html | 7 +++-- compensation/urls/compensation.py | 1 + compensation/views/compensation.py | 29 +++++++++++++++++-- 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/compensation/forms/modalForms.py b/compensation/forms/modalForms.py index 5368077c..ddf8502b 100644 --- a/compensation/forms/modalForms.py +++ b/compensation/forms/modalForms.py @@ -21,7 +21,7 @@ from konova.contexts import BaseContext from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm from konova.models import DeadlineType from konova.utils.message_templates import FORM_INVALID, ADDED_COMPENSATION_STATE, ADDED_DEADLINE, \ - ADDED_COMPENSATION_ACTION, PAYMENT_EDITED + ADDED_COMPENSATION_ACTION, PAYMENT_EDITED, COMPENSATION_STATE_EDITED class NewPaymentForm(BaseModalForm): @@ -261,6 +261,29 @@ class NewStateModalForm(BaseModalForm): raise NotImplementedError +class EditCompensationStateModalForm(NewStateModalForm): + state = None + + def __init__(self, *args, **kwargs): + self.state = kwargs.pop("state", None) + super().__init__(*args, **kwargs) + form_data = { + "biotope_type": self.state.biotope_type, + "biotope_extra": self.state.biotope_type_details.all(), + "surface": self.state.surface, + } + self.load_initial_data(form_data) + + def save(self, is_before_state: bool = False): + state = self.state + state.biotope_type = self.cleaned_data.get("biotope_type", None) + state.biotope_type_details.set(self.cleaned_data.get("biotope_extra", [])) + state.surface = self.cleaned_data.get("surface", None) + state.save() + self.instance.mark_as_edited(self.user, self.request, edit_comment=COMPENSATION_STATE_EDITED) + return state + + class RemoveCompensationStateModalForm(RemoveModalForm): """ Removing modal form for CompensationState diff --git a/compensation/templates/compensation/detail/compensation/includes/states-after.html b/compensation/templates/compensation/detail/compensation/includes/states-after.html index b3e86a26..c0d8b2d5 100644 --- a/compensation/templates/compensation/detail/compensation/includes/states-after.html +++ b/compensation/templates/compensation/detail/compensation/includes/states-after.html @@ -57,9 +57,12 @@ {% endif %} {{ state.surface|floatformat:2 }} m² - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/templates/compensation/detail/compensation/includes/states-before.html b/compensation/templates/compensation/detail/compensation/includes/states-before.html index c3c7e0f1..50855430 100644 --- a/compensation/templates/compensation/detail/compensation/includes/states-before.html +++ b/compensation/templates/compensation/detail/compensation/includes/states-before.html @@ -57,9 +57,12 @@ {% endif %} {{ state.surface|floatformat:2 }} m² - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/urls/compensation.py b/compensation/urls/compensation.py index ea50b011..9d644e73 100644 --- a/compensation/urls/compensation.py +++ b/compensation/urls/compensation.py @@ -21,6 +21,7 @@ urlpatterns = [ path('/state/new', state_new_view, name='new-state'), path('/action/new', action_new_view, name='new-action'), path('/state//remove', state_remove_view, name='state-remove'), + path('/state//edit', state_edit_view, name='state-edit'), path('/action//remove', action_remove_view, name='action-remove'), path('/deadline/new', deadline_new_view, name="new-deadline"), path('/deadline//remove', deadline_remove_view, name='deadline-remove'), diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py index 40ea9335..3baf03a8 100644 --- a/compensation/views/compensation.py +++ b/compensation/views/compensation.py @@ -6,7 +6,8 @@ from django.utils.translation import gettext_lazy as _ from compensation.forms.forms import NewCompensationForm, EditCompensationForm from compensation.forms.modalForms import NewStateModalForm, NewDeadlineModalForm, NewActionModalForm, \ - NewCompensationDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm + NewCompensationDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, \ + EditCompensationStateModalForm from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument from compensation.tables import CompensationTable from intervention.models import Intervention @@ -20,7 +21,7 @@ from konova.utils.generators import generate_qr_code from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION, \ CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \ COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, \ - DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED + DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED from konova.utils.user_checks import in_group @@ -469,6 +470,30 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): ) +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def state_edit_view(request: HttpRequest, id: str, state_id: str): + """ Renders a form for editing a compensation state + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + state_id (str): The state's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + state = get_object_or_404(CompensationState, id=state_id) + form = EditCompensationStateModalForm(request.POST or None, instance=comp, state=state, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_STATE_EDITED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + @login_required @default_group_required @shared_access_required(Compensation, "id") From aa242e040aa5078a60fd27e7bbe9702f13a6d734 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 11:15:01 +0100 Subject: [PATCH 25/31] #86 Edit states EMA/EcoAccount * adds support for editing of states for EMA and EcoAccount * adds buttons and urls --- .../eco_account/includes/states-after.html | 7 +++-- .../eco_account/includes/states-before.html | 7 +++-- compensation/urls/eco_account.py | 1 + compensation/views/eco_account.py | 29 +++++++++++++++++-- .../ema/detail/includes/states-after.html | 7 +++-- .../ema/detail/includes/states-before.html | 7 +++-- ema/urls.py | 1 + ema/views.py | 28 ++++++++++++++++-- 8 files changed, 75 insertions(+), 12 deletions(-) diff --git a/compensation/templates/compensation/detail/eco_account/includes/states-after.html b/compensation/templates/compensation/detail/eco_account/includes/states-after.html index ea696b31..ff074769 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/states-after.html +++ b/compensation/templates/compensation/detail/eco_account/includes/states-after.html @@ -57,9 +57,12 @@ {% endif %} {{ state.surface|floatformat:2 }} m² - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/templates/compensation/detail/eco_account/includes/states-before.html b/compensation/templates/compensation/detail/eco_account/includes/states-before.html index af3042ea..7b5fe785 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/states-before.html +++ b/compensation/templates/compensation/detail/eco_account/includes/states-before.html @@ -57,9 +57,12 @@ {% endif %} {{ state.surface|floatformat:2 }} m² - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/urls/eco_account.py b/compensation/urls/eco_account.py index e390d4fb..f23f2a11 100644 --- a/compensation/urls/eco_account.py +++ b/compensation/urls/eco_account.py @@ -22,6 +22,7 @@ urlpatterns = [ path('/state/new', state_new_view, name='new-state'), path('/action/new', action_new_view, name='new-action'), path('/state//remove', state_remove_view, name='state-remove'), + path('/state//edit', state_edit_view, name='state-edit'), path('/action//remove', action_remove_view, name='action-remove'), path('/deadline//remove', deadline_remove_view, name='deadline-remove'), path('/deadline/new', deadline_new_view, name="new-deadline"), diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index 19959f81..a0365068 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -16,7 +16,8 @@ from django.shortcuts import render, get_object_or_404, redirect from compensation.forms.forms import NewEcoAccountForm, EditEcoAccountForm from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ - NewEcoAccountDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm + NewEcoAccountDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, \ + EditCompensationStateModalForm from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction from compensation.tables import EcoAccountTable from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm, \ @@ -34,7 +35,7 @@ from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \ COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, \ - DEDUCTION_EDITED, DOCUMENT_EDITED + DEDUCTION_EDITED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED from konova.utils.user_checks import in_group @@ -442,6 +443,30 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): ) +@login_required +@default_group_required +@shared_access_required(EcoAccount, "id") +def state_edit_view(request: HttpRequest, id: str, state_id: str): + """ Renders a form for editing a compensation state + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + state_id (str): The state's id + + Returns: + + """ + acc = get_object_or_404(EcoAccount, id=id) + state = get_object_or_404(CompensationState, id=state_id) + form = EditCompensationStateModalForm(request.POST or None, instance=acc, state=state, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_STATE_EDITED, + redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" + ) + + @login_required @default_group_required @shared_access_required(EcoAccount, "id") diff --git a/ema/templates/ema/detail/includes/states-after.html b/ema/templates/ema/detail/includes/states-after.html index f0d3dfc4..e876a166 100644 --- a/ema/templates/ema/detail/includes/states-after.html +++ b/ema/templates/ema/detail/includes/states-after.html @@ -55,9 +55,12 @@ {% endif %} {{ state.surface|floatformat:2 }} m² - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/ema/templates/ema/detail/includes/states-before.html b/ema/templates/ema/detail/includes/states-before.html index f479bd8d..aec3f328 100644 --- a/ema/templates/ema/detail/includes/states-before.html +++ b/ema/templates/ema/detail/includes/states-before.html @@ -55,9 +55,12 @@ {% endif %} {{ state.surface|floatformat:2 }} m² - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/ema/urls.py b/ema/urls.py index a2ee618f..4008a69c 100644 --- a/ema/urls.py +++ b/ema/urls.py @@ -22,6 +22,7 @@ urlpatterns = [ path('/state/new', state_new_view, name='new-state'), path('/action/new', action_new_view, name='new-action'), path('/state//remove', state_remove_view, name='state-remove'), + path('/state//edit', state_edit_view, name='state-edit'), path('/action//remove', action_remove_view, name='action-remove'), path('/deadline//remove', deadline_remove_view, name='deadline-remove'), path('/deadline/new', deadline_new_view, name="new-deadline"), diff --git a/ema/views.py b/ema/views.py index 0650f4c9..bc54631c 100644 --- a/ema/views.py +++ b/ema/views.py @@ -7,7 +7,7 @@ from django.urls import reverse from django.utils.translation import gettext_lazy as _ from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ - RemoveCompensationActionModalForm, RemoveCompensationStateModalForm + RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, EditCompensationStateModalForm from compensation.models import CompensationAction, CompensationState from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentModalForm from ema.tables import EmaTable @@ -24,7 +24,7 @@ from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, \ - COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED + COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED from konova.utils.user_checks import in_group @@ -460,6 +460,30 @@ def state_remove_view(request: HttpRequest, id: str, state_id: str): ) +@login_required +@conservation_office_group_required +@shared_access_required(Ema, "id") +def state_edit_view(request: HttpRequest, id: str, state_id: str): + """ Renders a form for editing an EMA state + + Args: + request (HttpRequest): The incoming request + id (str): The ema id + state_id (str): The state's id + + Returns: + + """ + ema = get_object_or_404(Ema, id=id) + state = get_object_or_404(CompensationState, id=state_id) + form = EditCompensationStateModalForm(request.POST or None, instance=ema, state=state, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_STATE_EDITED, + redirect_url=reverse("ema:detail", args=(id,)) + "#related_data" + ) + + @login_required @conservation_office_group_required @shared_access_required(Ema, "id") From b581d1c4ad2d7063aebae6550442f730faade5ca Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 11:24:20 +0100 Subject: [PATCH 26/31] #86 Edit actions EMA * adds support for editing of CompensationAction * adds buttons and urls for EMA --- compensation/forms/modalForms.py | 29 +++++++++++++++++- .../ema/detail/includes/actions.html | 7 +++-- ema/urls.py | 6 +++- ema/views.py | 30 +++++++++++++++++-- 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/compensation/forms/modalForms.py b/compensation/forms/modalForms.py index ddf8502b..6436a7c8 100644 --- a/compensation/forms/modalForms.py +++ b/compensation/forms/modalForms.py @@ -21,7 +21,7 @@ from konova.contexts import BaseContext from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm from konova.models import DeadlineType from konova.utils.message_templates import FORM_INVALID, ADDED_COMPENSATION_STATE, ADDED_DEADLINE, \ - ADDED_COMPENSATION_ACTION, PAYMENT_EDITED, COMPENSATION_STATE_EDITED + ADDED_COMPENSATION_ACTION, PAYMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED class NewPaymentForm(BaseModalForm): @@ -466,6 +466,33 @@ class NewActionModalForm(BaseModalForm): return action +class EditCompensationActionModalForm(NewActionModalForm): + action = None + + def __init__(self, *args, **kwargs): + self.action = kwargs.pop("action", None) + super().__init__(*args, **kwargs) + form_data = { + "action_type": self.action.action_type, + "action_type_details": self.action.action_type_details.all(), + "amount": self.action.amount, + "unit": self.action.unit, + "comment": self.action.comment, + } + self.load_initial_data(form_data) + + def save(self): + action = self.action + action.action_type = self.cleaned_data.get("action_type", None) + action.action_type_details.set(self.cleaned_data.get("action_type_details", [])) + action.amount = self.cleaned_data.get("amount", None) + action.unit = self.cleaned_data.get("unit", None) + action.comment = self.cleaned_data.get("comment", None) + action.save() + self.instance.mark_as_edited(self.user, self.request, edit_comment=COMPENSATION_ACTION_EDITED) + return action + + class NewCompensationDocumentModalForm(NewDocumentModalForm): document_model = CompensationDocument diff --git a/ema/templates/ema/detail/includes/actions.html b/ema/templates/ema/detail/includes/actions.html index 74f0564a..30e5e527 100644 --- a/ema/templates/ema/detail/includes/actions.html +++ b/ema/templates/ema/detail/includes/actions.html @@ -58,9 +58,12 @@ {{ action.comment }} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/ema/urls.py b/ema/urls.py index 4008a69c..3e7092c6 100644 --- a/ema/urls.py +++ b/ema/urls.py @@ -19,11 +19,15 @@ urlpatterns = [ path('/remove', remove_view, name='remove'), path('/record', record_view, name='record'), path('/report', report_view, name='report'), + path('/state/new', state_new_view, name='new-state'), - path('/action/new', action_new_view, name='new-action'), path('/state//remove', state_remove_view, name='state-remove'), path('/state//edit', state_edit_view, name='state-edit'), + + path('/action/new', action_new_view, name='new-action'), + path('/action//edit', action_edit_view, name='action-edit'), path('/action//remove', action_remove_view, name='action-remove'), + path('/deadline//remove', deadline_remove_view, name='deadline-remove'), path('/deadline/new', deadline_new_view, name="new-deadline"), path('/share/', share_view, name='share'), diff --git a/ema/views.py b/ema/views.py index bc54631c..607d58df 100644 --- a/ema/views.py +++ b/ema/views.py @@ -7,7 +7,8 @@ from django.urls import reverse from django.utils.translation import gettext_lazy as _ from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ - RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, EditCompensationStateModalForm + RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, EditCompensationStateModalForm, \ + EditCompensationActionModalForm from compensation.models import CompensationAction, CompensationState from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentModalForm from ema.tables import EmaTable @@ -24,7 +25,8 @@ from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, \ - COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED + COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, \ + COMPENSATION_ACTION_EDITED from konova.utils.user_checks import in_group @@ -322,6 +324,30 @@ def action_new_view(request: HttpRequest, id: str): ) +@login_required +@conservation_office_group_required +@shared_access_required(Ema, "id") +def action_edit_view(request: HttpRequest, id: str, action_id: str): + """ Renders a form for editing an actions for an EMA + + Args: + request (HttpRequest): The incoming request + id (str): The EMA's id + action_id (str): The action id + + Returns: + + """ + ema = get_object_or_404(Ema, id=id) + action = get_object_or_404(CompensationAction, id=action_id) + form = EditCompensationActionModalForm(request.POST or None, instance=ema, action=action, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_ACTION_EDITED, + redirect_url=reverse("ema:detail", args=(id,)) + "#related_data" + ) + + @login_required @conservation_office_group_required @shared_access_required(Ema, "id") From 792b4a46324aa1f73e413d193a91c26006113269 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 11:31:13 +0100 Subject: [PATCH 27/31] #86 Edit actions compensation * adds support for editing of CompensationAction for compensation * adds buttons and urls --- .../detail/compensation/includes/actions.html | 7 +++-- compensation/urls/compensation.py | 8 ++++-- compensation/views/compensation.py | 28 +++++++++++++++++-- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/compensation/templates/compensation/detail/compensation/includes/actions.html b/compensation/templates/compensation/detail/compensation/includes/actions.html index d82d8c08..f8a917ee 100644 --- a/compensation/templates/compensation/detail/compensation/includes/actions.html +++ b/compensation/templates/compensation/detail/compensation/includes/actions.html @@ -61,9 +61,12 @@ {{ action.comment }} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/urls/compensation.py b/compensation/urls/compensation.py index 9d644e73..3b6a6822 100644 --- a/compensation/urls/compensation.py +++ b/compensation/urls/compensation.py @@ -18,11 +18,15 @@ urlpatterns = [ path('/log', log_view, name='log'), path('/edit', edit_view, name='edit'), path('/remove', remove_view, name='remove'), + path('/state/new', state_new_view, name='new-state'), - path('/action/new', action_new_view, name='new-action'), - path('/state//remove', state_remove_view, name='state-remove'), path('/state//edit', state_edit_view, name='state-edit'), + path('/state//remove', state_remove_view, name='state-remove'), + + path('/action/new', action_new_view, name='new-action'), + path('/action//edit', action_edit_view, name='action-edit'), path('/action//remove', action_remove_view, name='action-remove'), + path('/deadline/new', deadline_new_view, name="new-deadline"), path('/deadline//remove', deadline_remove_view, name='deadline-remove'), path('/report', report_view, name='report'), diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py index 3baf03a8..65d2ebc5 100644 --- a/compensation/views/compensation.py +++ b/compensation/views/compensation.py @@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _ from compensation.forms.forms import NewCompensationForm, EditCompensationForm from compensation.forms.modalForms import NewStateModalForm, NewDeadlineModalForm, NewActionModalForm, \ NewCompensationDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, \ - EditCompensationStateModalForm + EditCompensationStateModalForm, EditCompensationActionModalForm from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument from compensation.tables import CompensationTable from intervention.models import Intervention @@ -21,7 +21,7 @@ from konova.utils.generators import generate_qr_code from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION, \ CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \ COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, \ - DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED + DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED from konova.utils.user_checks import in_group @@ -400,6 +400,30 @@ def action_new_view(request: HttpRequest, id: str): ) +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def action_edit_view(request: HttpRequest, id: str, action_id: str): + """ Renders a form for editing actions for a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + action_id (str): The action's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + action = get_object_or_404(CompensationAction, id=action_id) + form = EditCompensationActionModalForm(request.POST or None, instance=comp, action=action, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_ACTION_EDITED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + @login_required @default_group_required @shared_access_required(Compensation, "id") From c88bbdabbcb19ce075baa167d61256b79a91f7f1 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 11:45:55 +0100 Subject: [PATCH 28/31] #86 Edit view tests * extends view tests --- .../compensation/includes/deadlines.html | 2 +- .../compensation/includes/documents.html | 2 +- .../detail/eco_account/includes/actions.html | 9 +++-- compensation/tests/ecoaccount/test_views.py | 33 +++++++++++++------ compensation/urls/eco_account.py | 8 +++-- compensation/views/eco_account.py | 28 ++++++++++++++-- ema/tests/test_views.py | 33 +++++++++++++------ .../detail/includes/compensations.html | 2 +- .../detail/includes/deductions.html | 2 +- .../detail/includes/documents.html | 2 +- .../detail/includes/payments.html | 2 +- .../detail/includes/revocation.html | 2 +- intervention/tests/test_views.py | 18 ++++++++++ 13 files changed, 109 insertions(+), 34 deletions(-) diff --git a/compensation/templates/compensation/detail/compensation/includes/deadlines.html b/compensation/templates/compensation/detail/compensation/includes/deadlines.html index da4e21d6..0af1498a 100644 --- a/compensation/templates/compensation/detail/compensation/includes/deadlines.html +++ b/compensation/templates/compensation/detail/compensation/includes/deadlines.html @@ -34,7 +34,7 @@ {% trans 'Comment' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/compensation/templates/compensation/detail/compensation/includes/documents.html b/compensation/templates/compensation/detail/compensation/includes/documents.html index e12de4e9..573fd865 100644 --- a/compensation/templates/compensation/detail/compensation/includes/documents.html +++ b/compensation/templates/compensation/detail/compensation/includes/documents.html @@ -34,7 +34,7 @@ {% trans 'Comment' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/compensation/templates/compensation/detail/eco_account/includes/actions.html b/compensation/templates/compensation/detail/eco_account/includes/actions.html index d1275c78..add698e0 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/actions.html +++ b/compensation/templates/compensation/detail/eco_account/includes/actions.html @@ -34,7 +34,7 @@ {% trans 'Comment' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} @@ -60,9 +60,12 @@ {{ action.comment }} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/tests/ecoaccount/test_views.py b/compensation/tests/ecoaccount/test_views.py index 670f4f0d..091e86db 100644 --- a/compensation/tests/ecoaccount/test_views.py +++ b/compensation/tests/ecoaccount/test_views.py @@ -44,12 +44,17 @@ class EcoAccountViewTestCase(CompensationViewTestCase): self.edit_url = reverse("compensation:acc:edit", args=(self.eco_account.id,)) self.remove_url = reverse("compensation:acc:remove", args=(self.eco_account.id,)) self.report_url = reverse("compensation:acc:report", args=(self.eco_account.id,)) + self.state_new_url = reverse("compensation:acc:new-state", args=(self.eco_account.id,)) + self.state_edit_url = reverse("compensation:acc:state-edit", args=(self.eco_account.id, self.comp_state.id)) + self.state_remove_url = reverse("compensation:acc:state-remove", args=(self.eco_account.id, self.comp_state.id,)) + self.action_new_url = reverse("compensation:acc:new-action", args=(self.eco_account.id,)) + self.action_edit_url = reverse("compensation:acc:action-edit", args=(self.eco_account.id, self.comp_action.id)) + self.action_remove_url = reverse("compensation:acc:action-remove", args=(self.eco_account.id, self.comp_action.id,)) + self.deadline_new_url = reverse("compensation:acc:new-deadline", args=(self.eco_account.id,)) self.new_doc_url = reverse("compensation:acc:new-doc", args=(self.eco_account.id,)) - self.state_remove_url = reverse("compensation:acc:state-remove", args=(self.eco_account.id, self.comp_state.id,)) - self.action_remove_url = reverse("compensation:acc:action-remove", args=(self.eco_account.id, self.comp_action.id,)) def test_logged_in_no_groups_shared(self): """ Check correct status code for all requests @@ -78,10 +83,12 @@ class EcoAccountViewTestCase(CompensationViewTestCase): self.edit_url, self.remove_url, self.state_new_url, - self.action_new_url, - self.deadline_new_url, + self.state_edit_url, self.state_remove_url, + self.action_new_url, + self.action_edit_url, self.action_remove_url, + self.deadline_new_url, self.new_doc_url, ] @@ -115,10 +122,12 @@ class EcoAccountViewTestCase(CompensationViewTestCase): self.edit_url, self.remove_url, self.state_new_url, - self.action_new_url, - self.deadline_new_url, + self.state_edit_url, self.state_remove_url, + self.action_new_url, + self.action_edit_url, self.action_remove_url, + self.deadline_new_url, self.new_doc_url, ] @@ -149,11 +158,13 @@ class EcoAccountViewTestCase(CompensationViewTestCase): self.new_id_url, self.edit_url, self.state_new_url, - self.action_new_url, - self.deadline_new_url, + self.state_edit_url, self.state_remove_url, + self.action_new_url, + self.action_edit_url, self.action_remove_url, self.new_doc_url, + self.deadline_new_url, self.log_url, self.remove_url, ] @@ -184,13 +195,15 @@ class EcoAccountViewTestCase(CompensationViewTestCase): fail_urls = [ self.edit_url, self.state_new_url, - self.action_new_url, - self.deadline_new_url, + self.state_edit_url, self.state_remove_url, + self.action_new_url, + self.action_edit_url, self.action_remove_url, self.new_doc_url, self.log_url, self.remove_url, + self.deadline_new_url, ] self.assert_url_fail(client, fail_urls) self.assert_url_success(client, success_urls) diff --git a/compensation/urls/eco_account.py b/compensation/urls/eco_account.py index f23f2a11..ccd549dd 100644 --- a/compensation/urls/eco_account.py +++ b/compensation/urls/eco_account.py @@ -19,11 +19,15 @@ urlpatterns = [ path('/report', report_view, name='report'), path('/edit', edit_view, name='edit'), path('/remove', remove_view, name='remove'), + path('/state/new', state_new_view, name='new-state'), - path('/action/new', action_new_view, name='new-action'), - path('/state//remove', state_remove_view, name='state-remove'), path('/state//edit', state_edit_view, name='state-edit'), + path('/state//remove', state_remove_view, name='state-remove'), + + path('/action/new', action_new_view, name='new-action'), + path('/action//edit', action_edit_view, name='action-edit'), path('/action//remove', action_remove_view, name='action-remove'), + path('/deadline//remove', deadline_remove_view, name='deadline-remove'), path('/deadline/new', deadline_new_view, name="new-deadline"), path('/share/', share_view, name='share'), diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index a0365068..37223786 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -17,7 +17,7 @@ from django.shortcuts import render, get_object_or_404, redirect from compensation.forms.forms import NewEcoAccountForm, EditEcoAccountForm from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ NewEcoAccountDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, \ - EditCompensationStateModalForm + EditCompensationStateModalForm, EditCompensationActionModalForm from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction from compensation.tables import EcoAccountTable from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm, \ @@ -35,7 +35,7 @@ from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \ COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, \ - DEDUCTION_EDITED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED + DEDUCTION_EDITED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED from konova.utils.user_checks import in_group @@ -491,6 +491,30 @@ def action_remove_view(request: HttpRequest, id: str, action_id: str): ) +@login_required +@default_group_required +@shared_access_required(EcoAccount, "id") +def action_edit_view(request: HttpRequest, id: str, action_id: str): + """ Renders a form for editing a compensation action + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + id (str): The action's id + + Returns: + + """ + acc = get_object_or_404(EcoAccount, id=id) + action = get_object_or_404(CompensationAction, id=action_id) + form = EditCompensationActionModalForm(request.POST or None, instance=acc, action=action, request=request) + return form.process_request( + request, + msg_success=COMPENSATION_ACTION_EDITED, + redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" + ) + + @login_required @default_group_required @shared_access_required(EcoAccount, "id") diff --git a/ema/tests/test_views.py b/ema/tests/test_views.py index 65f4d351..70090c7d 100644 --- a/ema/tests/test_views.py +++ b/ema/tests/test_views.py @@ -54,12 +54,17 @@ class EmaViewTestCase(CompensationViewTestCase): self.record_url = reverse("ema:record", args=(self.ema.id,)) self.report_url = reverse("ema:report", args=(self.ema.id,)) self.new_doc_url = reverse("ema:new-doc", args=(self.ema.id,)) + self.state_new_url = reverse("ema:new-state", args=(self.ema.id,)) - self.action_new_url = reverse("ema:new-action", args=(self.ema.id,)) - self.deadline_new_url = reverse("ema:new-deadline", args=(self.ema.id,)) + self.state_edit_url = reverse("ema:state-edit", args=(self.ema.id, state.id)) self.state_remove_url = reverse("ema:state-remove", args=(self.ema.id, state.id,)) + + self.action_new_url = reverse("ema:new-action", args=(self.ema.id,)) + self.action_edit_url = reverse("ema:action-edit", args=(self.ema.id, action.id)) self.action_remove_url = reverse("ema:action-remove", args=(self.ema.id, action.id,)) + self.deadline_new_url = reverse("ema:new-deadline", args=(self.ema.id,)) + def create_dummy_data(self): # Create dummy data # Create log entry @@ -108,10 +113,12 @@ class EmaViewTestCase(CompensationViewTestCase): self.new_id_url, self.edit_url, self.state_new_url, - self.action_new_url, - self.deadline_new_url, self.state_remove_url, + self.state_edit_url, + self.deadline_new_url, + self.action_edit_url, self.action_remove_url, + self.action_new_url, self.new_doc_url, self.log_url, self.remove_url, @@ -154,10 +161,12 @@ class EmaViewTestCase(CompensationViewTestCase): self.new_id_url, self.edit_url, self.state_new_url, - self.action_new_url, - self.deadline_new_url, + self.state_edit_url, self.state_remove_url, + self.action_new_url, + self.action_edit_url, self.action_remove_url, + self.deadline_new_url, self.new_doc_url, self.log_url, self.remove_url, @@ -191,9 +200,11 @@ class EmaViewTestCase(CompensationViewTestCase): self.new_id_url, self.edit_url, self.state_new_url, - self.action_new_url, - self.deadline_new_url, + self.state_edit_url, self.state_remove_url, + self.deadline_new_url, + self.action_new_url, + self.action_edit_url, self.action_remove_url, self.new_doc_url, self.log_url, @@ -229,9 +240,11 @@ class EmaViewTestCase(CompensationViewTestCase): fail_urls = [ self.edit_url, self.state_new_url, - self.action_new_url, - self.deadline_new_url, + self.state_edit_url, self.state_remove_url, + self.deadline_new_url, + self.action_new_url, + self.action_edit_url, self.action_remove_url, self.new_doc_url, self.log_url, diff --git a/intervention/templates/intervention/detail/includes/compensations.html b/intervention/templates/intervention/detail/includes/compensations.html index b7a546dd..c572a344 100644 --- a/intervention/templates/intervention/detail/includes/compensations.html +++ b/intervention/templates/intervention/detail/includes/compensations.html @@ -33,7 +33,7 @@ {% trans 'Title' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/intervention/templates/intervention/detail/includes/deductions.html b/intervention/templates/intervention/detail/includes/deductions.html index b11817d1..04110b8b 100644 --- a/intervention/templates/intervention/detail/includes/deductions.html +++ b/intervention/templates/intervention/detail/includes/deductions.html @@ -34,7 +34,7 @@ {% trans 'Created' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/intervention/templates/intervention/detail/includes/documents.html b/intervention/templates/intervention/detail/includes/documents.html index 93309f5c..9fccc4b7 100644 --- a/intervention/templates/intervention/detail/includes/documents.html +++ b/intervention/templates/intervention/detail/includes/documents.html @@ -34,7 +34,7 @@ {% trans 'Comment' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/intervention/templates/intervention/detail/includes/payments.html b/intervention/templates/intervention/detail/includes/payments.html index 4bf00cd8..65408e47 100644 --- a/intervention/templates/intervention/detail/includes/payments.html +++ b/intervention/templates/intervention/detail/includes/payments.html @@ -34,7 +34,7 @@ {% trans 'Comment' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/intervention/templates/intervention/detail/includes/revocation.html b/intervention/templates/intervention/detail/includes/revocation.html index 6eb68f99..39738d24 100644 --- a/intervention/templates/intervention/detail/includes/revocation.html +++ b/intervention/templates/intervention/detail/includes/revocation.html @@ -37,7 +37,7 @@ {% trans 'Comment' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/intervention/tests/test_views.py b/intervention/tests/test_views.py index 68e4b562..f12ee7a3 100644 --- a/intervention/tests/test_views.py +++ b/intervention/tests/test_views.py @@ -40,12 +40,14 @@ class InterventionViewTestCase(BaseViewTestCase): self.deduction.intervention = self.intervention self.deduction.save() self.deduction_new_url = reverse("intervention:new-deduction", args=(self.intervention.id,)) + self.deduction_edit_url = reverse("intervention:edit-deduction", args=(self.intervention.id, self.deduction.id,)) self.deduction_remove_url = reverse("intervention:remove-deduction", args=(self.intervention.id, self.deduction.id)) self.revocation = Revocation.objects.create( legal=self.intervention.legal ) self.revocation_new_url = reverse("intervention:new-revocation", args=(self.intervention.id,)) + self.revocation_edit_url = reverse("intervention:edit-revocation", args=(self.intervention.id, self.revocation.id)) self.revocation_remove_url = reverse("intervention:remove-revocation", args=(self.intervention.id, self.revocation.id)) def test_views_anonymous_user(self): @@ -76,8 +78,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.run_check_url: f"{login_redirect_base}{self.run_check_url}", self.record_url: f"{login_redirect_base}{self.record_url}", self.deduction_new_url: f"{login_redirect_base}{self.deduction_new_url}", + self.deduction_edit_url: f"{login_redirect_base}{self.deduction_edit_url}", self.deduction_remove_url: f"{login_redirect_base}{self.deduction_remove_url}", self.revocation_new_url: f"{login_redirect_base}{self.revocation_new_url}", + self.revocation_edit_url: f"{login_redirect_base}{self.revocation_edit_url}", self.revocation_remove_url: f"{login_redirect_base}{self.revocation_remove_url}", } @@ -115,8 +119,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.run_check_url, self.record_url, self.revocation_new_url, + self.revocation_edit_url, self.revocation_remove_url, self.deduction_new_url, + self.deduction_edit_url, self.deduction_remove_url, ] @@ -151,8 +157,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.remove_url, self.share_create_url, self.revocation_new_url, + self.revocation_edit_url, self.revocation_remove_url, self.deduction_new_url, + self.deduction_edit_url, self.deduction_remove_url, ] fail_urls = [ @@ -199,8 +207,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.share_create_url, self.log_url, self.revocation_new_url, + self.revocation_edit_url, self.revocation_remove_url, self.deduction_new_url, + self.deduction_edit_url, self.deduction_remove_url, ] success_urls_redirect = { @@ -243,8 +253,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.share_create_url, self.record_url, self.revocation_new_url, + self.revocation_edit_url, self.revocation_remove_url, self.deduction_new_url, + self.deduction_edit_url, self.deduction_remove_url, ] success_urls_redirect = { @@ -287,8 +299,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.record_url, self.run_check_url, self.revocation_new_url, + self.revocation_edit_url, self.revocation_remove_url, self.deduction_new_url, + self.deduction_edit_url, self.deduction_remove_url, ] success_urls_redirect = { @@ -331,8 +345,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.share_create_url, self.run_check_url, self.revocation_new_url, + self.revocation_edit_url, self.revocation_remove_url, self.deduction_new_url, + self.deduction_edit_url, self.deduction_remove_url, ] success_urls_redirect = { @@ -375,8 +391,10 @@ class InterventionViewTestCase(BaseViewTestCase): self.share_create_url, self.run_check_url, self.revocation_new_url, + self.revocation_edit_url, self.revocation_remove_url, self.deduction_new_url, + self.deduction_edit_url, self.deduction_remove_url, ] # Define urls where a redirect to a specific location is the proper response From 8d396c3e2b967797670504265328be427757c979 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 12:33:22 +0100 Subject: [PATCH 29/31] #86 Edit deadlines EMA * adds support for editing of EMA deadlines * adds buttons and urls --- compensation/forms/modalForms.py | 25 ++++++++++++++++- .../ema/detail/includes/deadlines.html | 7 +++-- ema/tests/test_views.py | 19 ++++++++++++- ema/urls.py | 4 ++- ema/views.py | 28 +++++++++++++++++-- 5 files changed, 76 insertions(+), 7 deletions(-) diff --git a/compensation/forms/modalForms.py b/compensation/forms/modalForms.py index 6436a7c8..804ebad4 100644 --- a/compensation/forms/modalForms.py +++ b/compensation/forms/modalForms.py @@ -21,7 +21,7 @@ from konova.contexts import BaseContext from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm from konova.models import DeadlineType from konova.utils.message_templates import FORM_INVALID, ADDED_COMPENSATION_STATE, ADDED_DEADLINE, \ - ADDED_COMPENSATION_ACTION, PAYMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED + ADDED_COMPENSATION_ACTION, PAYMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED, DEADLINE_EDITED class NewPaymentForm(BaseModalForm): @@ -373,6 +373,29 @@ class NewDeadlineModalForm(BaseModalForm): return deadline +class EditDeadlineModalForm(NewDeadlineModalForm): + deadline = None + + def __init__(self, *args, **kwargs): + self.deadline = kwargs.pop("deadline", None) + super().__init__(*args, **kwargs) + form_data = { + "type": self.deadline.type, + "date": str(self.deadline.date), + "comment": self.deadline.comment, + } + self.load_initial_data(form_data) + + def save(self): + deadline = self.deadline + deadline.type = self.cleaned_data.get("type", None) + deadline.date = self.cleaned_data.get("date", None) + deadline.comment = self.cleaned_data.get("comment", None) + deadline.save() + self.instance.mark_as_edited(self.user, self.request, edit_comment=DEADLINE_EDITED) + return deadline + + class NewActionModalForm(BaseModalForm): """ Form handling action related input diff --git a/ema/templates/ema/detail/includes/deadlines.html b/ema/templates/ema/detail/includes/deadlines.html index 02efaf8b..6c7214e4 100644 --- a/ema/templates/ema/detail/includes/deadlines.html +++ b/ema/templates/ema/detail/includes/deadlines.html @@ -52,9 +52,12 @@ {{ deadline.comment }} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/ema/tests/test_views.py b/ema/tests/test_views.py index 70090c7d..229f8841 100644 --- a/ema/tests/test_views.py +++ b/ema/tests/test_views.py @@ -13,7 +13,7 @@ from django.test.client import Client from compensation.tests.compensation.test_views import CompensationViewTestCase from ema.models import Ema from intervention.models import Responsibility -from konova.models import Geometry +from konova.models import Geometry, Deadline, DeadlineType from konova.settings import DEFAULT_GROUP, ETS_GROUP from user.models import UserActionLogEntry @@ -63,7 +63,16 @@ class EmaViewTestCase(CompensationViewTestCase): self.action_edit_url = reverse("ema:action-edit", args=(self.ema.id, action.id)) self.action_remove_url = reverse("ema:action-remove", args=(self.ema.id, action.id,)) + self.deadline = Deadline.objects.create( + type=DeadlineType.FINISHED, + date="2020-01-01", + comment="TESTCOMMENT", + ) + self.ema.deadlines.add(self.deadline) + self.deadline_new_url = reverse("ema:new-deadline", args=(self.ema.id,)) + self.deadline_edit_url = reverse("ema:deadline-edit", args=(self.ema.id, self.deadline.id)) + self.deadline_remove_url = reverse("ema:deadline-remove", args=(self.ema.id, self.deadline.id)) def create_dummy_data(self): # Create dummy data @@ -116,6 +125,8 @@ class EmaViewTestCase(CompensationViewTestCase): self.state_remove_url, self.state_edit_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.action_edit_url, self.action_remove_url, self.action_new_url, @@ -167,6 +178,8 @@ class EmaViewTestCase(CompensationViewTestCase): self.action_edit_url, self.action_remove_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.new_doc_url, self.log_url, self.remove_url, @@ -203,6 +216,8 @@ class EmaViewTestCase(CompensationViewTestCase): self.state_edit_url, self.state_remove_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.action_new_url, self.action_edit_url, self.action_remove_url, @@ -243,6 +258,8 @@ class EmaViewTestCase(CompensationViewTestCase): self.state_edit_url, self.state_remove_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.action_new_url, self.action_edit_url, self.action_remove_url, diff --git a/ema/urls.py b/ema/urls.py index 3e7092c6..90cafb66 100644 --- a/ema/urls.py +++ b/ema/urls.py @@ -28,8 +28,10 @@ urlpatterns = [ path('/action//edit', action_edit_view, name='action-edit'), path('/action//remove', action_remove_view, name='action-remove'), - path('/deadline//remove', deadline_remove_view, name='deadline-remove'), path('/deadline/new', deadline_new_view, name="new-deadline"), + path('/deadline//edit', deadline_edit_view, name='deadline-edit'), + path('/deadline//remove', deadline_remove_view, name='deadline-remove'), + path('/share/', share_view, name='share'), path('/share', create_share_view, name='share-create'), diff --git a/ema/views.py b/ema/views.py index 607d58df..e9d0acb6 100644 --- a/ema/views.py +++ b/ema/views.py @@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _ from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, EditCompensationStateModalForm, \ - EditCompensationActionModalForm + EditCompensationActionModalForm, EditDeadlineModalForm from compensation.models import CompensationAction, CompensationState from ema.forms import NewEmaForm, EditEmaForm, NewEmaDocumentModalForm from ema.tables import EmaTable @@ -26,7 +26,7 @@ from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, \ COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, \ - COMPENSATION_ACTION_EDITED + COMPENSATION_ACTION_EDITED, DEADLINE_EDITED from konova.utils.user_checks import in_group @@ -653,6 +653,30 @@ def create_share_view(request: HttpRequest, id: str): ) +@login_required +@conservation_office_group_required +@shared_access_required(Ema, "id") +def deadline_edit_view(request: HttpRequest, id: str, deadline_id: str): + """ Renders a form for editing deadlines from a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + deadline_id (str): The deadline's id + + Returns: + + """ + ema = get_object_or_404(Ema, id=id) + deadline = get_object_or_404(Deadline, id=deadline_id) + form = EditDeadlineModalForm(request.POST or None, instance=ema, deadline=deadline, request=request) + return form.process_request( + request, + msg_success=DEADLINE_EDITED, + redirect_url=reverse("ema:detail", args=(id,)) + "#related_data" + ) + + @login_required @conservation_office_group_required @shared_access_required(Ema, "id") From afb0cabec49df4b65f49dfd225a31adb7c98c7cc Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 12:42:41 +0100 Subject: [PATCH 30/31] #86 Edit deadline Compensation * adds support for editing of deadlines * adds buttons and urls * adds w-10 as base css-class for all action columns --- .../detail/compensation/includes/actions.html | 2 +- .../compensation/includes/deadlines.html | 7 +++-- .../compensation/includes/states-after.html | 2 +- .../compensation/includes/states-before.html | 2 +- .../eco_account/includes/deadlines.html | 2 +- .../eco_account/includes/deductions.html | 2 +- .../eco_account/includes/documents.html | 2 +- .../eco_account/includes/states-after.html | 2 +- .../eco_account/includes/states-before.html | 2 +- compensation/tests/compensation/test_views.py | 20 +++++++++++++ compensation/urls/compensation.py | 1 + compensation/views/compensation.py | 29 +++++++++++++++++-- .../ema/detail/includes/actions.html | 2 +- .../ema/detail/includes/deadlines.html | 2 +- .../ema/detail/includes/documents.html | 2 +- .../ema/detail/includes/states-after.html | 2 +- .../ema/detail/includes/states-before.html | 2 +- ema/tests/test_views.py | 4 +-- 18 files changed, 68 insertions(+), 19 deletions(-) diff --git a/compensation/templates/compensation/detail/compensation/includes/actions.html b/compensation/templates/compensation/detail/compensation/includes/actions.html index f8a917ee..33037ecf 100644 --- a/compensation/templates/compensation/detail/compensation/includes/actions.html +++ b/compensation/templates/compensation/detail/compensation/includes/actions.html @@ -35,7 +35,7 @@ {% trans 'Comment' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/compensation/templates/compensation/detail/compensation/includes/deadlines.html b/compensation/templates/compensation/detail/compensation/includes/deadlines.html index 0af1498a..7f445657 100644 --- a/compensation/templates/compensation/detail/compensation/includes/deadlines.html +++ b/compensation/templates/compensation/detail/compensation/includes/deadlines.html @@ -54,9 +54,12 @@ {{ deadline.comment }} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/templates/compensation/detail/compensation/includes/states-after.html b/compensation/templates/compensation/detail/compensation/includes/states-after.html index c0d8b2d5..2c95ca1a 100644 --- a/compensation/templates/compensation/detail/compensation/includes/states-after.html +++ b/compensation/templates/compensation/detail/compensation/includes/states-after.html @@ -36,7 +36,7 @@ {% trans 'Surface' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/compensation/templates/compensation/detail/compensation/includes/states-before.html b/compensation/templates/compensation/detail/compensation/includes/states-before.html index 50855430..d2ba3697 100644 --- a/compensation/templates/compensation/detail/compensation/includes/states-before.html +++ b/compensation/templates/compensation/detail/compensation/includes/states-before.html @@ -36,7 +36,7 @@ {% trans 'Surface' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/compensation/templates/compensation/detail/eco_account/includes/deadlines.html b/compensation/templates/compensation/detail/eco_account/includes/deadlines.html index ff8a5291..b9d664d4 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/deadlines.html +++ b/compensation/templates/compensation/detail/eco_account/includes/deadlines.html @@ -33,7 +33,7 @@ {% trans 'Comment' %} - + {% trans 'Action' %} diff --git a/compensation/templates/compensation/detail/eco_account/includes/deductions.html b/compensation/templates/compensation/detail/eco_account/includes/deductions.html index 76c116b2..10f177ea 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/deductions.html +++ b/compensation/templates/compensation/detail/eco_account/includes/deductions.html @@ -36,7 +36,7 @@ {% trans 'Created' %} - + {% trans 'Action' %} diff --git a/compensation/templates/compensation/detail/eco_account/includes/documents.html b/compensation/templates/compensation/detail/eco_account/includes/documents.html index e852842b..67881c90 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/documents.html +++ b/compensation/templates/compensation/detail/eco_account/includes/documents.html @@ -33,7 +33,7 @@ {% trans 'Comment' %} - + {% trans 'Action' %} diff --git a/compensation/templates/compensation/detail/eco_account/includes/states-after.html b/compensation/templates/compensation/detail/eco_account/includes/states-after.html index ff074769..bead6f2b 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/states-after.html +++ b/compensation/templates/compensation/detail/eco_account/includes/states-after.html @@ -36,7 +36,7 @@ {% trans 'Surface' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/compensation/templates/compensation/detail/eco_account/includes/states-before.html b/compensation/templates/compensation/detail/eco_account/includes/states-before.html index 7b5fe785..c19b4049 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/states-before.html +++ b/compensation/templates/compensation/detail/eco_account/includes/states-before.html @@ -36,7 +36,7 @@ {% trans 'Surface' %} {% if is_default_member and has_access %} - + {% trans 'Action' %} diff --git a/compensation/tests/compensation/test_views.py b/compensation/tests/compensation/test_views.py index 5844e264..27218f2a 100644 --- a/compensation/tests/compensation/test_views.py +++ b/compensation/tests/compensation/test_views.py @@ -8,6 +8,7 @@ Created on: 07.02.22 from django.test.client import Client from django.urls import reverse +from konova.models import Deadline, DeadlineType from konova.settings import DEFAULT_GROUP from konova.tests.test_views import BaseViewTestCase @@ -31,6 +32,13 @@ class CompensationViewTestCase(BaseViewTestCase): action = self.create_dummy_action() self.compensation.actions.set([action]) + self.deadline = Deadline.objects.get_or_create( + type=DeadlineType.FINISHED, + date="2020-01-01", + comment="TESTDEADDLINECOMMENT" + )[0] + self.compensation.deadlines.add(self.deadline) + # Prepare urls self.index_url = reverse("compensation:index", args=()) self.new_url = reverse("compensation:new", args=(self.intervention.id,)) @@ -43,6 +51,8 @@ class CompensationViewTestCase(BaseViewTestCase): self.state_new_url = reverse("compensation:new-state", args=(self.compensation.id,)) self.action_new_url = reverse("compensation:new-action", args=(self.compensation.id,)) self.deadline_new_url = reverse("compensation:new-deadline", args=(self.compensation.id,)) + self.deadline_edit_url = reverse("compensation:deadline-edit", args=(self.compensation.id, self.deadline.id)) + self.deadline_remove_url = reverse("compensation:deadline-remove", args=(self.compensation.id, self.deadline.id)) self.new_doc_url = reverse("compensation:new-doc", args=(self.compensation.id,)) self.state_remove_url = reverse("compensation:state-remove", args=(self.compensation.id, self.comp_state.id,)) @@ -72,6 +82,8 @@ class CompensationViewTestCase(BaseViewTestCase): self.state_new_url, self.action_new_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.state_remove_url, self.action_remove_url, self.new_doc_url, @@ -109,6 +121,8 @@ class CompensationViewTestCase(BaseViewTestCase): self.state_new_url, self.action_new_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.state_remove_url, self.action_remove_url, self.new_doc_url, @@ -147,6 +161,8 @@ class CompensationViewTestCase(BaseViewTestCase): self.state_new_url, self.action_new_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.state_remove_url, self.action_remove_url, self.new_doc_url, @@ -181,6 +197,8 @@ class CompensationViewTestCase(BaseViewTestCase): self.state_new_url, self.action_new_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.state_remove_url, self.action_remove_url, self.new_doc_url, @@ -217,6 +235,8 @@ class CompensationViewTestCase(BaseViewTestCase): self.state_new_url, self.action_new_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.state_remove_url, self.action_remove_url, self.new_doc_url, diff --git a/compensation/urls/compensation.py b/compensation/urls/compensation.py index 3b6a6822..e1a41ff2 100644 --- a/compensation/urls/compensation.py +++ b/compensation/urls/compensation.py @@ -28,6 +28,7 @@ urlpatterns = [ path('/action//remove', action_remove_view, name='action-remove'), path('/deadline/new', deadline_new_view, name="new-deadline"), + path('/deadline//edit', deadline_edit_view, name='deadline-edit'), path('/deadline//remove', deadline_remove_view, name='deadline-remove'), path('/report', report_view, name='report'), diff --git a/compensation/views/compensation.py b/compensation/views/compensation.py index 65d2ebc5..6dcf442b 100644 --- a/compensation/views/compensation.py +++ b/compensation/views/compensation.py @@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _ from compensation.forms.forms import NewCompensationForm, EditCompensationForm from compensation.forms.modalForms import NewStateModalForm, NewDeadlineModalForm, NewActionModalForm, \ NewCompensationDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, \ - EditCompensationStateModalForm, EditCompensationActionModalForm + EditCompensationStateModalForm, EditCompensationActionModalForm, EditDeadlineModalForm from compensation.models import Compensation, CompensationState, CompensationAction, CompensationDocument from compensation.tables import CompensationTable from intervention.models import Intervention @@ -21,7 +21,8 @@ from konova.utils.generators import generate_qr_code from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION, \ CHECKED_RECORDED_RESET, COMPENSATION_ADDED_TEMPLATE, COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, \ COMPENSATION_STATE_REMOVED, COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, \ - DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED + DEADLINE_ADDED, DEADLINE_REMOVED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED, \ + DEADLINE_EDITED from konova.utils.user_checks import in_group @@ -446,6 +447,30 @@ def deadline_new_view(request: HttpRequest, id: str): ) +@login_required +@default_group_required +@shared_access_required(Compensation, "id") +def deadline_edit_view(request: HttpRequest, id: str, deadline_id: str): + """ Renders a form for editing deadlines from a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + deadline_id (str): The deadline's id + + Returns: + + """ + comp = get_object_or_404(Compensation, id=id) + deadline = get_object_or_404(Deadline, id=deadline_id) + form = EditDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) + return form.process_request( + request, + msg_success=DEADLINE_EDITED, + redirect_url=reverse("compensation:detail", args=(id,)) + "#related_data" + ) + + @login_required @default_group_required @shared_access_required(Compensation, "id") diff --git a/ema/templates/ema/detail/includes/actions.html b/ema/templates/ema/detail/includes/actions.html index 30e5e527..02772b36 100644 --- a/ema/templates/ema/detail/includes/actions.html +++ b/ema/templates/ema/detail/includes/actions.html @@ -33,7 +33,7 @@ {% trans 'Comment' %} - + {% trans 'Action' %} diff --git a/ema/templates/ema/detail/includes/deadlines.html b/ema/templates/ema/detail/includes/deadlines.html index 6c7214e4..761ce067 100644 --- a/ema/templates/ema/detail/includes/deadlines.html +++ b/ema/templates/ema/detail/includes/deadlines.html @@ -33,7 +33,7 @@ {% trans 'Comment' %} - + {% trans 'Action' %} diff --git a/ema/templates/ema/detail/includes/documents.html b/ema/templates/ema/detail/includes/documents.html index 7be632d3..b38d5499 100644 --- a/ema/templates/ema/detail/includes/documents.html +++ b/ema/templates/ema/detail/includes/documents.html @@ -33,7 +33,7 @@ {% trans 'Comment' %} - + {% trans 'Action' %} diff --git a/ema/templates/ema/detail/includes/states-after.html b/ema/templates/ema/detail/includes/states-after.html index e876a166..e09f4ba5 100644 --- a/ema/templates/ema/detail/includes/states-after.html +++ b/ema/templates/ema/detail/includes/states-after.html @@ -35,7 +35,7 @@ {% trans 'Surface' %} - + {% trans 'Action' %} diff --git a/ema/templates/ema/detail/includes/states-before.html b/ema/templates/ema/detail/includes/states-before.html index aec3f328..1369829b 100644 --- a/ema/templates/ema/detail/includes/states-before.html +++ b/ema/templates/ema/detail/includes/states-before.html @@ -35,7 +35,7 @@ {% trans 'Surface' %} - + {% trans 'Action' %} diff --git a/ema/tests/test_views.py b/ema/tests/test_views.py index 229f8841..9654e8f3 100644 --- a/ema/tests/test_views.py +++ b/ema/tests/test_views.py @@ -63,11 +63,11 @@ class EmaViewTestCase(CompensationViewTestCase): self.action_edit_url = reverse("ema:action-edit", args=(self.ema.id, action.id)) self.action_remove_url = reverse("ema:action-remove", args=(self.ema.id, action.id,)) - self.deadline = Deadline.objects.create( + self.deadline = Deadline.objects.get_or_create( type=DeadlineType.FINISHED, date="2020-01-01", comment="TESTCOMMENT", - ) + )[0] self.ema.deadlines.add(self.deadline) self.deadline_new_url = reverse("ema:new-deadline", args=(self.ema.id,)) From 6fbdc3c1fb5ff340e4ed2f4fc58c5ea55b5edcbd Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Thu, 10 Feb 2022 12:49:30 +0100 Subject: [PATCH 31/31] #86 Edit deadlines EcoAccount * adds support for editing of deadlines in EcoAccount * adds buttons and urls --- .../eco_account/includes/deadlines.html | 7 +++-- compensation/tests/ecoaccount/test_views.py | 19 +++++++++++++ compensation/urls/eco_account.py | 4 ++- compensation/views/eco_account.py | 28 +++++++++++++++++-- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/compensation/templates/compensation/detail/eco_account/includes/deadlines.html b/compensation/templates/compensation/detail/eco_account/includes/deadlines.html index b9d664d4..beaecfda 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/deadlines.html +++ b/compensation/templates/compensation/detail/eco_account/includes/deadlines.html @@ -52,9 +52,12 @@ {{ deadline.comment }} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/tests/ecoaccount/test_views.py b/compensation/tests/ecoaccount/test_views.py index 091e86db..617f7437 100644 --- a/compensation/tests/ecoaccount/test_views.py +++ b/compensation/tests/ecoaccount/test_views.py @@ -9,6 +9,7 @@ from django.urls import reverse from django.test import Client from compensation.tests.compensation.test_views import CompensationViewTestCase +from konova.models import DeadlineType, Deadline from konova.settings import DEFAULT_GROUP @@ -53,7 +54,17 @@ class EcoAccountViewTestCase(CompensationViewTestCase): self.action_edit_url = reverse("compensation:acc:action-edit", args=(self.eco_account.id, self.comp_action.id)) self.action_remove_url = reverse("compensation:acc:action-remove", args=(self.eco_account.id, self.comp_action.id,)) + self.deadline = Deadline.objects.get_or_create( + type=DeadlineType.FINISHED, + date="2020-01-01", + comment="DEADLINE COMMENT" + )[0] + self.eco_account.deadlines.add(self.deadline) + self.deadline_new_url = reverse("compensation:acc:new-deadline", args=(self.eco_account.id,)) + self.deadline_edit_url = reverse("compensation:acc:deadline-edit", args=(self.eco_account.id, self.deadline.id)) + self.deadline_remove_url = reverse("compensation:acc:deadline-remove", args=(self.eco_account.id, self.deadline.id)) + self.new_doc_url = reverse("compensation:acc:new-doc", args=(self.eco_account.id,)) def test_logged_in_no_groups_shared(self): @@ -89,6 +100,8 @@ class EcoAccountViewTestCase(CompensationViewTestCase): self.action_edit_url, self.action_remove_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.new_doc_url, ] @@ -128,6 +141,8 @@ class EcoAccountViewTestCase(CompensationViewTestCase): self.action_edit_url, self.action_remove_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.new_doc_url, ] @@ -165,6 +180,8 @@ class EcoAccountViewTestCase(CompensationViewTestCase): self.action_remove_url, self.new_doc_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, self.log_url, self.remove_url, ] @@ -204,6 +221,8 @@ class EcoAccountViewTestCase(CompensationViewTestCase): self.log_url, self.remove_url, self.deadline_new_url, + self.deadline_edit_url, + self.deadline_remove_url, ] self.assert_url_fail(client, fail_urls) self.assert_url_success(client, success_urls) diff --git a/compensation/urls/eco_account.py b/compensation/urls/eco_account.py index ccd549dd..a3d1aa38 100644 --- a/compensation/urls/eco_account.py +++ b/compensation/urls/eco_account.py @@ -28,8 +28,10 @@ urlpatterns = [ path('/action//edit', action_edit_view, name='action-edit'), path('/action//remove', action_remove_view, name='action-remove'), - path('/deadline//remove', deadline_remove_view, name='deadline-remove'), path('/deadline/new', deadline_new_view, name="new-deadline"), + path('/deadline//edit', deadline_edit_view, name='deadline-edit'), + path('/deadline//remove', deadline_remove_view, name='deadline-remove'), + path('/share/', share_view, name='share'), path('/share', create_share_view, name='share-create'), diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index 37223786..cf5f3288 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -17,7 +17,7 @@ from django.shortcuts import render, get_object_or_404, redirect from compensation.forms.forms import NewEcoAccountForm, EditEcoAccountForm from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewDeadlineModalForm, \ NewEcoAccountDocumentModalForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm, \ - EditCompensationStateModalForm, EditCompensationActionModalForm + EditCompensationStateModalForm, EditCompensationActionModalForm, EditDeadlineModalForm from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction from compensation.tables import EcoAccountTable from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm, \ @@ -35,7 +35,7 @@ from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \ COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, \ - DEDUCTION_EDITED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED + DEDUCTION_EDITED, DOCUMENT_EDITED, COMPENSATION_STATE_EDITED, COMPENSATION_ACTION_EDITED, DEADLINE_EDITED from konova.utils.user_checks import in_group @@ -515,6 +515,30 @@ def action_edit_view(request: HttpRequest, id: str, action_id: str): ) +@login_required +@default_group_required +@shared_access_required(EcoAccount, "id") +def deadline_edit_view(request: HttpRequest, id: str, deadline_id: str): + """ Renders a form for editing deadlines from a compensation + + Args: + request (HttpRequest): The incoming request + id (str): The compensation's id + deadline_id (str): The deadline's id + + Returns: + + """ + comp = get_object_or_404(EcoAccount, id=id) + deadline = get_object_or_404(Deadline, id=deadline_id) + form = EditDeadlineModalForm(request.POST or None, instance=comp, deadline=deadline, request=request) + return form.process_request( + request, + msg_success=DEADLINE_EDITED, + redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" + ) + + @login_required @default_group_required @shared_access_required(EcoAccount, "id")