1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-02-12 03:01:17 +00:00

Compare commits

..

84 Commits

Author SHA1 Message Date
Yohann D'ANELLO
8434c0062c 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
2020-11-16 00:59:26 +01:00
Yohann D'ANELLO
6d976f32bf Update django oauth toolkit, fix #73 2020-11-16 00:49:53 +01:00
Yohann D'ANELLO
b9d49d53f2 Export JS translation files as static files 2020-11-16 00:29:27 +01:00
Yohann D'ANELLO
23243e09bb Fix some errors on JS string interpolation 2020-11-15 23:37:36 +01:00
Yohann D'ANELLO
2682e9a610 Add line in README on how to extract localized string in JS files 2020-11-15 23:31:10 +01:00
Yohann D'ANELLO
5635598bbc Extract strings from javascript files and translate them in french 2020-11-15 23:28:41 +01:00
Yohann D'ANELLO
b58a0c43cd Include auto-generated javascript translation file 2020-11-15 22:53:00 +01:00
Yohann D'ANELLO
7bd895c1df Grant treasurers to update a note picture 2020-10-26 17:58:30 +01:00
Yohann D'ANELLO
051591cb7a Don't see user detail in update form 2020-10-25 21:49:16 +01:00
Yohann D'ANELLO
0e7390b669 PC Kfet can see limited user information and clubs. It can create memberships but not see them 2020-10-25 21:38:04 +01:00
Yohann D'ANELLO
fe4363b83d Don't display too much detail when a user has no right to see a profile 2020-10-25 21:29:44 +01:00
Yohann D'ANELLO
6e80016b38 Don't delete object when checking an add permission: this is useless since we rollback to the initial DB state 2020-10-25 21:08:36 +01:00
Yohann D'ANELLO
08e50ffc22 Credit form didn't raise an error when the data didn't validate 2020-10-23 18:19:21 +02:00
Yohann D'ANELLO
224a0fdd8c SpecialTransactionProxy are force-saved 2020-10-23 16:55:33 +02:00
Yohann D'ANELLO
6dc7604e90 Alias were duplicated in profile alias list view 2020-10-23 16:48:33 +02:00
Yohann D'ANELLO
cb7f3c9f18 Note account can manage BDE memberships 2020-10-23 16:42:06 +02:00
Yohann D'ANELLO
f910feca9e PC Kfet can create and renew memberships 2020-10-23 13:17:07 +02:00
Yohann D'ANELLO
91f784872c Treasurers can update any roles, not only the BDE-related 2020-10-23 09:50:18 +02:00
Yohann D'ANELLO
58aa4983e3 The note account must be active in order to have access to the Rest Framework API 2020-10-20 10:30:41 +02:00
Yohann D'ANELLO
6cc3cf4174 A migration put the right role in the note account's memberships 2020-10-20 00:28:49 +02:00
Yohann D'ANELLO
2097e67321 Add permissions to PC Kfet 2020-10-20 00:19:49 +02:00
Yohann D'ANELLO
d773303d18 Add possibility to authenticate an account with its IP address 2020-10-19 23:44:56 +02:00
Yohann D'ANELLO
bf29efda0a Display real user name in the Soge credits list/detail 2020-10-08 10:36:30 +02:00
Yohann D'ANELLO
3eced33082 Well, everyone doesn't want a secondary bank account 2020-10-07 17:43:28 +02:00
Yohann D'ANELLO
acb3fb4a91 Highlight future users that declared that they opened a bank account 2020-10-07 17:42:46 +02:00
Yohann D'ANELLO
beb1853aef Forgot to create the aliases for BDE and Kfet in the migration that create the clubs 2020-10-07 11:54:04 +02:00
Yohann D'ANELLO
0078eb8f90 Index page is a redirection 2020-10-07 11:53:42 +02:00
Yohann D'ANELLO
e5e758f9d9 Display banners when a user is no more a BDE or Kfet member 2020-10-07 11:46:43 +02:00
Yohann D'ANELLO
4a78328717 The checkbox to tell that a Sogé account got opened is not mandatory 2020-10-07 11:31:20 +02:00
Yohann D'ANELLO
65a2e8c08c Better index page: non-Kfet members will be redirected to their profile page, the account note (when it will be managed) will see the consumption page 2020-10-07 11:29:52 +02:00
Yohann D'ANELLO
b5fa428bad Non-Kfet members can see their old aliases only, but no one else 2020-10-07 11:22:02 +02:00
Yohann D'ANELLO
fb72385773 Warn users that they have to open they Sogé account 2020-10-07 10:59:37 +02:00
Yohann D'ANELLO
2f68601e8b Delete the soge credit if the user declares that one was opened but in the validation form the checkbox was unchecked 2020-10-07 10:46:33 +02:00
Yohann D'ANELLO
0b1bed8048 Temporary give the right to treasurers to manage membership roles, but need to find a proper solution 2020-10-07 10:43:58 +02:00
Yohann D'ANELLO
8ada0e51f2 The validation filter of the soge credit list was buggy 2020-10-07 10:42:52 +02:00
Yohann D'ANELLO
c3d613947f Pre-registered users can declare that they opened a bank account in the signup form 2020-10-07 10:33:57 +02:00
Yohann D'ANELLO
36b8157372 Fix membership table order 2020-10-07 10:03:43 +02:00
Yohann D'ANELLO
992cfe8e23 Can set a parent club to None 2020-10-07 09:48:21 +02:00
Yohann D'ANELLO
18a8ff1b8a Set credit/debit reason non mandatory 2020-10-07 09:45:09 +02:00
Yohann D'ANELLO
c61bb2e90d When we credit the note of a club directly, fill the last name and the first name information with the club name instead of empty 2020-10-07 09:39:40 +02:00
Yohann D'ANELLO
4b12e3ed08 Display only the most recent membership 2020-10-07 09:29:41 +02:00
erdnaxe
af07ed9807 Merge branch 'erdnaxe-master-patch-99095' into 'beta'
Erdnaxe master patch 99095

See merge request bde/nk20!132
2020-10-05 16:37:14 +02:00
erdnaxe
bbe53b3b63 Update README.md 2020-10-05 16:25:06 +02:00
ynerant
536f0ec226 Merge branch 'beta' into 'master'
Ensure the integrity of all note balances in multiple transactions

See merge request bde/nk20!131
2020-10-04 22:12:12 +02:00
Yohann D'ANELLO
541ed59f40 When a membership is created, redirect to the user profile page rather than club detail 2020-10-04 21:08:35 +02:00
Yohann D'ANELLO
e172b4f4bb When a membership is renewed, set the same roles as the previous membership 2020-10-04 20:54:03 +02:00
Yohann D'ANELLO
d666179037 Display Renew membership button 15 days more 2020-10-04 20:50:10 +02:00
Yohann D'ANELLO
f22e92132c Select for update transaction notes, and not only the transaction 2020-10-04 20:47:15 +02:00
Yohann D'ANELLO
ca7ad05746 Use a signal to prevent a user that the note balance is negative 2020-10-04 20:26:43 +02:00
Pierre-antoine Comby
f55ca2f725 Merge branch 'beta' into 'master'
remove the display limit for pre-registred users.

See merge request bde/nk20!130
2020-10-03 18:07:39 +02:00
Pierre-antoine Comby
d4e4ed580f remove the display limit for pre-registred users. 2020-10-03 17:53:38 +02:00
ynerant
8756751344 Merge branch 'beta' into 'master'
Fix some membership issues

See merge request bde/nk20!129
2020-10-01 15:00:36 +02:00
Yohann D'ANELLO
fd83fe19bf Fix some membership date control 2020-10-01 09:17:02 +02:00
Yohann D'ANELLO
a00d95608b Add permission to treasurers to create a club, fix the permission check to renew a membership 2020-09-23 21:36:04 +02:00
ynerant
3303edd01f Merge branch 'beta' into 'master'
OAuth2, no side effect permissions

See merge request bde/nk20!128
2020-09-23 18:44:40 +02:00
Yohann D'ANELLO
e48ef92137 Revert commit that broke beta branch 2020-09-23 18:32:09 +02:00
erdnaxe
919d0b7e85 Merge branch 'ics_cache' into 'beta'
Ics cache

See merge request bde/nk20!127
2020-09-21 15:46:34 +02:00
Alexandre Iooss
439bf35b62
APT python memcache is PyPi memcached 2020-09-21 15:25:07 +02:00
Alexandre Iooss
74b26335d1
Cache ICS calendar 2020-09-21 15:13:59 +02:00
Alexandre Iooss
3d733ed6af
Use memcached cache 2020-09-21 15:13:43 +02:00
erdnaxe
d54ab94ceb Merge branch 'oauth' into 'beta'
Oauth

See merge request bde/nk20!126
2020-09-21 12:53:03 +02:00
Alexandre Iooss
4f188ca3e5 Admin is autodiscovering partially 2020-09-21 12:34:34 +02:00
Alexandre Iooss
72bac75fbd Add Django OAuth toolkit admin 2020-09-21 12:15:40 +02:00
Alexandre Iooss
6d54aae614
Fix django-oauth-toolkit version 2020-09-21 11:15:00 +02:00
Alexandre Iooss
8052152ea5
Add OAuth2 endpoints 2020-09-21 11:03:07 +02:00
Alexandre Iooss
70448db8e5
Remove Django CAS server and add oauth toolkit 2020-09-21 10:31:42 +02:00
ynerant
ac2d1e8111 Merge branch 'no_side_effect_permission_check' into 'beta'
No side effect permission check

See merge request bde/nk20!124
2020-09-20 11:24:46 +02:00
Yohann D'ANELLO
3ba61385a3 Debit is not credit 2020-09-20 11:12:44 +02:00
Yohann D'ANELLO
7353348d7a Rollback transaction when checking an add permission (experimental) 2020-09-20 09:07:51 +02:00
Yohann D'ANELLO
f63e2e088e Don't log when the permission to lock a note is checked 2020-09-20 08:56:42 +02:00
ynerant
5c702187e5 Merge branch 'beta' into 'master'
Corrections diverses

See merge request bde/nk20!123
2020-09-14 10:16:13 +02:00
ynerant
3191dba31f Merge branch 'beta' into 'master'
Fix permissions to let treasurers to make some initial registrations

See merge request bde/nk20!118
2020-09-07 23:52:12 +02:00
ynerant
9b8caa7fa1 Merge branch 'beta' into 'master'
Add animated profile picture support

See merge request bde/nk20!116
2020-09-07 21:48:28 +02:00
ynerant
f1dac73c08 Merge branch 'beta' into 'master'
First release

See merge request bde/nk20!115
2020-09-06 17:03:51 +02:00
ynerant
1ed74021a2 Merge branch 'beta' into 'master'
v1.0.0

See merge request bde/nk20!114
2020-09-06 16:06:26 +02:00
ynerant
81ad38927d Merge branch 'beta' into 'master'
Fix note pictures, better ansible

See merge request bde/nk20!113
2020-09-06 13:09:28 +02:00
ynerant
2f6c7ed156 Merge branch 'beta' into 'master'
Remove padding around note picture

See merge request bde/nk20!111
2020-09-05 19:41:28 +02:00
ynerant
2b2dde85dc Merge branch 'beta' into 'master'
Few fixes

Closes #61

See merge request bde/nk20!110
2020-09-05 14:52:29 +02:00
ynerant
863150d200 Merge branch 'beta' into 'master'
Last deployment fixes

See merge request bde/nk20!109
2020-09-05 11:21:07 +02:00
ynerant
ebe6ce61e4 Merge branch 'beta' into 'master'
Fix Ansible script for production

See merge request bde/nk20!108
2020-09-04 22:42:42 +02:00
Pierre-antoine Comby
3c636e9f71 Merge branch 'beta' into 'master'
Beta

Closes #59

See merge request bde/nk20!107
2020-09-04 22:32:14 +02:00
ynerant
d054d58661 Merge branch 'beta' into 'master'
Better CI

See merge request bde/nk20!105
2020-09-02 13:17:37 +02:00
Pierre-antoine Comby
9f42ecb97a Merge branch 'beta' into 'master'
Beta

Closes #52, #54, #55, #56 et #57

See merge request bde/nk20!104
2020-09-02 10:00:22 +02:00
ynerant
1fd7d76412 Merge branch 'beta' into 'master'
Fix import

See merge request bde/nk20!88
2020-08-01 18:11:01 +02:00
68 changed files with 1778 additions and 789 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
```
@ -267,14 +267,18 @@ La documentation plus haut niveau sur le développement est disponible sur [le W
### Regénérer les fichiers de traduction
Pour regénérer les traductions vous pouvez vous placer à la racine du projet et lancer le script `makemessages`. Il faut penser à ignorer les dossiers ne contenant pas notre code, dont le virtualenv.
Pour regénérer les traductions vous pouvez vous placer à la racine du projet et lancer le script `makemessages`.
Il faut penser à ignorer les dossiers ne contenant pas notre code, dont le virtualenv.
De plus, il faut aussi extraire les variables des fichiers JavaScript.
```bash
django-admin makemessages -i env
python3 manage.py makemessages -i env
python3 manage.py makemessages -i env -e js -d djangojs
```
Une fois les fichiers édités, vous pouvez compiler les nouvelles traductions avec
```bash
django-admin compilemessages
python3 manage.py compilemessages
python3 manage.py compilejsmessages
```

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

@ -1,4 +1,10 @@
---
- name: Collect static files
command: /var/www/note_kfet/env/bin/python manage.py collectstatic --noinput
args:
chdir: /var/www/note_kfet
become_user: www-data
- name: Migrate Django database
command: /var/www/note_kfet/env/bin/python manage.py migrate
args:
@ -11,14 +17,14 @@
chdir: /var/www/note_kfet
become_user: www-data
- name: Compile JavaScript messages
command: /var/www/note_kfet/env/bin/python manage.py compilejsmessages
args:
chdir: /var/www/note_kfet
become_user: www-data
- name: Install initial fixtures
command: /var/www/note_kfet/env/bin/python manage.py loaddata initial
args:
chdir: /var/www/note_kfet
become_user: postgres
- name: Collect static files
command: /var/www/note_kfet/env/bin/python manage.py collectstatic --noinput
args:
chdir: /var/www/note_kfet
become_user: www-data

View File

@ -30,7 +30,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
headers: {"X-CSRFTOKEN": CSRF_TOKEN}
})
.done(function() {
addMsg('Invité supprimé','success');
addMsg('{% trans "Guest deleted" %}', 'success');
$("#guests_table").load(location.pathname + " #guests_table");
})
.fail(function(xhr, textStatus, error) {

View File

@ -86,10 +86,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
}).done(function () {
if (target.hasClass("table-info"))
addMsg(
"Entrée effectuée, mais attention : la personne n'est plus adhérente Kfet.",
"{% trans "Entry done, but caution: the user is not a Kfet member." %}",
"warning", 10000);
else
addMsg("Entrée effectuée !", "success", 4000);
addMsg("Entry made!", "success", 4000);
reloadTable(true);
}).fail(function (xhr) {
errMsg(xhr.responseJSON, 4000);
@ -121,10 +121,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
}).done(function () {
if (target.hasClass("table-info"))
addMsg(
"Entrée effectuée, mais attention : la personne n'est plus adhérente Kfet.",
"{% trans "Entry done, but caution: the user is not a Kfet member." %}",
"warning", 10000);
else
addMsg("Entrée effectuée !", "success", 4000);
addMsg("{% trans "Entry done!" %}", "success", 4000);
reloadTable(true);
}).fail(function (xhr) {
errMsg(xhr.responseJSON, 4000);

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

@ -14,7 +14,7 @@ function create_alias (e) {
}).done(function () {
// Reload table
$('#alias_table').load(location.pathname + ' #alias_table')
addMsg('Alias ajouté', 'success')
addMsg(gettext('Alias successfully added'), 'success')
}).fail(function (xhr, _textStatus, _error) {
errMsg(xhr.responseJSON)
})
@ -22,7 +22,7 @@ function create_alias (e) {
/**
* On click of "delete", delete the alias
* @param Integer button_id Alias id to remove
* @param button_id:Integer Alias id to remove
*/
function delete_button (button_id) {
$.ajax({
@ -30,7 +30,7 @@ function delete_button (button_id) {
method: 'DELETE',
headers: { 'X-CSRFTOKEN': CSRF_TOKEN }
}).done(function () {
addMsg('Alias supprimé', 'success')
addMsg(gettext('Alias successfully deleted'), 'success')
$('#alias_table').load(location.pathname + ' #alias_table')
}).fail(function (xhr, _textStatus, _error) {
errMsg(xhr.responseJSON)

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,6 +25,7 @@
</a>
</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>
@ -45,6 +46,7 @@
<dt class="col-xl-6">{% trans 'paid'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.paid|yesno }}</dd>
{% endif %}
{% endif %}
</dl>
{% if user_object.pk == user.pk %}

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,6 +70,7 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
form.fields['email'].required = True
form.fields['email'].help_text = _("This address must be valid.")
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:
@ -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

@ -222,17 +222,14 @@ function consume (source, source_alias, dest, quantity, amount, reason, type, ca
if (!isNaN(source.balance)) {
const newBalance = source.balance - quantity * amount
if (newBalance <= -5000) {
addMsg('Attention, La transaction depuis la note ' + source_alias + ' a été réalisée avec ' +
'succès, mais la note émettrice ' + source_alias + ' est en négatif sévère.',
'danger', 30000)
addMsg(interpolate(gettext('Warning, the transaction from the note %s succeed, ' +
'but the emitter note %s is very negative.', [source_alias, source_alias])), 'danger', 30000)
} else if (newBalance < 0) {
addMsg('Attention, La transaction depuis la note ' + source_alias + ' a été réalisée avec ' +
'succès, mais la note émettrice ' + source_alias + ' est en négatif.',
'warning', 30000)
addMsg(interpolate(gettext('Warning, the transaction from the note %s succeed, ' +
'but the emitter note %s is negative.', [source_alias, source_alias])), 'warning', 30000)
}
if (source.membership && source.membership.date_end < new Date().toISOString()) {
addMsg('Attention : la note émettrice ' + source.name + " n'est plus adhérente.",
'danger', 30000)
addMsg(interpolate(gettext('Warning, the emitter note %s is no more a BDE member.', [source_alias])), 'danger', 30000)
}
}
reset()
@ -253,7 +250,7 @@ function consume (source, source_alias, dest, quantity, amount, reason, type, ca
template: template
}).done(function () {
reset()
addMsg(gettext("La transaction n'a pas pu être validée pour cause de solde insuffisant."), 'danger', 10000)
addMsg(gettext("The transaction couldn't be validated because of insufficient balance."), 'danger', 10000)
}).fail(function () {
reset()
errMsg(e.responseJSON)

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) {
@ -235,20 +239,20 @@ $('#btn_transfer').click(function () {
if (!amount_field.val() || isNaN(amount_field.val()) || amount_field.val() <= 0) {
amount_field.addClass('is-invalid')
$('#amount-required').html('<strong>Ce champ est requis et doit comporter un nombre décimal strictement positif.</strong>')
$('#amount-required').html('<strong>' + gettext('This field is required and must contain a decimal positive number.') + '</strong>')
error = true
}
const amount = Math.floor(100 * amount_field.val())
if (amount > 2147483647) {
amount_field.addClass('is-invalid')
$('#amount-required').html('<strong>Le montant ne doit pas excéder 21474836.47 €.</strong>')
$('#amount-required').html('<strong>' + gettext('The amount must stay under 21,474,836.47 €.') + '</strong>')
error = true
}
if (!reason_field.val()) {
if (!reason_field.val() && $('#type_transfer').is(':checked')) {
reason_field.addClass('is-invalid')
$('#reason-required').html('<strong>Ce champ est requis.</strong>')
$('#reason-required').html('<strong>' + gettext('This field is required.') + '</strong>')
error = true
}
@ -274,9 +278,8 @@ $('#btn_transfer').click(function () {
[...sources_notes_display].forEach(function (source) {
[...dests_notes_display].forEach(function (dest) {
if (source.note.id === dest.note.id) {
addMsg('Attention : la transaction de ' + pretty_money(amount) + ' de la note ' + source.name +
' vers la note ' + dest.name + " n'a pas été faite car il s'agit de la même note au départ" +
" et à l'arrivée.", 'warning', 10000)
addMsg(interpolate(gettext('Warning: the transaction of %s from %s to %s was not made because ' +
'it is the same source and destination note.'), [pretty_money(amount), source.name, dest.name]), 'warning', 10000)
LOCK = false
return
}
@ -296,43 +299,35 @@ $('#btn_transfer').click(function () {
destination_alias: dest.name
}).done(function () {
if (source.note.membership && source.note.membership.date_end < new Date().toISOString()) {
addMsg('Attention : la note émettrice ' + source.name + " n'est plus adhérente.",
'danger', 30000)
addMsg(interpolate(gettext('Warning, the emitter note %s is no more a BDE member.'), [source.name]), 'danger', 30000)
}
if (dest.note.membership && dest.note.membership.date_end < new Date().toISOString()) {
addMsg('Attention : la note destination ' + dest.name + " n'est plus adhérente.",
'danger', 30000)
addMsg(interpolate(gettext('Warning, the destination note %s is no more a BDE member.'), [source.name]), 'danger', 30000)
}
if (!isNaN(source.note.balance)) {
const newBalance = source.note.balance - source.quantity * dest.quantity * amount
if (newBalance <= -5000) {
addMsg('Le transfert de ' +
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' +
source.name + ' vers la note ' + dest.name + ' a été fait avec succès, ' +
'mais la note émettrice est en négatif sévère.', 'danger', 10000)
addMsg(interpolate(gettext('Warning, the transaction of %s from the note %s to the note %s succeed, but the emitter note %s is very negative.'),
[pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, source.name]), 'danger', 10000)
reset()
return
} else if (newBalance < 0) {
addMsg('Le transfert de ' +
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' +
source.name + ' vers la note ' + dest.name + ' a été fait avec succès, ' +
'mais la note émettrice est en négatif.', 'warning', 10000)
addMsg(interpolate(gettext('Warning, the transaction of %s from the note %s to the note %s succeed, but the emitter note %s is negative.'),
[pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, source.name]), 'danger', 10000)
reset()
return
}
}
addMsg('Le transfert de ' +
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
' vers la note ' + dest.name + ' a été fait avec succès !', 'success', 10000)
addMsg(interpolate(gettext('Transfer of %s from %s to %s succeed!'),
[pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name]), 'success', 10000)
reset()
}).fail(function (err) { // do it again but valid = false
const errObj = JSON.parse(err.responseText)
if (errObj.non_field_errors) {
addMsg('Le transfert de ' +
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
' vers la note ' + dest.name + ' a échoué : ' + errObj.non_field_errors, 'danger')
addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'),
[pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, errObj.non_field_errors]), 'danger')
LOCK = false
return
}
@ -352,17 +347,15 @@ $('#btn_transfer').click(function () {
destination: dest.note.id,
destination_alias: dest.name
}).done(function () {
addMsg('Le transfert de ' +
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
' vers la note ' + dest.name + ' a échoué : Solde insuffisant', 'danger', 10000)
addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'),
[pretty_money(source.quantity * dest.quantity * amount), source.name, + dest.name, gettext('insufficient funds')]), 'danger', 10000)
reset()
}).fail(function (err) {
const errObj = JSON.parse(err.responseText)
let error = errObj.detail ? errObj.detail : errObj.non_field_errors
if (!error) { error = err.responseText }
addMsg('Le transfert de ' +
pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
' vers la note ' + dest.name + ' a échoué : ' + error, 'danger')
addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'),
[pretty_money(source.quantity * dest.quantity * amount), source.name, + dest.name, error]), 'danger')
LOCK = false
})
})
@ -388,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/',
@ -408,14 +401,14 @@ $('#btn_transfer').click(function () {
first_name: $('#first_name').val(),
bank: $('#bank').val()
}).done(function () {
addMsg('Le crédit/retrait a bien été effectué !', 'success', 10000)
if (user_note.membership && user_note.membership.date_end < new Date().toISOString()) { addMsg('Attention : la note ' + alias + " n'est plus adhérente.", 'danger', 10000) }
addMsg(gettext('Credit/debit succeed!'), 'success', 10000)
if (user_note.membership && user_note.membership.date_end < new Date().toISOString()) { addMsg(gettext('Warning, the emitter note %s is no more a BDE member.'), 'danger', 10000) }
reset()
}).fail(function (err) {
const errObj = JSON.parse(err.responseText)
let error = errObj.detail ? errObj.detail : errObj.non_field_errors
if (!error) { error = err.responseText }
addMsg('Le crédit/retrait a échoué : ' + error, 'danger', 10000)
addMsg(interpolate(gettext('Credit/debit failed: %s'), [error]), 'danger', 10000)
LOCK = false
})
}

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()

@ -1 +1 @@
Subproject commit 7e27c3b71b04af0867d5fbe4916e2d1278637599
Subproject commit dbe7bf65917df40b0ce476f357d04726e20b406f

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

@ -14,6 +14,7 @@ fi
# Set up Django project
python3 manage.py collectstatic --noinput
python3 manage.py compilemessages
python3 manage.py compilejsmessages
python3 manage.py migrate
if [ "$1" ]; then

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-09-19 22:00+0200\n"
"POT-Creation-Date: 2020-11-15 23:26+0100\n"
"PO-Revision-Date: 2020-09-13 12:39+0200\n"
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
"Language-Team: \n"
@ -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"
@ -279,22 +279,28 @@ msgstr "Kontostand"
msgid "Guests list"
msgstr "Gastliste"
#: apps/activity/templates/activity/activity_detail.html:33
#, fuzzy
#| msgid "Guests list"
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:70
#: note_kfet/templates/base.html:73
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"
@ -308,6 +314,17 @@ msgstr "Eintritte"
msgid "Return to activity page"
msgstr "Zurück zur Veranstaltungseite"
#: apps/activity/templates/activity/activity_entry.html:89
#: apps/activity/templates/activity/activity_entry.html:124
msgid "Entry done, but caution: the user is not a Kfet member."
msgstr ""
#: apps/activity/templates/activity/activity_entry.html:127
#, fuzzy
#| msgid "Entry page"
msgid "Entry done!"
msgstr "Eintrittseite"
#: apps/activity/templates/activity/activity_form.html:16
#: apps/member/templates/member/add_members.html:46
#: apps/member/templates/member/club_form.html:16
@ -374,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:88
#: 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 \"{}\""
@ -422,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"
@ -443,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"
@ -524,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"
@ -793,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"
@ -814,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"
@ -827,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"
@ -946,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"
@ -1011,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"
@ -1090,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."
@ -1130,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"
@ -1167,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."
@ -1276,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."
@ -1395,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"
@ -1430,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"
@ -1620,7 +1637,7 @@ msgstr "Tatsen finden"
msgid "Update button"
msgstr "Tatse bearbeiten"
#: apps/note/views.py:151 note_kfet/templates/base.html:64
#: apps/note/views.py:151 note_kfet/templates/base.html:67
msgid "Consumptions"
msgstr "Verbräuche"
@ -1632,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."
@ -1686,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"
@ -1798,7 +1815,7 @@ msgstr ""
"diesen Parametern zu erstellen. Bitte korrigieren Sie Ihre Daten und "
"versuchen Sie es erneut."
#: apps/permission/views.py:110 note_kfet/templates/base.html:106
#: apps/permission/views.py:110 note_kfet/templates/base.html:109
msgid "Rights"
msgstr "Rechten"
@ -1815,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."
@ -1827,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"
@ -1883,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"
@ -1939,54 +1977,54 @@ 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"
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:94
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:97
msgid "Treasury"
msgstr "Quaestor"
@ -2128,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."
@ -2136,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}"
@ -2388,7 +2426,7 @@ msgstr "Krediten von der Société générale handeln"
#: apps/wei/apps.py:10 apps/wei/models.py:49 apps/wei/models.py:50
#: apps/wei/models.py:61 apps/wei/models.py:167
#: note_kfet/templates/base.html:100
#: note_kfet/templates/base.html:103
msgid "WEI"
msgstr "WEI"
@ -2924,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"
@ -3000,34 +3038,34 @@ msgstr "Reset"
msgid "The ENS Paris-Saclay BDE note."
msgstr "Die BDE ENS-Paris-Saclay Note."
#: note_kfet/templates/base.html:76
#: note_kfet/templates/base.html:79
msgid "Users"
msgstr "Users"
#: note_kfet/templates/base.html:82
#: note_kfet/templates/base.html:85
msgid "Clubs"
msgstr "Clubs"
#: note_kfet/templates/base.html:111
#: note_kfet/templates/base.html:114
msgid "Admin"
msgstr "Admin"
#: note_kfet/templates/base.html:125
#: note_kfet/templates/base.html:128
msgid "My account"
msgstr "Mein Konto"
#: note_kfet/templates/base.html:128
#: note_kfet/templates/base.html:131
msgid "Log out"
msgstr "Abmelden"
#: note_kfet/templates/base.html:136
#: 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"
#: note_kfet/templates/base.html:143
#: note_kfet/templates/base.html:146
#: note_kfet/templates/registration/login.html:6
#: note_kfet/templates/registration/login.html:15
#: note_kfet/templates/registration/login.html:38
@ -3035,7 +3073,17 @@ msgstr "Registrieren"
msgid "Log in"
msgstr "Anmelden"
#: note_kfet/templates/base.html:155
#: 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."
@ -3043,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:172
#: 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"
@ -3055,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 ""
@ -3183,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

@ -0,0 +1,119 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-11-15 23:21+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: apps/member/static/member/js/alias.js:17
msgid "Alias successfully added"
msgstr ""
#: apps/member/static/member/js/alias.js:33
msgid "Alias successfully deleted"
msgstr ""
#: apps/note/static/note/js/consos.js:225
#, javascript-format
msgid ""
"Warning, the transaction from the note %s succeed, but the emitter note %s "
"is very negative."
msgstr ""
#: apps/note/static/note/js/consos.js:228
#, javascript-format
msgid ""
"Warning, the transaction from the note %s succeed, but the emitter note %s "
"is negative."
msgstr ""
#: apps/note/static/note/js/consos.js:232
#: apps/note/static/note/js/transfer.js:298
#: apps/note/static/note/js/transfer.js:401
#, javascript-format
msgid "Warning, the emitter note %s is no more a BDE member."
msgstr ""
#: apps/note/static/note/js/consos.js:253
msgid "The transaction couldn't be validated because of insufficient balance."
msgstr ""
#: apps/note/static/note/js/transfer.js:238
msgid "This field is required and must contain a decimal positive number."
msgstr ""
#: apps/note/static/note/js/transfer.js:245
msgid "The amount must stay under 21,474,836.47 €."
msgstr ""
#: apps/note/static/note/js/transfer.js:251
msgid "This field is required."
msgstr ""
#: apps/note/static/note/js/transfer.js:277
#, javascript-format
msgid ""
"Warning: the transaction of %s from %s to %s was not made because it is the "
"same source and destination note."
msgstr ""
#: apps/note/static/note/js/transfer.js:301
#, javascript-format
msgid "Warning, the destination note %s is no more a BDE member."
msgstr ""
#: apps/note/static/note/js/transfer.js:307
#, javascript-format
msgid ""
"Warning, the transaction of %s from the note %s to the note %s succeed, but "
"the emitter note %s is very negative."
msgstr ""
#: apps/note/static/note/js/transfer.js:312
#, javascript-format
msgid ""
"Warning, the transaction of %s from the note %s to the note %s succeed, but "
"the emitter note %s is negative."
msgstr ""
#: apps/note/static/note/js/transfer.js:318
#, javascript-format
msgid "Transfer of %s from %s to %s succeed!"
msgstr ""
#: apps/note/static/note/js/transfer.js:325
#: apps/note/static/note/js/transfer.js:346
#: apps/note/static/note/js/transfer.js:353
#, javascript-format
msgid "Transfer of %s from %s to %s failed: %s"
msgstr ""
#: apps/note/static/note/js/transfer.js:347
msgid "insufficient funds"
msgstr ""
#: apps/note/static/note/js/transfer.js:400
msgid "Credit/debit succeed!"
msgstr ""
#: apps/note/static/note/js/transfer.js:407
#, javascript-format
msgid "Credit/debit failed: %s"
msgstr ""
#: note_kfet/static/js/base.js:366
msgid "An error occured while (in)validating this transaction:"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-09-19 22:00+0200\n"
"POT-Creation-Date: 2020-11-15 23:26+0100\n"
"PO-Revision-Date: 2020-09-19 14:56+0200\n"
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
"Language-Team: \n"
@ -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"
@ -278,22 +278,28 @@ msgstr "Saldo de la cuenta"
msgid "Guests list"
msgstr "Lista de los invitados"
#: apps/activity/templates/activity/activity_detail.html:33
#, fuzzy
#| msgid "Guests list"
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:70
#: note_kfet/templates/base.html:73
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"
@ -307,6 +313,17 @@ msgstr "Entradas"
msgid "Return to activity page"
msgstr "Regresar a la página de la actividad"
#: apps/activity/templates/activity/activity_entry.html:89
#: apps/activity/templates/activity/activity_entry.html:124
msgid "Entry done, but caution: the user is not a Kfet member."
msgstr ""
#: apps/activity/templates/activity/activity_entry.html:127
#, fuzzy
#| msgid "Entry page"
msgid "Entry done!"
msgstr "Página de las entradas"
#: apps/activity/templates/activity/activity_form.html:16
#: apps/member/templates/member/add_members.html:46
#: apps/member/templates/member/club_form.html:16
@ -373,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:88
#: 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 \"{}\""
@ -423,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"
@ -444,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"
@ -525,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"
@ -793,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"
@ -814,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"
@ -827,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"
@ -943,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"
@ -1008,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"
@ -1087,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."
@ -1127,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"
@ -1164,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."
@ -1274,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."
@ -1393,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"
@ -1427,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"
@ -1617,7 +1634,7 @@ msgstr "Buscar un botón"
msgid "Update button"
msgstr "Modificar el botón"
#: apps/note/views.py:151 note_kfet/templates/base.html:64
#: apps/note/views.py:151 note_kfet/templates/base.html:67
msgid "Consumptions"
msgstr "Consumiciones"
@ -1629,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."
@ -1683,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"
@ -1793,7 +1810,7 @@ msgid ""
"with these parameters. Please correct your data and retry."
msgstr ""
#: apps/permission/views.py:110 note_kfet/templates/base.html:106
#: apps/permission/views.py:110 note_kfet/templates/base.html:109
msgid "Rights"
msgstr "Permisos"
@ -1810,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."
@ -1821,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"
@ -1877,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"
@ -1933,54 +1971,54 @@ 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"
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:94
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:97
msgid "Treasury"
msgstr "Tesorería"
@ -2122,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."
@ -2131,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}"
@ -2377,7 +2415,7 @@ msgstr "Gestionar los créditos de la Société Générale"
#: apps/wei/apps.py:10 apps/wei/models.py:49 apps/wei/models.py:50
#: apps/wei/models.py:61 apps/wei/models.py:167
#: note_kfet/templates/base.html:100
#: note_kfet/templates/base.html:103
msgid "WEI"
msgstr "WEI"
@ -2908,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"
@ -2982,34 +3020,34 @@ msgstr ""
msgid "The ENS Paris-Saclay BDE note."
msgstr "La note del BDE de la ENS Paris-Saclay."
#: note_kfet/templates/base.html:76
#: note_kfet/templates/base.html:79
msgid "Users"
msgstr "Usuarios"
#: note_kfet/templates/base.html:82
#: note_kfet/templates/base.html:85
msgid "Clubs"
msgstr "Clubs"
#: note_kfet/templates/base.html:111
#: note_kfet/templates/base.html:114
msgid "Admin"
msgstr ""
#: note_kfet/templates/base.html:125
#: note_kfet/templates/base.html:128
msgid "My account"
msgstr "Mi cuenta"
#: note_kfet/templates/base.html:128
#: note_kfet/templates/base.html:131
msgid "Log out"
msgstr "Desconectarse"
#: note_kfet/templates/base.html:136
#: 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"
#: note_kfet/templates/base.html:143
#: note_kfet/templates/base.html:146
#: note_kfet/templates/registration/login.html:6
#: note_kfet/templates/registration/login.html:15
#: note_kfet/templates/registration/login.html:38
@ -3017,7 +3055,17 @@ msgstr "Registrar"
msgid "Log in"
msgstr "Conectarse"
#: note_kfet/templates/base.html:155
#: 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."
@ -3025,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:172
#: 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"
@ -3037,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."
@ -3159,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

@ -0,0 +1,119 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-11-15 23:21+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: apps/member/static/member/js/alias.js:17
msgid "Alias successfully added"
msgstr ""
#: apps/member/static/member/js/alias.js:33
msgid "Alias successfully deleted"
msgstr ""
#: apps/note/static/note/js/consos.js:225
#, javascript-format
msgid ""
"Warning, the transaction from the note %s succeed, but the emitter note %s "
"is very negative."
msgstr ""
#: apps/note/static/note/js/consos.js:228
#, javascript-format
msgid ""
"Warning, the transaction from the note %s succeed, but the emitter note %s "
"is negative."
msgstr ""
#: apps/note/static/note/js/consos.js:232
#: apps/note/static/note/js/transfer.js:298
#: apps/note/static/note/js/transfer.js:401
#, javascript-format
msgid "Warning, the emitter note %s is no more a BDE member."
msgstr ""
#: apps/note/static/note/js/consos.js:253
msgid "The transaction couldn't be validated because of insufficient balance."
msgstr ""
#: apps/note/static/note/js/transfer.js:238
msgid "This field is required and must contain a decimal positive number."
msgstr ""
#: apps/note/static/note/js/transfer.js:245
msgid "The amount must stay under 21,474,836.47 €."
msgstr ""
#: apps/note/static/note/js/transfer.js:251
msgid "This field is required."
msgstr ""
#: apps/note/static/note/js/transfer.js:277
#, javascript-format
msgid ""
"Warning: the transaction of %s from %s to %s was not made because it is the "
"same source and destination note."
msgstr ""
#: apps/note/static/note/js/transfer.js:301
#, javascript-format
msgid "Warning, the destination note %s is no more a BDE member."
msgstr ""
#: apps/note/static/note/js/transfer.js:307
#, javascript-format
msgid ""
"Warning, the transaction of %s from the note %s to the note %s succeed, but "
"the emitter note %s is very negative."
msgstr ""
#: apps/note/static/note/js/transfer.js:312
#, javascript-format
msgid ""
"Warning, the transaction of %s from the note %s to the note %s succeed, but "
"the emitter note %s is negative."
msgstr ""
#: apps/note/static/note/js/transfer.js:318
#, javascript-format
msgid "Transfer of %s from %s to %s succeed!"
msgstr ""
#: apps/note/static/note/js/transfer.js:325
#: apps/note/static/note/js/transfer.js:346
#: apps/note/static/note/js/transfer.js:353
#, javascript-format
msgid "Transfer of %s from %s to %s failed: %s"
msgstr ""
#: apps/note/static/note/js/transfer.js:347
msgid "insufficient funds"
msgstr ""
#: apps/note/static/note/js/transfer.js:400
msgid "Credit/debit succeed!"
msgstr ""
#: apps/note/static/note/js/transfer.js:407
#, javascript-format
msgid "Credit/debit failed: %s"
msgstr ""
#: note_kfet/static/js/base.js:366
msgid "An error occured while (in)validating this transaction:"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-09-19 22:00+0200\n"
"POT-Creation-Date: 2020-11-15 23:26+0100\n"
"PO-Revision-Date: 2020-09-13 12:36+0200\n"
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
"Language-Team: \n"
@ -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"
@ -279,22 +279,26 @@ msgstr "Solde du compte"
msgid "Guests list"
msgstr "Liste des invités"
#: apps/activity/templates/activity/activity_detail.html:33
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:70
#: note_kfet/templates/base.html:73
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"
@ -308,6 +312,16 @@ msgstr "Entrées"
msgid "Return to activity page"
msgstr "Retour à la page de l'activité"
#: apps/activity/templates/activity/activity_entry.html:89
#: apps/activity/templates/activity/activity_entry.html:124
msgid "Entry done, but caution: the user is not a Kfet member."
msgstr ""
"Entrée effectuée, mais attention : la personne n'est pas un adhérent Kfet."
#: apps/activity/templates/activity/activity_entry.html:127
msgid "Entry done!"
msgstr "Entrée effectuée !"
#: apps/activity/templates/activity/activity_form.html:16
#: apps/member/templates/member/add_members.html:46
#: apps/member/templates/member/club_form.html:16
@ -374,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:88
#: 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é « {} »"
@ -424,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"
@ -445,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"
@ -526,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"
@ -795,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"
@ -816,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"
@ -829,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"
@ -945,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"
@ -1010,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"
@ -1089,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."
@ -1129,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"
@ -1166,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."
@ -1276,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."
@ -1396,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."
@ -1404,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"
@ -1432,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"
@ -1622,7 +1636,7 @@ msgstr "Chercher un bouton"
msgid "Update button"
msgstr "Modifier le bouton"
#: apps/note/views.py:151 note_kfet/templates/base.html:64
#: apps/note/views.py:151 note_kfet/templates/base.html:67
msgid "Consumptions"
msgstr "Consommations"
@ -1634,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."
@ -1688,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"
@ -1801,7 +1815,7 @@ msgstr ""
"Vous n'avez pas la permission d'ajouter une instance du modèle « {model} » "
"avec ces paramètres. Merci de les corriger et de réessayer."
#: apps/permission/views.py:110 note_kfet/templates/base.html:106
#: apps/permission/views.py:110 note_kfet/templates/base.html:109
msgid "Rights"
msgstr "Droits"
@ -1818,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."
@ -1830,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"
@ -1886,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"
@ -1940,54 +1975,54 @@ 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"
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:94
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:97
msgid "Treasury"
msgstr "Trésorerie"
@ -2129,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."
@ -2137,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}"
@ -2387,7 +2422,7 @@ msgstr "Gérer les crédits de la Société générale"
#: apps/wei/apps.py:10 apps/wei/models.py:49 apps/wei/models.py:50
#: apps/wei/models.py:61 apps/wei/models.py:167
#: note_kfet/templates/base.html:100
#: note_kfet/templates/base.html:103
msgid "WEI"
msgstr "WEI"
@ -2922,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"
@ -2999,34 +3034,34 @@ msgstr "Réinitialiser"
msgid "The ENS Paris-Saclay BDE note."
msgstr "La note du BDE de l'ENS Paris-Saclay."
#: note_kfet/templates/base.html:76
#: note_kfet/templates/base.html:79
msgid "Users"
msgstr "Utilisateurs"
#: note_kfet/templates/base.html:82
#: note_kfet/templates/base.html:85
msgid "Clubs"
msgstr "Clubs"
#: note_kfet/templates/base.html:111
#: note_kfet/templates/base.html:114
msgid "Admin"
msgstr "Admin"
#: note_kfet/templates/base.html:125
#: note_kfet/templates/base.html:128
msgid "My account"
msgstr "Mon compte"
#: note_kfet/templates/base.html:128
#: note_kfet/templates/base.html:131
msgid "Log out"
msgstr "Se déconnecter"
#: note_kfet/templates/base.html:136
#: 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"
#: note_kfet/templates/base.html:143
#: note_kfet/templates/base.html:146
#: note_kfet/templates/registration/login.html:6
#: note_kfet/templates/registration/login.html:15
#: note_kfet/templates/registration/login.html:38
@ -3034,7 +3069,21 @@ msgstr "Inscription"
msgid "Log in"
msgstr "Se connecter"
#: note_kfet/templates/base.html:155
#: 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."
@ -3042,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:172
#: 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"
@ -3054,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."
@ -3181,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

@ -0,0 +1,134 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-11-15 23:21+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: apps/member/static/member/js/alias.js:17
msgid "Alias successfully added"
msgstr "Alias ajouté avec succès"
#: apps/member/static/member/js/alias.js:33
msgid "Alias successfully deleted"
msgstr "Alias supprimé avec succès"
#: apps/note/static/note/js/consos.js:225
#, javascript-format
msgid ""
"Warning, the transaction from the note %s succeed, but the emitter note %s "
"is very negative."
msgstr ""
"Attention, La transaction depuis la note %s a été réalisée avec succès, mais "
"la note émettrice %s est en négatif sévère."
#: apps/note/static/note/js/consos.js:228
#, javascript-format
msgid ""
"Warning, the transaction from the note %s succeed, but the emitter note %s "
"is negative."
msgstr ""
"Attention, La transaction depuis la note %s a été réalisée avec succès, mais "
"la note émettrice %s est en négatif."
#: apps/note/static/note/js/consos.js:232
#: apps/note/static/note/js/transfer.js:298
#: apps/note/static/note/js/transfer.js:401
#, javascript-format
msgid "Warning, the emitter note %s is no more a BDE member."
msgstr "Attention, la note émettrice %s n'est plus adhérente."
#: apps/note/static/note/js/consos.js:253
msgid "The transaction couldn't be validated because of insufficient balance."
msgstr ""
"La transaction n'a pas pu être validée pour cause de solde insuffisant."
#: apps/note/static/note/js/transfer.js:238
msgid "This field is required and must contain a decimal positive number."
msgstr ""
"Ce champ est requis et doit comporter un nombre décimal strictement positif."
#: apps/note/static/note/js/transfer.js:245
msgid "The amount must stay under 21,474,836.47 €."
msgstr "Le montant ne doit pas excéder 21 474 836.47 €."
#: apps/note/static/note/js/transfer.js:251
msgid "This field is required."
msgstr "Ce champ est requis."
#: apps/note/static/note/js/transfer.js:277
#, javascript-format
msgid ""
"Warning: the transaction of %s from %s to %s was not made because it is the "
"same source and destination note."
msgstr ""
"Attention : la transaction de %s de la note %s vers la note %s n'a pas été "
"faite car il s'agit de la même note au départ et à l'arrivée."
#: apps/note/static/note/js/transfer.js:301
#, javascript-format
msgid "Warning, the destination note %s is no more a BDE member."
msgstr "Attention, la note de destination %s n'est plus adhérente."
#: apps/note/static/note/js/transfer.js:307
#, javascript-format
msgid ""
"Warning, the transaction of %s from the note %s to the note %s succeed, but "
"the emitter note %s is very negative."
msgstr ""
"Attention, La transaction de %s depuis la note %s vers la note %s a été "
"réalisée avec succès, mais la note émettrice %s est en négatif sévère."
#: apps/note/static/note/js/transfer.js:312
#, javascript-format
msgid ""
"Warning, the transaction of %s from the note %s to the note %s succeed, but "
"the emitter note %s is negative."
msgstr ""
"Attention, La transaction de %s depuis la note %s vers la note %s a été "
"réalisée avec succès, mais la note émettrice %s est en négatif."
#: apps/note/static/note/js/transfer.js:318
#, javascript-format
msgid "Transfer of %s from %s to %s succeed!"
msgstr ""
"Le transfert de %s de la note %s vers la note %s a été fait avec succès !"
#: apps/note/static/note/js/transfer.js:325
#: apps/note/static/note/js/transfer.js:346
#: apps/note/static/note/js/transfer.js:353
#, javascript-format
msgid "Transfer of %s from %s to %s failed: %s"
msgstr "Le transfert de %s de la note %s vers la note %s a échoué : %s"
#: apps/note/static/note/js/transfer.js:347
msgid "insufficient funds"
msgstr "solde insuffisant"
#: apps/note/static/note/js/transfer.js:400
msgid "Credit/debit succeed!"
msgstr "Le crédit/retrait a bien été effectué !"
#: apps/note/static/note/js/transfer.js:407
#, javascript-format
msgid "Credit/debit failed: %s"
msgstr "Le crédit/retrait a échoué : %s"
#: note_kfet/static/js/base.js:366
msgid "An error occured while (in)validating this transaction:"
msgstr ""
"Une erreur est survenue lors de la validation/dévalidation de cette "
"transaction :"

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

@ -363,8 +363,7 @@ function de_validate (id, validated, resourcetype) {
const errObj = JSON.parse(err.responseText)
let error = errObj.detail ? errObj.detail : errObj.non_field_errors
if (!error) { error = err.responseText }
addMsg('Une erreur est survenue lors de la validation/dévalidation ' +
'de cette transaction : ' + error, 'danger')
addMsg(gettext('An error occured while (in)validating this transaction:') + ' ' + error, 'danger')
refreshBalance()
// error if this method doesn't exist. Please define it.

View File

@ -0,0 +1,134 @@
/*
* You should never see this file.
* It is here only for compatibility reasons in case of the command `compilejsmessages` was never executed.
* Please execute this command to generate translation strings.
*/
(function(globals) {
var django = globals.django || (globals.django = {});
django.pluralidx = function(n) {
var v=(n != 1);
if (typeof(v) == 'boolean') {
return v ? 1 : 0;
} else {
return v;
}
};
/* gettext library */
django.catalog = django.catalog || {};
if (!django.jsi18n_initialized) {
django.gettext = function(msgid) {
var value = django.catalog[msgid];
if (typeof(value) == 'undefined') {
return msgid;
} else {
return (typeof(value) == 'string') ? value : value[0];
}
};
django.ngettext = function(singular, plural, count) {
var value = django.catalog[singular];
if (typeof(value) == 'undefined') {
return (count == 1) ? singular : plural;
} else {
return value.constructor === Array ? value[django.pluralidx(count)] : value;
}
};
django.gettext_noop = function(msgid) { return msgid; };
django.pgettext = function(context, msgid) {
var value = django.gettext(context + '\x04' + msgid);
if (value.indexOf('\x04') != -1) {
value = msgid;
}
return value;
};
django.npgettext = function(context, singular, plural, count) {
var value = django.ngettext(context + '\x04' + singular, context + '\x04' + plural, count);
if (value.indexOf('\x04') != -1) {
value = django.ngettext(singular, plural, count);
}
return value;
};
django.interpolate = function(fmt, obj, named) {
if (named) {
return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
} else {
return fmt.replace(/%s/g, function(match){return String(obj.shift())});
}
};
/* formatting library */
django.formats = {
"DATETIME_FORMAT": "j \\d\\e F \\d\\e Y \\a \\l\\a\\s H:i",
"DATETIME_INPUT_FORMATS": [
"%d/%m/%Y %H:%M:%S",
"%d/%m/%Y %H:%M:%S.%f",
"%d/%m/%Y %H:%M",
"%d/%m/%y %H:%M:%S",
"%d/%m/%y %H:%M:%S.%f",
"%d/%m/%y %H:%M",
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%d %H:%M:%S.%f",
"%Y-%m-%d %H:%M",
"%Y-%m-%d"
],
"DATE_FORMAT": "j \\d\\e F \\d\\e Y",
"DATE_INPUT_FORMATS": [
"%d/%m/%Y",
"%d/%m/%y",
"%Y-%m-%d"
],
"DECIMAL_SEPARATOR": ",",
"FIRST_DAY_OF_WEEK": 1,
"MONTH_DAY_FORMAT": "j \\d\\e F",
"NUMBER_GROUPING": 3,
"SHORT_DATETIME_FORMAT": "d/m/Y H:i",
"SHORT_DATE_FORMAT": "d/m/Y",
"THOUSAND_SEPARATOR": ".",
"TIME_FORMAT": "H:i",
"TIME_INPUT_FORMATS": [
"%H:%M:%S",
"%H:%M:%S.%f",
"%H:%M"
],
"YEAR_MONTH_FORMAT": "F \\d\\e Y"
};
django.get_format = function(format_type) {
var value = django.formats[format_type];
if (typeof(value) == 'undefined') {
return format_type;
} else {
return value;
}
};
/* add to global namespace */
globals.pluralidx = django.pluralidx;
globals.gettext = django.gettext;
globals.ngettext = django.ngettext;
globals.gettext_noop = django.gettext_noop;
globals.pgettext = django.pgettext;
globals.npgettext = django.npgettext;
globals.interpolate = django.interpolate;
globals.get_format = django.get_format;
django.jsi18n_initialized = true;
}
}(this));

View File

@ -0,0 +1 @@
_default.js

View File

@ -0,0 +1 @@
_default.js

View File

@ -0,0 +1 @@
_default.js

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 %}
@ -38,6 +38,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
<script src="{% static "js/base.js" %}"></script>
<script src="{% static "js/konami.js" %}"></script>
{# Translation in javascript files #}
<script src="{% static "js/jsi18n/jsi18n."|add:LANGUAGE_CODE|add:".js" %}"></script>
{# If extra ressources are needed for a form, load here #}
{% if form.media %}
{{ form.media }}
@ -64,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>
@ -150,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 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 %}
<div id="messages"></div>
{% 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,16 +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 django.views.i18n import JavaScriptCatalog
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')),
@ -35,21 +33,17 @@ urlpatterns = [
# Make coffee
path('coffee/', include('django_htcpcp_tea.urls')),
# Translate js
path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
]
# During development, serve media files
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