From 085cd90a7f9098cb3aca7858d648925476c7f61f Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Fri, 19 Jul 2019 15:00:44 +0200
Subject: [PATCH] Implement signals

---
 note/admin.py               | 10 ++++++
 note/apps.py                | 17 +++++++++++
 note/models/notes.py        | 61 ++++++++++++++++++++++++-------------
 note/models/transactions.py | 22 +++++++++++++
 note/signals.py             | 23 ++++++++++++++
 5 files changed, 111 insertions(+), 22 deletions(-)
 create mode 100644 note/signals.py

diff --git a/note/admin.py b/note/admin.py
index ac17be4e..c576e121 100644
--- a/note/admin.py
+++ b/note/admin.py
@@ -119,6 +119,16 @@ class TransactionAdmin(admin.ModelAdmin):
 
     poly_destination.short_description = _('destination')
 
+    def get_readonly_fields(self, request, obj=None):
+        """
+        Only valid can be edited after creation
+        Else the amount of money would not be transferred
+        """
+        if obj:  # user is editing an existing object
+            return 'created_at', 'source', 'destination', 'quantity',\
+                   'amount', 'transaction_type'
+        return []
+
 
 @admin.register(TransactionTemplate)
 class TransactionTemplateAdmin(admin.ModelAdmin):
diff --git a/note/apps.py b/note/apps.py
index 439140b3..c53f915a 100644
--- a/note/apps.py
+++ b/note/apps.py
@@ -3,9 +3,26 @@
 # SPDX-License-Identifier: GPL-3.0-or-later
 
 from django.apps import AppConfig
+from django.conf import settings
+from django.db.models.signals import post_save
 from django.utils.translation import gettext_lazy as _
 
+from . import signals
+
 
 class NoteConfig(AppConfig):
     name = 'note'
     verbose_name = _('note')
+
+    def ready(self):
+        """
+        Define app internal signals to interact with other apps
+        """
+        post_save.connect(
+            signals.save_user_note,
+            sender=settings.AUTH_USER_MODEL
+        )
+        post_save.connect(
+            signals.save_club_note,
+            sender='member.Club'
+        )
diff --git a/note/models/notes.py b/note/models/notes.py
index 1cb34f56..4417447e 100644
--- a/note/models/notes.py
+++ b/note/models/notes.py
@@ -4,8 +4,6 @@
 
 from django.conf import settings
 from django.db import models
-from django.db.models.signals import post_save
-from django.dispatch import receiver
 from django.utils.translation import gettext_lazy as _
 from polymorphic.models import PolymorphicModel
 
@@ -64,6 +62,19 @@ class NoteUser(Note):
     def __str__(self):
         return _("%(user)s's note") % {'user': str(self.user)}
 
+    def save(self, *args, **kwargs):
+        """
+        When saving, also create an alias
+        TODO: check availability of alias
+        TODO: remove old alias
+        """
+        created = self.pk is None
+        if created:
+            alias = Alias.objects.create(name=str(self.user), note=self)
+            alias.save()
+
+        super().save(*args, **kwargs)
+
 
 class NoteClub(Note):
     """
@@ -83,6 +94,19 @@ class NoteClub(Note):
     def __str__(self):
         return _("Note for %(club)s club") % {'club': str(self.club)}
 
+    def save(self, *args, **kwargs):
+        """
+        When saving, also create an alias
+        TODO: check availability of alias
+        TODO: remove old alias
+        """
+        created = self.pk is None
+        if created:
+            alias = Alias.objects.create(name=str(self.club), note=self)
+            alias.save()
+
+        super().save(*args, **kwargs)
+
 
 class NoteSpecial(Note):
     """
@@ -106,6 +130,19 @@ class NoteSpecial(Note):
     def __str__(self):
         return self.special_type
 
+    def save(self, *args, **kwargs):
+        """
+        When saving, also create an alias
+        TODO: check availability of alias
+        TODO: remove old alias
+        """
+        created = self.pk is None
+        if created:
+            alias = Alias.objects.create(name=str(self.club), note=self)
+            alias.save()
+
+        super().save(*args, **kwargs)
+
 
 class Alias(models.Model):
     """
@@ -127,23 +164,3 @@ class Alias(models.Model):
 
     def __str__(self):
         return self.name
-
-
-@receiver(post_save, sender=settings.AUTH_USER_MODEL)
-def save_user_note(instance, created, **_kwargs):
-    """
-    Hook to create and save a note when an user is updated
-    """
-    if created:
-        NoteUser.objects.create(user=instance)
-    instance.note.save()
-
-
-@receiver(post_save, sender='member.Club')
-def save_club_note(instance, created, **_kwargs):
-    """
-    Hook to create and save a note when a club is updated
-    """
-    if created:
-        NoteClub.objects.create(club=instance)
-    instance.note.save()
diff --git a/note/models/transactions.py b/note/models/transactions.py
index c2d0135d..506908bf 100644
--- a/note/models/transactions.py
+++ b/note/models/transactions.py
@@ -79,6 +79,28 @@ class Transaction(models.Model):
         verbose_name = _("transaction")
         verbose_name_plural = _("transactions")
 
+    def save(self, *args, **kwargs):
+        """
+        When saving, also transfer money between two notes
+        """
+        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)
+            if old_transaction.valid:
+                self.source.balance += to_transfer
+                self.destination.balance -= to_transfer
+
+        if self.valid:
+            self.source.balance -= to_transfer
+            self.destination.balance += to_transfer
+
+        # Save notes
+        self.source.save()
+        self.destination.save()
+        super().save(*args, **kwargs)
+
 
 class MembershipTransaction(Transaction):
     membership = models.OneToOneField(
diff --git a/note/signals.py b/note/signals.py
new file mode 100644
index 00000000..6e5d5c9e
--- /dev/null
+++ b/note/signals.py
@@ -0,0 +1,23 @@
+# -*- mode: python; coding: utf-8 -*-
+# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+
+def save_user_note(instance, created, **_kwargs):
+    """
+    Hook to create and save a note when an user is updated
+    """
+    if created:
+        from .models import NoteUser
+        NoteUser.objects.create(user=instance)
+    instance.note.save()
+
+
+def save_club_note(instance, created, **_kwargs):
+    """
+    Hook to create and save a note when a club is updated
+    """
+    if created:
+        from .models import NoteClub
+        NoteClub.objects.create(club=instance)
+    instance.note.save()
-- 
GitLab