1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-06-20 17:41:55 +02:00

Merge branch 'master' into logging

This commit is contained in:
Yohann D'ANELLO
2020-02-27 14:36:27 +01:00
16 changed files with 145 additions and 71 deletions

View File

@ -14,6 +14,11 @@ from crispy_forms.layout import Layout
class SignUpForm(UserCreationForm):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.fields['username'].widget.attrs.pop("autofocus", None)
self.fields['first_name'].widget.attrs.update({"autofocus":"autofocus"})
class Meta:
model = User
fields = ['first_name', 'last_name', 'username', 'email']

27
apps/member/hashers.py Normal file
View File

@ -0,0 +1,27 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import hashlib
from django.contrib.auth.hashers import PBKDF2PasswordHasher
from django.utils.crypto import constant_time_compare
class CustomNK15Hasher(PBKDF2PasswordHasher):
"""
Permet d'importer les mots de passe depuis la Note KFet 2015.
Si un hash de mot de passe est de la forme :
`custom_nk15$<NB>$<ENCODED>`
où <NB> est un entier quelconque (symbolisant normalement un nombre d'itérations)
et <ENCODED> le hash du mot de passe dans la Note Kfet 2015,
alors ce hasher va vérifier le mot de passe.
N'ayant pas la priorité (cf note_kfet/settings/base.py), le mot de passe sera
converti automatiquement avec l'algorithme PBKDF2.
"""
algorithm = "custom_nk15"
def verify(self, password, encoded):
if '|' in encoded:
salt, db_hashed_pass = encoded.split('$')[2].split('|')
return constant_time_compare(hashlib.sha256((salt + password).encode("utf-8")).hexdigest(), db_hashed_pass)
return super().verify(password, encoded)

View File

@ -114,12 +114,13 @@ class UserDetailView(LoginRequiredMixin, DetailView):
"""
Affiche les informations sur un utilisateur, sa note, ses clubs...
"""
model = Profile
context_object_name = "profile"
model = User
context_object_name = "user_object"
template_name = "member/profile_detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = context['profile'].user
user = context['user_object']
history_list = \
Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note))
context['history_list'] = HistoryTable(history_list)

View File

@ -7,7 +7,8 @@ from polymorphic.admin import PolymorphicChildModelAdmin, \
PolymorphicChildModelFilter, PolymorphicParentModelAdmin
from .models.notes import Alias, Note, NoteClub, NoteSpecial, NoteUser
from .models.transactions import Transaction, TransactionCategory, TransactionTemplate
from .models.transactions import Transaction, TemplateCategory, TransactionTemplate, \
TemplateTransaction, MembershipTransaction
class AliasInlines(admin.TabularInline):
@ -97,13 +98,14 @@ class NoteUserAdmin(PolymorphicChildModelAdmin):
@admin.register(Transaction)
class TransactionAdmin(admin.ModelAdmin):
class TransactionAdmin(PolymorphicParentModelAdmin):
"""
Admin customisation for Transaction
"""
child_models = (TemplateTransaction, MembershipTransaction)
list_display = ('created_at', 'poly_source', 'poly_destination',
'quantity', 'amount', 'transaction_type', 'valid')
list_filter = ('transaction_type', 'valid')
'quantity', 'amount', 'valid')
list_filter = ('valid',)
autocomplete_fields = (
'source',
'destination',
@ -132,7 +134,7 @@ class TransactionAdmin(admin.ModelAdmin):
"""
if obj: # user is editing an existing object
return 'created_at', 'source', 'destination', 'quantity',\
'amount', 'transaction_type'
'amount'
return []
@ -141,8 +143,8 @@ class TransactionTemplateAdmin(admin.ModelAdmin):
"""
Admin customisation for TransactionTemplate
"""
list_display = ('name', 'poly_destination', 'amount', 'template_type')
list_filter = ('template_type', )
list_display = ('name', 'poly_destination', 'amount', 'category', 'display', )
list_filter = ('category', 'display')
autocomplete_fields = ('destination', )
def poly_destination(self, obj):
@ -154,8 +156,8 @@ class TransactionTemplateAdmin(admin.ModelAdmin):
poly_destination.short_description = _('destination')
@admin.register(TransactionCategory)
class TransactionCategoryAdmin(admin.ModelAdmin):
@admin.register(TemplateCategory)
class TemplateCategoryAdmin(admin.ModelAdmin):
"""
Admin customisation for TransactionTemplate
"""

View File

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

View File

@ -4,7 +4,7 @@
from dal import autocomplete
from django import forms
from .models import Transaction, TransactionTemplate
from .models import Transaction, TransactionTemplate, TemplateTransaction
class TransactionTemplateForm(forms.ModelForm):
@ -31,8 +31,6 @@ class TransactionTemplateForm(forms.ModelForm):
class TransactionForm(forms.ModelForm):
def save(self, commit=True):
self.instance.transaction_type = 'transfert'
super().save(commit)
class Meta:
@ -71,12 +69,13 @@ class ConsoForm(forms.ModelForm):
name=self.data['button']).get()
self.instance.destination = button.destination
self.instance.amount = button.amount
self.instance.transaction_type = 'bouton'
self.instance.reason = button.name
self.instance.reason = '{} ({})'.format(button.name, button.category)
self.instance.name = button.name
self.instance.category = button.category
super().save(commit)
class Meta:
model = Transaction
model = TemplateTransaction
fields = ('source', )
# Le champ d'utilisateur est remplacé par un champ d'auto-complétion.

View File

@ -3,11 +3,12 @@
from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser
from .transactions import MembershipTransaction, Transaction, \
TransactionCategory, TransactionTemplate
TemplateCategory, TransactionTemplate, TemplateTransaction
__all__ = [
# Notes
'Alias', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser',
# Transactions
'MembershipTransaction', 'Transaction', 'TransactionCategory', 'TransactionTemplate',
'MembershipTransaction', 'Transaction', 'TemplateCategory', 'TransactionTemplate',
'TemplateTransaction',
]

View File

@ -27,6 +27,12 @@ class Note(PolymorphicModel):
help_text=_('in centimes, money credited for this instance'),
default=0,
)
last_negative= models.DateTimeField(
verbose_name=_('last negative date'),
help_text=_('last time the balance was negative'),
null=True,
blank=True,
)
is_active = models.BooleanField(
_('active'),
default=True,
@ -64,7 +70,8 @@ class Note(PolymorphicModel):
if aliases.exists():
# Alias exists, so check if it is linked to this note
if aliases.first().note != self:
raise ValidationError(_('This alias is already taken.'))
raise ValidationError(_('This alias is already taken.'),
code="same_alias")
# Save note
super().save(*args, **kwargs)
@ -87,7 +94,8 @@ class Note(PolymorphicModel):
if aliases.exists():
# Alias exists, so check if it is linked to this note
if aliases.first().note != self:
raise ValidationError(_('This alias is already taken.'))
raise ValidationError(_('This alias is already taken.'),
code="same_alias",)
else:
# Alias does not exist yet, so check if it can exist
a = Alias(name=str(self))
@ -222,16 +230,19 @@ class Alias(models.Model):
def clean(self):
normalized_name = Alias.normalize(self.name)
if len(normalized_name) >= 255:
raise ValidationError(_('Alias too long.'))
raise ValidationError(_('Alias is too long.'),
code='alias_too_long')
try:
if self != Alias.objects.get(normalized_name=normalized_name):
raise ValidationError(
_('An alias with a similar name '
'already exists.'))
sim_alias = Alias.objects.get(normalized_name=normalized_name)
if self != sim_alias:
raise ValidationError(_('An alias with a similar name already exists:'),
code="same_alias"
)
except Alias.DoesNotExist:
pass
def delete(self, using=None, keep_parents=False):
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")
return super().delete(using, keep_parents)

View File

@ -5,6 +5,7 @@ from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from polymorphic.models import PolymorphicModel
from .notes import Note, NoteClub
@ -13,7 +14,7 @@ Defines transactions
"""
class TransactionCategory(models.Model):
class TemplateCategory(models.Model):
"""
Defined a recurrent transaction category
@ -43,6 +44,7 @@ class TransactionTemplate(models.Model):
verbose_name=_('name'),
max_length=255,
unique=True,
error_messages={'unique':_("A template with this name already exist")},
)
destination = models.ForeignKey(
NoteClub,
@ -54,12 +56,19 @@ class TransactionTemplate(models.Model):
verbose_name=_('amount'),
help_text=_('in centimes'),
)
template_type = models.ForeignKey(
TransactionCategory,
category = models.ForeignKey(
TemplateCategory,
on_delete=models.PROTECT,
verbose_name=_('type'),
max_length=31,
)
display = models.BooleanField(
default = True,
)
description = models.CharField(
verbose_name=_('description'),
max_length=255,
)
class Meta:
verbose_name = _("transaction template")
@ -69,7 +78,7 @@ class TransactionTemplate(models.Model):
return reverse('note:template_update', args=(self.pk, ))
class Transaction(models.Model):
class Transaction(PolymorphicModel):
"""
General transaction between two :model:`note.Note`
@ -100,10 +109,6 @@ class Transaction(models.Model):
default=1,
)
amount = models.PositiveIntegerField(verbose_name=_('amount'), )
transaction_type = models.CharField(
verbose_name=_('type'),
max_length=31,
)
reason = models.CharField(
verbose_name=_('reason'),
max_length=255,
@ -144,6 +149,22 @@ class Transaction(models.Model):
return self.amount * self.quantity
class TemplateTransaction(Transaction):
"""
Special type of :model:`note.Transaction` associated to a :model:`note.TransactionTemplate`.
"""
template = models.ForeignKey(
TransactionTemplate,
null=True,
on_delete=models.SET_NULL,
)
category = models.ForeignKey(
TemplateCategory,
on_delete=models.PROTECT,
)
class MembershipTransaction(Transaction):
"""
Special type of :model:`note.Transaction` associated to a :model:`member.Membership`.

View File

@ -8,7 +8,7 @@ from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, ListView, UpdateView
from .models import Transaction, TransactionTemplate, Alias
from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction
from .forms import TransactionForm, TransactionTemplateForm, ConsoForm
@ -129,7 +129,7 @@ class ConsoView(LoginRequiredMixin, CreateView):
"""
Consume
"""
model = Transaction
model = TemplateTransaction
template_name = "note/conso_form.html"
form_class = ConsoForm
@ -138,8 +138,8 @@ class ConsoView(LoginRequiredMixin, CreateView):
Add some context variables in template such as page title
"""
context = super().get_context_data(**kwargs)
context['transaction_templates'] = TransactionTemplate.objects.all() \
.order_by('template_type')
context['transaction_templates'] = TransactionTemplate.objects.filter(display=True) \
.order_by('category')
context['title'] = _("Consommations")
# select2 compatibility
@ -152,3 +152,4 @@ class ConsoView(LoginRequiredMixin, CreateView):
When clicking a button, reload the same page
"""
return reverse('note:consos')