# NewCompensation EditCompensation view

* refactors new and edit compensation views from function to class based
* adds checked property to compensation to return parent-intervention's checked info
* fixes bug where compensation could be added to recorded intervention
* updates translations
This commit is contained in:
mpeltriaux 2025-10-19 14:06:14 +02:00
parent 278a951e92
commit 73178b3fd2
7 changed files with 118 additions and 166 deletions

View File

@ -168,6 +168,17 @@ class NewCompensationForm(AbstractCompensationForm,
comp.log.add(action) comp.log.add(action)
return comp, action return comp, action
def is_valid(self):
valid = super().is_valid()
intervention = self.cleaned_data.get("intervention", None)
valid &= not intervention.is_recorded
if not valid:
self.add_error(
"intervention",
_("This intervention is currently recorded. You cannot add further compensations as long as it is recorded.")
)
return valid
def save(self, user: User, geom_form: SimpleGeomForm): def save(self, user: User, geom_form: SimpleGeomForm):
with transaction.atomic(): with transaction.atomic():
comp, action = self.__create_comp(user) comp, action = self.__create_comp(user)

View File

@ -510,6 +510,12 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin, PikMixin):
return retval return retval
@property
def checked(self):
if self.intervention:
return self.intervention.checked
return None
class CompensationDocument(AbstractDocument): class CompensationDocument(AbstractDocument):
""" """

View File

@ -17,19 +17,20 @@ from compensation.views.compensation.action import NewCompensationActionView, Ed
RemoveCompensationActionView RemoveCompensationActionView
from compensation.views.compensation.state import NewCompensationStateView, EditCompensationStateView, \ from compensation.views.compensation.state import NewCompensationStateView, EditCompensationStateView, \
RemoveCompensationStateView RemoveCompensationStateView
from compensation.views.compensation.compensation import new_view, edit_view, \ from compensation.views.compensation.compensation import \
remove_view, CompensationIndexView, CompensationIdentifierGeneratorView, CompensationDetailView remove_view, CompensationIndexView, CompensationIdentifierGeneratorView, CompensationDetailView, \
NewCompensationFormView, EditCompensationFormView
from compensation.views.compensation.log import CompensationLogView from compensation.views.compensation.log import CompensationLogView
urlpatterns = [ urlpatterns = [
# Main compensation # Main compensation
path("", CompensationIndexView.as_view(), name="index"), path("", CompensationIndexView.as_view(), name="index"),
path('new/id', CompensationIdentifierGeneratorView.as_view(), name='new-id'), path('new/id', CompensationIdentifierGeneratorView.as_view(), name='new-id'),
path('new/<intervention_id>', new_view, name='new'), path('new/<intervention_id>', NewCompensationFormView.as_view(), name='new'),
path('new', new_view, name='new'), path('new', NewCompensationFormView.as_view(), name='new'),
path('<id>', CompensationDetailView.as_view(), name='detail'), path('<id>', CompensationDetailView.as_view(), name='detail'),
path('<id>/log', CompensationLogView.as_view(), name='log'), path('<id>/log', CompensationLogView.as_view(), name='log'),
path('<id>/edit', edit_view, name='edit'), path('<id>/edit', EditCompensationFormView.as_view(), name='edit'),
path('<id>/remove', remove_view, name='remove'), path('<id>/remove', remove_view, name='remove'),
path('<id>/state/new', NewCompensationStateView.as_view(), name='new-state'), path('<id>/state/new', NewCompensationStateView.as_view(), name='new-state'),

View File

@ -18,15 +18,12 @@ from compensation.forms.compensation import EditCompensationForm, NewCompensatio
from compensation.models import Compensation from compensation.models import Compensation
from compensation.tables.compensation import CompensationTable from compensation.tables.compensation import CompensationTable
from intervention.models import Intervention from intervention.models import Intervention
from konova.contexts import BaseContext
from konova.decorators import shared_access_required, default_group_required, login_required_modal from konova.decorators import shared_access_required, default_group_required, login_required_modal
from konova.forms import SimpleGeomForm
from konova.forms.modals import RemoveModalForm from konova.forms.modals import RemoveModalForm
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \ from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_CHECKED_PREVIOUSLY_TEMPLATE, \
RECORDED_BLOCKS_EDIT, CHECK_STATE_RESET, FORM_INVALID, PARAMS_INVALID, IDENTIFIER_REPLACED, \ RECORDED_BLOCKS_EDIT, PARAMS_INVALID
COMPENSATION_ADDED_TEMPLATE, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \
from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView BaseEditSpatialLocatedObjectFormView
from konova.views.detail import BaseDetailView from konova.views.detail import BaseDetailView
@ -44,74 +41,55 @@ class CompensationIndexView(LoginRequiredMixin, BaseIndexView):
return qs return qs
@login_required class NewCompensationFormView(BaseNewSpatialLocatedObjectFormView):
@default_group_required _FORM_CLS = NewCompensationForm
@shared_access_required(Intervention, "intervention_id") _MODEL_CLS = Compensation
def new_view(request: HttpRequest, intervention_id: str = None): _TEMPLATE = "compensation/form/view.html"
""" _TAB_TITLE = _("New Compensation")
Renders a view for a new compensation creation _REDIRECT_URL = "compensation:detail"
Args: def _user_has_shared_access(self, user, **kwargs):
request (HttpRequest): The incoming request # On a new compensation make sure the intervention (if call came directly through an intervention's detail
# view) is shared with the user
Returns: intervention_id = kwargs.get("intervention_id", None)
if not intervention_id:
""" return True
template = "compensation/form/view.html"
if intervention_id is not None:
try:
intervention = Intervention.objects.get(id=intervention_id)
except ObjectDoesNotExist:
messages.error(request, PARAMS_INVALID)
return redirect("home")
if intervention.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("intervention:detail", id=intervention_id)
data_form = NewCompensationForm(request.POST or None, intervention_id=intervention_id)
geom_form = SimpleGeomForm(request.POST or None, read_only=False)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
generated_identifier = data_form.cleaned_data.get("identifier", None)
comp = data_form.save(request.user, geom_form)
if generated_identifier != comp.identifier:
messages.info(
request,
IDENTIFIER_REPLACED.format(
generated_identifier,
comp.identifier
)
)
messages.success(request, COMPENSATION_ADDED_TEMPLATE.format(comp.identifier))
if geom_form.has_geometry_simplified():
messages.info(
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("compensation:detail", id=comp.id)
else: else:
messages.error(request, FORM_INVALID, extra_tags="danger",) intervention = get_object_or_404(Intervention, id=intervention_id)
else: return intervention.is_shared_with(user)
# For clarification: nothing in this case
pass def _user_has_permission(self, user):
context = { # User has to be an ets user
"form": data_form, return user.is_default_user()
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("New compensation"), def dispatch(self, request, *args, **kwargs):
} # Make sure there is an existing intervention based on the given id
context = BaseContext(request, context).context # Compensations can not exist without an intervention
return render(request, template, context) intervention_id = kwargs.get("intervention_id", None)
if intervention_id:
try:
intervention = Intervention.objects.get(id=intervention_id)
if intervention.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("intervention:detail", id=intervention_id)
except ObjectDoesNotExist:
messages.error(request, PARAMS_INVALID, extra_tags="danger")
return redirect("home")
return super().dispatch(request, *args, **kwargs)
class EditCompensationFormView(BaseEditSpatialLocatedObjectFormView):
_MODEL_CLS = Compensation
_FORM_CLS = EditCompensationForm
_TEMPLATE = "compensation/form/view.html"
_REDIRECT_URL = "compensation:detail"
def _user_has_permission(self, user):
# User has to be an ets user
return user.is_default_user()
class CompensationIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView): class CompensationIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView):
@ -119,71 +97,6 @@ class CompensationIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGene
_REDIRECT_URL = "compensation:index" _REDIRECT_URL = "compensation:index"
@login_required
@default_group_required
@shared_access_required(Compensation, "id")
def edit_view(request: HttpRequest, id: str):
"""
Renders a view for editing compensations
Args:
request (HttpRequest): The incoming request
Returns:
"""
template = "compensation/form/view.html"
# Get object from db
comp = get_object_or_404(Compensation, id=id)
if comp.is_recorded:
messages.info(
request,
RECORDED_BLOCKS_EDIT
)
return redirect("compensation:detail", id=id)
# Create forms, initialize with values from db/from POST request
data_form = EditCompensationForm(request.POST or None, instance=comp)
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid():
# Preserve state of intervention checked to determine whether the user must be informed or not
# about a change of the check state
intervention_is_checked = comp.intervention.checked is not None
# The data form takes the geom form for processing, as well as the performing user
comp = data_form.save(request.user, geom_form)
if intervention_is_checked:
messages.info(request, CHECK_STATE_RESET)
messages.success(request, _("Compensation {} edited").format(comp.identifier))
if geom_form.has_geometry_simplified():
messages.info(
request,
GEOMETRY_SIMPLIFIED
)
num_ignored_geometries = geom_form.get_num_geometries_ignored()
if num_ignored_geometries > 0:
messages.info(
request,
GEOMETRIES_IGNORED_TEMPLATE.format(num_ignored_geometries)
)
return redirect("compensation:detail", id=comp.id)
else:
messages.error(request, FORM_INVALID, extra_tags="danger",)
else:
# For clarification: nothing in this case
pass
context = {
"form": data_form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: _("Edit {}").format(comp.identifier),
}
context = BaseContext(request, context).context
return render(request, template, context)
class CompensationDetailView(BaseDetailView): class CompensationDetailView(BaseDetailView):
_MODEL_CLS = Compensation _MODEL_CLS = Compensation
_TEMPLATE = "compensation/detail/compensation/view.html" _TEMPLATE = "compensation/detail/compensation/view.html"

View File

@ -225,10 +225,19 @@ class BaseNewSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
context = self._get_additional_context_data() context = self._get_additional_context_data()
context = BaseContext(request, additional_context=context).context context = BaseContext(request, additional_context=context).context
context.update(
{
"form": form,
"geom_form": geom_form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
}
)
return render(request, self._TEMPLATE, context) return render(request, self._TEMPLATE, context)
class BaseEditSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView): class BaseEditSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
_TAB_TITLE = _("Edit {}")
def get(self, request: HttpRequest, id: str): def get(self, request: HttpRequest, id: str):
obj = get_object_or_404( obj = get_object_or_404(
self._MODEL_CLS, self._MODEL_CLS,
@ -252,7 +261,7 @@ class BaseEditSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
{ {
"form": form, "form": form,
"geom_form": geom_form, "geom_form": geom_form,
TAB_TITLE_IDENTIFIER: self._TAB_TITLE, TAB_TITLE_IDENTIFIER: self._TAB_TITLE.format(obj.identifier),
} }
) )
return render(request, self._TEMPLATE, context) return render(request, self._TEMPLATE, context)

Binary file not shown.

View File

@ -45,7 +45,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-10-19 12:52+0200\n" "POT-Creation-Date: 2025-10-19 13:56+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -448,11 +448,19 @@ msgid "Select the intervention for which this compensation compensates"
msgstr "Wählen Sie den Eingriff, für den diese Kompensation bestimmt ist" msgstr "Wählen Sie den Eingriff, für den diese Kompensation bestimmt ist"
#: compensation/forms/compensation.py:114 #: compensation/forms/compensation.py:114
#: compensation/views/compensation/compensation.py:111 #: compensation/views/compensation/compensation.py:161
msgid "New compensation" msgid "New compensation"
msgstr "Neue Kompensation" msgstr "Neue Kompensation"
#: compensation/forms/compensation.py:190 #: compensation/forms/compensation.py:179
msgid ""
"This intervention is currently recorded. You cannot add further "
"compensations as long as it is recorded."
msgstr ""
"Dieser Eingriff ist derzeit verzeichnet. "
"Sie können keine weiteren Kompensationen hinzufügen, so lange er verzeichnet ist."
#: compensation/forms/compensation.py:202
msgid "Edit compensation" msgid "Edit compensation"
msgstr "Bearbeite Kompensation" msgstr "Bearbeite Kompensation"
@ -1288,18 +1296,25 @@ msgstr ""
msgid "Responsible data" msgid "Responsible data"
msgstr "Daten zu den verantwortlichen Stellen" msgstr "Daten zu den verantwortlichen Stellen"
#: compensation/views/compensation/compensation.py:34 #: compensation/views/compensation/compensation.py:35
msgid "Compensations - Overview" msgid "Compensations - Overview"
msgstr "Kompensationen - Übersicht" msgstr "Kompensationen - Übersicht"
#: compensation/views/compensation/compensation.py:158 #: compensation/views/compensation/compensation.py:52
#, fuzzy
#| msgid "New compensation"
msgid "New Compensation"
msgstr "Neue Kompensation"
#: compensation/views/compensation/compensation.py:208
#: konova/utils/message_templates.py:40 #: konova/utils/message_templates.py:40
msgid "Compensation {} edited" msgid "Compensation {} edited"
msgstr "Kompensation {} bearbeitet" msgstr "Kompensation {} bearbeitet"
#: compensation/views/compensation/compensation.py:181 #: compensation/views/compensation/compensation.py:231
#: compensation/views/eco_account/eco_account.py:159 ema/views/ema.py:213 #: compensation/views/eco_account/eco_account.py:159 ema/views/ema.py:59
#: intervention/views/intervention.py:59 intervention/views/intervention.py:186 #: intervention/views/intervention.py:59 intervention/views/intervention.py:179
#: konova/views/base.py:239
msgid "Edit {}" msgid "Edit {}"
msgstr "Bearbeite {}" msgstr "Bearbeite {}"
@ -1319,8 +1334,7 @@ msgstr "Ökokonto {} bearbeitet"
msgid "Eco-account removed" msgid "Eco-account removed"
msgstr "Ökokonto entfernt" msgstr "Ökokonto entfernt"
#: ema/forms.py:42 ema/tests/unit/test_forms.py:27 ema/views/ema.py:92 #: ema/forms.py:42 ema/tests/unit/test_forms.py:27 ema/views/ema.py:42
#: ema/views/ema.py:102
msgid "New EMA" msgid "New EMA"
msgstr "Neue EMA hinzufügen" msgstr "Neue EMA hinzufügen"
@ -1348,19 +1362,11 @@ msgstr ""
msgid "Payment funded compensation" msgid "Payment funded compensation"
msgstr "Ersatzzahlungsmaßnahme" msgstr "Ersatzzahlungsmaßnahme"
#: ema/views/ema.py:31 #: ema/views/ema.py:26
msgid "EMAs - Overview" msgid "EMAs - Overview"
msgstr "EMAs - Übersicht" msgstr "EMAs - Übersicht"
#: ema/views/ema.py:70 #: ema/views/ema.py:138
msgid "EMA {} added"
msgstr "EMA {} hinzugefügt"
#: ema/views/ema.py:190
msgid "EMA {} edited"
msgstr "EMA {} bearbeitet"
#: ema/views/ema.py:237
msgid "EMA removed" msgid "EMA removed"
msgstr "EMA entfernt" msgstr "EMA entfernt"
@ -1664,11 +1670,11 @@ msgstr "Prüfung durchgeführt"
msgid "Interventions - Overview" msgid "Interventions - Overview"
msgstr "Eingriffe - Übersicht" msgstr "Eingriffe - Übersicht"
#: intervention/views/intervention.py:161 #: intervention/views/intervention.py:154
msgid "Intervention {} edited" msgid "Intervention {} edited"
msgstr "Eingriff {} bearbeitet" msgstr "Eingriff {} bearbeitet"
#: intervention/views/intervention.py:211 #: intervention/views/intervention.py:204
msgid "{} removed" msgid "{} removed"
msgstr "{} entfernt" msgstr "{} entfernt"
@ -2306,7 +2312,7 @@ msgstr ""
msgid "{} added" msgid "{} added"
msgstr "{} hinzugefügt" msgstr "{} hinzugefügt"
#: konova/views/base.py:274 #: konova/views/base.py:281
msgid "{} edited" msgid "{} edited"
msgstr "{} bearbeitet" msgstr "{} bearbeitet"
@ -3151,3 +3157,9 @@ msgstr "Sie sind kein Mitglied dieses Teams"
#: user/views/views.py:204 #: user/views/views.py:204
msgid "Left Team" msgid "Left Team"
msgstr "Team verlassen" msgstr "Team verlassen"
#~ msgid "EMA {} added"
#~ msgstr "EMA {} hinzugefügt"
#~ msgid "EMA {} edited"
#~ msgstr "EMA {} bearbeitet"