mirror of https://gitlab.crans.org/bde/nk20
Format code
This commit is contained in:
parent
0d7db68372
commit
f89d91e524
|
@ -12,7 +12,7 @@ class ActivityAdmin(admin.ModelAdmin):
|
||||||
Admin customisation for Activity
|
Admin customisation for Activity
|
||||||
"""
|
"""
|
||||||
list_display = ('name', 'activity_type', 'organizer')
|
list_display = ('name', 'activity_type', 'organizer')
|
||||||
list_filter = ('activity_type',)
|
list_filter = ('activity_type', )
|
||||||
search_fields = ['name', 'organizer__name']
|
search_fields = ['name', 'organizer__name']
|
||||||
|
|
||||||
# Organize activities by start date
|
# Organize activities by start date
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
from ..models import ActivityType, Activity, Guest
|
from ..models import ActivityType, Activity, Guest
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
class ActivityTypeSerializer(serializers.ModelSerializer):
|
class ActivityTypeSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
REST API Serializer for Activity types.
|
REST API Serializer for Activity types.
|
||||||
|
|
|
@ -17,10 +17,13 @@ class UserSerializer(serializers.ModelSerializer):
|
||||||
REST API Serializer for Users.
|
REST API Serializer for Users.
|
||||||
The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
|
The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
exclude = ('password', 'groups', 'user_permissions',)
|
exclude = (
|
||||||
|
'password',
|
||||||
|
'groups',
|
||||||
|
'user_permissions',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserViewSet(viewsets.ModelViewSet):
|
class UserViewSet(viewsets.ModelViewSet):
|
||||||
|
|
|
@ -19,9 +19,9 @@ class ProfileInline(admin.StackedInline):
|
||||||
|
|
||||||
|
|
||||||
class CustomUserAdmin(UserAdmin):
|
class CustomUserAdmin(UserAdmin):
|
||||||
inlines = (ProfileInline,)
|
inlines = (ProfileInline, )
|
||||||
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
|
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
|
||||||
list_select_related = ('profile',)
|
list_select_related = ('profile', )
|
||||||
form = ProfileForm
|
form = ProfileForm
|
||||||
|
|
||||||
def get_inline_instances(self, request, obj=None):
|
def get_inline_instances(self, request, obj=None):
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django_filters import FilterSet, CharFilter,NumberFilter
|
from django_filters import FilterSet, CharFilter, NumberFilter
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models import CharField
|
from django.db.models import CharField
|
||||||
from crispy_forms.helper import FormHelper
|
from crispy_forms.helper import FormHelper
|
||||||
|
@ -10,22 +10,27 @@ from crispy_forms.layout import Layout, Submit
|
||||||
|
|
||||||
from .models import Club
|
from .models import Club
|
||||||
|
|
||||||
|
|
||||||
class UserFilter(FilterSet):
|
class UserFilter(FilterSet):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ['last_name','first_name','username','profile__section']
|
fields = ['last_name', 'first_name', 'username', 'profile__section']
|
||||||
filter_overrides={
|
filter_overrides = {
|
||||||
CharField:{
|
CharField: {
|
||||||
'filter_class':CharFilter,
|
'filter_class': CharFilter,
|
||||||
'extra': lambda f:{
|
'extra': lambda f: {
|
||||||
'lookup_expr':'icontains'
|
'lookup_expr': 'icontains'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class UserFilterFormHelper(FormHelper):
|
class UserFilterFormHelper(FormHelper):
|
||||||
form_method = 'GET'
|
form_method = 'GET'
|
||||||
layout = Layout(
|
layout = Layout(
|
||||||
'last_name','first_name','username','profile__section',
|
'last_name',
|
||||||
Submit('Submit','Apply Filter'),
|
'first_name',
|
||||||
|
'username',
|
||||||
|
'profile__section',
|
||||||
|
Submit('Submit', 'Apply Filter'),
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,11 +16,11 @@ from crispy_forms.bootstrap import InlineField, FormActions, StrictButton, Div,
|
||||||
from crispy_forms.layout import Layout
|
from crispy_forms.layout import Layout
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SignUpForm(UserCreationForm):
|
class SignUpForm(UserCreationForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ['first_name','last_name','username','email']
|
fields = ['first_name', 'last_name', 'username', 'email']
|
||||||
|
|
||||||
|
|
||||||
class ProfileForm(forms.ModelForm):
|
class ProfileForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
|
@ -31,48 +31,56 @@ class ProfileForm(forms.ModelForm):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
exclude = ['user']
|
exclude = ['user']
|
||||||
|
|
||||||
|
|
||||||
class ClubForm(forms.ModelForm):
|
class ClubForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Club
|
model = Club
|
||||||
fields ='__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
class AddMembersForm(forms.Form):
|
class AddMembersForm(forms.Form):
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ('',)
|
fields = ('', )
|
||||||
|
|
||||||
|
|
||||||
class MembershipForm(forms.ModelForm):
|
class MembershipForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Membership
|
model = Membership
|
||||||
fields = ('user','roles','date_start')
|
fields = ('user', 'roles', 'date_start')
|
||||||
# Le champ d'utilisateur est remplacé par un champ d'auto-complétion.
|
# Le champ d'utilisateur est remplacé par un champ d'auto-complétion.
|
||||||
# Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion
|
# Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion
|
||||||
# et récupère les noms d'utilisateur valides
|
# et récupère les noms d'utilisateur valides
|
||||||
widgets = {
|
widgets = {
|
||||||
'user': autocomplete.ModelSelect2(url='member:user_autocomplete',
|
'user':
|
||||||
|
autocomplete.ModelSelect2(
|
||||||
|
url='member:user_autocomplete',
|
||||||
attrs={
|
attrs={
|
||||||
'data-placeholder': 'Nom ...',
|
'data-placeholder': 'Nom ...',
|
||||||
'data-minimum-input-length': 1,
|
'data-minimum-input-length': 1,
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MemberFormSet = forms.modelformset_factory(Membership,
|
MemberFormSet = forms.modelformset_factory(
|
||||||
|
Membership,
|
||||||
form=MembershipForm,
|
form=MembershipForm,
|
||||||
extra=2,
|
extra=2,
|
||||||
can_delete=True)
|
can_delete=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FormSetHelper(FormHelper):
|
class FormSetHelper(FormHelper):
|
||||||
def __init__(self,*args,**kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args,**kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.form_tag = False
|
self.form_tag = False
|
||||||
self.form_method = 'POST'
|
self.form_method = 'POST'
|
||||||
self.form_class='form-inline'
|
self.form_class = 'form-inline'
|
||||||
# self.template = 'bootstrap/table_inline_formset.html'
|
# self.template = 'bootstrap/table_inline_formset.html'
|
||||||
self.layout = Layout(
|
self.layout = Layout(
|
||||||
Div(
|
Div(
|
||||||
Div('user',css_class='col-sm-2'),
|
Div('user', css_class='col-sm-2'),
|
||||||
Div('roles',css_class='col-sm-2'),
|
Div('roles', css_class='col-sm-2'),
|
||||||
Div('date_start',css_class='col-sm-2'),
|
Div('date_start', css_class='col-sm-2'),
|
||||||
css_class="row formset-row",
|
css_class="row formset-row",
|
||||||
)
|
))
|
||||||
)
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from django.dispatch import receiver
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
|
|
||||||
|
|
||||||
class Profile(models.Model):
|
class Profile(models.Model):
|
||||||
"""
|
"""
|
||||||
An user profile
|
An user profile
|
||||||
|
@ -50,7 +51,8 @@ class Profile(models.Model):
|
||||||
verbose_name_plural = _('user profile')
|
verbose_name_plural = _('user profile')
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('user_detail',args=(self.pk,))
|
return reverse('user_detail', args=(self.pk, ))
|
||||||
|
|
||||||
|
|
||||||
class Club(models.Model):
|
class Club(models.Model):
|
||||||
"""
|
"""
|
||||||
|
@ -98,7 +100,7 @@ class Club(models.Model):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse_lazy('member:club_detail', args=(self.pk,))
|
return reverse_lazy('member:club_detail', args=(self.pk, ))
|
||||||
|
|
||||||
|
|
||||||
class Role(models.Model):
|
class Role(models.Model):
|
||||||
|
@ -129,15 +131,15 @@ class Membership(models.Model):
|
||||||
"""
|
"""
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL,
|
||||||
on_delete=models.PROTECT
|
on_delete=models.PROTECT,
|
||||||
)
|
)
|
||||||
club = models.ForeignKey(
|
club = models.ForeignKey(
|
||||||
Club,
|
Club,
|
||||||
on_delete=models.PROTECT
|
on_delete=models.PROTECT,
|
||||||
)
|
)
|
||||||
roles = models.ForeignKey(
|
roles = models.ForeignKey(
|
||||||
Role,
|
Role,
|
||||||
on_delete=models.PROTECT
|
on_delete=models.PROTECT,
|
||||||
)
|
)
|
||||||
date_start = models.DateField(
|
date_start = models.DateField(
|
||||||
verbose_name=_('membership starts on'),
|
verbose_name=_('membership starts on'),
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# -*- mode: python; coding: utf-8 -*-
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,20 @@ from .models import Club
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
class ClubTable(tables.Table):
|
class ClubTable(tables.Table):
|
||||||
class Meta:
|
class Meta:
|
||||||
attrs = {'class':'table table-bordered table-condensed table-striped table-hover'}
|
attrs = {
|
||||||
|
'class':
|
||||||
|
'table table-bordered table-condensed table-striped table-hover'
|
||||||
|
}
|
||||||
model = Club
|
model = Club
|
||||||
template_name = 'django_tables2/bootstrap.html'
|
template_name = 'django_tables2/bootstrap.html'
|
||||||
fields = ('id','name','email')
|
fields = ('id', 'name', 'email')
|
||||||
row_attrs = {'class':'table-row',
|
row_attrs = {
|
||||||
'data-href': lambda record: record.pk }
|
'class': 'table-row',
|
||||||
|
'data-href': lambda record: record.pk
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class UserTable(tables.Table):
|
class UserTable(tables.Table):
|
||||||
|
@ -21,7 +26,10 @@ class UserTable(tables.Table):
|
||||||
solde = tables.Column(accessor='note.balance')
|
solde = tables.Column(accessor='note.balance')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
attrs = {'class':'table table-bordered table-condensed table-striped table-hover'}
|
attrs = {
|
||||||
|
'class':
|
||||||
|
'table table-bordered table-condensed table-striped table-hover'
|
||||||
|
}
|
||||||
template_name = 'django_tables2/bootstrap.html'
|
template_name = 'django_tables2/bootstrap.html'
|
||||||
fields = ('last_name','first_name','username','email')
|
fields = ('last_name', 'first_name', 'username', 'email')
|
||||||
model = User
|
model = User
|
||||||
|
|
|
@ -16,14 +16,14 @@ from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
from note.models import Alias, Note, NoteUser
|
from note.models import Alias, Note, NoteUser
|
||||||
from .models import Profile, Club, Membership
|
from .models import Profile, Club, Membership
|
||||||
from .forms import SignUpForm, ProfileForm, ClubForm,MembershipForm, MemberFormSet,FormSetHelper
|
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper
|
||||||
from .tables import ClubTable,UserTable
|
from .tables import ClubTable, UserTable
|
||||||
from .filters import UserFilter, UserFilterFormHelper
|
from .filters import UserFilter, UserFilterFormHelper
|
||||||
|
|
||||||
|
|
||||||
from note.models.transactions import Transaction
|
from note.models.transactions import Transaction
|
||||||
from note.tables import HistoryTable
|
from note.tables import HistoryTable
|
||||||
|
|
||||||
|
|
||||||
class UserCreateView(CreateView):
|
class UserCreateView(CreateView):
|
||||||
"""
|
"""
|
||||||
Une vue pour inscrire un utilisateur et lui créer un profile
|
Une vue pour inscrire un utilisateur et lui créer un profile
|
||||||
|
@ -31,10 +31,10 @@ class UserCreateView(CreateView):
|
||||||
|
|
||||||
form_class = SignUpForm
|
form_class = SignUpForm
|
||||||
success_url = reverse_lazy('login')
|
success_url = reverse_lazy('login')
|
||||||
template_name ='member/signup.html'
|
template_name = 'member/signup.html'
|
||||||
second_form = ProfileForm
|
second_form = ProfileForm
|
||||||
|
|
||||||
def get_context_data(self,**kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["profile_form"] = self.second_form()
|
context["profile_form"] = self.second_form()
|
||||||
|
|
||||||
|
@ -49,17 +49,20 @@ class UserCreateView(CreateView):
|
||||||
profile.save()
|
profile.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
class UserUpdateView(LoginRequiredMixin,UpdateView):
|
|
||||||
|
class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
model = User
|
model = User
|
||||||
fields = ['first_name','last_name','username','email']
|
fields = ['first_name', 'last_name', 'username', 'email']
|
||||||
template_name = 'member/profile_update.html'
|
template_name = 'member/profile_update.html'
|
||||||
|
|
||||||
second_form = ProfileForm
|
second_form = ProfileForm
|
||||||
def get_context_data(self,**kwargs):
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['user_modified'] = context['user']
|
context['user_modified'] = context['user']
|
||||||
context['user'] = self.request.user
|
context['user'] = self.request.user
|
||||||
context["profile_form"] = self.second_form(instance=context['user_modified'].profile)
|
context["profile_form"] = self.second_form(
|
||||||
|
instance=context['user_modified'].profile)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@ -71,21 +74,26 @@ class UserUpdateView(LoginRequiredMixin,UpdateView):
|
||||||
new_username = form.data['username']
|
new_username = form.data['username']
|
||||||
|
|
||||||
# Si l'utilisateur cherche à modifier son pseudo, le nouveau pseudo ne doit pas être proche d'un alias existant
|
# Si l'utilisateur cherche à modifier son pseudo, le nouveau pseudo ne doit pas être proche d'un alias existant
|
||||||
note = NoteUser.objects.filter(alias__normalized_name=Alias.normalize(new_username))
|
note = NoteUser.objects.filter(
|
||||||
|
alias__normalized_name=Alias.normalize(new_username))
|
||||||
if note.exists() and note.get().user != self.request.user:
|
if note.exists() and note.get().user != self.request.user:
|
||||||
form.add_error('username', _("An alias with a similar name already exists."))
|
form.add_error('username',
|
||||||
|
_("An alias with a similar name already exists."))
|
||||||
|
|
||||||
return form
|
return form
|
||||||
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
profile_form = ProfileForm(data=self.request.POST,instance=self.request.user.profile)
|
profile_form = ProfileForm(
|
||||||
|
data=self.request.POST,
|
||||||
|
instance=self.request.user.profile,
|
||||||
|
)
|
||||||
if form.is_valid() and profile_form.is_valid():
|
if form.is_valid() and profile_form.is_valid():
|
||||||
new_username = form.data['username']
|
new_username = form.data['username']
|
||||||
alias = Alias.objects.filter(name=new_username)
|
alias = Alias.objects.filter(name=new_username)
|
||||||
# Si le nouveau pseudo n'est pas un de nos alias, on supprime éventuellement un alias similaire pour le remplacer
|
# Si le nouveau pseudo n'est pas un de nos alias, on supprime éventuellement un alias similaire pour le remplacer
|
||||||
if not alias.exists():
|
if not alias.exists():
|
||||||
similar = Alias.objects.filter(normalized_name=Alias.normalize(new_username))
|
similar = Alias.objects.filter(
|
||||||
|
normalized_name=Alias.normalize(new_username))
|
||||||
if similar.exists():
|
if similar.exists():
|
||||||
similar.delete()
|
similar.delete()
|
||||||
|
|
||||||
|
@ -98,17 +106,20 @@ class UserUpdateView(LoginRequiredMixin,UpdateView):
|
||||||
|
|
||||||
def get_success_url(self, **kwargs):
|
def get_success_url(self, **kwargs):
|
||||||
if kwargs:
|
if kwargs:
|
||||||
return reverse_lazy('member:user_detail', kwargs = {'pk': kwargs['id']})
|
return reverse_lazy('member:user_detail',
|
||||||
|
kwargs={'pk': kwargs['id']})
|
||||||
else:
|
else:
|
||||||
return reverse_lazy('member:user_detail', args = (self.object.id,))
|
return reverse_lazy('member:user_detail', args=(self.object.id, ))
|
||||||
|
|
||||||
class UserDetailView(LoginRequiredMixin,DetailView):
|
|
||||||
|
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Affiche les informations sur un utilisateur, sa note, ses clubs ...
|
Affiche les informations sur un utilisateur, sa note, ses clubs ...
|
||||||
"""
|
"""
|
||||||
model = Profile
|
model = Profile
|
||||||
context_object_name = "profile"
|
context_object_name = "profile"
|
||||||
def get_context_data(slef,**kwargs):
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
user = context['profile'].user
|
user = context['profile'].user
|
||||||
history_list = \
|
history_list = \
|
||||||
|
@ -119,7 +130,8 @@ class UserDetailView(LoginRequiredMixin,DetailView):
|
||||||
context['club_list'] = ClubTable(club_list)
|
context['club_list'] = ClubTable(club_list)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
class UserListView(LoginRequiredMixin,SingleTableView):
|
|
||||||
|
class UserListView(LoginRequiredMixin, SingleTableView):
|
||||||
"""
|
"""
|
||||||
Affiche la liste des utilisateurs, avec une fonction de recherche statique
|
Affiche la liste des utilisateurs, avec une fonction de recherche statique
|
||||||
"""
|
"""
|
||||||
|
@ -129,13 +141,13 @@ class UserListView(LoginRequiredMixin,SingleTableView):
|
||||||
filter_class = UserFilter
|
filter_class = UserFilter
|
||||||
formhelper_class = UserFilterFormHelper
|
formhelper_class = UserFilterFormHelper
|
||||||
|
|
||||||
def get_queryset(self,**kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
qs = super().get_queryset()
|
qs = super().get_queryset()
|
||||||
self.filter = self.filter_class(self.request.GET,queryset=qs)
|
self.filter = self.filter_class(self.request.GET, queryset=qs)
|
||||||
self.filter.form.helper = self.formhelper_class()
|
self.filter.form.helper = self.formhelper_class()
|
||||||
return self.filter.qs
|
return self.filter.qs
|
||||||
|
|
||||||
def get_context_data(self,**kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["filter"] = self.filter
|
context["filter"] = self.filter
|
||||||
return context
|
return context
|
||||||
|
@ -149,22 +161,25 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView):
|
||||||
template_name = "member/manage_auth_tokens.html"
|
template_name = "member/manage_auth_tokens.html"
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if 'regenerate' in request.GET and Token.objects.filter(user=request.user).exists():
|
if 'regenerate' in request.GET and Token.objects.filter(
|
||||||
|
user=request.user).exists():
|
||||||
Token.objects.get(user=self.request.user).delete()
|
Token.objects.get(user=self.request.user).delete()
|
||||||
return redirect(reverse_lazy('member:auth_token') + "?show", permanent=True)
|
return redirect(reverse_lazy('member:auth_token') + "?show",
|
||||||
|
permanent=True)
|
||||||
|
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['token'] = Token.objects.get_or_create(user=self.request.user)[0]
|
context['token'] = Token.objects.get_or_create(
|
||||||
|
user=self.request.user)[0]
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class UserAutocomplete(autocomplete.Select2QuerySetView):
|
class UserAutocomplete(autocomplete.Select2QuerySetView):
|
||||||
"""
|
"""
|
||||||
Auto complete users by usernames
|
Auto complete users by usernames
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
Quand une personne cherche un utilisateur par pseudo, une requête est envoyée sur l'API dédiée à l'auto-complétion.
|
Quand une personne cherche un utilisateur par pseudo, une requête est envoyée sur l'API dédiée à l'auto-complétion.
|
||||||
|
@ -181,32 +196,36 @@ class UserAutocomplete(autocomplete.Select2QuerySetView):
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
############## CLUB ###############
|
############## CLUB ###############
|
||||||
###################################
|
###################################
|
||||||
|
|
||||||
class ClubCreateView(LoginRequiredMixin,CreateView):
|
|
||||||
|
class ClubCreateView(LoginRequiredMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create Club
|
Create Club
|
||||||
"""
|
"""
|
||||||
model = Club
|
model = Club
|
||||||
form_class = ClubForm
|
form_class = ClubForm
|
||||||
|
|
||||||
def form_valid(self,form):
|
def form_valid(self, form):
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
class ClubListView(LoginRequiredMixin,SingleTableView):
|
|
||||||
|
class ClubListView(LoginRequiredMixin, SingleTableView):
|
||||||
"""
|
"""
|
||||||
List existing Clubs
|
List existing Clubs
|
||||||
"""
|
"""
|
||||||
model = Club
|
model = Club
|
||||||
table_class = ClubTable
|
table_class = ClubTable
|
||||||
|
|
||||||
class ClubDetailView(LoginRequiredMixin,DetailView):
|
|
||||||
model = Club
|
|
||||||
context_object_name="club"
|
|
||||||
|
|
||||||
def get_context_data(self,**kwargs):
|
class ClubDetailView(LoginRequiredMixin, DetailView):
|
||||||
|
model = Club
|
||||||
|
context_object_name = "club"
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
club = context["club"]
|
club = context["club"]
|
||||||
club_transactions = \
|
club_transactions = \
|
||||||
|
@ -218,23 +237,25 @@ class ClubDetailView(LoginRequiredMixin,DetailView):
|
||||||
context['member_list'] = club_member
|
context['member_list'] = club_member
|
||||||
return context
|
return context
|
||||||
|
|
||||||
class ClubAddMemberView(LoginRequiredMixin,CreateView):
|
|
||||||
|
class ClubAddMemberView(LoginRequiredMixin, CreateView):
|
||||||
model = Membership
|
model = Membership
|
||||||
form_class = MembershipForm
|
form_class = MembershipForm
|
||||||
template_name = 'member/add_members.html'
|
template_name = 'member/add_members.html'
|
||||||
def get_context_data(self,**kwargs):
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['formset'] = MemberFormSet()
|
context['formset'] = MemberFormSet()
|
||||||
context['helper'] = FormSetHelper()
|
context['helper'] = FormSetHelper()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def post(self,request,*args,**kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
formset = MembershipFormset(request.POST)
|
formset = MembershipFormset(request.POST)
|
||||||
if formset.is_valid():
|
if formset.is_valid():
|
||||||
return self.form_valid(formset)
|
return self.form_valid(formset)
|
||||||
else:
|
else:
|
||||||
return self.form_invalid(formset)
|
return self.form_invalid(formset)
|
||||||
|
|
||||||
def form_valid(self,formset):
|
def form_valid(self, formset):
|
||||||
formset.save()
|
formset.save()
|
||||||
return super().form_valid(formset)
|
return super().form_valid(formset)
|
||||||
|
|
|
@ -25,7 +25,10 @@ class NoteAdmin(PolymorphicParentModelAdmin):
|
||||||
Parent regrouping all note types as children
|
Parent regrouping all note types as children
|
||||||
"""
|
"""
|
||||||
child_models = (NoteClub, NoteSpecial, NoteUser)
|
child_models = (NoteClub, NoteSpecial, NoteUser)
|
||||||
list_filter = (PolymorphicChildModelFilter, 'is_active',)
|
list_filter = (
|
||||||
|
PolymorphicChildModelFilter,
|
||||||
|
'is_active',
|
||||||
|
)
|
||||||
|
|
||||||
# Use a polymorphic list
|
# Use a polymorphic list
|
||||||
list_display = ('pretty', 'balance', 'is_active')
|
list_display = ('pretty', 'balance', 'is_active')
|
||||||
|
@ -44,11 +47,12 @@ class NoteClubAdmin(PolymorphicChildModelAdmin):
|
||||||
"""
|
"""
|
||||||
Child for a club note, see NoteAdmin
|
Child for a club note, see NoteAdmin
|
||||||
"""
|
"""
|
||||||
inlines = (AliasInlines,)
|
inlines = (AliasInlines, )
|
||||||
|
|
||||||
# We can't change club after creation or the balance
|
# We can't change club after creation or the balance
|
||||||
readonly_fields = ('club', 'balance')
|
readonly_fields = ('club', 'balance')
|
||||||
search_fields = ('club',)
|
search_fields = ('club', )
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
"""
|
"""
|
||||||
A club note should not be manually added
|
A club note should not be manually added
|
||||||
|
@ -67,7 +71,7 @@ class NoteSpecialAdmin(PolymorphicChildModelAdmin):
|
||||||
"""
|
"""
|
||||||
Child for a special note, see NoteAdmin
|
Child for a special note, see NoteAdmin
|
||||||
"""
|
"""
|
||||||
readonly_fields = ('balance',)
|
readonly_fields = ('balance', )
|
||||||
|
|
||||||
|
|
||||||
@admin.register(NoteUser)
|
@admin.register(NoteUser)
|
||||||
|
@ -75,7 +79,7 @@ class NoteUserAdmin(PolymorphicChildModelAdmin):
|
||||||
"""
|
"""
|
||||||
Child for an user note, see NoteAdmin
|
Child for an user note, see NoteAdmin
|
||||||
"""
|
"""
|
||||||
inlines = (AliasInlines,)
|
inlines = (AliasInlines, )
|
||||||
|
|
||||||
# We can't change user after creation or the balance
|
# We can't change user after creation or the balance
|
||||||
readonly_fields = ('user', 'balance')
|
readonly_fields = ('user', 'balance')
|
||||||
|
@ -101,7 +105,10 @@ class TransactionAdmin(admin.ModelAdmin):
|
||||||
list_display = ('created_at', 'poly_source', 'poly_destination',
|
list_display = ('created_at', 'poly_source', 'poly_destination',
|
||||||
'quantity', 'amount', 'transaction_type', 'valid')
|
'quantity', 'amount', 'transaction_type', 'valid')
|
||||||
list_filter = ('transaction_type', 'valid')
|
list_filter = ('transaction_type', 'valid')
|
||||||
autocomplete_fields = ('source', 'destination',)
|
autocomplete_fields = (
|
||||||
|
'source',
|
||||||
|
'destination',
|
||||||
|
)
|
||||||
|
|
||||||
def poly_source(self, obj):
|
def poly_source(self, obj):
|
||||||
"""
|
"""
|
||||||
|
@ -136,8 +143,8 @@ class TransactionTemplateAdmin(admin.ModelAdmin):
|
||||||
Admin customisation for TransactionTemplate
|
Admin customisation for TransactionTemplate
|
||||||
"""
|
"""
|
||||||
list_display = ('name', 'poly_destination', 'amount', 'template_type')
|
list_display = ('name', 'poly_destination', 'amount', 'template_type')
|
||||||
list_filter = ('template_type',)
|
list_filter = ('template_type', )
|
||||||
autocomplete_fields = ('destination',)
|
autocomplete_fields = ('destination', )
|
||||||
|
|
||||||
def poly_destination(self, obj):
|
def poly_destination(self, obj):
|
||||||
"""
|
"""
|
||||||
|
@ -153,5 +160,5 @@ class TransactionCategoryAdmin(admin.ModelAdmin):
|
||||||
"""
|
"""
|
||||||
Admin customisation for TransactionTemplate
|
Admin customisation for TransactionTemplate
|
||||||
"""
|
"""
|
||||||
list_display = ('name',)
|
list_display = ('name', )
|
||||||
list_filter = ('name',)
|
list_filter = ('name', )
|
||||||
|
|
|
@ -17,7 +17,10 @@ class NoteSerializer(serializers.ModelSerializer):
|
||||||
model = Note
|
model = Note
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'url': {'view_name': 'project-detail', 'lookup_field': 'pk'},
|
'url': {
|
||||||
|
'view_name': 'project-detail',
|
||||||
|
'lookup_field': 'pk'
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,6 +72,7 @@ class NotePolymorphicSerializer(PolymorphicSerializer):
|
||||||
NoteSpecial: NoteSpecialSerializer
|
NoteSpecial: NoteSpecialSerializer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TransactionTemplateSerializer(serializers.ModelSerializer):
|
class TransactionTemplateSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
REST API Serializer for Transaction templates.
|
REST API Serializer for Transaction templates.
|
||||||
|
|
|
@ -69,7 +69,9 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Note.objects.all()
|
queryset = Note.objects.all()
|
||||||
|
|
||||||
alias = self.request.query_params.get("alias", ".*")
|
alias = self.request.query_params.get("alias", ".*")
|
||||||
queryset = queryset.filter(Q(alias__name__regex=alias) | Q(alias__normalized_name__regex=alias.lower()))
|
queryset = queryset.filter(
|
||||||
|
Q(alias__name__regex=alias)
|
||||||
|
| Q(alias__normalized_name__regex=alias.lower()))
|
||||||
|
|
||||||
note_type = self.request.query_params.get("type", None)
|
note_type = self.request.query_params.get("type", None)
|
||||||
if note_type:
|
if note_type:
|
||||||
|
@ -79,7 +81,8 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
|
||||||
elif "club" in l:
|
elif "club" in l:
|
||||||
queryset = queryset.filter(polymorphic_ctype__model="noteclub")
|
queryset = queryset.filter(polymorphic_ctype__model="noteclub")
|
||||||
elif "special" in l:
|
elif "special" in l:
|
||||||
queryset = queryset.filter(polymorphic_ctype__model="notespecial")
|
queryset = queryset.filter(
|
||||||
|
polymorphic_ctype__model="notespecial")
|
||||||
else:
|
else:
|
||||||
queryset = queryset.none()
|
queryset = queryset.none()
|
||||||
|
|
||||||
|
@ -104,7 +107,8 @@ class AliasViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Alias.objects.all()
|
queryset = Alias.objects.all()
|
||||||
|
|
||||||
alias = self.request.query_params.get("alias", ".*")
|
alias = self.request.query_params.get("alias", ".*")
|
||||||
queryset = queryset.filter(Q(name__regex=alias) | Q(normalized_name__regex=alias.lower()))
|
queryset = queryset.filter(
|
||||||
|
Q(name__regex=alias) | Q(normalized_name__regex=alias.lower()))
|
||||||
|
|
||||||
note_id = self.request.query_params.get("note", None)
|
note_id = self.request.query_params.get("note", None)
|
||||||
if note_id:
|
if note_id:
|
||||||
|
@ -114,11 +118,14 @@ class AliasViewSet(viewsets.ModelViewSet):
|
||||||
if note_type:
|
if note_type:
|
||||||
l = str(note_type).lower()
|
l = str(note_type).lower()
|
||||||
if "user" in l:
|
if "user" in l:
|
||||||
queryset = queryset.filter(note__polymorphic_ctype__model="noteuser")
|
queryset = queryset.filter(
|
||||||
|
note__polymorphic_ctype__model="noteuser")
|
||||||
elif "club" in l:
|
elif "club" in l:
|
||||||
queryset = queryset.filter(note__polymorphic_ctype__model="noteclub")
|
queryset = queryset.filter(
|
||||||
|
note__polymorphic_ctype__model="noteclub")
|
||||||
elif "special" in l:
|
elif "special" in l:
|
||||||
queryset = queryset.filter(note__polymorphic_ctype__model="notespecial")
|
queryset = queryset.filter(
|
||||||
|
note__polymorphic_ctype__model="notespecial")
|
||||||
else:
|
else:
|
||||||
queryset = queryset.none()
|
queryset = queryset.none()
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ class NoteConfig(AppConfig):
|
||||||
"""
|
"""
|
||||||
post_save.connect(
|
post_save.connect(
|
||||||
signals.save_user_note,
|
signals.save_user_note,
|
||||||
sender=settings.AUTH_USER_MODEL
|
sender=settings.AUTH_USER_MODEL,
|
||||||
)
|
)
|
||||||
post_save.connect(
|
post_save.connect(
|
||||||
signals.save_club_note,
|
signals.save_club_note,
|
||||||
sender='member.Club'
|
sender='member.Club',
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,10 +4,11 @@ from dal import autocomplete, forward
|
||||||
from django import forms
|
from django import forms
|
||||||
from .models import Transaction, TransactionTemplate
|
from .models import Transaction, TransactionTemplate
|
||||||
|
|
||||||
|
|
||||||
class TransactionTemplateForm(forms.ModelForm):
|
class TransactionTemplateForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TransactionTemplate
|
model = TransactionTemplate
|
||||||
fields ='__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
# Le champ de destination est remplacé par un champ d'auto-complétion.
|
# Le champ de destination est remplacé par un champ d'auto-complétion.
|
||||||
# Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion
|
# Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion
|
||||||
|
@ -15,11 +16,14 @@ class TransactionTemplateForm(forms.ModelForm):
|
||||||
# Pour force le type d'une note, il faut rajouter le paramètre :
|
# Pour force le type d'une note, il faut rajouter le paramètre :
|
||||||
# forward=(forward.Const('TYPE', 'note_type') où TYPE est dans {user, club, special}
|
# forward=(forward.Const('TYPE', 'note_type') où TYPE est dans {user, club, special}
|
||||||
widgets = {
|
widgets = {
|
||||||
'destination': autocomplete.ModelSelect2(url='note:note_autocomplete',
|
'destination':
|
||||||
|
autocomplete.ModelSelect2(
|
||||||
|
url='note:note_autocomplete',
|
||||||
attrs={
|
attrs={
|
||||||
'data-placeholder': 'Note ...',
|
'data-placeholder': 'Note ...',
|
||||||
'data-minimum-input-length': 1,
|
'data-minimum-input-length': 1,
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,26 +35,38 @@ class TransactionForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Transaction
|
model = Transaction
|
||||||
fields = ('source', 'destination', 'reason', 'amount',)
|
fields = (
|
||||||
|
'source',
|
||||||
|
'destination',
|
||||||
|
'reason',
|
||||||
|
'amount',
|
||||||
|
)
|
||||||
|
|
||||||
# Voir ci-dessus
|
# Voir ci-dessus
|
||||||
widgets = {
|
widgets = {
|
||||||
'source': autocomplete.ModelSelect2(url='note:note_autocomplete',
|
'source':
|
||||||
|
autocomplete.ModelSelect2(
|
||||||
|
url='note:note_autocomplete',
|
||||||
attrs={
|
attrs={
|
||||||
'data-placeholder': 'Note ...',
|
'data-placeholder': 'Note ...',
|
||||||
'data-minimum-input-length': 1,
|
'data-minimum-input-length': 1,
|
||||||
},),
|
},
|
||||||
'destination': autocomplete.ModelSelect2(url='note:note_autocomplete',
|
),
|
||||||
|
'destination':
|
||||||
|
autocomplete.ModelSelect2(
|
||||||
|
url='note:note_autocomplete',
|
||||||
attrs={
|
attrs={
|
||||||
'data-placeholder': 'Note ...',
|
'data-placeholder': 'Note ...',
|
||||||
'data-minimum-input-length': 1,
|
'data-minimum-input-length': 1,
|
||||||
},),
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConsoForm(forms.ModelForm):
|
|
||||||
|
|
||||||
|
class ConsoForm(forms.ModelForm):
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
button: TransactionTemplate = TransactionTemplate.objects.filter(name=self.data['button']).get()
|
button: TransactionTemplate = TransactionTemplate.objects.filter(
|
||||||
|
name=self.data['button']).get()
|
||||||
self.instance.destination = button.destination
|
self.instance.destination = button.destination
|
||||||
self.instance.amount = button.amount
|
self.instance.amount = button.amount
|
||||||
self.instance.transaction_type = 'bouton'
|
self.instance.transaction_type = 'bouton'
|
||||||
|
@ -59,15 +75,18 @@ class ConsoForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Transaction
|
model = Transaction
|
||||||
fields = ('source',)
|
fields = ('source', )
|
||||||
|
|
||||||
# Le champ d'utilisateur est remplacé par un champ d'auto-complétion.
|
# Le champ d'utilisateur est remplacé par un champ d'auto-complétion.
|
||||||
# Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion
|
# Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion
|
||||||
# et récupère les aliases de note valides
|
# et récupère les aliases de note valides
|
||||||
widgets = {
|
widgets = {
|
||||||
'source': autocomplete.ModelSelect2(url='note:note_autocomplete',
|
'source':
|
||||||
|
autocomplete.ModelSelect2(
|
||||||
|
url='note:note_autocomplete',
|
||||||
attrs={
|
attrs={
|
||||||
'data-placeholder': 'Note ...',
|
'data-placeholder': 'Note ...',
|
||||||
'data-minimum-input-length': 1,
|
'data-minimum-input-length': 1,
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ from django.core.validators import RegexValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from polymorphic.models import PolymorphicModel
|
from polymorphic.models import PolymorphicModel
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Defines each note types
|
Defines each note types
|
||||||
"""
|
"""
|
||||||
|
@ -34,8 +33,7 @@ class Note(PolymorphicModel):
|
||||||
default=True,
|
default=True,
|
||||||
help_text=_(
|
help_text=_(
|
||||||
'Designates whether this note should be treated as active. '
|
'Designates whether this note should be treated as active. '
|
||||||
'Unselect this instead of deleting notes.'
|
'Unselect this instead of deleting notes.'),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
display_image = models.ImageField(
|
display_image = models.ImageField(
|
||||||
verbose_name=_('display image'),
|
verbose_name=_('display image'),
|
||||||
|
@ -85,7 +83,8 @@ class Note(PolymorphicModel):
|
||||||
"""
|
"""
|
||||||
Verify alias (simulate save)
|
Verify alias (simulate save)
|
||||||
"""
|
"""
|
||||||
aliases = Alias.objects.filter(normalized_name=Alias.normalize(str(self)))
|
aliases = Alias.objects.filter(
|
||||||
|
normalized_name=Alias.normalize(str(self)))
|
||||||
if aliases.exists():
|
if aliases.exists():
|
||||||
# Alias exists, so check if it is linked to this note
|
# Alias exists, so check if it is linked to this note
|
||||||
if aliases.first().note != self:
|
if aliases.first().note != self:
|
||||||
|
@ -181,15 +180,15 @@ class Alias(models.Model):
|
||||||
validators=[
|
validators=[
|
||||||
RegexValidator(
|
RegexValidator(
|
||||||
regex=settings.ALIAS_VALIDATOR_REGEX,
|
regex=settings.ALIAS_VALIDATOR_REGEX,
|
||||||
message=_('Invalid alias')
|
message=_('Invalid alias'),
|
||||||
)
|
)
|
||||||
] if settings.ALIAS_VALIDATOR_REGEX else []
|
] if settings.ALIAS_VALIDATOR_REGEX else [],
|
||||||
)
|
)
|
||||||
normalized_name = models.CharField(
|
normalized_name = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
unique=True,
|
unique=True,
|
||||||
default='',
|
default='',
|
||||||
editable=False
|
editable=False,
|
||||||
)
|
)
|
||||||
note = models.ForeignKey(
|
note = models.ForeignKey(
|
||||||
Note,
|
Note,
|
||||||
|
@ -209,11 +208,9 @@ class Alias(models.Model):
|
||||||
Normalizes a string: removes most diacritics and does casefolding
|
Normalizes a string: removes most diacritics and does casefolding
|
||||||
"""
|
"""
|
||||||
return ''.join(
|
return ''.join(
|
||||||
char
|
char for char in unicodedata.normalize('NFKD', string.casefold())
|
||||||
for char in unicodedata.normalize('NFKD', string.casefold())
|
|
||||||
if all(not unicodedata.category(char).startswith(cat)
|
if all(not unicodedata.category(char).startswith(cat)
|
||||||
for cat in {'M', 'P', 'Z', 'C'})
|
for cat in {'M', 'P', 'Z', 'C'})).casefold()
|
||||||
).casefold()
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -229,7 +226,8 @@ class Alias(models.Model):
|
||||||
raise ValidationError(_('Alias too long.'))
|
raise ValidationError(_('Alias too long.'))
|
||||||
try:
|
try:
|
||||||
if self != Alias.objects.get(normalized_name=normalized_name):
|
if self != Alias.objects.get(normalized_name=normalized_name):
|
||||||
raise ValidationError(_('An alias with a similar name '
|
raise ValidationError(
|
||||||
|
_('An alias with a similar name '
|
||||||
'already exists.'))
|
'already exists.'))
|
||||||
except Alias.DoesNotExist:
|
except Alias.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -7,16 +7,19 @@ from .models.transactions import Transaction
|
||||||
|
|
||||||
class HistoryTable(tables.Table):
|
class HistoryTable(tables.Table):
|
||||||
class Meta:
|
class Meta:
|
||||||
attrs = {'class':'table table-bordered table-condensed table-striped table-hover'}
|
attrs = {
|
||||||
|
'class':
|
||||||
|
'table table-bordered table-condensed table-striped table-hover'
|
||||||
|
}
|
||||||
model = Transaction
|
model = Transaction
|
||||||
template_name = 'django_tables2/bootstrap.html'
|
template_name = 'django_tables2/bootstrap.html'
|
||||||
sequence = ('...','total','valid')
|
sequence = ('...', 'total', 'valid')
|
||||||
|
|
||||||
total = tables.Column() #will use Transaction.total() !!
|
total = tables.Column() #will use Transaction.total() !!
|
||||||
|
|
||||||
def order_total(self, QuerySet, is_descending):
|
def order_total(self, QuerySet, is_descending):
|
||||||
# needed for rendering
|
# needed for rendering
|
||||||
QuerySet = QuerySet.annotate(
|
QuerySet = QuerySet.annotate(
|
||||||
total=F('amount') * F('quantity')
|
total=F('amount') *
|
||||||
).order_by(('-' if is_descending else '') + 'total')
|
F('quantity')).order_by(('-' if is_descending else '') + 'total')
|
||||||
return (QuerySet, True)
|
return (QuerySet, True)
|
||||||
|
|
|
@ -2,10 +2,17 @@ from django import template
|
||||||
|
|
||||||
|
|
||||||
def pretty_money(value):
|
def pretty_money(value):
|
||||||
if value%100 == 0:
|
if value % 100 == 0:
|
||||||
return "{:s}{:d} €".format("- " if value < 0 else "", abs(value) // 100)
|
return "{:s}{:d} €".format(
|
||||||
|
"- " if value < 0 else "",
|
||||||
|
abs(value) // 100,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return "{:s}{:d} € {:02d}".format("- " if value < 0 else "", abs(value) // 100, abs(value) % 100)
|
return "{:s}{:d} € {:02d}".format(
|
||||||
|
"- " if value < 0 else "",
|
||||||
|
abs(value) // 100,
|
||||||
|
abs(value) % 100,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
|
@ -12,6 +12,7 @@ from django.views.generic import CreateView, ListView, DetailView, UpdateView
|
||||||
from .models import Note, Transaction, TransactionCategory, TransactionTemplate, Alias
|
from .models import Note, Transaction, TransactionCategory, TransactionTemplate, Alias
|
||||||
from .forms import TransactionForm, TransactionTemplateForm, ConsoForm
|
from .forms import TransactionForm, TransactionTemplateForm, ConsoForm
|
||||||
|
|
||||||
|
|
||||||
class TransactionCreate(LoginRequiredMixin, CreateView):
|
class TransactionCreate(LoginRequiredMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Show transfer page
|
Show transfer page
|
||||||
|
@ -30,7 +31,6 @@ class TransactionCreate(LoginRequiredMixin, CreateView):
|
||||||
'to one or others')
|
'to one or others')
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
def get_form(self, form_class=None):
|
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.
|
If the user has no right to transfer funds, then it won't have the choice of the source of the transfer.
|
||||||
|
@ -56,7 +56,6 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
|
||||||
"""
|
"""
|
||||||
Auto complete note by aliases
|
Auto complete note by aliases
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
Quand une personne cherche un alias, une requête est envoyée sur l'API dédiée à l'auto-complétion.
|
Quand une personne cherche un alias, une requête est envoyée sur l'API dédiée à l'auto-complétion.
|
||||||
|
@ -101,27 +100,30 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
|
||||||
return str(result.note.pk)
|
return str(result.note.pk)
|
||||||
|
|
||||||
|
|
||||||
class TransactionTemplateCreateView(LoginRequiredMixin,CreateView):
|
class TransactionTemplateCreateView(LoginRequiredMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Create TransactionTemplate
|
Create TransactionTemplate
|
||||||
"""
|
"""
|
||||||
model = TransactionTemplate
|
model = TransactionTemplate
|
||||||
form_class = TransactionTemplateForm
|
form_class = TransactionTemplateForm
|
||||||
|
|
||||||
class TransactionTemplateListView(LoginRequiredMixin,ListView):
|
|
||||||
|
class TransactionTemplateListView(LoginRequiredMixin, ListView):
|
||||||
"""
|
"""
|
||||||
List TransactionsTemplates
|
List TransactionsTemplates
|
||||||
"""
|
"""
|
||||||
model = TransactionTemplate
|
model = TransactionTemplate
|
||||||
form_class = TransactionTemplateForm
|
form_class = TransactionTemplateForm
|
||||||
|
|
||||||
class TransactionTemplateUpdateView(LoginRequiredMixin,UpdateView):
|
|
||||||
|
class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
model = TransactionTemplate
|
model = TransactionTemplate
|
||||||
form_class = TransactionTemplateForm
|
form_class = TransactionTemplateForm
|
||||||
|
|
||||||
class ConsoView(LoginRequiredMixin,CreateView):
|
|
||||||
|
class ConsoView(LoginRequiredMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Consume
|
Consume
|
||||||
"""
|
"""
|
||||||
|
@ -139,11 +141,14 @@ class ConsoView(LoginRequiredMixin,CreateView):
|
||||||
if 'template_type' not in self.kwargs.keys():
|
if 'template_type' not in self.kwargs.keys():
|
||||||
return context
|
return context
|
||||||
|
|
||||||
template_type = TransactionCategory.objects.filter(name=self.kwargs.get('template_type')).get()
|
template_type = TransactionCategory.objects.filter(
|
||||||
context['buttons'] = TransactionTemplate.objects.filter(template_type=template_type)
|
name=self.kwargs.get('template_type')).get()
|
||||||
|
context['buttons'] = TransactionTemplate.objects.filter(
|
||||||
|
template_type=template_type)
|
||||||
context['title'] = template_type
|
context['title'] = template_type
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('note:consos',args=(self.kwargs.get('template_type'),))
|
return reverse('note:consos',
|
||||||
|
args=(self.kwargs.get('template_type'), ))
|
||||||
|
|
Loading…
Reference in New Issue