diff --git a/apps/member/models.py b/apps/member/models.py index cdbb933274e7cdccd378d956bef1480ffbf750f7..d0051e59d612019df3f4cb104857ba743c09e713 100644 --- a/apps/member/models.py +++ b/apps/member/models.py @@ -4,6 +4,7 @@ import datetime from django.conf import settings +from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse, reverse_lazy from django.utils.translation import gettext_lazy as _ @@ -67,6 +68,13 @@ class Club(models.Model): email = models.EmailField( verbose_name=_('email'), ) + parent_club = models.ForeignKey( + 'self', + null=True, + blank=True, + on_delete=models.PROTECT, + verbose_name=_('parent club'), + ) # Memberships membership_fee = models.PositiveIntegerField( @@ -158,6 +166,12 @@ class Membership(models.Model): else: 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: verbose_name = _('membership') verbose_name_plural = _('memberships') diff --git a/apps/member/urls.py b/apps/member/urls.py index bc536f604834ce94f61d7c7def8743887d82f69a..0b705bfde3a06330e84a4ddda17c19341f80adb6 100644 --- a/apps/member/urls.py +++ b/apps/member/urls.py @@ -14,12 +14,12 @@ urlpatterns = [ 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_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/<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_pic', views.ProfilePictureUpdateView.as_view(), name="user_update_pic"), - path('user/<int:pk>/aliases', views.AliasView.as_view(), name="user_alias"), - path('user/aliases/delete/<int:pk>', views.DeleteAliasView.as_view(), name="user_alias_delete"), + path('user/<int:pk>/aliases', views.ProfileAliasView.as_view(), name="user_alias"), path('manage-auth-token/', views.ManageAuthTokens.as_view(), name='auth_token'), # API for the user autocompleter path('user/user-autocomplete', views.UserAutocomplete.as_view(), name="user_autocomplete"), diff --git a/apps/member/views.py b/apps/member/views.py index c1bdf5ccfeec57281ed96f64bf71ca0755784e80..82510c6535dead932ac0df552279d9aa78209cd1 100644 --- a/apps/member/views.py +++ b/apps/member/views.py @@ -20,7 +20,8 @@ from django.views.generic import CreateView, DetailView, UpdateView, TemplateVie from django.views.generic.edit import FormMixin from django_tables2.views import SingleTableView 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.transactions import Transaction from note.tables import HistoryTable, AliasTable @@ -143,10 +144,6 @@ class UserDetailView(LoginRequiredMixin, DetailView): club_list = \ Membership.objects.all().filter(user=user).only("club") context['club_list'] = ClubTable(club_list) - context['title'] = _("Account #%(id)s: %(username)s") % { - 'id': user.pk, - 'username': user.username, - } return context @@ -171,57 +168,18 @@ class UserListView(LoginRequiredMixin, SingleTableView): context["filter"] = self.filter return context - -class AliasView(LoginRequiredMixin, FormMixin, DetailView): + +class ProfileAliasView(LoginRequiredMixin, DetailView): model = User template_name = 'member/profile_alias.html' context_object_name = 'user_object' - form_class = AliasForm - + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - note = context['user_object'].note + note = context['object'].note context["aliases"] = AliasTable(note.alias_set.all()) 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): form_class = ImageForm @@ -368,6 +326,17 @@ class ClubDetailView(LoginRequiredMixin, DetailView): context['member_list'] = club_member 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): model = Club @@ -395,12 +364,12 @@ class ClubAddMemberView(LoginRequiredMixin, CreateView): return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view") | PermissionBackend.filter_queryset(self.request.user, Membership, "change")) - def get_context_data(self, **kwargs): + club = Club.objects.get(pk=self.kwargs["pk"]) context = super().get_context_data(**kwargs) context['formset'] = MemberFormSet() context['helper'] = FormSetHelper() - + context['club'] = club context['no_cache'] = True return context diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py index a51b4263b7eba79ab45f5a47ac4e2d3de54dfddb..05c35aa552096124d813e5f04708bd63609a0543 100644 --- a/apps/note/api/serializers.py +++ b/apps/note/api/serializers.py @@ -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): diff --git a/apps/note/api/views.py b/apps/note/api/views.py index fc4a0e8f56695fa9c18b5c5af937d79092e46894..e70eb49e3dfbf0c6584ecd209a17719fddac4083 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -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. diff --git a/apps/note/fixtures/button.json b/apps/note/fixtures/button.json new file mode 100644 index 0000000000000000000000000000000000000000..39f880b57586f3906b0e95acbd2f1f67c475e44a --- /dev/null +++ b/apps/note/fixtures/button.json @@ -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" + } + } +] diff --git a/apps/note/fixtures/initial.json b/apps/note/fixtures/initial.json index efe37afa4244caf78f775be776150ca518fc461d..72853eb76acf3128bafc26c34b1b1c28da70afde 100644 --- a/apps/note/fixtures/initial.json +++ b/apps/note/fixtures/initial.json @@ -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" - } } ] \ No newline at end of file diff --git a/apps/note/forms.py b/apps/note/forms.py index ac6adaaf5d10a0f8157182062f8afd5d60412c5e..60252ad54c9e5abaff5265366a2eed77fd38a0bb 100644 --- a/apps/note/forms.py +++ b/apps/note/forms.py @@ -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'), diff --git a/apps/note/models/notes.py b/apps/note/models/notes.py index b6b00aa8ec91fedda1449a20948625f4ea8256e1..43faabfe59aedf8dc71ae3bf85139c8967eea3fd 100644 --- a/apps/note/models/notes.py +++ b/apps/note/models/notes.py @@ -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) diff --git a/apps/note/models/transactions.py b/apps/note/models/transactions.py index 0e40edf699cf9653e9a814f1d2a8895d73038a2e..c6b8baa633fdbe7abba189a022f34c75b2a1379d 100644 --- a/apps/note/models/transactions.py +++ b/apps/note/models/transactions.py @@ -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) diff --git a/apps/note/tables.py b/apps/note/tables.py index 20054d2cf52fbb79a2dfd277b3bab0243dcd0c18..201b6c43cc3fa4e9f41bd20f5cee2447ee7d83a5 100644 --- a/apps/note/tables.py +++ b/apps/note/tables.py @@ -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'}}) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 4c7de16d5bf5bb5c046cff28546bb81b477ec9b9..43d39a3684e01354a243ee3eae72e345cf75e585 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -327,7 +327,7 @@ "note", "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", "mask": 1, "field": "", @@ -387,7 +387,7 @@ "note", "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", "mask": 2, "field": "", diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index b6a8c1202f4818fc342f9423b7bcead9f6770217..c9eda5aaff8f81f6fc84e4d7c4f9d96730fefa9f 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\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" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -24,8 +24,8 @@ msgstr "" #: apps/activity/models.py:19 apps/activity/models.py:44 #: 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/transactions.py:44 apps/note/models/transactions.py:198 +#: apps/note/models/notes.py:188 apps/note/models/transactions.py:25 +#: apps/note/models/transactions.py:45 apps/note/models/transactions.py:232 #: templates/member/profile_detail.html:15 msgid "name" msgstr "" @@ -46,13 +46,13 @@ msgstr "" msgid "activity types" msgstr "" -#: apps/activity/models.py:48 apps/note/models/transactions.py:69 -#: apps/permission/models.py:90 +#: apps/activity/models.py:48 apps/note/models/transactions.py:70 +#: apps/permission/models.py:91 msgid "description" msgstr "" #: apps/activity/models.py:54 apps/note/models/notes.py:164 -#: apps/note/models/transactions.py:62 +#: apps/note/models/transactions.py:63 msgid "type" msgstr "" @@ -120,11 +120,11 @@ msgstr "" msgid "create" msgstr "" -#: apps/logs/models.py:61 +#: apps/logs/models.py:61 apps/note/tables.py:147 msgid "edit" msgstr "" -#: apps/logs/models.py:62 +#: apps/logs/models.py:62 apps/note/tables.py:151 msgid "delete" msgstr "" @@ -210,7 +210,7 @@ msgstr "" msgid "clubs" msgstr "" -#: apps/member/models.py:120 apps/permission/models.py:275 +#: apps/member/models.py:120 apps/permission/models.py:276 msgid "role" msgstr "" @@ -255,12 +255,12 @@ msgstr "" msgid "Alias successfully deleted" 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" msgstr "" #: 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" msgstr "" @@ -310,7 +310,7 @@ msgstr "" msgid "display image" 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" msgstr "" @@ -384,55 +384,64 @@ msgstr "" msgid "You can't delete your main alias." msgstr "" -#: apps/note/models/transactions.py:30 +#: apps/note/models/transactions.py:31 msgid "transaction category" msgstr "" -#: apps/note/models/transactions.py:31 +#: apps/note/models/transactions.py:32 msgid "transaction categories" msgstr "" -#: apps/note/models/transactions.py:47 +#: apps/note/models/transactions.py:48 msgid "A template with this name already exist" 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" msgstr "" -#: apps/note/models/transactions.py:57 +#: apps/note/models/transactions.py:58 msgid "in centimes" msgstr "" -#: apps/note/models/transactions.py:75 +#: apps/note/models/transactions.py:76 msgid "transaction template" msgstr "" -#: apps/note/models/transactions.py:76 +#: apps/note/models/transactions.py:77 msgid "transaction templates" 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" msgstr "" -#: apps/note/models/transactions.py:115 +#: apps/note/models/transactions.py:130 msgid "reason" msgstr "" -#: apps/note/models/transactions.py:119 +#: apps/note/models/transactions.py:135 msgid "valid" 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" msgstr "" -#: apps/note/models/transactions.py:125 +#: apps/note/models/transactions.py:148 msgid "transactions" 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:145 msgid "Transfer" @@ -634,15 +643,15 @@ msgid "" "again unless your session expires or you logout." msgstr "" -#: note_kfet/settings/base.py:153 +#: note_kfet/settings/base.py:151 msgid "German" msgstr "" -#: note_kfet/settings/base.py:154 +#: note_kfet/settings/base.py:152 msgid "English" msgstr "" -#: note_kfet/settings/base.py:155 +#: note_kfet/settings/base.py:153 msgid "French" msgstr "" @@ -650,18 +659,14 @@ msgstr "" msgid "The ENS Paris-Saclay BDE note." msgstr "" -#: templates/base.html:84 +#: templates/base.html:87 msgid "Clubs" msgstr "" -#: templates/base.html:89 +#: templates/base.html:92 msgid "Activities" msgstr "" -#: templates/base.html:94 -msgid "Buttons" -msgstr "" - #: templates/cas_server/base.html:7 msgid "Central Authentication Service" msgstr "" @@ -798,7 +803,7 @@ msgstr "" msgid "Sign up" 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" msgstr "" @@ -842,12 +847,28 @@ msgstr "" msgid "Transfer type" 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:169 #: templates/note/transaction_form.html:176 msgid "Select receivers" msgstr "" +#: templates/note/transaction_form.html:128 +msgid "Amount" +msgstr "" + #: templates/note/transaction_form.html:138 msgid "Reason" msgstr "" @@ -864,6 +885,22 @@ msgstr "" msgid "Buttons list" 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 msgid "Thanks for spending some quality time with the Web site today." msgstr "" @@ -873,7 +910,7 @@ msgid "Log in again" msgstr "" #: 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 msgid "Log in" msgstr "" @@ -885,7 +922,15 @@ msgid "" "page. Would you like to login to a different account?" 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?" msgstr "" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 67af6beeac1bcf6e5c4cd6f83f6d46db953320ab..ca43d5a4ccfcddcc42fdf81678c08499904d693f 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\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" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\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/member/models.py:63 apps/member/models.py:114 -#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24 -#: apps/note/models/transactions.py:44 apps/note/models/transactions.py:198 +#: apps/note/models/notes.py:188 apps/note/models/transactions.py:25 +#: apps/note/models/transactions.py:45 apps/note/models/transactions.py:232 #: templates/member/profile_detail.html:15 msgid "name" msgstr "nom" @@ -41,13 +41,13 @@ msgstr "type d'activité" msgid "activity types" msgstr "types d'activité" -#: apps/activity/models.py:48 apps/note/models/transactions.py:69 -#: apps/permission/models.py:90 +#: apps/activity/models.py:48 apps/note/models/transactions.py:70 +#: apps/permission/models.py:91 msgid "description" msgstr "description" #: apps/activity/models.py:54 apps/note/models/notes.py:164 -#: apps/note/models/transactions.py:62 +#: apps/note/models/transactions.py:63 msgid "type" msgstr "type" @@ -115,11 +115,11 @@ msgstr "Nouvelles données" msgid "create" msgstr "Créer" -#: apps/logs/models.py:61 +#: apps/logs/models.py:61 apps/note/tables.py:147 msgid "edit" msgstr "Modifier" -#: apps/logs/models.py:62 +#: apps/logs/models.py:62 apps/note/tables.py:151 msgid "delete" msgstr "Supprimer" @@ -209,7 +209,7 @@ msgstr "club" msgid "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" msgstr "rôle" @@ -254,12 +254,12 @@ msgstr "Compte n°%(id)s : %(username)s" msgid "Alias successfully deleted" msgstr "L'alias a bien été supprimé" -#: apps/note/admin.py:120 apps/note/models/transactions.py:94 +#: apps/note/admin.py:120 apps/note/models/transactions.py:95 msgid "source" msgstr "source" #: apps/note/admin.py:128 apps/note/admin.py:156 -#: apps/note/models/transactions.py:53 apps/note/models/transactions.py:100 +#: apps/note/models/transactions.py:54 apps/note/models/transactions.py:108 msgid "destination" msgstr "destination" @@ -310,7 +310,7 @@ msgstr "" msgid "display image" 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" 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." msgstr "Vous ne pouvez pas supprimer votre alias principal." -#: apps/note/models/transactions.py:30 +#: apps/note/models/transactions.py:31 msgid "transaction category" msgstr "catégorie de transaction" -#: apps/note/models/transactions.py:31 +#: apps/note/models/transactions.py:32 msgid "transaction categories" 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" 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" msgstr "montant" -#: apps/note/models/transactions.py:57 +#: apps/note/models/transactions.py:58 msgid "in centimes" msgstr "en centimes" -#: apps/note/models/transactions.py:75 +#: apps/note/models/transactions.py:76 msgid "transaction template" msgstr "modèle de transaction" -#: apps/note/models/transactions.py:76 +#: apps/note/models/transactions.py:77 msgid "transaction templates" 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" msgstr "quantité" @@ -634,15 +639,15 @@ msgid "" "again unless your session expires or you logout." msgstr "" -#: note_kfet/settings/base.py:153 +#: note_kfet/settings/base.py:151 msgid "German" msgstr "" -#: note_kfet/settings/base.py:154 +#: note_kfet/settings/base.py:152 msgid "English" msgstr "" -#: note_kfet/settings/base.py:155 +#: note_kfet/settings/base.py:153 msgid "French" msgstr "" @@ -650,18 +655,14 @@ msgstr "" msgid "The ENS Paris-Saclay BDE note." msgstr "La note du BDE de l'ENS Paris-Saclay." -#: templates/base.html:84 +#: templates/base.html:87 msgid "Clubs" msgstr "Clubs" -#: templates/base.html:89 +#: templates/base.html:92 msgid "Activities" msgstr "Activités" -#: templates/base.html:94 -msgid "Buttons" -msgstr "Boutons" - #: templates/cas_server/base.html:7 msgid "Central Authentication Service" msgstr "" @@ -800,7 +801,7 @@ msgstr "Sauvegarder les changements" msgid "Sign up" 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" msgstr "Sélection des émetteurs" @@ -844,12 +845,28 @@ msgstr "Paiement externe" msgid "Transfer type" 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:169 #: templates/note/transaction_form.html:176 msgid "Select receivers" msgstr "Sélection des destinataires" +#: templates/note/transaction_form.html:128 +msgid "Amount" +msgstr "Montant" + #: templates/note/transaction_form.html:138 msgid "Reason" msgstr "Raison" @@ -866,6 +883,22 @@ msgstr "Note à débiter" msgid "Buttons list" 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 msgid "Thanks for spending some quality time with the Web site today." msgstr "" @@ -875,7 +908,7 @@ msgid "Log in again" msgstr "" #: 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 msgid "Log in" msgstr "" @@ -887,7 +920,15 @@ msgid "" "page. Would you like to login to a different account?" 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?" msgstr "" diff --git a/static/js/alias.js b/static/js/alias.js new file mode 100644 index 0000000000000000000000000000000000000000..267410daccf36d42664f4b7df5832ddab2037edc --- /dev/null +++ b/static/js/alias.js @@ -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); + }); + } diff --git a/static/js/base.js b/static/js/base.js index b22df07710b59ec1fe25d881b3cb3def98984ee8..d21bd43389aa624c6fd149ac63557616068864ca 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -28,7 +28,15 @@ function addMsg(msg, alert_type) { + msg + "</div>\n"; 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 */ @@ -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 -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>"); // 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 }, data: { - "resourcetype": "RecurrentTransaction", - valid: !validated + resourcetype: "RecurrentTransaction", + valid: !validated, + invalidity_reason: invalidity_reason, }, success: function () { // Refresh jQuery objects - $(".validate").click(de_validate); + $(".validate").click(in_validate); refreshBalance(); // error if this method doesn't exist. Please define it. diff --git a/static/js/consos.js b/static/js/consos.js index 896f996ccccf6117ffa828d37c58c8aa6471c9aa..20859933431612d6db881f24fb1d6ce42bf61a72 100644 --- a/static/js/consos.js +++ b/static/js/consos.js @@ -167,7 +167,7 @@ function reset() { function consumeAll() { notes_display.forEach(function(note_display) { buttons.forEach(function(button) { - consume(note_display.id, button.dest, button.quantity * note_display.quantity, button.amount, + 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); }); }); @@ -176,6 +176,7 @@ function consumeAll() { /** * Create a new transaction from a button through the API. * @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 quantity The quantity sold (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 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/", { "csrfmiddlewaretoken": CSRF_TOKEN, @@ -195,12 +196,32 @@ function consume(source, dest, quantity, amount, reason, type, category, templat "polymorphic_ctype": type, "resourcetype": "RecurrentTransaction", "source": source, + "source_alias": source_alias, "destination": dest, "category": category, "template": template }, reset).fail(function (e) { - reset(); - - addMsg("Une erreur est survenue lors de la transaction : " + e.responseText, "danger"); + $.post("/api/note/transaction/transaction/", + { + "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); + }); }); } diff --git a/static/js/transfer.js b/static/js/transfer.js index a417191082ccbffbd1a1db8036e4ad040a7af5ba..cf62e45376ec9f201964cf71332055f29008b01a 100644 --- a/static/js/transfer.js +++ b/static/js/transfer.js @@ -83,19 +83,41 @@ $("#transfer").click(function() { "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE, "resourcetype": "Transaction", "source": user_id, - "destination": dest.id - }, function () { + "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 été fait avec succès !", "success"); reset(); - }).fail(function (err) { - addMsg("Le transfert de " - + pretty_money(dest.quantity * 100 * $("#amount").val()) + " de votre note " - + " vers la note " + dest.name + " a échoué : " + err.responseText, "danger"); + }).fail(function () { + $.post("/api/note/transaction/transaction/", + { + "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, "resourcetype": "Transaction", "source": source.id, - "destination": dest.id - }, function () { + "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 été fait avec succès !", "success"); reset(); }).fail(function (err) { - addMsg("Le transfert de " - + pretty_money(source.quantity * dest.quantity * 100 * $("#amount").val()) + " de la note " + source.name - + " vers la note " + dest.name + " a échoué : " + err.responseText, "danger"); + $.post("/api/note/transaction/transaction/", + { + "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, "resourcetype": "SpecialTransaction", "source": source, + "source_alias": source.name, "destination": dest, + "destination_alias": dest.name, "last_name": $("#last_name").val(), "first_name": $("#first_name").val(), "bank": $("#bank").val() - }, function () { + }).done(function () { addMsg("Le crédit/retrait a bien été effectué !", "success"); reset(); }).fail(function (err) { - addMsg("Le crédit/transfert a échoué : " + err.responseText, "danger"); + addMsg("Le crédit/retrait a échoué : " + err.responseText, "danger"); reset(); }); } diff --git a/templates/base.html b/templates/base.html index a12370713186c45b625f64c275c71e30ffce4a05..26903c2f1a00defa1401ec5d9c7afd5d8eff1502 100644 --- a/templates/base.html +++ b/templates/base.html @@ -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> </li> {% endif %} + {% if "note.transaction"|not_empty_model_list %} <li class="nav-item active"> <a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a> </li> + {% endif %} {% if "member.club"|not_empty_model_list %} <li class="nav-item active"> <a class="nav-link" href="{% url 'member:club_list' %}"><i class="fa fa-users"></i> {% trans 'Clubs' %}</a> diff --git a/templates/member/add_members.html b/templates/member/add_members.html index 8032af30cc368e3595db3ea304c2b41f16a5e447..8b57e7d4ae11f34b4d38e8c1564410ab1cf7f011 100644 --- a/templates/member/add_members.html +++ b/templates/member/add_members.html @@ -1,7 +1,11 @@ -{% extends "base.html" %} +{% extends "member/noteowner_detail.html" %} {% load crispy_forms_tags %} {% load static %} -{% block content %} + +{% block profile_info %} +{% include "member/club_info.html" %} +{% endblock %} +{% block profile_content %} <form method="post" action=""> {% csrf_token %} @@ -10,9 +14,9 @@ <input type="submit" name="submit" value="Add Members" class="btn btn-primary" id="submit-save"> </div> </form> +{% endblock %} -<!-- Include formset plugin - including jQuery dependency --> -<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> +{% block extrajavascript %} <script src="{% static 'js/dynamic-formset.js' %}"></script> <script> $('.formset-row').formset({ diff --git a/templates/member/alias_update.html b/templates/member/alias_update.html new file mode 100644 index 0000000000000000000000000000000000000000..d1f6115b8ddccecfa32a6ef70c0bfeb8782cde31 --- /dev/null +++ b/templates/member/alias_update.html @@ -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> diff --git a/templates/member/club_alias.html b/templates/member/club_alias.html new file mode 100644 index 0000000000000000000000000000000000000000..4b6f2882ccd827d0815b668eb1e05a7267226fe0 --- /dev/null +++ b/templates/member/club_alias.html @@ -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%} diff --git a/templates/member/club_info.html b/templates/member/club_info.html index a88527fcac1af3580d87462f27a277c9b2f75473..539d9867abe81522550ede08531d6cee272580c4 100644 --- a/templates/member/club_info.html +++ b/templates/member/club_info.html @@ -1,5 +1,8 @@ {% load i18n static pretty_money %} <div class="card bg-light shadow"> + <div class="card-header text-center"> + <h4> Club {{ club.name }} </h4> + </div> <div class="card-top text-center"> <a href="{% url 'member:club_update_pic' club.pk %}"> <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> <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> <dd class="col-xl-6">{{ club.membership_start }}</dd> @@ -22,11 +28,19 @@ <dt class="col-xl-6">{% trans 'membership fee'|capfirst %}</dt> <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> <dt class="col-xl-3">{% trans 'email'|capfirst %}</dt> <dd class="col-xl-9"><a href="mailto:{{ club.email }}">{{ club.email}}</a></dd> </dl> </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> diff --git a/templates/member/club_picture_update.html b/templates/member/club_picture_update.html index 70f5cf4ac0f99c1f729fde535090fbda67821984..4f72efec88cb13bc4a2955ad3a91def1870c49f7 100644 --- a/templates/member/club_picture_update.html +++ b/templates/member/club_picture_update.html @@ -1,10 +1,6 @@ {% extends "member/club_detail.html" %} {% load i18n static pretty_money django_tables2 crispy_forms_tags %} -{% block profile_info %} -{% include "member/club_info.html" %} -{% endblock%} - {% block profile_content%} {% include "member/picture_update.html" %} {% endblock%} diff --git a/templates/member/profile_alias.html b/templates/member/profile_alias.html index a83d7c3ef8f450aefd53c9d052ed759325eb984e..eb6e499aac817fdaee6e9f5b19b49cca1d85915e 100644 --- a/templates/member/profile_alias.html +++ b/templates/member/profile_alias.html @@ -2,18 +2,9 @@ {% load i18n static pretty_money django_tables2 crispy_forms_tags %} {% block profile_content %} - <div class="d-flex justify-content-center"> - <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> +{% include "member/alias_update.html"%} {% endblock %} + +{% block extrajavascript %} +<script src="/static/js/alias.js"></script> +{% endblock%} diff --git a/templates/member/profile_info.html b/templates/member/profile_info.html index 3038386650422cd85f05333a01b7f5950a7bd41a..9ff20385de4f9d201af1bb01918c13b89840006c 100644 --- a/templates/member/profile_info.html +++ b/templates/member/profile_info.html @@ -1,6 +1,9 @@ {% load i18n static pretty_money %} <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"> <a href="{% url 'member:user_update_pic' object.pk %}"> <img src="{{ object.note.display_image.url }}" class="img-thumbnail mt-2" > diff --git a/templates/member/profile_picture_update.html b/templates/member/profile_picture_update.html index db7c5767970a285c0a703240254b967090c1d073..4be78dc899e5f9c84768d6bf7e0ccbf23ad87c18 100644 --- a/templates/member/profile_picture_update.html +++ b/templates/member/profile_picture_update.html @@ -1,10 +1,6 @@ -{% extends "member/noteowner_detail.html" %} +{% extends "member/profile_detail.html" %} {% load i18n static pretty_money django_tables2 crispy_forms_tags %} -{% block profile_info %} -{% include "member/profile_info.html" %} -{% endblock%} - {% block profile_content%} {% include "member/picture_update.html" %} {% endblock%} diff --git a/templates/note/transactiontemplate_list.html b/templates/note/transactiontemplate_list.html index 043a06e990c92b99665e5bf9a025f114f3a07f05..af0a02b4ebbbd9d7bf748f7e8d1b04e00421edba 100644 --- a/templates/note/transactiontemplate_list.html +++ b/templates/note/transactiontemplate_list.html @@ -10,7 +10,7 @@ </h4> <input class="form-control mx-auto w-25" type="text" onkeyup="search_field_moved();return(false);" id="search_field"/> <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 class="row justify-content-center">