1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-06-21 01:48:21 +02:00

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

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

View File

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

View File

@ -2,10 +2,14 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from django.db.models import Q
from django.core.exceptions import ValidationError
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter, SearchFilter
from api.viewsets import ReadProtectedModelViewSet, ReadOnlyProtectedModelViewSet
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, \
TransactionTemplateSerializer, TransactionPolymorphicSerializer
@ -53,6 +57,22 @@ class AliasViewSet(ReadProtectedModelViewSet):
search_fields = ['$normalized_name', '$name', '$note__polymorphic_ctype__model', ]
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):
"""
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",
"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
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):
image = forms.ImageField(required=False,
label=_('select an image'),

View File

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

View File

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from django.db import models
from django.db.models import F
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
@ -93,12 +94,26 @@ class Transaction(PolymorphicModel):
related_name='+',
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(
Note,
on_delete=models.PROTECT,
related_name='+',
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(
verbose_name=_('created at'),
default=timezone.now,
@ -115,11 +130,19 @@ class Transaction(PolymorphicModel):
verbose_name=_('reason'),
max_length=255,
)
valid = models.BooleanField(
verbose_name=_('valid'),
default=True,
)
invalidity_reason = models.CharField(
verbose_name=_('invalidity reason'),
max_length=255,
default=None,
null=True,
)
class Meta:
verbose_name = _("transaction")
verbose_name_plural = _("transactions")
@ -134,6 +157,13 @@ class Transaction(PolymorphicModel):
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:
# When source == destination, no money is transfered
super().save(*args, **kwargs)
@ -152,6 +182,10 @@ class Transaction(PolymorphicModel):
self.source.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
super().save(*args, **kwargs)

View File

@ -5,6 +5,7 @@ import html
import django_tables2 as tables
from django.db.models import F
from django.utils.html import format_html
from django_tables2.utils import A
from django.utils.translation import gettext_lazy as _
@ -20,19 +21,48 @@ class HistoryTable(tables.Table):
'table table-condensed table-striped table-hover'
}
model = Transaction
exclude = ("id", "polymorphic_ctype", )
exclude = ("id", "polymorphic_ctype", "invalidity_reason", "source_alias", "destination_alias",)
template_name = 'django_tables2/bootstrap4.html'
sequence = ('...', 'type', 'total', 'valid', )
sequence = ('...', 'type', 'total', 'valid',)
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()
total = tables.Column() # will use Transaction.total() !!
valid = tables.Column(attrs={"td": {"id": lambda record: "validate_" + str(record.id),
"class": lambda record: str(record.valid).lower() + ' validate',
"onclick": lambda record: 'de_validate(' + str(record.id) + ', '
+ str(record.valid).lower() + ')'}})
valid = tables.Column(
attrs={
"td": {
"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):
# needed for rendering
@ -53,13 +83,23 @@ class HistoryTable(tables.Table):
def render_reason(self, value):
return html.unescape(value)
def render_valid(self, value):
return "" if value else ""
def render_valid(self, value, record):
"""
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
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:
attrs = {
'class':
'table table condensed table-striped table-hover'
'table table condensed table-striped table-hover',
'id':"alias_table"
}
model = Alias
fields = ('name',)
@ -75,15 +116,11 @@ class AliasTable(tables.Table):
show_header = False
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',
args=[A('pk')],
attrs={
'td': {'class': 'col-sm-2'},
'a': {'class': 'btn btn-danger'}},
text='delete', accessor='pk')
delete_col = tables.TemplateColumn(template_code=DELETE_TEMPLATE,
extra_context={"delete_trans": _('delete')},
attrs={'td': {'class': 'col-sm-1'}})
class ButtonTable(tables.Table):
@ -103,11 +140,11 @@ class ButtonTable(tables.Table):
edit = tables.LinkColumn('note:template_update',
args=[A('pk')],
attrs={'td': {'class': 'col-sm-1'},
'a': {'class': 'btn btn-primary'}},
'a': {'class': 'btn btn-sm btn-primary'}},
text=_('edit'),
accessor='pk')
delete = tables.TemplateColumn(template_code=DELETE_TEMPLATE,
delete_col = tables.TemplateColumn(template_code=DELETE_TEMPLATE,
extra_context={"delete_trans": _('delete')},
attrs={'td': {'class': 'col-sm-1'}})