#35 Sanity command

* refactors "toggling" of recorded/checked state
* introduces mark_as_edited() for RecordableObjectMixin
This commit is contained in:
mpeltriaux 2021-11-17 12:09:49 +01:00
parent 80e72c2146
commit bd36ab5b6f
11 changed files with 80 additions and 59 deletions

View File

@ -284,6 +284,8 @@ class EditCompensationForm(NewCompensationForm):
self.instance.save() self.instance.save()
self.instance.log.add(action) self.instance.log.add(action)
intervention.mark_as_edited(user)
return self.instance return self.instance

View File

@ -271,6 +271,11 @@ class Compensation(AbstractCompensation, CEFMixin, CoherenceMixin):
) )
return docs return docs
def add_new_action(self, form) -> CompensationAction:
super().add_new_action(form)
self.intervention.set_as_edited(form.user)
class CompensationDocument(AbstractDocument): class CompensationDocument(AbstractDocument):
""" """
Specializes document upload for revocations with certain path Specializes document upload for revocations with certain path

View File

@ -7,6 +7,7 @@ Created on: 16.11.21
""" """
import shutil import shutil
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
from django.db import models, transaction from django.db import models, transaction

View File

@ -274,7 +274,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
# Now mock the eco account as it would be recorded (with invalid data) # Now mock the eco account as it would be recorded (with invalid data)
# Make sure the deductible surface is high enough for the request # Make sure the deductible surface is high enough for the request
self.eco_account.toggle_recorded(self.superuser) self.eco_account.set_recorded(self.superuser)
self.eco_account.refresh_from_db() self.eco_account.refresh_from_db()
self.eco_account.deductable_surface = test_surface + 1.00 self.eco_account.deductable_surface = test_surface + 1.00
self.eco_account.save() self.eco_account.save()

View File

@ -15,7 +15,8 @@ from konova.decorators import *
from konova.forms import RemoveModalForm, SimpleGeomForm from konova.forms import RemoveModalForm, SimpleGeomForm
from konova.utils.documents import get_document, remove_document from konova.utils.documents import get_document, remove_document
from konova.utils.generators import generate_qr_code from konova.utils.generators import generate_qr_code
from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION from konova.utils.message_templates import FORM_INVALID, IDENTIFIER_REPLACED, DATA_UNSHARED_EXPLANATION, \
CHECKED_RECORDED_RESET
from konova.utils.user_checks import in_group from konova.utils.user_checks import in_group
@ -130,8 +131,15 @@ def edit_view(request: HttpRequest, id: str):
geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp) geom_form = SimpleGeomForm(request.POST or None, read_only=False, instance=comp)
if request.method == "POST": if request.method == "POST":
if data_form.is_valid() and geom_form.is_valid(): if data_form.is_valid() and geom_form.is_valid():
# Preserve state of intervention recorded/checked to determine whether the user must be informed or not
# about a change of the recorded/checked state
intervention_recorded = comp.intervention.recorded is not None
intervention_checked = comp.intervention.checked is not None
# The data form takes the geom form for processing, as well as the performing user # The data form takes the geom form for processing, as well as the performing user
comp = data_form.save(request.user, geom_form) comp = data_form.save(request.user, geom_form)
if intervention_recorded or intervention_checked:
messages.info(request, CHECKED_RECORDED_RESET)
messages.success(request, _("Compensation {} edited").format(comp.identifier)) messages.success(request, _("Compensation {} edited").format(comp.identifier))
return redirect("compensation:detail", id=comp.id) return redirect("compensation:detail", id=comp.id)
else: else:

View File

@ -7,6 +7,7 @@ Created on: 15.11.21
""" """
import shutil import shutil
from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.db.models import QuerySet from django.db.models import QuerySet

View File

@ -348,10 +348,7 @@ class EditInterventionForm(NewInterventionForm):
self.instance.save() self.instance.save()
# Uncheck and unrecord intervention due to changed data # Uncheck and unrecord intervention due to changed data
if self.instance.checked: self.instance.mark_as_edited(user)
self.instance.set_unchecked()
if self.instance.recorded:
self.instance.set_unrecorded(user)
return self.instance return self.instance

View File

@ -225,7 +225,7 @@ class CheckModalForm(BaseModalForm):
""" """
with transaction.atomic(): with transaction.atomic():
self.instance.toggle_checked(self.user) self.instance.set_checked(self.user)
class NewDeductionModalForm(BaseModalForm): class NewDeductionModalForm(BaseModalForm):

View File

@ -138,40 +138,33 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
) )
return revoc_docs, regular_docs return revoc_docs, regular_docs
def toggle_recorded(self, user: User): def set_unchecked(self):
""" Toggle the recorded state log_entry = super().set_unchecked()
self.add_log_entry_to_compensations(log_entry)
For interventions the recorded action needs to be added to their compensation objects as well def set_checked(self, user: User) -> UserActionLogEntry:
log_entry = super().set_checked(user)
self.add_log_entry_to_compensations(log_entry)
return log_entry
def set_unrecorded(self, user: User):
log_entry = super().set_unrecorded(user)
self.add_log_entry_to_compensations(log_entry)
def set_recorded(self, user: User) -> UserActionLogEntry:
log_entry = super().set_recorded(user)
self.add_log_entry_to_compensations(log_entry)
return log_entry
def add_log_entry_to_compensations(self, log_entry: UserActionLogEntry):
""" Adds the log entry to related compensations
Args: Args:
user (User): The performing user log_entry (UserActionLogEntry): The log entry
Returns: Returns:
""" """
log_entry = super().toggle_recorded(user)
# Add this action to the linked compensation logs as well
comps = self.compensations.all()
for comp in comps:
comp.log.add(log_entry)
def toggle_checked(self, user: User) -> UserActionLogEntry:
""" Toggle the checked state
For interventions the checked action needs to be added to their compensation objects as well
Args:
user (User): The performing user
Returns:
"""
log_entry = super().toggle_checked(user)
# Leave if the log_entry is None (means "unchecked")
if log_entry is None:
return
# Add this action to the linked compensation logs as well
comps = self.compensations.all() comps = self.compensations.all()
for comp in comps: for comp in comps:
comp.log.add(log_entry) comp.log.add(log_entry)
@ -202,7 +195,8 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
self.log.add(edited_action) self.log.add(edited_action)
self.modified = edited_action self.modified = edited_action
self.save() self.save()
return pay self.mark_as_edited(user)
return pay
def add_revocation(self, form): def add_revocation(self, form):
""" Adds a new revocation to the intervention """ Adds a new revocation to the intervention
@ -237,6 +231,7 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
file=form_data["file"], file=form_data["file"],
instance=revocation instance=revocation
) )
self.mark_as_edited(user)
return revocation return revocation
def add_deduction(self, form): def add_deduction(self, form):
@ -266,8 +261,23 @@ class Intervention(BaseObject, ShareableObjectMixin, RecordableObjectMixin, Chec
surface=form_data["surface"], surface=form_data["surface"],
created=user_action_create, created=user_action_create,
) )
self.mark_as_edited(user)
return deduction return deduction
def mark_as_edited(self, performing_user: User):
""" In case the object or a related object changed, internal processes need to be started, such as
unrecord and uncheck
Args:
performing_user (User): The user which performed the editing action
Returns:
"""
super().mark_as_edited(performing_user)
if self.checked:
self.set_unchecked()
class InterventionDocument(AbstractDocument): class InterventionDocument(AbstractDocument):
""" """

View File

@ -485,5 +485,8 @@ class RecordModalForm(BaseModalForm):
def save(self): def save(self):
with transaction.atomic(): with transaction.atomic():
if self.cleaned_data["confirm"]: if self.cleaned_data["confirm"]:
self.instance.toggle_recorded(self.user) if self.instance.recorded:
self.instance.set_unrecorded(self.user)
else:
self.instance.set_recorded(self.user)
return self.instance return self.instance

View File

@ -203,6 +203,8 @@ class RecordableObjectMixin(models.Model):
Returns: Returns:
""" """
if not self.recorded:
return None
action = UserActionLogEntry.get_unrecorded_action(user) action = UserActionLogEntry.get_unrecorded_action(user)
self.recorded = None self.recorded = None
self.save() self.save()
@ -218,26 +220,27 @@ class RecordableObjectMixin(models.Model):
Returns: Returns:
""" """
if self.recorded:
return None
action = UserActionLogEntry.get_recorded_action(user) action = UserActionLogEntry.get_recorded_action(user)
self.recorded = action self.recorded = action
self.save() self.save()
self.log.add(action) self.log.add(action)
return action return action
def toggle_recorded(self, user: User) -> UserActionLogEntry: def mark_as_edited(self, performing_user: User):
""" Un/Record intervention """ In case the object or a related object changed, internal processes need to be started, such as
unrecord and uncheck
Args: Args:
user (User): Performing user performing_user (User): The user which performed the editing action
Returns: Returns:
""" """
if not self.recorded:
ret_log_entry = self.set_recorded(user) if self.recorded:
else: self.set_unrecorded(performing_user)
ret_log_entry = self.set_unrecorded(user)
return ret_log_entry
class CheckableObjectMixin(models.Model): class CheckableObjectMixin(models.Model):
@ -262,6 +265,9 @@ class CheckableObjectMixin(models.Model):
Returns: Returns:
""" """
if not self.checked:
# Nothing to do
return
# Do not .delete() the checked attribute! Just set it to None, since a delete() would kill it out of the # Do not .delete() the checked attribute! Just set it to None, since a delete() would kill it out of the
# log history, which is not what we want! # log history, which is not what we want!
self.checked = None self.checked = None
@ -277,27 +283,15 @@ class CheckableObjectMixin(models.Model):
Returns: Returns:
""" """
if self.checked:
# Nothing to do
return
action = UserActionLogEntry.get_checked_action(user) action = UserActionLogEntry.get_checked_action(user)
self.checked = action self.checked = action
self.save() self.save()
self.log.add(action) self.log.add(action)
return action return action
def toggle_checked(self, user: User) -> UserActionLogEntry:
""" Un/Record intervention
Args:
user (User): Performing user
Returns:
"""
if not self.checked:
ret_log_entry = self.set_checked(user)
else:
ret_log_entry = self.set_unchecked()
return ret_log_entry
class ShareableObjectMixin(models.Model): class ShareableObjectMixin(models.Model):
# Users having access on this object # Users having access on this object