From bb2704323a8bae2ce68a12c154de4917e0c33ef9 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Thu, 13 Aug 2020 17:04:10 +0200 Subject: [PATCH] Spam click on invalidity button is no longer possible --- apps/logs/api/views.py | 4 ++-- apps/logs/signals.py | 20 ++++++++++++++------ apps/note/models/transactions.py | 23 ++++++++++++++++------- apps/permission/fixtures/initial.json | 2 +- note_kfet/settings/base.py | 1 + note_kfet/static/js/base.js | 9 ++++++++- 6 files changed, 42 insertions(+), 17 deletions(-) diff --git a/apps/logs/api/views.py b/apps/logs/api/views.py index b3b9b166..4160d609 100644 --- a/apps/logs/api/views.py +++ b/apps/logs/api/views.py @@ -19,5 +19,5 @@ class ChangelogViewSet(ReadOnlyProtectedModelViewSet): serializer_class = ChangelogSerializer filter_backends = [DjangoFilterBackend, OrderingFilter] filterset_fields = ['model', 'action', "instance_pk", 'user', 'ip', ] - ordering_fields = ['timestamp', ] - ordering = ['-timestamp', ] + ordering_fields = ['timestamp', 'id', ] + ordering = ['-id', ] diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 12d22155..bc8eec44 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -81,19 +81,27 @@ def save_object(sender, instance, **kwargs): if instance.last_login != previous.last_login: return - # On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles + fields = '__all__' + if previous: + # On ne garde que les champs modifiés + changed_fields = [] + for field in instance._meta.fields: + if getattr(instance, field.name) != getattr(previous, field.name): + changed_fields.append(field.name) + + if len(changed_fields) == 0: + # Pas de log s'il n'y a pas de modification + return + + # On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles avec uniquement les champs modifiés class CustomSerializer(ModelSerializer): class Meta: model = instance.__class__ - fields = '__all__' + fields = changed_fields previous_json = JSONRenderer().render(CustomSerializer(previous).data).decode("UTF-8") if previous else None instance_json = JSONRenderer().render(CustomSerializer(instance).data).decode("UTF-8") - if previous_json == instance_json: - # Pas de log s'il n'y a pas de modification - return - Changelog.objects.create(user=user, ip=ip, model=ContentType.objects.get_for_model(instance), diff --git a/apps/note/models/transactions.py b/apps/note/models/transactions.py index b3a541a1..f57cc9b5 100644 --- a/apps/note/models/transactions.py +++ b/apps/note/models/transactions.py @@ -167,26 +167,35 @@ class Transaction(PolymorphicModel): previous_source_balance = self.source.balance previous_dest_balance = self.destination.balance + source_balance = self.source.balance + dest_balance = self.destination.balance + created = self.pk is None to_transfer = self.amount * self.quantity if not created: # Revert old transaction old_transaction = Transaction.objects.get(pk=self.pk) + # Check that nothing important changed + for field_name in ["source_id", "destination_id", "quantity", "amount"]: + if getattr(self, field_name) != getattr(old_transaction, field_name): + raise ValidationError(_("You can't update the {field} on a Transaction. " + "Please invalidate it and create one other.").format(field=field_name)) + + if old_transaction.valid == self.valid: + # Don't change anything + return 0, 0 if old_transaction.valid: - self.source.balance += to_transfer - self.destination.balance -= to_transfer + source_balance += to_transfer + dest_balance -= to_transfer if self.valid: - self.source.balance -= to_transfer - self.destination.balance += to_transfer + source_balance -= to_transfer + dest_balance += to_transfer # When a transaction is declared valid, we ensure that the invalidity reason is null, if it was # previously invalid self.invalidity_reason = None - source_balance = self.source.balance - dest_balance = self.destination.balance - if source_balance > 2147483647 or source_balance < -2147483648\ or dest_balance > 2147483647 or dest_balance < -2147483648: raise ValidationError(_("The note balances must be between - 21 474 836.47 € and 21 474 836.47 €.")) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 80e38f6a..192b9391 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -2456,7 +2456,7 @@ 47, 49, 50, - 140 + 141 ] } }, diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py index 2e0ff8f5..2aa303ed 100644 --- a/note_kfet/settings/base.py +++ b/note_kfet/settings/base.py @@ -46,6 +46,7 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'django.forms', + 'django_filters', # API 'rest_framework', diff --git a/note_kfet/static/js/base.js b/note_kfet/static/js/base.js index 9db1d958..36e8ec84 100644 --- a/note_kfet/static/js/base.js +++ b/note_kfet/static/js/base.js @@ -349,8 +349,15 @@ function autoCompleteNote(field_id, note_list_id, notes, notes_display, alias_pr // When a validate button is clicked, we switch the validation status function de_validate(id, validated) { + let validate_obj = $("#validate_" + id); + + if (validate_obj.data("pending")) + // The button is already clicked + return; + let invalidity_reason = $("#invalidity_reason_" + id).val(); - $("#validate_" + id).html(""); + validate_obj.html(""); + validate_obj.data("pending", true); // Perform a PATCH request to the API in order to update the transaction // If the user has insufficient rights, an error message will appear