Merge remote-tracking branch 'origin/master' into activity

This commit is contained in:
Yohann D'ANELLO 2020-03-27 14:16:10 +01:00
commit dadd840645
28 changed files with 579 additions and 268 deletions

View File

@ -4,6 +4,7 @@
import datetime import datetime
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -67,6 +68,13 @@ class Club(models.Model):
email = models.EmailField( email = models.EmailField(
verbose_name=_('email'), verbose_name=_('email'),
) )
parent_club = models.ForeignKey(
'self',
null=True,
blank=True,
on_delete=models.PROTECT,
verbose_name=_('parent club'),
)
# Memberships # Memberships
membership_fee = models.PositiveIntegerField( membership_fee = models.PositiveIntegerField(
@ -158,6 +166,12 @@ class Membership(models.Model):
else: else:
return self.date_start.toordinal() <= datetime.datetime.now().toordinal() return self.date_start.toordinal() <= datetime.datetime.now().toordinal()
def save(self, *args, **kwargs):
if self.club.parent_club is not None:
if not Membership.objects.filter(user=self.user, club=self.club.parent_club):
raise ValidationError(_('User is not a member of the parent club'))
super().save(*args, **kwargs)
class Meta: class Meta:
verbose_name = _('membership') verbose_name = _('membership')
verbose_name_plural = _('memberships') verbose_name_plural = _('memberships')

View File

@ -14,12 +14,12 @@ urlpatterns = [
path('club/create/', views.ClubCreateView.as_view(), name="club_create"), path('club/create/', views.ClubCreateView.as_view(), name="club_create"),
path('club/<int:pk>/update', views.ClubUpdateView.as_view(), name="club_update"), path('club/<int:pk>/update', views.ClubUpdateView.as_view(), name="club_update"),
path('club/<int:pk>/update_pic', views.ClubPictureUpdateView.as_view(), name="club_update_pic"), path('club/<int:pk>/update_pic', views.ClubPictureUpdateView.as_view(), name="club_update_pic"),
path('club/<int:pk>/aliases', views.ClubAliasView.as_view(), name="club_alias"),
path('user/', views.UserListView.as_view(), name="user_list"), path('user/', views.UserListView.as_view(), name="user_list"),
path('user/<int:pk>', views.UserDetailView.as_view(), name="user_detail"), path('user/<int:pk>', views.UserDetailView.as_view(), name="user_detail"),
path('user/<int:pk>/update', views.UserUpdateView.as_view(), name="user_update_profile"), path('user/<int:pk>/update', views.UserUpdateView.as_view(), name="user_update_profile"),
path('user/<int:pk>/update_pic', views.ProfilePictureUpdateView.as_view(), name="user_update_pic"), path('user/<int:pk>/update_pic', views.ProfilePictureUpdateView.as_view(), name="user_update_pic"),
path('user/<int:pk>/aliases', views.AliasView.as_view(), name="user_alias"), path('user/<int:pk>/aliases', views.ProfileAliasView.as_view(), name="user_alias"),
path('user/aliases/delete/<int:pk>', views.DeleteAliasView.as_view(), name="user_alias_delete"),
path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'), path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'),
# API for the user autocompleter # API for the user autocompleter
path('user/user-autocomplete', views.UserAutocomplete.as_view(), name="user_autocomplete"), path('user/user-autocomplete', views.UserAutocomplete.as_view(), name="user_autocomplete"),

View File

@ -20,7 +20,8 @@ from django.views.generic import CreateView, DetailView, UpdateView, TemplateVie
from django.views.generic.edit import FormMixin from django.views.generic.edit import FormMixin
from django_tables2.views import SingleTableView from django_tables2.views import SingleTableView
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from note.forms import AliasForm, ImageForm from note.forms import ImageForm
#from note.forms import AliasForm, ImageForm
from note.models import Alias, NoteUser from note.models import Alias, NoteUser
from note.models.transactions import Transaction from note.models.transactions import Transaction
from note.tables import HistoryTable, AliasTable from note.tables import HistoryTable, AliasTable
@ -143,10 +144,6 @@ class UserDetailView(LoginRequiredMixin, DetailView):
club_list = \ club_list = \
Membership.objects.all().filter(user=user).only("club") Membership.objects.all().filter(user=user).only("club")
context['club_list'] = ClubTable(club_list) context['club_list'] = ClubTable(club_list)
context['title'] = _("Account #%(id)s: %(username)s") % {
'id': user.pk,
'username': user.username,
}
return context return context
@ -172,56 +169,17 @@ class UserListView(LoginRequiredMixin, SingleTableView):
return context return context
class AliasView(LoginRequiredMixin, FormMixin, DetailView): class ProfileAliasView(LoginRequiredMixin, DetailView):
model = User model = User
template_name = 'member/profile_alias.html' template_name = 'member/profile_alias.html'
context_object_name = 'user_object' context_object_name = 'user_object'
form_class = AliasForm
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
note = context['user_object'].note note = context['object'].note
context["aliases"] = AliasTable(note.alias_set.all()) context["aliases"] = AliasTable(note.alias_set.all())
return context return context
def get_success_url(self):
return reverse_lazy('member:user_alias', kwargs={'pk': self.object.id})
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
alias = form.save(commit=False)
alias.note = self.object.note
alias.save()
return super().form_valid(form)
class DeleteAliasView(LoginRequiredMixin, DeleteView):
model = Alias
def delete(self, request, *args, **kwargs):
try:
self.object = self.get_object()
self.object.delete()
except ValidationError as e:
# TODO: pass message to redirected view.
messages.error(self.request, str(e))
else:
messages.success(self.request, _("Alias successfully deleted"))
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
return reverse_lazy('member:user_alias', kwargs={'pk': self.object.note.user.pk})
def get(self, request, *args, **kwargs):
return self.post(request, *args, **kwargs)
class PictureUpdateView(LoginRequiredMixin, FormMixin, DetailView): class PictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
form_class = ImageForm form_class = ImageForm
@ -368,6 +326,17 @@ class ClubDetailView(LoginRequiredMixin, DetailView):
context['member_list'] = club_member context['member_list'] = club_member
return context return context
class ClubAliasView(LoginRequiredMixin, DetailView):
model = Club
template_name = 'member/club_alias.html'
context_object_name = 'club'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
note = context['object'].note
context["aliases"] = AliasTable(note.alias_set.all())
return context
class ClubUpdateView(LoginRequiredMixin, UpdateView): class ClubUpdateView(LoginRequiredMixin, UpdateView):
model = Club model = Club
@ -395,12 +364,12 @@ class ClubAddMemberView(LoginRequiredMixin, CreateView):
return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view") return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")
| PermissionBackend.filter_queryset(self.request.user, Membership, | PermissionBackend.filter_queryset(self.request.user, Membership,
"change")) "change"))
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
club = Club.objects.get(pk=self.kwargs["pk"])
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['formset'] = MemberFormSet() context['formset'] = MemberFormSet()
context['helper'] = FormSetHelper() context['helper'] = FormSetHelper()
context['club'] = club
context['no_cache'] = True context['no_cache'] = True
return context return context

View File

@ -78,7 +78,11 @@ class AliasSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Alias model = Alias
fields = '__all__' fields = '__all__'
read_only_fields = ('note', )
def validate(self, attrs):
instance = Alias(**attrs)
instance.clean()
return attrs
class NotePolymorphicSerializer(PolymorphicSerializer): class NotePolymorphicSerializer(PolymorphicSerializer):

View File

@ -2,10 +2,14 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.db.models import Q from django.db.models import Q
from django.core.exceptions import ValidationError
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.filters import OrderingFilter, SearchFilter
from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import status
from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet
from .serializers import NotePolymorphicSerializer, AliasSerializer, TemplateCategorySerializer, \ from .serializers import NotePolymorphicSerializer, AliasSerializer, TemplateCategorySerializer, \
TransactionTemplateSerializer, TransactionPolymorphicSerializer TransactionTemplateSerializer, TransactionPolymorphicSerializer
@ -53,6 +57,22 @@ class AliasViewSet(ReadProtectedModelViewSet):
search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ] search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
ordering_fields = ['name', 'normalized_name'] ordering_fields = ['name', 'normalized_name']
def get_serializer_class(self):
serializer_class = self.serializer_class
if self.request.method in ['PUT', 'PATCH']:
#alias owner cannot be change once establish
setattr(serializer_class.Meta, 'read_only_fields', ('note',))
return serializer_class
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
try:
self.perform_destroy(instance)
except ValidationError as e:
print(e)
return Response({e.code:e.message},status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_204_NO_CONTENT)
def get_queryset(self): def get_queryset(self):
""" """
Parse query and apply filters. Parse query and apply filters.

View File

@ -0,0 +1,58 @@
[
{
"model": "note.templatecategory",
"pk": 1,
"fields": {
"name": "Soft"
}
},
{
"model": "note.templatecategory",
"pk": 2,
"fields": {
"name": "Pulls"
}
},
{
"model": "note.templatecategory",
"pk": 3,
"fields": {
"name": "Gala"
}
},
{
"model": "note.templatecategory",
"pk": 4,
"fields": {
"name": "Clubs"
}
},
{
"model": "note.templatecategory",
"pk": 5,
"fields": {
"name": "Bouffe"
}
},
{
"model": "note.templatecategory",
"pk": 6,
"fields": {
"name": "BDA"
}
},
{
"model": "note.templatecategory",
"pk": 7,
"fields": {
"name": "Autre"
}
},
{
"model": "note.templatecategory",
"pk": 8,
"fields": {
"name": "Alcool"
}
}
]

View File

@ -184,61 +184,5 @@
"normalized_name": "kfet", "normalized_name": "kfet",
"note": 6 "note": 6
} }
},
{
"model": "note.templatecategory",
"pk": 1,
"fields": {
"name": "Soft"
}
},
{
"model": "note.templatecategory",
"pk": 2,
"fields": {
"name": "Pulls"
}
},
{
"model": "note.templatecategory",
"pk": 3,
"fields": {
"name": "Gala"
}
},
{
"model": "note.templatecategory",
"pk": 4,
"fields": {
"name": "Clubs"
}
},
{
"model": "note.templatecategory",
"pk": 5,
"fields": {
"name": "Bouffe"
}
},
{
"model": "note.templatecategory",
"pk": 6,
"fields": {
"name": "BDA"
}
},
{
"model": "note.templatecategory",
"pk": 7,
"fields": {
"name": "Autre"
}
},
{
"model": "note.templatecategory",
"pk": 8,
"fields": {
"name": "Alcool"
}
} }
] ]

View File

@ -9,17 +9,6 @@ from .models import Alias
from .models import TransactionTemplate from .models import TransactionTemplate
class AliasForm(forms.ModelForm):
class Meta:
model = Alias
fields = ("name",)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["name"].label = False
self.fields["name"].widget.attrs = {"placeholder": _('New Alias')}
class ImageForm(forms.Form): class ImageForm(forms.Form):
image = forms.ImageField(required=False, image = forms.ImageField(required=False,
label=_('select an image'), label=_('select an image'),

View File

@ -228,7 +228,7 @@ class Alias(models.Model):
for cat in {'M', 'P', 'Z', 'C'})).casefold() for cat in {'M', 'P', 'Z', 'C'})).casefold()
def clean(self): def clean(self):
normalized_name = Alias.normalize(self.name) normalized_name = self.normalize(self.name)
if len(normalized_name) >= 255: if len(normalized_name) >= 255:
raise ValidationError(_('Alias is too long.'), raise ValidationError(_('Alias is too long.'),
code='alias_too_long') code='alias_too_long')
@ -242,8 +242,12 @@ class Alias(models.Model):
pass pass
self.normalized_name = normalized_name self.normalized_name = normalized_name
def save(self,*args,**kwargs):
self.normalized_name = self.normalize(self.name)
super().save(*args,**kwargs)
def delete(self, using=None, keep_parents=False): def delete(self, using=None, keep_parents=False):
if self.name == str(self.note): if self.name == str(self.note):
raise ValidationError(_("You can't delete your main alias."), raise ValidationError(_("You can't delete your main alias."),
code="cant_delete_main_alias") code="main_alias")
return super().delete(using, keep_parents) return super().delete(using, keep_parents)

View File

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.db import models from django.db import models
from django.db.models import F
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -93,12 +94,26 @@ class Transaction(PolymorphicModel):
related_name='+', related_name='+',
verbose_name=_('source'), verbose_name=_('source'),
) )
source_alias = models.CharField(
max_length=255,
default="", # Will be remplaced by the name of the note on save
verbose_name=_('used alias'),
)
destination = models.ForeignKey( destination = models.ForeignKey(
Note, Note,
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='+', related_name='+',
verbose_name=_('destination'), verbose_name=_('destination'),
) )
destination_alias = models.CharField(
max_length=255,
default="", # Will be remplaced by the name of the note on save
verbose_name=_('used alias'),
)
created_at = models.DateTimeField( created_at = models.DateTimeField(
verbose_name=_('created at'), verbose_name=_('created at'),
default=timezone.now, default=timezone.now,
@ -115,11 +130,19 @@ class Transaction(PolymorphicModel):
verbose_name=_('reason'), verbose_name=_('reason'),
max_length=255, max_length=255,
) )
valid = models.BooleanField( valid = models.BooleanField(
verbose_name=_('valid'), verbose_name=_('valid'),
default=True, default=True,
) )
invalidity_reason = models.CharField(
verbose_name=_('invalidity reason'),
max_length=255,
default=None,
null=True,
)
class Meta: class Meta:
verbose_name = _("transaction") verbose_name = _("transaction")
verbose_name_plural = _("transactions") verbose_name_plural = _("transactions")
@ -134,6 +157,13 @@ class Transaction(PolymorphicModel):
When saving, also transfer money between two notes When saving, also transfer money between two notes
""" """
# If the aliases are not entered, we assume that the used alias is the name of the note
if not self.source_alias:
self.source_alias = str(self.source)
if not self.destination_alias:
self.destination_alias = str(self.destination)
if self.source.pk == self.destination.pk: if self.source.pk == self.destination.pk:
# When source == destination, no money is transfered # When source == destination, no money is transfered
super().save(*args, **kwargs) super().save(*args, **kwargs)
@ -152,6 +182,10 @@ class Transaction(PolymorphicModel):
self.source.balance -= to_transfer self.source.balance -= to_transfer
self.destination.balance += to_transfer self.destination.balance += to_transfer
# When a transaction is declared valid, we ensure that the invalidity reason is null, if it was
# previously invalid
self.invalidity_reason = None
# We save first the transaction, in case of the user has no right to transfer money # We save first the transaction, in case of the user has no right to transfer money
super().save(*args, **kwargs) super().save(*args, **kwargs)

View File

@ -5,6 +5,7 @@ import html
import django_tables2 as tables import django_tables2 as tables
from django.db.models import F from django.db.models import F
from django.utils.html import format_html
from django_tables2.utils import A from django_tables2.utils import A
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -20,19 +21,48 @@ class HistoryTable(tables.Table):
'table table-condensed table-striped table-hover' 'table table-condensed table-striped table-hover'
} }
model = Transaction model = Transaction
exclude = ("id", "polymorphic_ctype", ) exclude = ("id", "polymorphic_ctype", "invalidity_reason", "source_alias", "destination_alias",)
template_name = 'django_tables2/bootstrap4.html' template_name = 'django_tables2/bootstrap4.html'
sequence = ('...', 'type', 'total', 'valid', ) sequence = ('...', 'type', 'total', 'valid',)
orderable = False orderable = False
source = tables.Column(
attrs={
"td": {
"data-toggle": "tooltip",
"title": lambda record: _("used alias").capitalize() + " : " + record.source_alias,
}
}
)
destination = tables.Column(
attrs={
"td": {
"data-toggle": "tooltip",
"title": lambda record: _("used alias").capitalize() + " : " + record.destination_alias,
}
}
)
type = tables.Column() type = tables.Column()
total = tables.Column() # will use Transaction.total() !! total = tables.Column() # will use Transaction.total() !!
valid = tables.Column(attrs={"td": {"id": lambda record: "validate_" + str(record.id), valid = tables.Column(
"class": lambda record: str(record.valid).lower() + ' validate', attrs={
"onclick": lambda record: 'de_validate(' + str(record.id) + ', ' "td": {
+ str(record.valid).lower() + ')'}}) "id": lambda record: "validate_" + str(record.id),
"class": lambda record: str(record.valid).lower() + ' validate',
"data-toggle": "tooltip",
"title": lambda record: _("Click to invalidate") if record.valid else _("Click to validate"),
"onclick": lambda record: 'in_validate(' + str(record.id) + ', ' + str(record.valid).lower() + ')',
"onmouseover": lambda record: '$("#invalidity_reason_'
+ str(record.id) + '").show();$("#invalidity_reason_'
+ str(record.id) + '").focus();',
"onmouseout": lambda record: '$("#invalidity_reason_' + str(record.id) + '").hide()',
}
}
)
def order_total(self, queryset, is_descending): def order_total(self, queryset, is_descending):
# needed for rendering # needed for rendering
@ -53,13 +83,23 @@ class HistoryTable(tables.Table):
def render_reason(self, value): def render_reason(self, value):
return html.unescape(value) return html.unescape(value)
def render_valid(self, value): def render_valid(self, value, record):
return "" if value else "" """
When the validation status is hovered, an input field is displayed to let the user specify an invalidity reason
"""
val = "" if value else ""
val += "<input type='text' class='form-control' id='invalidity_reason_" + str(record.id) \
+ "' value='" + (html.escape(record.invalidity_reason)
if record.invalidity_reason else ("" if value else str(_("No reason specified")))) \
+ "'" + ("" if value else " disabled") \
+ " placeholder='" + html.escape(_("invalidity reason").capitalize()) + "'" \
+ " style='position: absolute; width: 15em; margin-left: -15.5em; margin-top: -2em; display: none;'>"
return format_html(val)
# function delete_button(id) provided in template file # function delete_button(id) provided in template file
DELETE_TEMPLATE = """ DELETE_TEMPLATE = """
<button id="{{ record.pk }}" class="btn btn-danger" onclick="delete_button(this.id)"> {{ delete_trans }}</button> <button id="{{ record.pk }}" class="btn btn-danger btn-sm" onclick="delete_button(this.id)"> {{ delete_trans }}</button>
""" """
@ -67,7 +107,8 @@ class AliasTable(tables.Table):
class Meta: class Meta:
attrs = { attrs = {
'class': 'class':
'table table condensed table-striped table-hover' 'table table condensed table-striped table-hover',
'id':"alias_table"
} }
model = Alias model = Alias
fields = ('name',) fields = ('name',)
@ -75,15 +116,11 @@ class AliasTable(tables.Table):
show_header = False show_header = False
name = tables.Column(attrs={'td': {'class': 'text-center'}}) name = tables.Column(attrs={'td': {'class': 'text-center'}})
# delete = tables.TemplateColumn(template_code=delete_template,
# attrs={'td':{'class': 'col-sm-1'}})
delete = tables.LinkColumn('member:user_alias_delete', delete_col = tables.TemplateColumn(template_code=DELETE_TEMPLATE,
args=[A('pk')], extra_context={"delete_trans": _('delete')},
attrs={ attrs={'td': {'class': 'col-sm-1'}})
'td': {'class': 'col-sm-2'},
'a': {'class': 'btn btn-danger'}},
text='delete', accessor='pk')
class ButtonTable(tables.Table): class ButtonTable(tables.Table):
@ -103,11 +140,11 @@ class ButtonTable(tables.Table):
edit = tables.LinkColumn('note:template_update', edit = tables.LinkColumn('note:template_update',
args=[A('pk')], args=[A('pk')],
attrs={'td': {'class': 'col-sm-1'}, attrs={'td': {'class': 'col-sm-1'},
'a': {'class': 'btn btn-primary'}}, 'a': {'class': 'btn btn-sm btn-primary'}},
text=_('edit'), text=_('edit'),
accessor='pk') accessor='pk')
delete = tables.TemplateColumn(template_code=DELETE_TEMPLATE, delete_col = tables.TemplateColumn(template_code=DELETE_TEMPLATE,
extra_context={"delete_trans": _('delete')}, extra_context={"delete_trans": _('delete')},
attrs={'td': {'class': 'col-sm-1'}}) attrs={'td': {'class': 'col-sm-1'}})

View File

@ -327,7 +327,7 @@
"note", "note",
"transaction" "transaction"
], ],
"query": "[\"AND\", {\"source\": [\"user\", \"note\"]}, {\"amount__lte\": [\"user\", \"note\", \"balance\"]}]", "query": "[\"AND\", {\"source\": [\"user\", \"note\"]}, [\"OR\", {\"amount__lte\": [\"user\", \"note\", \"balance\"]}, {\"valid\": false}]]",
"type": "add", "type": "add",
"mask": 1, "mask": 1,
"field": "", "field": "",
@ -387,7 +387,7 @@
"note", "note",
"recurrenttransaction" "recurrenttransaction"
], ],
"query": "[\"AND\", {\"destination\": [\"club\", \"note\"]}, {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}]", "query": "[\"AND\", {\"destination\": [\"club\", \"note\"]}, [\"OR\", {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}, {\"valid\": false}]]",
"type": "add", "type": "add",
"mask": 2, "mask": 2,
"field": "", "field": "",

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-24 15:49+0100\n" "POT-Creation-Date: 2020-03-26 14:40+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -24,8 +24,8 @@ msgstr ""
#: apps/activity/models.py:19 apps/activity/models.py:44 #: apps/activity/models.py:19 apps/activity/models.py:44
#: apps/member/models.py:63 apps/member/models.py:114 #: apps/member/models.py:63 apps/member/models.py:114
#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:25
#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:198 #: apps/note/models/transactions.py:45 apps/note/models/transactions.py:232
#: templates/member/profile_detail.html:15 #: templates/member/profile_detail.html:15
msgid "name" msgid "name"
msgstr "" msgstr ""
@ -46,13 +46,13 @@ msgstr ""
msgid "activity types" msgid "activity types"
msgstr "" msgstr ""
#: apps/activity/models.py:48 apps/note/models/transactions.py:69 #: apps/activity/models.py:48 apps/note/models/transactions.py:70
#: apps/permission/models.py:90 #: apps/permission/models.py:91
msgid "description" msgid "description"
msgstr "" msgstr ""
#: apps/activity/models.py:54 apps/note/models/notes.py:164 #: apps/activity/models.py:54 apps/note/models/notes.py:164
#: apps/note/models/transactions.py:62 #: apps/note/models/transactions.py:63
msgid "type" msgid "type"
msgstr "" msgstr ""
@ -120,11 +120,11 @@ msgstr ""
msgid "create" msgid "create"
msgstr "" msgstr ""
#: apps/logs/models.py:61 #: apps/logs/models.py:61 apps/note/tables.py:147
msgid "edit" msgid "edit"
msgstr "" msgstr ""
#: apps/logs/models.py:62 #: apps/logs/models.py:62 apps/note/tables.py:151
msgid "delete" msgid "delete"
msgstr "" msgstr ""
@ -210,7 +210,7 @@ msgstr ""
msgid "clubs" msgid "clubs"
msgstr "" msgstr ""
#: apps/member/models.py:120 apps/permission/models.py:275 #: apps/member/models.py:120 apps/permission/models.py:276
msgid "role" msgid "role"
msgstr "" msgstr ""
@ -255,12 +255,12 @@ msgstr ""
msgid "Alias successfully deleted" msgid "Alias successfully deleted"
msgstr "" msgstr ""
#: apps/note/admin.py:120 apps/note/models/transactions.py:94 #: apps/note/admin.py:120 apps/note/models/transactions.py:95
msgid "source" msgid "source"
msgstr "" msgstr ""
#: apps/note/admin.py:128 apps/note/admin.py:156 #: apps/note/admin.py:128 apps/note/admin.py:156
#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:100 #: apps/note/models/transactions.py:54 apps/note/models/transactions.py:108
msgid "destination" msgid "destination"
msgstr "" msgstr ""
@ -310,7 +310,7 @@ msgstr ""
msgid "display image" msgid "display image"
msgstr "" msgstr ""
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:103 #: apps/note/models/notes.py:53 apps/note/models/transactions.py:118
msgid "created at" msgid "created at"
msgstr "" msgstr ""
@ -384,55 +384,64 @@ msgstr ""
msgid "You can't delete your main alias." msgid "You can't delete your main alias."
msgstr "" msgstr ""
#: apps/note/models/transactions.py:30 #: apps/note/models/transactions.py:31
msgid "transaction category" msgid "transaction category"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:31 #: apps/note/models/transactions.py:32
msgid "transaction categories" msgid "transaction categories"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:47 #: apps/note/models/transactions.py:48
msgid "A template with this name already exist" msgid "A template with this name already exist"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:111 #: apps/note/models/transactions.py:57 apps/note/models/transactions.py:126
msgid "amount" msgid "amount"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:57 #: apps/note/models/transactions.py:58
msgid "in centimes" msgid "in centimes"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:75 #: apps/note/models/transactions.py:76
msgid "transaction template" msgid "transaction template"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:76 #: apps/note/models/transactions.py:77
msgid "transaction templates" msgid "transaction templates"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:107 #: apps/note/models/transactions.py:101 apps/note/models/transactions.py:114
#: apps/note/tables.py:33 apps/note/tables.py:42
msgid "used alias"
msgstr ""
#: apps/note/models/transactions.py:122
msgid "quantity" msgid "quantity"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:115 #: apps/note/models/transactions.py:130
msgid "reason" msgid "reason"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:119 #: apps/note/models/transactions.py:135
msgid "valid" msgid "valid"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:124 #: apps/note/models/transactions.py:140 apps/note/tables.py:95
msgid "invalidity reason"
msgstr ""
#: apps/note/models/transactions.py:147
msgid "transaction" msgid "transaction"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:125 #: apps/note/models/transactions.py:148
msgid "transactions" msgid "transactions"
msgstr "" msgstr ""
#: apps/note/models/transactions.py:168 templates/base.html:98 #: apps/note/models/transactions.py:202 templates/base.html:83
#: templates/note/transaction_form.html:19 #: templates/note/transaction_form.html:19
#: templates/note/transaction_form.html:145 #: templates/note/transaction_form.html:145
msgid "Transfer" msgid "Transfer"
@ -634,15 +643,15 @@ msgid ""
"again unless your session expires or you logout." "again unless your session expires or you logout."
msgstr "" msgstr ""
#: note_kfet/settings/base.py:153 #: note_kfet/settings/base.py:151
msgid "German" msgid "German"
msgstr "" msgstr ""
#: note_kfet/settings/base.py:154 #: note_kfet/settings/base.py:152
msgid "English" msgid "English"
msgstr "" msgstr ""
#: note_kfet/settings/base.py:155 #: note_kfet/settings/base.py:153
msgid "French" msgid "French"
msgstr "" msgstr ""
@ -650,18 +659,14 @@ msgstr ""
msgid "The ENS Paris-Saclay BDE note." msgid "The ENS Paris-Saclay BDE note."
msgstr "" msgstr ""
#: templates/base.html:84 #: templates/base.html:87
msgid "Clubs" msgid "Clubs"
msgstr "" msgstr ""
#: templates/base.html:89 #: templates/base.html:92
msgid "Activities" msgid "Activities"
msgstr "" msgstr ""
#: templates/base.html:94
msgid "Buttons"
msgstr ""
#: templates/cas_server/base.html:7 #: templates/cas_server/base.html:7
msgid "Central Authentication Service" msgid "Central Authentication Service"
msgstr "" msgstr ""
@ -798,7 +803,7 @@ msgstr ""
msgid "Sign up" msgid "Sign up"
msgstr "" msgstr ""
#: templates/note/conso_form.html:28 templates/note/transaction_form.html:40 #: templates/note/conso_form.html:28 templates/note/transaction_form.html:50
msgid "Select emitters" msgid "Select emitters"
msgstr "" msgstr ""
@ -842,12 +847,28 @@ msgstr ""
msgid "Transfer type" msgid "Transfer type"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:86
msgid "Name"
msgstr ""
#: templates/note/transaction_form.html:92
msgid "First name"
msgstr ""
#: templates/note/transaction_form.html:98
msgid "Bank"
msgstr ""
#: templates/note/transaction_form.html:111 #: templates/note/transaction_form.html:111
#: templates/note/transaction_form.html:169 #: templates/note/transaction_form.html:169
#: templates/note/transaction_form.html:176 #: templates/note/transaction_form.html:176
msgid "Select receivers" msgid "Select receivers"
msgstr "" msgstr ""
#: templates/note/transaction_form.html:128
msgid "Amount"
msgstr ""
#: templates/note/transaction_form.html:138 #: templates/note/transaction_form.html:138
msgid "Reason" msgid "Reason"
msgstr "" msgstr ""
@ -864,6 +885,22 @@ msgstr ""
msgid "Buttons list" msgid "Buttons list"
msgstr "" msgstr ""
#: templates/note/transactiontemplate_list.html:9
msgid "search button"
msgstr ""
#: templates/note/transactiontemplate_list.html:20
msgid "buttons listing "
msgstr ""
#: templates/note/transactiontemplate_list.html:71
msgid "button successfully deleted "
msgstr ""
#: templates/note/transactiontemplate_list.html:75
msgid "Unable to delete button "
msgstr ""
#: templates/registration/logged_out.html:8 #: templates/registration/logged_out.html:8
msgid "Thanks for spending some quality time with the Web site today." msgid "Thanks for spending some quality time with the Web site today."
msgstr "" msgstr ""
@ -873,7 +910,7 @@ msgid "Log in again"
msgstr "" msgstr ""
#: templates/registration/login.html:7 templates/registration/login.html:8 #: templates/registration/login.html:7 templates/registration/login.html:8
#: templates/registration/login.html:26 #: templates/registration/login.html:28
#: templates/registration/password_reset_complete.html:10 #: templates/registration/password_reset_complete.html:10
msgid "Log in" msgid "Log in"
msgstr "" msgstr ""
@ -885,7 +922,15 @@ msgid ""
"page. Would you like to login to a different account?" "page. Would you like to login to a different account?"
msgstr "" msgstr ""
#: templates/registration/login.html:27 #: templates/registration/login.html:22
msgid "You can also register via the central authentification server "
msgstr ""
#: templates/registration/login.html:23
msgid "using this link "
msgstr ""
#: templates/registration/login.html:29
msgid "Forgotten your password or username?" msgid "Forgotten your password or username?"
msgstr "" msgstr ""

View File

@ -3,7 +3,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-24 15:49+0100\n" "POT-Creation-Date: 2020-03-26 14:40+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -19,8 +19,8 @@ msgstr "activité"
#: apps/activity/models.py:19 apps/activity/models.py:44 #: apps/activity/models.py:19 apps/activity/models.py:44
#: apps/member/models.py:63 apps/member/models.py:114 #: apps/member/models.py:63 apps/member/models.py:114
#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 #: apps/note/models/notes.py:188 apps/note/models/transactions.py:25
#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:198 #: apps/note/models/transactions.py:45 apps/note/models/transactions.py:232
#: templates/member/profile_detail.html:15 #: templates/member/profile_detail.html:15
msgid "name" msgid "name"
msgstr "nom" msgstr "nom"
@ -41,13 +41,13 @@ msgstr "type d'activité"
msgid "activity types" msgid "activity types"
msgstr "types d'activité" msgstr "types d'activité"
#: apps/activity/models.py:48 apps/note/models/transactions.py:69 #: apps/activity/models.py:48 apps/note/models/transactions.py:70
#: apps/permission/models.py:90 #: apps/permission/models.py:91
msgid "description" msgid "description"
msgstr "description" msgstr "description"
#: apps/activity/models.py:54 apps/note/models/notes.py:164 #: apps/activity/models.py:54 apps/note/models/notes.py:164
#: apps/note/models/transactions.py:62 #: apps/note/models/transactions.py:63
msgid "type" msgid "type"
msgstr "type" msgstr "type"
@ -115,11 +115,11 @@ msgstr "Nouvelles données"
msgid "create" msgid "create"
msgstr "Créer" msgstr "Créer"
#: apps/logs/models.py:61 #: apps/logs/models.py:61 apps/note/tables.py:147
msgid "edit" msgid "edit"
msgstr "Modifier" msgstr "Modifier"
#: apps/logs/models.py:62 #: apps/logs/models.py:62 apps/note/tables.py:151
msgid "delete" msgid "delete"
msgstr "Supprimer" msgstr "Supprimer"
@ -209,7 +209,7 @@ msgstr "club"
msgid "clubs" msgid "clubs"
msgstr "clubs" msgstr "clubs"
#: apps/member/models.py:120 apps/permission/models.py:275 #: apps/member/models.py:120 apps/permission/models.py:276
msgid "role" msgid "role"
msgstr "rôle" msgstr "rôle"
@ -254,12 +254,12 @@ msgstr "Compte n°%(id)s : %(username)s"
msgid "Alias successfully deleted" msgid "Alias successfully deleted"
msgstr "L'alias a bien été supprimé" msgstr "L'alias a bien été supprimé"
#: apps/note/admin.py:120 apps/note/models/transactions.py:94 #: apps/note/admin.py:120 apps/note/models/transactions.py:95
msgid "source" msgid "source"
msgstr "source" msgstr "source"
#: apps/note/admin.py:128 apps/note/admin.py:156 #: apps/note/admin.py:128 apps/note/admin.py:156
#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:100 #: apps/note/models/transactions.py:54 apps/note/models/transactions.py:108
msgid "destination" msgid "destination"
msgstr "destination" msgstr "destination"
@ -310,7 +310,7 @@ msgstr ""
msgid "display image" msgid "display image"
msgstr "image affichée" msgstr "image affichée"
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:103 #: apps/note/models/notes.py:53 apps/note/models/transactions.py:118
msgid "created at" msgid "created at"
msgstr "créée le" msgstr "créée le"
@ -384,35 +384,40 @@ msgstr "Un alias avec un nom similaire existe déjà : {}"
msgid "You can't delete your main alias." msgid "You can't delete your main alias."
msgstr "Vous ne pouvez pas supprimer votre alias principal." msgstr "Vous ne pouvez pas supprimer votre alias principal."
#: apps/note/models/transactions.py:30 #: apps/note/models/transactions.py:31
msgid "transaction category" msgid "transaction category"
msgstr "catégorie de transaction" msgstr "catégorie de transaction"
#: apps/note/models/transactions.py:31 #: apps/note/models/transactions.py:32
msgid "transaction categories" msgid "transaction categories"
msgstr "catégories de transaction" msgstr "catégories de transaction"
#: apps/note/models/transactions.py:47 #: apps/note/models/transactions.py:48
msgid "A template with this name already exist" msgid "A template with this name already exist"
msgstr "Un modèle de transaction avec un nom similaire existe déjà." msgstr "Un modèle de transaction avec un nom similaire existe déjà."
#: apps/note/models/transactions.py:56 apps/note/models/transactions.py:111 #: apps/note/models/transactions.py:57 apps/note/models/transactions.py:126
msgid "amount" msgid "amount"
msgstr "montant" msgstr "montant"
#: apps/note/models/transactions.py:57 #: apps/note/models/transactions.py:58
msgid "in centimes" msgid "in centimes"
msgstr "en centimes" msgstr "en centimes"
#: apps/note/models/transactions.py:75 #: apps/note/models/transactions.py:76
msgid "transaction template" msgid "transaction template"
msgstr "modèle de transaction" msgstr "modèle de transaction"
#: apps/note/models/transactions.py:76 #: apps/note/models/transactions.py:77
msgid "transaction templates" msgid "transaction templates"
msgstr "modèles de transaction" msgstr "modèles de transaction"
#: apps/note/models/transactions.py:107 #: apps/note/models/transactions.py:101 apps/note/models/transactions.py:114
#: apps/note/tables.py:33 apps/note/tables.py:42
msgid "used alias"
msgstr "alias utilisé"
#: apps/note/models/transactions.py:122
msgid "quantity" msgid "quantity"
msgstr "quantité" msgstr "quantité"
@ -634,15 +639,15 @@ msgid ""
"again unless your session expires or you logout." "again unless your session expires or you logout."
msgstr "" msgstr ""
#: note_kfet/settings/base.py:153 #: note_kfet/settings/base.py:151
msgid "German" msgid "German"
msgstr "" msgstr ""
#: note_kfet/settings/base.py:154 #: note_kfet/settings/base.py:152
msgid "English" msgid "English"
msgstr "" msgstr ""
#: note_kfet/settings/base.py:155 #: note_kfet/settings/base.py:153
msgid "French" msgid "French"
msgstr "" msgstr ""
@ -650,18 +655,14 @@ msgstr ""
msgid "The ENS Paris-Saclay BDE note." msgid "The ENS Paris-Saclay BDE note."
msgstr "La note du BDE de l'ENS Paris-Saclay." msgstr "La note du BDE de l'ENS Paris-Saclay."
#: templates/base.html:84 #: templates/base.html:87
msgid "Clubs" msgid "Clubs"
msgstr "Clubs" msgstr "Clubs"
#: templates/base.html:89 #: templates/base.html:92
msgid "Activities" msgid "Activities"
msgstr "Activités" msgstr "Activités"
#: templates/base.html:94
msgid "Buttons"
msgstr "Boutons"
#: templates/cas_server/base.html:7 #: templates/cas_server/base.html:7
msgid "Central Authentication Service" msgid "Central Authentication Service"
msgstr "" msgstr ""
@ -800,7 +801,7 @@ msgstr "Sauvegarder les changements"
msgid "Sign up" msgid "Sign up"
msgstr "Inscription" msgstr "Inscription"
#: templates/note/conso_form.html:28 templates/note/transaction_form.html:40 #: templates/note/conso_form.html:28 templates/note/transaction_form.html:50
msgid "Select emitters" msgid "Select emitters"
msgstr "Sélection des émetteurs" msgstr "Sélection des émetteurs"
@ -844,12 +845,28 @@ msgstr "Paiement externe"
msgid "Transfer type" msgid "Transfer type"
msgstr "Type de transfert" msgstr "Type de transfert"
#: templates/note/transaction_form.html:86
msgid "Name"
msgstr "Nom"
#: templates/note/transaction_form.html:92
msgid "First name"
msgstr "Prénom"
#: templates/note/transaction_form.html:98
msgid "Bank"
msgstr "Banque"
#: templates/note/transaction_form.html:111 #: templates/note/transaction_form.html:111
#: templates/note/transaction_form.html:169 #: templates/note/transaction_form.html:169
#: templates/note/transaction_form.html:176 #: templates/note/transaction_form.html:176
msgid "Select receivers" msgid "Select receivers"
msgstr "Sélection des destinataires" msgstr "Sélection des destinataires"
#: templates/note/transaction_form.html:128
msgid "Amount"
msgstr "Montant"
#: templates/note/transaction_form.html:138 #: templates/note/transaction_form.html:138
msgid "Reason" msgid "Reason"
msgstr "Raison" msgstr "Raison"
@ -866,6 +883,22 @@ msgstr "Note à débiter"
msgid "Buttons list" msgid "Buttons list"
msgstr "Liste des boutons" msgstr "Liste des boutons"
#: templates/note/transactiontemplate_list.html:9
msgid "search button"
msgstr "Chercher un bouton"
#: templates/note/transactiontemplate_list.html:20
msgid "buttons listing "
msgstr "Liste des boutons"
#: templates/note/transactiontemplate_list.html:71
msgid "button successfully deleted "
msgstr "Le bouton a bien été supprimé"
#: templates/note/transactiontemplate_list.html:75
msgid "Unable to delete button "
msgstr "Impossible de supprimer le bouton "
#: templates/registration/logged_out.html:8 #: templates/registration/logged_out.html:8
msgid "Thanks for spending some quality time with the Web site today." msgid "Thanks for spending some quality time with the Web site today."
msgstr "" msgstr ""
@ -875,7 +908,7 @@ msgid "Log in again"
msgstr "" msgstr ""
#: templates/registration/login.html:7 templates/registration/login.html:8 #: templates/registration/login.html:7 templates/registration/login.html:8
#: templates/registration/login.html:26 #: templates/registration/login.html:28
#: templates/registration/password_reset_complete.html:10 #: templates/registration/password_reset_complete.html:10
msgid "Log in" msgid "Log in"
msgstr "" msgstr ""
@ -887,7 +920,15 @@ msgid ""
"page. Would you like to login to a different account?" "page. Would you like to login to a different account?"
msgstr "" msgstr ""
#: templates/registration/login.html:27 #: templates/registration/login.html:22
msgid "You can also register via the central authentification server "
msgstr ""
#: templates/registration/login.html:23
msgid "using this link "
msgstr ""
#: templates/registration/login.html:29
msgid "Forgotten your password or username?" msgid "Forgotten your password or username?"
msgstr "" msgstr ""

37
static/js/alias.js Normal file
View File

@ -0,0 +1,37 @@
$("#alias_input").on('keypress',function(e) {
if(e.which == 13) {
$("#alias_submit").click();
}
});
function create_alias(note_id){
$.post("/api/note/alias/",
{
"csrfmiddlewaretoken": CSRF_TOKEN,
"name": $("#alias_input").val(),
"note": note_id
}
).done(function(){
$("#alias_table").load(location.href+ " #alias_table");
addMsg("Alias ajouté","success");
})
.fail(function(xhr, textStatus, error){
errMsg(xhr.responseJSON);
});
}
// on click of button "delete" , call the API
function delete_button(button_id){
$.ajax({
url:"/api/note/alias/"+button_id+"/",
method:"DELETE",
headers: {"X-CSRFTOKEN": CSRF_TOKEN}
})
.done(function(){
addMsg('Alias supprimé','success');
$("#alias_table").load(location.href + " #alias_table");
})
.fail(function(xhr,textStatus, error){
errMsg(xhr.responseJSON);
});
}

View File

@ -28,7 +28,15 @@ function addMsg(msg, alert_type) {
+ msg + "</div>\n"; + msg + "</div>\n";
msgDiv.html(html); msgDiv.html(html);
} }
/**
* add Muliple error message from err_obj
* @param err_obj {error_code:erro_message}
*/
function errMsg(errs_obj){
for (const err_msg of Object.values(errs_obj)) {
addMsg(err_msg,'danger');
}
}
/** /**
* Reload the balance of the user on the right top corner * Reload the balance of the user on the right top corner
*/ */
@ -265,7 +273,16 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes
} }
// When a validate button is clicked, we switch the validation status // When a validate button is clicked, we switch the validation status
function de_validate(id, validated) { function in_validate(id, validated) {
let invalidity_reason;
let reason_obj = $("#invalidity_reason_" + id);
if (validated)
invalidity_reason = reason_obj.val();
else
invalidity_reason = null;
$("#validate_" + id).html("<strong style=\"font-size: 16pt;\">⟳ ...</strong>"); $("#validate_" + id).html("<strong style=\"font-size: 16pt;\">⟳ ...</strong>");
// Perform a PATCH request to the API in order to update the transaction // Perform a PATCH request to the API in order to update the transaction
@ -278,12 +295,13 @@ function de_validate(id, validated) {
"X-CSRFTOKEN": CSRF_TOKEN "X-CSRFTOKEN": CSRF_TOKEN
}, },
data: { data: {
"resourcetype": "RecurrentTransaction", resourcetype: "RecurrentTransaction",
valid: !validated valid: !validated,
invalidity_reason: invalidity_reason,
}, },
success: function () { success: function () {
// Refresh jQuery objects // Refresh jQuery objects
$(".validate").click(de_validate); $(".validate").click(in_validate);
refreshBalance(); refreshBalance();
// error if this method doesn't exist. Please define it. // error if this method doesn't exist. Please define it.

View File

@ -167,7 +167,7 @@ function reset() {
function consumeAll() { function consumeAll() {
notes_display.forEach(function(note_display) { notes_display.forEach(function(note_display) {
buttons.forEach(function(button) { buttons.forEach(function(button) {
consume(note_display.id, button.dest, button.quantity * note_display.quantity, button.amount, consume(note_display.id, note_display.name, button.dest, button.quantity * note_display.quantity, button.amount,
button.name + " (" + button.category_name + ")", button.type, button.category_id, button.id); button.name + " (" + button.category_name + ")", button.type, button.category_id, button.id);
}); });
}); });
@ -176,6 +176,7 @@ function consumeAll() {
/** /**
* Create a new transaction from a button through the API. * Create a new transaction from a button through the API.
* @param source The note that paid the item (type: int) * @param source The note that paid the item (type: int)
* @param source_alias The alias used for the source (type: str)
* @param dest The note that sold the item (type: int) * @param dest The note that sold the item (type: int)
* @param quantity The quantity sold (type: int) * @param quantity The quantity sold (type: int)
* @param amount The price of one item, in cents (type: int) * @param amount The price of one item, in cents (type: int)
@ -184,7 +185,7 @@ function consumeAll() {
* @param category The category id of the button (type: int) * @param category The category id of the button (type: int)
* @param template The button id (type: int) * @param template The button id (type: int)
*/ */
function consume(source, dest, quantity, amount, reason, type, category, template) { function consume(source, source_alias, dest, quantity, amount, reason, type, category, template) {
$.post("/api/note/transaction/transaction/", $.post("/api/note/transaction/transaction/",
{ {
"csrfmiddlewaretoken": CSRF_TOKEN, "csrfmiddlewaretoken": CSRF_TOKEN,
@ -195,12 +196,32 @@ function consume(source, dest, quantity, amount, reason, type, category, templat
"polymorphic_ctype": type, "polymorphic_ctype": type,
"resourcetype": "RecurrentTransaction", "resourcetype": "RecurrentTransaction",
"source": source, "source": source,
"source_alias": source_alias,
"destination": dest, "destination": dest,
"category": category, "category": category,
"template": template "template": template
}, reset).fail(function (e) { }, reset).fail(function (e) {
reset(); $.post("/api/note/transaction/transaction/",
{
addMsg("Une erreur est survenue lors de la transaction : " + e.responseText, "danger"); "csrfmiddlewaretoken": CSRF_TOKEN,
"quantity": quantity,
"amount": amount,
"reason": reason,
"valid": false,
"invalidity_reason": "Solde insuffisant",
"polymorphic_ctype": type,
"resourcetype": "RecurrentTransaction",
"source": source,
"source_alias": source_alias,
"destination": dest,
"category": category,
"template": template
}).done(function() {
reset();
addMsg("La transaction n'a pas pu être validée pour cause de solde insuffisant.", "danger");
}).fail(function () {
reset();
errMsg(e.responseJSON);
});
}); });
} }

View File

@ -83,19 +83,41 @@ $("#transfer").click(function() {
"polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE, "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE,
"resourcetype": "Transaction", "resourcetype": "Transaction",
"source": user_id, "source": user_id,
"destination": dest.id "destination": dest.id,
}, function () { "destination_alias": dest.name
}).done(function () {
addMsg("Le transfert de " addMsg("Le transfert de "
+ pretty_money(dest.quantity * 100 * $("#amount").val()) + " de votre note " + pretty_money(dest.quantity * 100 * $("#amount").val()) + " de votre note "
+ " vers la note " + dest.name + " a été fait avec succès !", "success"); + " vers la note " + dest.name + " a été fait avec succès !", "success");
reset(); reset();
}).fail(function (err) { }).fail(function () {
addMsg("Le transfert de " $.post("/api/note/transaction/transaction/",
+ pretty_money(dest.quantity * 100 * $("#amount").val()) + " de votre note " {
+ " vers la note " + dest.name + " a échoué : " + err.responseText, "danger"); "csrfmiddlewaretoken": CSRF_TOKEN,
"quantity": dest.quantity,
"amount": 100 * $("#amount").val(),
"reason": $("#reason").val(),
"valid": false,
"invalidity_reason": "Solde insuffisant",
"polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE,
"resourcetype": "Transaction",
"source": user_id,
"destination": dest.id,
"destination_alias": dest.name
}).done(function () {
addMsg("Le transfert de "
+ pretty_money(dest.quantity * 100 * $("#amount").val()) + " de votre note "
+ " vers la note " + dest.name + " a échoué : Solde insuffisant", "danger");
reset(); 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();
});
}); });
}); });
} }
@ -112,19 +134,43 @@ $("#transfer").click(function() {
"polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE, "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE,
"resourcetype": "Transaction", "resourcetype": "Transaction",
"source": source.id, "source": source.id,
"destination": dest.id "source_alias": source.name,
}, function () { "destination": dest.id,
"destination_alias": dest.name
}).done(function () {
addMsg("Le transfert de " addMsg("Le transfert de "
+ pretty_money(source.quantity * dest.quantity * 100 * $("#amount").val()) + " de la note " + source.name + 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"); + " vers la note " + dest.name + " a été fait avec succès !", "success");
reset(); reset();
}).fail(function (err) { }).fail(function (err) {
addMsg("Le transfert de " $.post("/api/note/transaction/transaction/",
+ pretty_money(source.quantity * dest.quantity * 100 * $("#amount").val()) + " de la note " + source.name {
+ " vers la note " + dest.name + " a échoué : " + err.responseText, "danger"); "csrfmiddlewaretoken": CSRF_TOKEN,
"quantity": source.quantity * dest.quantity,
"amount": 100 * $("#amount").val(),
"reason": $("#reason").val(),
"valid": false,
"invalidity_reason": "Solde insuffisant",
"polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE,
"resourcetype": "Transaction",
"source": source.id,
"source_alias": source.name,
"destination": dest.id,
"destination_alias": dest.name
}).done(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 échoué : Solde insuffisant", "danger");
reset(); 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();
});
}); });
}); });
}); });
@ -157,15 +203,17 @@ $("#transfer").click(function() {
"polymorphic_ctype": SPECIAL_TRANSFER_POLYMORPHIC_CTYPE, "polymorphic_ctype": SPECIAL_TRANSFER_POLYMORPHIC_CTYPE,
"resourcetype": "SpecialTransaction", "resourcetype": "SpecialTransaction",
"source": source, "source": source,
"source_alias": source.name,
"destination": dest, "destination": dest,
"destination_alias": dest.name,
"last_name": $("#last_name").val(), "last_name": $("#last_name").val(),
"first_name": $("#first_name").val(), "first_name": $("#first_name").val(),
"bank": $("#bank").val() "bank": $("#bank").val()
}, function () { }).done(function () {
addMsg("Le crédit/retrait a bien été effectué !", "success"); addMsg("Le crédit/retrait a bien été effectué !", "success");
reset(); reset();
}).fail(function (err) { }).fail(function (err) {
addMsg("Le crédit/transfert a échoué : " + err.responseText, "danger"); addMsg("Le crédit/retrait a échoué : " + err.responseText, "danger");
reset(); reset();
}); });
} }

View File

@ -79,9 +79,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
<a class="nav-link" href="{% url 'note:consos' %}"><i class="fa fa-coffee"></i> {% trans 'Consumptions' %}</a> <a class="nav-link" href="{% url 'note:consos' %}"><i class="fa fa-coffee"></i> {% trans 'Consumptions' %}</a>
</li> </li>
{% endif %} {% endif %}
{% if "note.transaction"|not_empty_model_list %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a> <a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a>
</li> </li>
{% endif %}
{% if "member.club"|not_empty_model_list %} {% if "member.club"|not_empty_model_list %}
<li class="nav-item active"> <li class="nav-item active">
<a class="nav-link" href="{% url 'member:club_list' %}"><i class="fa fa-users"></i> {% trans 'Clubs' %}</a> <a class="nav-link" href="{% url 'member:club_list' %}"><i class="fa fa-users"></i> {% trans 'Clubs' %}</a>

View File

@ -1,7 +1,11 @@
{% extends "base.html" %} {% extends "member/noteowner_detail.html" %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% load static %} {% load static %}
{% block content %}
{% block profile_info %}
{% include "member/club_info.html" %}
{% endblock %}
{% block profile_content %}
<form method="post" action=""> <form method="post" action="">
{% csrf_token %} {% csrf_token %}
@ -10,9 +14,9 @@
<input type="submit" name="submit" value="Add Members" class="btn btn-primary" id="submit-save"> <input type="submit" name="submit" value="Add Members" class="btn btn-primary" id="submit-save">
</div> </div>
</form> </form>
{% endblock %}
<!-- Include formset plugin - including jQuery dependency --> {% block extrajavascript %}
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="{% static 'js/dynamic-formset.js' %}"></script> <script src="{% static 'js/dynamic-formset.js' %}"></script>
<script> <script>
$('.formset-row').formset({ $('.formset-row').formset({

View File

@ -0,0 +1,12 @@
{% load django_tables2 crispy_forms_tags i18n %}
<div class="d-flex justify-content-center">
<input id="alias_input" type="text" value=""/>
<button id="alias_submit" class="btn btn-primary mx-2" onclick="create_alias( {{ object.note.pk }} )" type="submit">
{% trans "Add alias" %}
</button>
</div>
<div class="card bg-light shadow">
<div class="card-body">
{% render_table aliases %}
</div>
</div>

View File

@ -0,0 +1,10 @@
{% extends "member/club_detail.html" %}
{% load i18n static pretty_money django_tables2 crispy_forms_tags %}
{% block profile_content %}
{% include "member/alias_update.html" %}
{% endblock %}
{% block extrajavascript %}
<script src="/static/js/alias.js"></script>
{% endblock%}

View File

@ -1,5 +1,8 @@
{% load i18n static pretty_money %} {% load i18n static pretty_money %}
<div class="card bg-light shadow"> <div class="card bg-light shadow">
<div class="card-header text-center">
<h4> Club {{ club.name }} </h4>
</div>
<div class="card-top text-center"> <div class="card-top text-center">
<a href="{% url 'member:club_update_pic' club.pk %}"> <a href="{% url 'member:club_update_pic' club.pk %}">
<img src="{{ club.note.display_image.url }}" class="img-thumbnail mt-2" > <img src="{{ club.note.display_image.url }}" class="img-thumbnail mt-2" >
@ -10,6 +13,9 @@
<dt class="col-xl-6">{% trans 'name'|capfirst %}</dt> <dt class="col-xl-6">{% trans 'name'|capfirst %}</dt>
<dd class="col-xl-6">{{ club.name}}</dd> <dd class="col-xl-6">{{ club.name}}</dd>
<dt class="col-xl-6"><a href="{% url 'member:club_detail' club.parent_club.pk %}">{% trans 'Club Parent'|capfirst %}</a></dt>
<dd class="col-xl-6"> {{ club.parent_club.name}}</dd>
<dt class="col-xl-6">{% trans 'membership start'|capfirst %}</dt> <dt class="col-xl-6">{% trans 'membership start'|capfirst %}</dt>
<dd class="col-xl-6">{{ club.membership_start }}</dd> <dd class="col-xl-6">{{ club.membership_start }}</dd>
@ -22,11 +28,19 @@
<dt class="col-xl-6">{% trans 'membership fee'|capfirst %}</dt> <dt class="col-xl-6">{% trans 'membership fee'|capfirst %}</dt>
<dd class="col-xl-6">{{ club.membership_fee|pretty_money }}</dd> <dd class="col-xl-6">{{ club.membership_fee|pretty_money }}</dd>
<dt class="col-xl-6"><a href="{% url 'member:user_alias' club.pk %}">{% trans 'aliases'|capfirst %}</a></dt> <dt class="col-xl-6"><a href="{% url 'member:club_alias' club.pk %}">{% trans 'aliases'|capfirst %}</a></dt>
<dd class="col-xl-6 text-truncate">{{ object.note.alias_set.all|join:", " }}</dd> <dd class="col-xl-6 text-truncate">{{ object.note.alias_set.all|join:", " }}</dd>
<dt class="col-xl-3">{% trans 'email'|capfirst %}</dt> <dt class="col-xl-3">{% trans 'email'|capfirst %}</dt>
<dd class="col-xl-9"><a href="mailto:{{ club.email }}">{{ club.email}}</a></dd> <dd class="col-xl-9"><a href="mailto:{{ club.email }}">{{ club.email}}</a></dd>
</dl> </dl>
</div> </div>
<div class="card-footer text-center">
<a class="btn btn-primary btn-sm my-1" href="{% url 'member:club_add_member' pk=club.pk %}"> {% trans "Add member" %}</a>
<a class="btn btn-primary btn-sm my-1" href="{% url 'member:club_update' pk=club.pk %}"> {% trans "Edit" %}</a>
<a class="btn btn-primary btn-sm my-1" href="{% url 'member:club_add_member' pk=club.pk %}"> {% trans "Add roles" %}</a>
{% url 'member:club_detail' club.pk as club_detail_url %}
{%if request.get_full_path != club_detail_url %}
<a class="btn btn-primary btn-sm my-1" href="{{ user_profile_url }}">{% trans 'View Profile' %}</a>
{% endif %} </div>
</div> </div>

View File

@ -1,10 +1,6 @@
{% extends "member/club_detail.html" %} {% extends "member/club_detail.html" %}
{% load i18n static pretty_money django_tables2 crispy_forms_tags %} {% load i18n static pretty_money django_tables2 crispy_forms_tags %}
{% block profile_info %}
{% include "member/club_info.html" %}
{% endblock%}
{% block profile_content%} {% block profile_content%}
{% include "member/picture_update.html" %} {% include "member/picture_update.html" %}
{% endblock%} {% endblock%}

View File

@ -2,18 +2,9 @@
{% load i18n static pretty_money django_tables2 crispy_forms_tags %} {% load i18n static pretty_money django_tables2 crispy_forms_tags %}
{% block profile_content %} {% block profile_content %}
<div class="d-flex justify-content-center"> {% include "member/alias_update.html"%}
<form class=" text-center form my-2" action="" method="post">
{% csrf_token %}
{{ form |crispy }}
<button class="btn btn-primary mx-2" type="submit">
{% trans "Add alias" %}
</button>
</form>
</div>
<div class="card bg-light shadow">
<div class="card-body">
{% render_table aliases %}
</div>
</div>
{% endblock %} {% endblock %}
{% block extrajavascript %}
<script src="/static/js/alias.js"></script>
{% endblock%}

View File

@ -1,6 +1,9 @@
{% load i18n static pretty_money %} {% load i18n static pretty_money %}
<div class="card bg-light shadow"> <div class="card bg-light shadow">
<div class="card-header text-center" >
<h4> {% trans "Account #" %} {{ object.pk }}</h4>
</div>
<div class="card-top text-center"> <div class="card-top text-center">
<a href="{% url 'member:user_update_pic' object.pk %}"> <a href="{% url 'member:user_update_pic' object.pk %}">
<img src="{{ object.note.display_image.url }}" class="img-thumbnail mt-2" > <img src="{{ object.note.display_image.url }}" class="img-thumbnail mt-2" >

View File

@ -1,10 +1,6 @@
{% extends "member/noteowner_detail.html" %} {% extends "member/profile_detail.html" %}
{% load i18n static pretty_money django_tables2 crispy_forms_tags %} {% load i18n static pretty_money django_tables2 crispy_forms_tags %}
{% block profile_info %}
{% include "member/profile_info.html" %}
{% endblock%}
{% block profile_content%} {% block profile_content%}
{% include "member/picture_update.html" %} {% include "member/picture_update.html" %}
{% endblock%} {% endblock%}

View File

@ -10,7 +10,7 @@
</h4> </h4>
<input class="form-control mx-auto w-25" type="text" onkeyup="search_field_moved();return(false);" id="search_field"/> <input class="form-control mx-auto w-25" type="text" onkeyup="search_field_moved();return(false);" id="search_field"/>
<hr> <hr>
<a class="btn btn-primary text-center my-4" href="{% url 'note:template_create' %}">Créer un bouton</a> <a class="btn btn-primary text-center my-1" href="{% url 'note:template_create' %}">{% trans "New button" %}</a>
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">