From 040bb27528c222b517985fd3c51aaf627183004f Mon Sep 17 00:00:00 2001
From: Yohann D'ANELLO <yohann.danello@gmail.com>
Date: Sat, 14 Mar 2020 15:13:58 +0100
Subject: [PATCH] Credit/debit support

---
 apps/note/api/serializers.py         |  14 +-
 apps/note/models/transactions.py     |  22 ++-
 apps/note/tables.py                  |   4 -
 apps/note/views.py                   |   5 +-
 templates/note/transaction_form.html | 208 +++++++++++++++++++--------
 5 files changed, 186 insertions(+), 67 deletions(-)

diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py
index 2d85cc41..85f500ed 100644
--- a/apps/note/api/serializers.py
+++ b/apps/note/api/serializers.py
@@ -6,7 +6,7 @@ from rest_polymorphic.serializers import PolymorphicSerializer
 
 from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
 from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \
-    TemplateTransaction
+    TemplateTransaction, SpecialTransaction
 
 
 class NoteSerializer(serializers.ModelSerializer):
@@ -144,9 +144,21 @@ class MembershipTransactionSerializer(serializers.ModelSerializer):
         fields = '__all__'
 
 
+class SpecialTransactionSerializer(serializers.ModelSerializer):
+    """
+    REST API Serializer for Special transactions.
+    The djangorestframework plugin will analyse the model `SpecialTransaction` and parse all fields in the API.
+    """
+
+    class Meta:
+        model = SpecialTransaction
+        fields = '__all__'
+
+
 class TransactionPolymorphicSerializer(PolymorphicSerializer):
     model_serializer_mapping = {
         Transaction: TransactionSerializer,
         TemplateTransaction: TemplateTransactionSerializer,
         MembershipTransaction: MembershipTransactionSerializer,
+        SpecialTransaction: SpecialTransactionSerializer,
     }
diff --git a/apps/note/models/transactions.py b/apps/note/models/transactions.py
index 2fdca76e..8997411a 100644
--- a/apps/note/models/transactions.py
+++ b/apps/note/models/transactions.py
@@ -165,7 +165,6 @@ class Transaction(PolymorphicModel):
 class TemplateTransaction(Transaction):
     """
     Special type of :model:`note.Transaction` associated to a :model:`note.TransactionTemplate`.
-
     """
 
     template = models.ForeignKey(
@@ -183,6 +182,27 @@ class TemplateTransaction(Transaction):
         return _('template')
 
 
+class SpecialTransaction(Transaction):
+    """
+    Special type of :model:`note.Transaction` associated to transactions with special notes
+    """
+
+    last_name = models.CharField(
+        max_length=255,
+        verbose_name=_("name"),
+    )
+
+    first_name = models.CharField(
+        max_length=255,
+        verbose_name=_("first_name"),
+    )
+
+    bank = models.CharField(
+        max_length=255,
+        verbose_name=_("bank")
+    )
+
+
 class MembershipTransaction(Transaction):
     """
     Special type of :model:`note.Transaction` associated to a :model:`member.Membership`.
diff --git a/apps/note/tables.py b/apps/note/tables.py
index ceb710df..4db13871 100644
--- a/apps/note/tables.py
+++ b/apps/note/tables.py
@@ -37,20 +37,16 @@ class HistoryTable(tables.Table):
             .order_by(('-' if is_descending else '') + 'total')
         return queryset, True
 
-
     def render_amount(self, value):
         return pretty_money(value)
 
-
     def render_total(self, value):
         return pretty_money(value)
 
-
     # Django-tables escape strings. That's a wrong thing.
     def render_reason(self, value):
         return html.unescape(value)
 
-
     def render_valid(self, value):
         return "✔" if value else "✖"
 
diff --git a/apps/note/views.py b/apps/note/views.py
index 74da6497..07fd03f8 100644
--- a/apps/note/views.py
+++ b/apps/note/views.py
@@ -10,7 +10,8 @@ from django.views.generic import CreateView, ListView, UpdateView, TemplateView
 from django_tables2 import SingleTableView
 
 from .forms import TransactionTemplateForm
-from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction
+from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction, NoteSpecial
+from .models.transactions import SpecialTransaction
 from .tables import HistoryTable
 
 
@@ -30,6 +31,8 @@ class TransactionCreate(LoginRequiredMixin, TemplateView):
         context['title'] = _('Transfer money from your account '
                              'to one or others')
         context['polymorphic_ctype'] = ContentType.objects.get_for_model(Transaction).pk
+        context['special_polymorphic_ctype'] = ContentType.objects.get_for_model(SpecialTransaction).pk
+        context['special_types'] = NoteSpecial.objects.order_by("special_type").all()
 
         return context
 
diff --git a/templates/note/transaction_form.html b/templates/note/transaction_form.html
index dc83c171..0543f2d6 100644
--- a/templates/note/transaction_form.html
+++ b/templates/note/transaction_form.html
@@ -9,7 +9,24 @@ SPDX-License-Identifier: GPL-2.0-or-later
 
     <div class="row">
         <div class="col-xl-12">
-            <button id="switch_mode" class="form-control btn btn-secondary">Passer en mode transfert</button>
+            <div class="btn-group btn-group-toggle" style="width: 100%" data-toggle="buttons">
+                <label class="btn btn-sm btn-outline-primary active">
+                    <input type="radio" name="transaction_type" id="type_gift" checked>
+                    Virement
+                </label>
+                <label class="btn btn-sm btn-outline-primary">
+                    <input type="radio" name="transaction_type" id="type_transfer">
+                    Transfert
+                </label>
+                <label class="btn btn-sm btn-outline-primary">
+                    <input type="radio" name="transaction_type" id="type_credit">
+                    Crédit
+                </label>
+                <label class="btn btn-sm btn-outline-primary">
+                    <input type="radio" name="transaction_type" id="type_debit">
+                    Retrait
+                </label>
+            </div>
         </div>
     </div>
 
@@ -31,6 +48,48 @@ SPDX-License-Identifier: GPL-2.0-or-later
             </div>
         </div>
 
+        <div class="col-md-6" id="external_div" style="display: none;">
+            <div class="card border-success shadow mb-4">
+                <div class="card-header">
+                    <p class="card-text font-weight-bold">
+                        Paiement externe
+                    </p>
+                </div>
+                <ul class="list-group list-group-flush" id="source_note_list">
+                </ul>
+                <div class="card-body">
+                    <div class="form-row">
+                        <div class="col-md-12">
+                            <label for="credit_type">Type de transfert :</label>
+                            <select id="credit_type" class="custom-select">
+                                {% for special_type in special_types %}
+                                    <option value="{{ special_type.id }}">{{ special_type.special_type }}</option>
+                                {% endfor %}
+                            </select>
+                        </div>
+                    </div>
+                    <div class="form-row">
+                        <div class="col-md-12">
+                            <label for="last_name">Nom :</label>
+                            <input type="text" id="last_name" class="form-control" />
+                        </div>
+                    </div>
+                    <div class="form-row">
+                        <div class="col-md-12">
+                            <label for="first_name">Prénom :</label>
+                            <input type="text" id="first_name" class="form-control" />
+                        </div>
+                    </div>
+                    <div class="form-row">
+                        <div class="col-md-12">
+                            <label for="bank">Banque :</label>
+                            <input type="text" id="bank" class="form-control" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
         <div class="col-md-12" id="dests_div">
             <div class="card border-info shadow mb-4">
                 <div class="card-header">
@@ -75,7 +134,23 @@ SPDX-License-Identifier: GPL-2.0-or-later
         sources_notes_display = [];
         dests = [];
         dests_notes_display = [];
-        transfer_mode = false;
+
+        function reset() {
+            sources_notes_display.length = 0;
+            sources.length = 0;
+            dests_notes_display.length = 0;
+            dests.length = 0;
+            $("#source_note_list").html("");
+            $("#dest_note_list").html("");
+            $("#source_alias_matched").html("");
+            $("#dest_alias_matched").html("");
+            $("#amount").val("");
+            $("#reason").val("");
+            $("#last_name").val("");
+            $("#first_name").val("");
+            $("#bank").val("");
+            refreshBalance();
+        }
 
         $(document).ready(function() {
            autoCompleteNote("source_note", "source_alias_matched", "source_note_list", sources, sources_notes_display,
@@ -84,22 +159,32 @@ SPDX-License-Identifier: GPL-2.0-or-later
                 "dest_alias", "dest_note");
         });
 
-        $("#switch_mode").click(function () {
-            transfer_mode ^= true;
-            if (transfer_mode) {
-                $("#switch_mode").text("Passer en mode virement");
-                $("#emitters_div").show();
-                $("#dests_div").attr('class', 'col-md-6');
-            }
-            else {
-                $("#switch_mode").text("Passer en mode transfert");
-                $("#emitters_div").hide();
-                $("#dests_div").attr('class', 'col-md-12');
-            }
+        $("#type_gift").click(function() {
+            $("#emitters_div").hide();
+            $("#external_div").hide();
+            $("#dests_div").attr('class', 'col-md-12');
+        });
+
+        $("#type_transfer").click(function() {
+            $("#emitters_div").show();
+            $("#external_div").hide();
+            $("#dests_div").attr('class', 'col-md-6');
+        });
+
+        $("#type_credit").click(function() {
+            $("#emitters_div").hide();
+            $("#external_div").show();
+            $("#dests_div").attr('class', 'col-md-6');
+        });
+
+        $("#type_debit").click(function() {
+            $("#emitters_div").hide();
+            $("#external_div").show();
+            $("#dests_div").attr('class', 'col-md-6');
         });
 
         $("#transfer").click(function() {
-            if (sources_notes_display.length === 0) {
+            if ($("#type_gift").is(':checked')) {
                 dests_notes_display.forEach(function (dest) {
                     $.post("/api/note/transaction/transaction/",
                         {
@@ -120,17 +205,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                                 + " vers la note " + dest[0] + " a été fait avec succès !</div>\n";
                             msgDiv.html(html);
 
-                            sources_notes_display.length = 0;
-                            sources.length = 0;
-                            dests_notes_display.length = 0;
-                            dests.length = 0;
-                            $("#source_note_list").html("");
-                            $("#dest_note_list").html("");
-                            $("#source_alias_matched").html("");
-                            $("#dest_alias_matched").html("");
-                            $("#amount").val("");
-                            $("#reason").val("");
-                            refreshBalance();
+                            reset();
                         }).fail(function (err) {
                         let msgDiv = $("#messages");
                         let html = msgDiv.html();
@@ -139,21 +214,11 @@ SPDX-License-Identifier: GPL-2.0-or-later
                             + " vers la note " + dest[0] + " a échoué : " + err.responseText + "</div>\n";
                         msgDiv.html(html);
 
-                        sources_notes_display.length = 0;
-                        sources.length = 0;
-                        dests_notes_display.length = 0;
-                        dests.length = 0;
-                        $("#source_note_list").html("");
-                        $("#dest_note_list").html("");
-                        $("#source_alias_matched").html("");
-                        $("#dest_alias_matched").html("");
-                        $("#amount").val("");
-                        $("#reason").val("");
-                        refreshBalance();
+                        reset();
                     });
                 });
             }
-            else {
+            else if ($("#type_transfer").is(':checked')) {
                 sources_notes_display.forEach(function (source) {
                     dests_notes_display.forEach(function (dest) {
                         $.post("/api/note/transaction/transaction/",
@@ -175,17 +240,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
                                     + " vers la note " + dest[0] + " a été fait avec succès !</div>\n";
                                 msgDiv.html(html);
 
-                                sources_notes_display.length = 0;
-                                sources.length = 0;
-                                dests_notes_display.length = 0;
-                                dests.length = 0;
-                                $("#source_note_list").html("");
-                                $("#dest_note_list").html("");
-                                $("#source_alias_matched").html("");
-                                $("#dest_alias_matched").html("");
-                                $("#amount").val("");
-                                $("#reason").val("");
-                                refreshBalance();
+                                reset();
                             }).fail(function (err) {
                                 let msgDiv = $("#messages");
                                 let html = msgDiv.html();
@@ -194,20 +249,53 @@ SPDX-License-Identifier: GPL-2.0-or-later
                                     + " vers la note " + dest[0] + " a échoué : " + err.responseText + "</div>\n";
                                 msgDiv.html(html);
 
-                                sources_notes_display.length = 0;
-                                sources.length = 0;
-                                dests_notes_display.length = 0;
-                                dests.length = 0;
-                                $("#source_note_list").html("");
-                                $("#dest_note_list").html("");
-                                $("#source_alias_matched").html("");
-                                $("#dest_alias_matched").html("");
-                                $("#amount").val("");
-                                $("#reason").val("");
-                                refreshBalance();
+                                reset();
                         });
                     });
                 });
+            } else if ($("#type_credit").is(':checked') || $("#type_debit").is(':checked')) {
+                let special_note = $("#credit_type").val();
+                let user_note = dests_notes_display[0][1];
+                let source, dest, reason;
+                if ($("#type_credit").is(':checked')) {
+                    source = special_note;
+                    dest = user_note;
+                    reason = $("#reason").val() + " (Crédit " + $("#credit_type option:selected").text().toLowerCase() + ")";
+                }
+                else {
+                    source = user_note;
+                    dest = special_note;
+                    reason = $("#reason").val() + " (Retrait " + $("#credit_type option:selected").text().toLowerCase() + ")";
+                }
+                $.post("/api/note/transaction/transaction/",
+                    {
+                        "csrfmiddlewaretoken": CSRF_TOKEN,
+                        "quantity": dest[3],
+                        "amount": $("#amount").val(),
+                        "reason": reason,
+                        "valid": true,
+                        "polymorphic_ctype": {{ special_polymorphic_ctype }},
+                        "resourcetype": "SpecialTransaction",
+                        "source": source,
+                        "destination": dest,
+                        "last_name": $("#last_name").val(),
+                        "first_name": $("#first_name").val(),
+                        "bank": $("#bank").val()
+                    }, function () {
+                        let msgDiv = $("#messages");
+                        let html = msgDiv.html();
+                        html += "<div class=\"alert alert-success\">Le crédit/retrait a bien été effectué !</div>\n";
+                        msgDiv.html(html);
+
+                        reset();
+                    }).fail(function (err) {
+                    let msgDiv = $("#messages");
+                    let html = msgDiv.html();
+                    html += "<div class=\"alert alert-danger\">Le crédit/transfert a échoué : " + err.responseText + "</div>\n";
+                    msgDiv.html(html);
+
+                    reset();
+                });
             }
         });
     </script>
-- 
GitLab