mirror of
https://gitlab.crans.org/bde/nk20
synced 2024-12-23 07:52:23 +00:00
Merge branch 'consos' into 'master'
Page de consommations Closes #21 See merge request bde/nk20!57
This commit is contained in:
commit
0ecef74a1c
@ -124,7 +124,7 @@ class UserDetailView(LoginRequiredMixin, DetailView):
|
||||
context = super().get_context_data(**kwargs)
|
||||
user = context['user_object']
|
||||
history_list = \
|
||||
Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note))
|
||||
Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note)).order_by("-id")
|
||||
context['history_list'] = HistoryTable(history_list)
|
||||
club_list = \
|
||||
Membership.objects.all().filter(user=user).only("club")
|
||||
|
@ -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):
|
||||
@ -18,12 +18,6 @@ class NoteSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Note
|
||||
fields = '__all__'
|
||||
extra_kwargs = {
|
||||
'url': {
|
||||
'view_name': 'project-detail',
|
||||
'lookup_field': 'pk'
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class NoteClubSerializer(serializers.ModelSerializer):
|
||||
@ -31,44 +25,60 @@ class NoteClubSerializer(serializers.ModelSerializer):
|
||||
REST API Serializer for Club's notes.
|
||||
The djangorestframework plugin will analyse the model `NoteClub` and parse all fields in the API.
|
||||
"""
|
||||
name = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = NoteClub
|
||||
fields = '__all__'
|
||||
|
||||
def get_name(self, obj):
|
||||
return str(obj)
|
||||
|
||||
|
||||
class NoteSpecialSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
REST API Serializer for special notes.
|
||||
The djangorestframework plugin will analyse the model `NoteSpecial` and parse all fields in the API.
|
||||
"""
|
||||
name = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = NoteSpecial
|
||||
fields = '__all__'
|
||||
|
||||
def get_name(self, obj):
|
||||
return str(obj)
|
||||
|
||||
|
||||
class NoteUserSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
REST API Serializer for User's notes.
|
||||
The djangorestframework plugin will analyse the model `NoteUser` and parse all fields in the API.
|
||||
"""
|
||||
name = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = NoteUser
|
||||
fields = '__all__'
|
||||
|
||||
def get_name(self, obj):
|
||||
return str(obj)
|
||||
|
||||
|
||||
class AliasSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
REST API Serializer for Aliases.
|
||||
The djangorestframework plugin will analyse the model `Alias` and parse all fields in the API.
|
||||
"""
|
||||
note = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Alias
|
||||
fields = '__all__'
|
||||
|
||||
def get_note(self, alias):
|
||||
return NotePolymorphicSerializer().to_representation(alias.note)
|
||||
|
||||
|
||||
class NotePolymorphicSerializer(PolymorphicSerializer):
|
||||
model_serializer_mapping = {
|
||||
@ -134,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,
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
from django.db.models import Q
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.filters import SearchFilter
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
|
||||
from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \
|
||||
NoteUserSerializer, AliasSerializer, \
|
||||
@ -61,6 +61,9 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
queryset = Note.objects.all()
|
||||
serializer_class = NotePolymorphicSerializer
|
||||
filter_backends = [SearchFilter, OrderingFilter]
|
||||
search_fields = ['$alias__normalized_name', '$alias__name', '$polymorphic_ctype__model', ]
|
||||
ordering_fields = ['alias__name', 'alias__normalized_name']
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
@ -82,12 +85,11 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
|
||||
elif "club" in types:
|
||||
queryset = queryset.filter(polymorphic_ctype__model="noteclub")
|
||||
elif "special" in types:
|
||||
queryset = queryset.filter(
|
||||
polymorphic_ctype__model="notespecial")
|
||||
queryset = queryset.filter(polymorphic_ctype__model="notespecial")
|
||||
else:
|
||||
queryset = queryset.none()
|
||||
|
||||
return queryset
|
||||
return queryset.distinct()
|
||||
|
||||
|
||||
class AliasViewSet(viewsets.ModelViewSet):
|
||||
@ -98,6 +100,9 @@ class AliasViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
queryset = Alias.objects.all()
|
||||
serializer_class = AliasSerializer
|
||||
filter_backends = [SearchFilter, OrderingFilter]
|
||||
search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
|
||||
ordering_fields = ['name', 'normalized_name']
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
|
@ -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
|
||||
from .models import TransactionTemplate
|
||||
|
||||
|
||||
class AliasForm(forms.ModelForm):
|
||||
@ -50,52 +50,3 @@ class TransactionTemplateForm(forms.ModelForm):
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class TransactionForm(forms.ModelForm):
|
||||
def save(self, commit=True):
|
||||
super().save(commit)
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
If the user has no right to transfer funds, then it will be the source of the transfer by default.
|
||||
Transactions between a note and the same note are not authorized.
|
||||
"""
|
||||
|
||||
cleaned_data = super().clean()
|
||||
if "source" not in cleaned_data: # TODO Replace it with "if %user has no right to transfer funds"
|
||||
cleaned_data["source"] = self.user.note
|
||||
|
||||
if cleaned_data["source"].pk == cleaned_data["destination"].pk:
|
||||
self.add_error("destination", _("Source and destination must be different."))
|
||||
|
||||
return cleaned_data
|
||||
|
||||
class Meta:
|
||||
model = Transaction
|
||||
fields = (
|
||||
'source',
|
||||
'destination',
|
||||
'reason',
|
||||
'amount',
|
||||
)
|
||||
|
||||
# Voir ci-dessus
|
||||
widgets = {
|
||||
'source':
|
||||
autocomplete.ModelSelect2(
|
||||
url='note:note_autocomplete',
|
||||
attrs={
|
||||
'data-placeholder': 'Note ...',
|
||||
'data-minimum-input-length': 1,
|
||||
},
|
||||
),
|
||||
'destination':
|
||||
autocomplete.ModelSelect2(
|
||||
url='note:note_autocomplete',
|
||||
attrs={
|
||||
'data-placeholder': 'Note ...',
|
||||
'data-minimum-input-length': 1,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from polymorphic.models import PolymorphicModel
|
||||
|
||||
from .notes import Note, NoteClub
|
||||
from .notes import Note, NoteClub, NoteSpecial
|
||||
|
||||
"""
|
||||
Defines transactions
|
||||
@ -68,6 +68,7 @@ class TransactionTemplate(models.Model):
|
||||
description = models.CharField(
|
||||
verbose_name=_('description'),
|
||||
max_length=255,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@ -106,7 +107,10 @@ class Transaction(PolymorphicModel):
|
||||
verbose_name=_('quantity'),
|
||||
default=1,
|
||||
)
|
||||
amount = models.PositiveIntegerField(verbose_name=_('amount'), )
|
||||
amount = models.PositiveIntegerField(
|
||||
verbose_name=_('amount'),
|
||||
)
|
||||
|
||||
reason = models.CharField(
|
||||
verbose_name=_('reason'),
|
||||
max_length=255,
|
||||
@ -132,6 +136,7 @@ class Transaction(PolymorphicModel):
|
||||
|
||||
if self.source.pk == self.destination.pk:
|
||||
# When source == destination, no money is transfered
|
||||
super().save(*args, **kwargs)
|
||||
return
|
||||
|
||||
created = self.pk is None
|
||||
@ -156,11 +161,14 @@ class Transaction(PolymorphicModel):
|
||||
def total(self):
|
||||
return self.amount * self.quantity
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return _('Transfer')
|
||||
|
||||
|
||||
class TemplateTransaction(Transaction):
|
||||
"""
|
||||
Special type of :model:`note.Transaction` associated to a :model:`note.TransactionTemplate`.
|
||||
|
||||
"""
|
||||
|
||||
template = models.ForeignKey(
|
||||
@ -173,6 +181,36 @@ class TemplateTransaction(Transaction):
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
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"),
|
||||
blank=True,
|
||||
)
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return _('Credit') if isinstance(self.source, NoteSpecial) else _("Debit")
|
||||
|
||||
|
||||
class MembershipTransaction(Transaction):
|
||||
"""
|
||||
@ -189,3 +227,7 @@ class MembershipTransaction(Transaction):
|
||||
class Meta:
|
||||
verbose_name = _("membership transaction")
|
||||
verbose_name_plural = _("membership transactions")
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return _('membership transaction')
|
||||
|
@ -1,9 +1,12 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import html
|
||||
|
||||
import django_tables2 as tables
|
||||
from django.db.models import F
|
||||
from django_tables2.utils import A
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models.notes import Alias
|
||||
from .models.transactions import Transaction
|
||||
@ -17,17 +20,25 @@ class HistoryTable(tables.Table):
|
||||
'table table-condensed table-striped table-hover'
|
||||
}
|
||||
model = Transaction
|
||||
exclude = ("polymorphic_ctype", )
|
||||
exclude = ("id", "polymorphic_ctype", )
|
||||
template_name = 'django_tables2/bootstrap4.html'
|
||||
sequence = ('...', 'total', 'valid')
|
||||
sequence = ('...', 'type', 'total', 'valid', )
|
||||
orderable = False
|
||||
|
||||
type = tables.Column()
|
||||
|
||||
total = tables.Column() # will use Transaction.total() !!
|
||||
|
||||
valid = tables.Column(attrs={"td": {"id": lambda record: "validate_" + str(record.id),
|
||||
"class": lambda record: str(record.valid).lower() + ' validate',
|
||||
"onclick": lambda record: 'de_validate(' + str(record.id) + ', '
|
||||
+ str(record.valid).lower() + ')'}})
|
||||
|
||||
def order_total(self, queryset, is_descending):
|
||||
# needed for rendering
|
||||
queryset = queryset.annotate(total=F('amount') * F('quantity')) \
|
||||
.order_by(('-' if is_descending else '') + 'total')
|
||||
return (queryset, True)
|
||||
return queryset, True
|
||||
|
||||
def render_amount(self, value):
|
||||
return pretty_money(value)
|
||||
@ -35,6 +46,16 @@ class HistoryTable(tables.Table):
|
||||
def render_total(self, value):
|
||||
return pretty_money(value)
|
||||
|
||||
def render_type(self, value):
|
||||
return _(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 "✖"
|
||||
|
||||
|
||||
class AliasTable(tables.Table):
|
||||
class Meta:
|
||||
|
@ -11,7 +11,7 @@ def pretty_money(value):
|
||||
abs(value) // 100,
|
||||
)
|
||||
else:
|
||||
return "{:s}{:d} € {:02d}".format(
|
||||
return "{:s}{:d}.{:02d} €".format(
|
||||
"- " if value < 0 else "",
|
||||
abs(value) // 100,
|
||||
abs(value) % 100,
|
||||
|
@ -3,53 +3,43 @@
|
||||
|
||||
from dal import autocomplete
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
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
|
||||
from .models import Transaction, TransactionTemplate, Alias
|
||||
from .forms import TransactionTemplateForm
|
||||
from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction, NoteSpecial
|
||||
from .models.transactions import SpecialTransaction
|
||||
from .tables import HistoryTable
|
||||
|
||||
|
||||
class TransactionCreate(LoginRequiredMixin, CreateView):
|
||||
class TransactionCreate(LoginRequiredMixin, SingleTableView):
|
||||
"""
|
||||
Show transfer page
|
||||
|
||||
TODO: If user have sufficient rights, they can transfer from an other note
|
||||
"""
|
||||
model = Transaction
|
||||
form_class = TransactionForm
|
||||
queryset = Transaction.objects.order_by("-id").all()[:50]
|
||||
template_name = "note/transaction_form.html"
|
||||
|
||||
# Transaction history table
|
||||
table_class = HistoryTable
|
||||
table_pagination = {"per_page": 50}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Add some context variables in template such as page title
|
||||
"""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _('Transfer money from your account '
|
||||
'to one or others')
|
||||
|
||||
context['no_cache'] = True
|
||||
context['title'] = _('Transfer money')
|
||||
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
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
"""
|
||||
If the user has no right to transfer funds, then it won't have the choice of the source of the transfer.
|
||||
"""
|
||||
form = super().get_form(form_class)
|
||||
|
||||
if False: # TODO: fix it with "if %user has no right to transfer funds"
|
||||
del form.fields['source']
|
||||
form.user = self.request.user
|
||||
|
||||
return form
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('note:transfer')
|
||||
|
||||
|
||||
class NoteAutocomplete(autocomplete.Select2QuerySetView):
|
||||
"""
|
||||
@ -127,21 +117,25 @@ class ConsoView(LoginRequiredMixin, SingleTableView):
|
||||
"""
|
||||
Consume
|
||||
"""
|
||||
model = Transaction
|
||||
queryset = Transaction.objects.order_by("-id").all()[:50]
|
||||
template_name = "note/conso_form.html"
|
||||
|
||||
# Transaction history table
|
||||
table_class = HistoryTable
|
||||
table_pagination = {"per_page": 10}
|
||||
table_pagination = {"per_page": 50}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Add some context variables in template such as page title
|
||||
"""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['transaction_templates'] = TransactionTemplate.objects.filter(display=True) \
|
||||
.order_by('category')
|
||||
from django.db.models import Count
|
||||
buttons = TransactionTemplate.objects.filter(display=True) \
|
||||
.annotate(clicks=Count('templatetransaction')).order_by('category__name', 'name')
|
||||
context['transaction_templates'] = buttons
|
||||
context['most_used'] = buttons.order_by('-clicks', 'name')[:10]
|
||||
context['title'] = _("Consumptions")
|
||||
context['polymorphic_ctype'] = ContentType.objects.get_for_model(TemplateTransaction).pk
|
||||
|
||||
# select2 compatibility
|
||||
context['no_cache'] = True
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit 123466cfa914422422cd372197e64adf65ef05f7
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-03-11 11:44+0100\n"
|
||||
"POT-Creation-Date: 2020-03-16 11:53+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -23,9 +23,10 @@ msgid "activity"
|
||||
msgstr ""
|
||||
|
||||
#: apps/activity/models.py:19 apps/activity/models.py:44
|
||||
#: apps/member/models.py:60 apps/member/models.py:111
|
||||
#: apps/member/models.py:61 apps/member/models.py:112
|
||||
#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
|
||||
#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15
|
||||
#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:202
|
||||
#: templates/member/profile_detail.html:15
|
||||
msgid "name"
|
||||
msgstr ""
|
||||
|
||||
@ -50,7 +51,7 @@ msgid "description"
|
||||
msgstr ""
|
||||
|
||||
#: apps/activity/models.py:54 apps/note/models/notes.py:164
|
||||
#: apps/note/models/transactions.py:62
|
||||
#: apps/note/models/transactions.py:62 apps/note/models/transactions.py:115
|
||||
msgid "type"
|
||||
msgstr ""
|
||||
|
||||
@ -86,7 +87,7 @@ msgstr ""
|
||||
msgid "API"
|
||||
msgstr ""
|
||||
|
||||
#: apps/logs/apps.py:10
|
||||
#: apps/logs/apps.py:11
|
||||
msgid "Logs"
|
||||
msgstr ""
|
||||
|
||||
@ -166,73 +167,73 @@ msgstr ""
|
||||
msgid "user profile"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:65
|
||||
#: apps/member/models.py:66
|
||||
msgid "email"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:70
|
||||
#: apps/member/models.py:71
|
||||
msgid "membership fee"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:74
|
||||
#: apps/member/models.py:75
|
||||
msgid "membership duration"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:75
|
||||
#: apps/member/models.py:76
|
||||
msgid "The longest time a membership can last (NULL = infinite)."
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:80
|
||||
#: apps/member/models.py:81
|
||||
msgid "membership start"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:81
|
||||
#: apps/member/models.py:82
|
||||
msgid "How long after January 1st the members can renew their membership."
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:86
|
||||
#: apps/member/models.py:87
|
||||
msgid "membership end"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:87
|
||||
#: apps/member/models.py:88
|
||||
msgid ""
|
||||
"How long the membership can last after January 1st of the next year after "
|
||||
"members can renew their membership."
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:93 apps/note/models/notes.py:139
|
||||
#: apps/member/models.py:94 apps/note/models/notes.py:139
|
||||
msgid "club"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:94
|
||||
#: apps/member/models.py:95
|
||||
msgid "clubs"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:117
|
||||
#: apps/member/models.py:118
|
||||
msgid "role"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:118
|
||||
#: apps/member/models.py:119
|
||||
msgid "roles"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:142
|
||||
#: apps/member/models.py:143
|
||||
msgid "membership starts on"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:145
|
||||
#: apps/member/models.py:146
|
||||
msgid "membership ends on"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:149
|
||||
#: apps/member/models.py:150
|
||||
msgid "fee"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:153
|
||||
#: apps/member/models.py:154
|
||||
msgid "membership"
|
||||
msgstr ""
|
||||
|
||||
#: apps/member/models.py:154
|
||||
#: apps/member/models.py:155
|
||||
msgid "memberships"
|
||||
msgstr ""
|
||||
|
||||
@ -253,12 +254,12 @@ msgstr ""
|
||||
msgid "Alias successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/admin.py:120 apps/note/models/transactions.py:93
|
||||
#: apps/note/admin.py:120 apps/note/models/transactions.py:94
|
||||
msgid "source"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/admin.py:128 apps/note/admin.py:156
|
||||
#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:99
|
||||
#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:100
|
||||
msgid "destination"
|
||||
msgstr ""
|
||||
|
||||
@ -278,10 +279,6 @@ msgstr ""
|
||||
msgid "Maximal size: 2MB"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/forms.py:70
|
||||
msgid "Source and destination must be different."
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/notes.py:27
|
||||
msgid "account balance"
|
||||
msgstr ""
|
||||
@ -312,7 +309,7 @@ msgstr ""
|
||||
msgid "display image"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:102
|
||||
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:103
|
||||
msgid "created at"
|
||||
msgstr ""
|
||||
|
||||
@ -374,15 +371,15 @@ msgstr ""
|
||||
msgid "aliases"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/notes.py:229
|
||||
#: apps/note/models/notes.py:233
|
||||
msgid "Alias is too long."
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/notes.py:234
|
||||
#: apps/note/models/notes.py:238
|
||||
msgid "An alias with a similar name already exists: {} "
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/notes.py:243
|
||||
#: apps/note/models/notes.py:247
|
||||
msgid "You can't delete your main alias."
|
||||
msgstr ""
|
||||
|
||||
@ -398,7 +395,7 @@ msgstr ""
|
||||
msgid "A template with this name already exist"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:109
|
||||
#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:111
|
||||
msgid "amount"
|
||||
msgstr ""
|
||||
|
||||
@ -406,51 +403,81 @@ msgstr ""
|
||||
msgid "in centimes"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:74
|
||||
#: apps/note/models/transactions.py:75
|
||||
msgid "transaction template"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:75
|
||||
#: apps/note/models/transactions.py:76
|
||||
msgid "transaction templates"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:106
|
||||
#: apps/note/models/transactions.py:107
|
||||
msgid "quantity"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:111
|
||||
msgid "reason"
|
||||
#: apps/note/models/transactions.py:117 templates/note/transaction_form.html:15
|
||||
msgid "Gift"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:115
|
||||
msgid "valid"
|
||||
#: apps/note/models/transactions.py:118 templates/base.html:90
|
||||
#: templates/note/transaction_form.html:19
|
||||
#: templates/note/transaction_form.html:126
|
||||
msgid "Transfer"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:120
|
||||
msgid "transaction"
|
||||
#: apps/note/models/transactions.py:119
|
||||
msgid "Template"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:121
|
||||
msgid "transactions"
|
||||
#: apps/note/models/transactions.py:120 templates/note/transaction_form.html:23
|
||||
msgid "Credit"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:185
|
||||
#: apps/note/models/transactions.py:121 templates/note/transaction_form.html:27
|
||||
msgid "Debit"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:122 apps/note/models/transactions.py:230
|
||||
msgid "membership transaction"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:186
|
||||
#: apps/note/models/transactions.py:129
|
||||
msgid "reason"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:133
|
||||
msgid "valid"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:138
|
||||
msgid "transaction"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:139
|
||||
msgid "transactions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:207
|
||||
msgid "first_name"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:212
|
||||
msgid "bank"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/models/transactions.py:231
|
||||
msgid "membership transactions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/views.py:29
|
||||
msgid "Transfer money from your account to one or others"
|
||||
#: apps/note/views.py:31
|
||||
msgid "Transfer money"
|
||||
msgstr ""
|
||||
|
||||
#: apps/note/views.py:139
|
||||
msgid "Consommations"
|
||||
#: apps/note/views.py:132 templates/base.html:78
|
||||
msgid "Consumptions"
|
||||
msgstr ""
|
||||
|
||||
#: note_kfet/settings/__init__.py:63
|
||||
#: note_kfet/settings/__init__.py:61
|
||||
msgid ""
|
||||
"The Central Authentication Service grants you access to most of our websites "
|
||||
"by authenticating only once, so you don't need to type your credentials "
|
||||
@ -473,24 +500,16 @@ msgstr ""
|
||||
msgid "The ENS Paris-Saclay BDE note."
|
||||
msgstr ""
|
||||
|
||||
#: templates/base.html:70
|
||||
msgid "Consumptions"
|
||||
msgstr ""
|
||||
|
||||
#: templates/base.html:73
|
||||
#: templates/base.html:81
|
||||
msgid "Clubs"
|
||||
msgstr ""
|
||||
|
||||
#: templates/base.html:76
|
||||
#: templates/base.html:84
|
||||
msgid "Activities"
|
||||
msgstr ""
|
||||
|
||||
#: templates/base.html:79
|
||||
msgid "Button"
|
||||
msgstr ""
|
||||
|
||||
#: templates/base.html:82 templates/note/transaction_form.html:35
|
||||
msgid "Transfer"
|
||||
#: templates/base.html:87
|
||||
msgid "Buttons"
|
||||
msgstr ""
|
||||
|
||||
#: templates/cas_server/base.html:7
|
||||
@ -549,6 +568,7 @@ msgid "Field filters"
|
||||
msgstr ""
|
||||
|
||||
#: templates/django_filters/rest_framework/form.html:5
|
||||
#: templates/member/club_form.html:10
|
||||
msgid "Submit"
|
||||
msgstr ""
|
||||
|
||||
@ -572,6 +592,14 @@ msgstr ""
|
||||
msgid "Transaction history"
|
||||
msgstr ""
|
||||
|
||||
#: templates/member/club_form.html:6
|
||||
msgid "Clubs list"
|
||||
msgstr ""
|
||||
|
||||
#: templates/member/club_list.html:8
|
||||
msgid "New club"
|
||||
msgstr ""
|
||||
|
||||
#: templates/member/manage_auth_tokens.html:16
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
@ -620,8 +648,87 @@ msgstr ""
|
||||
msgid "Save Changes"
|
||||
msgstr ""
|
||||
|
||||
#: templates/member/signup.html:5 templates/member/signup.html:8
|
||||
#: templates/member/signup.html:14
|
||||
msgid "Sign Up"
|
||||
msgid "Sign up"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/conso_form.html:28 templates/note/transaction_form.html:38
|
||||
msgid "Select emitters"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/conso_form.html:45
|
||||
msgid "Select consumptions"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/conso_form.html:51
|
||||
msgid "Consume!"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/conso_form.html:64
|
||||
msgid "Most used buttons"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/conso_form.html:121
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/conso_form.html:126
|
||||
msgid "Single consumptions"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/conso_form.html:130
|
||||
msgid "Double consumptions"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/conso_form.html:141
|
||||
msgid "Recent transactions history"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transaction_form.html:55
|
||||
msgid "External payment"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transaction_form.html:63
|
||||
msgid "Transfer type"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transaction_form.html:73
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transaction_form.html:79
|
||||
msgid "First name"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transaction_form.html:85
|
||||
msgid "Bank"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transaction_form.html:97
|
||||
#: templates/note/transaction_form.html:179
|
||||
#: templates/note/transaction_form.html:186
|
||||
msgid "Select receivers"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transaction_form.html:114
|
||||
msgid "Amount"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transaction_form.html:119
|
||||
msgid "Reason"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transaction_form.html:193
|
||||
msgid "Credit note"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transaction_form.html:200
|
||||
msgid "Debit note"
|
||||
msgstr ""
|
||||
|
||||
#: templates/note/transactiontemplate_form.html:6
|
||||
msgid "Buttons list"
|
||||
msgstr ""
|
||||
|
||||
#: templates/registration/logged_out.html:8
|
||||
|
@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-03-11 11:44+0100\n"
|
||||
"POT-Creation-Date: 2020-03-16 11:53+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -18,9 +18,10 @@ msgid "activity"
|
||||
msgstr "activité"
|
||||
|
||||
#: apps/activity/models.py:19 apps/activity/models.py:44
|
||||
#: apps/member/models.py:60 apps/member/models.py:111
|
||||
#: apps/member/models.py:61 apps/member/models.py:112
|
||||
#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
|
||||
#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15
|
||||
#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:202
|
||||
#: templates/member/profile_detail.html:15
|
||||
msgid "name"
|
||||
msgstr "nom"
|
||||
|
||||
@ -45,7 +46,7 @@ msgid "description"
|
||||
msgstr "description"
|
||||
|
||||
#: apps/activity/models.py:54 apps/note/models/notes.py:164
|
||||
#: apps/note/models/transactions.py:62
|
||||
#: apps/note/models/transactions.py:62 apps/note/models/transactions.py:115
|
||||
msgid "type"
|
||||
msgstr "type"
|
||||
|
||||
@ -81,7 +82,7 @@ msgstr "invités"
|
||||
msgid "API"
|
||||
msgstr ""
|
||||
|
||||
#: apps/logs/apps.py:10
|
||||
#: apps/logs/apps.py:11
|
||||
msgid "Logs"
|
||||
msgstr ""
|
||||
|
||||
@ -161,37 +162,37 @@ msgstr "payé"
|
||||
msgid "user profile"
|
||||
msgstr "profil utilisateur"
|
||||
|
||||
#: apps/member/models.py:65
|
||||
#: apps/member/models.py:66
|
||||
msgid "email"
|
||||
msgstr "courriel"
|
||||
|
||||
#: apps/member/models.py:70
|
||||
#: apps/member/models.py:71
|
||||
msgid "membership fee"
|
||||
msgstr "cotisation pour adhérer"
|
||||
|
||||
#: apps/member/models.py:74
|
||||
#: apps/member/models.py:75
|
||||
msgid "membership duration"
|
||||
msgstr "durée de l'adhésion"
|
||||
|
||||
#: apps/member/models.py:75
|
||||
#: apps/member/models.py:76
|
||||
msgid "The longest time a membership can last (NULL = infinite)."
|
||||
msgstr "La durée maximale d'une adhésion (NULL = infinie)."
|
||||
|
||||
#: apps/member/models.py:80
|
||||
#: apps/member/models.py:81
|
||||
msgid "membership start"
|
||||
msgstr "début de l'adhésion"
|
||||
|
||||
#: apps/member/models.py:81
|
||||
#: apps/member/models.py:82
|
||||
msgid "How long after January 1st the members can renew their membership."
|
||||
msgstr ""
|
||||
"Combien de temps après le 1er Janvier les adhérents peuvent renouveler leur "
|
||||
"adhésion."
|
||||
|
||||
#: apps/member/models.py:86
|
||||
#: apps/member/models.py:87
|
||||
msgid "membership end"
|
||||
msgstr "fin de l'adhésion"
|
||||
|
||||
#: apps/member/models.py:87
|
||||
#: apps/member/models.py:88
|
||||
msgid ""
|
||||
"How long the membership can last after January 1st of the next year after "
|
||||
"members can renew their membership."
|
||||
@ -199,45 +200,39 @@ msgstr ""
|
||||
"Combien de temps l'adhésion peut durer après le 1er Janvier de l'année "
|
||||
"suivante avant que les adhérents peuvent renouveler leur adhésion."
|
||||
|
||||
#: apps/member/models.py:93 apps/note/models/notes.py:139
|
||||
#: apps/member/models.py:94 apps/note/models/notes.py:139
|
||||
msgid "club"
|
||||
msgstr "club"
|
||||
|
||||
msgid "New club"
|
||||
msgstr "Nouveau club"
|
||||
|
||||
msgid "Clubs list"
|
||||
msgstr "Liste des clubs"
|
||||
|
||||
#: apps/member/models.py:94
|
||||
#: apps/member/models.py:95
|
||||
msgid "clubs"
|
||||
msgstr "clubs"
|
||||
|
||||
#: apps/member/models.py:117
|
||||
#: apps/member/models.py:118
|
||||
msgid "role"
|
||||
msgstr "rôle"
|
||||
|
||||
#: apps/member/models.py:118
|
||||
#: apps/member/models.py:119
|
||||
msgid "roles"
|
||||
msgstr "rôles"
|
||||
|
||||
#: apps/member/models.py:142
|
||||
#: apps/member/models.py:143
|
||||
msgid "membership starts on"
|
||||
msgstr "l'adhésion commence le"
|
||||
|
||||
#: apps/member/models.py:145
|
||||
#: apps/member/models.py:146
|
||||
msgid "membership ends on"
|
||||
msgstr "l'adhésion finie le"
|
||||
|
||||
#: apps/member/models.py:149
|
||||
#: apps/member/models.py:150
|
||||
msgid "fee"
|
||||
msgstr "cotisation"
|
||||
|
||||
#: apps/member/models.py:153
|
||||
#: apps/member/models.py:154
|
||||
msgid "membership"
|
||||
msgstr "adhésion"
|
||||
|
||||
#: apps/member/models.py:154
|
||||
#: apps/member/models.py:155
|
||||
msgid "memberships"
|
||||
msgstr "adhésions"
|
||||
|
||||
@ -258,12 +253,12 @@ msgstr "Compte n°%(id)s : %(username)s"
|
||||
msgid "Alias successfully deleted"
|
||||
msgstr "L'alias a bien été supprimé"
|
||||
|
||||
#: apps/note/admin.py:120 apps/note/models/transactions.py:93
|
||||
#: apps/note/admin.py:120 apps/note/models/transactions.py:94
|
||||
msgid "source"
|
||||
msgstr "source"
|
||||
|
||||
#: apps/note/admin.py:128 apps/note/admin.py:156
|
||||
#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:99
|
||||
#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:100
|
||||
msgid "destination"
|
||||
msgstr "destination"
|
||||
|
||||
@ -283,10 +278,6 @@ msgstr "Choisissez une image"
|
||||
msgid "Maximal size: 2MB"
|
||||
msgstr "Taille maximale : 2 Mo"
|
||||
|
||||
#: apps/note/forms.py:70
|
||||
msgid "Source and destination must be different."
|
||||
msgstr "La source et la destination doivent être différentes."
|
||||
|
||||
#: apps/note/models/notes.py:27
|
||||
msgid "account balance"
|
||||
msgstr "solde du compte"
|
||||
@ -318,7 +309,7 @@ msgstr ""
|
||||
msgid "display image"
|
||||
msgstr "image affichée"
|
||||
|
||||
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:102
|
||||
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:103
|
||||
msgid "created at"
|
||||
msgstr "créée le"
|
||||
|
||||
@ -380,15 +371,15 @@ msgstr "alias"
|
||||
msgid "aliases"
|
||||
msgstr "alias"
|
||||
|
||||
#: apps/note/models/notes.py:229
|
||||
#: apps/note/models/notes.py:233
|
||||
msgid "Alias is too long."
|
||||
msgstr "L'alias est trop long."
|
||||
|
||||
#: apps/note/models/notes.py:234
|
||||
#: apps/note/models/notes.py:238
|
||||
msgid "An alias with a similar name already exists: {} "
|
||||
msgstr "Un alias avec un nom similaire existe déjà : {}"
|
||||
|
||||
#: apps/note/models/notes.py:243
|
||||
#: apps/note/models/notes.py:247
|
||||
msgid "You can't delete your main alias."
|
||||
msgstr "Vous ne pouvez pas supprimer votre alias principal."
|
||||
|
||||
@ -404,7 +395,7 @@ msgstr "catégories de transaction"
|
||||
msgid "A template with this name already exist"
|
||||
msgstr "Un modèle de transaction avec un nom similaire existe déjà."
|
||||
|
||||
#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:109
|
||||
#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:111
|
||||
msgid "amount"
|
||||
msgstr "montant"
|
||||
|
||||
@ -412,47 +403,81 @@ msgstr "montant"
|
||||
msgid "in centimes"
|
||||
msgstr "en centimes"
|
||||
|
||||
#: apps/note/models/transactions.py:74
|
||||
#: apps/note/models/transactions.py:75
|
||||
msgid "transaction template"
|
||||
msgstr "modèle de transaction"
|
||||
|
||||
#: apps/note/models/transactions.py:75
|
||||
#: apps/note/models/transactions.py:76
|
||||
msgid "transaction templates"
|
||||
msgstr "modèles de transaction"
|
||||
|
||||
#: apps/note/models/transactions.py:106
|
||||
#: apps/note/models/transactions.py:107
|
||||
msgid "quantity"
|
||||
msgstr "quantité"
|
||||
|
||||
#: apps/note/models/transactions.py:111
|
||||
msgid "reason"
|
||||
msgstr "raison"
|
||||
#: apps/note/models/transactions.py:117 templates/note/transaction_form.html:15
|
||||
msgid "Gift"
|
||||
msgstr "Don"
|
||||
|
||||
#: apps/note/models/transactions.py:115
|
||||
msgid "valid"
|
||||
msgstr "valide"
|
||||
#: apps/note/models/transactions.py:118 templates/base.html:90
|
||||
#: templates/note/transaction_form.html:19
|
||||
#: templates/note/transaction_form.html:126
|
||||
msgid "Transfer"
|
||||
msgstr "Virement"
|
||||
|
||||
#: apps/note/models/transactions.py:120
|
||||
msgid "transaction"
|
||||
msgstr "transaction"
|
||||
#: apps/note/models/transactions.py:119
|
||||
msgid "Template"
|
||||
msgstr "Bouton"
|
||||
|
||||
#: apps/note/models/transactions.py:121
|
||||
msgid "transactions"
|
||||
msgstr "transactions"
|
||||
#: apps/note/models/transactions.py:120 templates/note/transaction_form.html:23
|
||||
msgid "Credit"
|
||||
msgstr "Crédit"
|
||||
|
||||
#: apps/note/models/transactions.py:185
|
||||
#: apps/note/models/transactions.py:121 templates/note/transaction_form.html:27
|
||||
msgid "Debit"
|
||||
msgstr "Retrait"
|
||||
|
||||
#: apps/note/models/transactions.py:122 apps/note/models/transactions.py:230
|
||||
msgid "membership transaction"
|
||||
msgstr "transaction d'adhésion"
|
||||
|
||||
#: apps/note/models/transactions.py:186
|
||||
#: apps/note/models/transactions.py:129
|
||||
msgid "reason"
|
||||
msgstr "raison"
|
||||
|
||||
#: apps/note/models/transactions.py:133
|
||||
msgid "valid"
|
||||
msgstr "valide"
|
||||
|
||||
#: apps/note/models/transactions.py:138
|
||||
msgid "transaction"
|
||||
msgstr "transaction"
|
||||
|
||||
#: apps/note/models/transactions.py:139
|
||||
msgid "transactions"
|
||||
msgstr "transactions"
|
||||
|
||||
#: apps/note/models/transactions.py:207
|
||||
msgid "first_name"
|
||||
msgstr "Prénom"
|
||||
|
||||
#: apps/note/models/transactions.py:212
|
||||
msgid "bank"
|
||||
msgstr "Banque"
|
||||
|
||||
#: apps/note/models/transactions.py:231
|
||||
msgid "membership transactions"
|
||||
msgstr "transactions d'adhésion"
|
||||
|
||||
#: apps/note/views.py:29
|
||||
msgid "Transfer money from your account to one or others"
|
||||
msgstr "Transfert d'argent de ton compte vers un ou plusieurs autres"
|
||||
#: apps/note/views.py:31
|
||||
msgid "Transfer money"
|
||||
msgstr "Transferts d'argent"
|
||||
|
||||
#: note_kfet/settings/__init__.py:63
|
||||
#: apps/note/views.py:132 templates/base.html:78
|
||||
msgid "Consumptions"
|
||||
msgstr "Consommations"
|
||||
|
||||
#: note_kfet/settings/__init__.py:61
|
||||
msgid ""
|
||||
"The Central Authentication Service grants you access to most of our websites "
|
||||
"by authenticating only once, so you don't need to type your credentials "
|
||||
@ -475,29 +500,18 @@ msgstr ""
|
||||
msgid "The ENS Paris-Saclay BDE note."
|
||||
msgstr "La note du BDE de l'ENS Paris-Saclay."
|
||||
|
||||
#: templates/base.html:70
|
||||
msgid "Consumptions"
|
||||
msgstr "Consommations"
|
||||
|
||||
#: templates/base.html:73
|
||||
#: templates/base.html:81
|
||||
msgid "Clubs"
|
||||
msgstr "Clubs"
|
||||
|
||||
#: templates/base.html:76
|
||||
#: templates/base.html:84
|
||||
msgid "Activities"
|
||||
msgstr "Activités"
|
||||
|
||||
#: templates/base.html:79
|
||||
#: templates/base.html:87
|
||||
msgid "Buttons"
|
||||
msgstr "Boutons"
|
||||
|
||||
msgid "Buttons list"
|
||||
msgstr "Liste des boutons"
|
||||
|
||||
#: templates/base.html:82 templates/note/transaction_form.html:35
|
||||
msgid "Transfer"
|
||||
msgstr "Virement"
|
||||
|
||||
#: templates/cas_server/base.html:7
|
||||
msgid "Central Authentication Service"
|
||||
msgstr ""
|
||||
@ -556,8 +570,9 @@ msgid "Field filters"
|
||||
msgstr ""
|
||||
|
||||
#: templates/django_filters/rest_framework/form.html:5
|
||||
#: templates/member/club_form.html:10
|
||||
msgid "Submit"
|
||||
msgstr ""
|
||||
msgstr "Envoyer"
|
||||
|
||||
#: templates/member/club_detail.html:10
|
||||
msgid "Membership starts on"
|
||||
@ -579,6 +594,14 @@ msgstr "solde du compte"
|
||||
msgid "Transaction history"
|
||||
msgstr "Historique des transactions"
|
||||
|
||||
#: templates/member/club_form.html:6
|
||||
msgid "Clubs list"
|
||||
msgstr "Liste des clubs"
|
||||
|
||||
#: templates/member/club_list.html:8
|
||||
msgid "New club"
|
||||
msgstr "Nouveau club"
|
||||
|
||||
#: templates/member/manage_auth_tokens.html:16
|
||||
msgid "Token"
|
||||
msgstr "Jeton"
|
||||
@ -627,11 +650,89 @@ msgstr "Voir mes adhésions"
|
||||
msgid "Save Changes"
|
||||
msgstr "Sauvegarder les changements"
|
||||
|
||||
#: templates/member/signup.html:8
|
||||
#: templates/member/signup.html:5 templates/member/signup.html:8
|
||||
#: templates/member/signup.html:14
|
||||
msgid "Sign up"
|
||||
msgstr "Inscription"
|
||||
|
||||
#: templates/note/conso_form.html:28 templates/note/transaction_form.html:38
|
||||
msgid "Select emitters"
|
||||
msgstr "Sélection des émetteurs"
|
||||
|
||||
#: templates/note/conso_form.html:45
|
||||
msgid "Select consumptions"
|
||||
msgstr "Consommations"
|
||||
|
||||
#: templates/note/conso_form.html:51
|
||||
msgid "Consume!"
|
||||
msgstr "Consommer !"
|
||||
|
||||
#: templates/note/conso_form.html:64
|
||||
msgid "Most used buttons"
|
||||
msgstr "Boutons les plus utilisés"
|
||||
|
||||
#: templates/note/conso_form.html:121
|
||||
msgid "Edit"
|
||||
msgstr "Éditer"
|
||||
|
||||
#: templates/note/conso_form.html:126
|
||||
msgid "Single consumptions"
|
||||
msgstr "Consos simples"
|
||||
|
||||
#: templates/note/conso_form.html:130
|
||||
msgid "Double consumptions"
|
||||
msgstr "Consos doubles"
|
||||
|
||||
#: templates/note/conso_form.html:141
|
||||
msgid "Recent transactions history"
|
||||
msgstr "Historique des transactions récentes"
|
||||
|
||||
#: templates/note/transaction_form.html:55
|
||||
msgid "External payment"
|
||||
msgstr "Paiement extérieur"
|
||||
|
||||
#: templates/note/transaction_form.html:63
|
||||
msgid "Transfer type"
|
||||
msgstr "Type de transfert"
|
||||
|
||||
#: templates/note/transaction_form.html:73
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: templates/note/transaction_form.html:79
|
||||
msgid "First name"
|
||||
msgstr "Prénom"
|
||||
|
||||
#: templates/note/transaction_form.html:85
|
||||
msgid "Bank"
|
||||
msgstr "Banque"
|
||||
|
||||
#: templates/note/transaction_form.html:97
|
||||
#: templates/note/transaction_form.html:179
|
||||
#: templates/note/transaction_form.html:186
|
||||
msgid "Select receivers"
|
||||
msgstr "Sélection des destinataires"
|
||||
|
||||
#: templates/note/transaction_form.html:114
|
||||
msgid "Amount"
|
||||
msgstr "Montant"
|
||||
|
||||
#: templates/note/transaction_form.html:119
|
||||
msgid "Reason"
|
||||
msgstr "Raison"
|
||||
|
||||
#: templates/note/transaction_form.html:193
|
||||
msgid "Credit note"
|
||||
msgstr "Note à créditer"
|
||||
|
||||
#: templates/note/transaction_form.html:200
|
||||
msgid "Debit note"
|
||||
msgstr "Note à débiter"
|
||||
|
||||
#: templates/note/transactiontemplate_form.html:6
|
||||
msgid "Buttons list"
|
||||
msgstr "Liste des boutons"
|
||||
|
||||
#: templates/registration/logged_out.html:8
|
||||
msgid "Thanks for spending some quality time with the Web site today."
|
||||
msgstr ""
|
||||
|
281
static/js/base.js
Normal file
281
static/js/base.js
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
||||
/**
|
||||
* Convert balance in cents to a human readable amount
|
||||
* @param value the balance, in cents
|
||||
* @returns {string}
|
||||
*/
|
||||
function pretty_money(value) {
|
||||
if (value % 100 === 0)
|
||||
return (value < 0 ? "- " : "") + Math.floor(Math.abs(value) / 100) + " €";
|
||||
else
|
||||
return (value < 0 ? "- " : "") + Math.floor(Math.abs(value) / 100) + "."
|
||||
+ (Math.abs(value) % 100 < 10 ? "0" : "") + (Math.abs(value) % 100) + " €";
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a message on the top of the page.
|
||||
* @param msg The message to display
|
||||
* @param alert_type The type of the alert. Choices: info, success, warning, danger
|
||||
*/
|
||||
function addMsg(msg, alert_type) {
|
||||
let msgDiv = $("#messages");
|
||||
let html = msgDiv.html();
|
||||
html += "<div class=\"alert alert-" + alert_type + " alert-dismissible\">" +
|
||||
"<button class=\"close\" data-dismiss=\"alert\" href=\"#\"><span aria-hidden=\"true\">×</span></button>"
|
||||
+ msg + "</div>\n";
|
||||
msgDiv.html(html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the balance of the user on the right top corner
|
||||
*/
|
||||
function refreshBalance() {
|
||||
$("#user_balance").load("/ #user_balance");
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the 20 first matched notes with a given pattern
|
||||
* @param pattern The pattern that is queried
|
||||
* @param fun For each found note with the matched alias `alias`, fun(note, alias) is called.
|
||||
*/
|
||||
function getMatchedNotes(pattern, fun) {
|
||||
$.getJSON("/api/note/alias/?format=json&alias=" + pattern + "&search=user|club&ordering=normalized_name", fun);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a <li> entry with a given id and text
|
||||
*/
|
||||
function li(id, text) {
|
||||
return "<li class=\"list-group-item py-1 d-flex justify-content-between align-items-center\"" +
|
||||
" id=\"" + id + "\">" + text + "</li>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Render note name and picture
|
||||
* @param note The note to render
|
||||
* @param alias The alias to be displayed
|
||||
* @param user_note_field
|
||||
* @param profile_pic_field
|
||||
*/
|
||||
function displayNote(note, alias, user_note_field=null, profile_pic_field=null) {
|
||||
let img = note == null ? null : note.display_image;
|
||||
if (img == null)
|
||||
img = '/media/pic/default.png';
|
||||
if (note !== null && alias !== note.name)
|
||||
alias += " (aka. " + note.name + ")";
|
||||
if (note !== null && user_note_field !== null)
|
||||
$("#" + user_note_field).text(alias + " : " + pretty_money(note.balance));
|
||||
if (profile_pic_field != null)
|
||||
$("#" + profile_pic_field).attr('src', img);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a note from the emitters.
|
||||
* @param d The note to remove
|
||||
* @param note_prefix The prefix of the identifiers of the <li> blocks of the emitters
|
||||
* @param notes_display An array containing the infos of the buyers: [alias, note id, note object, quantity]
|
||||
* @param note_list_id The div block identifier where the notes of the buyers are displayed
|
||||
* @param user_note_field The identifier of the field that display the note of the hovered note (useful in
|
||||
* consumptions, put null if not used)
|
||||
* @param profile_pic_field The identifier of the field that display the profile picture of the hovered note
|
||||
* (useful in consumptions, put null if not used)
|
||||
* @returns an anonymous function to be compatible with jQuery events
|
||||
*/
|
||||
function removeNote(d, note_prefix="note", notes_display, note_list_id, user_note_field=null, profile_pic_field=null) {
|
||||
return (function() {
|
||||
let new_notes_display = [];
|
||||
let html = "";
|
||||
notes_display.forEach(function (disp) {
|
||||
if (disp.quantity > 1 || disp.id !== d.id) {
|
||||
disp.quantity -= disp.id === d.id ? 1 : 0;
|
||||
new_notes_display.push(disp);
|
||||
html += li(note_prefix + "_" + disp.id, disp.name
|
||||
+ "<span class=\"badge badge-dark badge-pill\">" + disp.quantity + "</span>");
|
||||
}
|
||||
});
|
||||
|
||||
notes_display.length = 0;
|
||||
new_notes_display.forEach(function(disp) {
|
||||
notes_display.push(disp);
|
||||
});
|
||||
|
||||
$("#" + note_list_id).html(html);
|
||||
notes_display.forEach(function (disp) {
|
||||
let obj = $("#" + note_prefix + "_" + disp.id);
|
||||
obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field, profile_pic_field));
|
||||
obj.hover(function() {
|
||||
if (disp.note)
|
||||
displayNote(disp.note, disp.name, user_note_field, profile_pic_field);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an auto-complete field to query a note with its alias
|
||||
* @param field_id The identifier of the text field where the alias is typed
|
||||
* @param alias_matched_id The div block identifier where the matched aliases are displayed
|
||||
* @param note_list_id The div block identifier where the notes of the buyers are displayed
|
||||
* @param notes An array containing the note objects of the buyers
|
||||
* @param notes_display An array containing the infos of the buyers: [alias, note id, note object, quantity]
|
||||
* @param alias_prefix The prefix of the <li> blocks for the matched aliases
|
||||
* @param note_prefix The prefix of the <li> blocks for the notes of the buyers
|
||||
* @param user_note_field The identifier of the field that display the note of the hovered note (useful in
|
||||
* consumptions, put null if not used)
|
||||
* @param profile_pic_field The identifier of the field that display the profile picture of the hovered note
|
||||
* (useful in consumptions, put null if not used)
|
||||
* @param alias_click Function that is called when an alias is clicked. If this method exists and doesn't return true,
|
||||
* the associated note is not displayed.
|
||||
* Useful for a consumption if the item is selected before.
|
||||
*/
|
||||
function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes_display, alias_prefix="alias",
|
||||
note_prefix="note", user_note_field=null, profile_pic_field=null, alias_click=null) {
|
||||
let field = $("#" + field_id);
|
||||
// When the user clicks on the search field, it is immediately cleared
|
||||
field.click(function() {
|
||||
field.val("");
|
||||
});
|
||||
|
||||
let old_pattern = null;
|
||||
|
||||
// When the user type "Enter", the first alias is clicked
|
||||
field.keypress(function(event) {
|
||||
if (event.originalEvent.charCode === 13)
|
||||
$("#" + alias_matched_id + " li").first().trigger("click");
|
||||
});
|
||||
|
||||
// When the user type something, the matched aliases are refreshed
|
||||
field.keyup(function(e) {
|
||||
if (e.originalEvent.charCode === 13)
|
||||
return;
|
||||
|
||||
let pattern = field.val();
|
||||
// If the pattern is not modified, we don't query the API
|
||||
if (pattern === old_pattern || pattern === "")
|
||||
return;
|
||||
|
||||
old_pattern = pattern;
|
||||
|
||||
// Clear old matched notes
|
||||
notes.length = 0;
|
||||
|
||||
let aliases_matched_obj = $("#" + alias_matched_id);
|
||||
let aliases_matched_html = "";
|
||||
|
||||
// Get matched notes with the given pattern
|
||||
getMatchedNotes(pattern, function(aliases) {
|
||||
// The response arrived too late, we stop the request
|
||||
if (pattern !== $("#" + field_id).val())
|
||||
return;
|
||||
|
||||
aliases.results.forEach(function (alias) {
|
||||
let note = alias.note;
|
||||
aliases_matched_html += li(alias_prefix + "_" + alias.id, alias.name);
|
||||
note.alias = alias;
|
||||
notes.push(note);
|
||||
});
|
||||
|
||||
// Display the list of matched aliases
|
||||
aliases_matched_obj.html(aliases_matched_html);
|
||||
|
||||
notes.forEach(function (note) {
|
||||
let alias = note.alias;
|
||||
let alias_obj = $("#" + alias_prefix + "_" + alias.id);
|
||||
// When an alias is hovered, the profile picture and the balance are displayed at the right place
|
||||
alias_obj.hover(function () {
|
||||
displayNote(note, alias.name, user_note_field, profile_pic_field);
|
||||
});
|
||||
|
||||
// When the user click on an alias, the associated note is added to the emitters
|
||||
alias_obj.click(function () {
|
||||
field.val("");
|
||||
// If the note is already an emitter, we increase the quantity
|
||||
var disp = null;
|
||||
notes_display.forEach(function (d) {
|
||||
// We compare the note ids
|
||||
if (d.id === note.id) {
|
||||
d.quantity += 1;
|
||||
disp = d;
|
||||
}
|
||||
});
|
||||
// In the other case, we add a new emitter
|
||||
if (disp == null) {
|
||||
disp = {
|
||||
name: alias.name,
|
||||
id: note.id,
|
||||
note: note,
|
||||
quantity: 1
|
||||
};
|
||||
notes_display.push(disp);
|
||||
}
|
||||
|
||||
// If the function alias_click exists, it is called. If it doesn't return true, then the notes are
|
||||
// note displayed. Useful for a consumption when a button is already clicked
|
||||
if (alias_click && !alias_click())
|
||||
return;
|
||||
|
||||
let note_list = $("#" + note_list_id);
|
||||
let html = "";
|
||||
notes_display.forEach(function (disp) {
|
||||
html += li(note_prefix + "_" + disp.id, disp.name
|
||||
+ "<span class=\"badge badge-dark badge-pill\">" + disp.quantity + "</span>");
|
||||
});
|
||||
|
||||
// Emitters are displayed
|
||||
note_list.html(html);
|
||||
|
||||
notes_display.forEach(function (disp) {
|
||||
let line_obj = $("#" + note_prefix + "_" + disp.id);
|
||||
// Hover an emitter display also the profile picture
|
||||
line_obj.hover(function () {
|
||||
displayNote(disp.note, disp.name, user_note_field, profile_pic_field);
|
||||
});
|
||||
|
||||
// When an emitter is clicked, it is removed
|
||||
line_obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field,
|
||||
profile_pic_field));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// When a validate button is clicked, we switch the validation status
|
||||
function de_validate(id, validated) {
|
||||
$("#validate_" + id).html("<strong style=\"font-size: 16pt;\">⟳ ...</strong>");
|
||||
|
||||
// Perform a PATCH request to the API in order to update the transaction
|
||||
// If the user has insuffisent rights, an error message will appear
|
||||
$.ajax({
|
||||
"url": "/api/note/transaction/transaction/" + id + "/",
|
||||
type: "PATCH",
|
||||
dataType: "json",
|
||||
headers: {
|
||||
"X-CSRFTOKEN": CSRF_TOKEN
|
||||
},
|
||||
data: {
|
||||
"resourcetype": "TemplateTransaction",
|
||||
valid: !validated
|
||||
},
|
||||
success: function () {
|
||||
// Refresh jQuery objects
|
||||
$(".validate").click(de_validate);
|
||||
|
||||
refreshBalance();
|
||||
// error if this method doesn't exist. Please define it.
|
||||
refreshHistory();
|
||||
},
|
||||
error: function(err) {
|
||||
addMsg("Une erreur est survenue lors de la validation/dévalidation " +
|
||||
"de cette transaction : " + err.responseText, "danger");
|
||||
|
||||
refreshBalance();
|
||||
// error if this method doesn't exist. Please define it.
|
||||
refreshHistory();
|
||||
}
|
||||
});
|
||||
}
|
205
static/js/consos.js
Normal file
205
static/js/consos.js
Normal file
@ -0,0 +1,205 @@
|
||||
// Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* Refresh the history table on the consumptions page.
|
||||
*/
|
||||
function refreshHistory() {
|
||||
$("#history").load("/note/consos/ #history");
|
||||
$("#most_used").load("/note/consos/ #most_used");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// If hash of a category in the URL, then select this category
|
||||
// else select the first one
|
||||
if (location.hash) {
|
||||
$("a[href='" + location.hash + "']").tab("show");
|
||||
} else {
|
||||
$("a[data-toggle='tab']").first().tab("show");
|
||||
}
|
||||
|
||||
// When selecting a category, change URL
|
||||
$(document.body).on("click", "a[data-toggle='tab']", function() {
|
||||
location.hash = this.getAttribute("href");
|
||||
});
|
||||
|
||||
// Switching in double consumptions mode should update the layout
|
||||
let double_conso_obj = $("#double_conso");
|
||||
double_conso_obj.click(function() {
|
||||
$("#consos_list_div").show();
|
||||
$("#infos_div").attr('class', 'col-sm-5 col-xl-6');
|
||||
$("#note_infos_div").attr('class', 'col-xl-3');
|
||||
$("#user_select_div").attr('class', 'col-xl-4');
|
||||
$("#buttons_div").attr('class', 'col-sm-7 col-xl-6');
|
||||
|
||||
let note_list_obj = $("#note_list");
|
||||
if (buttons.length > 0 && note_list_obj.text().length > 0) {
|
||||
$("#consos_list").html(note_list_obj.html());
|
||||
note_list_obj.html("");
|
||||
|
||||
buttons.forEach(function(button) {
|
||||
$("#conso_button_" + button.id).click(removeNote(button, "conso_button", buttons,
|
||||
"consos_list"));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let single_conso_obj = $("#single_conso");
|
||||
single_conso_obj.click(function() {
|
||||
$("#consos_list_div").hide();
|
||||
$("#infos_div").attr('class', 'col-sm-5 col-md-4');
|
||||
$("#note_infos_div").attr('class', 'col-xl-5');
|
||||
$("#user_select_div").attr('class', 'col-xl-7');
|
||||
$("#buttons_div").attr('class', 'col-sm-7 col-md-8');
|
||||
|
||||
let consos_list_obj = $("#consos_list");
|
||||
if (buttons.length > 0) {
|
||||
if (notes_display.length === 0 && consos_list_obj.text().length > 0) {
|
||||
$("#note_list").html(consos_list_obj.html());
|
||||
consos_list_obj.html("");
|
||||
buttons.forEach(function(button) {
|
||||
$("#conso_button_" + button.id).click(removeNote(button, "conso_button", buttons,
|
||||
"note_list"));
|
||||
});
|
||||
}
|
||||
else {
|
||||
buttons.length = 0;
|
||||
consos_list_obj.html("");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure we begin in single consumption. Removing these lines may cause problems when reloading.
|
||||
single_conso_obj.prop('checked', 'true');
|
||||
double_conso_obj.removeAttr('checked');
|
||||
$("label[for='double_conso']").attr('class', 'btn btn-sm btn-outline-primary');
|
||||
|
||||
$("#consos_list_div").hide();
|
||||
|
||||
$("#consume_all").click(consumeAll);
|
||||
});
|
||||
|
||||
notes = [];
|
||||
notes_display = [];
|
||||
buttons = [];
|
||||
|
||||
// When the user searches an alias, we update the auto-completion
|
||||
autoCompleteNote("note", "alias_matched", "note_list", notes, notes_display,
|
||||
"alias", "note", "user_note", "profile_pic", function() {
|
||||
if (buttons.length > 0 && $("#single_conso").is(":checked")) {
|
||||
consumeAll();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a transaction from a button.
|
||||
* @param dest Where the money goes
|
||||
* @param amount The price of the item
|
||||
* @param type The type of the transaction (content type id for TemplateTransaction)
|
||||
* @param category_id The category identifier
|
||||
* @param category_name The category name
|
||||
* @param template_id The identifier of the button
|
||||
* @param template_name The name of the button
|
||||
*/
|
||||
function addConso(dest, amount, type, category_id, category_name, template_id, template_name) {
|
||||
var button = null;
|
||||
buttons.forEach(function(b) {
|
||||
if (b.id === template_id) {
|
||||
b.quantity += 1;
|
||||
button = b;
|
||||
}
|
||||
});
|
||||
if (button == null) {
|
||||
button = {
|
||||
id: template_id,
|
||||
name: template_name,
|
||||
dest: dest,
|
||||
quantity: 1,
|
||||
amount: amount,
|
||||
type: type,
|
||||
category_id: category_id,
|
||||
category_name: category_name
|
||||
};
|
||||
buttons.push(button);
|
||||
}
|
||||
|
||||
let dc_obj = $("#double_conso");
|
||||
if (dc_obj.is(":checked") || notes_display.length === 0) {
|
||||
let list = dc_obj.is(":checked") ? "consos_list" : "note_list";
|
||||
let html = "";
|
||||
buttons.forEach(function(button) {
|
||||
html += li("conso_button_" + button.id, button.name
|
||||
+ "<span class=\"badge badge-dark badge-pill\">" + button.quantity + "</span>");
|
||||
});
|
||||
|
||||
$("#" + list).html(html);
|
||||
|
||||
buttons.forEach(function(button) {
|
||||
$("#conso_button_" + button.id).click(removeNote(button, "conso_button", buttons, list));
|
||||
});
|
||||
}
|
||||
else
|
||||
consumeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the page as its initial state.
|
||||
*/
|
||||
function reset() {
|
||||
notes_display.length = 0;
|
||||
notes.length = 0;
|
||||
buttons.length = 0;
|
||||
$("#note_list").html("");
|
||||
$("#alias_matched").html("");
|
||||
$("#consos_list").html("");
|
||||
displayNote(null, "");
|
||||
refreshHistory();
|
||||
refreshBalance();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply all transactions: all notes in `notes` buy each item in `buttons`
|
||||
*/
|
||||
function consumeAll() {
|
||||
notes_display.forEach(function(note_display) {
|
||||
buttons.forEach(function(button) {
|
||||
consume(note_display.id, button.dest, button.quantity * note_display.quantity, button.amount,
|
||||
button.name + " (" + button.category_name + ")", button.type, button.category_id, button.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new transaction from a button through the API.
|
||||
* @param source The note that paid the item (type: int)
|
||||
* @param dest The note that sold the item (type: int)
|
||||
* @param quantity The quantity sold (type: int)
|
||||
* @param amount The price of one item, in cents (type: int)
|
||||
* @param reason The transaction details (type: str)
|
||||
* @param type The type of the transaction (content type id for TemplateTransaction)
|
||||
* @param category The category id of the button (type: int)
|
||||
* @param template The button id (type: int)
|
||||
*/
|
||||
function consume(source, dest, quantity, amount, reason, type, category, template) {
|
||||
$.post("/api/note/transaction/transaction/",
|
||||
{
|
||||
"csrfmiddlewaretoken": CSRF_TOKEN,
|
||||
"quantity": quantity,
|
||||
"amount": amount,
|
||||
"reason": reason,
|
||||
"valid": true,
|
||||
"polymorphic_ctype": type,
|
||||
"resourcetype": "TemplateTransaction",
|
||||
"source": source,
|
||||
"destination": dest,
|
||||
"category": category,
|
||||
"template": template
|
||||
}, reset).fail(function (e) {
|
||||
reset();
|
||||
|
||||
addMsg("Une erreur est survenue lors de la transaction : " + e.responseText, "danger");
|
||||
});
|
||||
}
|
157
static/js/transfer.js
Normal file
157
static/js/transfer.js
Normal file
@ -0,0 +1,157 @@
|
||||
sources = [];
|
||||
sources_notes_display = [];
|
||||
dests = [];
|
||||
dests_notes_display = [];
|
||||
|
||||
function refreshHistory() {
|
||||
$("#history").load("/note/transfer/ #history");
|
||||
}
|
||||
|
||||
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();
|
||||
refreshHistory();
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
autoCompleteNote("source_note", "source_alias_matched", "source_note_list", sources, sources_notes_display,
|
||||
"source_alias", "source_note", "user_note", "profile_pic");
|
||||
autoCompleteNote("dest_note", "dest_alias_matched", "dest_note_list", dests, dests_notes_display,
|
||||
"dest_alias", "dest_note", "user_note", "profile_pic", function() {
|
||||
let last = dests_notes_display[dests_notes_display.length - 1];
|
||||
dests_notes_display.length = 0;
|
||||
dests_notes_display.push(last);
|
||||
|
||||
last.quantity = 1;
|
||||
|
||||
$.getJSON("/api/user/" + last.note.user + "/", function(user) {
|
||||
$("#last_name").val(user.last_name);
|
||||
$("#first_name").val(user.first_name);
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
// Ensure we begin in gift mode. Removing these lines may cause problems when reloading.
|
||||
$("#type_gift").prop('checked', 'true');
|
||||
$("#type_transfer").removeAttr('checked');
|
||||
$("#type_credit").removeAttr('checked');
|
||||
$("#type_debit").removeAttr('checked');
|
||||
$("label[for='type_transfer']").attr('class', 'btn btn-sm btn-outline-primary');
|
||||
$("label[for='type_credit']").attr('class', 'btn btn-sm btn-outline-primary');
|
||||
$("label[for='type_debit']").attr('class', 'btn btn-sm btn-outline-primary');
|
||||
});
|
||||
|
||||
$("#transfer").click(function() {
|
||||
if ($("#type_gift").is(':checked')) {
|
||||
dests_notes_display.forEach(function (dest) {
|
||||
$.post("/api/note/transaction/transaction/",
|
||||
{
|
||||
"csrfmiddlewaretoken": CSRF_TOKEN,
|
||||
"quantity": dest.quantity,
|
||||
"amount": 100 * $("#amount").val(),
|
||||
"reason": $("#reason").val(),
|
||||
"valid": true,
|
||||
"polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE,
|
||||
"resourcetype": "Transaction",
|
||||
"source": user_id,
|
||||
"destination": dest.id
|
||||
}, function () {
|
||||
addMsg("Le transfert de "
|
||||
+ pretty_money(dest.quantity * 100 * $("#amount").val()) + " de votre note "
|
||||
+ " vers la note " + dest.name + " a été fait avec succès !", "success");
|
||||
|
||||
reset();
|
||||
}).fail(function (err) {
|
||||
addMsg("Le transfert de "
|
||||
+ pretty_money(dest.quantity * 100 * $("#amount").val()) + " de votre note "
|
||||
+ " vers la note " + dest.name + " a échoué : " + err.responseText, "danger");
|
||||
|
||||
reset();
|
||||
});
|
||||
});
|
||||
}
|
||||
else if ($("#type_transfer").is(':checked')) {
|
||||
sources_notes_display.forEach(function (source) {
|
||||
dests_notes_display.forEach(function (dest) {
|
||||
$.post("/api/note/transaction/transaction/",
|
||||
{
|
||||
"csrfmiddlewaretoken": CSRF_TOKEN,
|
||||
"quantity": source.quantity * dest.quantity,
|
||||
"amount": 100 * $("#amount").val(),
|
||||
"reason": $("#reason").val(),
|
||||
"valid": true,
|
||||
"polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE,
|
||||
"resourcetype": "Transaction",
|
||||
"source": source.id,
|
||||
"destination": dest.id
|
||||
}, function () {
|
||||
addMsg("Le transfert de "
|
||||
+ pretty_money(source.quantity * dest.quantity * 100 * $("#amount").val()) + " de la note " + source.name
|
||||
+ " vers la note " + dest.name + " a été fait avec succès !", "success");
|
||||
|
||||
reset();
|
||||
}).fail(function (err) {
|
||||
addMsg("Le transfert de "
|
||||
+ pretty_money(source.quantity * dest.quantity * 100 * $("#amount").val()) + " de la note " + source.name
|
||||
+ " vers la note " + dest.name + " a échoué : " + err.responseText, "danger");
|
||||
|
||||
reset();
|
||||
});
|
||||
});
|
||||
});
|
||||
} else if ($("#type_credit").is(':checked') || $("#type_debit").is(':checked')) {
|
||||
let special_note = $("#credit_type").val();
|
||||
let user_note = dests_notes_display[0].id;
|
||||
let given_reason = $("#reason").val();
|
||||
let source, dest, reason;
|
||||
if ($("#type_credit").is(':checked')) {
|
||||
source = special_note;
|
||||
dest = user_note;
|
||||
reason = "Crédit " + $("#credit_type option:selected").text().toLowerCase();
|
||||
if (given_reason.length > 0)
|
||||
reason += " (" + given_reason + ")";
|
||||
}
|
||||
else {
|
||||
source = user_note;
|
||||
dest = special_note;
|
||||
reason = "Retrait " + $("#credit_type option:selected").text().toLowerCase();
|
||||
if (given_reason.length > 0)
|
||||
reason += " (" + given_reason + ")";
|
||||
}
|
||||
$.post("/api/note/transaction/transaction/",
|
||||
{
|
||||
"csrfmiddlewaretoken": CSRF_TOKEN,
|
||||
"quantity": 1,
|
||||
"amount": 100 * $("#amount").val(),
|
||||
"reason": reason,
|
||||
"valid": true,
|
||||
"polymorphic_ctype": SPECIAL_TRANSFER_POLYMORPHIC_CTYPE,
|
||||
"resourcetype": "SpecialTransaction",
|
||||
"source": source,
|
||||
"destination": dest,
|
||||
"last_name": $("#last_name").val(),
|
||||
"first_name": $("#first_name").val(),
|
||||
"bank": $("#bank").val()
|
||||
}, function () {
|
||||
addMsg("Le crédit/retrait a bien été effectué !", "success");
|
||||
reset();
|
||||
}).fail(function (err) {
|
||||
addMsg("Le crédit/transfert a échoué : " + err.responseText, "danger");
|
||||
reset();
|
||||
});
|
||||
}
|
||||
});
|
@ -46,12 +46,20 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/turbolinks/5.2.0/turbolinks.js"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="/static/js/base.js"></script>
|
||||
|
||||
{# Si un formulaire requiert des données supplémentaires (notamment JS), les données sont chargées #}
|
||||
{% if form.media %}
|
||||
{{ form.media }}
|
||||
{% endif %}
|
||||
|
||||
<style>
|
||||
.validate:hover {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% block extracss %}{% endblock %}
|
||||
</head>
|
||||
<body class="d-flex w-100 h-100 flex-column">
|
||||
@ -86,7 +94,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% if user.is_authenticated %}
|
||||
<li class="dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa fa-user"></i> {{ user.username }} ({{ user.note.balance | pretty_money }})
|
||||
<i class="fa fa-user"></i>
|
||||
<span id="user_balance">{{ user.username }} ({{ user.note.balance | pretty_money }})</span>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right"
|
||||
aria-labelledby="navbarDropdownMenuLink">
|
||||
@ -115,6 +124,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</nav>
|
||||
<div class="container-fluid my-3" style="max-width: 1600px;">
|
||||
{% block contenttitle %}<h1>{{ title }}</h1>{% endblock %}
|
||||
<div id="messages"></div>
|
||||
{% block content %}
|
||||
<p>Default content...</p>
|
||||
{% endblock content %}
|
||||
@ -158,6 +168,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
CSRF_TOKEN = "{{ csrf_token }}";
|
||||
</script>
|
||||
|
||||
{% block extrajavascript %}
|
||||
{% endblock extrajavascript %}
|
||||
</body>
|
||||
|
@ -10,7 +10,7 @@
|
||||
<img src="{{ object.note.display_image.url }}" class="img-thumbnail mt-2" >
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-body" id="profile_infos">
|
||||
<dl class="row">
|
||||
<dt class="col-xl-6">{% trans 'name'|capfirst %}, {% trans 'first name' %}</dt>
|
||||
<dd class="col-xl-6">{{ object.last_name }} {{ object.first_name }}</dd>
|
||||
@ -76,7 +76,9 @@
|
||||
</a>
|
||||
</div>
|
||||
<div id="historyListCollapse" class="collapse" style="overflow:auto hidden" aria-labelledby="historyListHeading" data-parent="#accordionProfile">
|
||||
{% render_table history_list %}
|
||||
<div id="history_list">
|
||||
{% render_table history_list %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -84,3 +86,12 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script>
|
||||
function refreshHistory() {
|
||||
$("#history_list").load("{% url 'member:user_detail' pk=object.pk %} #history_list");
|
||||
$("#profile_infos").load("{% url 'member:user_detail' pk=object.pk %} #profile_infos");
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -7,67 +7,88 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-sm-5 col-md-4">
|
||||
<div class="col-sm-5 col-md-4" id="infos_div">
|
||||
<div class="row">
|
||||
{# User details column #}
|
||||
<div class="col-xl-5">
|
||||
<div class="col-xl-5" id="note_infos_div">
|
||||
<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">
|
||||
<img src="/media/pic/default.png"
|
||||
id="profile_pic" alt="" class="img-fluid rounded mx-auto d-block">
|
||||
<div class="card-body text-center">
|
||||
Paquito (aka. PAC) : -230 €
|
||||
<span id="user_note"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# User selection column #}
|
||||
<div class="col-xl-7">
|
||||
<div class="col-xl-7" id="user_select_div">
|
||||
<div class="card border-success shadow mb-4">
|
||||
<div class="card-header">
|
||||
<p class="card-text font-weight-bold">
|
||||
Sélection des émitteurs
|
||||
{% trans "Select emitters" %}
|
||||
</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 class="list-group list-group-flush" id="note_list">
|
||||
</ul>
|
||||
<div class="card-body">
|
||||
TODO: reimplement select2 here in JS
|
||||
<input class="form-control mx-auto d-block" type="text" id="note" />
|
||||
<ul class="list-group list-group-flush" id="alias_matched">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-5" id="consos_list_div">
|
||||
<div class="card border-info shadow mb-4">
|
||||
<div class="card-header">
|
||||
<p class="card-text font-weight-bold">
|
||||
{% trans "Select consumptions" %}
|
||||
</p>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush" id="consos_list">
|
||||
</ul>
|
||||
<button id="consume_all" class="form-control btn btn-primary">
|
||||
{% trans "Consume!" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Buttons column #}
|
||||
<div class="col-sm-7 col-md-8">
|
||||
<div class="col-sm-7 col-md-8" id="buttons_div">
|
||||
{# 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.
|
||||
<div class="card-header">
|
||||
<p class="card-text font-weight-bold">
|
||||
{% trans "Most used buttons" %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-body text-nowrap" style="overflow:auto hidden">
|
||||
<div class="d-inline-flex flex-wrap justify-content-center" id="most_used">
|
||||
{% for button in most_used %}
|
||||
{% if button.display %}
|
||||
<button class="btn btn-outline-dark rounded-0 flex-fill"
|
||||
id="most_used_button{{ button.id }}" name="button" value="{{ button.name }}">
|
||||
{{ button.name }} ({{ button.amount | pretty_money }})
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Regroup buttons under categories #}
|
||||
{% regroup transaction_templates by template_type as template_types %}
|
||||
{% regroup transaction_templates by category as categories %}
|
||||
|
||||
<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 %}
|
||||
{% for category in categories %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link font-weight-bold" data-toggle="tab" href="#{{ template_type.grouper|slugify }}">
|
||||
{{ template_type.grouper }}
|
||||
<a class="nav-link font-weight-bold" data-toggle="tab" href="#{{ category.grouper|slugify }}">
|
||||
{{ category.grouper }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
@ -77,14 +98,16 @@
|
||||
{# 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 }}">
|
||||
{% 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 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>
|
||||
{% for button in category.list %}
|
||||
{% if button.display %}
|
||||
<button class="btn btn-outline-dark rounded-0 flex-fill"
|
||||
id="button{{ button.id }}" name="button" value="{{ button.name }}">
|
||||
{{ button.name }} ({{ button.amount | pretty_money }})
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@ -95,16 +118,16 @@
|
||||
{# 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
|
||||
<i class="fa fa-edit"></i> {% trans "Edit" %}
|
||||
</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 for="single_conso" class="btn btn-sm btn-outline-primary active">
|
||||
<input type="radio" name="conso_type" id="single_conso" checked>
|
||||
{% trans "Single consumptions" %}
|
||||
</label>
|
||||
<label class="btn btn-sm btn-outline-primary">
|
||||
<input type="radio" name="options" id="option2">
|
||||
Consomations doubles
|
||||
<label for="double_conso" class="btn btn-sm btn-outline-primary">
|
||||
<input type="radio" name="conso_type" id="double_conso">
|
||||
{% trans "Double consumptions" %}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -112,40 +135,37 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card shadow mb-4" id="history">
|
||||
<div class="card-header">
|
||||
<p class="card-text font-weight-bold">
|
||||
Historique des transactions récentes
|
||||
{% trans "Recent transactions history" %}
|
||||
</p>
|
||||
</div>
|
||||
{% render_table table %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extracss %}
|
||||
<style>
|
||||
.select2-container{
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script type="text/javascript" src="/static/js/consos.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
// If hash of a category in the URL, then select this category
|
||||
// else select the first one
|
||||
if (location.hash) {
|
||||
$("a[href='" + location.hash + "']").tab("show");
|
||||
} else {
|
||||
$("a[data-toggle='tab']").first().tab("show");
|
||||
}
|
||||
{% for button in most_used %}
|
||||
{% if button.display %}
|
||||
$("#most_used_button{{ button.id }}").click(function() {
|
||||
addConso({{ button.destination.id }}, {{ button.amount }},
|
||||
{{ polymorphic_ctype }}, {{ button.category.id }}, "{{ button.category.name }}",
|
||||
{{ button.id }}, "{{ button.name }}");
|
||||
});
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
// When selecting a category, change URL
|
||||
$(document.body).on("click", "a[data-toggle='tab']", function(event) {
|
||||
location.hash = this.getAttribute("href");
|
||||
});
|
||||
});
|
||||
{% for button in transaction_templates %}
|
||||
{% if button.display %}
|
||||
$("#button{{ button.id }}").click(function() {
|
||||
addConso({{ button.destination.id }}, {{ button.amount }},
|
||||
{{ polymorphic_ctype }}, {{ button.category.id }}, "{{ button.category.name }}",
|
||||
{{ button.id }}, "{{ button.name }}");
|
||||
});
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -3,35 +3,188 @@
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load i18n static %}
|
||||
{% load i18n static django_tables2 %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post" onsubmit="window.onbeforeunload=null">{% csrf_token %}
|
||||
{% if form.non_field_errors %}
|
||||
<p class="errornote">
|
||||
{% for error in form.non_field_errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endif %}
|
||||
<fieldset class="module aligned">
|
||||
{% 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">
|
||||
<div class="col-xl-12">
|
||||
<div class="btn-group btn-group-toggle" style="width: 100%; padding: 0 0 2em 0" data-toggle="buttons">
|
||||
<label for="type_gift" class="btn btn-sm btn-outline-primary active">
|
||||
<input type="radio" name="transaction_type" id="type_gift" checked>
|
||||
{% trans "Gift" %}
|
||||
</label>
|
||||
<label for="type_transfer" class="btn btn-sm btn-outline-primary">
|
||||
<input type="radio" name="transaction_type" id="type_transfer">
|
||||
{% trans "Transfer" %}
|
||||
</label>
|
||||
<label for="type_credit" class="btn btn-sm btn-outline-primary">
|
||||
<input type="radio" name="transaction_type" id="type_credit">
|
||||
{% trans "Credit" %}
|
||||
</label>
|
||||
<label type="type_debit" class="btn btn-sm btn-outline-primary">
|
||||
<input type="radio" name="transaction_type" id="type_debit">
|
||||
{% trans "Debit" %}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4" id="emitters_div" style="display: none;">
|
||||
<div class="card border-success shadow mb-4">
|
||||
<div class="card-header">
|
||||
<p class="card-text font-weight-bold">
|
||||
{% trans "Select emitters" %}
|
||||
</p>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush" id="source_note_list">
|
||||
</ul>
|
||||
<div class="card-body">
|
||||
<input class="form-control mx-auto d-block" type="text" id="source_note" />
|
||||
<ul class="list-group list-group-flush" id="source_alias_matched">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4" id="note_infos_div">
|
||||
<div class="card border-success shadow mb-4">
|
||||
<img src="/media/pic/default.png"
|
||||
id="profile_pic" alt="" class="img-fluid rounded mx-auto d-block">
|
||||
<div class="card-body text-center">
|
||||
<span id="user_note"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4" 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">
|
||||
{% trans "External payment" %}
|
||||
</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">{% trans "Transfer type" %} :</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">{% trans "Name" %} :</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">{% trans "First name" %} :</label>
|
||||
<input type="text" id="first_name" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="col-md-12">
|
||||
<label for="bank">{% trans "Bank" %} :</label>
|
||||
<input type="text" id="bank" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
<input type="submit" value="{% trans 'Transfer' %}">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8" id="dests_div">
|
||||
<div class="card border-info shadow mb-4">
|
||||
<div class="card-header">
|
||||
<p class="card-text font-weight-bold" id="dest_title">
|
||||
{% trans "Select receivers" %}
|
||||
</p>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush" id="dest_note_list">
|
||||
</ul>
|
||||
<div class="card-body">
|
||||
<input class="form-control mx-auto d-block" type="text" id="dest_note" />
|
||||
<ul class="list-group list-group-flush" id="dest_alias_matched">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="amount">{% trans "Amount" %} :</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control mx-auto d-block" type="number" min="0" step="0.01" id="amount" />
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text">€</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group col-md-6">
|
||||
<label for="reason">{% trans "Reason" %} :</label>
|
||||
<input class="form-control mx-auto d-block" type="text" id="reason" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="col-md-12">
|
||||
<button id="transfer" class="form-control btn btn-primary">{% trans 'Transfer' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow mb-4" id="history">
|
||||
<div class="card-header">
|
||||
<p class="card-text font-weight-bold">
|
||||
{% trans "Recent transactions history" %}
|
||||
</p>
|
||||
</div>
|
||||
{% render_table table %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script>
|
||||
TRANSFER_POLYMORPHIC_CTYPE = {{ polymorphic_ctype }};
|
||||
SPECIAL_TRANSFER_POLYMORPHIC_CTYPE = {{ special_polymorphic_ctype }};
|
||||
user_id = {{ user.note.pk }};
|
||||
|
||||
$("#type_gift").click(function() {
|
||||
$("#emitters_div").hide();
|
||||
$("#external_div").hide();
|
||||
$("#dests_div").attr('class', 'col-md-8');
|
||||
$("#dest_title").text("{% trans "Select receivers" %}");
|
||||
});
|
||||
|
||||
$("#type_transfer").click(function() {
|
||||
$("#emitters_div").show();
|
||||
$("#external_div").hide();
|
||||
$("#dests_div").attr('class', 'col-md-4');
|
||||
$("#dest_title").text("{% trans "Select receivers" %}");
|
||||
});
|
||||
|
||||
$("#type_credit").click(function() {
|
||||
$("#emitters_div").hide();
|
||||
$("#external_div").show();
|
||||
$("#dests_div").attr('class', 'col-md-4');
|
||||
$("#dest_title").text("{% trans "Credit note" %}");
|
||||
});
|
||||
|
||||
$("#type_debit").click(function() {
|
||||
$("#emitters_div").hide();
|
||||
$("#external_div").show();
|
||||
$("#dests_div").attr('class', 'col-md-4');
|
||||
$("#dest_title").text("{% trans "Debit note" %}");
|
||||
});
|
||||
</script>
|
||||
<script src="/static/js/transfer.js"></script>
|
||||
{% endblock %}
|
||||
|
@ -15,7 +15,7 @@
|
||||
<td><a href="{{object.get_absolute_url}}">{{ object.name }}</a></td>
|
||||
<td>{{ object.destination }}</td>
|
||||
<td>{{ object.amount | pretty_money }}</td>
|
||||
<td>{{ object.template_type }}</td>
|
||||
<td>{{ object.category }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
Loading…
Reference in New Issue
Block a user