Merge branch 'beta' into JS_translations

# Conflicts:
#	apps/note/static/note/js/consos.js
#	locale/de/LC_MESSAGES/django.po
#	locale/es/LC_MESSAGES/django.po
#	locale/fr/LC_MESSAGES/django.po
This commit is contained in:
Yohann D'ANELLO 2020-11-16 00:59:26 +01:00
commit 8434c0062c
53 changed files with 1121 additions and 684 deletions

View File

@ -16,8 +16,8 @@ py37-django22:
apt-get install --no-install-recommends -t buster-backports -y
python3-django python3-django-crispy-forms
python3-django-extensions python3-django-filters python3-django-polymorphic
python3-djangorestframework python3-django-cas-server python3-psycopg2 python3-pil
python3-babel python3-lockfile python3-pip python3-phonenumbers
python3-djangorestframework python3-django-oauth-toolkit python3-psycopg2 python3-pil
python3-babel python3-lockfile python3-pip python3-phonenumbers python3-memcache
python3-bs4 python3-setuptools tox texlive-xetex
script: tox -e py37-django22
@ -33,8 +33,8 @@ py38-django22:
apt-get install --no-install-recommends -y
python3-django python3-django-crispy-forms
python3-django-extensions python3-django-filters python3-django-polymorphic
python3-djangorestframework python3-django-cas-server python3-psycopg2 python3-pil
python3-babel python3-lockfile python3-pip python3-phonenumbers
python3-djangorestframework python3-django-oauth-toolkit python3-psycopg2 python3-pil
python3-babel python3-lockfile python3-pip python3-phonenumbers python3-memcache
python3-bs4 python3-setuptools tox texlive-xetex
script: tox -e py38-django22

View File

@ -8,8 +8,8 @@ RUN apt-get update && \
apt-get install --no-install-recommends -t buster-backports -y \
python3-django python3-django-crispy-forms \
python3-django-extensions python3-django-filters python3-django-polymorphic \
python3-djangorestframework python3-django-cas-server python3-psycopg2 python3-pil \
python3-babel python3-lockfile python3-pip python3-phonenumbers ipython3 \
python3-djangorestframework python3-django-oauth-toolkit python3-psycopg2 python3-pil \
python3-babel python3-lockfile python3-pip python3-phonenumbers python3-memcache ipython3 \
python3-bs4 python3-setuptools \
uwsgi uwsgi-plugin-python3 \
texlive-xetex gettext libjs-bootstrap4 fonts-font-awesome && \

View File

@ -93,10 +93,10 @@ Sinon vous pouvez suivre les étapes décrites ci-dessous.
$ sudo apt install --no-install-recommends -t buster-backports -y \
python3-django python3-django-crispy-forms \
python3-django-extensions python3-django-filters python3-django-polymorphic \
python3-djangorestframework python3-django-cas-server python3-psycopg2 python3-pil \
python3-babel python3-lockfile python3-pip python3-phonenumbers ipython3 \
python3-bs4 python3-setuptools \
uwsgi uwsgi-plugin-python3 \
python3-djangorestframework python3-django-oauth-toolkit python3-psycopg2 python3-pil \
python3-babel python3-lockfile python3-pip python3-phonenumbers python3-memcache ipython3 \
python3-bs4 python3-setuptools python3-docutils \
memcached uwsgi uwsgi-plugin-python3 \
texlive-xetex gettext libjs-bootstrap4 fonts-font-awesome \
nginx python3-venv git acl
```

View File

@ -23,13 +23,14 @@
- python3-babel
- python3-bs4
- python3-django
- python3-django-cas-server
- python3-django-crispy-forms
- python3-django-extensions
- python3-django-filters
- python3-django-oauth-toolkit
- python3-django-polymorphic
- python3-djangorestframework
- python3-lockfile
- python3-memcache
- python3-phonenumbers
- python3-pil
- python3-pip
@ -40,6 +41,9 @@
# LaTeX (PDF generation)
- texlive-xetex
# Cache server
- memcached
# WSGI server
- uwsgi
- uwsgi-plugin-python3

View File

@ -12,8 +12,10 @@ from django.db.models import F, Q
from django.http import HttpResponse
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.decorators.cache import cache_page
from django.views.generic import DetailView, TemplateView, UpdateView
from django_tables2.views import SingleTableView
from note.models import Alias, NoteSpecial, NoteUser
@ -288,6 +290,8 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
return context
# Cache for 1 hour
@method_decorator(cache_page(60 * 60), name='dispatch')
class CalendarView(View):
"""
Render an ICS calendar with all valid activities.

View File

@ -150,6 +150,7 @@ class ClubForm(forms.ModelForm):
"membership_fee_unpaid": AmountInput(),
"parent_club": Autocomplete(
Club,
resetable=True,
attrs={
'api_url': '/api/members/club/',
}

View File

@ -7,6 +7,7 @@ def create_bde_and_kfet(apps, schema_editor):
"""
Club = apps.get_model("member", "club")
NoteClub = apps.get_model("note", "noteclub")
Alias = apps.get_model("note", "alias")
ContentType = apps.get_model('contenttypes', 'ContentType')
polymorphic_ctype_id = ContentType.objects.get_for_model(NoteClub).id
@ -45,6 +46,19 @@ def create_bde_and_kfet(apps, schema_editor):
polymorphic_ctype_id=polymorphic_ctype_id,
)
Alias.objects.get_or_create(
id=5,
note_id=5,
name="BDE",
normalized_name="bde",
)
Alias.objects.get_or_create(
id=6,
note_id=6,
name="Kfet",
normalized_name="kfet",
)
class Migration(migrations.Migration):
dependencies = [

View File

@ -0,0 +1,50 @@
import sys
from django.db import migrations
def give_note_account_permissions(apps, schema_editor):
"""
Automatically manage the membership of the Note account.
"""
User = apps.get_model("auth", "user")
Membership = apps.get_model("member", "membership")
Role = apps.get_model("permission", "role")
note = User.objects.filter(username="note")
if not note.exists():
# We are in a test environment, don't log error message
if len(sys.argv) > 1 and sys.argv[1] == 'test':
return
print("Warning: Note account was not found. The note account was not imported.")
print("Make sure you have imported the NK15 database. The new import script handles correctly the permissions.")
print("This migration will be ignored, you can re-run it if you forgot the note account or ignore it if you "
"don't want this account.")
return
note = note.get()
# Set for the two clubs a large expiration date and the correct role.
for m in Membership.objects.filter(user_id=note.id).all():
m.date_end = "3142-12-12"
m.roles.set(Role.objects.filter(name="PC Kfet").all())
m.save()
# By default, the note account is only authorized to be logged from localhost.
note.password = "ipbased$127.0.0.1"
note.is_active = True
note.save()
# Ensure that the note of the account is disabled
note.note.inactivity_reason = 'forced'
note.note.is_active = False
note.save()
class Migration(migrations.Migration):
dependencies = [
('member', '0005_remove_null_tag_on_charfields'),
('permission', '0001_initial'),
]
operations = [
migrations.RunPython(give_note_account_permissions),
]

View File

@ -43,8 +43,24 @@ class UserTable(tables.Table):
section = tables.Column(accessor='profile__section')
# Override the column to let replace the URL
email = tables.EmailColumn(linkify=lambda record: "mailto:{}".format(record.email))
balance = tables.Column(accessor='note__balance', verbose_name=_("Balance"))
def render_email(self, record, value):
# Replace the email by a dash if the user can't see the profile detail
# Replace also the URL
if not PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_profile", record.profile):
value = ""
record.email = value
return value
def render_section(self, record, value):
return value \
if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_profile", record.profile) \
else ""
def render_balance(self, record, value):
return pretty_money(value)\
if PermissionBackend.check_perm(get_current_authenticated_user(), "note.view_note", record.note) else ""
@ -112,7 +128,7 @@ class MembershipTable(tables.Table):
fee=0,
)
if PermissionBackend.check_perm(get_current_authenticated_user(),
"member:add_membership", empty_membership): # If the user has right
"member.add_membership", empty_membership): # If the user has right
renew_url = reverse_lazy('member:club_renew_membership',
kwargs={"pk": record.pk})
t = format_html(

View File

@ -25,25 +25,27 @@
</a>
</dd>
<dt class="col-xl-6">{% trans 'section'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.section }}</dd>
{% if "member.view_profile"|has_perm:user_object.profile %}
<dt class="col-xl-6">{% trans 'section'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.section }}</dd>
<dt class="col-xl-6">{% trans 'email'|capfirst %}</dt>
<dd class="col-xl-6"><a href="mailto:{{ user_object.email }}">{{ user_object.email }}</a></dd>
<dt class="col-xl-6">{% trans 'email'|capfirst %}</dt>
<dd class="col-xl-6"><a href="mailto:{{ user_object.email }}">{{ user_object.email }}</a></dd>
<dt class="col-xl-6">{% trans 'phone number'|capfirst %}</dt>
<dd class="col-xl-6"><a href="tel:{{ user_object.profile.phone_number }}">{{ user_object.profile.phone_number }}</a>
</dd>
<dt class="col-xl-6">{% trans 'phone number'|capfirst %}</dt>
<dd class="col-xl-6"><a href="tel:{{ user_object.profile.phone_number }}">{{ user_object.profile.phone_number }}</a>
</dd>
<dt class="col-xl-6">{% trans 'address'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.address }}</dd>
<dt class="col-xl-6">{% trans 'address'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.address }}</dd>
{% if user_object.note and "note.view_note"|has_perm:user_object.note %}
<dt class="col-xl-6">{% trans 'balance'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.note.balance | pretty_money }}</dd>
{% if user_object.note and "note.view_note"|has_perm:user_object.note %}
<dt class="col-xl-6">{% trans 'balance'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.note.balance | pretty_money }}</dd>
<dt class="col-xl-6">{% trans 'paid'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.paid|yesno }}</dd>
<dt class="col-xl-6">{% trans 'paid'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.paid|yesno }}</dd>
{% endif %}
{% endif %}
</dl>

View File

@ -5,7 +5,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% load i18n perms %}
{% block content %}
{% if "member.change_profile_registration_valid"|has_perm:user %}
{% if can_manage_registrations %}
<a class="btn btn-block btn-secondary mb-3" href="{% url 'registration:future_user_list' %}">
<i class="fa fa-user-plus"></i> {% trans "Registrations" %}
</a>

View File

View File

@ -0,0 +1,22 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from datetime import date
from django import template
from django.contrib.auth.models import User
from ..models import Club, Membership
def is_member(user, club):
if isinstance(user, str):
club = User.objects.get(username=user)
if isinstance(club, str):
club = Club.objects.get(name=club)
return Membership.objects\
.filter(user=user, club=club, date_start__lte=date.today(), date_end__gte=date.today()).exists()
register = template.Library()
register.filter("is_member", is_member)

View File

@ -41,7 +41,7 @@ class TemplateLoggedInTests(TestCase):
password="adminadmin",
permission_mask=3,
))
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL, 302, 200)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL, 302, 302)
def test_logout(self):
response = self.client.get(reverse("logout"))

View File

@ -205,7 +205,7 @@ class TestMemberships(TestCase):
first_name="Toto",
bank="Le matelas",
))
self.assertRedirects(response, club.get_absolute_url(), 302, 200)
self.assertRedirects(response, user.profile.get_absolute_url(), 302, 200)
self.assertTrue(Membership.objects.filter(user=user, club=club).exists())
@ -244,9 +244,9 @@ class TestMemberships(TestCase):
first_name="Toto",
bank="Bank",
))
self.assertRedirects(response, club.get_absolute_url(), 302, 200)
self.assertRedirects(response, user.profile.get_absolute_url(), 302, 200)
response = self.client.get(user.profile.get_absolute_url())
response = self.client.get(club.get_absolute_url())
self.assertEqual(response.status_code, 200)
def test_auto_join_kfet_when_join_bde_with_soge(self):
@ -273,7 +273,7 @@ class TestMemberships(TestCase):
first_name="Toto",
bank="Société générale",
))
self.assertRedirects(response, bde.get_absolute_url(), 302, 200)
self.assertRedirects(response, user.profile.get_absolute_url(), 302, 200)
self.assertTrue(Membership.objects.filter(user=user, club=bde).exists())
self.assertTrue(Membership.objects.filter(user=user, club=kfet).exists())

View File

@ -70,10 +70,11 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
form.fields['email'].required = True
form.fields['email'].help_text = _("This address must be valid.")
context['profile_form'] = self.profile_form(instance=context['user_object'].profile,
data=self.request.POST if self.request.POST else None)
if not self.object.profile.report_frequency:
del context['profile_form'].fields["last_report"]
if PermissionBackend.check_perm(self.request.user, "member.change_profile", context['user_object'].profile):
context['profile_form'] = self.profile_form(instance=context['user_object'].profile,
data=self.request.POST if self.request.POST else None)
if not self.object.profile.report_frequency:
del context['profile_form'].fields["last_report"]
return context
@ -157,8 +158,12 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
history_table.paginate(per_page=20, page=self.request.GET.get("transaction-page", 1))
context['history_list'] = history_table
club_list = Membership.objects.filter(user=user, date_end__gte=date.today())\
.filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view"))
club_list = Membership.objects.filter(user=user, date_end__gte=date.today() - timedelta(days=15))\
.filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view"))\
.order_by("club__name", "-date_start")
# Display only the most recent membership
club_list = club_list.distinct("club__name")\
if settings.DATABASES["default"]["ENGINE"] == 'django.db.backends.postgresql' else club_list
membership_table = MembershipTable(data=club_list, prefix='membership-')
membership_table.paginate(per_page=10, page=self.request.GET.get("membership-page", 1))
context['club_list'] = membership_table
@ -166,6 +171,8 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
# Check permissions to see if the authenticated user can lock/unlock the note
with transaction.atomic():
modified_note = NoteUser.objects.get(pk=user.note.pk)
# Don't log these tests
modified_note._no_signal = True
modified_note.is_active = True
modified_note.inactivity_reason = 'manual'
context["can_lock_note"] = user.note.is_active and PermissionBackend\
@ -178,6 +185,7 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context["can_force_lock"] = user.note.is_active and PermissionBackend\
.check_perm(self.request.user, "note.change_note_is_active", modified_note)
old_note._force_save = True
old_note._no_signal = True
old_note.save()
modified_note.refresh_from_db()
modified_note.is_active = True
@ -227,6 +235,13 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
pre_registered_users = User.objects.filter(PermissionBackend.filter_queryset(self.request.user, User, "view"))\
.filter(profile__registration_valid=False)
context["can_manage_registrations"] = pre_registered_users.exists()
return context
class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
"""
@ -240,8 +255,8 @@ class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
note = context['object'].note
context["aliases"] = AliasTable(note.alias_set.filter(PermissionBackend
.filter_queryset(self.request.user, Alias, "view")).all())
context["aliases"] = AliasTable(
note.alias_set.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")).distinct().all())
context["can_create"] = PermissionBackend.check_perm(self.request.user, "note.add_alias", Alias(
note=context["object"].note,
name="",
@ -392,7 +407,8 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
if PermissionBackend.check_perm(self.request.user, "member.change_club_membership_start", club):
club.update_membership_dates()
# managers list
managers = Membership.objects.filter(club=self.object, roles__name="Bureau de club")\
managers = Membership.objects.filter(club=self.object, roles__name="Bureau de club",
date_start__lte=date.today(), date_end__gte=date.today())\
.order_by('user__last_name').all()
context["managers"] = ClubManagerTable(data=managers, prefix="managers-")
# transaction history
@ -405,8 +421,12 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
# member list
club_member = Membership.objects.filter(
club=club,
date_end__gte=date.today(),
).filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view"))
date_end__gte=date.today() - timedelta(days=15),
).filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view"))\
.order_by("user__username", "-date_start")
# Display only the most recent membership
club_member = club_member.distinct("user__username")\
if settings.DATABASES["default"]["ENGINE"] == 'django.db.backends.postgresql' else club_member
membership_table = MembershipTable(data=club_member, prefix="membership-")
membership_table.paginate(per_page=5, page=self.request.GET.get('membership-page', 1))
@ -438,8 +458,8 @@ class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
note = context['object'].note
context["aliases"] = AliasTable(note.alias_set.filter(PermissionBackend
.filter_queryset(self.request.user, Alias, "view")).all())
context["aliases"] = AliasTable(note.alias_set.filter(
PermissionBackend.filter_queryset(self.request.user, Alias, "view")).distinct().all())
context["can_create"] = PermissionBackend.check_perm(self.request.user, "note.add_alias", Alias(
note=context["object"].note,
name="",
@ -638,8 +658,8 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
if club.name != "Kfet" and club.parent_club and not Membership.objects.filter(
user=form.instance.user,
club=club.parent_club,
date_start__lte=club.parent_club.membership_start,
date_end__gte=club.parent_club.membership_end,
date_start__gte=club.parent_club.membership_start,
date_end__lte=club.parent_club.membership_end,
).exists():
form.add_error('user', _('User is not a member of the parent club') + ' ' + club.parent_club.name)
error = True
@ -658,11 +678,13 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"):
if not last_name:
form.add_error('last_name', _("This field is required."))
error = True
if not first_name:
form.add_error('first_name', _("This field is required."))
error = True
if not bank and credit_type.special_type == "Chèque":
form.add_error('bank', _("This field is required."))
return self.form_invalid(form)
error = True
return not error
@ -676,6 +698,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view")) \
.get(pk=self.kwargs["club_pk"])
user = form.instance.user
old_membership = None
else: # get from url for renewal
old_membership = self.get_queryset().get(pk=self.kwargs["pk"])
club = old_membership.club
@ -750,6 +773,9 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
member_role = Role.objects.filter(Q(name="Adhérent BDE") | Q(name="Membre de club")).all() \
if club.name == "BDE" else Role.objects.filter(Q(name="Adhérent Kfet") | Q(name="Membre de club")).all() \
if club.name == "Kfet"else Role.objects.filter(name="Membre de club").all()
# Set the same roles as before
if old_membership:
member_role = member_role.union(old_membership.roles.all())
form.instance.roles.set(member_role)
form.instance._force_save = True
form.instance.save()
@ -787,7 +813,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
return ret
def get_success_url(self):
return reverse_lazy('member:club_detail', kwargs={'pk': self.object.club.id})
return reverse_lazy('member:user_detail', kwargs={'pk': self.object.user.id})
class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):

View File

@ -3,7 +3,7 @@
from django.apps import AppConfig
from django.conf import settings
from django.db.models.signals import post_save, pre_delete
from django.db.models.signals import pre_delete, pre_save, post_save
from django.utils.translation import gettext_lazy as _
from . import signals
@ -17,6 +17,15 @@ class NoteConfig(AppConfig):
"""
Define app internal signals to interact with other apps
"""
pre_save.connect(
signals.pre_save_note,
sender="note.noteuser",
)
pre_save.connect(
signals.pre_save_note,
sender="note.noteclub",
)
post_save.connect(
signals.save_user_note,
sender=settings.AUTH_USER_MODEL,

View File

@ -159,20 +159,6 @@ class NoteUser(Note):
def pretty(self):
return _("%(user)s's note") % {'user': str(self.user)}
@transaction.atomic
def save(self, *args, **kwargs):
if self.pk and self.balance < 0:
old_note = NoteUser.objects.get(pk=self.pk)
super().save(*args, **kwargs)
if old_note.balance >= 0:
# Passage en négatif
self.last_negative = timezone.now()
self._force_save = True
self.save(*args, **kwargs)
self.send_mail_negative_balance()
else:
super().save(*args, **kwargs)
def send_mail_negative_balance(self):
plain_text = render_to_string("note/mails/negative_balance.txt", dict(note=self))
html = render_to_string("note/mails/negative_balance.html", dict(note=self))
@ -201,20 +187,6 @@ class NoteClub(Note):
def pretty(self):
return _("Note of %(club)s club") % {'club': str(self.club)}
@transaction.atomic
def save(self, *args, **kwargs):
if self.pk and self.balance < 0:
old_note = NoteClub.objects.get(pk=self.pk)
super().save(*args, **kwargs)
if old_note.balance >= 0:
# Passage en négatif
self.last_negative = timezone.now()
self._force_save = True
self.save(*args, **kwargs)
self.send_mail_negative_balance()
else:
super().save(*args, **kwargs)
def send_mail_negative_balance(self):
plain_text = render_to_string("note/mails/negative_balance.txt", dict(note=self))
html = render_to_string("note/mails/negative_balance.html", dict(note=self))

View File

@ -217,6 +217,9 @@ class Transaction(PolymorphicModel):
# When source == destination, no money is transferred and no transaction is created
return
self.source = Note.objects.select_for_update().get(pk=self.source_id)
self.destination = Note.objects.select_for_update().get(pk=self.destination_id)
# Check that the amounts stay between big integer bounds
diff_source, diff_dest = self.validate()

View File

@ -1,6 +1,8 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.utils import timezone
def save_user_note(instance, raw, **_kwargs):
"""
@ -25,6 +27,16 @@ def save_club_note(instance, raw, **_kwargs):
instance.note.save()
def pre_save_note(instance, raw, **_kwargs):
if not raw and instance.pk and not hasattr(instance, "_no_signal") and instance.balance < 0:
from note.models import Note
old_note = Note.objects.get(pk=instance.pk)
if old_note.balance >= 0:
# Passage en négatif
instance.last_negative = timezone.now()
instance.send_mail_negative_balance()
def delete_transaction(instance, **_kwargs):
"""
Whenever we want to delete a transaction (caution with this), we ensure the transaction is invalid first.

View File

@ -67,7 +67,11 @@ $(document).ready(function () {
last.quantity = 1
if (!last.note.user) {
if (last.note.club) {
$('#last_name').val(last.note.name)
$('#first_name').val(last.note.name)
}
else if (!last.note.user) {
$.getJSON('/api/note/note/' + last.note.id + '/?format=json', function (note) {
last.note.user = note.user
$.getJSON('/api/user/' + last.note.user + '/', function (user) {
@ -246,7 +250,7 @@ $('#btn_transfer').click(function () {
error = true
}
if (!reason_field.val()) {
if (!reason_field.val() && $('#type_transfer').is(':checked')) {
reason_field.addClass('is-invalid')
$('#reason-required').html('<strong>' + gettext('This field is required.') + '</strong>')
error = true
@ -377,7 +381,7 @@ $('#btn_transfer').click(function () {
alias = sources_notes_display[0].name
source_id = user_note.id
dest_id = special_note
reason = 'Retrait ' + $('#credit_type option:selected').text().toLowerCase()
reason = 'Retrait ' + $('#debit_type option:selected').text().toLowerCase()
if (given_reason.length > 0) { reason += ' (' + given_reason + ')' }
}
$.post('/api/note/transaction/transaction/',

View File

@ -159,7 +159,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endblock %}
{% block extrajavascript %}
<script type="text/javascript" src="{% static "note/js/consos.js" 'javascript-catalog' %}"></script>
<script type="text/javascript" src="{% static "note/js/consos.js" %}"></script>
<script type="text/javascript">
{% for button in highlighted %}
{% if button.display %}

View File

@ -115,7 +115,7 @@
"type": "view",
"mask": 1,
"field": "",
"permanent": true,
"permanent": false,
"description": "Voir les aliases des notes des clubs et des adhérents du club Kfet"
}
},
@ -799,12 +799,12 @@
"member",
"membership"
],
"query": "{\"club\": [\"club\"]}",
"query": "{}",
"type": "change",
"mask": 3,
"field": "roles",
"permanent": false,
"description": "Modifier les rôles d'un adhérent d'un club"
"description": "Modifier les rôles d'une adhésion"
}
},
{
@ -2081,7 +2081,7 @@
],
"query": "{}",
"type": "change",
"mask": 1,
"mask": 2,
"field": "invalidity_reason",
"permanent": false,
"description": "Modifier la raison d'invalidité d'une transaction"
@ -2791,6 +2791,86 @@
"description": "Voir tous les alias, y compris ceux des non adhérents"
}
},
{
"model": "permission.permission",
"pk": 179,
"fields": {
"model": [
"note",
"alias"
],
"query": "{\"note__noteuser__user\": [\"user\"]}",
"type": "view",
"mask": 1,
"field": "",
"permanent": true,
"description": "Voir ses propres alias, pour toujours"
}
},
{
"model": "permission.permission",
"pk": 180,
"fields": {
"model": [
"auth",
"user"
],
"query": "{\"profile__registration_valid\": false}",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir n'importe quel utilisateur non encore inscrit"
}
},
{
"model": "permission.permission",
"pk": 181,
"fields": {
"model": [
"member",
"profile"
],
"query": "{\"registration_valid\": false}",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir n'importe quel profil non encore inscrit"
}
},
{
"model": "permission.permission",
"pk": 182,
"fields": {
"model": [
"auth",
"user"
],
"query": "{\"memberships__club__name\": \"BDE\", \"memberships__roles__name\": \"Adhérent BDE\", \"memberships__date_start__lte\": [\"today\"], \"memberships__date_end__gte\": [\"today\"]}",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir n'importe quel utilisateur qui est adhérent BDE"
}
},
{
"model": "permission.permission",
"pk": 183,
"fields": {
"model": [
"note",
"note"
],
"query": "{}",
"type": "change",
"mask": 1,
"field": "display_image",
"permanent": false,
"description": "Changer l'image de n'importe quelle note"
}
},
{
"model": "permission.role",
"pk": 1,
@ -2861,7 +2941,8 @@
157,
158,
159,
160
160,
179
]
}
},
@ -2922,14 +3003,14 @@
62,
127,
133,
135,
136,
141,
142,
150,
166,
167,
168
168,
182
]
}
},
@ -2965,6 +3046,7 @@
31,
32,
33,
51,
53,
54,
55,
@ -2988,6 +3070,7 @@
137,
138,
139,
140,
143,
146,
147,
@ -3003,7 +3086,8 @@
175,
176,
177,
178
178,
183
]
}
},
@ -3186,7 +3270,12 @@
175,
176,
177,
178
178,
179,
180,
181,
182,
183
]
}
},
@ -3220,7 +3309,12 @@
170,
171,
176,
177
177,
178,
179,
180,
181,
182
]
}
},
@ -3383,7 +3477,6 @@
135,
136,
137,
138,
139,
140,
143,
@ -3396,6 +3489,41 @@
]
}
},
{
"model": "permission.role",
"pk": 20,
"fields": {
"for_club": 2,
"name": "PC Kfet",
"permissions": [
6,
22,
24,
25,
26,
27,
30,
49,
50,
55,
56,
57,
58,
137,
143,
147,
150,
166,
167,
168,
176,
177,
180,
181,
182
]
}
},
{
"model": "wei.weirole",
"pk": 12,

View File

@ -43,7 +43,9 @@ class InstancedPermission:
obj = copy(obj)
obj.pk = 0
with transaction.atomic():
sid = transaction.savepoint()
for o in self.model.model_class().objects.filter(pk=0).all():
o._no_signal = True
o._force_delete = True
Model.delete(o)
# An object with pk 0 wouldn't deleted. That's not normal, we alert admins.
@ -61,9 +63,7 @@ class InstancedPermission:
obj._no_signal = True
Model.save(obj, force_insert=True)
ret = self.model.model_class().objects.filter(self.query & Q(pk=0)).exists()
# Delete testing object
obj._force_delete = True
Model.delete(obj)
transaction.savepoint_rollback(sid)
return ret

View File

@ -51,8 +51,10 @@ class ProtectQuerysetMixin:
# No worry if the user change the hidden fields: a 403 error will be performed if the user tries to make
# a custom request.
# We could also delete the field, but some views might be affected.
meta = form.instance._meta
for key in form.base_fields:
if not PermissionBackend.check_perm(self.request.user, "wei.change_weiregistration_" + key, self.object):
if not PermissionBackend.check_perm(self.request.user,
f"{meta.app_label}.change_{meta.model_name}_" + key, self.object):
form.fields[key].widget = HiddenInput()
return form

View File

@ -44,6 +44,15 @@ class SignUpForm(UserCreationForm):
fields = ('first_name', 'last_name', 'username', 'email', )
class DeclareSogeAccountOpenedForm(forms.Form):
soge_account = forms.BooleanField(
label=_("I declare that I opened a bank account in the Société générale with the BDE partnership."),
help_text=_("Warning: this engages you to open your bank account. If you finally decides to don't open your "
"account, you will have to pay the BDE membership."),
required=False,
)
class WEISignupForm(forms.Form):
wei_registration = forms.BooleanField(
label=_("Register to the WEI"),

View File

@ -4,6 +4,8 @@
import django_tables2 as tables
from django.contrib.auth.models import User
from treasury.models import SogeCredit
class FutureUserTable(tables.Table):
"""
@ -21,6 +23,7 @@ class FutureUserTable(tables.Table):
fields = ('last_name', 'first_name', 'username', 'email', )
model = User
row_attrs = {
'class': 'table-row',
'class': lambda record: 'table-row'
+ (' bg-warning' if SogeCredit.objects.filter(user=record).exists() else ''),
'data-href': lambda record: record.pk
}

View File

@ -56,6 +56,13 @@ SPDX-License-Identifier: GPL-3.0-or-later
<div class="card-header text-center" >
<h4> {% trans "Validate account" %}</h4>
</div>
{% if declare_soge_account %}
<div class="alert alert-info">
{% trans "The user declared that he/she opened a bank account in the Société générale." %}
</div>
{% endif %}
<div class="card-body" id="profile_infos">
{% csrf_token %}
{{ form|crispy }}
@ -104,7 +111,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
soge_field.change(fillFields);
{% if object.profile.soge %}
{% if declare_soge_account %}
soge_field.attr('checked', true);
fillFields();
{% endif %}

View File

@ -24,7 +24,7 @@ from permission.models import Role
from permission.views import ProtectQuerysetMixin
from treasury.models import SogeCredit
from .forms import SignUpForm, ValidationForm
from .forms import SignUpForm, ValidationForm, DeclareSogeAccountOpenedForm
from .tables import FutureUserTable
from .tokens import email_validation_token
@ -42,6 +42,7 @@ class UserCreateView(CreateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["profile_form"] = self.second_form(self.request.POST if self.request.POST else None)
context["soge_form"] = DeclareSogeAccountOpenedForm(self.request.POST if self.request.POST else None)
del context["profile_form"].fields["section"]
del context["profile_form"].fields["report_frequency"]
del context["profile_form"].fields["last_report"]
@ -72,6 +73,13 @@ class UserCreateView(CreateView):
user.profile.send_email_validation_link()
soge_form = DeclareSogeAccountOpenedForm(self.request.POST)
if "soge_account" in soge_form.data and soge_form.data["soge_account"]:
# If the user declares that a bank account got opened, prepare the soge credit to warn treasurers
soge_credit = SogeCredit(user=user)
soge_credit._force_save = True
soge_credit.save()
return super().form_valid(form)
def get_success_url(self):
@ -182,7 +190,7 @@ class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVi
| Q(username__iregex="^" + pattern)
)
return qs[:20]
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@ -227,6 +235,8 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
fee += 8000
ctx["total_fee"] = "{:.02f}".format(fee / 100, )
ctx["declare_soge_account"] = SogeCredit.objects.filter(user=user).exists()
return ctx
def get_form(self, form_class=None):
@ -307,6 +317,13 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
user.profile.save()
user.refresh_from_db()
if not soge and SogeCredit.objects.filter(user=user).exists():
# If the user declared that a bank account was opened but in the validation form the SoGé case was
# unchecked, delete the associated credit
soge_credit = SogeCredit.objects.get(user=user)
soge_credit._force_delete = True
soge_credit.delete()
if credit_type is not None and credit_amount > 0:
# Credit the note
SpecialTransaction.objects.create(
@ -373,6 +390,8 @@ class FutureUserInvalidateView(ProtectQuerysetMixin, LoginRequiredMixin, View):
user = User.objects.filter(profile__registration_valid=False)\
.filter(PermissionBackend.filter_queryset(request.user, User, "change", "is_valid"))\
.get(pk=self.kwargs["pk"])
# Delete associated soge credits before
SogeCredit.objects.filter(user=user).delete()
user.delete()

View File

@ -28,6 +28,8 @@ class TreasuryConfig(AppConfig):
source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
specialtransactionproxy=None,
):
SpecialTransactionProxy.objects.create(transaction=transaction, remittance=None)
proxy = SpecialTransactionProxy(transaction=transaction, remittance=None)
proxy._force_save = True
proxy.save()
post_migrate.connect(setup_specialtransactions_proxies, sender=SpecialTransactionProxy)

View File

@ -10,7 +10,7 @@ from django.db.models import Q
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from note.models import NoteSpecial, SpecialTransaction, MembershipTransaction
from note.models import NoteSpecial, SpecialTransaction, MembershipTransaction, NoteUser
class Invoice(models.Model):
@ -335,6 +335,11 @@ class SogeCredit(models.Model):
@transaction.atomic
def save(self, *args, **kwargs):
# This is a pre-registered user that declared that a SoGé account was opened.
# No note exists yet.
if not NoteUser.objects.filter(user=self.user).exists():
return super().save(*args, **kwargs)
if not self.credit_transaction:
credit_transaction = SpecialTransaction(
source=NoteSpecial.objects.get(special_type="Virement bancaire"),

View File

@ -10,9 +10,8 @@ def save_special_transaction(instance, created, **kwargs):
"""
if not hasattr(instance, "_no_signal"):
if instance.is_credit():
if created and RemittanceType.objects.filter(note=instance.source).exists():
SpecialTransactionProxy.objects.create(transaction=instance, remittance=None).save()
else:
if created and RemittanceType.objects.filter(note=instance.destination).exists():
SpecialTransactionProxy.objects.create(transaction=instance, remittance=None).save()
if created and RemittanceType.objects.filter(
note=instance.source if instance.is_credit() else instance.destination).exists():
proxy = SpecialTransactionProxy(transaction=instance, remittance=None)
proxy._force_save = True
proxy.save()

View File

@ -147,4 +147,4 @@ class SogeCreditTable(tables.Table):
class Meta:
model = SogeCredit
fields = ('user', 'amount', 'valid', )
fields = ('user', 'user__last_name', 'user__first_name', 'amount', 'valid', )

View File

@ -11,8 +11,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
</div>
<div class="card-body">
<dl class="row">
<dt class="col-xl-6 text-right">{% trans 'user'|capfirst %}</dt>
<dd class="col-xl-6"><a href="{% url 'member:user_detail' pk=object.user.pk %}">{{ object.user }}</a></dd>
<dt class="col-xl-6 text-right">{% trans 'last name'|capfirst %}</dt>
<dd class="col-xl-6">{{ object.user.last_name }}</dd>
<dt class="col-xl-6 text-right">{% trans 'first name'|capfirst %}</dt>
<dd class="col-xl-6">{{ object.user.first_name }}</dd>
<dt class="col-xl-6 text-right">{% trans 'username'|capfirst %}</dt>
<dd class="col-xl-6"><a href="{% url 'member:user_detail' pk=object.user.pk %}">{{ object.user.username }}</a></dd>
{% if "note.view_note_balance"|has_perm:object.user.note %}
<dt class="col-xl-6 text-right">{% trans 'balance'|capfirst %}</dt>

View File

@ -60,7 +60,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
let pattern = searchbar_obj.val();
$("#credits_table").load(location.pathname + "?search=" + pattern.replace(" ", "%20") + (
invalid_only_obj.is(':checked') ? "&valid=false" : "") + " #credits_table");
invalid_only_obj.is(':checked') ? "" : "&valid=1") + " #credits_table");
$(".table-row").click(function () {
window.document.location = $(this).data("href");

View File

@ -431,7 +431,7 @@ class SogeCreditListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableVi
if "valid" not in self.request.GET or not self.request.GET["valid"]:
qs = qs.filter(credit_transaction__valid=False)
return qs[:20]
return qs
class SogeCreditManageView(LoginRequiredMixin, ProtectQuerysetMixin, BaseFormView, DetailView):

View File

@ -52,9 +52,9 @@ msgstr "Sie dürfen höchstens 3 Leute zu dieser Veranstaltung einladen."
#: apps/member/models.py:199
#: apps/member/templates/member/includes/club_info.html:4
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/note/models/notes.py:260 apps/note/models/transactions.py:26
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:297
#: apps/permission/models.py:330
#: apps/note/models/notes.py:232 apps/note/models/transactions.py:26
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:300
#: apps/permission/models.py:333
#: apps/registration/templates/registration/future_profile_detail.html:16
#: apps/wei/models.py:66 apps/wei/models.py:118
#: apps/wei/templates/wei/base.html:26
@ -90,8 +90,8 @@ msgstr "Vearnstaltungarte"
#: apps/activity/models.py:68
#: apps/activity/templates/activity/includes/activity_info.html:19
#: apps/note/models/transactions.py:81 apps/permission/models.py:110
#: apps/permission/models.py:189 apps/wei/models.py:72 apps/wei/models.py:129
#: apps/note/models/transactions.py:81 apps/permission/models.py:113
#: apps/permission/models.py:192 apps/wei/models.py:72 apps/wei/models.py:129
msgid "description"
msgstr "Beschreibung"
@ -105,8 +105,8 @@ msgstr "Wo findet die Veranstaltung statt ? (z.B Kfet)"
#: apps/activity/models.py:83
#: apps/activity/templates/activity/includes/activity_info.html:22
#: apps/note/models/notes.py:236 apps/note/models/transactions.py:66
#: apps/permission/models.py:164
#: apps/note/models/notes.py:208 apps/note/models/transactions.py:66
#: apps/permission/models.py:167
msgid "type"
msgstr "Type"
@ -254,15 +254,15 @@ msgstr "entfernen"
msgid "Type"
msgstr "Type"
#: apps/activity/tables.py:82 apps/member/forms.py:185
#: apps/registration/forms.py:81 apps/treasury/forms.py:130
#: apps/activity/tables.py:82 apps/member/forms.py:186
#: apps/registration/forms.py:90 apps/treasury/forms.py:130
#: apps/wei/forms/registration.py:96
msgid "Last name"
msgstr "Nachname"
#: apps/activity/tables.py:84 apps/member/forms.py:190
#: apps/activity/tables.py:84 apps/member/forms.py:191
#: apps/note/templates/note/transaction_form.html:134
#: apps/registration/forms.py:86 apps/treasury/forms.py:132
#: apps/registration/forms.py:95 apps/treasury/forms.py:132
#: apps/wei/forms/registration.py:101
msgid "First name"
msgstr "Vorname"
@ -286,7 +286,7 @@ msgid "Guest deleted"
msgstr "Gastliste"
#: apps/activity/templates/activity/activity_entry.html:14
#: apps/note/models/transactions.py:253
#: apps/note/models/transactions.py:256
#: apps/note/templates/note/transaction_form.html:16
#: apps/note/templates/note/transaction_form.html:148
#: note_kfet/templates/base.html:73
@ -294,13 +294,13 @@ msgid "Transfer"
msgstr "Überweisen"
#: apps/activity/templates/activity/activity_entry.html:18
#: apps/note/models/transactions.py:313
#: apps/note/models/transactions.py:316
#: apps/note/templates/note/transaction_form.html:21
msgid "Credit"
msgstr "Kredit"
#: apps/activity/templates/activity/activity_entry.html:21
#: apps/note/models/transactions.py:313
#: apps/note/models/transactions.py:316
#: apps/note/templates/note/transaction_form.html:25
msgid "Debit"
msgstr "Soll"
@ -391,39 +391,39 @@ msgstr "bearbeiten"
msgid "Invite"
msgstr "Einladen"
#: apps/activity/views.py:34
#: apps/activity/views.py:36
msgid "Create new activity"
msgstr "Neue Veranstaltung schaffen"
#: apps/activity/views.py:65 note_kfet/templates/base.html:91
#: apps/activity/views.py:67 note_kfet/templates/base.html:91
msgid "Activities"
msgstr "Veranstaltungen"
#: apps/activity/views.py:93
#: apps/activity/views.py:95
msgid "Activity detail"
msgstr "Veranstaltunginfo"
#: apps/activity/views.py:113
#: apps/activity/views.py:115
msgid "Update activity"
msgstr "Veranstaltung bearbeiten"
#: apps/activity/views.py:140
#: apps/activity/views.py:142
msgid "Invite guest to the activity \"{}\""
msgstr "Gast zur Veranstaltung \"{}\" einladen"
#: apps/activity/views.py:175
#: apps/activity/views.py:177
msgid "You are not allowed to display the entry interface for this activity."
msgstr "Sie haben nicht das Recht diese Seite zu benuzten."
#: apps/activity/views.py:178
#: apps/activity/views.py:180
msgid "This activity does not support activity entries."
msgstr "Diese Veranstaltung braucht nicht Eintritt."
#: apps/activity/views.py:181
#: apps/activity/views.py:183
msgid "This activity is closed."
msgstr "Diese Veranstaltung ist geschlossen."
#: apps/activity/views.py:277
#: apps/activity/views.py:279
msgid "Entry for activity \"{}\""
msgstr "Eintritt zur Veranstaltung \"{}\""
@ -439,7 +439,7 @@ msgstr "Logs"
msgid "IP Address"
msgstr "IP Adresse"
#: apps/logs/models.py:36 apps/permission/models.py:134
#: apps/logs/models.py:36 apps/permission/models.py:137
msgid "model"
msgstr "Model"
@ -460,7 +460,7 @@ msgid "create"
msgstr "schaffen"
#: apps/logs/models.py:65 apps/note/tables.py:165 apps/note/tables.py:201
#: apps/permission/models.py:127 apps/treasury/tables.py:38
#: apps/permission/models.py:130 apps/treasury/tables.py:38
#: apps/wei/tables.py:75
msgid "delete"
msgstr "entfernen"
@ -541,48 +541,48 @@ msgid "This image cannot be loaded."
msgstr "Dieses Bild kann nicht geladen werden."
#: apps/member/forms.py:141 apps/member/views.py:100
#: apps/registration/forms.py:33 apps/registration/views.py:244
#: apps/registration/forms.py:33 apps/registration/views.py:254
msgid "An alias with a similar name already exists."
msgstr "Ein ähnliches Alias ist schon benutzt."
#: apps/member/forms.py:164 apps/registration/forms.py:61
#: apps/member/forms.py:165 apps/registration/forms.py:70
msgid "Inscription paid by Société Générale"
msgstr "Mitgliedschaft von der Société Générale bezahlt"
#: apps/member/forms.py:166 apps/registration/forms.py:63
#: apps/member/forms.py:167 apps/registration/forms.py:72
msgid "Check this case if the Société Générale paid the inscription."
msgstr "Die Société Générale die Mitgliedschaft bezahlt."
#: apps/member/forms.py:171 apps/registration/forms.py:68
#: apps/member/forms.py:172 apps/registration/forms.py:77
#: apps/wei/forms/registration.py:83
msgid "Credit type"
msgstr "Kredittype"
#: apps/member/forms.py:172 apps/registration/forms.py:69
#: apps/member/forms.py:173 apps/registration/forms.py:78
#: apps/wei/forms/registration.py:84
msgid "No credit"
msgstr "Kein Kredit"
#: apps/member/forms.py:174
#: apps/member/forms.py:175
msgid "You can credit the note of the user."
msgstr "Sie dûrfen diese Note kreditieren."
#: apps/member/forms.py:178 apps/registration/forms.py:74
#: apps/member/forms.py:179 apps/registration/forms.py:83
#: apps/wei/forms/registration.py:89
msgid "Credit amount"
msgstr "Kreditanzahl"
#: apps/member/forms.py:195 apps/note/templates/note/transaction_form.html:140
#: apps/registration/forms.py:91 apps/treasury/forms.py:134
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:140
#: apps/registration/forms.py:100 apps/treasury/forms.py:134
#: apps/wei/forms/registration.py:106
msgid "Bank"
msgstr "Bank"
#: apps/member/forms.py:222
#: apps/member/forms.py:223
msgid "User"
msgstr "User"
#: apps/member/forms.py:236
#: apps/member/forms.py:237
msgid "Roles"
msgstr "Rollen"
@ -810,7 +810,7 @@ msgstr ""
"Maximales Datum einer Mitgliedschaft, nach dem Mitglieder es erneuern müssen."
#: apps/member/models.py:286 apps/member/models.py:311
#: apps/note/models/notes.py:191
#: apps/note/models/notes.py:177
msgid "club"
msgstr "Club"
@ -831,11 +831,11 @@ msgstr "Mitgliedschaft endet am"
msgid "The role {role} does not apply to the club {club}."
msgstr "Die Rolle {role} ist nicht erlaubt für das Club {club}."
#: apps/member/models.py:430 apps/member/views.py:634
#: apps/member/models.py:430 apps/member/views.py:646
msgid "User is already a member of the club"
msgstr "User ist schon ein Mitglied dieser club"
#: apps/member/models.py:442 apps/member/views.py:644
#: apps/member/models.py:442 apps/member/views.py:656
msgid "User is not a member of the parent club"
msgstr "User ist noch nicht Mitglied des Urclubs"
@ -844,7 +844,7 @@ msgstr "User ist noch nicht Mitglied des Urclubs"
msgid "Membership of {user} for the club {club}"
msgstr "Mitgliedschaft von {user} für das Club {club}"
#: apps/member/models.py:498 apps/note/models/transactions.py:355
#: apps/member/models.py:498 apps/note/models/transactions.py:358
msgid "membership"
msgstr "Mitgliedschaft"
@ -963,8 +963,8 @@ msgstr ""
"erlaubt."
#: apps/member/templates/member/club_alias.html:10
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:238
#: apps/member/views.py:436
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:245
#: apps/member/views.py:448
msgid "Note aliases"
msgstr "Note Aliases"
@ -1028,7 +1028,7 @@ msgstr "Kontostand"
#: apps/member/templates/member/includes/club_info.html:47
#: apps/member/templates/member/includes/profile_info.html:20
#: apps/note/models/notes.py:283 apps/wei/templates/wei/base.html:66
#: apps/note/models/notes.py:255 apps/wei/templates/wei/base.html:66
msgid "aliases"
msgstr "Aliases"
@ -1107,39 +1107,39 @@ msgstr "Diese Adresse muss gültig sein."
msgid "Profile detail"
msgstr "Profile detail"
#: apps/member/views.py:197
#: apps/member/views.py:204
msgid "Search user"
msgstr "User finden"
#: apps/member/views.py:258
#: apps/member/views.py:265
msgid "Update note picture"
msgstr "Notebild ändern"
#: apps/member/views.py:304
#: apps/member/views.py:311
msgid "Manage auth token"
msgstr "Auth token bearbeiten"
#: apps/member/views.py:331
#: apps/member/views.py:338
msgid "Create new club"
msgstr "Neue Club"
#: apps/member/views.py:350
#: apps/member/views.py:357
msgid "Search club"
msgstr "Club finden"
#: apps/member/views.py:383
#: apps/member/views.py:390
msgid "Club detail"
msgstr "Club Details"
#: apps/member/views.py:459
#: apps/member/views.py:471
msgid "Update club"
msgstr "Club bearbeiten"
#: apps/member/views.py:493
#: apps/member/views.py:505
msgid "Add new member to the club"
msgstr "Neue Mitglieder"
#: apps/member/views.py:625 apps/wei/views.py:928
#: apps/member/views.py:637 apps/wei/views.py:928
msgid ""
"This user don't have enough money to join this club, and can't have a "
"negative balance."
@ -1147,25 +1147,25 @@ msgstr ""
"Diese User hat nicht genug Geld um Mitglied zu werden, und darf nich im Rot "
"sein."
#: apps/member/views.py:648
#: apps/member/views.py:660
msgid "The membership must start after {:%m-%d-%Y}."
msgstr "Die Mitgliedschaft muss nach {:%m-%d-Y} anfängen."
#: apps/member/views.py:653
#: apps/member/views.py:665
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr "Die Mitgliedschaft muss vor {:%m-%d-Y} anfängen."
#: apps/member/views.py:660 apps/member/views.py:662 apps/member/views.py:664
#: apps/registration/views.py:294 apps/registration/views.py:296
#: apps/registration/views.py:298 apps/wei/views.py:933 apps/wei/views.py:937
#: apps/member/views.py:672 apps/member/views.py:674 apps/member/views.py:676
#: apps/registration/views.py:304 apps/registration/views.py:306
#: apps/registration/views.py:308 apps/wei/views.py:933 apps/wei/views.py:937
msgid "This field is required."
msgstr "Dies ist ein Pflichtfeld."
#: apps/member/views.py:800
#: apps/member/views.py:816
msgid "Manage roles of an user in the club"
msgstr "Rollen in diesen Club bearbeiten"
#: apps/member/views.py:825
#: apps/member/views.py:841
msgid "Members of the club"
msgstr "Mitlglieder dieses Club"
@ -1184,7 +1184,7 @@ msgid "amount"
msgstr "Anzahl"
#: apps/note/api/serializers.py:183 apps/note/api/serializers.py:189
#: apps/note/models/transactions.py:224
#: apps/note/models/transactions.py:227
msgid ""
"The transaction can't be saved since the source note or the destination note "
"is not active."
@ -1293,51 +1293,51 @@ msgstr "User Note"
msgid "%(user)s's note"
msgstr "%(user)s's note"
#: apps/note/models/notes.py:195
#: apps/note/models/notes.py:181
msgid "club note"
msgstr "Club Note"
#: apps/note/models/notes.py:196
#: apps/note/models/notes.py:182
msgid "clubs notes"
msgstr "Club Notes"
#: apps/note/models/notes.py:202
#: apps/note/models/notes.py:188
#, python-format
msgid "Note of %(club)s club"
msgstr "%(club)s Note"
#: apps/note/models/notes.py:242
#: apps/note/models/notes.py:214
msgid "special note"
msgstr "Sondernote"
#: apps/note/models/notes.py:243
#: apps/note/models/notes.py:215
msgid "special notes"
msgstr "Sondernoten"
#: apps/note/models/notes.py:266
#: apps/note/models/notes.py:238
msgid "Invalid alias"
msgstr "Unerlaublt Alias"
#: apps/note/models/notes.py:282
#: apps/note/models/notes.py:254
msgid "alias"
msgstr "Alias"
#: apps/note/models/notes.py:306
#: apps/note/models/notes.py:278
msgid "Alias is too long."
msgstr "Alias ist zu lang."
#: apps/note/models/notes.py:309
#: apps/note/models/notes.py:281
msgid ""
"This alias contains only complex character. Please use a more simple alias."
msgstr ""
"Dieser Alias enthält nur komplexe Zeichen. Bitte verwenden Sie einen "
"einfacheren Alias."
#: apps/note/models/notes.py:313
#: apps/note/models/notes.py:285
msgid "An alias with a similar name already exists: {} "
msgstr "Ein Alias mit einem ähnlichen Namen existiert bereits: {} "
#: apps/note/models/notes.py:327
#: apps/note/models/notes.py:299
msgid "You can't delete your main alias."
msgstr "Sie können Ihren Hauptalias nicht löschen."
@ -1412,34 +1412,34 @@ msgstr ""
"Die Notenguthaben müssen zwischen - 92 233 720 368 547 758,08 € und 92 233 "
"720 368 547 758,07 € liegen."
#: apps/note/models/transactions.py:273
#: apps/note/models/transactions.py:276
msgid ""
"The destination of this transaction must equal to the destination of the "
"template."
msgstr ""
"Der Empfänger dieser Transaktion muss dem Empfänger der Vorlage entsprechen."
#: apps/note/models/transactions.py:283
#: apps/note/models/transactions.py:286
msgid "Template"
msgstr "Vorlage"
#: apps/note/models/transactions.py:286
#: apps/note/models/transactions.py:289
msgid "recurrent transaction"
msgstr "wiederkehrende Transaktion"
#: apps/note/models/transactions.py:287
#: apps/note/models/transactions.py:290
msgid "recurrent transactions"
msgstr "wiederkehrende Transaktionen"
#: apps/note/models/transactions.py:302
#: apps/note/models/transactions.py:305
msgid "first_name"
msgstr "Vorname"
#: apps/note/models/transactions.py:307
#: apps/note/models/transactions.py:310
msgid "bank"
msgstr "Bank"
#: apps/note/models/transactions.py:324
#: apps/note/models/transactions.py:327
msgid ""
"A special transaction is only possible between a Note associated to a "
"payment method and a User or a Club"
@ -1447,19 +1447,19 @@ msgstr ""
"Eine Sondertransaktion ist nur zwischen einer Note, die einer "
"Zahlungsmethode zugeordnet ist, und einem User oder einem Club möglich"
#: apps/note/models/transactions.py:333
#: apps/note/models/transactions.py:336
msgid "Special transaction"
msgstr "Sondertransaktion"
#: apps/note/models/transactions.py:334
#: apps/note/models/transactions.py:337
msgid "Special transactions"
msgstr "Sondertranskationen"
#: apps/note/models/transactions.py:350
#: apps/note/models/transactions.py:353
msgid "membership transaction"
msgstr "Mitgliedschafttransaktion"
#: apps/note/models/transactions.py:351 apps/treasury/models.py:284
#: apps/note/models/transactions.py:354 apps/treasury/models.py:284
msgid "membership transactions"
msgstr "Mitgliedschaftttransaktionen"
@ -1649,53 +1649,53 @@ msgstr "Sie können keine Taste sehen."
msgid "Search transactions"
msgstr "Transaktion finden"
#: apps/permission/models.py:89
#: apps/permission/models.py:92
#, python-brace-format
msgid "Can {type} {model}.{field} in {query}"
msgstr "Kann {type} {model}.{field} in {query}"
#: apps/permission/models.py:91
#: apps/permission/models.py:94
#, python-brace-format
msgid "Can {type} {model} in {query}"
msgstr "Kann {type} {model} in {query}"
#: apps/permission/models.py:104
#: apps/permission/models.py:107
msgid "rank"
msgstr "Rank"
#: apps/permission/models.py:117
#: apps/permission/models.py:120
msgid "permission mask"
msgstr "Berechtigungsmaske"
#: apps/permission/models.py:118
#: apps/permission/models.py:121
msgid "permission masks"
msgstr "Berechtigungsmasken"
#: apps/permission/models.py:124
#: apps/permission/models.py:127
msgid "add"
msgstr "hinzufügen"
#: apps/permission/models.py:125
#: apps/permission/models.py:128
msgid "view"
msgstr "Schauen"
#: apps/permission/models.py:126
#: apps/permission/models.py:129
msgid "change"
msgstr "bearbeiten"
#: apps/permission/models.py:158
#: apps/permission/models.py:161
msgid "query"
msgstr "Abfrage"
#: apps/permission/models.py:171
#: apps/permission/models.py:174
msgid "mask"
msgstr "Maske"
#: apps/permission/models.py:177
#: apps/permission/models.py:180
msgid "field"
msgstr "Feld"
#: apps/permission/models.py:182
#: apps/permission/models.py:185
msgid ""
"Tells if the permission should be granted even if the membership of the user "
"is expired."
@ -1703,28 +1703,28 @@ msgstr ""
"Gibt an, ob die Berechtigung auch erteilt werden soll, wenn die "
"Mitgliedschaft des Benutzers abgelaufen ist."
#: apps/permission/models.py:183
#: apps/permission/models.py:186
#: apps/permission/templates/permission/all_rights.html:89
msgid "permanent"
msgstr "permanent"
#: apps/permission/models.py:194
#: apps/permission/models.py:197
msgid "permission"
msgstr "Berechtigung"
#: apps/permission/models.py:195 apps/permission/models.py:335
#: apps/permission/models.py:198 apps/permission/models.py:338
msgid "permissions"
msgstr "Berechtigungen"
#: apps/permission/models.py:200
#: apps/permission/models.py:203
msgid "Specifying field applies only to view and change permission types."
msgstr "Angabefeld gilt nur zum Anzeigen und Ändern von Berechtigungstypen."
#: apps/permission/models.py:340
#: apps/permission/models.py:343
msgid "for club"
msgstr "Für Club"
#: apps/permission/models.py:350 apps/permission/models.py:351
#: apps/permission/models.py:353 apps/permission/models.py:354
msgid "role permissions"
msgstr "Berechtigung Rollen"
@ -1832,10 +1832,24 @@ msgid "This email address is already used."
msgstr "Diese email adresse ist schon benutzt."
#: apps/registration/forms.py:49
#, fuzzy
#| msgid "You already opened an account in the Société générale."
msgid ""
"I declare that I opened a bank account in the Société générale with the BDE "
"partnership."
msgstr "Sie haben bereits ein Konto in der Société générale eröffnet."
#: apps/registration/forms.py:50
msgid ""
"Warning: this engages you to open your bank account. If you finally decides "
"to don't open your account, you will have to pay the BDE membership."
msgstr ""
#: apps/registration/forms.py:58
msgid "Register to the WEI"
msgstr "Zu WEI anmelden"
#: apps/registration/forms.py:51
#: apps/registration/forms.py:60
msgid ""
"Check this case if you want to register to the WEI. If you hesitate, you "
"will be able to register later, after validating your account in the Kfet."
@ -1844,11 +1858,11 @@ msgstr ""
"falls Zweifel, können Sie sich später nach Bestätigung Ihres Kontos im Kfet "
"registrieren."
#: apps/registration/forms.py:96
#: apps/registration/forms.py:105
msgid "Join BDE Club"
msgstr "BDE Mitglieder werden"
#: apps/registration/forms.py:103
#: apps/registration/forms.py:112
msgid "Join Kfet Club"
msgstr "Kfet Mitglieder werden"
@ -1900,7 +1914,14 @@ msgstr "Registrierung löschen"
msgid "Validate account"
msgstr "Konto validieren"
#: apps/registration/templates/registration/future_profile_detail.html:64
#: apps/registration/templates/registration/future_profile_detail.html:62
#, fuzzy
#| msgid "You already opened an account in the Société générale."
msgid ""
"The user declared that he/she opened a bank account in the Société générale."
msgstr "Sie haben bereits ein Konto in der Société générale eröffnet."
#: apps/registration/templates/registration/future_profile_detail.html:71
#: apps/wei/templates/wei/weimembership_form.html:127
#: apps/wei/templates/wei/weimembership_form.html:186
msgid "Validate registration"
@ -1956,50 +1977,50 @@ msgstr "Die NoteKfet Team."
msgid "Register new user"
msgstr "Neuen User registrieren"
#: apps/registration/views.py:85
#: apps/registration/views.py:93
msgid "Email validation"
msgstr "Email validierung"
#: apps/registration/views.py:87
#: apps/registration/views.py:95
msgid "Validate email"
msgstr "Email validieren"
#: apps/registration/views.py:129
#: apps/registration/views.py:137
msgid "Email validation unsuccessful"
msgstr "Email validierung unerfolgreich"
#: apps/registration/views.py:140
#: apps/registration/views.py:148
msgid "Email validation email sent"
msgstr "Validierungsemail wurde gesendet"
#: apps/registration/views.py:148
#: apps/registration/views.py:156
msgid "Resend email validation link"
msgstr "E-Mail-Validierungslink erneut senden"
#: apps/registration/views.py:166
#: apps/registration/views.py:174
msgid "Pre-registered users list"
msgstr "Vorregistrierte Userliste"
#: apps/registration/views.py:190
#: apps/registration/views.py:198
msgid "Unregistered users"
msgstr "Unregistrierte Users"
#: apps/registration/views.py:203
#: apps/registration/views.py:211
msgid "Registration detail"
msgstr "Registrierung Detailen"
#: apps/registration/views.py:263
#: apps/registration/views.py:273
msgid "You must join the BDE."
msgstr "Sie müssen die BDE beitreten."
#: apps/registration/views.py:287
#: apps/registration/views.py:297
msgid ""
"The entered amount is not enough for the memberships, should be at least {}"
msgstr ""
"Der eingegebene Betrag reicht für die Mitgliedschaft nicht aus, sollte "
"mindestens {} betragen"
#: apps/registration/views.py:367
#: apps/registration/views.py:384
msgid "Invalidate pre-registration"
msgstr "Ungültige Vorregistrierung"
@ -2145,7 +2166,7 @@ msgstr "spezielle Transaktion Proxies"
msgid "credit transaction"
msgstr "Kredit Transaktion"
#: apps/treasury/models.py:369
#: apps/treasury/models.py:374
msgid ""
"This user doesn't have enough money to pay the memberships with its note. "
"Please ask her/him to credit the note before invalidating this credit."
@ -2153,16 +2174,16 @@ msgstr ""
"Dieser Benutzer hat nicht genug Geld, um die Mitgliedschaften mit seiner "
"Note zu bezahlen."
#: apps/treasury/models.py:384
#: apps/treasury/models.py:389
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
msgid "Credit from the Société générale"
msgstr "Kredit von der Société générale"
#: apps/treasury/models.py:385
#: apps/treasury/models.py:390
msgid "Credits from the Société générale"
msgstr "Krediten von der Société générale"
#: apps/treasury/models.py:388
#: apps/treasury/models.py:393
#, python-brace-format
msgid "Soge credit for {user}"
msgstr "Kredit von der Société générale für {user}"
@ -2941,19 +2962,19 @@ msgstr "Überprüfen Sie die WEI-Registrierung"
msgid "This user didn't give her/his caution check."
msgstr "Dieser User hat seine / ihre Vorsicht nicht überprüft."
#: note_kfet/settings/base.py:155
#: note_kfet/settings/base.py:157
msgid "German"
msgstr "Deutsch"
#: note_kfet/settings/base.py:156
#: note_kfet/settings/base.py:158
msgid "English"
msgstr "English"
#: note_kfet/settings/base.py:157
#: note_kfet/settings/base.py:159
msgid "Spanish"
msgstr "Spanisch"
#: note_kfet/settings/base.py:158
#: note_kfet/settings/base.py:160
msgid "French"
msgstr "Französich"
@ -3040,7 +3061,7 @@ msgstr "Abmelden"
#: note_kfet/templates/base.html:139
#: note_kfet/templates/registration/signup.html:6
#: note_kfet/templates/registration/signup.html:11
#: note_kfet/templates/registration/signup.html:27
#: note_kfet/templates/registration/signup.html:28
msgid "Sign up"
msgstr "Registrieren"
@ -3052,7 +3073,17 @@ msgstr "Registrieren"
msgid "Log in"
msgstr "Anmelden"
#: note_kfet/templates/base.html:158
#: note_kfet/templates/base.html:156
msgid ""
"You are not a BDE member anymore. Please renew your membership if you want "
"to use the note."
msgstr ""
#: note_kfet/templates/base.html:160
msgid "You are not a Kfet member, so you can't use your note account."
msgstr ""
#: note_kfet/templates/base.html:166
msgid ""
"Your e-mail address is not validated. Please check your mail inbox and click "
"on the validation link."
@ -3060,7 +3091,16 @@ msgstr ""
"Ihre E-Mail-Adresse ist nicht validiert. Bitte überprüfen Sie Ihren "
"Posteingang und klicken Sie auf den Validierungslink."
#: note_kfet/templates/base.html:175
#: note_kfet/templates/base.html:171
msgid ""
"You declared that you opened a bank account in the Société générale. The "
"bank did not validate the creation of the account to the BDE, so the "
"registration bonus of 80 € is not credited and the membership is not paid "
"yet. This verification procedure may last a few days. Please make sure that "
"you go to the end of the account creation."
msgstr ""
#: note_kfet/templates/base.html:194
msgid "Contact us"
msgstr "Kontakt"
@ -3072,21 +3112,6 @@ msgstr "Suche nach Attributen wie Name…"
msgid "There is no results."
msgstr "Es gibt keine Ergebnisse."
#: note_kfet/templates/cas_server/base.html:7
msgid "Central Authentication Service"
msgstr "Central Authentication Service"
#: note_kfet/templates/cas_server/base.html:43
#, python-format
msgid ""
"A new version of the application is available. This instance runs "
"%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
"upgrading."
msgstr ""
"Eine neue Version der Anwendung ist verfügbar. Diese Instanz führt "
"%(VERSION) s aus und die letzte Version ist %(LAST_VERSION) s. Bitte erwägen "
"Sie ein Upgrade."
#: note_kfet/templates/registration/logged_out.html:13
msgid "Thanks for spending some quality time with the Web site today."
msgstr ""
@ -3200,5 +3225,18 @@ msgstr ""
"müssen Ihre E-Mail-Adresse auch überprüfen, indem Sie dem Link folgen, den "
"Sie erhalten haben."
#~ msgid "Central Authentication Service"
#~ msgstr "Central Authentication Service"
#, python-format
#~ msgid ""
#~ "A new version of the application is available. This instance runs "
#~ "%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
#~ "upgrading."
#~ msgstr ""
#~ "Eine neue Version der Anwendung ist verfügbar. Diese Instanz führt "
#~ "%(VERSION) s aus und die letzte Version ist %(LAST_VERSION) s. Bitte "
#~ "erwägen Sie ein Upgrade."
#~ msgid "Check this case is the Société Générale paid the inscription."
#~ msgstr "Die Société Générale die Mitgliedschaft bezahlt."

View File

@ -51,9 +51,9 @@ msgstr "Usted no puede invitar más de 3 persona a esta actividad."
#: apps/member/models.py:199
#: apps/member/templates/member/includes/club_info.html:4
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/note/models/notes.py:260 apps/note/models/transactions.py:26
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:297
#: apps/permission/models.py:330
#: apps/note/models/notes.py:232 apps/note/models/transactions.py:26
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:300
#: apps/permission/models.py:333
#: apps/registration/templates/registration/future_profile_detail.html:16
#: apps/wei/models.py:66 apps/wei/models.py:118
#: apps/wei/templates/wei/base.html:26
@ -89,8 +89,8 @@ msgstr "tipos de actividad"
#: apps/activity/models.py:68
#: apps/activity/templates/activity/includes/activity_info.html:19
#: apps/note/models/transactions.py:81 apps/permission/models.py:110
#: apps/permission/models.py:189 apps/wei/models.py:72 apps/wei/models.py:129
#: apps/note/models/transactions.py:81 apps/permission/models.py:113
#: apps/permission/models.py:192 apps/wei/models.py:72 apps/wei/models.py:129
msgid "description"
msgstr "descripción"
@ -104,8 +104,8 @@ msgstr "Lugar donde se organiza la actividad, por ejemplo la Kfet."
#: apps/activity/models.py:83
#: apps/activity/templates/activity/includes/activity_info.html:22
#: apps/note/models/notes.py:236 apps/note/models/transactions.py:66
#: apps/permission/models.py:164
#: apps/note/models/notes.py:208 apps/note/models/transactions.py:66
#: apps/permission/models.py:167
msgid "type"
msgstr "tipo"
@ -253,15 +253,15 @@ msgstr "quitar"
msgid "Type"
msgstr "Tipo"
#: apps/activity/tables.py:82 apps/member/forms.py:185
#: apps/registration/forms.py:81 apps/treasury/forms.py:130
#: apps/activity/tables.py:82 apps/member/forms.py:186
#: apps/registration/forms.py:90 apps/treasury/forms.py:130
#: apps/wei/forms/registration.py:96
msgid "Last name"
msgstr "Apellido"
#: apps/activity/tables.py:84 apps/member/forms.py:190
#: apps/activity/tables.py:84 apps/member/forms.py:191
#: apps/note/templates/note/transaction_form.html:134
#: apps/registration/forms.py:86 apps/treasury/forms.py:132
#: apps/registration/forms.py:95 apps/treasury/forms.py:132
#: apps/wei/forms/registration.py:101
msgid "First name"
msgstr "Nombre"
@ -285,7 +285,7 @@ msgid "Guest deleted"
msgstr "Lista de los invitados"
#: apps/activity/templates/activity/activity_entry.html:14
#: apps/note/models/transactions.py:253
#: apps/note/models/transactions.py:256
#: apps/note/templates/note/transaction_form.html:16
#: apps/note/templates/note/transaction_form.html:148
#: note_kfet/templates/base.html:73
@ -293,13 +293,13 @@ msgid "Transfer"
msgstr "Transferencia"
#: apps/activity/templates/activity/activity_entry.html:18
#: apps/note/models/transactions.py:313
#: apps/note/models/transactions.py:316
#: apps/note/templates/note/transaction_form.html:21
msgid "Credit"
msgstr "Crédito"
#: apps/activity/templates/activity/activity_entry.html:21
#: apps/note/models/transactions.py:313
#: apps/note/models/transactions.py:316
#: apps/note/templates/note/transaction_form.html:25
msgid "Debit"
msgstr "Débito"
@ -390,41 +390,41 @@ msgstr "modificar"
msgid "Invite"
msgstr "Invitar"
#: apps/activity/views.py:34
#: apps/activity/views.py:36
msgid "Create new activity"
msgstr "Crear una nueva actividad"
#: apps/activity/views.py:65 note_kfet/templates/base.html:91
#: apps/activity/views.py:67 note_kfet/templates/base.html:91
msgid "Activities"
msgstr "Actividades"
#: apps/activity/views.py:93
#: apps/activity/views.py:95
msgid "Activity detail"
msgstr "Detalles de la actividad"
#: apps/activity/views.py:113
#: apps/activity/views.py:115
msgid "Update activity"
msgstr "Modificar la actividad"
#: apps/activity/views.py:140
#: apps/activity/views.py:142
msgid "Invite guest to the activity \"{}\""
msgstr "Invitar alguien para la actividad \"{}\""
#: apps/activity/views.py:175
#: apps/activity/views.py:177
msgid "You are not allowed to display the entry interface for this activity."
msgstr ""
"Usted no tiene derecho a mostrar la interfaz de las entradas para esta "
"actividad."
#: apps/activity/views.py:178
#: apps/activity/views.py:180
msgid "This activity does not support activity entries."
msgstr "Esta actividad no necesita entradas."
#: apps/activity/views.py:181
#: apps/activity/views.py:183
msgid "This activity is closed."
msgstr "Esta actividad esta cerrada."
#: apps/activity/views.py:277
#: apps/activity/views.py:279
msgid "Entry for activity \"{}\""
msgstr "Entradas para la actividad \"{}\""
@ -440,7 +440,7 @@ msgstr "Logs"
msgid "IP Address"
msgstr "Dirección IP"
#: apps/logs/models.py:36 apps/permission/models.py:134
#: apps/logs/models.py:36 apps/permission/models.py:137
msgid "model"
msgstr "modelo"
@ -461,7 +461,7 @@ msgid "create"
msgstr "crear"
#: apps/logs/models.py:65 apps/note/tables.py:165 apps/note/tables.py:201
#: apps/permission/models.py:127 apps/treasury/tables.py:38
#: apps/permission/models.py:130 apps/treasury/tables.py:38
#: apps/wei/tables.py:75
msgid "delete"
msgstr "suprimir"
@ -542,48 +542,48 @@ msgid "This image cannot be loaded."
msgstr "Esta imagen no puede ser cargada."
#: apps/member/forms.py:141 apps/member/views.py:100
#: apps/registration/forms.py:33 apps/registration/views.py:244
#: apps/registration/forms.py:33 apps/registration/views.py:254
msgid "An alias with a similar name already exists."
msgstr "Un alias similar ya existe."
#: apps/member/forms.py:164 apps/registration/forms.py:61
#: apps/member/forms.py:165 apps/registration/forms.py:70
msgid "Inscription paid by Société Générale"
msgstr "Registración pagadas por Société Générale"
#: apps/member/forms.py:166 apps/registration/forms.py:63
#: apps/member/forms.py:167 apps/registration/forms.py:72
msgid "Check this case if the Société Générale paid the inscription."
msgstr "Marcar esta casilla si Société Générale pagó la registración."
#: apps/member/forms.py:171 apps/registration/forms.py:68
#: apps/member/forms.py:172 apps/registration/forms.py:77
#: apps/wei/forms/registration.py:83
msgid "Credit type"
msgstr "Tipo de crédito"
#: apps/member/forms.py:172 apps/registration/forms.py:69
#: apps/member/forms.py:173 apps/registration/forms.py:78
#: apps/wei/forms/registration.py:84
msgid "No credit"
msgstr "No crédito"
#: apps/member/forms.py:174
#: apps/member/forms.py:175
msgid "You can credit the note of the user."
msgstr "Usted puede acreditar la note del usuario."
#: apps/member/forms.py:178 apps/registration/forms.py:74
#: apps/member/forms.py:179 apps/registration/forms.py:83
#: apps/wei/forms/registration.py:89
msgid "Credit amount"
msgstr "Valor del crédito"
#: apps/member/forms.py:195 apps/note/templates/note/transaction_form.html:140
#: apps/registration/forms.py:91 apps/treasury/forms.py:134
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:140
#: apps/registration/forms.py:100 apps/treasury/forms.py:134
#: apps/wei/forms/registration.py:106
msgid "Bank"
msgstr "Banco"
#: apps/member/forms.py:222
#: apps/member/forms.py:223
msgid "User"
msgstr "Usuario"
#: apps/member/forms.py:236
#: apps/member/forms.py:237
msgid "Roles"
msgstr "Papeles"
@ -810,7 +810,7 @@ msgstr ""
"prorrogarla."
#: apps/member/models.py:286 apps/member/models.py:311
#: apps/note/models/notes.py:191
#: apps/note/models/notes.py:177
msgid "club"
msgstr "club"
@ -831,11 +831,11 @@ msgstr "afiliación termina el"
msgid "The role {role} does not apply to the club {club}."
msgstr "El papel {role} no se encuentra en el club {club}."
#: apps/member/models.py:430 apps/member/views.py:634
#: apps/member/models.py:430 apps/member/views.py:646
msgid "User is already a member of the club"
msgstr "Usuario ya esta un miembro del club"
#: apps/member/models.py:442 apps/member/views.py:644
#: apps/member/models.py:442 apps/member/views.py:656
msgid "User is not a member of the parent club"
msgstr "Usuario no es un miembro del club pariente"
@ -844,7 +844,7 @@ msgstr "Usuario no es un miembro del club pariente"
msgid "Membership of {user} for the club {club}"
msgstr "Afiliación of {user} for the club {club}"
#: apps/member/models.py:498 apps/note/models/transactions.py:355
#: apps/member/models.py:498 apps/note/models/transactions.py:358
msgid "membership"
msgstr "afiliación"
@ -960,8 +960,8 @@ msgstr ""
"nuevo posibles."
#: apps/member/templates/member/club_alias.html:10
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:238
#: apps/member/views.py:436
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:245
#: apps/member/views.py:448
msgid "Note aliases"
msgstr "Alias de la note"
@ -1025,7 +1025,7 @@ msgstr "saldo de la cuenta"
#: apps/member/templates/member/includes/club_info.html:47
#: apps/member/templates/member/includes/profile_info.html:20
#: apps/note/models/notes.py:283 apps/wei/templates/wei/base.html:66
#: apps/note/models/notes.py:255 apps/wei/templates/wei/base.html:66
msgid "aliases"
msgstr "alias"
@ -1104,39 +1104,39 @@ msgstr "Este correo tiene que ser valido."
msgid "Profile detail"
msgstr "Detalles del usuario"
#: apps/member/views.py:197
#: apps/member/views.py:204
msgid "Search user"
msgstr "Buscar un usuario"
#: apps/member/views.py:258
#: apps/member/views.py:265
msgid "Update note picture"
msgstr "Modificar la imagen de la note"
#: apps/member/views.py:304
#: apps/member/views.py:311
msgid "Manage auth token"
msgstr "Gestionar los token de autentificación"
#: apps/member/views.py:331
#: apps/member/views.py:338
msgid "Create new club"
msgstr "Crear un nuevo club"
#: apps/member/views.py:350
#: apps/member/views.py:357
msgid "Search club"
msgstr "Buscar un club"
#: apps/member/views.py:383
#: apps/member/views.py:390
msgid "Club detail"
msgstr "Detalles del club"
#: apps/member/views.py:459
#: apps/member/views.py:471
msgid "Update club"
msgstr "Modificar el club"
#: apps/member/views.py:493
#: apps/member/views.py:505
msgid "Add new member to the club"
msgstr "Añadir un nuevo miembro al club"
#: apps/member/views.py:625 apps/wei/views.py:928
#: apps/member/views.py:637 apps/wei/views.py:928
msgid ""
"This user don't have enough money to join this club, and can't have a "
"negative balance."
@ -1144,25 +1144,25 @@ msgstr ""
"Este usuario no tiene suficiente dinero para unirse a este club, y no puede "
"tener un saldo negativo."
#: apps/member/views.py:648
#: apps/member/views.py:660
msgid "The membership must start after {:%m-%d-%Y}."
msgstr "La afiliación tiene que empezar después del {:%d-%m-%Y}."
#: apps/member/views.py:653
#: apps/member/views.py:665
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr "La afiliación tiene que empezar antes del {:%d-%m-%Y}."
#: apps/member/views.py:660 apps/member/views.py:662 apps/member/views.py:664
#: apps/registration/views.py:294 apps/registration/views.py:296
#: apps/registration/views.py:298 apps/wei/views.py:933 apps/wei/views.py:937
#: apps/member/views.py:672 apps/member/views.py:674 apps/member/views.py:676
#: apps/registration/views.py:304 apps/registration/views.py:306
#: apps/registration/views.py:308 apps/wei/views.py:933 apps/wei/views.py:937
msgid "This field is required."
msgstr "Este campo es obligatorio."
#: apps/member/views.py:800
#: apps/member/views.py:816
msgid "Manage roles of an user in the club"
msgstr "Gestionar los papeles de un usuario en el club"
#: apps/member/views.py:825
#: apps/member/views.py:841
msgid "Members of the club"
msgstr "Miembros del club"
@ -1181,7 +1181,7 @@ msgid "amount"
msgstr "monto"
#: apps/note/api/serializers.py:183 apps/note/api/serializers.py:189
#: apps/note/models/transactions.py:224
#: apps/note/models/transactions.py:227
msgid ""
"The transaction can't be saved since the source note or the destination note "
"is not active."
@ -1291,51 +1291,51 @@ msgstr "notes de los usuarios"
msgid "%(user)s's note"
msgstr "Note de %(user)s"
#: apps/note/models/notes.py:195
#: apps/note/models/notes.py:181
msgid "club note"
msgstr "note de un club"
#: apps/note/models/notes.py:196
#: apps/note/models/notes.py:182
msgid "clubs notes"
msgstr "notes de los clubs"
#: apps/note/models/notes.py:202
#: apps/note/models/notes.py:188
#, python-format
msgid "Note of %(club)s club"
msgstr "Note del club %(club)s"
#: apps/note/models/notes.py:242
#: apps/note/models/notes.py:214
msgid "special note"
msgstr "note especial"
#: apps/note/models/notes.py:243
#: apps/note/models/notes.py:215
msgid "special notes"
msgstr "notes especiales"
#: apps/note/models/notes.py:266
#: apps/note/models/notes.py:238
msgid "Invalid alias"
msgstr "Alias inválido"
#: apps/note/models/notes.py:282
#: apps/note/models/notes.py:254
msgid "alias"
msgstr "alias"
#: apps/note/models/notes.py:306
#: apps/note/models/notes.py:278
msgid "Alias is too long."
msgstr "El alias es demasiado largo."
#: apps/note/models/notes.py:309
#: apps/note/models/notes.py:281
msgid ""
"This alias contains only complex character. Please use a more simple alias."
msgstr ""
"Este alias solo contiene caracteres complejos. Por favor usa un alias más "
"sencillo."
#: apps/note/models/notes.py:313
#: apps/note/models/notes.py:285
msgid "An alias with a similar name already exists: {} "
msgstr "Un alias parecido ya existe : {} "
#: apps/note/models/notes.py:327
#: apps/note/models/notes.py:299
msgid "You can't delete your main alias."
msgstr "No puede suprimir su alias principal."
@ -1410,33 +1410,33 @@ msgstr ""
"El saldo de la note tiene que ser entre - 92 233 720 368 547 758.08 € y 92 "
"233 720 368 547 758.07 €."
#: apps/note/models/transactions.py:273
#: apps/note/models/transactions.py:276
msgid ""
"The destination of this transaction must equal to the destination of the "
"template."
msgstr ""
#: apps/note/models/transactions.py:283
#: apps/note/models/transactions.py:286
msgid "Template"
msgstr ""
#: apps/note/models/transactions.py:286
#: apps/note/models/transactions.py:289
msgid "recurrent transaction"
msgstr ""
#: apps/note/models/transactions.py:287
#: apps/note/models/transactions.py:290
msgid "recurrent transactions"
msgstr ""
#: apps/note/models/transactions.py:302
#: apps/note/models/transactions.py:305
msgid "first_name"
msgstr "nombre"
#: apps/note/models/transactions.py:307
#: apps/note/models/transactions.py:310
msgid "bank"
msgstr "banco"
#: apps/note/models/transactions.py:324
#: apps/note/models/transactions.py:327
msgid ""
"A special transaction is only possible between a Note associated to a "
"payment method and a User or a Club"
@ -1444,19 +1444,19 @@ msgstr ""
"Una transacción especial solo esta disponible entre una note de un modo de "
"pago y un usuario o un club"
#: apps/note/models/transactions.py:333
#: apps/note/models/transactions.py:336
msgid "Special transaction"
msgstr "Transacción especial"
#: apps/note/models/transactions.py:334
#: apps/note/models/transactions.py:337
msgid "Special transactions"
msgstr "Transacciones especiales"
#: apps/note/models/transactions.py:350
#: apps/note/models/transactions.py:353
msgid "membership transaction"
msgstr "transacción de afiliación"
#: apps/note/models/transactions.py:351 apps/treasury/models.py:284
#: apps/note/models/transactions.py:354 apps/treasury/models.py:284
msgid "membership transactions"
msgstr "transacciones de afiliación"
@ -1646,53 +1646,53 @@ msgstr "Usted no puede ver ningún botón."
msgid "Search transactions"
msgstr "Buscar transacciones"
#: apps/permission/models.py:89
#: apps/permission/models.py:92
#, python-brace-format
msgid "Can {type} {model}.{field} in {query}"
msgstr ""
#: apps/permission/models.py:91
#: apps/permission/models.py:94
#, python-brace-format
msgid "Can {type} {model} in {query}"
msgstr ""
#: apps/permission/models.py:104
#: apps/permission/models.py:107
msgid "rank"
msgstr "posición"
#: apps/permission/models.py:117
#: apps/permission/models.py:120
msgid "permission mask"
msgstr "antifaz de permisos"
#: apps/permission/models.py:118
#: apps/permission/models.py:121
msgid "permission masks"
msgstr "antifaces de permisos"
#: apps/permission/models.py:124
#: apps/permission/models.py:127
msgid "add"
msgstr "añadir"
#: apps/permission/models.py:125
#: apps/permission/models.py:128
msgid "view"
msgstr "ver"
#: apps/permission/models.py:126
#: apps/permission/models.py:129
msgid "change"
msgstr "cambiar"
#: apps/permission/models.py:158
#: apps/permission/models.py:161
msgid "query"
msgstr "consulta"
#: apps/permission/models.py:171
#: apps/permission/models.py:174
msgid "mask"
msgstr "antifaz"
#: apps/permission/models.py:177
#: apps/permission/models.py:180
msgid "field"
msgstr "campo"
#: apps/permission/models.py:182
#: apps/permission/models.py:185
msgid ""
"Tells if the permission should be granted even if the membership of the user "
"is expired."
@ -1700,30 +1700,30 @@ msgstr ""
"Indica si el permiso tiene que ser dado aunque la afiliación del usuario "
"terminó."
#: apps/permission/models.py:183
#: apps/permission/models.py:186
#: apps/permission/templates/permission/all_rights.html:89
msgid "permanent"
msgstr "permanente"
#: apps/permission/models.py:194
#: apps/permission/models.py:197
msgid "permission"
msgstr "permiso"
#: apps/permission/models.py:195 apps/permission/models.py:335
#: apps/permission/models.py:198 apps/permission/models.py:338
msgid "permissions"
msgstr "permisos"
#: apps/permission/models.py:200
#: apps/permission/models.py:203
msgid "Specifying field applies only to view and change permission types."
msgstr ""
"Especifica el campo interesado, solo funciona para los permisos view y "
"change."
#: apps/permission/models.py:340
#: apps/permission/models.py:343
msgid "for club"
msgstr "interesa el club"
#: apps/permission/models.py:350 apps/permission/models.py:351
#: apps/permission/models.py:353 apps/permission/models.py:354
msgid "role permissions"
msgstr "permisos por papeles"
@ -1827,10 +1827,24 @@ msgid "This email address is already used."
msgstr "Este correo electrónico ya esta utilizado."
#: apps/registration/forms.py:49
#, fuzzy
#| msgid "You already opened an account in the Société générale."
msgid ""
"I declare that I opened a bank account in the Société générale with the BDE "
"partnership."
msgstr "Usted ya abrió una cuenta a la Société Générale."
#: apps/registration/forms.py:50
msgid ""
"Warning: this engages you to open your bank account. If you finally decides "
"to don't open your account, you will have to pay the BDE membership."
msgstr ""
#: apps/registration/forms.py:58
msgid "Register to the WEI"
msgstr "Registrarse en el WEI"
#: apps/registration/forms.py:51
#: apps/registration/forms.py:60
msgid ""
"Check this case if you want to register to the WEI. If you hesitate, you "
"will be able to register later, after validating your account in the Kfet."
@ -1838,11 +1852,11 @@ msgstr ""
"Marcar esta casilla si usted quiere registrarse en el WEI. Si duda, podrá "
"registrarse más tarde, después de validar su cuenta Note Kfet."
#: apps/registration/forms.py:96
#: apps/registration/forms.py:105
msgid "Join BDE Club"
msgstr "Afiliarse al club BDE"
#: apps/registration/forms.py:103
#: apps/registration/forms.py:112
msgid "Join Kfet Club"
msgstr "Afiliarse al club Kfet"
@ -1894,7 +1908,14 @@ msgstr "Suprimir afiliación"
msgid "Validate account"
msgstr "Validar la cuenta"
#: apps/registration/templates/registration/future_profile_detail.html:64
#: apps/registration/templates/registration/future_profile_detail.html:62
#, fuzzy
#| msgid "You already opened an account in the Société générale."
msgid ""
"The user declared that he/she opened a bank account in the Société générale."
msgstr "Usted ya abrió una cuenta a la Société Générale."
#: apps/registration/templates/registration/future_profile_detail.html:71
#: apps/wei/templates/wei/weimembership_form.html:127
#: apps/wei/templates/wei/weimembership_form.html:186
msgid "Validate registration"
@ -1950,50 +1971,50 @@ msgstr "El equipo Note Kfet."
msgid "Register new user"
msgstr "Registrar un nuevo usuario"
#: apps/registration/views.py:85
#: apps/registration/views.py:93
msgid "Email validation"
msgstr "Validación del correo electrónico"
#: apps/registration/views.py:87
#: apps/registration/views.py:95
msgid "Validate email"
msgstr "Validar el correo electrónico"
#: apps/registration/views.py:129
#: apps/registration/views.py:137
msgid "Email validation unsuccessful"
msgstr "La validación del correo electrónico fracasó"
#: apps/registration/views.py:140
#: apps/registration/views.py:148
msgid "Email validation email sent"
msgstr "Correo de validación enviado"
#: apps/registration/views.py:148
#: apps/registration/views.py:156
msgid "Resend email validation link"
msgstr "Reenviar el enlace de validación"
#: apps/registration/views.py:166
#: apps/registration/views.py:174
msgid "Pre-registered users list"
msgstr "Lista de los usuarios con afiliación pendiente"
#: apps/registration/views.py:190
#: apps/registration/views.py:198
msgid "Unregistered users"
msgstr "Usuarios con afiliación pendiente"
#: apps/registration/views.py:203
#: apps/registration/views.py:211
msgid "Registration detail"
msgstr "Detalles de la afiliación"
#: apps/registration/views.py:263
#: apps/registration/views.py:273
msgid "You must join the BDE."
msgstr "Usted tiene que afiliarse al BDE."
#: apps/registration/views.py:287
#: apps/registration/views.py:297
msgid ""
"The entered amount is not enough for the memberships, should be at least {}"
msgstr ""
"El monto dado no es suficiente para las afiliaciones, tiene que ser al menos "
"{}"
#: apps/registration/views.py:367
#: apps/registration/views.py:384
msgid "Invalidate pre-registration"
msgstr "Invalidar la afiliación"
@ -2139,7 +2160,7 @@ msgstr "proxys de transacciones especiales"
msgid "credit transaction"
msgstr "transacción de crédito"
#: apps/treasury/models.py:369
#: apps/treasury/models.py:374
msgid ""
"This user doesn't have enough money to pay the memberships with its note. "
"Please ask her/him to credit the note before invalidating this credit."
@ -2148,16 +2169,16 @@ msgstr ""
"afiliaciones. Por favor pídelo acreditar su note antes de invalidar este "
"crédito."
#: apps/treasury/models.py:384
#: apps/treasury/models.py:389
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
msgid "Credit from the Société générale"
msgstr "Crédito de la Société Générale"
#: apps/treasury/models.py:385
#: apps/treasury/models.py:390
msgid "Credits from the Société générale"
msgstr "Créditos de la Société Générale"
#: apps/treasury/models.py:388
#: apps/treasury/models.py:393
#, python-brace-format
msgid "Soge credit for {user}"
msgstr "Crédito de la Société Générale para {user}"
@ -2925,19 +2946,19 @@ msgstr "Validar la inscripción WEI"
msgid "This user didn't give her/his caution check."
msgstr "Este usuario no dio su cheque de garantía."
#: note_kfet/settings/base.py:155
#: note_kfet/settings/base.py:157
msgid "German"
msgstr "Alemán"
#: note_kfet/settings/base.py:156
#: note_kfet/settings/base.py:158
msgid "English"
msgstr "Ingles"
#: note_kfet/settings/base.py:157
#: note_kfet/settings/base.py:159
msgid "Spanish"
msgstr "Español"
#: note_kfet/settings/base.py:158
#: note_kfet/settings/base.py:160
msgid "French"
msgstr "Francés"
@ -3022,7 +3043,7 @@ msgstr "Desconectarse"
#: note_kfet/templates/base.html:139
#: note_kfet/templates/registration/signup.html:6
#: note_kfet/templates/registration/signup.html:11
#: note_kfet/templates/registration/signup.html:27
#: note_kfet/templates/registration/signup.html:28
msgid "Sign up"
msgstr "Registrar"
@ -3034,7 +3055,17 @@ msgstr "Registrar"
msgid "Log in"
msgstr "Conectarse"
#: note_kfet/templates/base.html:158
#: note_kfet/templates/base.html:156
msgid ""
"You are not a BDE member anymore. Please renew your membership if you want "
"to use the note."
msgstr ""
#: note_kfet/templates/base.html:160
msgid "You are not a Kfet member, so you can't use your note account."
msgstr ""
#: note_kfet/templates/base.html:166
msgid ""
"Your e-mail address is not validated. Please check your mail inbox and click "
"on the validation link."
@ -3042,7 +3073,16 @@ msgstr ""
"Su correo electrónico no fue validado. Por favor mire en sus correos y haga "
"clic en el enlace de validación."
#: note_kfet/templates/base.html:175
#: note_kfet/templates/base.html:171
msgid ""
"You declared that you opened a bank account in the Société générale. The "
"bank did not validate the creation of the account to the BDE, so the "
"registration bonus of 80 € is not credited and the membership is not paid "
"yet. This verification procedure may last a few days. Please make sure that "
"you go to the end of the account creation."
msgstr ""
#: note_kfet/templates/base.html:194
msgid "Contact us"
msgstr "Contactarnos"
@ -3054,20 +3094,6 @@ msgstr "Buscar con atributo, como el nombre…"
msgid "There is no results."
msgstr "No hay resultado."
#: note_kfet/templates/cas_server/base.html:7
msgid "Central Authentication Service"
msgstr "Servicio Central de Autentificación"
#: note_kfet/templates/cas_server/base.html:43
#, python-format
msgid ""
"A new version of the application is available. This instance runs "
"%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
"upgrading."
msgstr ""
"Una nueva versión es disponible. Se está usando %(VERSION)s y la ultima "
"versión está %(LAST_VERSION)s. Piensa en actualizar."
#: note_kfet/templates/registration/logged_out.html:13
msgid "Thanks for spending some quality time with the Web site today."
msgstr "Gracias por usar la Note Kfet."
@ -3176,6 +3202,18 @@ msgstr ""
"pagar su afiliación. Tambien tiene que validar su correo electronico con el "
"enlace que recibió."
#~ msgid "Central Authentication Service"
#~ msgstr "Servicio Central de Autentificación"
#, python-format
#~ msgid ""
#~ "A new version of the application is available. This instance runs "
#~ "%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
#~ "upgrading."
#~ msgstr ""
#~ "Una nueva versión es disponible. Se está usando %(VERSION)s y la ultima "
#~ "versión está %(LAST_VERSION)s. Piensa en actualizar."
#~ msgid "Check this case is the Société Générale paid the inscription."
#~ msgstr "Marcar esta casilla si Société Générale pagó la registración."

View File

@ -52,9 +52,9 @@ msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité."
#: apps/member/models.py:199
#: apps/member/templates/member/includes/club_info.html:4
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/note/models/notes.py:260 apps/note/models/transactions.py:26
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:297
#: apps/permission/models.py:330
#: apps/note/models/notes.py:232 apps/note/models/transactions.py:26
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:300
#: apps/permission/models.py:333
#: apps/registration/templates/registration/future_profile_detail.html:16
#: apps/wei/models.py:66 apps/wei/models.py:118
#: apps/wei/templates/wei/base.html:26
@ -90,8 +90,8 @@ msgstr "types d'activité"
#: apps/activity/models.py:68
#: apps/activity/templates/activity/includes/activity_info.html:19
#: apps/note/models/transactions.py:81 apps/permission/models.py:110
#: apps/permission/models.py:189 apps/wei/models.py:72 apps/wei/models.py:129
#: apps/note/models/transactions.py:81 apps/permission/models.py:113
#: apps/permission/models.py:192 apps/wei/models.py:72 apps/wei/models.py:129
msgid "description"
msgstr "description"
@ -105,8 +105,8 @@ msgstr "Lieu où l'activité est organisée, par exemple la Kfet."
#: apps/activity/models.py:83
#: apps/activity/templates/activity/includes/activity_info.html:22
#: apps/note/models/notes.py:236 apps/note/models/transactions.py:66
#: apps/permission/models.py:164
#: apps/note/models/notes.py:208 apps/note/models/transactions.py:66
#: apps/permission/models.py:167
msgid "type"
msgstr "type"
@ -254,15 +254,15 @@ msgstr "supprimer"
msgid "Type"
msgstr "Type"
#: apps/activity/tables.py:82 apps/member/forms.py:185
#: apps/registration/forms.py:81 apps/treasury/forms.py:130
#: apps/activity/tables.py:82 apps/member/forms.py:186
#: apps/registration/forms.py:90 apps/treasury/forms.py:130
#: apps/wei/forms/registration.py:96
msgid "Last name"
msgstr "Nom de famille"
#: apps/activity/tables.py:84 apps/member/forms.py:190
#: apps/activity/tables.py:84 apps/member/forms.py:191
#: apps/note/templates/note/transaction_form.html:134
#: apps/registration/forms.py:86 apps/treasury/forms.py:132
#: apps/registration/forms.py:95 apps/treasury/forms.py:132
#: apps/wei/forms/registration.py:101
msgid "First name"
msgstr "Prénom"
@ -284,7 +284,7 @@ msgid "Guest deleted"
msgstr "Invité supprimé"
#: apps/activity/templates/activity/activity_entry.html:14
#: apps/note/models/transactions.py:253
#: apps/note/models/transactions.py:256
#: apps/note/templates/note/transaction_form.html:16
#: apps/note/templates/note/transaction_form.html:148
#: note_kfet/templates/base.html:73
@ -292,13 +292,13 @@ msgid "Transfer"
msgstr "Virement"
#: apps/activity/templates/activity/activity_entry.html:18
#: apps/note/models/transactions.py:313
#: apps/note/models/transactions.py:316
#: apps/note/templates/note/transaction_form.html:21
msgid "Credit"
msgstr "Crédit"
#: apps/activity/templates/activity/activity_entry.html:21
#: apps/note/models/transactions.py:313
#: apps/note/models/transactions.py:316
#: apps/note/templates/note/transaction_form.html:25
msgid "Debit"
msgstr "Débit"
@ -388,41 +388,41 @@ msgstr "modifier"
msgid "Invite"
msgstr "Inviter"
#: apps/activity/views.py:34
#: apps/activity/views.py:36
msgid "Create new activity"
msgstr "Créer une nouvelle activité"
#: apps/activity/views.py:65 note_kfet/templates/base.html:91
#: apps/activity/views.py:67 note_kfet/templates/base.html:91
msgid "Activities"
msgstr "Activités"
#: apps/activity/views.py:93
#: apps/activity/views.py:95
msgid "Activity detail"
msgstr "Détails de l'activité"
#: apps/activity/views.py:113
#: apps/activity/views.py:115
msgid "Update activity"
msgstr "Modifier l'activité"
#: apps/activity/views.py:140
#: apps/activity/views.py:142
msgid "Invite guest to the activity \"{}\""
msgstr "Invitation pour l'activité « {} »"
#: apps/activity/views.py:175
#: apps/activity/views.py:177
msgid "You are not allowed to display the entry interface for this activity."
msgstr ""
"Vous n'êtes pas autorisé à afficher l'interface des entrées pour cette "
"activité."
#: apps/activity/views.py:178
#: apps/activity/views.py:180
msgid "This activity does not support activity entries."
msgstr "Cette activité ne requiert pas d'entrées."
#: apps/activity/views.py:181
#: apps/activity/views.py:183
msgid "This activity is closed."
msgstr "Cette activité est fermée."
#: apps/activity/views.py:277
#: apps/activity/views.py:279
msgid "Entry for activity \"{}\""
msgstr "Entrées pour l'activité « {} »"
@ -438,7 +438,7 @@ msgstr "Logs"
msgid "IP Address"
msgstr "Adresse IP"
#: apps/logs/models.py:36 apps/permission/models.py:134
#: apps/logs/models.py:36 apps/permission/models.py:137
msgid "model"
msgstr "modèle"
@ -459,7 +459,7 @@ msgid "create"
msgstr "créer"
#: apps/logs/models.py:65 apps/note/tables.py:165 apps/note/tables.py:201
#: apps/permission/models.py:127 apps/treasury/tables.py:38
#: apps/permission/models.py:130 apps/treasury/tables.py:38
#: apps/wei/tables.py:75
msgid "delete"
msgstr "supprimer"
@ -540,48 +540,48 @@ msgid "This image cannot be loaded."
msgstr "Cette image ne peut pas être chargée."
#: apps/member/forms.py:141 apps/member/views.py:100
#: apps/registration/forms.py:33 apps/registration/views.py:244
#: apps/registration/forms.py:33 apps/registration/views.py:254
msgid "An alias with a similar name already exists."
msgstr "Un alias avec un nom similaire existe déjà."
#: apps/member/forms.py:164 apps/registration/forms.py:61
#: apps/member/forms.py:165 apps/registration/forms.py:70
msgid "Inscription paid by Société Générale"
msgstr "Inscription payée par la Société générale"
#: apps/member/forms.py:166 apps/registration/forms.py:63
#: apps/member/forms.py:167 apps/registration/forms.py:72
msgid "Check this case if the Société Générale paid the inscription."
msgstr "Cochez cette case si la Société Générale a payé l'inscription."
#: apps/member/forms.py:171 apps/registration/forms.py:68
#: apps/member/forms.py:172 apps/registration/forms.py:77
#: apps/wei/forms/registration.py:83
msgid "Credit type"
msgstr "Type de rechargement"
#: apps/member/forms.py:172 apps/registration/forms.py:69
#: apps/member/forms.py:173 apps/registration/forms.py:78
#: apps/wei/forms/registration.py:84
msgid "No credit"
msgstr "Pas de rechargement"
#: apps/member/forms.py:174
#: apps/member/forms.py:175
msgid "You can credit the note of the user."
msgstr "Vous pouvez créditer la note de l'utilisateur avant l'adhésion."
#: apps/member/forms.py:178 apps/registration/forms.py:74
#: apps/member/forms.py:179 apps/registration/forms.py:83
#: apps/wei/forms/registration.py:89
msgid "Credit amount"
msgstr "Montant à créditer"
#: apps/member/forms.py:195 apps/note/templates/note/transaction_form.html:140
#: apps/registration/forms.py:91 apps/treasury/forms.py:134
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:140
#: apps/registration/forms.py:100 apps/treasury/forms.py:134
#: apps/wei/forms/registration.py:106
msgid "Bank"
msgstr "Banque"
#: apps/member/forms.py:222
#: apps/member/forms.py:223
msgid "User"
msgstr "Utilisateur"
#: apps/member/forms.py:236
#: apps/member/forms.py:237
msgid "Roles"
msgstr "Rôles"
@ -809,7 +809,7 @@ msgstr ""
"renouveler."
#: apps/member/models.py:286 apps/member/models.py:311
#: apps/note/models/notes.py:191
#: apps/note/models/notes.py:177
msgid "club"
msgstr "club"
@ -830,11 +830,11 @@ msgstr "l'adhésion finit le"
msgid "The role {role} does not apply to the club {club}."
msgstr "Le rôle {role} ne s'applique pas au club {club}."
#: apps/member/models.py:430 apps/member/views.py:634
#: apps/member/models.py:430 apps/member/views.py:646
msgid "User is already a member of the club"
msgstr "L'utilisateur est déjà membre du club"
#: apps/member/models.py:442 apps/member/views.py:644
#: apps/member/models.py:442 apps/member/views.py:656
msgid "User is not a member of the parent club"
msgstr "L'utilisateur n'est pas membre du club parent"
@ -843,7 +843,7 @@ msgstr "L'utilisateur n'est pas membre du club parent"
msgid "Membership of {user} for the club {club}"
msgstr "Adhésion de {user} pour le club {club}"
#: apps/member/models.py:498 apps/note/models/transactions.py:355
#: apps/member/models.py:498 apps/note/models/transactions.py:358
msgid "membership"
msgstr "adhésion"
@ -959,8 +959,8 @@ msgstr ""
"à nouveau possible."
#: apps/member/templates/member/club_alias.html:10
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:238
#: apps/member/views.py:436
#: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:245
#: apps/member/views.py:448
msgid "Note aliases"
msgstr "Alias de la note"
@ -1024,7 +1024,7 @@ msgstr "solde du compte"
#: apps/member/templates/member/includes/club_info.html:47
#: apps/member/templates/member/includes/profile_info.html:20
#: apps/note/models/notes.py:283 apps/wei/templates/wei/base.html:66
#: apps/note/models/notes.py:255 apps/wei/templates/wei/base.html:66
msgid "aliases"
msgstr "alias"
@ -1103,39 +1103,39 @@ msgstr "Cette adresse doit être valide."
msgid "Profile detail"
msgstr "Détails de l'utilisateur"
#: apps/member/views.py:197
#: apps/member/views.py:204
msgid "Search user"
msgstr "Chercher un utilisateur"
#: apps/member/views.py:258
#: apps/member/views.py:265
msgid "Update note picture"
msgstr "Modifier la photo de la note"
#: apps/member/views.py:304
#: apps/member/views.py:311
msgid "Manage auth token"
msgstr "Gérer les jetons d'authentification"
#: apps/member/views.py:331
#: apps/member/views.py:338
msgid "Create new club"
msgstr "Créer un nouveau club"
#: apps/member/views.py:350
#: apps/member/views.py:357
msgid "Search club"
msgstr "Chercher un club"
#: apps/member/views.py:383
#: apps/member/views.py:390
msgid "Club detail"
msgstr "Détails du club"
#: apps/member/views.py:459
#: apps/member/views.py:471
msgid "Update club"
msgstr "Modifier le club"
#: apps/member/views.py:493
#: apps/member/views.py:505
msgid "Add new member to the club"
msgstr "Ajouter un nouveau membre au club"
#: apps/member/views.py:625 apps/wei/views.py:928
#: apps/member/views.py:637 apps/wei/views.py:928
msgid ""
"This user don't have enough money to join this club, and can't have a "
"negative balance."
@ -1143,25 +1143,25 @@ msgstr ""
"Cet utilisateur n'a pas assez d'argent pour rejoindre ce club et ne peut pas "
"avoir un solde négatif."
#: apps/member/views.py:648
#: apps/member/views.py:660
msgid "The membership must start after {:%m-%d-%Y}."
msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}."
#: apps/member/views.py:653
#: apps/member/views.py:665
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
#: apps/member/views.py:660 apps/member/views.py:662 apps/member/views.py:664
#: apps/registration/views.py:294 apps/registration/views.py:296
#: apps/registration/views.py:298 apps/wei/views.py:933 apps/wei/views.py:937
#: apps/member/views.py:672 apps/member/views.py:674 apps/member/views.py:676
#: apps/registration/views.py:304 apps/registration/views.py:306
#: apps/registration/views.py:308 apps/wei/views.py:933 apps/wei/views.py:937
msgid "This field is required."
msgstr "Ce champ est requis."
#: apps/member/views.py:800
#: apps/member/views.py:816
msgid "Manage roles of an user in the club"
msgstr "Gérer les rôles d'un utilisateur dans le club"
#: apps/member/views.py:825
#: apps/member/views.py:841
msgid "Members of the club"
msgstr "Membres du club"
@ -1180,7 +1180,7 @@ msgid "amount"
msgstr "montant"
#: apps/note/api/serializers.py:183 apps/note/api/serializers.py:189
#: apps/note/models/transactions.py:224
#: apps/note/models/transactions.py:227
msgid ""
"The transaction can't be saved since the source note or the destination note "
"is not active."
@ -1290,51 +1290,51 @@ msgstr "notes des utilisateurs"
msgid "%(user)s's note"
msgstr "Note de %(user)s"
#: apps/note/models/notes.py:195
#: apps/note/models/notes.py:181
msgid "club note"
msgstr "note d'un club"
#: apps/note/models/notes.py:196
#: apps/note/models/notes.py:182
msgid "clubs notes"
msgstr "notes des clubs"
#: apps/note/models/notes.py:202
#: apps/note/models/notes.py:188
#, python-format
msgid "Note of %(club)s club"
msgstr "Note du club %(club)s"
#: apps/note/models/notes.py:242
#: apps/note/models/notes.py:214
msgid "special note"
msgstr "note spéciale"
#: apps/note/models/notes.py:243
#: apps/note/models/notes.py:215
msgid "special notes"
msgstr "notes spéciales"
#: apps/note/models/notes.py:266
#: apps/note/models/notes.py:238
msgid "Invalid alias"
msgstr "Alias invalide"
#: apps/note/models/notes.py:282
#: apps/note/models/notes.py:254
msgid "alias"
msgstr "alias"
#: apps/note/models/notes.py:306
#: apps/note/models/notes.py:278
msgid "Alias is too long."
msgstr "L'alias est trop long."
#: apps/note/models/notes.py:309
#: apps/note/models/notes.py:281
msgid ""
"This alias contains only complex character. Please use a more simple alias."
msgstr ""
"Cet alias ne contient que des caractères complexes. Merci d'utiliser un "
"alias plus simple."
#: apps/note/models/notes.py:313
#: apps/note/models/notes.py:285
msgid "An alias with a similar name already exists: {} "
msgstr "Un alias avec un nom similaire existe déjà : {} "
#: apps/note/models/notes.py:327
#: apps/note/models/notes.py:299
msgid "You can't delete your main alias."
msgstr "Vous ne pouvez pas supprimer votre alias principal."
@ -1410,7 +1410,7 @@ msgstr ""
"€ et 92 233 720 368 547 758.07 €. Ne cherchez pas à capitaliser l'argent du "
"BDE."
#: apps/note/models/transactions.py:273
#: apps/note/models/transactions.py:276
msgid ""
"The destination of this transaction must equal to the destination of the "
"template."
@ -1418,27 +1418,27 @@ msgstr ""
"Le destinataire de cette transaction doit être identique à celui du bouton "
"utilisé."
#: apps/note/models/transactions.py:283
#: apps/note/models/transactions.py:286
msgid "Template"
msgstr "Bouton"
#: apps/note/models/transactions.py:286
#: apps/note/models/transactions.py:289
msgid "recurrent transaction"
msgstr "transaction issue de bouton"
#: apps/note/models/transactions.py:287
#: apps/note/models/transactions.py:290
msgid "recurrent transactions"
msgstr "transactions issues de boutons"
#: apps/note/models/transactions.py:302
#: apps/note/models/transactions.py:305
msgid "first_name"
msgstr "prénom"
#: apps/note/models/transactions.py:307
#: apps/note/models/transactions.py:310
msgid "bank"
msgstr "banque"
#: apps/note/models/transactions.py:324
#: apps/note/models/transactions.py:327
msgid ""
"A special transaction is only possible between a Note associated to a "
"payment method and a User or a Club"
@ -1446,19 +1446,19 @@ msgstr ""
"Une transaction spéciale n'est possible que entre une note associée à un "
"mode de paiement et un utilisateur ou un club"
#: apps/note/models/transactions.py:333
#: apps/note/models/transactions.py:336
msgid "Special transaction"
msgstr "Transaction de crédit/retrait"
#: apps/note/models/transactions.py:334
#: apps/note/models/transactions.py:337
msgid "Special transactions"
msgstr "Transactions de crédit/retrait"
#: apps/note/models/transactions.py:350
#: apps/note/models/transactions.py:353
msgid "membership transaction"
msgstr "transaction d'adhésion"
#: apps/note/models/transactions.py:351 apps/treasury/models.py:284
#: apps/note/models/transactions.py:354 apps/treasury/models.py:284
msgid "membership transactions"
msgstr "transactions d'adhésion"
@ -1648,53 +1648,53 @@ msgstr "Vous ne pouvez pas voir le moindre bouton."
msgid "Search transactions"
msgstr "Rechercher des transactions"
#: apps/permission/models.py:89
#: apps/permission/models.py:92
#, python-brace-format
msgid "Can {type} {model}.{field} in {query}"
msgstr "Can {type} {model}.{field} in {query}"
#: apps/permission/models.py:91
#: apps/permission/models.py:94
#, python-brace-format
msgid "Can {type} {model} in {query}"
msgstr "Can {type} {model} in {query}"
#: apps/permission/models.py:104
#: apps/permission/models.py:107
msgid "rank"
msgstr "rang"
#: apps/permission/models.py:117
#: apps/permission/models.py:120
msgid "permission mask"
msgstr "masque de permissions"
#: apps/permission/models.py:118
#: apps/permission/models.py:121
msgid "permission masks"
msgstr "masques de permissions"
#: apps/permission/models.py:124
#: apps/permission/models.py:127
msgid "add"
msgstr "ajouter"
#: apps/permission/models.py:125
#: apps/permission/models.py:128
msgid "view"
msgstr "voir"
#: apps/permission/models.py:126
#: apps/permission/models.py:129
msgid "change"
msgstr "modifier"
#: apps/permission/models.py:158
#: apps/permission/models.py:161
msgid "query"
msgstr "requête"
#: apps/permission/models.py:171
#: apps/permission/models.py:174
msgid "mask"
msgstr "masque"
#: apps/permission/models.py:177
#: apps/permission/models.py:180
msgid "field"
msgstr "champ"
#: apps/permission/models.py:182
#: apps/permission/models.py:185
msgid ""
"Tells if the permission should be granted even if the membership of the user "
"is expired."
@ -1702,30 +1702,30 @@ msgstr ""
"Indique si la permission doit être attribuée même si l'adhésion de "
"l'utilisateur est expirée."
#: apps/permission/models.py:183
#: apps/permission/models.py:186
#: apps/permission/templates/permission/all_rights.html:89
msgid "permanent"
msgstr "permanent"
#: apps/permission/models.py:194
#: apps/permission/models.py:197
msgid "permission"
msgstr "permission"
#: apps/permission/models.py:195 apps/permission/models.py:335
#: apps/permission/models.py:198 apps/permission/models.py:338
msgid "permissions"
msgstr "permissions"
#: apps/permission/models.py:200
#: apps/permission/models.py:203
msgid "Specifying field applies only to view and change permission types."
msgstr ""
"Spécifie le champ concerné, ne fonctionne que pour les permissions view et "
"change."
#: apps/permission/models.py:340
#: apps/permission/models.py:343
msgid "for club"
msgstr "s'applique au club"
#: apps/permission/models.py:350 apps/permission/models.py:351
#: apps/permission/models.py:353 apps/permission/models.py:354
msgid "role permissions"
msgstr "permissions par rôles"
@ -1832,10 +1832,26 @@ msgid "This email address is already used."
msgstr "Cet email est déjà pris."
#: apps/registration/forms.py:49
msgid ""
"I declare that I opened a bank account in the Société générale with the BDE "
"partnership."
msgstr ""
"Je déclare avoir ouvert un compte à la société générale avec le partenariat "
"du BDE."
#: apps/registration/forms.py:50
msgid ""
"Warning: this engages you to open your bank account. If you finally decides "
"to don't open your account, you will have to pay the BDE membership."
msgstr ""
"Attention : cocher cette case vous engage à ouvrir votre compte. Si vous "
"décidez de ne pas le faire, vous devrez payer l'adhésion au BDE."
#: apps/registration/forms.py:58
msgid "Register to the WEI"
msgstr "S'inscrire au WEI"
#: apps/registration/forms.py:51
#: apps/registration/forms.py:60
msgid ""
"Check this case if you want to register to the WEI. If you hesitate, you "
"will be able to register later, after validating your account in the Kfet."
@ -1844,11 +1860,11 @@ msgstr ""
"pourrez toujours vous inscrire plus tard, après avoir validé votre compte à "
"la Kfet."
#: apps/registration/forms.py:96
#: apps/registration/forms.py:105
msgid "Join BDE Club"
msgstr "Adhérer au club BDE"
#: apps/registration/forms.py:103
#: apps/registration/forms.py:112
msgid "Join Kfet Club"
msgstr "Adhérer au club Kfet"
@ -1900,7 +1916,12 @@ msgstr "Supprimer l'inscription"
msgid "Validate account"
msgstr "Valider le compte"
#: apps/registration/templates/registration/future_profile_detail.html:64
#: apps/registration/templates/registration/future_profile_detail.html:62
msgid ""
"The user declared that he/she opened a bank account in the Société générale."
msgstr "L'utilisateur a déclaré avoir ouvert un compte à la société générale."
#: apps/registration/templates/registration/future_profile_detail.html:71
#: apps/wei/templates/wei/weimembership_form.html:127
#: apps/wei/templates/wei/weimembership_form.html:186
msgid "Validate registration"
@ -1954,50 +1975,50 @@ msgstr "L'équipe de la Note Kfet."
msgid "Register new user"
msgstr "Enregistrer un nouvel utilisateur"
#: apps/registration/views.py:85
#: apps/registration/views.py:93
msgid "Email validation"
msgstr "Validation de l'adresse mail"
#: apps/registration/views.py:87
#: apps/registration/views.py:95
msgid "Validate email"
msgstr "Valider l'adresse e-mail"
#: apps/registration/views.py:129
#: apps/registration/views.py:137
msgid "Email validation unsuccessful"
msgstr "La validation de l'adresse mail a échoué"
#: apps/registration/views.py:140
#: apps/registration/views.py:148
msgid "Email validation email sent"
msgstr "L'email de vérification de l'adresse email a bien été envoyé"
#: apps/registration/views.py:148
#: apps/registration/views.py:156
msgid "Resend email validation link"
msgstr "Renvoyer le lien de validation"
#: apps/registration/views.py:166
#: apps/registration/views.py:174
msgid "Pre-registered users list"
msgstr "Liste des utilisateurs en attente d'inscription"
#: apps/registration/views.py:190
#: apps/registration/views.py:198
msgid "Unregistered users"
msgstr "Utilisateurs en attente d'inscription"
#: apps/registration/views.py:203
#: apps/registration/views.py:211
msgid "Registration detail"
msgstr "Détails de l'inscription"
#: apps/registration/views.py:263
#: apps/registration/views.py:273
msgid "You must join the BDE."
msgstr "Vous devez adhérer au BDE."
#: apps/registration/views.py:287
#: apps/registration/views.py:297
msgid ""
"The entered amount is not enough for the memberships, should be at least {}"
msgstr ""
"Le montant crédité est trop faible pour adhérer, il doit être au minimum de "
"{}"
#: apps/registration/views.py:367
#: apps/registration/views.py:384
msgid "Invalidate pre-registration"
msgstr "Invalider l'inscription"
@ -2143,7 +2164,7 @@ msgstr "proxys de transactions spéciales"
msgid "credit transaction"
msgstr "transaction de crédit"
#: apps/treasury/models.py:369
#: apps/treasury/models.py:374
msgid ""
"This user doesn't have enough money to pay the memberships with its note. "
"Please ask her/him to credit the note before invalidating this credit."
@ -2151,16 +2172,16 @@ msgstr ""
"Cet utilisateur n'a pas assez d'argent pour payer les adhésions avec sa "
"note. Merci de lui demander de recharger sa note avant d'invalider ce crédit."
#: apps/treasury/models.py:384
#: apps/treasury/models.py:389
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
msgid "Credit from the Société générale"
msgstr "Crédit de la Société générale"
#: apps/treasury/models.py:385
#: apps/treasury/models.py:390
msgid "Credits from the Société générale"
msgstr "Crédits de la Société générale"
#: apps/treasury/models.py:388
#: apps/treasury/models.py:393
#, python-brace-format
msgid "Soge credit for {user}"
msgstr "Crédit de la société générale pour l'utilisateur {user}"
@ -2936,19 +2957,19 @@ msgstr "Valider l'inscription WEI"
msgid "This user didn't give her/his caution check."
msgstr "Cet utilisateur n'a pas donné son chèque de caution."
#: note_kfet/settings/base.py:155
#: note_kfet/settings/base.py:157
msgid "German"
msgstr "Allemand"
#: note_kfet/settings/base.py:156
#: note_kfet/settings/base.py:158
msgid "English"
msgstr "Anglais"
#: note_kfet/settings/base.py:157
#: note_kfet/settings/base.py:159
msgid "Spanish"
msgstr "Espagnol"
#: note_kfet/settings/base.py:158
#: note_kfet/settings/base.py:160
msgid "French"
msgstr "Français"
@ -3036,7 +3057,7 @@ msgstr "Se déconnecter"
#: note_kfet/templates/base.html:139
#: note_kfet/templates/registration/signup.html:6
#: note_kfet/templates/registration/signup.html:11
#: note_kfet/templates/registration/signup.html:27
#: note_kfet/templates/registration/signup.html:28
msgid "Sign up"
msgstr "Inscription"
@ -3048,7 +3069,21 @@ msgstr "Inscription"
msgid "Log in"
msgstr "Se connecter"
#: note_kfet/templates/base.html:158
#: note_kfet/templates/base.html:156
msgid ""
"You are not a BDE member anymore. Please renew your membership if you want "
"to use the note."
msgstr ""
"Vous n'êtes plus adhérent BDE. Merci de réadhérer si vous voulez profiter de "
"la note."
#: note_kfet/templates/base.html:160
msgid "You are not a Kfet member, so you can't use your note account."
msgstr ""
"Vous n'êtes pas adhérent Kfet, vous ne pouvez par conséquent pas utiliser "
"votre compte note."
#: note_kfet/templates/base.html:166
msgid ""
"Your e-mail address is not validated. Please check your mail inbox and click "
"on the validation link."
@ -3056,7 +3091,22 @@ msgstr ""
"Votre adresse e-mail n'est pas validée. Merci de vérifier votre boîte mail "
"et de cliquer sur le lien de validation."
#: note_kfet/templates/base.html:175
#: note_kfet/templates/base.html:171
msgid ""
"You declared that you opened a bank account in the Société générale. The "
"bank did not validate the creation of the account to the BDE, so the "
"registration bonus of 80 € is not credited and the membership is not paid "
"yet. This verification procedure may last a few days. Please make sure that "
"you go to the end of the account creation."
msgstr ""
"Vous avez déclaré que vous avez ouvert un compte bancaire à la société "
"générale. La banque n'a pas encore validé la création du compte auprès du "
"BDE, le bonus d'inscription de 80 € n'a donc pas encore été créditée et "
"l'adhésion n'est pas encore payée. Cette procédure de vérification peut "
"durer quelques jours. Merci de vous assurer de bien aller au bout de vos "
"démarches."
#: note_kfet/templates/base.html:194
msgid "Contact us"
msgstr "Nous contacter"
@ -3068,21 +3118,6 @@ msgstr "Chercher par un attribut tel que le nom …"
msgid "There is no results."
msgstr "Il n'y a pas de résultat."
#: note_kfet/templates/cas_server/base.html:7
msgid "Central Authentication Service"
msgstr "Service Central d'Authentification"
#: note_kfet/templates/cas_server/base.html:43
#, python-format
msgid ""
"A new version of the application is available. This instance runs "
"%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
"upgrading."
msgstr ""
"Une nouvelle version de l'application est disponible. Cette instance utilise "
"la version %(VERSION)s et la dernière version est %(LAST_VERSION)s. Merci de "
"vous mettre à jour."
#: note_kfet/templates/registration/logged_out.html:13
msgid "Thanks for spending some quality time with the Web site today."
msgstr "Merci d'avoir utilisé la Note Kfet."
@ -3195,5 +3230,18 @@ msgstr ""
"d'adhésion. Vous devez également valider votre adresse email en suivant le "
"lien que vous avez reçu."
#~ msgid "Central Authentication Service"
#~ msgstr "Service Central d'Authentification"
#, python-format
#~ msgid ""
#~ "A new version of the application is available. This instance runs "
#~ "%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
#~ "upgrading."
#~ msgstr ""
#~ "Une nouvelle version de l'application est disponible. Cette instance "
#~ "utilise la version %(VERSION)s et la dernière version est "
#~ "%(LAST_VERSION)s. Merci de vous mettre à jour."
#~ msgid "Check this case is the Société Générale paid the inscription."
#~ msgstr "Cochez cette case si la Société Générale a payé l'inscription."

View File

@ -20,3 +20,5 @@
55 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py send_reports
# Mettre à jour les boutons mis en avant
00 9 * * * root cd /var/www/note_kfet && env/bin/python manage.py refresh_highlighted_buttons
# Vider les tokens Oauth2
00 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py cleartokens

View File

@ -26,6 +26,14 @@ admin_site = StrongAdminSite()
admin_site.register(Site, SiteAdmin)
# Add external apps model
if "oauth2_provider" in settings.INSTALLED_APPS:
from oauth2_provider.admin import Application, ApplicationAdmin, Grant, \
GrantAdmin, AccessToken, AccessTokenAdmin, RefreshToken, RefreshTokenAdmin
admin_site.register(Application, ApplicationAdmin)
admin_site.register(Grant, GrantAdmin)
admin_site.register(AccessToken, AccessTokenAdmin)
admin_site.register(RefreshToken, RefreshTokenAdmin)
if "django_htcpcp_tea" in settings.INSTALLED_APPS:
from django_htcpcp_tea.admin import *
from django_htcpcp_tea.models import *
@ -44,9 +52,3 @@ if "rest_framework" in settings.INSTALLED_APPS:
from rest_framework.authtoken.admin import *
from rest_framework.authtoken.models import *
admin_site.register(Token, TokenAdmin)
if "cas_server" in settings.INSTALLED_APPS:
from cas_server.admin import *
from cas_server.models import *
admin_site.register(ServicePattern, ServicePatternAdmin)
admin_site.register(FederatedIendityProvider, FederatedIendityProviderAdmin)

View File

@ -1,11 +0,0 @@
[
{
"model": "cas_server.servicepattern",
"pk": 1,
"fields": {
"pos": 1,
"pattern": ".*",
"name": "REPLACEME"
}
}
]

View File

@ -2,12 +2,12 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.contrib.auth import login
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.sessions.backends.db import SessionStore
from threading import local
from django.contrib.sessions.backends.db import SessionStore
USER_ATTR_NAME = getattr(settings, 'LOCAL_USER_ATTR_NAME', '_current_user')
SESSION_ATTR_NAME = getattr(settings, 'LOCAL_SESSION_ATTR_NAME', '_current_session')
IP_ATTR_NAME = getattr(settings, 'LOCAL_IP_ATTR_NAME', '_current_ip')
@ -78,6 +78,41 @@ class SessionMiddleware(object):
return response
class LoginByIPMiddleware(object):
"""
Allow some users to be authenticated based on their IP address.
For example, the "note" account should not be used elsewhere than the Kfet computer,
and should not have any password.
The password that is stored in database should be on the form "ipbased$my.public.ip.address".
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
"""
If the user is not authenticated, get the used IP address
and check if an user is authorized to be automatically logged with this address.
If it is the case, the logging is performed with the full rights.
"""
if not request.user.is_authenticated:
if 'HTTP_X_REAL_IP' in request.META:
ip = request.META.get('HTTP_X_REAL_IP')
elif 'HTTP_X_FORWARDED_FOR' in request.META:
ip = request.META.get('HTTP_X_FORWARDED_FOR').split(', ')[0]
else:
ip = request.META.get('REMOTE_ADDR')
qs = User.objects.filter(password=f"ipbased${ip}")
if qs.exists():
login(request, qs.get())
session = request.session
session["permission_mask"] = 42
session.save()
return self.get_response(request)
class TurbolinksMiddleware(object):
"""
Send the `Turbolinks-Location` header in response to a visit that was redirected,

View File

@ -49,16 +49,6 @@ try:
except ImportError:
pass
if "cas_server" in INSTALLED_APPS:
# CAS Settings
CAS_AUTO_CREATE_USER = False
CAS_LOGO_URL = "/static/img/Saperlistpopette.png"
CAS_FAVICON_URL = "/static/favicon/favicon-32x32.png"
CAS_SHOW_POWERED = False
if "logs" in INSTALLED_APPS:
MIDDLEWARE += ('note_kfet.middlewares.SessionMiddleware',)
if DEBUG:
PASSWORD_HASHERS += ['member.hashers.DebugSuperuserBackdoor']
if "debug_toolbar" in INSTALLED_APPS:

View File

@ -35,8 +35,10 @@ INSTALLED_APPS = [
'mailer',
'phonenumber_field',
'polymorphic',
'oauth2_provider',
# Django contrib
# Django Admin will autodiscover our apps for our custom admin site.
'django.contrib.admin',
'django.contrib.admindocs',
'django.contrib.auth',
@ -77,6 +79,8 @@ MIDDLEWARE = [
'django.middleware.locale.LocaleMiddleware',
'django.contrib.sites.middleware.CurrentSiteMiddleware',
'django_htcpcp_tea.middleware.HTCPCPTeaMiddleware',
'note_kfet.middlewares.SessionMiddleware',
'note_kfet.middlewares.LoginByIPMiddleware',
'note_kfet.middlewares.TurbolinksMiddleware',
]
@ -214,6 +218,16 @@ EMAIL_HOST_PASSWORD = os.getenv('EMAIL_PASSWORD', None)
SERVER_EMAIL = os.getenv("NOTE_MAIL", "notekfet@example.com")
DEFAULT_FROM_EMAIL = "NoteKfet2020 <" + SERVER_EMAIL + ">"
# Cache
# https://docs.djangoproject.com/en/2.2/topics/cache/#setting-up-the-cache
cache_address = os.getenv("CACHE_ADDRESS", "127.0.0.1:11211")
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': cache_address,
}
}
# Django REST Framework
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
@ -233,7 +247,7 @@ REST_FRAMEWORK = {
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
# After login redirect user to transfer page
LOGIN_REDIRECT_URL = '/note/transfer/'
LOGIN_REDIRECT_URL = '/'
# An user session will expired after 3 hours
SESSION_COOKIE_AGE = 60 * 60 * 3

View File

@ -24,6 +24,14 @@ if os.getenv("DJANGO_DEV_STORE_METHOD", "sqlite") != "postgresql":
}
}
# Dummy cache for development
# https://docs.djangoproject.com/en/2.2/topics/cache/#setting-up-the-cache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
# Break it, fix it!
DEBUG = True

View File

@ -3,7 +3,6 @@
# CAS
OPTIONAL_APPS = [
# 'cas_server',
# 'debug_toolbar'
]

View File

@ -1,4 +1,4 @@
{% load static i18n pretty_money static getenv perms %}
{% load static i18n pretty_money static getenv perms memberinfo %}
{% comment %}
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
@ -67,7 +67,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}"><i class="fa fa-coffee"></i> {% trans 'Consumptions' %}</a>
</li>
{% endif %}
{% if "note.transaction"|not_empty_model_list %}
{% if user.is_authenticated and user|is_member:"Kfet" %}
<li class="nav-item">
{% url 'note:transfer' as url %}
<a class="nav-link {% if request.path_info == url %}active{% endif %}" href="{{ url }}"><i class="fa fa-exchange"></i> {% trans 'Transfer' %} </a>
@ -153,12 +153,36 @@ SPDX-License-Identifier: GPL-3.0-or-later
</div>
</nav>
<div class="{% block containertype %}container{% endblock %} my-3">
{% if request.user.is_authenticated and not request.user.profile.email_confirmed %}
<div class="alert alert-warning">
{% trans "Your e-mail address is not validated. Please check your mail inbox and click on the validation link." %}
</div>
{% endif %}
<div id="messages"></div>
<div id="messages">
{% if user.is_authenticated %}
{% if not user|is_member:"BDE" %}
<div class="alert alert-danger">
{% trans "You are not a BDE member anymore. Please renew your membership if you want to use the note." %}
</div>
{% elif not user|is_member:"Kfet" %}
<div class="alert alert-warning">
{% trans "You are not a Kfet member, so you can't use your note account." %}
</div>
{% endif %}
{% if not user.profile.email_confirmed %}
<div class="alert alert-warning">
{% trans "Your e-mail address is not validated. Please check your mail inbox and click on the validation link." %}
</div>
{% endif %}
{% endif %}
{% if user.sogecredit and not user.sogecredit.valid %}
<div class="alert alert-info">
{% blocktrans trimmed %}
You declared that you opened a bank account in the Société générale. The bank did not validate the creation of the account to the BDE,
so the registration bonus of 80 € is not credited and the membership is not paid yet.
This verification procedure may last a few days.
Please make sure that you go to the end of the account creation.
{% endblocktrans %}
</div>
{% endif %}
{# TODO Add banners #}
</div>
{% block content %}
<p>Default content...</p>
{% endblock %}

View File

@ -1,99 +0,0 @@
{% load i18n %}{% load static %}{% get_current_language as LANGUAGE_CODE %}<!DOCTYPE html>
<html{% if LANGUAGE_CODE %} lang="{{LANGUAGE_CODE}}"{% endif %}>
<head>
<meta charset="utf-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge" /><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}{% trans "Central Authentication Service" %}{% endblock %}</title>
<link href="{{settings.CAS_COMPONENT_URLS.bootstrap3_css}}" rel="stylesheet">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="{{settings.CAS_COMPONENT_URLS.html5shiv}}"></script>
<script src="{{settings.CAS_COMPONENT_URLS.respond}}"></script>
<![endif]-->
{% if settings.CAS_FAVICON_URL %}<link rel="shortcut icon" href="{{settings.CAS_FAVICON_URL}}" />{% endif %}
<link href="{% static "cas_server/styles.css" %}" rel="stylesheet">
</head>
<body>
<div id="wrap">
<div class="container">
{% if auto_submit %}<noscript>{% endif %}
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<h1 id="app-name">
{% if settings.CAS_LOGO_URL %}<img src="{{settings.CAS_LOGO_URL}}" alt="cas-logo" />{% endif %}
Authentification Note Kfet 2020</h1>
</div>
</div>
{% if auto_submit %}</noscript>{% endif %}
<div class="row">
<div class="col-lg-3 col-md-3 col-sm-2 col-xs-12"></div>
<div class="col-lg-6 col-md-6 col-sm-8 col-xs-12">
{% if auto_submit %}<noscript>{% endif %}
{% for msg in CAS_INFO_RENDER %}
<div class="alert alert-{{msg.type}}{% if msg.discardable %} alert-dismissable{% endif %}">
{% if msg.discardable %}<button type="button" class="close" data-dismiss="alert" aria-hidden="true" id="info-{{msg.name}}">&#215;</button>{% endif %}
<p>{{msg.message}}</p>
</div>
{% endfor %}
{% if settings.CAS_NEW_VERSION_HTML_WARNING and upgrade_available %}
<div class="alert alert-info alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true" id="alert-version">&#215;</button>
<p>{% blocktrans %}A new version of the application is available. This instance runs {{VERSION}} and the last version is {{LAST_VERSION}}. Please consider upgrading.{% endblocktrans %}</p>
</div>
{% endif %}
{% block ante_messages %}{% endblock %}
{% for message in messages %}
<div {% spaceless %}
{% if message.level == message_levels.DEBUG %}
class="alert alert-warning"
{% elif message.level == message_levels.INFO %}
class="alert alert-info"
{% elif message.level == message_levels.SUCCESS %}
class="alert alert-success"
{% elif message.level == message_levels.WARNING %}
class="alert alert-warning"
{% else %}
class="alert alert-danger"
{% endif %}
{% endspaceless %}>
<p>{{message}}</p>
</div>
{% endfor %}
{% if auto_submit %}</noscript>{% endif %}
{% block content %}{% endblock %}
</div>
<div class="col-lg-3 col-md-3 col-sm-2 col-xs-0"></div>
</div>
</div> <!-- /container -->
</div>
<div style="clear: both;"></div>
{% if settings.CAS_SHOW_POWERED %}
<div id="footer">
<p><a class="text-muted" href="https://pypi.org/project/django-cas-server/">django-cas-server powered</a></p>
</div>
{% endif %}
<script src="{{settings.CAS_COMPONENT_URLS.jquery}}"></script>
<script src="{{settings.CAS_COMPONENT_URLS.bootstrap3_js}}"></script>
<script src="{% static "cas_server/functions.js" %}"></script>
<script type="text/javascript">
{% if settings.CAS_NEW_VERSION_HTML_WARNING and upgrade_available %}
discard_and_remember("#alert-version", "cas-alert-version", "{{LAST_VERSION}}");
{% endif %}
{% for msg in CAS_INFO_RENDER %}
{% if msg.discardable %}
discard_and_remember("#info-{{msg.name}}", "cas-info-{{msg.name}}", "{{msg.hash}}");
{% endif %}
{% endfor %}
{% block javascript_inline %}{% endblock %}
</script>
{% block javascript %}{% endblock %}
</body>
</html>
<!--
Powered by django-cas-server version {{VERSION}}
Pypi: https://pypi.org/project/django-cas-server/
github: https://github.com/nitmir/django-cas-server
-->

View File

@ -23,6 +23,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% csrf_token %}
{{ form|crispy }}
{{ profile_form|crispy }}
{{ soge_form|crispy }}
<button class="btn btn-success" type="submit">
{% trans "Sign up" %}
</button>

View File

@ -5,15 +5,14 @@ from django.conf import settings
from django.conf.urls.static import static
from django.urls import path, include
from django.views.defaults import bad_request, permission_denied, page_not_found, server_error
from django.views.generic import RedirectView
from member.views import CustomLoginView
from .admin import admin_site
from .views import IndexView
urlpatterns = [
# Dev so redirect to something random
path('', RedirectView.as_view(pattern_name='note:transfer'), name='index'),
path('', IndexView.as_view(), name='index'),
# Include project routers
path('note/', include('note.urls')),
@ -40,12 +39,11 @@ urlpatterns = [
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if "cas_server" in settings.INSTALLED_APPS:
urlpatterns += [
# Include CAS Server routers
path('cas/', include('cas_server.urls', namespace="cas_server")),
]
if "oauth2_provider" in settings.INSTALLED_APPS:
# OAuth2 provider
urlpatterns.append(
path('o/', include('oauth2_provider.urls', namespace='oauth2_provider'))
)
if "debug_toolbar" in settings.INSTALLED_APPS:
import debug_toolbar

30
note_kfet/views.py Normal file
View File

@ -0,0 +1,30 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from django.views.generic import RedirectView
from note.models import Alias
from permission.backends import PermissionBackend
class IndexView(LoginRequiredMixin, RedirectView):
def get_redirect_url(self, *args, **kwargs):
"""
Calculate the index page according to the roles.
A normal user will have access to the transfer page.
A non-Kfet member will have access to its user detail page.
The user "note" will display the consumption interface.
"""
user = self.request.user
# The account note will have the consumption page as default page
if not PermissionBackend.check_perm(user, "auth.view_user", user):
return reverse("note:consos")
# People that can see the alias BDE are Kfet members
if PermissionBackend.check_perm(user, "alias.view_alias", Alias.objects.get(name="BDE")):
return reverse("note:transfer")
# Non-Kfet members will don't see the transfer page, but their profile page
return reverse("member:user_detail", args=(user.pk,))

View File

@ -1,17 +1,18 @@
beautifulsoup4~=4.7.1
Django~=2.2.15
django-bootstrap-datepicker-plus~=3.0.5
django-cas-server>=1.2.0
django-colorfield~=0.3.2
django-crispy-forms~=1.7.2
django-extensions~=2.1.4
django-filter~=2.1.0
django-htcpcp-tea~=0.3.1
django-mailer~=2.0.1
django-oauth-toolkit~=1.3.3
django-phonenumber-field~=5.0.0
django-polymorphic~=2.0.3
djangorestframework~=3.9.0
django-rest-polymorphic~=0.1.9
django-tables2~=2.3.1
python-memcached~=1.59
phonenumbers~=8.9.10
Pillow>=5.4.1