#86 Edit deductions

* adds support for editing deductions
* adds tests
* improves major base test logic
pull/111/head
mpeltriaux 3 years ago
parent ce9143e4b2
commit d106977c34

@ -12,15 +12,17 @@ class BaseAPIV1TestCase(BaseTestCase):
def setUpTestData(cls): def setUpTestData(cls):
super().setUpTestData() super().setUpTestData()
cls.superuser.get_API_token() def setUp(self) -> None:
cls.superuser.api_token.is_active = True super().setUp()
cls.superuser.api_token.save() self.superuser.get_API_token()
default_group = cls.groups.get(name=DEFAULT_GROUP) self.superuser.api_token.is_active = True
cls.superuser.groups.add(default_group) self.superuser.api_token.save()
default_group = self.groups.get(name=DEFAULT_GROUP)
cls.header_data = { self.superuser.groups.add(default_group)
"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,
} }

@ -60,9 +60,12 @@
</td> </td>
<td class="align-middle">{{ deduction.surface|floatformat:2|intcomma }} m²</td> <td class="align-middle">{{ deduction.surface|floatformat:2|intcomma }} m²</td>
<td class="align-middle">{{ deduction.created.timestamp|default_if_none:""|naturalday}}</td> <td class="align-middle">{{ deduction.created.timestamp|default_if_none:""|naturalday}}</td>
<td> <td class="align-middle float-right">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:acc:remove-deduction' deduction.account.id deduction.id %}" class="btn btn-default btn-modal float-right" title="{% trans 'Remove Deduction' %}"> <button data-form-url="{% url 'compensation:acc:edit-deduction' deduction.account.id deduction.id %}" class="btn btn-default btn-modal" title="{% trans 'Edit Deduction' %}">
{% fa5_icon 'edit' %}
</button>
<button data-form-url="{% url 'compensation:acc:remove-deduction' deduction.account.id deduction.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove Deduction' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}
</button> </button>
{% endif %} {% endif %}

@ -21,29 +21,32 @@ class CompensationViewTestCase(BaseViewTestCase):
@classmethod @classmethod
def setUpTestData(cls) -> None: def setUpTestData(cls) -> None:
super().setUpTestData() super().setUpTestData()
state = cls.create_dummy_states()
cls.compensation.before_states.set([state])
cls.compensation.after_states.set([state])
action = cls.create_dummy_action() def setUp(self) -> None:
cls.compensation.actions.set([action]) super().setUp()
state = self.create_dummy_states()
self.compensation.before_states.set([state])
self.compensation.after_states.set([state])
action = self.create_dummy_action()
self.compensation.actions.set([action])
# Prepare urls # Prepare urls
cls.index_url = reverse("compensation:index", args=()) self.index_url = reverse("compensation:index", args=())
cls.new_url = reverse("compensation:new", args=(cls.intervention.id,)) self.new_url = reverse("compensation:new", args=(self.intervention.id,))
cls.new_id_url = reverse("compensation:new-id", args=()) self.new_id_url = reverse("compensation:new-id", args=())
cls.detail_url = reverse("compensation:detail", args=(cls.compensation.id,)) self.detail_url = reverse("compensation:detail", args=(self.compensation.id,))
cls.log_url = reverse("compensation:log", args=(cls.compensation.id,)) self.log_url = reverse("compensation:log", args=(self.compensation.id,))
cls.edit_url = reverse("compensation:edit", args=(cls.compensation.id,)) self.edit_url = reverse("compensation:edit", args=(self.compensation.id,))
cls.remove_url = reverse("compensation:remove", args=(cls.compensation.id,)) self.remove_url = reverse("compensation:remove", args=(self.compensation.id,))
cls.report_url = reverse("compensation:report", args=(cls.compensation.id,)) self.report_url = reverse("compensation:report", args=(self.compensation.id,))
cls.state_new_url = reverse("compensation:new-state", args=(cls.compensation.id,)) self.state_new_url = reverse("compensation:new-state", args=(self.compensation.id,))
cls.action_new_url = reverse("compensation:new-action", args=(cls.compensation.id,)) self.action_new_url = reverse("compensation:new-action", args=(self.compensation.id,))
cls.deadline_new_url = reverse("compensation:new-deadline", args=(cls.compensation.id,)) self.deadline_new_url = reverse("compensation:new-deadline", args=(self.compensation.id,))
cls.new_doc_url = reverse("compensation:new-doc", args=(cls.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,)) self.state_remove_url = reverse("compensation:state-remove", args=(self.compensation.id, self.comp_state.id,))
cls.action_remove_url = reverse("compensation:action-remove", args=(cls.compensation.id, cls.comp_action.id,)) self.action_remove_url = reverse("compensation:action-remove", args=(self.compensation.id, self.comp_action.id,))
def test_anonymous_user(self): def test_anonymous_user(self):
""" Check correct status code for all requests """ Check correct status code for all requests

@ -21,17 +21,18 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
def setUpTestData(cls): def setUpTestData(cls):
super().setUpTestData() super().setUpTestData()
def setUp(self) -> None:
super().setUp()
# Give the user shared access to the dummy intervention -> inherits the access to the compensation # Give the user shared access to the dummy intervention -> inherits the access to the compensation
cls.intervention.share_with(cls.superuser) self.intervention.share_with(self.superuser)
# Make sure the intervention itself would be fine with valid data # Make sure the intervention itself would be fine with valid data
cls.intervention = cls.fill_out_intervention(cls.intervention) self.intervention = self.fill_out_intervention(self.intervention)
# Make sure the compensation is linked to the intervention # Make sure the compensation is linked to the intervention
cls.intervention.compensations.set([cls.compensation]) self.intervention.compensations.set([self.compensation])
def setUp(self) -> None:
super().setUp()
# Delete all existing compensations, which might be created by tests # Delete all existing compensations, which might be created by tests
Compensation.objects.all().delete() Compensation.objects.all().delete()

@ -25,28 +25,31 @@ class EcoAccountViewTestCase(CompensationViewTestCase):
@classmethod @classmethod
def setUpTestData(cls) -> None: def setUpTestData(cls) -> None:
super().setUpTestData() 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() def setUp(self) -> None:
cls.eco_account.actions.set([action]) super().setUp()
state = self.create_dummy_states()
self.eco_account.before_states.set([state])
self.eco_account.after_states.set([state])
action = self.create_dummy_action()
self.eco_account.actions.set([action])
# Prepare urls # Prepare urls
cls.index_url = reverse("compensation:acc:index", args=()) self.index_url = reverse("compensation:acc:index", args=())
cls.new_url = reverse("compensation:acc:new", args=()) self.new_url = reverse("compensation:acc:new", args=())
cls.new_id_url = reverse("compensation:acc:new-id", args=()) self.new_id_url = reverse("compensation:acc:new-id", args=())
cls.detail_url = reverse("compensation:acc:detail", args=(cls.eco_account.id,)) self.detail_url = reverse("compensation:acc:detail", args=(self.eco_account.id,))
cls.log_url = reverse("compensation:acc:log", args=(cls.eco_account.id,)) self.log_url = reverse("compensation:acc:log", args=(self.eco_account.id,))
cls.edit_url = reverse("compensation:acc:edit", args=(cls.eco_account.id,)) self.edit_url = reverse("compensation:acc:edit", args=(self.eco_account.id,))
cls.remove_url = reverse("compensation:acc:remove", args=(cls.eco_account.id,)) self.remove_url = reverse("compensation:acc:remove", args=(self.eco_account.id,))
cls.report_url = reverse("compensation:acc:report", args=(cls.eco_account.id,)) self.report_url = reverse("compensation:acc:report", args=(self.eco_account.id,))
cls.state_new_url = reverse("compensation:acc:new-state", args=(cls.eco_account.id,)) self.state_new_url = reverse("compensation:acc:new-state", args=(self.eco_account.id,))
cls.action_new_url = reverse("compensation:acc:new-action", args=(cls.eco_account.id,)) self.action_new_url = reverse("compensation:acc:new-action", args=(self.eco_account.id,))
cls.deadline_new_url = reverse("compensation:acc:new-deadline", args=(cls.eco_account.id,)) self.deadline_new_url = reverse("compensation:acc:new-deadline", args=(self.eco_account.id,))
cls.new_doc_url = reverse("compensation:acc:new-doc", args=(cls.eco_account.id,)) self.new_doc_url = reverse("compensation:acc:new-doc", args=(self.eco_account.id,))
cls.state_remove_url = reverse("compensation:acc:state-remove", args=(cls.eco_account.id, cls.comp_state.id,)) self.state_remove_url = reverse("compensation:acc:state-remove", args=(self.eco_account.id, self.comp_state.id,))
cls.action_remove_url = reverse("compensation:acc:action-remove", args=(cls.eco_account.id, cls.comp_action.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): def test_logged_in_no_groups_shared(self):
""" Check correct status code for all requests """ Check correct status code for all requests

@ -11,7 +11,7 @@ from django.contrib.gis.geos import MultiPolygon
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.urls import reverse 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.settings import ETS_GROUP, DEFAULT_GROUP
from konova.tests.test_views import BaseWorkflowTestCase from konova.tests.test_views import BaseWorkflowTestCase
from user.models import UserAction from user.models import UserAction
@ -168,7 +168,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
self.assertIn(recorded, self.eco_account.log.all()) self.assertIn(recorded, self.eco_account.log.all())
self.assertEqual(pre_record_log_count + 1, self.eco_account.log.count()) 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. This tests the deductability of an eco account.
@ -187,7 +187,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
test_surface = 10.00 test_surface = 10.00
post_data = { post_data = {
"surface": test_surface, "surface": test_surface,
"account": self.id, "account": self.eco_account.id,
"intervention": self.intervention.id, "intervention": self.intervention.id,
} }
# Perform request --> expect to fail # 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.assertEqual(pre_deduction_int_log_count + 1, self.intervention.log.count())
self.assertTrue(self.intervention.log.first().action == UserAction.EDITED) 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)

@ -19,16 +19,18 @@ class PaymentViewTestCase(BaseViewTestCase):
def setUpTestData(cls) -> None: def setUpTestData(cls) -> None:
super().setUpTestData() super().setUpTestData()
cls.payment = Payment.objects.get_or_create( def setUp(self) -> None:
intervention=cls.intervention, super().setUp()
self.payment = Payment.objects.get_or_create(
intervention=self.intervention,
amount=1, amount=1,
due_on="2020-01-01", due_on="2020-01-01",
comment="Testcomment" comment="Testcomment"
)[0] )[0]
cls.new_url = reverse("compensation:pay:new", args=(cls.intervention.id,)) self.new_url = reverse("compensation:pay:new", args=(self.intervention.id,))
cls.edit_url = reverse("compensation:pay:edit", args=(cls.intervention.id, cls.payment.id)) self.edit_url = reverse("compensation:pay:edit", args=(self.intervention.id, self.payment.id))
cls.remove_url = reverse("compensation:pay:remove", args=(cls.intervention.id, cls.payment.id)) self.remove_url = reverse("compensation:pay:remove", args=(self.intervention.id, self.payment.id))
def test_anonymous_user(self): def test_anonymous_user(self):
""" Check correct status code for all requests """ Check correct status code for all requests

@ -18,11 +18,13 @@ class PaymentWorkflowTestCase(BaseWorkflowTestCase):
def setUpTestData(cls): def setUpTestData(cls):
super().setUpTestData() super().setUpTestData()
def setUp(self) -> None:
super().setUp()
# Give the user shared access to the dummy intervention # 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( self.payment = Payment.objects.get_or_create(
intervention=cls.intervention, intervention=self.intervention,
amount=1, amount=1,
due_on="2020-01-01", due_on="2020-01-01",
comment="Testcomment" comment="Testcomment"

@ -34,7 +34,8 @@ urlpatterns = [
path('document/<doc_id>/remove/', remove_document_view, name='remove-doc'), path('document/<doc_id>/remove/', remove_document_view, name='remove-doc'),
# Eco-account deductions # Eco-account deductions
path('<id>/remove/<deduction_id>', deduction_remove_view, name='remove-deduction'), path('<id>/deduction/<deduction_id>/remove', deduction_remove_view, name='remove-deduction'),
path('<id>/deduction/<deduction_id>/edit', deduction_edit_view, name='edit-deduction'),
path('<id>/deduct/new', new_deduction_view, name='new-deduction'), path('<id>/deduct/new', new_deduction_view, name='new-deduction'),
] ]

@ -19,7 +19,8 @@ from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm,
NewEcoAccountDocumentForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm NewEcoAccountDocumentForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm
from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction
from compensation.tables import EcoAccountTable 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.contexts import BaseContext
from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \ from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \
shared_access_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.generators import generate_qr_code
from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ 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, \ 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 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 @login_required
@default_group_required @default_group_required
@shared_access_required(EcoAccount, "id") @shared_access_required(EcoAccount, "id")

@ -31,42 +31,43 @@ class EmaViewTestCase(CompensationViewTestCase):
def setUpTestData(cls) -> None: def setUpTestData(cls) -> None:
super().setUpTestData() super().setUpTestData()
def setUp(self) -> None:
super().setUp()
# Create dummy data and related objects, like states or actions # Create dummy data and related objects, like states or actions
cls.create_dummy_data() self.create_dummy_data()
state = cls.create_dummy_states() state = self.create_dummy_states()
action = cls.create_dummy_action() action = self.create_dummy_action()
cls.ema.before_states.set([state]) self.ema.before_states.set([state])
cls.ema.after_states.set([state]) self.ema.after_states.set([state])
cls.ema.actions.set([action]) self.ema.actions.set([action])
# Prepare urls # Prepare urls
cls.index_url = reverse("ema:index", args=()) self.index_url = reverse("ema:index", args=())
cls.new_url = reverse("ema:new", args=()) self.new_url = reverse("ema:new", args=())
cls.new_id_url = reverse("ema:new-id", args=()) self.new_id_url = reverse("ema:new-id", args=())
cls.detail_url = reverse("ema:detail", args=(cls.ema.id,)) self.detail_url = reverse("ema:detail", args=(self.ema.id,))
cls.log_url = reverse("ema:log", args=(cls.ema.id,)) self.log_url = reverse("ema:log", args=(self.ema.id,))
cls.edit_url = reverse("ema:edit", args=(cls.ema.id,)) self.edit_url = reverse("ema:edit", args=(self.ema.id,))
cls.remove_url = reverse("ema:remove", args=(cls.ema.id,)) self.remove_url = reverse("ema:remove", args=(self.ema.id,))
cls.share_url = reverse("ema:share", args=(cls.ema.id, cls.ema.access_token,)) self.share_url = reverse("ema:share", args=(self.ema.id, self.ema.access_token,))
cls.share_create_url = reverse("ema:share-create", args=(cls.ema.id,)) self.share_create_url = reverse("ema:share-create", args=(self.ema.id,))
cls.record_url = reverse("ema:record", args=(cls.ema.id,)) self.record_url = reverse("ema:record", args=(self.ema.id,))
cls.report_url = reverse("ema:report", args=(cls.ema.id,)) self.report_url = reverse("ema:report", args=(self.ema.id,))
cls.new_doc_url = reverse("ema:new-doc", args=(cls.ema.id,)) self.new_doc_url = reverse("ema:new-doc", args=(self.ema.id,))
cls.state_new_url = reverse("ema:new-state", args=(cls.ema.id,)) self.state_new_url = reverse("ema:new-state", args=(self.ema.id,))
cls.action_new_url = reverse("ema:new-action", args=(cls.ema.id,)) self.action_new_url = reverse("ema:new-action", args=(self.ema.id,))
cls.deadline_new_url = reverse("ema:new-deadline", args=(cls.ema.id,)) self.deadline_new_url = reverse("ema:new-deadline", args=(self.ema.id,))
cls.state_remove_url = reverse("ema:state-remove", args=(cls.ema.id, state.id,)) self.state_remove_url = reverse("ema:state-remove", args=(self.ema.id, state.id,))
cls.action_remove_url = reverse("ema:action-remove", args=(cls.ema.id, action.id,)) self.action_remove_url = reverse("ema:action-remove", args=(self.ema.id, action.id,))
@classmethod def create_dummy_data(self):
def create_dummy_data(cls):
# Create dummy data # Create dummy data
# Create log entry # Create log entry
action = UserActionLogEntry.get_created_action(cls.superuser) action = UserActionLogEntry.get_created_action(self.superuser)
# Create responsible data object # Create responsible data object
responsibility_data = Responsibility.objects.create() responsibility_data = Responsibility.objects.create()
geometry = Geometry.objects.create() geometry = Geometry.objects.create()
cls.ema = Ema.objects.create( self.ema = Ema.objects.create(
identifier="TEST", identifier="TEST",
title="Test_title", title="Test_title",
created=action, created=action,

@ -7,7 +7,7 @@ Created on: 27.09.21
""" """
from dal import autocomplete 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 user.models import User, UserActionLogEntry
from django.db import transaction from django.db import transaction
from django import forms from django import forms
@ -349,6 +349,21 @@ class NewDeductionModalForm(BaseModalForm):
else: else:
raise NotImplementedError 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): def is_valid(self):
""" Custom validity check """ Custom validity check
@ -367,10 +382,7 @@ class NewDeductionModalForm(BaseModalForm):
) )
return False return False
# Calculate valid surface rest_surface = self._get_available_surface(acc)
deductable_surface = acc.deductable_surface
sum_surface_deductions = acc.get_deductions_surface()
rest_surface = deductable_surface - sum_surface_deductions
form_surface = float(self.cleaned_data["surface"]) form_surface = float(self.cleaned_data["surface"])
is_valid_surface = form_surface <= rest_surface is_valid_surface = form_surface <= rest_surface
if not is_valid_surface: if not is_valid_surface:
@ -407,6 +419,57 @@ class NewDeductionModalForm(BaseModalForm):
return deduction 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): class RemoveEcoAccountDeductionModalForm(RemoveModalForm):
""" Removing modal form for EcoAccountDeduction """ Removing modal form for EcoAccountDeduction

@ -55,9 +55,12 @@
</td> </td>
<td class="align-middle">{{ deduction.surface|floatformat:2|intcomma }} m²</td> <td class="align-middle">{{ deduction.surface|floatformat:2|intcomma }} m²</td>
<td class="align-middle">{{ deduction.created.timestamp|default_if_none:""|naturalday}}</td> <td class="align-middle">{{ deduction.created.timestamp|default_if_none:""|naturalday}}</td>
<td> <td class="align-middle float-right">
{% if is_default_member and has_access %} {% if is_default_member and has_access %}
<button data-form-url="{% url 'intervention:remove-deduction' obj.id deduction.id %}" class="btn btn-default btn-modal float-right" title="{% trans 'Remove Deduction' %}"> <button data-form-url="{% url 'intervention:edit-deduction' obj.id deduction.id %}" class="btn btn-default btn-modal" title="{% trans 'Edit Deduction' %}">
{% fa5_icon 'edit' %}
</button>
<button data-form-url="{% url 'intervention:remove-deduction' obj.id deduction.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove Deduction' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}
</button> </button>
{% endif %} {% endif %}

@ -21,30 +21,32 @@ class InterventionViewTestCase(BaseViewTestCase):
def setUpTestData(cls) -> None: def setUpTestData(cls) -> None:
super().setUpTestData() super().setUpTestData()
def setUp(self) -> None:
super().setUp()
# Prepare urls # Prepare urls
cls.index_url = reverse("intervention:index", args=()) self.index_url = reverse("intervention:index", args=())
cls.new_url = reverse("intervention:new", args=()) self.new_url = reverse("intervention:new", args=())
cls.new_id_url = reverse("intervention:new-id", args=()) self.new_id_url = reverse("intervention:new-id", args=())
cls.detail_url = reverse("intervention:detail", args=(cls.intervention.id,)) self.detail_url = reverse("intervention:detail", args=(self.intervention.id,))
cls.log_url = reverse("intervention:log", args=(cls.intervention.id,)) self.log_url = reverse("intervention:log", args=(self.intervention.id,))
cls.edit_url = reverse("intervention:edit", args=(cls.intervention.id,)) self.edit_url = reverse("intervention:edit", args=(self.intervention.id,))
cls.remove_url = reverse("intervention:remove", args=(cls.intervention.id,)) self.remove_url = reverse("intervention:remove", args=(self.intervention.id,))
cls.share_url = reverse("intervention:share", args=(cls.intervention.id, cls.intervention.access_token,)) self.share_url = reverse("intervention:share", args=(self.intervention.id, self.intervention.access_token,))
cls.share_create_url = reverse("intervention:share-create", args=(cls.intervention.id,)) self.share_create_url = reverse("intervention:share-create", args=(self.intervention.id,))
cls.run_check_url = reverse("intervention:check", args=(cls.intervention.id,)) self.run_check_url = reverse("intervention:check", args=(self.intervention.id,))
cls.record_url = reverse("intervention:record", args=(cls.intervention.id,)) self.record_url = reverse("intervention:record", args=(self.intervention.id,))
cls.report_url = reverse("intervention:report", args=(cls.intervention.id,)) self.report_url = reverse("intervention:report", args=(self.intervention.id,))
cls.deduction.intervention = cls.intervention self.deduction.intervention = self.intervention
cls.deduction.save() self.deduction.save()
cls.deduction_new_url = reverse("intervention:new-deduction", args=(cls.intervention.id,)) self.deduction_new_url = reverse("intervention:new-deduction", args=(self.intervention.id,))
cls.deduction_remove_url = reverse("intervention:remove-deduction", args=(cls.intervention.id, cls.deduction.id)) self.deduction_remove_url = reverse("intervention:remove-deduction", args=(self.intervention.id, self.deduction.id))
cls.revocation = Revocation.objects.create( self.revocation = Revocation.objects.create(
legal=cls.intervention.legal legal=self.intervention.legal
) )
cls.revocation_new_url = reverse("intervention:new-revocation", args=(cls.intervention.id,)) self.revocation_new_url = reverse("intervention:new-revocation", args=(self.intervention.id,))
cls.revocation_remove_url = reverse("intervention:remove-revocation", args=(cls.intervention.id, cls.revocation.id)) self.revocation_remove_url = reverse("intervention:remove-revocation", args=(self.intervention.id, self.revocation.id))
def test_views_anonymous_user(self): def test_views_anonymous_user(self):
""" Check correct status code for all requests """ Check correct status code for all requests

@ -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, \ 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, \ 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, \ 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" app_name = "intervention"
urlpatterns = [ urlpatterns = [
@ -37,6 +37,7 @@ urlpatterns = [
# Deductions # Deductions
path('<id>/deduction/new', new_deduction_view, name='new-deduction'), path('<id>/deduction/new', new_deduction_view, name='new-deduction'),
path('<id>/deduction/<deduction_id>/edit', edit_deduction_view, name='edit-deduction'),
path('<id>/deduction/<deduction_id>/remove', remove_deduction_view, name='remove-deduction'), path('<id>/deduction/<deduction_id>/remove', remove_deduction_view, name='remove-deduction'),
# Revocation routes # Revocation routes

@ -7,7 +7,7 @@ from django.shortcuts import render
from intervention.forms.forms import NewInterventionForm, EditInterventionForm from intervention.forms.forms import NewInterventionForm, EditInterventionForm
from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \ from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \
CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm, RemoveEcoAccountDeductionModalForm, \ CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm, RemoveEcoAccountDeductionModalForm, \
RemoveRevocationModalForm RemoveRevocationModalForm, EditEcoAccountDeductionModalForm
from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument
from intervention.tables import InterventionTable from intervention.tables import InterventionTable
from konova.contexts import BaseContext 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.generators import generate_qr_code
from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED, \ from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED, \
CHECKED_RECORDED_RESET, DEDUCTION_REMOVED, DEDUCTION_ADDED, REVOCATION_ADDED, REVOCATION_REMOVED, \ 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 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 @login_required
@conservation_office_group_required @conservation_office_group_required
@shared_access_required(Intervention, "id") @shared_access_required(Intervention, "id")

@ -47,43 +47,58 @@ class BaseTestCase(TestCase):
class Meta: class Meta:
abstract = True abstract = True
@classmethod def setUp(self) -> None:
def setUpTestData(cls): """ Setup data before each test run
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()
@classmethod Returns:
def create_users(cls):
"""
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 # Create superuser and regular user
cls.superuser = User.objects.create_superuser( self.superuser = User.objects.create_superuser(
username="root", username="root",
email="root@root.com", 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", username="user1",
email="user@root.com", 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 # Create groups
for group_data in GROUPS_DATA: for group_data in GROUPS_DATA:
name = group_data.get("name") name = group_data.get("name")
Group.objects.get_or_create( Group.objects.get_or_create(
name=name, name=name,
) )
cls.groups = Group.objects.all() self.groups = Group.objects.all()
@staticmethod @staticmethod
def create_dummy_string(prefix: str = ""): def create_dummy_string(prefix: str = ""):
@ -94,8 +109,7 @@ class BaseTestCase(TestCase):
""" """
return f"{prefix}{generate_random_string(3, True)}" return f"{prefix}{generate_random_string(3, True)}"
@classmethod def create_dummy_intervention(self):
def create_dummy_intervention(cls):
""" Creates an intervention which can be used for tests """ Creates an intervention which can be used for tests
Returns: Returns:
@ -103,7 +117,7 @@ class BaseTestCase(TestCase):
""" """
# Create dummy data # Create dummy data
# Create log entry # 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) # Create legal data object (without M2M laws first)
legal_data = Legal.objects.create() legal_data = Legal.objects.create()
# Create responsible data object # Create responsible data object
@ -122,32 +136,30 @@ class BaseTestCase(TestCase):
intervention.generate_access_token(make_unique=True) intervention.generate_access_token(make_unique=True)
return intervention return intervention
@classmethod def create_dummy_compensation(self):
def create_dummy_compensation(cls):
""" Creates a compensation which can be used for tests """ Creates a compensation which can be used for tests
Returns: Returns:
""" """
if cls.intervention is None: if self.intervention is None:
cls.intervention = cls.create_dummy_intervention() self.intervention = self.create_dummy_intervention()
# Create dummy data # Create dummy data
# Create log entry # Create log entry
action = UserActionLogEntry.get_created_action(cls.superuser) action = UserActionLogEntry.get_created_action(self.superuser)
geometry = Geometry.objects.create() geometry = Geometry.objects.create()
# Finally create main object, holding the other objects # Finally create main object, holding the other objects
compensation = Compensation.objects.create( compensation = Compensation.objects.create(
identifier="TEST", identifier="TEST",
title="Test_title", title="Test_title",
intervention=cls.intervention, intervention=self.intervention,
created=action, created=action,
geometry=geometry, geometry=geometry,
comment="Test", comment="Test",
) )
return compensation return compensation
@classmethod def create_dummy_eco_account(self):
def create_dummy_eco_account(cls):
""" Creates an eco account which can be used for tests """ Creates an eco account which can be used for tests
Returns: Returns:
@ -155,7 +167,7 @@ class BaseTestCase(TestCase):
""" """
# Create dummy data # Create dummy data
# Create log entry # Create log entry
action = UserActionLogEntry.get_created_action(cls.superuser) action = UserActionLogEntry.get_created_action(self.superuser)
geometry = Geometry.objects.create() geometry = Geometry.objects.create()
# Create responsible data object # Create responsible data object
lega_data = Legal.objects.create() lega_data = Legal.objects.create()
@ -172,8 +184,7 @@ class BaseTestCase(TestCase):
) )
return eco_account return eco_account
@classmethod def create_dummy_ema(self):
def create_dummy_ema(cls):
""" Creates an ema which can be used for tests """ Creates an ema which can be used for tests
Returns: Returns:
@ -181,7 +192,7 @@ class BaseTestCase(TestCase):
""" """
# Create dummy data # Create dummy data
# Create log entry # Create log entry
action = UserActionLogEntry.get_created_action(cls.superuser) action = UserActionLogEntry.get_created_action(self.superuser)
geometry = Geometry.objects.create() geometry = Geometry.objects.create()
# Create responsible data object # Create responsible data object
responsible_data = Responsibility.objects.create() responsible_data = Responsibility.objects.create()
@ -196,41 +207,37 @@ class BaseTestCase(TestCase):
) )
return ema return ema
@classmethod def create_dummy_deduction(self):
def create_dummy_deduction(cls):
return EcoAccountDeduction.objects.create( return EcoAccountDeduction.objects.create(
account=cls.create_dummy_eco_account(), account=self.create_dummy_eco_account(),
intervention=cls.create_dummy_intervention(), intervention=self.create_dummy_intervention(),
surface=100, surface=100,
) )
@classmethod def create_dummy_states(self):
def create_dummy_states(cls):
""" Creates an intervention which can be used for tests """ Creates an intervention which can be used for tests
Returns: Returns:
""" """
cls.comp_state = CompensationState.objects.create( self.comp_state = CompensationState.objects.create(
surface=10.00, surface=10.00,
biotope_type=None, biotope_type=None,
) )
return cls.comp_state return self.comp_state
@classmethod def create_dummy_action(self):
def create_dummy_action(cls):
""" Creates an intervention which can be used for tests """ Creates an intervention which can be used for tests
Returns: Returns:
""" """
cls.comp_action = CompensationAction.objects.create( self.comp_action = CompensationAction.objects.create(
amount=10 amount=10
) )
return cls.comp_action return self.comp_action
@classmethod def create_dummy_codes(self):
def create_dummy_codes(cls):
""" Creates some dummy KonovaCodes which can be used for testing """ Creates some dummy KonovaCodes which can be used for testing
Returns: Returns:
@ -256,8 +263,7 @@ class BaseTestCase(TestCase):
polygon = polygon.transform(3857, clone=True) polygon = polygon.transform(3857, clone=True)
return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form
@classmethod def fill_out_intervention(self, intervention: Intervention) -> Intervention:
def fill_out_intervention(cls, intervention: Intervention) -> Intervention:
""" Adds all required (dummy) data to an intervention """ Adds all required (dummy) data to an intervention
Args: Args:
@ -277,13 +283,12 @@ class BaseTestCase(TestCase):
intervention.legal.process_type = KonovaCode.objects.get(id=3) intervention.legal.process_type = KonovaCode.objects.get(id=3)
intervention.legal.save() intervention.legal.save()
intervention.legal.laws.set([KonovaCode.objects.get(id=(4))]) 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.geometry.save()
intervention.save() intervention.save()
return intervention return intervention
@classmethod def fill_out_compensation(self, compensation: Compensation) -> Compensation:
def fill_out_compensation(cls, compensation: Compensation) -> Compensation:
""" Adds all required (dummy) data to a compensation """ Adds all required (dummy) data to a compensation
Args: Args:
@ -292,15 +297,14 @@ class BaseTestCase(TestCase):
Returns: Returns:
compensation (Compensation): The modified compensation compensation (Compensation): The modified compensation
""" """
compensation.after_states.add(cls.comp_state) compensation.after_states.add(self.comp_state)
compensation.before_states.add(cls.comp_state) compensation.before_states.add(self.comp_state)
compensation.actions.add(cls.comp_action) compensation.actions.add(self.comp_action)
compensation.geometry.geom = cls.create_dummy_geometry() compensation.geometry.geom = self.create_dummy_geometry()
compensation.geometry.save() compensation.geometry.save()
return compensation return compensation
@classmethod def get_conservation_office_code(self):
def get_conservation_office_code(cls):
""" Returns a dummy KonovaCode as conservation office code """ Returns a dummy KonovaCode as conservation office code
Returns: Returns:
@ -313,39 +317,37 @@ class BaseTestCase(TestCase):
codelist.codes.add(code) codelist.codes.add(code)
return code return code
@classmethod def fill_out_ema(self, ema):
def fill_out_ema(cls, ema):
""" Adds all required (dummy) data to an Ema """ Adds all required (dummy) data to an Ema
Returns: 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.conservation_file_number = "test"
ema.responsible.handler = "handler" ema.responsible.handler = "handler"
ema.responsible.save() ema.responsible.save()
ema.after_states.add(cls.comp_state) ema.after_states.add(self.comp_state)
ema.before_states.add(cls.comp_state) ema.before_states.add(self.comp_state)
ema.actions.add(cls.comp_action) ema.actions.add(self.comp_action)
ema.geometry.geom = cls.create_dummy_geometry() ema.geometry.geom = self.create_dummy_geometry()
ema.geometry.save() ema.geometry.save()
return ema return ema
@classmethod def fill_out_eco_account(self, eco_account):
def fill_out_eco_account(cls, eco_account):
""" Adds all required (dummy) data to an EcoAccount """ Adds all required (dummy) data to an EcoAccount
Returns: Returns:
""" """
eco_account.legal.registration_date = "2022-01-01" eco_account.legal.registration_date = "2022-01-01"
eco_account.legal.save() 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.conservation_file_number = "test"
eco_account.responsible.handler = "handler" eco_account.responsible.handler = "handler"
eco_account.responsible.save() eco_account.responsible.save()
eco_account.after_states.add(cls.comp_state) eco_account.after_states.add(self.comp_state)
eco_account.before_states.add(cls.comp_state) eco_account.before_states.add(self.comp_state)
eco_account.actions.add(cls.comp_action) eco_account.actions.add(self.comp_action)
eco_account.geometry.geom = cls.create_dummy_geometry() eco_account.geometry.geom = self.create_dummy_geometry()
eco_account.geometry.save() eco_account.geometry.save()
eco_account.deductable_surface = eco_account.get_state_after_surface_sum() eco_account.deductable_surface = eco_account.get_state_after_surface_sum()
eco_account.save() eco_account.save()
@ -390,7 +392,10 @@ class BaseViewTestCase(BaseTestCase):
@classmethod @classmethod
def setUpTestData(cls) -> None: def setUpTestData(cls) -> None:
super().setUpTestData() 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): def assert_url_success(self, client: Client, urls: list):
""" Assert for all given urls a direct 200 response """ Assert for all given urls a direct 200 response
@ -549,22 +554,6 @@ class BaseWorkflowTestCase(BaseTestCase):
def setUpTestData(cls): def setUpTestData(cls):
super().setUpTestData() 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): def assert_object_is_deleted(self, obj):
""" Provides a quick check whether an object has been removed from the database or not """ Provides a quick check whether an object has been removed from the database or not

@ -30,18 +30,22 @@ ADDED_COMPENSATION_STATE = _("Added compensation state")
# COMPENSATION STATE # COMPENSATION STATE
COMPENSATION_STATE_REMOVED = _("State removed") COMPENSATION_STATE_REMOVED = _("State removed")
COMPENSATION_STATE_EDITED = _("State edited")
COMPENSATION_STATE_ADDED = _("State added") COMPENSATION_STATE_ADDED = _("State added")
# COMPENSATION ACTION # COMPENSATION ACTION
COMPENSATION_ACTION_ADDED = _("Action added") COMPENSATION_ACTION_ADDED = _("Action added")
COMPENSATION_ACTION_EDITED = _("Action edited")
COMPENSATION_ACTION_REMOVED = _("Action removed") COMPENSATION_ACTION_REMOVED = _("Action removed")
# DEDUCTIONS # DEDUCTIONS
DEDUCTION_ADDED = _("Deduction added") DEDUCTION_ADDED = _("Deduction added")
DEDUCTION_EDITED = _("Deduction edited")
DEDUCTION_REMOVED = _("Deduction removed") DEDUCTION_REMOVED = _("Deduction removed")
# DEADLINE # DEADLINE
DEADLINE_ADDED = _("Deadline added") DEADLINE_ADDED = _("Deadline added")
DEADLINE_EDITED = _("Deadline edited")
DEADLINE_REMOVED = _("Deadline removed") DEADLINE_REMOVED = _("Deadline removed")
# PAYMENTS # PAYMENTS
@ -51,6 +55,7 @@ PAYMENT_REMOVED = _("Payment removed")
# REVOCATIONS # REVOCATIONS
REVOCATION_ADDED = _("Revocation added") REVOCATION_ADDED = _("Revocation added")
REVOCATION_EDITED = _("Revocation edited")
REVOCATION_REMOVED = _("Revocation removed") REVOCATION_REMOVED = _("Revocation removed")
# DOCUMENTS # DOCUMENTS

Binary file not shown.

@ -5,7 +5,7 @@
# #
#: compensation/filters.py:122 compensation/forms/modalForms.py:35 #: compensation/filters.py:122 compensation/forms/modalForms.py:35
#: compensation/forms/modalForms.py:46 compensation/forms/modalForms.py:62 #: compensation/forms/modalForms.py:46 compensation/forms/modalForms.py:62
#: compensation/forms/modalForms.py:329 compensation/forms/modalForms.py:422 #: compensation/forms/modalForms.py:332 compensation/forms/modalForms.py:425
#: intervention/forms/forms.py:54 intervention/forms/forms.py:156 #: intervention/forms/forms.py:54 intervention/forms/forms.py:156
#: intervention/forms/forms.py:168 intervention/forms/modalForms.py:124 #: intervention/forms/forms.py:168 intervention/forms/modalForms.py:124
#: intervention/forms/modalForms.py:137 intervention/forms/modalForms.py:150 #: intervention/forms/modalForms.py:137 intervention/forms/modalForms.py:150
@ -26,7 +26,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-02-09 09:50+0100\n" "POT-Creation-Date: 2022-02-09 12:52+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -95,7 +95,7 @@ msgstr ""
#: analysis/templates/analysis/reports/includes/eco_account/amount.html:3 #: analysis/templates/analysis/reports/includes/eco_account/amount.html:3
#: analysis/templates/analysis/reports/includes/intervention/amount.html:3 #: analysis/templates/analysis/reports/includes/intervention/amount.html:3
#: analysis/templates/analysis/reports/includes/old_data/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 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:34
#: intervention/templates/intervention/detail/includes/deductions.html:31 #: intervention/templates/intervention/detail/includes/deductions.html:31
msgid "Amount" 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:9
#: analysis/templates/analysis/reports/includes/eco_account/deductions.html:11 #: 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-after.html:36
#: compensation/templates/compensation/detail/compensation/includes/states-before.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-after.html:36
@ -353,7 +353,7 @@ msgid "Compensation XY; Location ABC"
msgstr "Kompensation XY; Flur ABC" msgstr "Kompensation XY; Flur ABC"
#: compensation/forms/forms.py:57 compensation/forms/modalForms.py:61 #: 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/actions.html:35
#: compensation/templates/compensation/detail/compensation/includes/deadlines.html:34 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:34
#: compensation/templates/compensation/detail/compensation/includes/documents.html:31 #: compensation/templates/compensation/detail/compensation/includes/documents.html:31
@ -371,7 +371,7 @@ msgstr "Kompensation XY; Flur ABC"
msgid "Comment" msgid "Comment"
msgstr "Kommentar" 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 #: intervention/forms/forms.py:182
msgid "Additional comment" msgid "Additional comment"
msgstr "Zusätzlicher Kommentar" msgstr "Zusätzlicher Kommentar"
@ -457,7 +457,7 @@ msgstr "Vereinbarungsdatum"
msgid "When did the parties agree on this?" msgid "When did the parties agree on this?"
msgstr "Wann wurde dieses Ökokonto offiziell vereinbart?" 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" msgid "New Eco-Account"
msgstr "Neues Ökokonto" msgstr "Neues Ökokonto"
@ -482,7 +482,7 @@ msgstr "Fällig am"
msgid "Due on which date" msgid "Due on which date"
msgstr "Zahlung wird an diesem Datum erwartet" 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 #: intervention/forms/modalForms.py:151 konova/forms.py:393
msgid "Additional comment, maximum {} letters" msgid "Additional comment, maximum {} letters"
msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" 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." 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." 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" msgid "Biotope Type"
msgstr "Biotoptyp" msgstr "Biotoptyp"
#: compensation/forms/modalForms.py:157 #: compensation/forms/modalForms.py:160
msgid "Select the biotope type" msgid "Select the biotope type"
msgstr "Biotoptyp wählen" 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" msgid "Biotope additional type"
msgstr "Zusatzbezeichnung" msgstr "Zusatzbezeichnung"
#: compensation/forms/modalForms.py:174 #: compensation/forms/modalForms.py:177
msgid "Select an additional biotope type" msgid "Select an additional biotope type"
msgstr "Zusatzbezeichnung wählen" 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²" msgid "in m²"
msgstr "" msgstr ""
#: compensation/forms/modalForms.py:204 #: compensation/forms/modalForms.py:207
msgid "New state" msgid "New state"
msgstr "Neuer Zustand" msgstr "Neuer Zustand"
#: compensation/forms/modalForms.py:205 #: compensation/forms/modalForms.py:208
msgid "Insert data for the new state" msgid "Insert data for the new state"
msgstr "Geben Sie die Daten des neuen Zustandes ein" 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" msgid "Object removed"
msgstr "Objekt entfernt" msgstr "Objekt entfernt"
#: compensation/forms/modalForms.py:300 #: compensation/forms/modalForms.py:303
msgid "Deadline Type" msgid "Deadline Type"
msgstr "Fristart" msgstr "Fristart"
#: compensation/forms/modalForms.py:303 #: compensation/forms/modalForms.py:306
msgid "Select the deadline type" msgid "Select the deadline type"
msgstr "Fristart wählen" 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/compensation/includes/deadlines.html:31
#: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31 #: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31
#: ema/templates/ema/detail/includes/deadlines.html:31 #: ema/templates/ema/detail/includes/deadlines.html:31
@ -543,27 +543,27 @@ msgstr "Fristart wählen"
msgid "Date" msgid "Date"
msgstr "Datum" msgstr "Datum"
#: compensation/forms/modalForms.py:315 #: compensation/forms/modalForms.py:318
msgid "Select date" msgid "Select date"
msgstr "Datum wählen" msgstr "Datum wählen"
#: compensation/forms/modalForms.py:342 #: compensation/forms/modalForms.py:345
msgid "New deadline" msgid "New deadline"
msgstr "Neue Frist" msgstr "Neue Frist"
#: compensation/forms/modalForms.py:343 #: compensation/forms/modalForms.py:346
msgid "Insert data for the new deadline" msgid "Insert data for the new deadline"
msgstr "Geben Sie die Daten der neuen Frist ein" msgstr "Geben Sie die Daten der neuen Frist ein"
#: compensation/forms/modalForms.py:360 #: compensation/forms/modalForms.py:363
msgid "Action Type" msgid "Action Type"
msgstr "Maßnahmentyp" msgstr "Maßnahmentyp"
#: compensation/forms/modalForms.py:363 #: compensation/forms/modalForms.py:366
msgid "Select the action type" msgid "Select the action type"
msgstr "Maßnahmentyp wählen" 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/actions.html:40
#: compensation/templates/compensation/detail/compensation/includes/deadlines.html:39 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:39
#: compensation/templates/compensation/detail/compensation/includes/documents.html:36 #: compensation/templates/compensation/detail/compensation/includes/documents.html:36
@ -589,31 +589,31 @@ msgstr "Maßnahmentyp wählen"
msgid "Action" msgid "Action"
msgstr "Aktionen" 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" msgid "Action Type detail"
msgstr "Zusatzmerkmal" msgstr "Zusatzmerkmal"
#: compensation/forms/modalForms.py:380 #: compensation/forms/modalForms.py:383
msgid "Select the action type detail" msgid "Select the action type detail"
msgstr "Zusatzmerkmal wählen" msgstr "Zusatzmerkmal wählen"
#: compensation/forms/modalForms.py:394 #: compensation/forms/modalForms.py:397
msgid "Unit" msgid "Unit"
msgstr "Einheit" msgstr "Einheit"
#: compensation/forms/modalForms.py:397 #: compensation/forms/modalForms.py:400
msgid "Select the unit" msgid "Select the unit"
msgstr "Einheit wählen" msgstr "Einheit wählen"
#: compensation/forms/modalForms.py:409 #: compensation/forms/modalForms.py:412
msgid "Insert the amount" msgid "Insert the amount"
msgstr "Menge eingeben" msgstr "Menge eingeben"
#: compensation/forms/modalForms.py:434 #: compensation/forms/modalForms.py:437
msgid "New action" msgid "New action"
msgstr "Neue Maßnahme" msgstr "Neue Maßnahme"
#: compensation/forms/modalForms.py:435 #: compensation/forms/modalForms.py:438
msgid "Insert data for the new action" msgid "Insert data for the new action"
msgstr "Geben Sie die Daten der neuen Maßnahme ein" msgstr "Geben Sie die Daten der neuen Maßnahme ein"
@ -992,7 +992,7 @@ msgid "Recorded on"
msgstr "Verzeichnet am" msgstr "Verzeichnet am"
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:65 #: 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" msgid "Remove Deduction"
msgstr "Abbuchung entfernen" msgstr "Abbuchung entfernen"
@ -1084,63 +1084,63 @@ msgstr "Kompensationen - Übersicht"
msgid "Compensation {} edited" msgid "Compensation {} edited"
msgstr "Kompensation {} bearbeitet" 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 #: ema/views.py:230 intervention/views.py:305
msgid "Edit {}" msgid "Edit {}"
msgstr "Bearbeite {}" 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 #: ema/views.py:191 intervention/views.py:483
msgid "Log" msgid "Log"
msgstr "Log" msgstr "Log"
#: compensation/views/compensation.py:487 compensation/views/eco_account.py:590 #: compensation/views/compensation.py:487 compensation/views/eco_account.py:620
#: ema/views.py:477 intervention/views.py:601 #: ema/views.py:477 intervention/views.py:629
msgid "Report {}" msgid "Report {}"
msgstr "Bericht {}" msgstr "Bericht {}"
#: compensation/views/eco_account.py:60 #: compensation/views/eco_account.py:62
msgid "Eco-account - Overview" msgid "Eco-account - Overview"
msgstr "Ökokonten - Übersicht" msgstr "Ökokonten - Übersicht"
#: compensation/views/eco_account.py:93 #: compensation/views/eco_account.py:95
msgid "Eco-Account {} added" msgid "Eco-Account {} added"
msgstr "Ökokonto {} hinzugefügt" msgstr "Ökokonto {} hinzugefügt"
#: compensation/views/eco_account.py:151 #: compensation/views/eco_account.py:153
msgid "Eco-Account {} edited" msgid "Eco-Account {} edited"
msgstr "Ökokonto {} bearbeitet" msgstr "Ökokonto {} bearbeitet"
#: compensation/views/eco_account.py:264 #: compensation/views/eco_account.py:266
msgid "Eco-account removed" msgid "Eco-account removed"
msgstr "Ökokonto entfernt" msgstr "Ökokonto entfernt"
#: compensation/views/eco_account.py:338 ema/views.py:272 #: compensation/views/eco_account.py:368 ema/views.py:272
#: intervention/views.py:554 #: intervention/views.py:582
msgid "{} unrecorded" msgid "{} unrecorded"
msgstr "{} entzeichnet" msgstr "{} entzeichnet"
#: compensation/views/eco_account.py:338 ema/views.py:272 #: compensation/views/eco_account.py:368 ema/views.py:272
#: intervention/views.py:554 #: intervention/views.py:582
msgid "{} recorded" msgid "{} recorded"
msgstr "{} verzeichnet" 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 #: intervention/views.py:380
msgid "{} has already been shared with you" msgid "{} has already been shared with you"
msgstr "{} wurde bereits für Sie freigegeben" 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 #: intervention/views.py:385
msgid "{} has been shared with you" msgid "{} has been shared with you"
msgstr "{} ist nun für Sie freigegeben" 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 #: intervention/views.py:392
msgid "Share link invalid" msgid "Share link invalid"
msgstr "Freigabelink ungültig" 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 #: intervention/views.py:415
msgid "Share settings updated" msgid "Share settings updated"
msgstr "Freigabe Einstellungen aktualisiert" msgstr "Freigabe Einstellungen aktualisiert"
@ -1337,7 +1337,7 @@ msgstr "Neue Abbuchung"
msgid "Enter the information for a new deduction from a chosen eco-account" msgid "Enter the information for a new deduction from a chosen eco-account"
msgstr "Geben Sie die Informationen für eine neue Abbuchung ein." msgstr "Geben Sie die Informationen für eine neue Abbuchung ein."
#: intervention/forms/modalForms.py:366 #: intervention/forms/modalForms.py:381
msgid "" msgid ""
"Eco-account {} is not recorded yet. You can only deduct from recorded " "Eco-account {} is not recorded yet. You can only deduct from recorded "
"accounts." "accounts."
@ -1345,7 +1345,7 @@ msgstr ""
"Ökokonto {} ist noch nicht verzeichnet. Abbuchungen können nur von " "Ökokonto {} ist noch nicht verzeichnet. Abbuchungen können nur von "
"verzeichneten Ökokonten erfolgen." "verzeichneten Ökokonten erfolgen."
#: intervention/forms/modalForms.py:379 #: intervention/forms/modalForms.py:391
msgid "" msgid ""
"The account {} has not enough surface for a deduction of {} m². There are " "The account {} has not enough surface for a deduction of {} m². There are "
"only {} m² left" "only {} m² left"
@ -1373,6 +1373,10 @@ msgstr "Ökokonto gelöscht! Abbuchung ungültig!"
msgid "Eco-account not recorded! Deduction invalid!" msgid "Eco-account not recorded! Deduction invalid!"
msgstr "Ökokonto nicht verzeichnet! Abbuchung ungültig!" 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/detail/includes/payments.html:8
#: intervention/templates/intervention/report/report.html:73 #: intervention/templates/intervention/report/report.html:73
msgid "Payments" msgid "Payments"
@ -1464,7 +1468,7 @@ msgstr "{} entfernt"
msgid "Check performed" msgid "Check performed"
msgstr "Prüfung durchgeführt" msgstr "Prüfung durchgeführt"
#: intervention/views.py:559 #: intervention/views.py:587
msgid "There are errors on this intervention:" msgid "There are errors on this intervention:"
msgstr "Es liegen Fehler in diesem Eingriff vor:" msgstr "Es liegen Fehler in diesem Eingriff vor:"
@ -1824,74 +1828,94 @@ msgid "State removed"
msgstr "Zustand gelöscht" msgstr "Zustand gelöscht"
#: konova/utils/message_templates.py:33 #: konova/utils/message_templates.py:33
msgid "State edited"
msgstr "Zustand bearbeitet"
#: konova/utils/message_templates.py:34
msgid "State added" msgid "State added"
msgstr "Zustand hinzugefügt" msgstr "Zustand hinzugefügt"
#: konova/utils/message_templates.py:36 #: konova/utils/message_templates.py:37
msgid "Action added" msgid "Action added"
msgstr "Maßnahme hinzugefügt" 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" msgid "Action removed"
msgstr "Maßnahme entfernt" msgstr "Maßnahme entfernt"
#: konova/utils/message_templates.py:40 #: konova/utils/message_templates.py:42
msgid "Deduction added" msgid "Deduction added"
msgstr "Abbuchung hinzugefügt" 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" msgid "Deduction removed"
msgstr "Abbuchung entfernt" msgstr "Abbuchung entfernt"
#: konova/utils/message_templates.py:44 #: konova/utils/message_templates.py:47
msgid "Deadline added" msgid "Deadline added"
msgstr "Frist/Termin hinzugefügt" 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" msgid "Deadline removed"
msgstr "Frist/Termin gelöscht" msgstr "Frist/Termin gelöscht"
#: konova/utils/message_templates.py:48 #: konova/utils/message_templates.py:52
msgid "Payment added" msgid "Payment added"
msgstr "Zahlung hinzugefügt" msgstr "Zahlung hinzugefügt"
#: konova/utils/message_templates.py:49 #: konova/utils/message_templates.py:53
msgid "Payment edited" msgid "Payment edited"
msgstr "Zahlung bearbeitet" msgstr "Zahlung bearbeitet"
#: konova/utils/message_templates.py:50 #: konova/utils/message_templates.py:54
msgid "Payment removed" msgid "Payment removed"
msgstr "Zahlung gelöscht" msgstr "Zahlung gelöscht"
#: konova/utils/message_templates.py:53 #: konova/utils/message_templates.py:57
msgid "Revocation added" msgid "Revocation added"
msgstr "Widerspruch hinzugefügt" 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" msgid "Revocation removed"
msgstr "Widerspruch entfernt" msgstr "Widerspruch entfernt"
#: konova/utils/message_templates.py:57 #: konova/utils/message_templates.py:62
msgid "Document '{}' deleted" msgid "Document '{}' deleted"
msgstr "Dokument '{}' gelöscht" msgstr "Dokument '{}' gelöscht"
#: konova/utils/message_templates.py:58 #: konova/utils/message_templates.py:63
msgid "Document added" msgid "Document added"
msgstr "Dokument hinzugefügt" msgstr "Dokument hinzugefügt"
#: konova/utils/message_templates.py:61 #: konova/utils/message_templates.py:66
msgid "Edited general data" msgid "Edited general data"
msgstr "Allgemeine Daten bearbeitet" msgstr "Allgemeine Daten bearbeitet"
#: konova/utils/message_templates.py:62 #: konova/utils/message_templates.py:67
msgid "Added deadline" msgid "Added deadline"
msgstr "Frist/Termin hinzugefügt" msgstr "Frist/Termin hinzugefügt"
#: konova/utils/message_templates.py:65 #: konova/utils/message_templates.py:70
msgid "Geometry conflict detected with {}" msgid "Geometry conflict detected with {}"
msgstr "Geometriekonflikt mit folgenden Einträgen erkannt: {}" msgstr "Geometriekonflikt mit folgenden Einträgen erkannt: {}"
#: konova/utils/message_templates.py:68 #: konova/utils/message_templates.py:73
msgid "This intervention has {} revocations" msgid "This intervention has {} revocations"
msgstr "Dem Eingriff liegen {} Widersprüche vor" msgstr "Dem Eingriff liegen {} Widersprüche vor"

Loading…
Cancel
Save