Compare commits

..

No commits in common. "1b5cda648e8273dc36ab7ce47f46daed631a78e7" and "a096b2a413ddfc8537105dd90e7d487edfe1679c" have entirely different histories.

37 changed files with 367 additions and 1202 deletions

View File

@ -12,17 +12,15 @@ class BaseAPIV1TestCase(BaseTestCase):
def setUpTestData(cls):
super().setUpTestData()
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.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)
self.header_data = {
"HTTP_ksptoken": self.superuser.api_token.token,
"HTTP_kspuser": self.superuser.username,
cls.header_data = {
"HTTP_ksptoken": cls.superuser.api_token.token,
"HTTP_kspuser": cls.superuser.username,
}

View File

@ -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_EDITED
ADDED_COMPENSATION_ACTION
class NewPaymentForm(BaseModalForm):
@ -103,32 +103,6 @@ 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

View File

@ -133,7 +133,7 @@ class CompensationTable(BaseTable, TableRenderMixin):
Returns:
"""
parcels = value.get_underlying_parcels().values_list(
parcels = value.parcels.values_list(
"gmrkng",
flat=True
).distinct()
@ -294,7 +294,7 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
Returns:
"""
parcels = value.get_underlying_parcels().values_list(
parcels = value.parcels.values_list(
"gmrkng",
flat=True
).distinct()

View File

@ -60,12 +60,9 @@
</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 float-right">
<td>
{% if is_default_member and has_access %}
<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' %}">
<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' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

View File

@ -21,32 +21,29 @@ 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 = self.create_dummy_action()
self.compensation.actions.set([action])
action = cls.create_dummy_action()
cls.compensation.actions.set([action])
# Prepare urls
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.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.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,))
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,))
def test_anonymous_user(self):
""" Check correct status code for all requests
@ -120,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 not shared
Assumption: User logged in and has no groups and data is shared
Returns:

View File

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

View File

@ -25,31 +25,28 @@ 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 = self.create_dummy_action()
self.eco_account.actions.set([action])
action = cls.create_dummy_action()
cls.eco_account.actions.set([action])
# Prepare urls
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,))
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

View File

@ -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, EcoAccountDeduction
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
@ -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_new_deduction(self):
def test_deductability(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.eco_account.id,
"account": self.id,
"intervention": self.intervention.id,
}
# Perform request --> expect to fail
@ -228,75 +228,4 @@ 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)

View File

@ -1,7 +0,0 @@
"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 09.02.22
"""

View File

@ -1,156 +0,0 @@
"""
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()
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]
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
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)

View File

@ -1,127 +0,0 @@
"""
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()
def setUp(self) -> None:
super().setUp()
# Give the user shared access to the dummy intervention
self.intervention.share_with(self.superuser)
self.payment = Payment.objects.get_or_create(
intervention=self.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)

View File

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

View File

@ -12,5 +12,4 @@ app_name = "pay"
urlpatterns = [
path('<id>/new', new_payment_view, name='new'),
path('<id>/remove/<payment_id>', payment_remove_view, name='remove'),
path('<id>/edit/<payment_id>', payment_edit_view, name='edit'),
]

View File

@ -19,8 +19,7 @@ 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, \
EditEcoAccountDeductionModalForm
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
@ -32,8 +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, DEADLINE_ADDED, DEADLINE_REMOVED, \
DEDUCTION_EDITED
COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED
from konova.utils.user_checks import in_group
@ -296,34 +294,6 @@ 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")

View File

@ -11,17 +11,16 @@ 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, EditPaymentModalForm
from compensation.forms.modalForms import NewPaymentForm, RemovePaymentModalForm
from compensation.models import Payment
from intervention.models import Intervention
from konova.decorators import default_group_required, shared_access_required
from konova.decorators import default_group_required
from konova.forms import RemoveModalForm
from konova.utils.message_templates import PAYMENT_ADDED, PAYMENT_REMOVED, PAYMENT_EDITED
from konova.utils.message_templates import PAYMENT_ADDED, PAYMENT_REMOVED
@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
@ -43,7 +42,6 @@ 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
@ -64,27 +62,3 @@ 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"
)

View File

@ -103,7 +103,7 @@ class EmaTable(BaseTable, TableRenderMixin):
Returns:
"""
parcels = value.get_underlying_parcels().values_list(
parcels = value.parcels.values_list(
"gmrkng",
flat=True
).distinct()

View File

@ -31,43 +31,42 @@ 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
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])
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])
# Prepare urls
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,))
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,))
def create_dummy_data(self):
@classmethod
def create_dummy_data(cls):
# Create dummy data
# Create log entry
action = UserActionLogEntry.get_created_action(self.superuser)
action = UserActionLogEntry.get_created_action(cls.superuser)
# Create responsible data object
responsibility_data = Responsibility.objects.create()
geometry = Geometry.objects.create()
self.ema = Ema.objects.create(
cls.ema = Ema.objects.create(
identifier="TEST",
title="Test_title",
created=action,

View File

@ -6,11 +6,8 @@ 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, \
REVOCATION_EDITED, FILE_TYPE_UNSUPPORTED, FILE_SIZE_TOO_LARGE
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
@ -18,7 +15,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 intervention.models import Intervention, InterventionDocument
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
@ -160,7 +157,6 @@ class NewRevocationModalForm(BaseModalForm):
}
)
)
document_model = RevocationDocument
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -170,61 +166,12 @@ 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
@ -402,21 +349,6 @@ 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
@ -435,7 +367,10 @@ class NewDeductionModalForm(BaseModalForm):
)
return False
rest_surface = self._get_available_surface(acc)
# Calculate valid surface
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"])
is_valid_surface = form_surface <= rest_surface
if not is_valid_surface:
@ -472,57 +407,6 @@ 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

View File

@ -8,8 +8,6 @@ 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
@ -204,41 +202,6 @@ 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

View File

@ -130,7 +130,7 @@ class InterventionTable(BaseTable, TableRenderMixin):
Returns:
"""
parcels = value.get_underlying_parcels().values_list(
parcels = value.parcels.values_list(
"gmrkng",
flat=True
).distinct()

View File

@ -55,12 +55,9 @@
</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 float-right">
<td>
{% if is_default_member and has_access %}
<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' %}">
<button data-form-url="{% url 'intervention:remove-deduction' obj.id deduction.id %}" class="btn btn-default btn-modal float-right" title="{% trans 'Remove Deduction' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

View File

@ -54,12 +54,9 @@
{{ pay.comment }}
</div>
</td>
<td class="align-middle float-right">
<td class="align-middle">
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:pay:edit' obj.id pay.id %}" class="btn btn-default btn-modal" title="{% trans 'Edit payment' %}">
{% fa5_icon 'edit' %}
</button>
<button data-form-url="{% url 'compensation:pay:remove' obj.id pay.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove payment' %}">
<button data-form-url="{% url 'compensation:pay:remove' obj.id pay.id %}" class="btn btn-default btn-modal float-right" title="{% trans 'Remove payment' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

View File

@ -63,12 +63,9 @@
{{ rev.comment }}
</div>
</td>
<td class="align-middle float-right">
<td class="align-middle">
{% if is_default_member and has_access %}
<button data-form-url="{% url 'intervention:edit-revocation' obj.id rev.id %}" class="btn btn-default btn-modal" title="{% trans 'Edit revocation' %}">
{% fa5_icon 'edit' %}
</button>
<button data-form-url="{% url 'intervention:remove-revocation' obj.id rev.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove revocation' %}">
<button data-form-url="{% url 'intervention:remove-revocation' obj.id rev.id %}" class="btn btn-default btn-modal float-right" title="{% trans 'Remove revocation' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}

View File

@ -21,32 +21,30 @@ class InterventionViewTestCase(BaseViewTestCase):
def setUpTestData(cls) -> None:
super().setUpTestData()
def setUp(self) -> None:
super().setUp()
# Prepare urls
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.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.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.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.revocation = Revocation.objects.create(
legal=self.intervention.legal
cls.revocation = Revocation.objects.create(
legal=cls.intervention.legal
)
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))
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

View File

@ -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
app_name = "intervention"
urlpatterns = [
@ -37,12 +37,10 @@ urlpatterns = [
# Deductions
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'),
# Revocation routes
path('<id>/revocation/new', new_revocation_view, name='new-revocation'),
path('<id>/revocation/<revocation_id>/edit', edit_revocation_view, name='edit-revocation'),
path('<id>/revocation/<revocation_id>/remove', remove_revocation_view, name='remove-revocation'),
path('revocation/<doc_id>', get_revocation_view, name='get-doc-revocation'),
]

View File

@ -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, EditRevocationModalForm
RemoveRevocationModalForm
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, REVOCATION_EDITED
COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED
from konova.utils.user_checks import in_group
@ -331,31 +331,6 @@ 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")
@ -364,8 +339,7 @@ def remove_revocation_view(request: HttpRequest, id: str, revocation_id: str):
Args:
request (HttpRequest): The incoming request
id (str): The intervention's id as string
revocation_id (str): The revocation's id as string
id (str): The revocation's id as string
Returns:
@ -562,34 +536,6 @@ 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")

View File

@ -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, FILE_TYPE_UNSUPPORTED, FILE_SIZE_TOO_LARGE
from konova.utils.message_templates import FORM_INVALID
from user.models import UserActionLogEntry
@ -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 = None):
def load_initial_data(self, form_data: dict, disabled_fields: list):
""" Initializes form data from instance
Inserts instance data into form and disables form fields
@ -99,7 +99,6 @@ class BaseForm(forms.Form):
return
for k, v in form_data.items():
self.initialize_form_field(k, v)
if disabled_fields:
for field in disabled_fields:
self.disable_form_field(field)
@ -424,14 +423,14 @@ class NewDocumentForm(BaseModalForm):
if not mime_type_valid:
self.add_error(
"file",
FILE_TYPE_UNSUPPORTED
_("Unsupported file type")
)
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 too large")
)
file_valid = mime_type_valid and file_size_valid

View File

@ -1,54 +0,0 @@
# 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',
),
]

View File

@ -1,17 +0,0 @@
# 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',
),
]

View File

@ -101,19 +101,3 @@ 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()

View File

@ -99,7 +99,7 @@ class Geometry(BaseResource):
Returns:
"""
from konova.models import Parcel, District, ParcelIntersection
from konova.models import Parcel, District
parcel_fetcher = ParcelWFSFetcher(
geometry_id=self.id,
)
@ -107,7 +107,6 @@ class Geometry(BaseResource):
fetched_parcels = parcel_fetcher.get_features(
typename
)
_now = timezone.now()
underlying_parcels = []
for result in fetched_parcels:
fetched_parcel = result[typename]
@ -126,35 +125,19 @@ class Geometry(BaseResource):
krs=fetched_parcel["ave:kreis"],
)[0]
parcel_obj.district = district
parcel_obj.updated_on = _now
parcel_obj.updated_on = timezone.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.filter(
parcelintersection__calculated_on__isnull=False,
).prefetch_related(
parcels = self.parcels.all().prefetch_related(
"district"
).order_by(
"gmrkng",

View File

@ -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", blank=True, related_name="parcels", through='ParcelIntersection')
geometries = models.ManyToManyField("konova.Geometry", related_name="parcels", blank=True)
district = models.ForeignKey("konova.District", on_delete=models.SET_NULL, null=True, blank=True, related_name="parcels")
gmrkng = models.CharField(
max_length=1000,
@ -77,22 +77,3 @@ 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)

View File

@ -4,19 +4,13 @@ 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, ParcelIntersection
from konova.models import Geometry
try:
geom = Geometry.objects.get(id=geometry_id)
objs = geom.parcelintersection_set.all()
for obj in objs:
obj.calculated_on = None
ParcelIntersection.objects.bulk_update(
objs,
["calculated_on"]
)
geom.parcels.clear()
geom.update_parcels()
except ObjectDoesNotExist:
if recheck:

View File

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

View File

@ -18,10 +18,6 @@ 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.")
@ -34,32 +30,26 @@ 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
PAYMENT_ADDED = _("Payment added")
PAYMENT_EDITED = _("Payment edited")
PAYMENT_REMOVED = _("Payment removed")
# REVOCATIONS
REVOCATION_ADDED = _("Revocation added")
REVOCATION_EDITED = _("Revocation edited")
REVOCATION_REMOVED = _("Revocation removed")
# DOCUMENTS

Binary file not shown.

View File

@ -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:332 compensation/forms/modalForms.py:425
#: 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
@ -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: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
#, 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-08 15:16+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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:189
#: analysis/forms.py:69 konova/forms.py:188
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:409
#: 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"
@ -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:222
#: 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
@ -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:193
#: 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
@ -239,6 +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:90
#: compensation/templates/compensation/detail/compensation/view.html:19
#: konova/templates/konova/includes/quickstart/compensations.html:4
#: templates/navbars/navbar.html:28
@ -283,8 +284,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
#: 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
@ -292,7 +293,7 @@ msgid "Intervention"
msgstr "Eingriff"
#: analysis/templates/analysis/reports/includes/old_data/amount.html:34
#: compensation/tables.py:266
#: 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
@ -313,7 +314,7 @@ msgid "Show only unrecorded"
msgstr "Nur unverzeichnete anzeigen"
#: compensation/forms/forms.py:32 compensation/tables.py:25
#: compensation/tables.py:197 ema/tables.py:29 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"
@ -325,7 +326,7 @@ msgid "Generated automatically"
msgstr "Automatisch generiert"
#: compensation/forms/forms.py:44 compensation/tables.py:30
#: compensation/tables.py:202
#: 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
@ -340,7 +341,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:355
msgid "Title"
msgstr "Bezeichnung"
@ -353,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:331 compensation/forms/modalForms.py:424
#: 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
@ -367,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:391 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:426
#: compensation/forms/forms.py:59 compensation/forms/modalForms.py:400
#: intervention/forms/forms.py:182
msgid "Additional comment"
msgstr "Zusätzlicher Kommentar"
@ -457,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:105
#: compensation/forms/forms.py:354 compensation/views/eco_account.py:103
msgid "New Eco-Account"
msgstr "Neues Ökokonto"
@ -482,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:333
#: intervention/forms/modalForms.py:151 konova/forms.py:393
#: 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"
@ -495,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:157 compensation/forms/modalForms.py:169
#: compensation/forms/modalForms.py:131 compensation/forms/modalForms.py:143
msgid "Biotope Type"
msgstr "Biotoptyp"
#: compensation/forms/modalForms.py:160
#: compensation/forms/modalForms.py:134
msgid "Select the biotope type"
msgstr "Biotoptyp wählen"
#: compensation/forms/modalForms.py:174 compensation/forms/modalForms.py:186
#: compensation/forms/modalForms.py:148 compensation/forms/modalForms.py:160
msgid "Biotope additional type"
msgstr "Zusatzbezeichnung"
#: compensation/forms/modalForms.py:177
#: compensation/forms/modalForms.py:151
msgid "Select an additional biotope type"
msgstr "Zusatzbezeichnung wählen"
#: compensation/forms/modalForms.py:196 intervention/forms/modalForms.py:313
#: compensation/forms/modalForms.py:170 intervention/forms/modalForms.py:313
msgid "in m²"
msgstr ""
#: compensation/forms/modalForms.py:207
#: compensation/forms/modalForms.py:181
msgid "New state"
msgstr "Neuer Zustand"
#: compensation/forms/modalForms.py:208
#: 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:215 konova/forms.py:191
#: compensation/forms/modalForms.py:189 konova/forms.py:190
msgid "Object removed"
msgstr "Objekt entfernt"
#: compensation/forms/modalForms.py:303
#: compensation/forms/modalForms.py:277
msgid "Deadline Type"
msgstr "Fristart"
#: compensation/forms/modalForms.py:306
#: compensation/forms/modalForms.py:280
msgid "Select the deadline type"
msgstr "Fristart wählen"
#: compensation/forms/modalForms.py:315
#: 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
@ -543,27 +544,27 @@ msgstr "Fristart wählen"
msgid "Date"
msgstr "Datum"
#: compensation/forms/modalForms.py:318
#: compensation/forms/modalForms.py:292
msgid "Select date"
msgstr "Datum wählen"
#: compensation/forms/modalForms.py:345
#: compensation/forms/modalForms.py:319
msgid "New deadline"
msgstr "Neue Frist"
#: compensation/forms/modalForms.py:346
#: 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:363
#: compensation/forms/modalForms.py:337
msgid "Action Type"
msgstr "Maßnahmentyp"
#: compensation/forms/modalForms.py:366
#: compensation/forms/modalForms.py:340
msgid "Select the action type"
msgstr "Maßnahmentyp wählen"
#: compensation/forms/modalForms.py:375
#: 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
@ -589,31 +590,31 @@ msgstr "Maßnahmentyp wählen"
msgid "Action"
msgstr "Aktionen"
#: compensation/forms/modalForms.py:380 compensation/forms/modalForms.py:392
#: compensation/forms/modalForms.py:354 compensation/forms/modalForms.py:366
msgid "Action Type detail"
msgstr "Zusatzmerkmal"
#: compensation/forms/modalForms.py:383
#: compensation/forms/modalForms.py:357
msgid "Select the action type detail"
msgstr "Zusatzmerkmal wählen"
#: compensation/forms/modalForms.py:397
#: compensation/forms/modalForms.py:371
msgid "Unit"
msgstr "Einheit"
#: compensation/forms/modalForms.py:400
#: compensation/forms/modalForms.py:374
msgid "Select the unit"
msgstr "Einheit wählen"
#: compensation/forms/modalForms.py:412
#: compensation/forms/modalForms.py:386
msgid "Insert the amount"
msgstr "Menge eingeben"
#: compensation/forms/modalForms.py:437
#: compensation/forms/modalForms.py:411
msgid "New action"
msgstr "Neue Maßnahme"
#: compensation/forms/modalForms.py:438
#: compensation/forms/modalForms.py:412
msgid "Insert data for the new action"
msgstr "Geben Sie die Daten der neuen Maßnahme ein"
@ -656,35 +657,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:207 ema/tables.py:39
#: 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:228 ema/tables.py:50
#: compensation/tables.py:52 compensation/tables.py:225 ema/tables.py:50
#: intervention/tables.py:51
msgid "Editable"
msgstr "Freigegeben"
#: compensation/tables.py:58 compensation/tables.py:234 ema/tables.py:56
#: 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:89 compensation/tables.py:266 ema/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:114 intervention/tables.py:111
#: compensation/tables.py:111 intervention/tables.py:111
msgid "Not checked yet"
msgstr "Noch nicht geprüft"
#: compensation/tables.py:119 intervention/tables.py:116
#: compensation/tables.py:116 intervention/tables.py:116
msgid "Checked on {} by {}"
msgstr "Am {} von {} geprüft worden"
#: compensation/tables.py:160
#: 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
@ -694,32 +695,32 @@ msgstr "Am {} von {} geprüft worden"
msgid "Not recorded yet"
msgstr "Noch nicht verzeichnet"
#: compensation/tables.py:165 compensation/tables.py:326 ema/tables.py:136
#: 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:189 compensation/tables.py:348 ema/tables.py:159
#: 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:189 compensation/tables.py:348 ema/tables.py:159
#: 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:212
#: 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:243
#: compensation/tables.py:240
msgid "Eco Accounts"
msgstr "Ökokonten"
#: compensation/tables.py:321
#: 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."
@ -821,7 +822,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:409
#: konova/forms.py:408
msgid "Add new document"
msgstr "Neues Dokument hinzufügen"
@ -992,7 +993,7 @@ msgid "Recorded on"
msgstr "Verzeichnet am"
#: compensation/templates/compensation/detail/eco_account/includes/deductions.html:65
#: intervention/templates/intervention/detail/includes/deductions.html:63
#: intervention/templates/intervention/detail/includes/deductions.html:60
msgid "Remove Deduction"
msgstr "Abbuchung entfernen"
@ -1084,64 +1085,64 @@ msgstr "Kompensationen - Übersicht"
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
#: 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:238 compensation/views/eco_account.py:347
#: ema/views.py:191 intervention/views.py:483
#: 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:487 compensation/views/eco_account.py:620
#: ema/views.py:477 intervention/views.py:629
#: 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:62
#: compensation/views/eco_account.py:60
msgid "Eco-account - Overview"
msgstr "Ökokonten - Übersicht"
#: compensation/views/eco_account.py:95
#: compensation/views/eco_account.py:93
msgid "Eco-Account {} added"
msgstr "Ökokonto {} hinzugefügt"
#: compensation/views/eco_account.py:153
#: compensation/views/eco_account.py:151
msgid "Eco-Account {} edited"
msgstr "Ökokonto {} bearbeitet"
#: compensation/views/eco_account.py:266
#: compensation/views/eco_account.py:264
msgid "Eco-account removed"
msgstr "Ökokonto entfernt"
#: compensation/views/eco_account.py:368 ema/views.py:272
#: intervention/views.py:582
#: compensation/views/eco_account.py:338 ema/views.py:272
#: intervention/views.py:562
msgid "{} unrecorded"
msgstr "{} entzeichnet"
#: compensation/views/eco_account.py:368 ema/views.py:272
#: intervention/views.py:582
#: compensation/views/eco_account.py:338 ema/views.py:272
#: intervention/views.py:562
msgid "{} recorded"
msgstr "{} verzeichnet"
#: compensation/views/eco_account.py:693 ema/views.py:543
#: intervention/views.py:380
#: 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:698 ema/views.py:548
#: intervention/views.py:385
#: 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:705 ema/views.py:555
#: intervention/views.py:392
#: 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:728 ema/views.py:578
#: intervention/views.py:415
#: compensation/views/eco_account.py:698 ema/views.py:578
#: intervention/views.py:423
msgid "Share settings updated"
msgstr "Freigabe Einstellungen aktualisiert"
@ -1313,7 +1314,7 @@ msgstr "Kompensationen und Zahlungen geprüft"
msgid "Run check"
msgstr "Prüfung vornehmen"
#: intervention/forms/modalForms.py:213 konova/forms.py:475
#: intervention/forms/modalForms.py:213 konova/forms.py:474
msgid ""
"I, {} {}, confirm that all necessary control steps have been performed by "
"myself."
@ -1337,7 +1338,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:381
#: intervention/forms/modalForms.py:366
msgid ""
"Eco-account {} is not recorded yet. You can only deduct from recorded "
"accounts."
@ -1345,7 +1346,7 @@ msgstr ""
"Ökokonto {} ist noch nicht verzeichnet. Abbuchungen können nur von "
"verzeichneten Ökokonten erfolgen."
#: intervention/forms/modalForms.py:391
#: intervention/forms/modalForms.py:379
msgid ""
"The account {} has not enough surface for a deduction of {} m². There are "
"only {} m² left"
@ -1373,10 +1374,6 @@ 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"
@ -1392,10 +1389,6 @@ 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"
@ -1456,19 +1449,23 @@ msgstr "Eingriffe - Übersicht"
msgid "Intervention {} added"
msgstr "Eingriff {} hinzugefügt"
#: intervention/views.py:293
#: intervention/views.py:252
msgid "This intervention has {} revocations"
msgstr "Dem Eingriff liegen {} Widersprüche vor"
#: intervention/views.py:301
msgid "Intervention {} edited"
msgstr "Eingriff {} bearbeitet"
#: intervention/views.py:329
#: intervention/views.py:337
msgid "{} removed"
msgstr "{} entfernt"
#: intervention/views.py:436
#: intervention/views.py:444
msgid "Check performed"
msgstr "Prüfung durchgeführt"
#: intervention/views.py:587
#: intervention/views.py:567
msgid "There are errors on this intervention:"
msgstr "Es liegen Fehler in diesem Eingriff vor:"
@ -1560,73 +1557,73 @@ msgstr "Speichern"
msgid "Not editable"
msgstr "Nicht editierbar"
#: konova/forms.py:140 konova/forms.py:312
#: konova/forms.py:139 konova/forms.py:311
msgid "Confirm"
msgstr "Bestätige"
#: konova/forms.py:152 konova/forms.py:321
#: konova/forms.py:151 konova/forms.py:320
msgid "Remove"
msgstr "Löschen"
#: konova/forms.py:154
#: konova/forms.py:153
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:240 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:321
msgid "Are you sure?"
msgstr "Sind Sie sicher?"
#: konova/forms.py:366
#: konova/forms.py:365
msgid "Created on"
msgstr "Erstellt"
#: konova/forms.py:368
#: 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:379
#: konova/forms.py:378
#: venv/lib/python3.7/site-packages/django/db/models/fields/files.py:231
msgid "File"
msgstr "Datei"
#: konova/forms.py:381
#: 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:427
#: konova/forms.py:426
msgid "Unsupported file type"
msgstr "Dateiformat nicht unterstützt"
#: konova/forms.py:434
#: konova/forms.py:433
msgid "File too large"
msgstr "Datei zu groß"
#: konova/forms.py:443
#: konova/forms.py:442
msgid "Added document"
msgstr "Dokument hinzugefügt"
#: konova/forms.py:466
#: konova/forms.py:465
msgid "Confirm record"
msgstr "Verzeichnen bestätigen"
#: konova/forms.py:474
#: konova/forms.py:473
msgid "Record data"
msgstr "Daten verzeichnen"
#: konova/forms.py:481
#: konova/forms.py:480
msgid "Confirm unrecord"
msgstr "Entzeichnen bestätigen"
#: konova/forms.py:482
#: konova/forms.py:481
msgid "Unrecord data"
msgstr "Daten entzeichnen"
#: konova/forms.py:483
#: 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."
@ -1828,97 +1825,69 @@ 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:37
#: konova/utils/message_templates.py:36
msgid "Action added"
msgstr "Maßnahme hinzugefügt"
#: konova/utils/message_templates.py:38
msgid "Action edited"
msgstr "Maßnahme bearbeitet"
#: konova/utils/message_templates.py:39
#: konova/utils/message_templates.py:37
msgid "Action removed"
msgstr "Maßnahme entfernt"
#: konova/utils/message_templates.py:42
#: konova/utils/message_templates.py:40
msgid "Deduction added"
msgstr "Abbuchung hinzugefügt"
#: konova/utils/message_templates.py:43
msgid "Deduction edited"
msgstr "Abbuchung bearbeitet"
#: konova/utils/message_templates.py:44
#: konova/utils/message_templates.py:41
msgid "Deduction removed"
msgstr "Abbuchung entfernt"
#: konova/utils/message_templates.py:47
#: konova/utils/message_templates.py:44
msgid "Deadline added"
msgstr "Frist/Termin hinzugefügt"
#: konova/utils/message_templates.py:48
msgid "Deadline edited"
msgstr "Frist/Termin bearbeitet"
#: konova/utils/message_templates.py:49
#: konova/utils/message_templates.py:45
msgid "Deadline removed"
msgstr "Frist/Termin gelöscht"
#: konova/utils/message_templates.py:52
#: konova/utils/message_templates.py:48
msgid "Payment added"
msgstr "Zahlung hinzugefügt"
#: konova/utils/message_templates.py:53
msgid "Payment edited"
msgstr "Zahlung bearbeitet"
#: konova/utils/message_templates.py:54
#: konova/utils/message_templates.py:49
msgid "Payment removed"
msgstr "Zahlung gelöscht"
#: konova/utils/message_templates.py:57
#: konova/utils/message_templates.py:52
msgid "Revocation added"
msgstr "Widerspruch hinzugefügt"
#: konova/utils/message_templates.py:58
msgid "Revocation edited"
msgstr "Widerspruch bearbeitet"
#: konova/utils/message_templates.py:59
#: konova/utils/message_templates.py:53
msgid "Revocation removed"
msgstr "Widerspruch entfernt"
#: konova/utils/message_templates.py:62
#: konova/utils/message_templates.py:56
msgid "Document '{}' deleted"
msgstr "Dokument '{}' gelöscht"
#: konova/utils/message_templates.py:63
#: konova/utils/message_templates.py:57
msgid "Document added"
msgstr "Dokument hinzugefügt"
#: konova/utils/message_templates.py:66
#: konova/utils/message_templates.py:60
msgid "Edited general data"
msgstr "Allgemeine Daten bearbeitet"
#: konova/utils/message_templates.py:67
#: konova/utils/message_templates.py:61
msgid "Added deadline"
msgstr "Frist/Termin hinzugefügt"
#: konova/utils/message_templates.py:70
#: konova/utils/message_templates.py:64
msgid "Geometry conflict detected with {}"
msgstr "Geometriekonflikt mit folgenden Einträgen erkannt: {}"
#: konova/utils/message_templates.py:73
msgid "This intervention has {} revocations"
msgstr "Dem Eingriff liegen {} Widersprüche vor"
#: konova/utils/messenger.py:70
msgid "{} checked"
msgstr "{} geprüft"
@ -3973,6 +3942,9 @@ msgstr ""
#~ msgid "No file given!"
#~ msgstr "Keine Datei angegeben!"
#~ msgid "Added payment"
#~ msgstr "Zahlung hinzufügen"
#~ msgid "Added state"
#~ msgstr "Zustand hinzugefügt"