diff --git a/api/tests/v1/share/test_api_sharing.py b/api/tests/v1/share/test_api_sharing.py index 9e7c9eec..1da0ce1b 100644 --- a/api/tests/v1/share/test_api_sharing.py +++ b/api/tests/v1/share/test_api_sharing.py @@ -12,15 +12,17 @@ class BaseAPIV1TestCase(BaseTestCase): def setUpTestData(cls): super().setUpTestData() - cls.superuser.get_API_token() - cls.superuser.api_token.is_active = True - cls.superuser.api_token.save() - default_group = cls.groups.get(name=DEFAULT_GROUP) - cls.superuser.groups.add(default_group) + def setUp(self) -> None: + super().setUp() + self.superuser.get_API_token() + self.superuser.api_token.is_active = True + self.superuser.api_token.save() + default_group = self.groups.get(name=DEFAULT_GROUP) + self.superuser.groups.add(default_group) - cls.header_data = { - "HTTP_ksptoken": cls.superuser.api_token.token, - "HTTP_kspuser": cls.superuser.username, + self.header_data = { + "HTTP_ksptoken": self.superuser.api_token.token, + "HTTP_kspuser": self.superuser.username, } diff --git a/compensation/templates/compensation/detail/eco_account/includes/deductions.html b/compensation/templates/compensation/detail/eco_account/includes/deductions.html index e72ab2ab..76c116b2 100644 --- a/compensation/templates/compensation/detail/eco_account/includes/deductions.html +++ b/compensation/templates/compensation/detail/eco_account/includes/deductions.html @@ -60,9 +60,12 @@ {{ deduction.surface|floatformat:2|intcomma }} m² {{ deduction.created.timestamp|default_if_none:""|naturalday}} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/compensation/tests/compensation/test_views.py b/compensation/tests/compensation/test_views.py index 465a1026..5844e264 100644 --- a/compensation/tests/compensation/test_views.py +++ b/compensation/tests/compensation/test_views.py @@ -21,29 +21,32 @@ class CompensationViewTestCase(BaseViewTestCase): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() - state = cls.create_dummy_states() - cls.compensation.before_states.set([state]) - cls.compensation.after_states.set([state]) + + def setUp(self) -> None: + super().setUp() + state = self.create_dummy_states() + self.compensation.before_states.set([state]) + self.compensation.after_states.set([state]) - action = cls.create_dummy_action() - cls.compensation.actions.set([action]) + action = self.create_dummy_action() + self.compensation.actions.set([action]) # Prepare urls - cls.index_url = reverse("compensation:index", args=()) - cls.new_url = reverse("compensation:new", args=(cls.intervention.id,)) - cls.new_id_url = reverse("compensation:new-id", args=()) - cls.detail_url = reverse("compensation:detail", args=(cls.compensation.id,)) - cls.log_url = reverse("compensation:log", args=(cls.compensation.id,)) - cls.edit_url = reverse("compensation:edit", args=(cls.compensation.id,)) - cls.remove_url = reverse("compensation:remove", args=(cls.compensation.id,)) - cls.report_url = reverse("compensation:report", args=(cls.compensation.id,)) - cls.state_new_url = reverse("compensation:new-state", args=(cls.compensation.id,)) - cls.action_new_url = reverse("compensation:new-action", args=(cls.compensation.id,)) - cls.deadline_new_url = reverse("compensation:new-deadline", args=(cls.compensation.id,)) - cls.new_doc_url = reverse("compensation:new-doc", args=(cls.compensation.id,)) + self.index_url = reverse("compensation:index", args=()) + self.new_url = reverse("compensation:new", args=(self.intervention.id,)) + self.new_id_url = reverse("compensation:new-id", args=()) + self.detail_url = reverse("compensation:detail", args=(self.compensation.id,)) + self.log_url = reverse("compensation:log", args=(self.compensation.id,)) + self.edit_url = reverse("compensation:edit", args=(self.compensation.id,)) + self.remove_url = reverse("compensation:remove", args=(self.compensation.id,)) + self.report_url = reverse("compensation:report", args=(self.compensation.id,)) + self.state_new_url = reverse("compensation:new-state", args=(self.compensation.id,)) + self.action_new_url = reverse("compensation:new-action", args=(self.compensation.id,)) + self.deadline_new_url = reverse("compensation:new-deadline", args=(self.compensation.id,)) + self.new_doc_url = reverse("compensation:new-doc", args=(self.compensation.id,)) - cls.state_remove_url = reverse("compensation:state-remove", args=(cls.compensation.id, cls.comp_state.id,)) - cls.action_remove_url = reverse("compensation:action-remove", args=(cls.compensation.id, cls.comp_action.id,)) + self.state_remove_url = reverse("compensation:state-remove", args=(self.compensation.id, self.comp_state.id,)) + self.action_remove_url = reverse("compensation:action-remove", args=(self.compensation.id, self.comp_action.id,)) def test_anonymous_user(self): """ Check correct status code for all requests diff --git a/compensation/tests/compensation/test_workflow.py b/compensation/tests/compensation/test_workflow.py index d1f13782..7b73be89 100644 --- a/compensation/tests/compensation/test_workflow.py +++ b/compensation/tests/compensation/test_workflow.py @@ -21,17 +21,18 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase): def setUpTestData(cls): super().setUpTestData() - # Give the user shared access to the dummy intervention -> inherits the access to the compensation - cls.intervention.share_with(cls.superuser) - - # Make sure the intervention itself would be fine with valid data - cls.intervention = cls.fill_out_intervention(cls.intervention) - - # Make sure the compensation is linked to the intervention - cls.intervention.compensations.set([cls.compensation]) - def setUp(self) -> None: super().setUp() + + # Give the user shared access to the dummy intervention -> inherits the access to the compensation + self.intervention.share_with(self.superuser) + + # Make sure the intervention itself would be fine with valid data + self.intervention = self.fill_out_intervention(self.intervention) + + # Make sure the compensation is linked to the intervention + self.intervention.compensations.set([self.compensation]) + # Delete all existing compensations, which might be created by tests Compensation.objects.all().delete() diff --git a/compensation/tests/ecoaccount/test_views.py b/compensation/tests/ecoaccount/test_views.py index c4e742fe..670f4f0d 100644 --- a/compensation/tests/ecoaccount/test_views.py +++ b/compensation/tests/ecoaccount/test_views.py @@ -25,28 +25,31 @@ class EcoAccountViewTestCase(CompensationViewTestCase): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() - state = cls.create_dummy_states() - cls.eco_account.before_states.set([state]) - cls.eco_account.after_states.set([state]) + + def setUp(self) -> None: + super().setUp() + state = self.create_dummy_states() + self.eco_account.before_states.set([state]) + self.eco_account.after_states.set([state]) - action = cls.create_dummy_action() - cls.eco_account.actions.set([action]) + action = self.create_dummy_action() + self.eco_account.actions.set([action]) # Prepare urls - cls.index_url = reverse("compensation:acc:index", args=()) - cls.new_url = reverse("compensation:acc:new", args=()) - cls.new_id_url = reverse("compensation:acc:new-id", args=()) - cls.detail_url = reverse("compensation:acc:detail", args=(cls.eco_account.id,)) - cls.log_url = reverse("compensation:acc:log", args=(cls.eco_account.id,)) - cls.edit_url = reverse("compensation:acc:edit", args=(cls.eco_account.id,)) - cls.remove_url = reverse("compensation:acc:remove", args=(cls.eco_account.id,)) - cls.report_url = reverse("compensation:acc:report", args=(cls.eco_account.id,)) - cls.state_new_url = reverse("compensation:acc:new-state", args=(cls.eco_account.id,)) - cls.action_new_url = reverse("compensation:acc:new-action", args=(cls.eco_account.id,)) - cls.deadline_new_url = reverse("compensation:acc:new-deadline", args=(cls.eco_account.id,)) - cls.new_doc_url = reverse("compensation:acc:new-doc", args=(cls.eco_account.id,)) - cls.state_remove_url = reverse("compensation:acc:state-remove", args=(cls.eco_account.id, cls.comp_state.id,)) - cls.action_remove_url = reverse("compensation:acc:action-remove", args=(cls.eco_account.id, cls.comp_action.id,)) + self.index_url = reverse("compensation:acc:index", args=()) + self.new_url = reverse("compensation:acc:new", args=()) + self.new_id_url = reverse("compensation:acc:new-id", args=()) + self.detail_url = reverse("compensation:acc:detail", args=(self.eco_account.id,)) + self.log_url = reverse("compensation:acc:log", args=(self.eco_account.id,)) + self.edit_url = reverse("compensation:acc:edit", args=(self.eco_account.id,)) + self.remove_url = reverse("compensation:acc:remove", args=(self.eco_account.id,)) + self.report_url = reverse("compensation:acc:report", args=(self.eco_account.id,)) + self.state_new_url = reverse("compensation:acc:new-state", args=(self.eco_account.id,)) + self.action_new_url = reverse("compensation:acc:new-action", args=(self.eco_account.id,)) + self.deadline_new_url = reverse("compensation:acc:new-deadline", args=(self.eco_account.id,)) + self.new_doc_url = reverse("compensation:acc:new-doc", args=(self.eco_account.id,)) + self.state_remove_url = reverse("compensation:acc:state-remove", args=(self.eco_account.id, self.comp_state.id,)) + self.action_remove_url = reverse("compensation:acc:action-remove", args=(self.eco_account.id, self.comp_action.id,)) def test_logged_in_no_groups_shared(self): """ Check correct status code for all requests diff --git a/compensation/tests/ecoaccount/test_workflow.py b/compensation/tests/ecoaccount/test_workflow.py index f394ec7c..1cdb0308 100644 --- a/compensation/tests/ecoaccount/test_workflow.py +++ b/compensation/tests/ecoaccount/test_workflow.py @@ -11,7 +11,7 @@ from django.contrib.gis.geos import MultiPolygon from django.core.exceptions import ObjectDoesNotExist from django.urls import reverse -from compensation.models import EcoAccount +from compensation.models import EcoAccount, EcoAccountDeduction from konova.settings import ETS_GROUP, DEFAULT_GROUP from konova.tests.test_views import BaseWorkflowTestCase from user.models import UserAction @@ -168,7 +168,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): self.assertIn(recorded, self.eco_account.log.all()) self.assertEqual(pre_record_log_count + 1, self.eco_account.log.count()) - def test_deductability(self): + def test_new_deduction(self): """ This tests the deductability of an eco account. @@ -187,7 +187,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): test_surface = 10.00 post_data = { "surface": test_surface, - "account": self.id, + "account": self.eco_account.id, "intervention": self.intervention.id, } # Perform request --> expect to fail @@ -228,4 +228,75 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase): self.assertEqual(pre_deduction_int_log_count + 1, self.intervention.log.count()) self.assertTrue(self.intervention.log.first().action == UserAction.EDITED) + def test_edit_deduction(self): + test_surface = self.eco_account.get_available_rest()[0] + self.eco_account.set_recorded(self.superuser) + self.eco_account.refresh_from_db() + deduction = EcoAccountDeduction.objects.create( + intervention=self.intervention, + account=self.eco_account, + surface=0 + ) + self.assertEqual(1, self.intervention.deductions.count()) + self.assertEqual(1, self.eco_account.deductions.count()) + + # Prepare url and form data to be posted + new_url = reverse("compensation:acc:edit-deduction", args=(self.eco_account.id, deduction.id)) + post_data = { + "intervention": deduction.intervention.id, + "account": deduction.account.id, + "surface": test_surface, + } + pre_edit_intervention_log_count = self.intervention.log.count() + pre_edit_account_log_count = self.eco_account.log.count() + num_deductions_intervention = self.intervention.deductions.count() + num_deductions_account = self.eco_account.deductions.count() + + self.client_user.post(new_url, post_data) + + self.intervention.refresh_from_db() + self.eco_account.refresh_from_db() + deduction.refresh_from_db() + + self.assertEqual(num_deductions_intervention, self.intervention.deductions.count()) + self.assertEqual(num_deductions_account, self.eco_account.deductions.count()) + self.assertEqual(deduction.surface, test_surface) + + # Expect logs to be set + self.assertEqual(pre_edit_intervention_log_count + 1, self.intervention.log.count()) + self.assertEqual(pre_edit_account_log_count + 1, self.eco_account.log.count()) + self.assertEqual(self.intervention.log.first().action, UserAction.EDITED) + self.assertEqual(self.eco_account.log.first().action, UserAction.EDITED) + + def test_remove_deduction(self): + intervention = self.deduction.intervention + account = self.deduction.account + + # Prepare url and form data to be posted + new_url = reverse("compensation:acc:remove-deduction", args=(account.id, self.deduction.id)) + post_data = { + "confirm": True, + } + + intervention.share_with(self.superuser) + account.share_with(self.superuser) + + pre_edit_intervention_log_count = intervention.log.count() + pre_edit_account_log_count = account.log.count() + num_deductions_intervention = intervention.deductions.count() + num_deductions_account = account.deductions.count() + + self.client_user.post(new_url, post_data) + + intervention.refresh_from_db() + account.refresh_from_db() + + self.assertEqual(num_deductions_intervention - 1, intervention.deductions.count()) + self.assertEqual(num_deductions_account - 1, account.deductions.count()) + + # Expect logs to be set + self.assertEqual(pre_edit_intervention_log_count + 1, intervention.log.count()) + self.assertEqual(pre_edit_account_log_count + 1, account.log.count()) + self.assertEqual(intervention.log.first().action, UserAction.EDITED) + self.assertEqual(account.log.first().action, UserAction.EDITED) diff --git a/compensation/tests/payment/test_views.py b/compensation/tests/payment/test_views.py index 69130e86..b1eca5ae 100644 --- a/compensation/tests/payment/test_views.py +++ b/compensation/tests/payment/test_views.py @@ -19,16 +19,18 @@ class PaymentViewTestCase(BaseViewTestCase): def setUpTestData(cls) -> None: super().setUpTestData() - cls.payment = Payment.objects.get_or_create( - intervention=cls.intervention, + def setUp(self) -> None: + super().setUp() + self.payment = Payment.objects.get_or_create( + intervention=self.intervention, amount=1, due_on="2020-01-01", comment="Testcomment" )[0] - cls.new_url = reverse("compensation:pay:new", args=(cls.intervention.id,)) - cls.edit_url = reverse("compensation:pay:edit", args=(cls.intervention.id, cls.payment.id)) - cls.remove_url = reverse("compensation:pay:remove", args=(cls.intervention.id, cls.payment.id)) + self.new_url = reverse("compensation:pay:new", args=(self.intervention.id,)) + self.edit_url = reverse("compensation:pay:edit", args=(self.intervention.id, self.payment.id)) + self.remove_url = reverse("compensation:pay:remove", args=(self.intervention.id, self.payment.id)) def test_anonymous_user(self): """ Check correct status code for all requests diff --git a/compensation/tests/payment/test_workflow.py b/compensation/tests/payment/test_workflow.py index 09ff0e69..790fb619 100644 --- a/compensation/tests/payment/test_workflow.py +++ b/compensation/tests/payment/test_workflow.py @@ -18,11 +18,13 @@ class PaymentWorkflowTestCase(BaseWorkflowTestCase): def setUpTestData(cls): super().setUpTestData() + def setUp(self) -> None: + super().setUp() # Give the user shared access to the dummy intervention - cls.intervention.share_with(cls.superuser) + self.intervention.share_with(self.superuser) - cls.payment = Payment.objects.get_or_create( - intervention=cls.intervention, + self.payment = Payment.objects.get_or_create( + intervention=self.intervention, amount=1, due_on="2020-01-01", comment="Testcomment" diff --git a/compensation/urls/eco_account.py b/compensation/urls/eco_account.py index 7deb48f7..0c3a0293 100644 --- a/compensation/urls/eco_account.py +++ b/compensation/urls/eco_account.py @@ -34,7 +34,8 @@ urlpatterns = [ path('document//remove/', remove_document_view, name='remove-doc'), # Eco-account deductions - path('/remove/', deduction_remove_view, name='remove-deduction'), + path('/deduction//remove', deduction_remove_view, name='remove-deduction'), + path('/deduction//edit', deduction_edit_view, name='edit-deduction'), path('/deduct/new', new_deduction_view, name='new-deduction'), ] \ No newline at end of file diff --git a/compensation/views/eco_account.py b/compensation/views/eco_account.py index 7d1e560f..08e01a61 100644 --- a/compensation/views/eco_account.py +++ b/compensation/views/eco_account.py @@ -19,7 +19,8 @@ from compensation.forms.modalForms import NewStateModalForm, NewActionModalForm, NewEcoAccountDocumentForm, RemoveCompensationActionModalForm, RemoveCompensationStateModalForm from compensation.models import EcoAccount, EcoAccountDocument, CompensationState, CompensationAction from compensation.tables import EcoAccountTable -from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm +from intervention.forms.modalForms import NewDeductionModalForm, ShareModalForm, RemoveEcoAccountDeductionModalForm, \ + EditEcoAccountDeductionModalForm from konova.contexts import BaseContext from konova.decorators import any_group_check, default_group_required, conservation_office_group_required, \ shared_access_required @@ -31,7 +32,8 @@ from konova.utils.documents import get_document, remove_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import IDENTIFIER_REPLACED, FORM_INVALID, DATA_UNSHARED, DATA_UNSHARED_EXPLANATION, \ CANCEL_ACC_RECORDED_OR_DEDUCTED, DEDUCTION_REMOVED, DEDUCTION_ADDED, DOCUMENT_ADDED, COMPENSATION_STATE_REMOVED, \ - COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED + COMPENSATION_STATE_ADDED, COMPENSATION_ACTION_REMOVED, COMPENSATION_ACTION_ADDED, DEADLINE_ADDED, DEADLINE_REMOVED, \ + DEDUCTION_EDITED from konova.utils.user_checks import in_group @@ -294,6 +296,34 @@ def deduction_remove_view(request: HttpRequest, id: str, deduction_id: str): ) +@login_required +@default_group_required +@shared_access_required(EcoAccount, "id") +def deduction_edit_view(request: HttpRequest, id: str, deduction_id: str): + """ Renders a modal view for editing deductions + + Args: + request (HttpRequest): The incoming request + id (str): The eco account's id + deduction_id (str): The deduction's id + + Returns: + + """ + acc = get_object_or_404(EcoAccount, id=id) + try: + eco_deduction = acc.deductions.get(id=deduction_id) + except ObjectDoesNotExist: + raise Http404("Unknown deduction") + + form = EditEcoAccountDeductionModalForm(request.POST or None, instance=acc, deduction=eco_deduction, request=request) + return form.process_request( + request=request, + msg_success=DEDUCTION_EDITED, + redirect_url=reverse("compensation:acc:detail", args=(id,)) + "#related_data" + ) + + @login_required @default_group_required @shared_access_required(EcoAccount, "id") diff --git a/ema/tests/test_views.py b/ema/tests/test_views.py index dd37fced..65f4d351 100644 --- a/ema/tests/test_views.py +++ b/ema/tests/test_views.py @@ -31,42 +31,43 @@ class EmaViewTestCase(CompensationViewTestCase): def setUpTestData(cls) -> None: super().setUpTestData() + def setUp(self) -> None: + super().setUp() # Create dummy data and related objects, like states or actions - cls.create_dummy_data() - state = cls.create_dummy_states() - action = cls.create_dummy_action() - cls.ema.before_states.set([state]) - cls.ema.after_states.set([state]) - cls.ema.actions.set([action]) + self.create_dummy_data() + state = self.create_dummy_states() + action = self.create_dummy_action() + self.ema.before_states.set([state]) + self.ema.after_states.set([state]) + self.ema.actions.set([action]) # Prepare urls - cls.index_url = reverse("ema:index", args=()) - cls.new_url = reverse("ema:new", args=()) - cls.new_id_url = reverse("ema:new-id", args=()) - cls.detail_url = reverse("ema:detail", args=(cls.ema.id,)) - cls.log_url = reverse("ema:log", args=(cls.ema.id,)) - cls.edit_url = reverse("ema:edit", args=(cls.ema.id,)) - cls.remove_url = reverse("ema:remove", args=(cls.ema.id,)) - cls.share_url = reverse("ema:share", args=(cls.ema.id, cls.ema.access_token,)) - cls.share_create_url = reverse("ema:share-create", args=(cls.ema.id,)) - cls.record_url = reverse("ema:record", args=(cls.ema.id,)) - cls.report_url = reverse("ema:report", args=(cls.ema.id,)) - cls.new_doc_url = reverse("ema:new-doc", args=(cls.ema.id,)) - cls.state_new_url = reverse("ema:new-state", args=(cls.ema.id,)) - cls.action_new_url = reverse("ema:new-action", args=(cls.ema.id,)) - cls.deadline_new_url = reverse("ema:new-deadline", args=(cls.ema.id,)) - cls.state_remove_url = reverse("ema:state-remove", args=(cls.ema.id, state.id,)) - cls.action_remove_url = reverse("ema:action-remove", args=(cls.ema.id, action.id,)) + self.index_url = reverse("ema:index", args=()) + self.new_url = reverse("ema:new", args=()) + self.new_id_url = reverse("ema:new-id", args=()) + self.detail_url = reverse("ema:detail", args=(self.ema.id,)) + self.log_url = reverse("ema:log", args=(self.ema.id,)) + self.edit_url = reverse("ema:edit", args=(self.ema.id,)) + self.remove_url = reverse("ema:remove", args=(self.ema.id,)) + self.share_url = reverse("ema:share", args=(self.ema.id, self.ema.access_token,)) + self.share_create_url = reverse("ema:share-create", args=(self.ema.id,)) + self.record_url = reverse("ema:record", args=(self.ema.id,)) + self.report_url = reverse("ema:report", args=(self.ema.id,)) + self.new_doc_url = reverse("ema:new-doc", args=(self.ema.id,)) + self.state_new_url = reverse("ema:new-state", args=(self.ema.id,)) + self.action_new_url = reverse("ema:new-action", args=(self.ema.id,)) + self.deadline_new_url = reverse("ema:new-deadline", args=(self.ema.id,)) + self.state_remove_url = reverse("ema:state-remove", args=(self.ema.id, state.id,)) + self.action_remove_url = reverse("ema:action-remove", args=(self.ema.id, action.id,)) - @classmethod - def create_dummy_data(cls): + def create_dummy_data(self): # Create dummy data # Create log entry - action = UserActionLogEntry.get_created_action(cls.superuser) + action = UserActionLogEntry.get_created_action(self.superuser) # Create responsible data object responsibility_data = Responsibility.objects.create() geometry = Geometry.objects.create() - cls.ema = Ema.objects.create( + self.ema = Ema.objects.create( identifier="TEST", title="Test_title", created=action, diff --git a/intervention/forms/modalForms.py b/intervention/forms/modalForms.py index 6e2a2a39..6a044c7c 100644 --- a/intervention/forms/modalForms.py +++ b/intervention/forms/modalForms.py @@ -7,7 +7,7 @@ Created on: 27.09.21 """ from dal import autocomplete -from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED, DEDUCTION_REMOVED +from konova.utils.message_templates import DEDUCTION_ADDED, REVOCATION_ADDED, DEDUCTION_REMOVED, DEDUCTION_EDITED from user.models import User, UserActionLogEntry from django.db import transaction from django import forms @@ -349,6 +349,21 @@ class NewDeductionModalForm(BaseModalForm): else: raise NotImplementedError + def _get_available_surface(self, acc): + """ Calculates how much available surface is left on the account + + Args: + acc (EcoAccount): + + Returns: + + """ + # Calculate valid surface + deductable_surface = acc.deductable_surface + sum_surface_deductions = acc.get_deductions_surface() + rest_surface = deductable_surface - sum_surface_deductions + return rest_surface + def is_valid(self): """ Custom validity check @@ -367,10 +382,7 @@ class NewDeductionModalForm(BaseModalForm): ) return False - # Calculate valid surface - deductable_surface = acc.deductable_surface - sum_surface_deductions = acc.get_deductions_surface() - rest_surface = deductable_surface - sum_surface_deductions + rest_surface = self._get_available_surface(acc) form_surface = float(self.cleaned_data["surface"]) is_valid_surface = form_surface <= rest_surface if not is_valid_surface: @@ -407,6 +419,57 @@ class NewDeductionModalForm(BaseModalForm): return deduction +class EditEcoAccountDeductionModalForm(NewDeductionModalForm): + deduction = None + + def __init__(self, *args, **kwargs): + self.deduction = kwargs.pop("deduction", None) + super().__init__(*args, **kwargs) + form_data = { + "account": self.deduction.account, + "intervention": self.deduction.intervention, + "surface": self.deduction.surface, + } + self.load_initial_data(form_data) + + def _get_available_surface(self, acc): + rest_surface = super()._get_available_surface(acc) + # Increase available surface by the currently deducted surface, so we can 'deduct' the same amount again or + # increase the surface only a little, which will still be valid. + # Example: 200 m² left, 500 m² deducted. Entering 700 m² would fail if we would not add the 500 m² to the available + # surface again. + rest_surface += self.deduction.surface + return rest_surface + + def save(self): + deduction = self.deduction + form_account = self.cleaned_data.get("account", None) + form_intervention = self.cleaned_data.get("intervention", None) + current_account = deduction.account + current_intervention = deduction.intervention + + + # If account or intervention has been changed, we put that change in the logs just as if the deduction has + # been removed for this entry. Act as if the deduction is newly created for the new entries + if current_account != form_account: + current_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_REMOVED) + form_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_ADDED) + else: + current_account.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_EDITED) + + if current_intervention != form_intervention: + current_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_REMOVED) + form_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_ADDED) + else: + current_intervention.mark_as_edited(self.user, self.request, edit_comment=DEDUCTION_EDITED) + + deduction.account = form_account + deduction.intervention = self.cleaned_data.get("intervention", None) + deduction.surface = self.cleaned_data.get("surface", None) + deduction.save() + return deduction + + class RemoveEcoAccountDeductionModalForm(RemoveModalForm): """ Removing modal form for EcoAccountDeduction diff --git a/intervention/templates/intervention/detail/includes/deductions.html b/intervention/templates/intervention/detail/includes/deductions.html index 99f11cb8..b11817d1 100644 --- a/intervention/templates/intervention/detail/includes/deductions.html +++ b/intervention/templates/intervention/detail/includes/deductions.html @@ -55,9 +55,12 @@ {{ deduction.surface|floatformat:2|intcomma }} m² {{ deduction.created.timestamp|default_if_none:""|naturalday}} - + {% if is_default_member and has_access %} - + {% endif %} diff --git a/intervention/tests/test_views.py b/intervention/tests/test_views.py index 2cdb299d..68e4b562 100644 --- a/intervention/tests/test_views.py +++ b/intervention/tests/test_views.py @@ -21,30 +21,32 @@ class InterventionViewTestCase(BaseViewTestCase): def setUpTestData(cls) -> None: super().setUpTestData() + def setUp(self) -> None: + super().setUp() # Prepare urls - cls.index_url = reverse("intervention:index", args=()) - cls.new_url = reverse("intervention:new", args=()) - cls.new_id_url = reverse("intervention:new-id", args=()) - cls.detail_url = reverse("intervention:detail", args=(cls.intervention.id,)) - cls.log_url = reverse("intervention:log", args=(cls.intervention.id,)) - cls.edit_url = reverse("intervention:edit", args=(cls.intervention.id,)) - cls.remove_url = reverse("intervention:remove", args=(cls.intervention.id,)) - cls.share_url = reverse("intervention:share", args=(cls.intervention.id, cls.intervention.access_token,)) - cls.share_create_url = reverse("intervention:share-create", args=(cls.intervention.id,)) - cls.run_check_url = reverse("intervention:check", args=(cls.intervention.id,)) - cls.record_url = reverse("intervention:record", args=(cls.intervention.id,)) - cls.report_url = reverse("intervention:report", args=(cls.intervention.id,)) + self.index_url = reverse("intervention:index", args=()) + self.new_url = reverse("intervention:new", args=()) + self.new_id_url = reverse("intervention:new-id", args=()) + self.detail_url = reverse("intervention:detail", args=(self.intervention.id,)) + self.log_url = reverse("intervention:log", args=(self.intervention.id,)) + self.edit_url = reverse("intervention:edit", args=(self.intervention.id,)) + self.remove_url = reverse("intervention:remove", args=(self.intervention.id,)) + self.share_url = reverse("intervention:share", args=(self.intervention.id, self.intervention.access_token,)) + self.share_create_url = reverse("intervention:share-create", args=(self.intervention.id,)) + self.run_check_url = reverse("intervention:check", args=(self.intervention.id,)) + self.record_url = reverse("intervention:record", args=(self.intervention.id,)) + self.report_url = reverse("intervention:report", args=(self.intervention.id,)) - cls.deduction.intervention = cls.intervention - cls.deduction.save() - cls.deduction_new_url = reverse("intervention:new-deduction", args=(cls.intervention.id,)) - cls.deduction_remove_url = reverse("intervention:remove-deduction", args=(cls.intervention.id, cls.deduction.id)) + self.deduction.intervention = self.intervention + self.deduction.save() + self.deduction_new_url = reverse("intervention:new-deduction", args=(self.intervention.id,)) + self.deduction_remove_url = reverse("intervention:remove-deduction", args=(self.intervention.id, self.deduction.id)) - cls.revocation = Revocation.objects.create( - legal=cls.intervention.legal + self.revocation = Revocation.objects.create( + legal=self.intervention.legal ) - cls.revocation_new_url = reverse("intervention:new-revocation", args=(cls.intervention.id,)) - cls.revocation_remove_url = reverse("intervention:remove-revocation", args=(cls.intervention.id, cls.revocation.id)) + self.revocation_new_url = reverse("intervention:new-revocation", args=(self.intervention.id,)) + self.revocation_remove_url = reverse("intervention:remove-revocation", args=(self.intervention.id, self.revocation.id)) def test_views_anonymous_user(self): """ Check correct status code for all requests diff --git a/intervention/urls.py b/intervention/urls.py index 7d6ff32f..8ad7f31e 100644 --- a/intervention/urls.py +++ b/intervention/urls.py @@ -10,7 +10,7 @@ from django.urls import path from intervention.views import index_view, new_view, detail_view, edit_view, remove_view, new_document_view, share_view, \ create_share_view, remove_revocation_view, new_revocation_view, check_view, log_view, new_deduction_view, \ record_view, remove_document_view, get_document_view, get_revocation_view, new_id_view, report_view, \ - remove_deduction_view, remove_compensation_view + remove_deduction_view, remove_compensation_view, edit_deduction_view app_name = "intervention" urlpatterns = [ @@ -37,6 +37,7 @@ urlpatterns = [ # Deductions path('/deduction/new', new_deduction_view, name='new-deduction'), + path('/deduction//edit', edit_deduction_view, name='edit-deduction'), path('/deduction//remove', remove_deduction_view, name='remove-deduction'), # Revocation routes diff --git a/intervention/views.py b/intervention/views.py index 8a59d9a9..6db35474 100644 --- a/intervention/views.py +++ b/intervention/views.py @@ -7,7 +7,7 @@ from django.shortcuts import render from intervention.forms.forms import NewInterventionForm, EditInterventionForm from intervention.forms.modalForms import ShareModalForm, NewRevocationModalForm, \ CheckModalForm, NewDeductionModalForm, NewInterventionDocumentForm, RemoveEcoAccountDeductionModalForm, \ - RemoveRevocationModalForm + RemoveRevocationModalForm, EditEcoAccountDeductionModalForm from intervention.models import Intervention, Revocation, InterventionDocument, RevocationDocument from intervention.tables import InterventionTable from konova.contexts import BaseContext @@ -18,7 +18,7 @@ from konova.utils.documents import remove_document, get_document from konova.utils.generators import generate_qr_code from konova.utils.message_templates import INTERVENTION_INVALID, FORM_INVALID, IDENTIFIER_REPLACED, \ CHECKED_RECORDED_RESET, DEDUCTION_REMOVED, DEDUCTION_ADDED, REVOCATION_ADDED, REVOCATION_REMOVED, \ - COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED + COMPENSATION_REMOVED_TEMPLATE, DOCUMENT_ADDED, DEDUCTION_EDITED from konova.utils.user_checks import in_group @@ -536,6 +536,34 @@ def remove_deduction_view(request: HttpRequest, id: str, deduction_id: str): ) +@login_required +@default_group_required +@shared_access_required(Intervention, "id") +def edit_deduction_view(request: HttpRequest, id: str, deduction_id: str): + """ Renders a modal view for removing deductions + + Args: + request (HttpRequest): The incoming request + id (str): The intervention's id + deduction_id (str): The deduction's id + + Returns: + + """ + intervention = get_object_or_404(Intervention, id=id) + try: + eco_deduction = intervention.deductions.get(id=deduction_id) + except ObjectDoesNotExist: + raise Http404("Unknown deduction") + + form = EditEcoAccountDeductionModalForm(request.POST or None, instance=intervention, deduction=eco_deduction, request=request) + return form.process_request( + request=request, + msg_success=DEDUCTION_EDITED, + redirect_url=reverse("intervention:detail", args=(id,)) + "#related_data" + ) + + @login_required @conservation_office_group_required @shared_access_required(Intervention, "id") diff --git a/konova/tests/test_views.py b/konova/tests/test_views.py index 536a7868..6218a6c0 100644 --- a/konova/tests/test_views.py +++ b/konova/tests/test_views.py @@ -47,43 +47,58 @@ class BaseTestCase(TestCase): class Meta: abstract = True - @classmethod - def setUpTestData(cls): - cls.create_users() - cls.create_groups() - cls.intervention = cls.create_dummy_intervention() - cls.compensation = cls.create_dummy_compensation() - cls.eco_account = cls.create_dummy_eco_account() - cls.ema = cls.create_dummy_ema() - cls.deduction = cls.create_dummy_deduction() - cls.create_dummy_states() - cls.create_dummy_action() - cls.codes = cls.create_dummy_codes() + def setUp(self) -> None: + """ Setup data before each test run - @classmethod - def create_users(cls): + Returns: + + """ + super().setUp() + + self.create_users() + self.create_groups() + self.intervention = self.create_dummy_intervention() + self.compensation = self.create_dummy_compensation() + self.eco_account = self.create_dummy_eco_account() + self.ema = self.create_dummy_ema() + self.deduction = self.create_dummy_deduction() + self.create_dummy_states() + self.create_dummy_action() + self.codes = self.create_dummy_codes() + + # Set the default group as only group for the user + default_group = self.groups.get(name=DEFAULT_GROUP) + self.superuser.groups.set([default_group]) + + # Create fresh logged in client and a non-logged in client (anon) for each test + self.client_user = Client() + self.client_user.login(username=self.superuser.username, password=self.superuser_pw) + self.client_anon = Client() + + + def create_users(self): # Create superuser and regular user - cls.superuser = User.objects.create_superuser( + self.superuser = User.objects.create_superuser( username="root", email="root@root.com", - password=cls.superuser_pw, + password=self.superuser_pw, ) - cls.user = User.objects.create_user( + self.user = User.objects.create_user( username="user1", email="user@root.com", - password=cls.user_pw + password=self.user_pw ) - cls.users = User.objects.all() + self.users = User.objects.all() - @classmethod - def create_groups(cls): + + def create_groups(self): # Create groups for group_data in GROUPS_DATA: name = group_data.get("name") Group.objects.get_or_create( name=name, ) - cls.groups = Group.objects.all() + self.groups = Group.objects.all() @staticmethod def create_dummy_string(prefix: str = ""): @@ -94,8 +109,7 @@ class BaseTestCase(TestCase): """ return f"{prefix}{generate_random_string(3, True)}" - @classmethod - def create_dummy_intervention(cls): + def create_dummy_intervention(self): """ Creates an intervention which can be used for tests Returns: @@ -103,7 +117,7 @@ class BaseTestCase(TestCase): """ # Create dummy data # Create log entry - action = UserActionLogEntry.get_created_action(cls.superuser) + action = UserActionLogEntry.get_created_action(self.superuser) # Create legal data object (without M2M laws first) legal_data = Legal.objects.create() # Create responsible data object @@ -122,32 +136,30 @@ class BaseTestCase(TestCase): intervention.generate_access_token(make_unique=True) return intervention - @classmethod - def create_dummy_compensation(cls): + def create_dummy_compensation(self): """ Creates a compensation which can be used for tests Returns: """ - if cls.intervention is None: - cls.intervention = cls.create_dummy_intervention() + if self.intervention is None: + self.intervention = self.create_dummy_intervention() # Create dummy data # Create log entry - action = UserActionLogEntry.get_created_action(cls.superuser) + action = UserActionLogEntry.get_created_action(self.superuser) geometry = Geometry.objects.create() # Finally create main object, holding the other objects compensation = Compensation.objects.create( identifier="TEST", title="Test_title", - intervention=cls.intervention, + intervention=self.intervention, created=action, geometry=geometry, comment="Test", ) return compensation - @classmethod - def create_dummy_eco_account(cls): + def create_dummy_eco_account(self): """ Creates an eco account which can be used for tests Returns: @@ -155,7 +167,7 @@ class BaseTestCase(TestCase): """ # Create dummy data # Create log entry - action = UserActionLogEntry.get_created_action(cls.superuser) + action = UserActionLogEntry.get_created_action(self.superuser) geometry = Geometry.objects.create() # Create responsible data object lega_data = Legal.objects.create() @@ -172,8 +184,7 @@ class BaseTestCase(TestCase): ) return eco_account - @classmethod - def create_dummy_ema(cls): + def create_dummy_ema(self): """ Creates an ema which can be used for tests Returns: @@ -181,7 +192,7 @@ class BaseTestCase(TestCase): """ # Create dummy data # Create log entry - action = UserActionLogEntry.get_created_action(cls.superuser) + action = UserActionLogEntry.get_created_action(self.superuser) geometry = Geometry.objects.create() # Create responsible data object responsible_data = Responsibility.objects.create() @@ -196,41 +207,37 @@ class BaseTestCase(TestCase): ) return ema - @classmethod - def create_dummy_deduction(cls): + def create_dummy_deduction(self): return EcoAccountDeduction.objects.create( - account=cls.create_dummy_eco_account(), - intervention=cls.create_dummy_intervention(), + account=self.create_dummy_eco_account(), + intervention=self.create_dummy_intervention(), surface=100, ) - @classmethod - def create_dummy_states(cls): + def create_dummy_states(self): """ Creates an intervention which can be used for tests Returns: """ - cls.comp_state = CompensationState.objects.create( + self.comp_state = CompensationState.objects.create( surface=10.00, biotope_type=None, ) - return cls.comp_state + return self.comp_state - @classmethod - def create_dummy_action(cls): + def create_dummy_action(self): """ Creates an intervention which can be used for tests Returns: """ - cls.comp_action = CompensationAction.objects.create( + self.comp_action = CompensationAction.objects.create( amount=10 ) - return cls.comp_action + return self.comp_action - @classmethod - def create_dummy_codes(cls): + def create_dummy_codes(self): """ Creates some dummy KonovaCodes which can be used for testing Returns: @@ -256,8 +263,7 @@ class BaseTestCase(TestCase): polygon = polygon.transform(3857, clone=True) return MultiPolygon(polygon, srid=3857) # 3857 is the default srid used for MultiPolygonField in the form - @classmethod - def fill_out_intervention(cls, intervention: Intervention) -> Intervention: + def fill_out_intervention(self, intervention: Intervention) -> Intervention: """ Adds all required (dummy) data to an intervention Args: @@ -277,13 +283,12 @@ class BaseTestCase(TestCase): intervention.legal.process_type = KonovaCode.objects.get(id=3) intervention.legal.save() intervention.legal.laws.set([KonovaCode.objects.get(id=(4))]) - intervention.geometry.geom = cls.create_dummy_geometry() + intervention.geometry.geom = self.create_dummy_geometry() intervention.geometry.save() intervention.save() return intervention - @classmethod - def fill_out_compensation(cls, compensation: Compensation) -> Compensation: + def fill_out_compensation(self, compensation: Compensation) -> Compensation: """ Adds all required (dummy) data to a compensation Args: @@ -292,15 +297,14 @@ class BaseTestCase(TestCase): Returns: compensation (Compensation): The modified compensation """ - compensation.after_states.add(cls.comp_state) - compensation.before_states.add(cls.comp_state) - compensation.actions.add(cls.comp_action) - compensation.geometry.geom = cls.create_dummy_geometry() + compensation.after_states.add(self.comp_state) + compensation.before_states.add(self.comp_state) + compensation.actions.add(self.comp_action) + compensation.geometry.geom = self.create_dummy_geometry() compensation.geometry.save() return compensation - @classmethod - def get_conservation_office_code(cls): + def get_conservation_office_code(self): """ Returns a dummy KonovaCode as conservation office code Returns: @@ -313,39 +317,37 @@ class BaseTestCase(TestCase): codelist.codes.add(code) return code - @classmethod - def fill_out_ema(cls, ema): + def fill_out_ema(self, ema): """ Adds all required (dummy) data to an Ema Returns: """ - ema.responsible.conservation_office = cls.get_conservation_office_code() + ema.responsible.conservation_office = self.get_conservation_office_code() ema.responsible.conservation_file_number = "test" ema.responsible.handler = "handler" ema.responsible.save() - ema.after_states.add(cls.comp_state) - ema.before_states.add(cls.comp_state) - ema.actions.add(cls.comp_action) - ema.geometry.geom = cls.create_dummy_geometry() + ema.after_states.add(self.comp_state) + ema.before_states.add(self.comp_state) + ema.actions.add(self.comp_action) + ema.geometry.geom = self.create_dummy_geometry() ema.geometry.save() return ema - @classmethod - def fill_out_eco_account(cls, eco_account): + def fill_out_eco_account(self, eco_account): """ Adds all required (dummy) data to an EcoAccount Returns: """ eco_account.legal.registration_date = "2022-01-01" eco_account.legal.save() - eco_account.responsible.conservation_office = cls.get_conservation_office_code() + eco_account.responsible.conservation_office = self.get_conservation_office_code() eco_account.responsible.conservation_file_number = "test" eco_account.responsible.handler = "handler" eco_account.responsible.save() - eco_account.after_states.add(cls.comp_state) - eco_account.before_states.add(cls.comp_state) - eco_account.actions.add(cls.comp_action) - eco_account.geometry.geom = cls.create_dummy_geometry() + eco_account.after_states.add(self.comp_state) + eco_account.before_states.add(self.comp_state) + eco_account.actions.add(self.comp_action) + eco_account.geometry.geom = self.create_dummy_geometry() eco_account.geometry.save() eco_account.deductable_surface = eco_account.get_state_after_surface_sum() eco_account.save() @@ -390,7 +392,10 @@ class BaseViewTestCase(BaseTestCase): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() - cls.login_url = reverse("simple-sso-login") + + def setUp(self) -> None: + super().setUp() + self.login_url = reverse("simple-sso-login") def assert_url_success(self, client: Client, urls: list): """ Assert for all given urls a direct 200 response @@ -549,22 +554,6 @@ class BaseWorkflowTestCase(BaseTestCase): def setUpTestData(cls): super().setUpTestData() - def setUp(self) -> None: - """ Setup data before each test run - - Returns: - - """ - super().setUp() - # Set the default group as only group for the user - default_group = self.groups.get(name=DEFAULT_GROUP) - self.superuser.groups.set([default_group]) - - # Create fresh logged in client and a non-logged in client (anon) for each test - self.client_user = Client() - self.client_user.login(username=self.superuser.username, password=self.superuser_pw) - self.client_anon = Client() - def assert_object_is_deleted(self, obj): """ Provides a quick check whether an object has been removed from the database or not diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index b7eb5253..ea778ea2 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -30,18 +30,22 @@ ADDED_COMPENSATION_STATE = _("Added compensation state") # COMPENSATION STATE COMPENSATION_STATE_REMOVED = _("State removed") +COMPENSATION_STATE_EDITED = _("State edited") COMPENSATION_STATE_ADDED = _("State added") # COMPENSATION ACTION COMPENSATION_ACTION_ADDED = _("Action added") +COMPENSATION_ACTION_EDITED = _("Action edited") COMPENSATION_ACTION_REMOVED = _("Action removed") # DEDUCTIONS DEDUCTION_ADDED = _("Deduction added") +DEDUCTION_EDITED = _("Deduction edited") DEDUCTION_REMOVED = _("Deduction removed") # DEADLINE DEADLINE_ADDED = _("Deadline added") +DEADLINE_EDITED = _("Deadline edited") DEADLINE_REMOVED = _("Deadline removed") # PAYMENTS @@ -51,6 +55,7 @@ PAYMENT_REMOVED = _("Payment removed") # REVOCATIONS REVOCATION_ADDED = _("Revocation added") +REVOCATION_EDITED = _("Revocation edited") REVOCATION_REMOVED = _("Revocation removed") # DOCUMENTS diff --git a/locale/de/LC_MESSAGES/django.mo b/locale/de/LC_MESSAGES/django.mo index 85b75fdd..86cf2739 100644 Binary files a/locale/de/LC_MESSAGES/django.mo and b/locale/de/LC_MESSAGES/django.mo differ diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 3feeb6db..67ae2338 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ # #: compensation/filters.py:122 compensation/forms/modalForms.py:35 #: compensation/forms/modalForms.py:46 compensation/forms/modalForms.py:62 -#: compensation/forms/modalForms.py:329 compensation/forms/modalForms.py:422 +#: compensation/forms/modalForms.py:332 compensation/forms/modalForms.py:425 #: intervention/forms/forms.py:54 intervention/forms/forms.py:156 #: intervention/forms/forms.py:168 intervention/forms/modalForms.py:124 #: intervention/forms/modalForms.py:137 intervention/forms/modalForms.py:150 @@ -26,7 +26,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-02-09 09:50+0100\n" +"POT-Creation-Date: 2022-02-09 12:52+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -95,7 +95,7 @@ msgstr "" #: analysis/templates/analysis/reports/includes/eco_account/amount.html:3 #: analysis/templates/analysis/reports/includes/intervention/amount.html:3 #: analysis/templates/analysis/reports/includes/old_data/amount.html:3 -#: compensation/forms/modalForms.py:406 +#: compensation/forms/modalForms.py:409 #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:34 #: intervention/templates/intervention/detail/includes/deductions.html:31 msgid "Amount" @@ -213,7 +213,7 @@ msgstr "Abbuchungen" #: analysis/templates/analysis/reports/includes/eco_account/deductions.html:9 #: analysis/templates/analysis/reports/includes/eco_account/deductions.html:11 -#: compensation/forms/modalForms.py:190 +#: compensation/forms/modalForms.py:193 #: compensation/templates/compensation/detail/compensation/includes/states-after.html:36 #: compensation/templates/compensation/detail/compensation/includes/states-before.html:36 #: compensation/templates/compensation/detail/eco_account/includes/states-after.html:36 @@ -353,7 +353,7 @@ msgid "Compensation XY; Location ABC" msgstr "Kompensation XY; Flur ABC" #: compensation/forms/forms.py:57 compensation/forms/modalForms.py:61 -#: compensation/forms/modalForms.py:328 compensation/forms/modalForms.py:421 +#: compensation/forms/modalForms.py:331 compensation/forms/modalForms.py:424 #: compensation/templates/compensation/detail/compensation/includes/actions.html:35 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:34 #: compensation/templates/compensation/detail/compensation/includes/documents.html:31 @@ -371,7 +371,7 @@ msgstr "Kompensation XY; Flur ABC" msgid "Comment" msgstr "Kommentar" -#: compensation/forms/forms.py:59 compensation/forms/modalForms.py:423 +#: compensation/forms/forms.py:59 compensation/forms/modalForms.py:426 #: intervention/forms/forms.py:182 msgid "Additional comment" msgstr "Zusätzlicher Kommentar" @@ -457,7 +457,7 @@ msgstr "Vereinbarungsdatum" msgid "When did the parties agree on this?" msgstr "Wann wurde dieses Ökokonto offiziell vereinbart?" -#: compensation/forms/forms.py:354 compensation/views/eco_account.py:103 +#: compensation/forms/forms.py:354 compensation/views/eco_account.py:105 msgid "New Eco-Account" msgstr "Neues Ökokonto" @@ -482,7 +482,7 @@ msgstr "Fällig am" msgid "Due on which date" msgstr "Zahlung wird an diesem Datum erwartet" -#: compensation/forms/modalForms.py:63 compensation/forms/modalForms.py:330 +#: compensation/forms/modalForms.py:63 compensation/forms/modalForms.py:333 #: intervention/forms/modalForms.py:151 konova/forms.py:393 msgid "Additional comment, maximum {} letters" msgstr "Zusätzlicher Kommentar, maximal {} Zeichen" @@ -495,47 +495,47 @@ msgstr "Neue Ersatzzahlung zu Eingriff '{}' hinzufügen" msgid "If there is no date you can enter, please explain why." msgstr "Falls Sie kein Datum angeben können, erklären Sie bitte weshalb." -#: compensation/forms/modalForms.py:154 compensation/forms/modalForms.py:166 +#: compensation/forms/modalForms.py:157 compensation/forms/modalForms.py:169 msgid "Biotope Type" msgstr "Biotoptyp" -#: compensation/forms/modalForms.py:157 +#: compensation/forms/modalForms.py:160 msgid "Select the biotope type" msgstr "Biotoptyp wählen" -#: compensation/forms/modalForms.py:171 compensation/forms/modalForms.py:183 +#: compensation/forms/modalForms.py:174 compensation/forms/modalForms.py:186 msgid "Biotope additional type" msgstr "Zusatzbezeichnung" -#: compensation/forms/modalForms.py:174 +#: compensation/forms/modalForms.py:177 msgid "Select an additional biotope type" msgstr "Zusatzbezeichnung wählen" -#: compensation/forms/modalForms.py:193 intervention/forms/modalForms.py:313 +#: compensation/forms/modalForms.py:196 intervention/forms/modalForms.py:313 msgid "in m²" msgstr "" -#: compensation/forms/modalForms.py:204 +#: compensation/forms/modalForms.py:207 msgid "New state" msgstr "Neuer Zustand" -#: compensation/forms/modalForms.py:205 +#: compensation/forms/modalForms.py:208 msgid "Insert data for the new state" msgstr "Geben Sie die Daten des neuen Zustandes ein" -#: compensation/forms/modalForms.py:212 konova/forms.py:191 +#: compensation/forms/modalForms.py:215 konova/forms.py:191 msgid "Object removed" msgstr "Objekt entfernt" -#: compensation/forms/modalForms.py:300 +#: compensation/forms/modalForms.py:303 msgid "Deadline Type" msgstr "Fristart" -#: compensation/forms/modalForms.py:303 +#: compensation/forms/modalForms.py:306 msgid "Select the deadline type" msgstr "Fristart wählen" -#: compensation/forms/modalForms.py:312 +#: compensation/forms/modalForms.py:315 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:31 #: compensation/templates/compensation/detail/eco_account/includes/deadlines.html:31 #: ema/templates/ema/detail/includes/deadlines.html:31 @@ -543,27 +543,27 @@ msgstr "Fristart wählen" msgid "Date" msgstr "Datum" -#: compensation/forms/modalForms.py:315 +#: compensation/forms/modalForms.py:318 msgid "Select date" msgstr "Datum wählen" -#: compensation/forms/modalForms.py:342 +#: compensation/forms/modalForms.py:345 msgid "New deadline" msgstr "Neue Frist" -#: compensation/forms/modalForms.py:343 +#: compensation/forms/modalForms.py:346 msgid "Insert data for the new deadline" msgstr "Geben Sie die Daten der neuen Frist ein" -#: compensation/forms/modalForms.py:360 +#: compensation/forms/modalForms.py:363 msgid "Action Type" msgstr "Maßnahmentyp" -#: compensation/forms/modalForms.py:363 +#: compensation/forms/modalForms.py:366 msgid "Select the action type" msgstr "Maßnahmentyp wählen" -#: compensation/forms/modalForms.py:372 +#: compensation/forms/modalForms.py:375 #: compensation/templates/compensation/detail/compensation/includes/actions.html:40 #: compensation/templates/compensation/detail/compensation/includes/deadlines.html:39 #: compensation/templates/compensation/detail/compensation/includes/documents.html:36 @@ -589,31 +589,31 @@ msgstr "Maßnahmentyp wählen" msgid "Action" msgstr "Aktionen" -#: compensation/forms/modalForms.py:377 compensation/forms/modalForms.py:389 +#: compensation/forms/modalForms.py:380 compensation/forms/modalForms.py:392 msgid "Action Type detail" msgstr "Zusatzmerkmal" -#: compensation/forms/modalForms.py:380 +#: compensation/forms/modalForms.py:383 msgid "Select the action type detail" msgstr "Zusatzmerkmal wählen" -#: compensation/forms/modalForms.py:394 +#: compensation/forms/modalForms.py:397 msgid "Unit" msgstr "Einheit" -#: compensation/forms/modalForms.py:397 +#: compensation/forms/modalForms.py:400 msgid "Select the unit" msgstr "Einheit wählen" -#: compensation/forms/modalForms.py:409 +#: compensation/forms/modalForms.py:412 msgid "Insert the amount" msgstr "Menge eingeben" -#: compensation/forms/modalForms.py:434 +#: compensation/forms/modalForms.py:437 msgid "New action" msgstr "Neue Maßnahme" -#: compensation/forms/modalForms.py:435 +#: compensation/forms/modalForms.py:438 msgid "Insert data for the new action" msgstr "Geben Sie die Daten der neuen Maßnahme ein" @@ -992,7 +992,7 @@ msgid "Recorded on" msgstr "Verzeichnet am" #: compensation/templates/compensation/detail/eco_account/includes/deductions.html:65 -#: intervention/templates/intervention/detail/includes/deductions.html:60 +#: intervention/templates/intervention/detail/includes/deductions.html:63 msgid "Remove Deduction" msgstr "Abbuchung entfernen" @@ -1084,63 +1084,63 @@ msgstr "Kompensationen - Übersicht" msgid "Compensation {} edited" msgstr "Kompensation {} bearbeitet" -#: compensation/views/compensation.py:159 compensation/views/eco_account.py:161 +#: compensation/views/compensation.py:159 compensation/views/eco_account.py:163 #: ema/views.py:230 intervention/views.py:305 msgid "Edit {}" msgstr "Bearbeite {}" -#: compensation/views/compensation.py:238 compensation/views/eco_account.py:317 +#: compensation/views/compensation.py:238 compensation/views/eco_account.py:347 #: ema/views.py:191 intervention/views.py:483 msgid "Log" msgstr "Log" -#: compensation/views/compensation.py:487 compensation/views/eco_account.py:590 -#: ema/views.py:477 intervention/views.py:601 +#: compensation/views/compensation.py:487 compensation/views/eco_account.py:620 +#: ema/views.py:477 intervention/views.py:629 msgid "Report {}" msgstr "Bericht {}" -#: compensation/views/eco_account.py:60 +#: compensation/views/eco_account.py:62 msgid "Eco-account - Overview" msgstr "Ökokonten - Übersicht" -#: compensation/views/eco_account.py:93 +#: compensation/views/eco_account.py:95 msgid "Eco-Account {} added" msgstr "Ökokonto {} hinzugefügt" -#: compensation/views/eco_account.py:151 +#: compensation/views/eco_account.py:153 msgid "Eco-Account {} edited" msgstr "Ökokonto {} bearbeitet" -#: compensation/views/eco_account.py:264 +#: compensation/views/eco_account.py:266 msgid "Eco-account removed" msgstr "Ökokonto entfernt" -#: compensation/views/eco_account.py:338 ema/views.py:272 -#: intervention/views.py:554 +#: compensation/views/eco_account.py:368 ema/views.py:272 +#: intervention/views.py:582 msgid "{} unrecorded" msgstr "{} entzeichnet" -#: compensation/views/eco_account.py:338 ema/views.py:272 -#: intervention/views.py:554 +#: compensation/views/eco_account.py:368 ema/views.py:272 +#: intervention/views.py:582 msgid "{} recorded" msgstr "{} verzeichnet" -#: compensation/views/eco_account.py:663 ema/views.py:543 +#: compensation/views/eco_account.py:693 ema/views.py:543 #: intervention/views.py:380 msgid "{} has already been shared with you" msgstr "{} wurde bereits für Sie freigegeben" -#: compensation/views/eco_account.py:668 ema/views.py:548 +#: compensation/views/eco_account.py:698 ema/views.py:548 #: intervention/views.py:385 msgid "{} has been shared with you" msgstr "{} ist nun für Sie freigegeben" -#: compensation/views/eco_account.py:675 ema/views.py:555 +#: compensation/views/eco_account.py:705 ema/views.py:555 #: intervention/views.py:392 msgid "Share link invalid" msgstr "Freigabelink ungültig" -#: compensation/views/eco_account.py:698 ema/views.py:578 +#: compensation/views/eco_account.py:728 ema/views.py:578 #: intervention/views.py:415 msgid "Share settings updated" msgstr "Freigabe Einstellungen aktualisiert" @@ -1337,7 +1337,7 @@ msgstr "Neue Abbuchung" msgid "Enter the information for a new deduction from a chosen eco-account" msgstr "Geben Sie die Informationen für eine neue Abbuchung ein." -#: intervention/forms/modalForms.py:366 +#: intervention/forms/modalForms.py:381 msgid "" "Eco-account {} is not recorded yet. You can only deduct from recorded " "accounts." @@ -1345,7 +1345,7 @@ msgstr "" "Ökokonto {} ist noch nicht verzeichnet. Abbuchungen können nur von " "verzeichneten Ökokonten erfolgen." -#: intervention/forms/modalForms.py:379 +#: intervention/forms/modalForms.py:391 msgid "" "The account {} has not enough surface for a deduction of {} m². There are " "only {} m² left" @@ -1373,6 +1373,10 @@ msgstr "Ökokonto gelöscht! Abbuchung ungültig!" msgid "Eco-account not recorded! Deduction invalid!" msgstr "Ökokonto nicht verzeichnet! Abbuchung ungültig!" +#: intervention/templates/intervention/detail/includes/deductions.html:60 +msgid "Edit Deduction" +msgstr "Abbuchung bearbeiten" + #: intervention/templates/intervention/detail/includes/payments.html:8 #: intervention/templates/intervention/report/report.html:73 msgid "Payments" @@ -1464,7 +1468,7 @@ msgstr "{} entfernt" msgid "Check performed" msgstr "Prüfung durchgeführt" -#: intervention/views.py:559 +#: intervention/views.py:587 msgid "There are errors on this intervention:" msgstr "Es liegen Fehler in diesem Eingriff vor:" @@ -1824,74 +1828,94 @@ msgid "State removed" msgstr "Zustand gelöscht" #: konova/utils/message_templates.py:33 +msgid "State edited" +msgstr "Zustand bearbeitet" + +#: konova/utils/message_templates.py:34 msgid "State added" msgstr "Zustand hinzugefügt" -#: konova/utils/message_templates.py:36 +#: konova/utils/message_templates.py:37 msgid "Action added" msgstr "Maßnahme hinzugefügt" -#: konova/utils/message_templates.py:37 +#: konova/utils/message_templates.py:38 +msgid "Action edited" +msgstr "Maßnahme bearbeitet" + +#: konova/utils/message_templates.py:39 msgid "Action removed" msgstr "Maßnahme entfernt" -#: konova/utils/message_templates.py:40 +#: konova/utils/message_templates.py:42 msgid "Deduction added" msgstr "Abbuchung hinzugefügt" -#: konova/utils/message_templates.py:41 +#: konova/utils/message_templates.py:43 +msgid "Deduction edited" +msgstr "Abbuchung bearbeitet" + +#: konova/utils/message_templates.py:44 msgid "Deduction removed" msgstr "Abbuchung entfernt" -#: konova/utils/message_templates.py:44 +#: konova/utils/message_templates.py:47 msgid "Deadline added" msgstr "Frist/Termin hinzugefügt" -#: konova/utils/message_templates.py:45 +#: konova/utils/message_templates.py:48 +msgid "Deadline edited" +msgstr "Frist/Termin bearbeitet" + +#: konova/utils/message_templates.py:49 msgid "Deadline removed" msgstr "Frist/Termin gelöscht" -#: konova/utils/message_templates.py:48 +#: konova/utils/message_templates.py:52 msgid "Payment added" msgstr "Zahlung hinzugefügt" -#: konova/utils/message_templates.py:49 +#: konova/utils/message_templates.py:53 msgid "Payment edited" msgstr "Zahlung bearbeitet" -#: konova/utils/message_templates.py:50 +#: konova/utils/message_templates.py:54 msgid "Payment removed" msgstr "Zahlung gelöscht" -#: konova/utils/message_templates.py:53 +#: konova/utils/message_templates.py:57 msgid "Revocation added" msgstr "Widerspruch hinzugefügt" -#: konova/utils/message_templates.py:54 +#: konova/utils/message_templates.py:58 +msgid "Revocation edited" +msgstr "Widerspruch bearbeitet" + +#: konova/utils/message_templates.py:59 msgid "Revocation removed" msgstr "Widerspruch entfernt" -#: konova/utils/message_templates.py:57 +#: konova/utils/message_templates.py:62 msgid "Document '{}' deleted" msgstr "Dokument '{}' gelöscht" -#: konova/utils/message_templates.py:58 +#: konova/utils/message_templates.py:63 msgid "Document added" msgstr "Dokument hinzugefügt" -#: konova/utils/message_templates.py:61 +#: konova/utils/message_templates.py:66 msgid "Edited general data" msgstr "Allgemeine Daten bearbeitet" -#: konova/utils/message_templates.py:62 +#: konova/utils/message_templates.py:67 msgid "Added deadline" msgstr "Frist/Termin hinzugefügt" -#: konova/utils/message_templates.py:65 +#: konova/utils/message_templates.py:70 msgid "Geometry conflict detected with {}" msgstr "Geometriekonflikt mit folgenden Einträgen erkannt: {}" -#: konova/utils/message_templates.py:68 +#: konova/utils/message_templates.py:73 msgid "This intervention has {} revocations" msgstr "Dem Eingriff liegen {} Widersprüche vor"