mirror of https://gitlab.crans.org/bde/nk20
Merge branch 'master' into 'fix-what-i-broke'
# Conflicts: # templates/base.html
This commit is contained in:
commit
46e472ed6d
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "apps/scripts"]
|
||||||
|
path = apps/scripts
|
||||||
|
url = git@gitlab.crans.org:bde/nk20-scripts.git
|
|
@ -46,6 +46,7 @@ class Profile(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('user profile')
|
verbose_name = _('user profile')
|
||||||
verbose_name_plural = _('user profile')
|
verbose_name_plural = _('user profile')
|
||||||
|
indexes = [ models.Index(fields=['user']) ]
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('user_detail', args=(self.pk,))
|
return reverse('user_detail', args=(self.pk,))
|
||||||
|
@ -152,6 +153,7 @@ class Membership(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('membership')
|
verbose_name = _('membership')
|
||||||
verbose_name_plural = _('memberships')
|
verbose_name_plural = _('memberships')
|
||||||
|
indexes = [ models.Index(fields=['user']) ]
|
||||||
|
|
||||||
# @receiver(post_save, sender=settings.AUTH_USER_MODEL)
|
# @receiver(post_save, sender=settings.AUTH_USER_MODEL)
|
||||||
# def save_user_profile(instance, created, **_kwargs):
|
# def save_user_profile(instance, created, **_kwargs):
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django import forms
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from .models import Alias
|
from .models import Alias
|
||||||
from .models import Transaction, TransactionTemplate, TemplateTransaction
|
from .models import Transaction, TransactionTemplate
|
||||||
|
|
||||||
|
|
||||||
class AliasForm(forms.ModelForm):
|
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,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
|
@ -209,6 +209,10 @@ class Alias(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("alias")
|
verbose_name = _("alias")
|
||||||
verbose_name_plural = _("aliases")
|
verbose_name_plural = _("aliases")
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=['name']),
|
||||||
|
models.Index(fields=['normalized_name']),
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -119,6 +119,11 @@ class Transaction(PolymorphicModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("transaction")
|
verbose_name = _("transaction")
|
||||||
verbose_name_plural = _("transactions")
|
verbose_name_plural = _("transactions")
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=['created_at']),
|
||||||
|
models.Index(fields=['source']),
|
||||||
|
models.Index(fields=['destination']),
|
||||||
|
]
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -7,9 +7,11 @@ from django.db.models import Q
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import CreateView, ListView, UpdateView
|
from django.views.generic import CreateView, ListView, UpdateView
|
||||||
|
from django_tables2 import SingleTableView
|
||||||
|
|
||||||
from .forms import TransactionForm, TransactionTemplateForm, ConsoForm
|
from .forms import TransactionForm, TransactionTemplateForm
|
||||||
from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction
|
from .models import Transaction, TransactionTemplate, Alias
|
||||||
|
from .tables import HistoryTable
|
||||||
|
|
||||||
|
|
||||||
class TransactionCreate(LoginRequiredMixin, CreateView):
|
class TransactionCreate(LoginRequiredMixin, CreateView):
|
||||||
|
@ -121,13 +123,16 @@ class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
form_class = TransactionTemplateForm
|
form_class = TransactionTemplateForm
|
||||||
|
|
||||||
|
|
||||||
class ConsoView(LoginRequiredMixin, CreateView):
|
class ConsoView(LoginRequiredMixin, SingleTableView):
|
||||||
"""
|
"""
|
||||||
Consume
|
Consume
|
||||||
"""
|
"""
|
||||||
model = TemplateTransaction
|
model = Transaction
|
||||||
template_name = "note/conso_form.html"
|
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):
|
def get_context_data(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -142,9 +147,3 @@ class ConsoView(LoginRequiredMixin, CreateView):
|
||||||
context['no_cache'] = True
|
context['no_cache'] = True
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
"""
|
|
||||||
When clicking a button, reload the same page
|
|
||||||
"""
|
|
||||||
return reverse('note:consos')
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 123466cfa914422422cd372197e64adf65ef05f7
|
|
@ -1,80 +1,134 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% load i18n static pretty_money %}
|
{% load i18n static pretty_money django_tables2 %}
|
||||||
|
|
||||||
{# Remove page title #}
|
{# Remove page title #}
|
||||||
{% block contenttitle %}{% endblock %}
|
{% block contenttitle %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{# Regroup buttons under categories #}
|
<div class="row mt-4">
|
||||||
{% regroup transaction_templates by category as categories %}
|
<div class="col-sm-5 col-md-4">
|
||||||
|
<div class="row">
|
||||||
<form method="post" onsubmit="window.onbeforeunload=null">
|
{# User details column #}
|
||||||
{% csrf_token %}
|
<div class="col-xl-5">
|
||||||
|
<div class="card border-success shadow mb-4">
|
||||||
<div class="row">
|
<img src="https://perso.crans.org/erdnaxe/site-crans/img/logo.svg"
|
||||||
<div class="col-sm-5 mb-4">
|
alt="" class="img-fluid rounded mx-auto d-block">
|
||||||
{% if form.non_field_errors %}
|
<div class="card-body text-center">
|
||||||
<p class="errornote">
|
Paquito (aka. PAC) : -230 €
|
||||||
{% 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-7">
|
{# User selection column #}
|
||||||
<div class="card text-center shadow">
|
<div class="col-xl-7">
|
||||||
{# Tabs for button categories #}
|
<div class="card border-success shadow mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<ul class="nav nav-tabs nav-fill card-header-tabs">
|
<p class="card-text font-weight-bold">
|
||||||
{% for category in categories %}
|
Sélection des émitteurs
|
||||||
<li class="nav-item">
|
</p>
|
||||||
<a class="nav-link" data-toggle="tab" href="#{{ category.grouper|slugify }}">
|
</div>
|
||||||
{{ category.grouper }}
|
<ul class="list-group list-group-flush">
|
||||||
</a>
|
<li class="list-group-item py-1 d-flex justify-content-between align-items-center">
|
||||||
</li>
|
Cras justo odio
|
||||||
{% endfor %}
|
<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>
|
</ul>
|
||||||
</div>
|
<div class="card-body">
|
||||||
|
TODO: reimplement select2 here in JS
|
||||||
{# 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>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
|
{# 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 %}
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</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>
|
||||||
|
</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 %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extrajavascript %}
|
{% block extrajavascript %}
|
||||||
|
|
Loading…
Reference in New Issue