diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..94cf1be69e8cbd2701c78623485f8f508ac64c9c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "apps/scripts"] + path = apps/scripts + url = git@gitlab.crans.org:bde/nk20-scripts.git diff --git a/apps/member/models.py b/apps/member/models.py index 50b0bea1651e6d1947f96dd784a19e09f4f3729a..5cdc4c77e6d151f34b94023f7803b5ee3955af5a 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -46,6 +46,7 @@ class Profile(models.Model): class Meta: verbose_name = _('user profile') verbose_name_plural = _('user profile') + indexes = [ models.Index(fields=['user']) ] def get_absolute_url(self): return reverse('user_detail', args=(self.pk,)) @@ -152,6 +153,7 @@ class Membership(models.Model): class Meta: verbose_name = _('membership') verbose_name_plural = _('memberships') + indexes = [ models.Index(fields=['user']) ] # @receiver(post_save, sender=settings.AUTH_USER_MODEL) # def save_user_profile(instance, created, **_kwargs): diff --git a/apps/note/forms.py b/apps/note/forms.py index 208044126ba83bb011b9779654cbf2204ba1ab49..2e8e44561d532c2f539b631ff1886d97a3cf6f58 100644 --- a/apps/note/forms.py +++ b/apps/note/forms.py @@ -6,7 +6,7 @@ from django import forms from django.utils.translation import gettext_lazy as _ from .models import Alias -from .models import Transaction, TransactionTemplate, TemplateTransaction +from .models import Transaction, TransactionTemplate class AliasForm(forms.ModelForm): @@ -99,33 +99,3 @@ class TransactionForm(forms.ModelForm): }, ), } - - -class ConsoForm(forms.ModelForm): - def save(self, commit=True): - button: TransactionTemplate = TransactionTemplate.objects.filter( - name=self.data['button']).get() - self.instance.destination = button.destination - self.instance.amount = button.amount - self.instance.reason = '{} ({})'.format(button.name, button.category) - self.instance.template = button - self.instance.category = button.category - super().save(commit) - - class Meta: - model = TemplateTransaction - fields = ('source',) - - # Le champ d'utilisateur est remplacé par un champ d'auto-complétion. - # Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion - # et récupère les aliases de note valides - widgets = { - 'source': - autocomplete.ModelSelect2( - url='note:note_autocomplete', - attrs={ - 'data-placeholder': 'Note ...', - 'data-minimum-input-length': 1, - }, - ), - } diff --git a/apps/note/models/notes.py b/apps/note/models/notes.py index c53c014040f9349ae09ee953cb932fb23c4957e4..b6b00aa8ec91fedda1449a20948625f4ea8256e1 100644 --- a/apps/note/models/notes.py +++ b/apps/note/models/notes.py @@ -209,6 +209,10 @@ class Alias(models.Model): class Meta: verbose_name = _("alias") verbose_name_plural = _("aliases") + indexes = [ + models.Index(fields=['name']), + models.Index(fields=['normalized_name']), + ] def __str__(self): return self.name diff --git a/apps/note/models/transactions.py b/apps/note/models/transactions.py index 3bb7ca76386845d439d04b58c2d76117c282b4d6..809e7c44459e5a47655d6fcd48dda6b19cdad6a1 100644 --- a/apps/note/models/transactions.py +++ b/apps/note/models/transactions.py @@ -119,6 +119,11 @@ class Transaction(PolymorphicModel): class Meta: verbose_name = _("transaction") verbose_name_plural = _("transactions") + indexes = [ + models.Index(fields=['created_at']), + models.Index(fields=['source']), + models.Index(fields=['destination']), + ] def save(self, *args, **kwargs): """ diff --git a/apps/note/views.py b/apps/note/views.py index 7064370ae5881ddb2c37a58d8c6211ed5c226f94..16e2e39b9ea04b21894dc30bd8431902cd38a459 100644 --- a/apps/note/views.py +++ b/apps/note/views.py @@ -7,9 +7,11 @@ from django.db.models import Q from django.urls import reverse from django.utils.translation import gettext_lazy as _ from django.views.generic import CreateView, ListView, UpdateView +from django_tables2 import SingleTableView -from .forms import TransactionForm, TransactionTemplateForm, ConsoForm -from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction +from .forms import TransactionForm, TransactionTemplateForm +from .models import Transaction, TransactionTemplate, Alias +from .tables import HistoryTable class TransactionCreate(LoginRequiredMixin, CreateView): @@ -121,13 +123,16 @@ class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView): form_class = TransactionTemplateForm -class ConsoView(LoginRequiredMixin, CreateView): +class ConsoView(LoginRequiredMixin, SingleTableView): """ Consume """ - model = TemplateTransaction + model = Transaction template_name = "note/conso_form.html" - form_class = ConsoForm + + # Transaction history table + table_class = HistoryTable + table_pagination = {"per_page": 10} def get_context_data(self, **kwargs): """ @@ -142,9 +147,3 @@ class ConsoView(LoginRequiredMixin, CreateView): context['no_cache'] = True return context - - def get_success_url(self): - """ - When clicking a button, reload the same page - """ - return reverse('note:consos') diff --git a/apps/scripts b/apps/scripts new file mode 160000 index 0000000000000000000000000000000000000000..123466cfa914422422cd372197e64adf65ef05f7 --- /dev/null +++ b/apps/scripts @@ -0,0 +1 @@ +Subproject commit 123466cfa914422422cd372197e64adf65ef05f7 diff --git a/templates/note/conso_form.html b/templates/note/conso_form.html index 10b06589cfd4434aa6bc79100307901d017b1547..2c2066e8052046abf4013586a084102c501f23c9 100644 --- a/templates/note/conso_form.html +++ b/templates/note/conso_form.html @@ -1,80 +1,134 @@ {% extends "base.html" %} -{% load i18n static pretty_money %} +{% load i18n static pretty_money django_tables2 %} {# Remove page title #} {% block contenttitle %}{% endblock %} {% block content %} - {# Regroup buttons under categories #} - {% regroup transaction_templates by category as categories %} - - <form method="post" onsubmit="window.onbeforeunload=null"> - {% csrf_token %} - - <div class="row"> - <div class="col-sm-5 mb-4"> - {% if form.non_field_errors %} - <p class="errornote"> - {% for error in form.non_field_errors %} - {{ error }} - {% endfor %} - </p> - {% endif %} - {% for field in form %} - <div class="form-row{% if field.errors %} errors{% endif %}"> - {{ field.errors }} - <div> - {{ field.label_tag }} - {% if field.is_readonly %} - <div class="readonly">{{ field.contents }}</div> - {% else %} - {{ field }} - {% endif %} - {% if field.field.help_text %} - <div class="help">{{ field.field.help_text|safe }}</div> - {% endif %} + <div class="row mt-4"> + <div class="col-sm-5 col-md-4"> + <div class="row"> + {# User details column #} + <div class="col-xl-5"> + <div class="card border-success shadow mb-4"> + <img src="https://perso.crans.org/erdnaxe/site-crans/img/logo.svg" + alt="" class="img-fluid rounded mx-auto d-block"> + <div class="card-body text-center"> + Paquito (aka. PAC) : -230 € </div> </div> - {% endfor %} - </div> + </div> - <div class="col-sm-7"> - <div class="card text-center shadow"> - {# Tabs for button categories #} - <div class="card-header"> - <ul class="nav nav-tabs nav-fill card-header-tabs"> - {% for category in categories %} - <li class="nav-item"> - <a class="nav-link" data-toggle="tab" href="#{{ category.grouper|slugify }}"> - {{ category.grouper }} - </a> - </li> - {% endfor %} + {# User selection column #} + <div class="col-xl-7"> + <div class="card border-success shadow mb-4"> + <div class="card-header"> + <p class="card-text font-weight-bold"> + Sélection des émitteurs + </p> + </div> + <ul class="list-group list-group-flush"> + <li class="list-group-item py-1 d-flex justify-content-between align-items-center"> + Cras justo odio + <span class="badge badge-dark badge-pill">14</span> + </li> + <li class="list-group-item py-1 d-flex justify-content-between align-items-center"> + Dapibus ac facilisis in + <span class="badge badge-dark badge-pill">1</span> + </li> </ul> + <div class="card-body"> + TODO: reimplement select2 here in JS + </div> </div> + </div> + </div> + </div> + + {# Buttons column #} + <div class="col-sm-7 col-md-8"> + {# Show last used buttons #} + <div class="card shadow mb-4"> + <div class="card-body text-nowrap" style="overflow:auto hidden"> + <p class="card-text text-muted font-weight-light font-italic"> + Les boutons les plus utilisés s'afficheront ici. + </p> + </div> + </div> + + {# Regroup buttons under categories #} + {% regroup transaction_templates by template_type as template_types %} - {# Tabs content #} - <div class="card-body"> - <div class="tab-content"> - {% for category in categories %} - <div class="tab-pane" id="{{ category.grouper|slugify }}"> - <div class="d-inline-flex flex-wrap justify-content-center"> - {% for button in category.list %} - <button class="btn btn-outline-dark rounded-0 flex-fill" - name="button" value="{{ button.name }}"> - {{ button.name }} ({{ button.amount | pretty_money }}) - </button> - {% endfor %} - </div> + <div class="card border-primary text-center shadow mb-4"> + {# Tabs for button categories #} + <div class="card-header"> + <ul class="nav nav-tabs nav-fill card-header-tabs"> + {% for template_type in template_types %} + <li class="nav-item"> + <a class="nav-link font-weight-bold" data-toggle="tab" href="#{{ template_type.grouper|slugify }}"> + {{ template_type.grouper }} + </a> + </li> + {% endfor %} + </ul> + </div> + + {# Tabs content #} + <div class="card-body"> + <div class="tab-content"> + {% for template_type in template_types %} + <div class="tab-pane" id="{{ template_type.grouper|slugify }}"> + <div class="d-inline-flex flex-wrap justify-content-center"> + {% for button in template_type.list %} + <button class="btn btn-outline-dark rounded-0 flex-fill" + name="button" value="{{ button.name }}"> + {{ button.name }} ({{ button.amount | pretty_money }}) + </button> + {% endfor %} </div> - {% endfor %} - </div> + </div> + {% endfor %} + </div> + </div> + + {# Mode switch #} + <div class="card-footer border-primary"> + <a class="btn btn-sm btn-secondary float-left" href="{% url 'note:template_list' %}"> + <i class="fa fa-edit"></i> Éditer + </a> + <div class="btn-group btn-group-toggle float-right" data-toggle="buttons"> + <label class="btn btn-sm btn-outline-primary active"> + <input type="radio" name="options" id="option1" checked> + Consomations simples + </label> + <label class="btn btn-sm btn-outline-primary"> + <input type="radio" name="options" id="option2"> + Consomations doubles + </label> </div> </div> </div> </div> - </form> + </div> + + <div class="card shadow mb-4"> + <div class="card-header"> + <p class="card-text font-weight-bold"> + Historique des transactions récentes + </p> + </div> + {% render_table table %} + </div> +{% endblock %} + +{% block extracss %} + <style> + .select2-container{ + max-width: 100%; + min-width: 100%; + } + </style> {% endblock %} {% block extrajavascript %}