1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-06-27 20:22:15 +02:00

Compare commits

...

69 Commits

Author SHA1 Message Date
b5b09312b8 Merge branch 'nix-shell' into 'main'
Nix shell

See merge request bde/nk20!201
2023-08-31 16:39:16 +02:00
18a5b65a1c Merge branch 'VSS' into 'main'
anti VSS

See merge request bde/nk20!219
2023-08-31 15:58:52 +02:00
f545af4977 typo 2023-08-31 15:40:49 +02:00
103e2d0635 add GC anti-VSS 2023-08-31 15:25:44 +02:00
aedf0e87ba prez BDE can block note 2023-08-31 13:46:27 +02:00
dab45b5fd4 translation 2023-08-31 13:40:53 +02:00
b3353b563c add VSS checkbox on registration 2023-08-31 12:21:38 +02:00
6bc52be707 Merge branch 'WEI_with_questions' into 'main'
Wei with questions

See merge request bde/nk20!218
2023-08-31 12:01:39 +02:00
834d68fe35 typo 2023-08-31 11:45:17 +02:00
c6a2849d35 test 2023-08-30 16:16:29 +02:00
4ab22c92b3 After WEI registration validation, come back to unvalidate registration page 2023-08-30 09:52:17 +02:00
c328c1457c add register button at the end of WEI registration 2023-08-28 22:27:45 +02:00
96da7d01ae change on a field that everyone have (1A don't have bus) 2023-08-28 19:26:51 +02:00
d27f942339 typo 2023-08-28 10:13:28 +02:00
738d6c932d questions ! 2023-08-28 00:42:33 +02:00
1760196578 more tests 2023-08-27 23:11:40 +02:00
13b9b6edea tests 2023-08-27 18:09:46 +02:00
e06e3b2972 one question by page 2023-08-26 23:47:10 +02:00
9596aa7b8c base for questions instead of words 2023-08-26 17:52:48 +02:00
ba0d64f0d4 Merge branch 'new_default_year' into 'main'
new default year

See merge request bde/nk20!217
2023-08-23 23:53:45 +02:00
8d17801e28 new default year 2023-08-23 23:32:01 +02:00
609362c4f8 Merge branch 'update_permission' into 'main'
Update permission

See merge request bde/nk20!216
2023-08-23 22:50:24 +02:00
03d2d5f03e change -50€ to -20€ and doc 2023-08-22 21:51:02 +02:00
d2057a9f45 remove respo-info perm and change Prez BDE prem 2023-08-22 21:19:05 +02:00
b6e68eeebe Merge branch 'charliep-main-patch-47507' into 'main'
Update forms.py - Homogénéisation des cases

See merge request bde/nk20!215
2023-08-08 15:39:44 +02:00
6410542027 Update forms.py - Homogénéisation des cases 2023-08-08 15:38:29 +02:00
6b1cd3ba7a manage self aliases for BDE member instead of kfet 2023-07-24 12:42:44 +02:00
9f114b8ca2 fixtures activities 2023-07-24 12:26:34 +02:00
e0132b6dc8 migration permission 2023-07-24 12:20:16 +02:00
f1cc82fab3 Merge branch 'linters' into 'main'
Linters

See merge request bde/nk20!214
2023-07-17 09:27:22 +02:00
644cf14c4b missing brackets 2023-07-17 09:11:25 +02:00
f19a489313 linters (removing B019) 2023-07-17 08:50:10 +02:00
dedd6c69cc new commits in nk20-scripts 2023-07-17 06:58:01 +02:00
b42f5afeab Merge branch 'registration2023' into 'main'
Registration2023

See merge request bde/nk20!213
2023-07-16 17:12:33 +02:00
31e67ae3f6 typo 2023-07-09 16:06:30 +02:00
b08da7a727 help text on WEI emergency contact 2023-07-09 14:57:48 +02:00
451aa64f33 Unisexe clothing cut 2023-07-09 12:30:23 +02:00
3c99b0f3e9 do not change transactions date when validating/deleting credit-soge (and typo) 2023-07-09 11:23:33 +02:00
201a179947 linters 2023-07-09 10:36:36 +02:00
96784aee3b remove (comment) soge from registration 2023-07-07 21:44:18 +02:00
981c4d0300 fix update of club membership start/end date 2023-07-07 20:39:19 +02:00
11223430fd Merge branch 'WEI2023' into 'main'
Préparation WEI 2023

See merge request bde/nk20!212
2023-07-04 19:17:17 +02:00
7aeb977e72 Oubli dans le fichier test_wei_registration_.py d'un 2022 en 2023 2023-07-04 18:33:54 +02:00
52fef1df42 Préparation WEI 2023 2023-07-04 18:23:43 +02:00
16f8a60a3f possibilité de l'adhésion au BDA lors de l'inscription 2023-07-04 17:32:48 +02:00
2839d3de1e club facultatif pour un role lors du changement dans l'interface admin 2023-06-22 14:52:11 +02:00
30afa6da0a création d'une permission pour faire les crédits uniquement 2023-06-12 18:29:23 +02:00
84fc77696f see activities: BDE members instead of kfet 2023-06-05 19:04:19 +02:00
19fc620d1f see kfet members' note for respot 2023-06-05 17:26:49 +02:00
d5819ac562 Merge branch 'FAQ' into 'main'
Ajout d'un lien vers la FAQ de la note.

See merge request bde/nk20!209
2023-04-18 15:51:38 +02:00
a79df8f1f6 Merge branch 'invoice_bg_storlist' into 'main'
changement du fond des factures

See merge request bde/nk20!211
2023-04-14 19:29:26 +02:00
364b18e188 migrations 2023-04-14 16:52:46 +02:00
10a883b2e5 new treasury phone number 2023-04-14 16:00:48 +02:00
1410ab6c4f Almost on time, the SIRET number is now changed 2023-04-14 15:35:18 +02:00
623dd61be6 Remove phone number 2023-04-14 14:56:34 +02:00
48a0a87e7c changement du fond des factures 2023-04-14 00:25:26 +02:00
563f525b11 Merge branch 'cron' into 'main'
fréquence des mails de négatif aux trez : 1 mois -> 1 semaine, et les notes liées au BDE n'apparaissent plus

See merge request bde/nk20!210
2023-04-08 13:04:59 +02:00
63c1d74f1a Ignore notes containing '- BDE-' in the list of negative balances 2023-04-07 15:47:06 +02:00
c42fb380a6 frequence des mails de négatif aux trez : 1 mois -> 1 semiane 2023-04-06 09:04:27 +02:00
c636d52a73 traduction (allemand et espagnol probablement pas optimal) 2023-03-31 17:21:58 +02:00
9c9149b53a Ajout d'un lien vers la FAQ de la note. 2023-03-31 12:34:14 +02:00
6bceb394c5 prez BDE sould see invoice list 2023-03-29 20:43:54 +02:00
62cf8f9d84 forgetted coma 2023-03-28 20:41:53 +02:00
2dd1c3fb89 change mask for some perm 2023-03-20 22:35:51 +01:00
c8665c5798 change permissions for role 2023-03-20 22:21:18 +01:00
e9f1b6f52d change permanent permissions 2023-03-20 17:19:14 +01:00
1d95ae4810 sort perm by number 2023-03-20 16:16:32 +01:00
dde1baa25c typo 2022-08-21 19:50:53 +02:00
7a7ee47e0b Add two shell.nix to enable easier development on nixos. 2022-08-21 19:46:11 +02:00
49 changed files with 1713 additions and 925 deletions

1
.gitignore vendored
View File

@ -48,7 +48,6 @@ backups/
env/
venv/
db.sqlite3
shell.nix
# ansibles customs host
ansible/host_vars/*.yaml

View File

@ -6,7 +6,7 @@
"name": "Pot",
"manage_entries": true,
"can_invite": true,
"guest_entry_fee": 500
"guest_entry_fee": 1000
}
},
{
@ -28,5 +28,25 @@
"can_invite": false,
"guest_entry_fee": 0
}
},
{
"model": "activity.activitytype",
"pk": 5,
"fields": {
"name": "Soir\u00e9e avec entrées",
"manage_entries": true,
"can_invite": false,
"guest_entry_fee": 0
}
},
{
"model": "activity.activitytype",
"pk": 7,
"fields": {
"name": "Soir\u00e9e avec invitations",
"manage_entries": true,
"can_invite": true,
"guest_entry_fee": 0
}
}
]

View File

@ -1,5 +1,5 @@
from rest_framework.pagination import PageNumberPagination
class CustomPagination(PageNumberPagination):
page_size_query_param = 'page_size'

View File

@ -47,6 +47,13 @@ class ProfileForm(forms.ModelForm):
last_report = forms.DateTimeField(required=False, disabled=True, label=_("Last report date"))
VSS_charter_read = forms.BooleanField(
required=True,
label=_("Anti-VSS (<em>Violences Sexistes et Sexuelles</em>) charter read and approved"),
help_text=_("Tick after having read and accepted the anti-VSS charter \
<a href=https://perso.crans.org/club-bde/Charte-anti-VSS.pdf target=_blank> available here in pdf</a>")
)
def clean_promotion(self):
promotion = self.cleaned_data["promotion"]
if promotion > timezone.now().year:

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.28 on 2023-08-23 21:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('member', '0009_auto_20220904_2325'),
]
operations = [
migrations.AlterField(
model_name='profile',
name='promotion',
field=models.PositiveSmallIntegerField(default=2023, help_text='Year of entry to the school (None if not ENS student)', null=True, verbose_name='promotion'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.28 on 2023-08-31 09:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('member', '0010_new_default_year'),
]
operations = [
migrations.AddField(
model_name='profile',
name='VSS_charter_read',
field=models.BooleanField(default=False, verbose_name='VSS charter read'),
),
]

View File

@ -134,6 +134,11 @@ class Profile(models.Model):
default=False,
)
VSS_charter_read = models.BooleanField(
verbose_name=_("VSS charter read"),
default=False
)
@property
def ens_year(self):
"""
@ -263,7 +268,7 @@ class Club(models.Model):
today = datetime.date.today()
if (today - self.membership_start).days >= 365:
while (today - self.membership_start).days >= 365:
if self.membership_start:
self.membership_start = datetime.date(self.membership_start.year + 1,
self.membership_start.month, self.membership_start.day)

View File

@ -183,7 +183,7 @@ class TestMemberships(TestCase):
club = Club.objects.get(name="Kfet")
else:
club = Club.objects.create(
name="Second club " + ("with BDE" if bde_parent else "without BDE"),
name="Second club without BDE",
parent_club=None,
email="newclub@example.com",
require_memberships=True,
@ -335,6 +335,7 @@ class TestMemberships(TestCase):
ml_sports_registration=True,
ml_art_registration=True,
report_frequency=7,
VSS_charter_read=True
))
self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200)
self.assertTrue(User.objects.filter(username="toto changed").exists())

View File

@ -753,6 +753,10 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
club = old_membership.club
user = old_membership.user
# Update club membership date
if PermissionBackend.check_perm(self.request, "member.change_club_membership_start", club):
club.update_membership_dates()
form.instance.club = club
# Get form data

View File

@ -325,8 +325,8 @@ class SpecialTransaction(Transaction):
def clean(self):
# SpecialTransaction are only possible with NoteSpecial object
if self.is_credit() == self.is_debit():
raise(ValidationError(_("A special transaction is only possible between a"
" Note associated to a payment method and a User or a Club")))
raise ValidationError(_("A special transaction is only possible between a"
" Note associated to a payment method and a User or a Club"))
@transaction.atomic
def save(self, *args, **kwargs):

View File

@ -221,7 +221,7 @@ function consume (source, source_alias, dest, quantity, amount, reason, type, ca
.done(function () {
if (!isNaN(source.balance)) {
const newBalance = source.balance - quantity * amount
if (newBalance <= -5000) {
if (newBalance <= -2000) {
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) {

View File

@ -314,7 +314,7 @@ $('#btn_transfer').click(function () {
if (!isNaN(source.note.balance)) {
const newBalance = source.note.balance - source.quantity * dest.quantity * amount
if (newBalance <= -5000) {
if (newBalance <= -2000) {
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()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.28 on 2023-07-24 10:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('permission', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='role',
name='for_club',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='member.Club', verbose_name='for club'),
),
]

View File

@ -339,6 +339,7 @@ class Role(models.Model):
"member.Club",
verbose_name=_("for club"),
on_delete=models.PROTECT,
blank=True,
null=True,
default=None,
)

View File

@ -5,6 +5,7 @@ from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
from member.models import Club
from note.models import NoteSpecial, Alias
from note_kfet.inputs import AmountInput
@ -44,14 +45,14 @@ class SignUpForm(UserCreationForm):
fields = ('first_name', 'last_name', 'username', 'email', )
class DeclareSogeAccountOpenedForm(forms.Form):
soge_account = forms.BooleanField(
label=_("I declare that I opened or I will open soon 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 DeclareSogeAccountOpenedForm(forms.Form):
# soge_account = forms.BooleanField(
# label=_("I declare that I opened or I will open soon 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):
@ -67,11 +68,11 @@ class ValidationForm(forms.Form):
"""
Validate the inscription of the new users and pay memberships.
"""
soge = forms.BooleanField(
label=_("Inscription paid by Société Générale"),
required=False,
help_text=_("Check this case if the Société Générale paid the inscription."),
)
# soge = forms.BooleanField(
# label=_("Inscription paid by Société Générale"),
# required=False,
# help_text=_("Check this case if the Société Générale paid the inscription."),
# )
credit_type = forms.ModelChoiceField(
queryset=NoteSpecial.objects,
@ -114,3 +115,12 @@ class ValidationForm(forms.Form):
required=False,
initial=True,
)
# If the bda exists
if Club.objects.filter(name__iexact="bda").exists():
# The user can join the bda club at the inscription
join_bda = forms.BooleanField(
label=_("Join BDA Club"),
required=False,
initial=True,
)

View File

@ -57,11 +57,13 @@ SPDX-License-Identifier: GPL-3.0-or-later
<h4> {% trans "Validate account" %}</h4>
</div>
{% comment "Soge not for membership (only WEI)" %}
{% 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 %}
{% endcomment %}
<div class="card-body" id="profile_infos">
{% csrf_token %}
@ -76,6 +78,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
</div>
{% endblock %}
{% comment "Soge not for membership (only WEI)" %}
{% block extrajavascript %}
<script>
soge_field = $("#id_soge");
@ -118,3 +121,4 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endif %}
</script>
{% endblock %}
{% endcomment %}

View File

@ -48,6 +48,7 @@ class TestSignup(TestCase):
ml_events_registration="en",
ml_sport_registration=True,
ml_art_registration=True,
VSS_charter_read=True
))
self.assertRedirects(response, reverse("registration:email_validation_sent"), 302, 200)
self.assertTrue(User.objects.filter(username="toto").exists())
@ -105,6 +106,7 @@ class TestSignup(TestCase):
ml_events_registration="en",
ml_sport_registration=True,
ml_art_registration=True,
VSS_charter_read=True
))
self.assertTrue(response.status_code, 200)
@ -124,6 +126,7 @@ class TestSignup(TestCase):
ml_events_registration="en",
ml_sport_registration=True,
ml_art_registration=True,
VSS_charter_read=True
))
self.assertTrue(response.status_code, 200)
@ -143,6 +146,27 @@ class TestSignup(TestCase):
ml_events_registration="en",
ml_sport_registration=True,
ml_art_registration=True,
VSS_charter_read=True
))
self.assertTrue(response.status_code, 200)
# The VSS charter is not read
response = self.client.post(reverse("registration:signup"), dict(
first_name="Toto",
last_name="TOTO",
username="Ihaveanotherusername",
email="othertoto@example.com",
password1="toto1234",
password2="toto1234",
phone_number="+33123456789",
department="EXT",
promotion=Club.objects.get(name="BDE").membership_start.year,
address="Earth",
paid=False,
ml_events_registration="en",
ml_sport_registration=True,
ml_art_registration=True,
VSS_charter_read=False
))
self.assertTrue(response.status_code, 200)
@ -190,7 +214,7 @@ class TestValidateRegistration(TestCase):
# BDE Membership is mandatory
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
soge=False,
# soge=False,
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
credit_amount=4200,
last_name="TOTO",
@ -204,7 +228,7 @@ class TestValidateRegistration(TestCase):
# Same
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
soge=False,
# soge=False,
credit_type="",
credit_amount=0,
last_name="TOTO",
@ -218,7 +242,7 @@ class TestValidateRegistration(TestCase):
# The BDE membership is not free
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
soge=False,
# soge=False,
credit_type=NoteSpecial.objects.get(special_type="Espèces").id,
credit_amount=0,
last_name="TOTO",
@ -232,7 +256,7 @@ class TestValidateRegistration(TestCase):
# Last and first names are required for a credit
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
soge=False,
# soge=False,
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
credit_amount=4000,
last_name="",
@ -249,7 +273,7 @@ class TestValidateRegistration(TestCase):
self.user.username = "admïntoto"
self.user.save()
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
soge=False,
# soge=False,
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
credit_amount=500,
last_name="TOTO",
@ -275,7 +299,7 @@ class TestValidateRegistration(TestCase):
self.user.profile.save()
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
soge=False,
# soge=False,
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
credit_amount=500,
last_name="TOTO",
@ -290,6 +314,7 @@ class TestValidateRegistration(TestCase):
self.assertTrue(NoteUser.objects.filter(user=self.user).exists())
self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists())
self.assertFalse(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
self.assertFalse(Membership.objects.filter(club__name__iexact="BDA", user=self.user).exists())
self.assertFalse(SogeCredit.objects.filter(user=self.user).exists())
self.assertEqual(Transaction.objects.filter(
Q(source=self.user.note) | Q(destination=self.user.note)).count(), 2)
@ -311,7 +336,7 @@ class TestValidateRegistration(TestCase):
self.user.profile.save()
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
soge=False,
# soge=False,
credit_type=NoteSpecial.objects.get(special_type="Espèces").id,
credit_amount=4000,
last_name="TOTO",
@ -326,6 +351,7 @@ class TestValidateRegistration(TestCase):
self.assertTrue(NoteUser.objects.filter(user=self.user).exists())
self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists())
self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
self.assertFalse(Membership.objects.filter(club__name__iexact="BDA", user=self.user).exists())
self.assertFalse(SogeCredit.objects.filter(user=self.user).exists())
self.assertEqual(Transaction.objects.filter(
Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
@ -333,42 +359,43 @@ class TestValidateRegistration(TestCase):
response = self.client.get(self.user.profile.get_absolute_url())
self.assertEqual(response.status_code, 200)
def test_validate_kfet_registration_with_soge(self):
"""
The user joins the BDE and the Kfet, but the membership is paid by the Société générale.
"""
response = self.client.get(reverse("registration:future_user_detail", args=(self.user.pk,)))
self.assertEqual(response.status_code, 200)
response = self.client.get(self.user.profile.get_absolute_url())
self.assertEqual(response.status_code, 404)
self.user.profile.email_confirmed = True
self.user.profile.save()
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
soge=True,
credit_type=NoteSpecial.objects.get(special_type="Espèces").id,
credit_amount=4000,
last_name="TOTO",
first_name="Toto",
bank="Société générale",
join_bde=True,
join_kfet=True,
))
self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200)
self.user.profile.refresh_from_db()
self.assertTrue(self.user.profile.registration_valid)
self.assertTrue(NoteUser.objects.filter(user=self.user).exists())
self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists())
self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
self.assertTrue(SogeCredit.objects.filter(user=self.user).exists())
self.assertEqual(Transaction.objects.filter(
Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
self.assertFalse(Transaction.objects.filter(valid=True).exists())
response = self.client.get(self.user.profile.get_absolute_url())
self.assertEqual(response.status_code, 200)
# def test_validate_kfet_registration_with_soge(self):
# """
# The user joins the BDE and the Kfet, but the membership is paid by the Société générale.
# """
# response = self.client.get(reverse("registration:future_user_detail", args=(self.user.pk,)))
# self.assertEqual(response.status_code, 200)
#
# response = self.client.get(self.user.profile.get_absolute_url())
# self.assertEqual(response.status_code, 404)
#
# self.user.profile.email_confirmed = True
# self.user.profile.save()
#
# response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
# soge=True,
# credit_type=NoteSpecial.objects.get(special_type="Espèces").id,
# credit_amount=4000,
# last_name="TOTO",
# first_name="Toto",
# bank="Société générale",
# join_bde=True,
# join_kfet=True,
# ))
# self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200)
# self.user.profile.refresh_from_db()
# self.assertTrue(self.user.profile.registration_valid)
# self.assertTrue(NoteUser.objects.filter(user=self.user).exists())
# self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists())
# self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
# self.assertFalse(Membership.objects.filter(club__name__iexact="BDA", user=self.user).exists())
# self.assertTrue(SogeCredit.objects.filter(user=self.user).exists())
# self.assertEqual(Transaction.objects.filter(
# Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
# self.assertFalse(Transaction.objects.filter(valid=True).exists())
#
# response = self.client.get(self.user.profile.get_absolute_url())
# self.assertEqual(response.status_code, 200)
def test_invalidate_registration(self):
"""

View File

@ -24,7 +24,8 @@ from permission.models import Role
from permission.views import ProtectQuerysetMixin
from treasury.models import SogeCredit
from .forms import SignUpForm, ValidationForm, DeclareSogeAccountOpenedForm
# from .forms import SignUpForm, ValidationForm, DeclareSogeAccountOpenedForm
from .forms import SignUpForm, ValidationForm
from .tables import FutureUserTable
from .tokens import email_validation_token
@ -42,7 +43,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)
# 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"]
@ -75,12 +76,12 @@ 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()
# 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)
@ -237,9 +238,12 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
fee += bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid
kfet = Club.objects.get(name="Kfet")
fee += kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
if Club.objects.filter(name__iexact="BDA").exists():
bda = Club.objects.get(name__iexact="BDA")
fee += bda.membership_fee_paid if user.profile.paid else bda.membership_fee_unpaid
ctx["total_fee"] = "{:.02f}".format(fee / 100, )
ctx["declare_soge_account"] = SogeCredit.objects.filter(user=user).exists()
# ctx["declare_soge_account"] = SogeCredit.objects.filter(user=user).exists()
return ctx
@ -262,8 +266,13 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
form.add_error(None, _("An alias with a similar name already exists."))
return self.form_invalid(form)
# Check if BDA exist to propose membership at regisration
bda_exists = False
if Club.objects.filter(name__iexact="BDA").exists():
bda_exists = True
# Get form data
soge = form.cleaned_data["soge"]
# soge = form.cleaned_data["soge"]
credit_type = form.cleaned_data["credit_type"]
credit_amount = form.cleaned_data["credit_amount"]
last_name = form.cleaned_data["last_name"]
@ -271,11 +280,13 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
bank = form.cleaned_data["bank"]
join_bde = form.cleaned_data["join_bde"]
join_kfet = form.cleaned_data["join_kfet"]
if bda_exists:
join_bda = form.cleaned_data["join_bda"]
if soge:
# If Société Générale pays the inscription, the user automatically joins the two clubs.
join_bde = True
join_kfet = True
# if soge:
# # If Société Générale pays the inscription, the user automatically joins the two clubs.
# join_bde = True
# join_kfet = True
if not join_bde:
# This software belongs to the BDE.
@ -292,15 +303,21 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
# Add extra fee for the full membership
fee += kfet_fee if join_kfet else 0
if bda_exists:
bda = Club.objects.get(name__iexact="BDA")
bda_fee = bda.membership_fee_paid if user.profile.paid else bda.membership_fee_unpaid
# Add extra fee for the bda membership
fee += bda_fee if join_bda else 0
# If the bank pays, then we don't credit now. Treasurers will validate the transaction
# and credit the note later.
credit_type = None if soge else credit_type
# # If the bank pays, then we don't credit now. Treasurers will validate the transaction
# # and credit the note later.
# credit_type = None if soge else credit_type
# If the user does not select any payment method, then no credit will be performed.
credit_amount = 0 if credit_type is None else credit_amount
if fee > credit_amount and not soge:
# if fee > credit_amount and not soge:
if fee > credit_amount:
# Check if the user credits enough money
form.add_error('credit_type',
_("The entered amount is not enough for the memberships, should be at least {}")
@ -320,12 +337,12 @@ 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 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
@ -334,7 +351,8 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
destination=user.note,
quantity=1,
amount=credit_amount,
reason="Crédit " + ("Société générale" if soge else credit_type.special_type) + " (Inscription)",
reason="Crédit " + credit_type.special_type + " (Inscription)",
# reason="Crédit " + ("Société générale" if soge else credit_type.special_type) + " (Inscription)",
last_name=last_name,
first_name=first_name,
bank=bank,
@ -348,8 +366,8 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
user=user,
fee=bde_fee,
)
if soge:
membership._soge = True
# if soge:
# membership._soge = True
membership.save()
membership.refresh_from_db()
membership.roles.add(Role.objects.get(name="Adhérent BDE"))
@ -362,17 +380,29 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
user=user,
fee=kfet_fee,
)
if soge:
membership._soge = True
# if soge:
# membership._soge = True
membership.save()
membership.refresh_from_db()
membership.roles.add(Role.objects.get(name="Adhérent Kfet"))
membership.save()
if soge:
soge_credit = SogeCredit.objects.get(user=user)
# Update the credit transaction amount
soge_credit.save()
if bda_exists and join_bda:
# Create membership for the user to the BDA starting today
membership = Membership(
club=bda,
user=user,
fee=bda_fee,
)
membership.save()
membership.refresh_from_db()
membership.roles.add(Role.objects.get(name="Membre de club"))
membership.save()
# if soge:
# soge_credit = SogeCredit.objects.get(user=user)
# # Update the credit transaction amount
# soge_credit.save()
return ret

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.28 on 2023-04-14 14:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('treasury', '0005_auto_20230129_2348'),
]
operations = [
migrations.AlterField(
model_name='invoice',
name='bde',
field=models.CharField(choices=[('SecretStorlist', 'SecretStor[list]'), ('TotalistSpies', 'Tota[list]Spies'), ('Saperlistpopette', 'Saper[list]popette'), ('Finalist', 'Fina[list]'), ('Listorique', '[List]orique'), ('Satellist', 'Satel[list]'), ('Monopolist', 'Monopo[list]'), ('Kataclist', 'Katac[list]')], default='SecretStorlist', max_length=32, verbose_name='BDE'),
),
]

View File

@ -1,6 +1,5 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import datetime
from datetime import date
from django.conf import settings
@ -12,7 +11,8 @@ 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 member.models import Club, Membership
# from member.models import Club, Membership # Club unused because of disabled soge
from member.models import Membership
from note.models import NoteSpecial, SpecialTransaction, MembershipTransaction, NoteUser
@ -28,9 +28,10 @@ class Invoice(models.Model):
bde = models.CharField(
max_length=32,
default='TotalistSpies',
default='SecretStorlist',
choices=(
('TotalistSpies', 'Tota[list]Spies'),
('SecretStorlist', 'SecretStor[list]'),
('TotalistSpies', 'Tota[list]Spies'),
('Saperlistpopette', 'Saper[list]popette'),
('Finalist', 'Fina[list]'),
('Listorique', '[List]orique'),
@ -325,12 +326,12 @@ class SogeCredit(models.Model):
if self.valid or not self.pk:
return
bde = Club.objects.get(name="BDE")
kfet = Club.objects.get(name="Kfet")
bde_qs = Membership.objects.filter(user=self.user, club=bde, date_start__gte=bde.membership_start)
kfet_qs = Membership.objects.filter(user=self.user, club=kfet, date_start__gte=kfet.membership_start)
# Soge do not pay BDE and kfet memberships since 2022
# bde = Club.objects.get(name="BDE")
# kfet = Club.objects.get(name="Kfet")
# bde_qs = Membership.objects.filter(user=self.user, club=bde, date_start__gte=bde.membership_start)
# kfet_qs = Membership.objects.filter(user=self.user, club=kfet, date_start__gte=kfet.membership_start)
## Soge do not pay BDE and kfet memberships this year (2022-2023)
# if bde_qs.exists():
# m = bde_qs.get()
# if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
@ -387,7 +388,6 @@ class SogeCredit(models.Model):
for tr in self.transactions.all():
tr.valid = True
tr._force_save = True
tr.created_at = timezone.now()
tr.save()
@transaction.atomic
@ -436,12 +436,11 @@ class SogeCredit(models.Model):
for tr in self.transactions.all():
tr._force_save = True
tr.valid = True
tr.created_at = timezone.now()
tr.save()
if self.credit_transaction:
# If the soge credit is deleted while the user is not validated yet,
# there is not credit transaction.
# There is a credit transaction iff the user declares that no bank account
# There is a credit transaction if the user declares that no bank account
# was opened after the validation of the account.
self.credit_transaction.valid = False
self.credit_transaction.reason += " (invalide)"

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@ -105,8 +105,8 @@
\renewcommand{\headrulewidth}{0pt}
\cfoot{
\small{\MonNom ~--~ \MonAdresseRue ~ \MonAdresseVille ~--~ Téléphone : +33(0)6 89 88 56 50\newline
Site web : bde.ens-cachan.fr ~--~ E-mail : tresorerie.bde@lists.crans.org \newline Numéro SIRET : 399 485 838 00011
\small{\MonNom ~--~ \MonAdresseRue ~ \MonAdresseVille ~--~ Téléphone : +33(0)7 78 17 22 34\newline
Site web : bde.ens-cachan.fr ~--~ E-mail : tresorerie.bde@lists.crans.org \newline Numéro SIRET : 399 485 838 00029
}
}

View File

@ -108,7 +108,7 @@ class InvoiceListView(LoginRequiredMixin, SingleTableView):
name="",
address="",
)
if not PermissionBackend.check_perm(self.request, "treasury.add_invoice", sample_invoice):
if not PermissionBackend.check_perm(self.request, "treasury.view_invoice", sample_invoice):
raise PermissionDenied(_("You are not able to see the treasury interface."))
return super().dispatch(request, *args, **kwargs)

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django import forms
@ -38,7 +38,7 @@ class WEIRegistrationForm(forms.ModelForm):
class Meta:
model = WEIRegistration
exclude = ('wei', )
exclude = ('wei', 'clothing_cut')
widgets = {
"user": Autocomplete(
User,

View File

@ -2,11 +2,11 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm
from .wei2022 import WEISurvey2022
from .wei2023 import WEISurvey2023
__all__ = [
'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey',
]
CurrentSurvey = WEISurvey2022
CurrentSurvey = WEISurvey2023

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import time
@ -14,17 +14,17 @@ from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInf
from ...models import WEIMembership
WORDS = [
'ABBA', 'After', 'Alcoolique anonyme', 'Ambiance festive', 'Années 2000', 'Apéro', 'Art',
'Baby foot billard biere pong', 'BBQ', 'Before', 'Bière pong', 'Bon enfant', 'Calme', 'Canapé',
'Chanson paillarde', 'Chanson populaire', 'Chartreuse', 'Cheerleader', 'Chill', 'Choré',
'Cinéma', 'Cocktail', 'Comédie musicle', 'Commercial', 'Copaing', 'Danse', 'Dancefloor',
'Electro', 'Fanfare', 'Gin tonic', 'Inclusif', 'Jazz', "Jeux d'alcool", 'Jeux de carte',
'Jeux de rôle', 'Jeux de société', 'JUL', 'Jus de fruit', 'Kfet', 'Kleptomanie assurée',
'LGBTQ+', 'Livre', 'Morning beer', 'Musique', 'NAPS', 'Paillettes', 'Pastis', 'Paté Hénaff',
'Peluche', 'Pena baiona', "Peu d'alcool", 'Pilier de bar', 'PMU', 'Poulpe', 'Punch', 'Rap',
'Réveil', 'Rock', 'Rugby', 'Sandwich', 'Serge', 'Shot', 'Sociable', 'Spectacle', 'Techno',
'Techno house', 'Thérapie Taxi', 'Tradition kchanaises', 'Troisième mi-temps', 'Turn up',
'Vodka', 'Vodka pomme', 'Volley', 'Vomi stratégique'
'ABBA', 'After', 'Alcoolique anonyme', 'Ambiance festive', 'Années 2000', 'Apéro', 'Art',
'Baby foot billard biere pong', 'BBQ', 'Before', 'Bière pong', 'Bon enfant', 'Calme', 'Canapé',
'Chanson paillarde', 'Chanson populaire', 'Chartreuse', 'Cheerleader', 'Chill', 'Choré',
'Cinéma', 'Cocktail', 'Comédie musicle', 'Commercial', 'Copaing', 'Danse', 'Dancefloor',
'Electro', 'Fanfare', 'Gin tonic', 'Inclusif', 'Jazz', "Jeux d'alcool", 'Jeux de carte',
'Jeux de rôle', 'Jeux de société', 'JUL', 'Jus de fruit', 'Kfet', 'Kleptomanie assurée',
'LGBTQ+', 'Livre', 'Morning beer', 'Musique', 'NAPS', 'Paillettes', 'Pastis', 'Paté Hénaff',
'Peluche', 'Pena baiona', "Peu d'alcool", 'Pilier de bar', 'PMU', 'Poulpe', 'Punch', 'Rap',
'Réveil', 'Rock', 'Rugby', 'Sandwich', 'Serge', 'Shot', 'Sociable', 'Spectacle', 'Techno',
'Techno house', 'Thérapie Taxi', 'Tradition kchanaises', 'Troisième mi-temps', 'Turn up',
'Vodka', 'Vodka pomme', 'Volley', 'Vomi stratégique'
]

View File

@ -0,0 +1,391 @@
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from functools import lru_cache
from django import forms
from django.db import transaction
from django.db.models import Q
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInformation
from ...models import WEIMembership
WORDS = {
"ambiance": ["Ambiance de bus :", {
1: "Ambiance calme et posée",
2: "Ambiance rigolage entre copaing",
3: "Ambiance danse de camping autour d'une piscine inexistante",
4: "Grosse soirée avec de la musique qui fait bouger",
5: "On retourne le camping et le bus (dans le respect et le savoir vivre)"
}],
"musique": ["Musique :", {
1: "Musique tranquille",
2: "Musique commerciale",
3: "Chansons paillardes",
4: "Musique de Colonie de vacances",
5: "Grosse techno"
}],
"boisson": ["Boissons :", {
1: "Boisson soft",
2: "Des cocktails de temps en temps",
3: "Des coktails fancy de pétasse (parce que c'est les meilleurs)",
4: "Bière !",
5: "L'alcool c'est dans les céréales"
}],
"beauferie": ["Échelle de la beauferie :", {
1: "Je suis toujours classe",
2: "Je rote de temps en temps",
3: "Claquette chaussette, c'est confortable",
4: "L'aviron bayonnais est dans ma plyaylist",
5: "Je suis champion⋅ne de concours de rots et d'éclatage de gobelet sur mon front"
}],
"sommeil": ["Échelle de ton sommeil pendant le WEI :", {
1: "Dormir, c'est pour les faibles",
2: "5h maximum",
3: "10h",
4: "15h",
5: "Deux bonnes nuits de sommeil, c'est important pour être en forme pour les activités proposées par nos supers GC WEI"
}],
"vacances": ["Tes vacances de rêve :", {
1: "Dans ma chambre",
2: "Retourner chez popa et moman pour pouvoir enfin arrêter de manger des pasta box",
3: "Être une grosse larve sous le soleil des troopiiiiiiiiques",
4: "Faire un road trip camping sauvage, manger des racines et boire son pipi",
5: "Le crime ne prend pas de vacances"
}],
"activite": ["T'as une heure de trou pendant ton WEI, que fais-tu ?", {
1: "Je cherche des copaines pour faire un petit jeu de société",
2: "Je cherche un moyen de me dépenser, n'importe quel ballon ferait l'affaire",
3: "Je cherche un endroit où il y a de la musique pour bouger sur le dancefloor",
4: "Petit apéro, petite pétanque avec les collègues autour d'un bon pastaga",
5: "Je cherche une connerie à faire (mais pas trop méchante, pour ne pas embêter mes GC WEI préférés)"
}],
"hygiene": ["Échelle de ton hygiène :", {
1: "La douche, c'eest tous les jours",
2: "La règle des 2 jours, c'est un droit et un devoir",
3: "Je ne me lave qu'après le sport",
4: "« Ne vous inquiétez pas, je pue pas »",
5: "Y a que les sales qui se lavent"
}],
"animal": ["Tu décrirais ton animal totem plutôt comme :", {
1: "Un dragon qui raserait des villes entières d'un seul souffle",
2: "Une mouette qui pique des frites aux dunkerquois",
3: "Un poulpe tout meunion",
4: "Un pitbull qui au fond cache un petit cœur en sucre",
5: "Un canard en plastique au bord d'une baignoire qui n'a pas servi depuis 10 ans"
}],
"fensfoire": ["Quel est ton rapport à la F[ENS]foire ?", {
1: "Je réveille les autres à 6h avec mon instrument",
2: "Je la suis partout",
3: "J'aime bien l'écouter de temps en temps",
4: "Je mets des bouchons d'oreilles pour ne pas l'entendre",
5: "La quoi ?"
}],
"kokarde": ["Qu'est-ce que le mot Kokarde t'évoque ?", {
1: "Vraiment pas mon truc les soirées…",
2: "Bof, je viens pour manger et je repars aussitôt",
3: "Je kiffe, good vibes",
4: "Perso, je ne m'arrêterai pas de danser sur la piste !",
5: "J'resterai jusqu'à 3h ou rien"
}],
"copain": ["Qu'est-ce que tu fais avec un⋅e «copain⋅ine» ?", {
1: "Je l'insulte de sale merde",
2: "J'lui fais faire des trucs cons et je l'affiche !",
3: "On parlerait ensemble et on se marrerait",
4: "On aurait des vrais gros délires",
5: "Je meurs pour lui/elle"
}],
"vie": ["Selon toi, qu'est-ce que la vie ?", {
1: "La vie, cette sale race !",
2: "Un moment paisible avant la mort",
3: "C'est difficile à définir...",
4: "En vrai, c'est cool !",
5: "Une gigantestque tranche de kiff ! Et tous les jours, j'en mange un morceau"
}],
"jeux": ["Quel est ton rapport avec les jeux de société ?", {
1: "éloigné",
2: "nonchalant",
3: "timide",
4: "assumé",
5: "sexuel"
}],
"calin": ["Qu'est-ce que tu penses des câlins ?", {
1: "Jamais je n'en fais et jamais je n'en ferai !",
2: "J'en fais mais ça ne me plaît pas",
3: "J'en fais rarement mais c'est toujours cool",
4: "J'en fais tous les jours avec mes ami⋅es !",
5: "Je pourrais en faire à n'importe qui. Pourquoi ne pas créer le club Câl[ENS] ?"
}],
"vomi": ["Quel est ton rapport au vomi ?", {
1: "C'est compliqué…",
2: "Jamais je ne vomis mais je nettoie quand mes potes vomissent",
3: "Jamais je ne vomis et jamais je ne nettoie celui de quelqu'un d'autre",
4: "Je vomis quelquefois, ça arrive, faites pas cette tête, mais je fins toujours par nettoyer !",
5: "Je vomis à chaque soirée et ce n'est jamais moi qui nettoie"
}],
"kfet": ["Qu'est ce que la Kfet t'évoque ?", {
1: "La Kfet, quel lieu de dépravé⋅es sérieux…",
2: "C'est un endroit à l'hygiène plus que douteuse…",
3: "Téma les prix des boissons et des snacks, c'est aberrant !",
4: "En vrai, c'est cool, petit billard, petit canapé, chill !",
5: "Banger, j'y reste jusqu'à la fin de mes jours"
}],
"fatigue": ["Comment combattre la fatigue lors de ton WEI ?", {
1: "Le sport en journée, ça réveille",
2: "Le sucre du coca, ça réveille",
3: "La taurine du Red Bull, ça réveille",
4: "L'alcool dans le sang, ça réveille",
5: "L'écocup sur le front, ça réveille"
}],
"duree trajet": ["Quelle serait ta durée de trajet préférée ?", {
1: "Trajet instantané, pas le temps de niaiser",
2: "1h, histoire de faire connaissance avec quelques personnes avant de se jeter sur les boissons",
3: "3h, on peut vraiment parler et apprendre à connaître nos voisin⋅es",
4: "6h, histoire d'avoir le temps de faire des conneries dans le bus pour bien se marrer !",
5: "12h, il faut bien trouver un moment pour dormir, ce seront deux gros dodos dans un bus"
}],
"scolarite": ["Comment tu vois ton cursus à l'ENS ?", {
1: "La tranquillité et le travail",
2: "On va s'amuser tout en bossant",
3: "Ça va profiter et réviser au dernier moment pour les exams…",
4: "Nous festoierons sans songer aux conséquences",
5: "Je ne vois qu'une seule issue : la débauche"
}]
}
class WEISurveyForm2023(forms.Form):
"""
Survey form for the year 2023.
Members answer 20 questions, from which we calculate the best associated bus.
"""
def set_registration(self, registration):
"""
Filter the bus selector with the buses of the current WEI.
"""
information = WEISurveyInformation2023(registration)
question = information.questions[information.step]
self.fields[question] = forms.ChoiceField(
label=WORDS[question][0],
widget=forms.RadioSelect(),
)
answers = [(answer, WORDS[question][1][answer]) for answer in WORDS[question][1]]
self.fields[question].choices = answers
class WEIBusInformation2023(WEIBusInformation):
"""
For each question, the bus has ordered answers
"""
scores: dict
def __init__(self, bus):
self.scores = {}
for question in WORDS:
self.scores[question] = []
super().__init__(bus)
class WEISurveyInformation2023(WEISurveyInformation):
"""
We store the id of the selected bus. We store only the name, but is not used in the selection:
that's only for humans that try to read data.
"""
step = 0
questions = list(WORDS.keys())
def __init__(self, registration):
for question in WORDS:
setattr(self, str(question), None)
super().__init__(registration)
class WEISurvey2023(WEISurvey):
"""
Survey for the year 2023.
"""
@classmethod
def get_year(cls):
return 2023
@classmethod
def get_survey_information_class(cls):
return WEISurveyInformation2023
def get_form_class(self):
return WEISurveyForm2023
def update_form(self, form):
"""
Filter the bus selector with the buses of the WEI.
"""
form.set_registration(self.registration)
@transaction.atomic
def form_valid(self, form):
self.information.step += 1
for question in WORDS:
if question in form.cleaned_data:
answer = form.cleaned_data[question]
setattr(self.information, question, answer)
self.save()
@classmethod
def get_algorithm_class(cls):
return WEISurveyAlgorithm2023
def is_complete(self) -> bool:
"""
The survey is complete once the bus is chosen.
"""
for question in WORDS:
if not getattr(self.information, question):
return False
return True
@lru_cache()
def score(self, bus):
if not self.is_complete():
raise ValueError("Survey is not ended, can't calculate score")
bus_info = self.get_algorithm_class().get_bus_information(bus)
# Score is the given score by the bus subtracted to the mid-score of the buses.
s = 0
for question in WORDS:
s += bus_info.scores[question][str(getattr(self.information, question))]
return s
@lru_cache()
def scores_per_bus(self):
return {bus: self.score(bus) for bus in self.get_algorithm_class().get_buses()}
@lru_cache()
def ordered_buses(self):
values = list(self.scores_per_bus().items())
values.sort(key=lambda item: -item[1])
return values
@classmethod
def clear_cache(cls):
return super().clear_cache()
class WEISurveyAlgorithm2023(WEISurveyAlgorithm):
"""
The algorithm class for the year 2023.
We use Gale-Shapley algorithm to attribute 1y students into buses.
"""
@classmethod
def get_survey_class(cls):
return WEISurvey2023
@classmethod
def get_bus_information_class(cls):
return WEIBusInformation2023
def run_algorithm(self, display_tqdm=False):
"""
Gale-Shapley algorithm implementation.
We modify it to allow buses to have multiple "weddings".
"""
surveys = list(self.get_survey_class()(r) for r in self.get_registrations()) # All surveys
surveys = [s for s in surveys if s.is_complete()] # Don't consider invalid surveys
# Don't manage hardcoded people
surveys = [s for s in surveys if not hasattr(s.information, 'hardcoded') or not s.information.hardcoded]
# Reset previous algorithm run
for survey in surveys:
survey.free()
survey.save()
non_men = [s for s in surveys if s.registration.gender != 'male']
men = [s for s in surveys if s.registration.gender == 'male']
quotas = {}
registrations = self.get_registrations()
non_men_total = registrations.filter(~Q(gender='male')).count()
for bus in self.get_buses():
free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count()
# Remove hardcoded people
free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True,
registration__information_json__icontains="hardcoded").count()
quotas[bus] = 4 + int(non_men_total / registrations.count() * free_seats)
tqdm_obj = None
if display_tqdm:
from tqdm import tqdm
tqdm_obj = tqdm(total=len(non_men), desc="Non-hommes")
# Repartition for non men people first
self.make_repartition(non_men, quotas, tqdm_obj=tqdm_obj)
quotas = {}
for bus in self.get_buses():
free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count()
free_seats -= sum(1 for s in non_men if s.information.selected_bus_pk == bus.pk)
# Remove hardcoded people
free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True,
registration__information_json__icontains="hardcoded").count()
quotas[bus] = free_seats
if display_tqdm:
tqdm_obj.close()
from tqdm import tqdm
tqdm_obj = tqdm(total=len(men), desc="Hommes")
self.make_repartition(men, quotas, tqdm_obj=tqdm_obj)
if display_tqdm:
tqdm_obj.close()
# Clear cache information after running algorithm
WEISurvey2023.clear_cache()
def make_repartition(self, surveys, quotas=None, tqdm_obj=None):
free_surveys = surveys.copy() # Remaining surveys
while free_surveys: # Some students are not affected
survey = free_surveys[0]
buses = survey.ordered_buses() # Preferences of the student
for bus, current_score in buses:
if self.get_bus_information(bus).has_free_seats(surveys, quotas):
# Selected bus has free places. Put student in the bus
survey.select_bus(bus)
survey.save()
free_surveys.remove(survey)
break
else:
# Current bus has not enough places. Remove the least preferred student from the bus if existing
least_preferred_survey = None
least_score = -1
# Find the least student in the bus that has a lower score than the current student
for survey2 in surveys:
if not survey2.information.valid or survey2.information.get_selected_bus() != bus:
continue
score2 = survey2.score(bus)
if current_score <= score2: # Ignore better students
continue
if least_preferred_survey is None or score2 < least_score:
least_preferred_survey = survey2
least_score = score2
if least_preferred_survey is not None:
# Remove the least student from the bus and put the current student in.
# If it does not exist, choose the next bus.
least_preferred_survey.free()
least_preferred_survey.save()
free_surveys.append(least_preferred_survey)
survey.select_bus(bus)
survey.save()
free_surveys.remove(survey)
break
else:
raise ValueError(f"User {survey.registration.user} has no free seat")
if tqdm_obj is not None:
tqdm_obj.n = len(surveys) - len(free_surveys)
tqdm_obj.refresh()

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.28 on 2023-07-09 09:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wei', '0005_auto_20230128_1850'),
]
operations = [
migrations.AlterField(
model_name='weiregistration',
name='clothing_cut',
field=models.CharField(choices=[('male', 'Male'), ('female', 'Female'), ('unisex', 'Unisex')], default='unisex', max_length=16, verbose_name='clothing cut'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.28 on 2023-07-09 12:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wei', '0006_unisex_clothing_cut'),
]
operations = [
migrations.AlterField(
model_name='weiregistration',
name='emergency_contact_name',
field=models.CharField(help_text='The emergency contact must not be a WEI participant', max_length=255, verbose_name='emergency contact name'),
),
]

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import json
@ -209,7 +209,9 @@ class WEIRegistration(models.Model):
choices=(
('male', _("Male")),
('female', _("Female")),
('unisex', _("Unisex")),
),
default='unisex',
verbose_name=_("clothing cut"),
)
@ -235,6 +237,7 @@ class WEIRegistration(models.Model):
emergency_contact_name = models.CharField(
max_length=255,
verbose_name=_("emergency contact name"),
help_text=_("The emergency contact must not be a WEI participant")
)
emergency_contact_phone = PhoneNumberField(

View File

@ -56,7 +56,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<dd class="col-xl-6">{{ registration.get_gender_display }}</dd>
<dt class="col-xl-6">{% trans 'clothing cut'|capfirst %}</dt>
<dd class="col-xl-6">{{ registration.clothing_cut }}</dd>
<dd class="col-xl-6">{{ registration.get_clothing_cut_display }}</dd>
<dt class="col-xl-6">{% trans 'clothing size'|capfirst %}</dt>
<dd class="col-xl-6">{{ registration.clothing_size }}</dd>

View File

@ -0,0 +1,170 @@
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import random
from datetime import date
from django.contrib.auth.models import User
from django.test import TestCase
from django.urls import reverse
from note.models import NoteUser
from ..forms.surveys.wei2023 import WEIBusInformation2023, WEISurvey2023, WORDS, WEISurveyInformation2023
from ..models import Bus, WEIClub, WEIRegistration
class TestWEIAlgorithm(TestCase):
"""
Run some tests to ensure that the WEI algorithm is working well.
"""
fixtures = ('initial',)
def setUp(self):
"""
Create some test data, with one WEI and 10 buses with random score attributions.
"""
self.user = User.objects.create_superuser(
username="weiadmin",
password="admin",
email="admin@example.com",
)
self.user.save()
self.client.force_login(self.user)
sess = self.client.session
sess["permission_mask"] = 42
sess.save()
self.wei = WEIClub.objects.create(
name="WEI 2023",
email="wei2023@example.com",
parent_club_id=2,
membership_fee_paid=12500,
membership_fee_unpaid=5500,
membership_start='2023-08-26',
membership_end='2023-09-15',
date_start='2023-09-16',
date_end='2023-09-18',
year=2023,
)
self.buses = []
for i in range(10):
bus = Bus.objects.create(wei=self.wei, name=f"Bus {i}", size=10)
self.buses.append(bus)
information = WEIBusInformation2023(bus)
for question in WORDS:
information.scores[question] = {answer: random.randint(1, 5) for answer in WORDS[question][1]}
information.save()
bus.save()
def test_survey_algorithm_small(self):
"""
There are only a few people in each bus, ensure that each person has its best bus
"""
# Add a few users
for i in range(10):
user = User.objects.create(username=f"user{i}")
registration = WEIRegistration.objects.create(
user=user,
wei=self.wei,
first_year=True,
birth_date='2000-01-01',
)
information = WEISurveyInformation2023(registration)
for question in WORDS:
setattr(information, question, random.randint(1, 5))
information.step = 20
information.save(registration)
registration.save()
# Run algorithm
WEISurvey2023.get_algorithm_class()().run_algorithm()
# Ensure that everyone has its first choice
for r in WEIRegistration.objects.filter(wei=self.wei).all():
survey = WEISurvey2023(r)
preferred_bus = survey.ordered_buses()[0][0]
chosen_bus = survey.information.get_selected_bus()
self.assertEqual(preferred_bus, chosen_bus)
def test_survey_algorithm_full(self):
"""
Buses are full of first year people, ensure that they are happy
"""
# Add a lot of users
for i in range(95):
user = User.objects.create(username=f"user{i}")
registration = WEIRegistration.objects.create(
user=user,
wei=self.wei,
first_year=True,
birth_date='2000-01-01',
)
information = WEISurveyInformation2023(registration)
for question in WORDS:
setattr(information, question, random.randint(1, 5))
information.step = 20
information.save(registration)
registration.save()
# Run algorithm
WEISurvey2023.get_algorithm_class()().run_algorithm()
penalty = 0
# Ensure that everyone seems to be happy
# We attribute a penalty for each user that didn't have its first choice
# The penalty is the square of the distance between the score of the preferred bus
# and the score of the attributed bus
# We consider it acceptable if the mean of this distance is lower than 5 %
for r in WEIRegistration.objects.filter(wei=self.wei).all():
survey = WEISurvey2023(r)
chosen_bus = survey.information.get_selected_bus()
buses = survey.ordered_buses()
score = min(v for bus, v in buses if bus == chosen_bus)
max_score = buses[0][1]
penalty += (max_score - score) ** 2
self.assertLessEqual(max_score - score, 25) # Always less than 25 % of tolerance
self.assertLessEqual(penalty / 100, 25) # Tolerance of 5 %
def test_register_1a(self):
"""
Test register a first year member to the WEI and complete the survey
"""
response = self.client.get(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=self.wei.pk)))
self.assertEqual(response.status_code, 200)
user = User.objects.create(username="toto", email="toto@example.com")
NoteUser.objects.create(user=user)
response = self.client.post(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=self.wei.pk)), dict(
user=user.id,
soge_credit=True,
birth_date=date(2000, 1, 1),
gender='nonbinary',
clothing_cut='female',
clothing_size='XS',
health_issues='I am a bot',
emergency_contact_name='NoteKfet2020',
emergency_contact_phone='+33123456789',
))
qs = WEIRegistration.objects.filter(user_id=user.id)
self.assertTrue(qs.exists())
registration = qs.get()
self.assertRedirects(response, reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), 302, 200)
for question in WORDS:
# Fill 1A Survey, 20 pages
# be careful if questionnary form change (number of page, type of answer...)
response = self.client.post(reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), {
question: "1"
})
registration.refresh_from_db()
survey = WEISurvey2023(registration)
self.assertRedirects(response, reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), 302,
302 if survey.is_complete() else 200)
self.assertIsNotNone(getattr(survey.information, question), "Survey page " + question + " failed")
survey = WEISurvey2023(registration)
self.assertTrue(survey.is_complete())
survey.select_bus(self.buses[0])
survey.save()
self.assertIsNotNone(survey.information.get_selected_bus())

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import subprocess
@ -380,7 +380,7 @@ class TestWEIRegistration(TestCase):
def test_register_1a(self):
"""
Test register a first year member to the WEI and complete the survey.
Test register a first year member to the WEI.
"""
response = self.client.get(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=self.wei.pk)))
self.assertEqual(response.status_code, 200)
@ -402,21 +402,6 @@ class TestWEIRegistration(TestCase):
self.assertTrue(qs.exists())
registration = qs.get()
self.assertRedirects(response, reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), 302, 200)
for i in range(1, 21):
# Fill 1A Survey, 20 pages
response = self.client.post(reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), dict(
word="Jus de fruit",
))
registration.refresh_from_db()
survey = CurrentSurvey(registration)
self.assertRedirects(response, reverse("wei:wei_survey", kwargs=dict(pk=registration.pk)), 302,
302 if survey.is_complete() else 200)
self.assertIsNotNone(getattr(survey.information, "word" + str(i)), "Survey page #" + str(i) + " failed")
survey = CurrentSurvey(registration)
self.assertTrue(survey.is_complete())
survey.select_bus(self.bus)
survey.save()
self.assertIsNotNone(survey.information.get_selected_bus())
# Check that the user can't be registered twice
response = self.client.post(reverse("wei:wei_register_1A", kwargs=dict(wei_pk=self.wei.pk)), dict(
@ -662,7 +647,7 @@ class TestWEIRegistration(TestCase):
first_name="admin",
bank="Société générale",
))
self.assertRedirects(response, reverse("wei:wei_detail", kwargs=dict(pk=self.registration.wei.pk)), 302, 200)
self.assertRedirects(response, reverse("wei:wei_registrations", kwargs=dict(pk=self.registration.wei.pk)), 302, 200)
# Check if the membership is successfully created
membership = WEIMembership.objects.filter(user_id=self.user.id, club=self.wei)
self.assertTrue(membership.exists())
@ -782,7 +767,7 @@ class TestDefaultWEISurvey(TestCase):
WEISurvey.update_form(None, None)
self.assertEqual(CurrentSurvey.get_algorithm_class().get_survey_class(), CurrentSurvey)
self.assertEqual(CurrentSurvey.get_year(), 2022)
self.assertEqual(CurrentSurvey.get_year(), 2023)
class TestWeiAPI(TestAPI):

View File

@ -969,7 +969,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
if not registration.soge_credit and user.note.balance + credit_amount < fee:
# Users must have money before registering to the WEI.
form.add_error('bus',
form.add_error('credit_type',
_("This user don't have enough money to join this club, and can't have a negative balance."))
return super().form_invalid(form)
@ -1014,7 +1014,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
def get_success_url(self):
self.object.refresh_from_db()
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.club.pk})
return reverse_lazy("wei:wei_registrations", kwargs={"pk": self.object.club.pk})
class WEISurveyView(LoginRequiredMixin, BaseFormView, DetailView):
@ -1084,7 +1084,44 @@ class WEISurveyEndView(LoginRequiredMixin, TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["club"] = WEIRegistration.objects.get(pk=self.kwargs["pk"]).wei
club = WEIRegistration.objects.get(pk=self.kwargs["pk"]).wei
context["club"] = club
random_user = User.objects.filter(~Q(wei__wei__in=[club])).first()
if random_user is None:
# This case occurs when all users are registered to the WEI.
# Don't worry, Pikachu never went to the WEI.
# This bug can arrive only in dev mode.
context["can_add_first_year_member"] = True
context["can_add_any_member"] = True
else:
# Check if the user has the right to create a registration of a random first year member.
empty_fy_registration = WEIRegistration(
wei=club,
user=random_user,
first_year=True,
birth_date="1970-01-01",
gender="No",
emergency_contact_name="No",
emergency_contact_phone="No",
)
context["can_add_first_year_member"] = PermissionBackend \
.check_perm(self.request, "wei.add_weiregistration", empty_fy_registration)
# Check if the user has the right to create a registration of a random old member.
empty_old_registration = WEIRegistration(
wei=club,
user=User.objects.filter(~Q(wei__wei__in=[club])).first(),
first_year=False,
birth_date="1970-01-01",
gender="No",
emergency_contact_name="No",
emergency_contact_phone="No",
)
context["can_add_any_member"] = PermissionBackend \
.check_perm(self.request, "wei.add_weiregistration", empty_old_registration)
return context

View File

@ -448,6 +448,10 @@ Options
"value": "female",
"display_name": "Femme"
}
{
"value": "unisex",
"display_name": "Unisexe"
},
]
},
"clothing_size": {

View File

@ -118,13 +118,13 @@ Exemples
{"F": [
"ADD",
["F", "source__balance"],
5000]
2000]
}
}
]
| si la destination est la note du club dont on est membre et si le montant est inférieur au solde de la source + 50 €,
autrement dit le solde final est au-dessus de -50 €.
| si la destination est la note du club dont on est membre et si le montant est inférieur au solde de la source + 20 €,
autrement dit le solde final est au-dessus de -20 €.
Masques de permissions

View File

@ -83,13 +83,6 @@ Je suis trésorier d'un club, qu'ai-je le droit de faire ?
bien sûr permis pour faciliter des transferts. Tout abus de droits constaté
pourra mener à des sanctions prises par le bureau du BDE.
.. warning::
Une fonctionnalité pour permettre de gérer plus proprement les remboursements
entre amis est en cours de développement. Temporairement et pour des raisons
de confort, les trésoriers de clubs ont le droit de prélever n'importe quelle
adhérente vers n'importe quelle autre note adhérente, tant que la source ne
descend pas sous ``- 50 €``. Ces droits seront retirés d'ici quelques semaines.
Je suis trésorier d'un club, je n'arrive pas à voir le solde du club / faire des transactions
---------------------------------------------------------------------------------------------------

View File

@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-10 22:34+0200\n"
"POT-Creation-Date: 2023-08-31 13:25+0200\n"
"PO-Revision-Date: 2020-11-16 20:02+0000\n"
"Last-Translator: Yohann D'ANELLO <ynerant@crans.org>\n"
"Last-Translator: bleizi <bleizi@crans.org>\n"
"Language-Team: German <http://translate.ynerant.fr/projects/nk20/nk20/de/>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
@ -53,7 +53,7 @@ msgid "You can't invite more than 3 people to this activity."
msgstr "Sie dürfen höchstens 3 Leute zu dieser Veranstaltung einladen."
#: apps/activity/models.py:28 apps/activity/models.py:63
#: apps/member/models.py:199
#: apps/member/models.py:204
#: apps/member/templates/member/includes/club_info.html:4
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/note/models/notes.py:263 apps/note/models/transactions.py:26
@ -114,8 +114,8 @@ msgstr "Wo findet die Veranstaltung statt ? (z.B Kfet)."
msgid "type"
msgstr "Type"
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:312
#: apps/note/models/notes.py:148 apps/treasury/models.py:287
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
#: apps/wei/templates/wei/survey.html:15
msgid "user"
@ -258,19 +258,19 @@ msgstr "Eingetreten um "
msgid "remove"
msgstr "entfernen"
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:199
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:201
msgid "Type"
msgstr "Type"
#: apps/activity/tables.py:84 apps/member/forms.py:186
#: apps/registration/forms.py:91 apps/treasury/forms.py:131
#: apps/activity/tables.py:84 apps/member/forms.py:193
#: apps/registration/forms.py:93 apps/treasury/forms.py:131
#: apps/wei/forms/registration.py:104
msgid "Last name"
msgstr "Nachname"
#: apps/activity/tables.py:86 apps/member/forms.py:191
#: apps/activity/tables.py:86 apps/member/forms.py:198
#: apps/note/templates/note/transaction_form.html:138
#: apps/registration/forms.py:96 apps/treasury/forms.py:133
#: apps/registration/forms.py:98 apps/treasury/forms.py:133
#: apps/wei/forms/registration.py:109
msgid "First name"
msgstr "Vorname"
@ -498,21 +498,21 @@ msgstr "Changelogs"
msgid "Changelog of type \"{action}\" for model {model} at {timestamp}"
msgstr "Changelog \"{action}\" für Model {model} an {timestamp}"
#: apps/member/admin.py:50 apps/member/models.py:226
#: apps/member/admin.py:50 apps/member/models.py:231
#: apps/member/templates/member/includes/club_info.html:34
msgid "membership fee (paid students)"
msgstr "Mitgliedschaftpreis (bezahlte Studenten)"
#: apps/member/admin.py:51 apps/member/models.py:231
#: apps/member/admin.py:51 apps/member/models.py:236
#: apps/member/templates/member/includes/club_info.html:37
msgid "membership fee (unpaid students)"
msgstr "Mitgliedschaftpreis (unbezahlte Studenten)"
#: apps/member/admin.py:65 apps/member/models.py:319
#: apps/member/admin.py:65 apps/member/models.py:324
msgid "roles"
msgstr "Rollen"
#: apps/member/admin.py:66 apps/member/models.py:333
#: apps/member/admin.py:66 apps/member/models.py:338
msgid "fee"
msgstr "Preis"
@ -532,65 +532,81 @@ msgstr "Bericht Frequenz"
msgid "Last report date"
msgstr "Letzen Bericht Datum"
#: apps/member/forms.py:52
msgid ""
"Anti-VSS (<em>Violences Sexistes et Sexuelles</em>) charter read and approved"
msgstr ""
"Anti-VSS (<em>Violences Sexistes et Sexuelles</em>) Charta gelesen und angenommen"
#: apps/member/forms.py:53
msgid ""
"Tick after having read and accepted the anti-VSS charter <a "
"href=https://perso.crans.org/club-bde/Charte-anti-VSS.pdf target=_blank> "
"available here in pdf</a>"
msgstr ""
"Kreuzen Sie an, nachdem Sie die Anti-VSS-Charta gelesen und akzeptiert haben, <a "
"href=https://perso.crans.org/club-bde/Charte-anti-VSS.pdf target=_blank> "
"die hier als pdf-Datei verfügbar ist</a>"
#: apps/member/forms.py:60
msgid "You can't register to the note if you come from the future."
msgstr "Sie dürfen nicht einloggen wenn sie aus der Zukunft kommen."
#: apps/member/forms.py:79
#: apps/member/forms.py:86
msgid "select an image"
msgstr "Wählen sie ein Bild aus"
#: apps/member/forms.py:80
#: apps/member/forms.py:87
msgid "Maximal size: 2MB"
msgstr "Maximal Größe: 2MB"
#: apps/member/forms.py:105
#: apps/member/forms.py:112
msgid "This image cannot be loaded."
msgstr "Dieses Bild kann nicht geladen werden."
#: apps/member/forms.py:141 apps/member/views.py:103
#: apps/registration/forms.py:33 apps/registration/views.py:262
#: apps/member/forms.py:148 apps/member/views.py:103
#: apps/registration/forms.py:35 apps/registration/views.py:266
msgid "An alias with a similar name already exists."
msgstr "Ein ähnliches Alias ist schon benutzt."
#: apps/member/forms.py:165 apps/registration/forms.py:71
#: apps/member/forms.py:172
msgid "Inscription paid by Société Générale"
msgstr "Mitgliedschaft von der Société Générale bezahlt"
#: apps/member/forms.py:167 apps/registration/forms.py:73
#: apps/member/forms.py:174
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:172 apps/registration/forms.py:78
#: apps/member/forms.py:179 apps/registration/forms.py:80
#: apps/wei/forms/registration.py:91
msgid "Credit type"
msgstr "Kredittype"
#: apps/member/forms.py:173 apps/registration/forms.py:79
#: apps/member/forms.py:180 apps/registration/forms.py:81
#: apps/wei/forms/registration.py:92
msgid "No credit"
msgstr "Kein Kredit"
#: apps/member/forms.py:175
#: apps/member/forms.py:182
msgid "You can credit the note of the user."
msgstr "Sie dûrfen diese Note kreditieren."
#: apps/member/forms.py:179 apps/registration/forms.py:84
#: apps/member/forms.py:186 apps/registration/forms.py:86
#: apps/wei/forms/registration.py:97
msgid "Credit amount"
msgstr "Kreditanzahl"
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:144
#: apps/registration/forms.py:101 apps/treasury/forms.py:135
#: apps/member/forms.py:203 apps/note/templates/note/transaction_form.html:144
#: apps/registration/forms.py:103 apps/treasury/forms.py:135
#: apps/wei/forms/registration.py:114
msgid "Bank"
msgstr "Bank"
#: apps/member/forms.py:223
#: apps/member/forms.py:230
msgid "User"
msgstr "User"
#: apps/member/forms.py:237
#: apps/member/forms.py:244
msgid "Roles"
msgstr "Rollen"
@ -777,15 +793,19 @@ msgstr "email bestätigt"
msgid "registration valid"
msgstr "Anmeldung gültig"
#: apps/member/models.py:162 apps/member/models.py:163
#: apps/member/models.py:138
msgid "VSS charter read"
msgstr "VSS-Charta gelesen"
#: apps/member/models.py:167 apps/member/models.py:168
msgid "user profile"
msgstr "Userprofile"
#: apps/member/models.py:173
#: apps/member/models.py:178
msgid "Activate your Note Kfet account"
msgstr "Ihre Note Kfet Konto bestätigen"
#: apps/member/models.py:204
#: apps/member/models.py:209
#: apps/member/templates/member/includes/club_info.html:55
#: apps/member/templates/member/includes/profile_info.html:40
#: apps/registration/templates/registration/future_profile_detail.html:22
@ -794,88 +814,88 @@ msgstr "Ihre Note Kfet Konto bestätigen"
msgid "email"
msgstr "Email"
#: apps/member/models.py:211
#: apps/member/models.py:216
msgid "parent club"
msgstr "Urclub"
#: apps/member/models.py:220
#: apps/member/models.py:225
msgid "require memberships"
msgstr "erfordern Mitgliedschaft"
#: apps/member/models.py:221
#: apps/member/models.py:226
msgid "Uncheck if this club don't require memberships."
msgstr ""
"Deaktivieren Sie diese Option, wenn für diesen Club keine Mitgliedschaft "
"erforderlich ist."
#: apps/member/models.py:237
#: apps/member/models.py:242
#: apps/member/templates/member/includes/club_info.html:26
msgid "membership duration"
msgstr "Mitgliedscahftzeit"
#: apps/member/models.py:238
#: apps/member/models.py:243
msgid "The longest time (in days) a membership can last (NULL = infinite)."
msgstr "Wie lang am höchsten eine Mitgliedschaft dauern kann."
#: apps/member/models.py:245
#: apps/member/models.py:250
#: apps/member/templates/member/includes/club_info.html:16
msgid "membership start"
msgstr "Mitgliedschaftanfangsdatum"
#: apps/member/models.py:246
#: apps/member/models.py:251
msgid "Date from which the members can renew their membership."
msgstr "Ab wann kann man sein Mitgliedschaft erneuern."
#: apps/member/models.py:252
#: apps/member/models.py:257
#: apps/member/templates/member/includes/club_info.html:21
msgid "membership end"
msgstr "Mitgliedschaftenddatum"
#: apps/member/models.py:253
#: apps/member/models.py:258
msgid "Maximal date of a membership, after which members must renew it."
msgstr ""
"Maximales Datum einer Mitgliedschaft, nach dem Mitglieder es erneuern müssen."
#: apps/member/models.py:288 apps/member/models.py:313
#: apps/member/models.py:293 apps/member/models.py:318
#: apps/note/models/notes.py:176
msgid "club"
msgstr "Club"
#: apps/member/models.py:289
#: apps/member/models.py:294
msgid "clubs"
msgstr "Clubs"
#: apps/member/models.py:324
#: apps/member/models.py:329
msgid "membership starts on"
msgstr "Mitgliedschaft fängt an"
#: apps/member/models.py:328
#: apps/member/models.py:333
msgid "membership ends on"
msgstr "Mitgliedschaft endet am"
#: apps/member/models.py:430
#: apps/member/models.py:435
#, python-brace-format
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:439 apps/member/views.py:712
#: apps/member/models.py:444 apps/member/views.py:712
msgid "User is already a member of the club"
msgstr "User ist schon ein Mitglied dieser club"
#: apps/member/models.py:451 apps/member/views.py:721
#: apps/member/models.py:456 apps/member/views.py:721
msgid "User is not a member of the parent club"
msgstr "User ist noch nicht Mitglied des Urclubs"
#: apps/member/models.py:504
#: apps/member/models.py:509
#, python-brace-format
msgid "Membership of {user} for the club {club}"
msgstr "Mitgliedschaft von {user} für das Club {club}"
#: apps/member/models.py:507 apps/note/models/transactions.py:389
#: apps/member/models.py:512 apps/note/models/transactions.py:389
msgid "membership"
msgstr "Mitgliedschaft"
#: apps/member/models.py:508
#: apps/member/models.py:513
msgid "memberships"
msgstr "Mitgliedschaften"
@ -1193,7 +1213,7 @@ msgstr "Speichern"
msgid "Registrations"
msgstr "Anmeldung"
#: apps/member/views.py:73 apps/registration/forms.py:23
#: apps/member/views.py:73 apps/registration/forms.py:24
msgid "This address must be valid."
msgstr "Diese Adresse muss gültig sein."
@ -1249,11 +1269,11 @@ msgstr "Die Mitgliedschaft muss nach {:%m-%d-Y} anfängen."
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr "Die Mitgliedschaft muss vor {:%m-%d-Y} anfängen."
#: apps/member/views.py:876
#: apps/member/views.py:880
msgid "Manage roles of an user in the club"
msgstr "Rollen in diesen Club bearbeiten"
#: apps/member/views.py:901
#: apps/member/views.py:905
msgid "Members of the club"
msgstr "Mitlglieder dieses Club"
@ -1570,7 +1590,7 @@ msgstr "Sondertranskationen"
msgid "membership transaction"
msgstr "Mitgliedschafttransaktion"
#: apps/note/models/transactions.py:385 apps/treasury/models.py:292
#: apps/note/models/transactions.py:385 apps/treasury/models.py:294
msgid "membership transactions"
msgstr "Mitgliedschaftttransaktionen"
@ -1689,7 +1709,7 @@ msgid "Amount"
msgstr "Anzahl"
#: apps/note/templates/note/transaction_form.html:132
#: apps/treasury/models.py:54
#: apps/treasury/models.py:56
msgid "Name"
msgstr "Name"
@ -1860,7 +1880,7 @@ msgstr "Angabefeld gilt nur zum Anzeigen und Ändern von Berechtigungstypen."
msgid "for club"
msgstr "Für Club"
#: apps/permission/models.py:350 apps/permission/models.py:351
#: apps/permission/models.py:351 apps/permission/models.py:352
msgid "role permissions"
msgstr "Berechtigung Rollen"
@ -1982,29 +2002,15 @@ msgstr "Alle Rechten"
msgid "registration"
msgstr "Anmeldung"
#: apps/registration/forms.py:39
#: apps/registration/forms.py:41
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 or I will open soon 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:51
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:59
#: apps/registration/forms.py:61
msgid "Register to the WEI"
msgstr "Zu WEI anmelden"
#: apps/registration/forms.py:61
#: apps/registration/forms.py:63
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."
@ -2013,14 +2019,18 @@ msgstr ""
"falls Zweifel, können Sie sich später nach Bestätigung Ihres Kontos im Kfet "
"registrieren."
#: apps/registration/forms.py:106
#: apps/registration/forms.py:108
msgid "Join BDE Club"
msgstr "BDE Mitglieder werden"
#: apps/registration/forms.py:113
#: apps/registration/forms.py:115
msgid "Join Kfet Club"
msgstr "Kfet Mitglieder werden"
#: apps/registration/forms.py:124
msgid "Join BDA Club"
msgstr "BDA Mitglieder werden"
#: apps/registration/templates/registration/email_validation_complete.html:15
msgid "Your email have successfully been validated."
msgstr "Ihre E-Mail wurde erfolgreich validiert."
@ -2069,14 +2079,14 @@ msgstr "Registrierung löschen"
msgid "Validate account"
msgstr "Konto validieren"
#: apps/registration/templates/registration/future_profile_detail.html:62
#: apps/registration/templates/registration/future_profile_detail.html:63
#, 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/registration/templates/registration/future_profile_detail.html:73
#: apps/wei/templates/wei/weimembership_form.html:127
#: apps/wei/templates/wei/weimembership_form.html:186
msgid "Validate registration"
@ -2128,54 +2138,54 @@ msgstr "Danke"
msgid "The Note Kfet team."
msgstr "Die NoteKfet Team."
#: apps/registration/views.py:40
#: apps/registration/views.py:41
msgid "Register new user"
msgstr "Neuen User registrieren"
#: apps/registration/views.py:98
#: apps/registration/views.py:99
msgid "Email validation"
msgstr "Email validierung"
#: apps/registration/views.py:100
#: apps/registration/views.py:101
msgid "Validate email"
msgstr "Email validieren"
#: apps/registration/views.py:144
#: apps/registration/views.py:145
msgid "Email validation unsuccessful"
msgstr "Email validierung unerfolgreich"
#: apps/registration/views.py:155
#: apps/registration/views.py:156
msgid "Email validation email sent"
msgstr "Validierungsemail wurde gesendet"
#: apps/registration/views.py:163
#: apps/registration/views.py:164
msgid "Resend email validation link"
msgstr "E-Mail-Validierungslink erneut senden"
#: apps/registration/views.py:181
#: apps/registration/views.py:182
msgid "Pre-registered users list"
msgstr "Vorregistrierte Userliste"
#: apps/registration/views.py:205
#: apps/registration/views.py:206
msgid "Unregistered users"
msgstr "Unregistrierte Users"
#: apps/registration/views.py:218
#: apps/registration/views.py:219
msgid "Registration detail"
msgstr "Registrierung Detailen"
#: apps/registration/views.py:282
#: apps/registration/views.py:293
msgid "You must join the BDE."
msgstr "Sie müssen die BDE beitreten."
#: apps/registration/views.py:306
#: apps/registration/views.py:323
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:387
#: apps/registration/views.py:417
msgid "Invalidate pre-registration"
msgstr "Ungültige Vorregistrierung"
@ -2183,7 +2193,7 @@ msgstr "Ungültige Vorregistrierung"
msgid "Treasury"
msgstr "Quaestor"
#: apps/treasury/forms.py:26 apps/treasury/models.py:93
#: apps/treasury/forms.py:26 apps/treasury/models.py:95
#: apps/treasury/templates/treasury/invoice_form.html:22
msgid "This invoice is locked and can no longer be edited."
msgstr "Diese Rechnung ist gesperrt und kann nicht mehr bearbeitet werden."
@ -2196,7 +2206,7 @@ msgstr "Überweisung ist bereits geschlossen."
msgid "You can't change the type of the remittance."
msgstr "Sie können die Art der Überweisung nicht ändern."
#: apps/treasury/forms.py:125 apps/treasury/models.py:267
#: apps/treasury/forms.py:125 apps/treasury/models.py:269
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
#: apps/treasury/templates/treasury/invoice_list.html:16
#: apps/treasury/templates/treasury/remittance_list.html:16
@ -2212,116 +2222,116 @@ msgstr "Keine beigefügte Überweisung"
msgid "Invoice identifier"
msgstr "Rechnungskennung"
#: apps/treasury/models.py:40
#: apps/treasury/models.py:42
msgid "BDE"
msgstr "BDE"
#: apps/treasury/models.py:45
#: apps/treasury/models.py:47
msgid "Object"
msgstr "Objekt"
#: apps/treasury/models.py:49
#: apps/treasury/models.py:51
msgid "Description"
msgstr "Beschreibung"
#: apps/treasury/models.py:58
#: apps/treasury/models.py:60
msgid "Address"
msgstr "Adresse"
#: apps/treasury/models.py:63 apps/treasury/models.py:193
#: apps/treasury/models.py:65 apps/treasury/models.py:195
msgid "Date"
msgstr "Datum"
#: apps/treasury/models.py:67
#: apps/treasury/models.py:69
msgid "Acquitted"
msgstr "Bezahlt"
#: apps/treasury/models.py:72
#: apps/treasury/models.py:74
msgid "Locked"
msgstr "Gesperrt"
#: apps/treasury/models.py:73
#: apps/treasury/models.py:75
msgid "An invoice can't be edited when it is locked."
msgstr "Eine Rechnung kann nicht bearbeitet werden, wenn sie gesperrt ist."
#: apps/treasury/models.py:79
#: apps/treasury/models.py:81
msgid "tex source"
msgstr "Tex Quelle"
#: apps/treasury/models.py:113 apps/treasury/models.py:129
#: apps/treasury/models.py:115 apps/treasury/models.py:131
msgid "invoice"
msgstr "Rechnung"
#: apps/treasury/models.py:114
#: apps/treasury/models.py:116
msgid "invoices"
msgstr "Rechnungen"
#: apps/treasury/models.py:117
#: apps/treasury/models.py:119
#, python-brace-format
msgid "Invoice #{id}"
msgstr "Rechnung #{id}"
#: apps/treasury/models.py:134
#: apps/treasury/models.py:136
msgid "Designation"
msgstr "Bezeichnung"
#: apps/treasury/models.py:140
#: apps/treasury/models.py:142
msgid "Quantity"
msgstr "Qualität"
#: apps/treasury/models.py:145
#: apps/treasury/models.py:147
msgid "Unit price"
msgstr "Einzelpreis"
#: apps/treasury/models.py:161
#: apps/treasury/models.py:163
msgid "product"
msgstr "Produkt"
#: apps/treasury/models.py:162
#: apps/treasury/models.py:164
msgid "products"
msgstr "Produkten"
#: apps/treasury/models.py:182
#: apps/treasury/models.py:184
msgid "remittance type"
msgstr "Überweisungstyp"
#: apps/treasury/models.py:183
#: apps/treasury/models.py:185
msgid "remittance types"
msgstr "Überweisungstypen"
#: apps/treasury/models.py:204
#: apps/treasury/models.py:206
msgid "Comment"
msgstr "Kommentar"
#: apps/treasury/models.py:209
#: apps/treasury/models.py:211
msgid "Closed"
msgstr "Geschlossen"
#: apps/treasury/models.py:213
#: apps/treasury/models.py:215
msgid "remittance"
msgstr "Überweisung"
#: apps/treasury/models.py:214
#: apps/treasury/models.py:216
msgid "remittances"
msgstr "Überweisungen"
#: apps/treasury/models.py:247
#: apps/treasury/models.py:249
msgid "Remittance #{:d}: {}"
msgstr "Überweisung #{:d}:{}"
#: apps/treasury/models.py:271
#: apps/treasury/models.py:273
msgid "special transaction proxy"
msgstr "spezielle Transaktion Proxy"
#: apps/treasury/models.py:272
#: apps/treasury/models.py:274
msgid "special transaction proxies"
msgstr "spezielle Transaktion Proxies"
#: apps/treasury/models.py:298
#: apps/treasury/models.py:300
msgid "credit transaction"
msgstr "Kredit Transaktion"
#: apps/treasury/models.py:430
#: apps/treasury/models.py:432
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."
@ -2329,16 +2339,16 @@ msgstr ""
"Dieser Benutzer hat nicht genug Geld, um die Mitgliedschaften mit seiner "
"Note zu bezahlen."
#: apps/treasury/models.py:451
#: apps/treasury/models.py:452
#: 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:452
#: apps/treasury/models.py:453
msgid "Credits from the Société générale"
msgstr "Krediten von der Société générale"
#: apps/treasury/models.py:455
#: apps/treasury/models.py:456
#, python-brace-format
msgid "Soge credit for {user}"
msgstr "Kredit von der Société générale für {user}"
@ -2602,7 +2612,7 @@ msgid "The selected user is not validated. Please validate its account first"
msgstr ""
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
#: apps/wei/models.py:323
#: apps/wei/models.py:326
msgid "bus"
msgstr "Bus"
@ -2640,7 +2650,8 @@ msgstr "Wählen Sie die Rollen aus, an denen Sie interessiert sind."
msgid "This team doesn't belong to the given bus."
msgstr "Dieses Team gehört nicht zum angegebenen Bus."
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:35
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:38
#: apps/wei/forms/surveys/wei2023.py:38
msgid "Choose a word:"
msgstr "Wählen Sie ein Wort:"
@ -2727,40 +2738,48 @@ msgstr "Nicht binär"
msgid "gender"
msgstr "Geschlecht"
#: apps/wei/models.py:213 apps/wei/templates/wei/weimembership_form.html:58
#: apps/wei/models.py:212
msgid "Unisex"
msgstr "Unisex"
#: apps/wei/models.py:215 apps/wei/templates/wei/weimembership_form.html:58
msgid "clothing cut"
msgstr "Kleidung Schnitt"
#: apps/wei/models.py:226 apps/wei/templates/wei/weimembership_form.html:61
#: apps/wei/models.py:228 apps/wei/templates/wei/weimembership_form.html:61
msgid "clothing size"
msgstr "Kleidergröße"
#: apps/wei/models.py:232 apps/wei/templates/wei/attribute_bus_1A.html:28
#: apps/wei/models.py:234 apps/wei/templates/wei/attribute_bus_1A.html:28
#: apps/wei/templates/wei/weimembership_form.html:67
msgid "health issues"
msgstr "Gesundheitsprobleme"
#: apps/wei/models.py:237 apps/wei/templates/wei/weimembership_form.html:70
#: apps/wei/models.py:239 apps/wei/templates/wei/weimembership_form.html:70
msgid "emergency contact name"
msgstr "Notfall-Kontakt"
#: apps/wei/models.py:242 apps/wei/templates/wei/weimembership_form.html:73
#: apps/wei/models.py:240
msgid "The emergency contact must not be a WEI participant"
msgstr "Der Notfallkontakt darf kein WEI-Teilnehmer sein"
#: apps/wei/models.py:245 apps/wei/templates/wei/weimembership_form.html:73
msgid "emergency contact phone"
msgstr "Notfallkontakttelefon"
#: apps/wei/models.py:247 apps/wei/templates/wei/weimembership_form.html:52
#: apps/wei/models.py:250 apps/wei/templates/wei/weimembership_form.html:52
msgid "first year"
msgstr "Erste Jahr"
#: apps/wei/models.py:248
#: apps/wei/models.py:251
msgid "Tells if the user is new in the school."
msgstr "Gibt an, ob der USer neu in der Schule ist."
#: apps/wei/models.py:253
#: apps/wei/models.py:256
msgid "registration information"
msgstr "Registrierung Detailen"
#: apps/wei/models.py:254
#: apps/wei/models.py:257
msgid ""
"Information about the registration (buses for old members, survey for the "
"new members), encoded in JSON"
@ -2768,27 +2787,27 @@ msgstr ""
"Informationen zur Registrierung (Busse für alte Mitglieder, Umfrage für neue "
"Mitglieder), verschlüsselt in JSON"
#: apps/wei/models.py:312
#: apps/wei/models.py:315
msgid "WEI User"
msgstr "WEI User"
#: apps/wei/models.py:313
#: apps/wei/models.py:316
msgid "WEI Users"
msgstr "WEI Users"
#: apps/wei/models.py:333
#: apps/wei/models.py:336
msgid "team"
msgstr "Team"
#: apps/wei/models.py:343
#: apps/wei/models.py:346
msgid "WEI registration"
msgstr "WEI Registrierung"
#: apps/wei/models.py:347
#: apps/wei/models.py:350
msgid "WEI membership"
msgstr "WEI Mitgliedschaft"
#: apps/wei/models.py:348
#: apps/wei/models.py:351
msgid "WEI memberships"
msgstr "WEI Mitgliedschaften"
@ -3361,6 +3380,10 @@ msgstr "Kontakt"
msgid "Technical Support"
msgstr ""
#: note_kfet/templates/base.html:198
msgid "FAQ (FR)"
msgstr "FAQ (FR)"
#: note_kfet/templates/base_search.html:15
msgid "Search by attribute such as name…"
msgstr "Suche nach Attributen wie Name…"
@ -3608,10 +3631,16 @@ msgstr ""
"müssen Ihre E-Mail-Adresse auch überprüfen, indem Sie dem Link folgen, den "
"Sie erhalten haben."
#, fuzzy
#~| msgid "You already opened an account in the Société générale."
#~ msgid ""
#~ "I declare that I opened or I will open soon 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."
#~ msgid "This user didn't give her/his caution check."
#~ msgstr "Dieser User hat seine / ihre Vorsicht nicht überprüft."
#, 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 "

View File

@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-10 22:34+0200\n"
"POT-Creation-Date: 2023-08-31 13:25+0200\n"
"PO-Revision-Date: 2022-04-11 23:12+0200\n"
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
"Last-Translator: bleizi <bleizi@crans.org>\n"
"Language-Team: \n"
"Language: es\n"
"MIME-Version: 1.0\n"
@ -52,7 +52,7 @@ msgid "You can't invite more than 3 people to this activity."
msgstr "Usted no puede invitar más de 3 persona a esta actividad."
#: apps/activity/models.py:28 apps/activity/models.py:63
#: apps/member/models.py:199
#: apps/member/models.py:204
#: apps/member/templates/member/includes/club_info.html:4
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/note/models/notes.py:263 apps/note/models/transactions.py:26
@ -113,8 +113,8 @@ msgstr "Lugar donde se organiza la actividad, por ejemplo la Kfet."
msgid "type"
msgstr "tipo"
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:312
#: apps/note/models/notes.py:148 apps/treasury/models.py:287
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
#: apps/wei/templates/wei/survey.html:15
msgid "user"
@ -257,19 +257,19 @@ msgstr "Entrado el "
msgid "remove"
msgstr "quitar"
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:199
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:201
msgid "Type"
msgstr "Tipo"
#: apps/activity/tables.py:84 apps/member/forms.py:186
#: apps/registration/forms.py:91 apps/treasury/forms.py:131
#: apps/activity/tables.py:84 apps/member/forms.py:193
#: apps/registration/forms.py:93 apps/treasury/forms.py:131
#: apps/wei/forms/registration.py:104
msgid "Last name"
msgstr "Apellido"
#: apps/activity/tables.py:86 apps/member/forms.py:191
#: apps/activity/tables.py:86 apps/member/forms.py:198
#: apps/note/templates/note/transaction_form.html:138
#: apps/registration/forms.py:96 apps/treasury/forms.py:133
#: apps/registration/forms.py:98 apps/treasury/forms.py:133
#: apps/wei/forms/registration.py:109
msgid "First name"
msgstr "Nombre"
@ -495,21 +495,21 @@ msgstr "diario de cambios"
msgid "Changelog of type \"{action}\" for model {model} at {timestamp}"
msgstr ""
#: apps/member/admin.py:50 apps/member/models.py:226
#: apps/member/admin.py:50 apps/member/models.py:231
#: apps/member/templates/member/includes/club_info.html:34
msgid "membership fee (paid students)"
msgstr "pago de afiliación (estudiantes pagados)"
#: apps/member/admin.py:51 apps/member/models.py:231
#: apps/member/admin.py:51 apps/member/models.py:236
#: apps/member/templates/member/includes/club_info.html:37
msgid "membership fee (unpaid students)"
msgstr "pago de afiliación (estudiantes no pagados)"
#: apps/member/admin.py:65 apps/member/models.py:319
#: apps/member/admin.py:65 apps/member/models.py:324
msgid "roles"
msgstr "papel"
#: apps/member/admin.py:66 apps/member/models.py:333
#: apps/member/admin.py:66 apps/member/models.py:338
msgid "fee"
msgstr "pago"
@ -529,65 +529,81 @@ msgstr "Frecuencia de los informes (en días)"
msgid "Last report date"
msgstr "Fecha del último informe"
#: apps/member/forms.py:52
msgid ""
"Anti-VSS (<em>Violences Sexistes et Sexuelles</em>) charter read and approved"
msgstr ""
"Carta Anti-VSS (<em>Violences Sexistes et Sexuelles</em>) leída y aprobada"
#: apps/member/forms.py:53
msgid ""
"Tick after having read and accepted the anti-VSS charter <a "
"href=https://perso.crans.org/club-bde/Charte-anti-VSS.pdf target=_blank> "
"available here in pdf</a>"
msgstr ""
"Marque después de leer y aceptar la carta anti-VVS <a "
"href=https://perso.crans.org/club-bde/Charte-anti-VSS.pdf target=_blank> "
"disponible en pdf aquí</a>"
#: apps/member/forms.py:60
msgid "You can't register to the note if you come from the future."
msgstr "Usted no puede registrar si viene del futuro."
#: apps/member/forms.py:79
#: apps/member/forms.py:86
msgid "select an image"
msgstr "elegir una imagen"
#: apps/member/forms.py:80
#: apps/member/forms.py:87
msgid "Maximal size: 2MB"
msgstr "Tamaño máximo : 2Mo"
#: apps/member/forms.py:105
#: apps/member/forms.py:112
msgid "This image cannot be loaded."
msgstr "Esta imagen no puede ser cargada."
#: apps/member/forms.py:141 apps/member/views.py:103
#: apps/registration/forms.py:33 apps/registration/views.py:262
#: apps/member/forms.py:148 apps/member/views.py:103
#: apps/registration/forms.py:35 apps/registration/views.py:266
msgid "An alias with a similar name already exists."
msgstr "Un alias similar ya existe."
#: apps/member/forms.py:165 apps/registration/forms.py:71
#: apps/member/forms.py:172
msgid "Inscription paid by Société Générale"
msgstr "Registración pagadas por Société Générale"
#: apps/member/forms.py:167 apps/registration/forms.py:73
#: apps/member/forms.py:174
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:172 apps/registration/forms.py:78
#: apps/member/forms.py:179 apps/registration/forms.py:80
#: apps/wei/forms/registration.py:91
msgid "Credit type"
msgstr "Tipo de crédito"
#: apps/member/forms.py:173 apps/registration/forms.py:79
#: apps/member/forms.py:180 apps/registration/forms.py:81
#: apps/wei/forms/registration.py:92
msgid "No credit"
msgstr "No crédito"
#: apps/member/forms.py:175
#: apps/member/forms.py:182
msgid "You can credit the note of the user."
msgstr "Usted puede acreditar la note del usuario."
#: apps/member/forms.py:179 apps/registration/forms.py:84
#: apps/member/forms.py:186 apps/registration/forms.py:86
#: apps/wei/forms/registration.py:97
msgid "Credit amount"
msgstr "Valor del crédito"
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:144
#: apps/registration/forms.py:101 apps/treasury/forms.py:135
#: apps/member/forms.py:203 apps/note/templates/note/transaction_form.html:144
#: apps/registration/forms.py:103 apps/treasury/forms.py:135
#: apps/wei/forms/registration.py:114
msgid "Bank"
msgstr "Banco"
#: apps/member/forms.py:223
#: apps/member/forms.py:230
msgid "User"
msgstr "Usuario"
#: apps/member/forms.py:237
#: apps/member/forms.py:244
msgid "Roles"
msgstr "Papeles"
@ -772,15 +788,19 @@ msgstr "correo electrónico confirmado"
msgid "registration valid"
msgstr "registración valida"
#: apps/member/models.py:162 apps/member/models.py:163
#: apps/member/models.py:138
msgid "VSS charter read"
msgstr "Carta VSS leída"
#: apps/member/models.py:167 apps/member/models.py:168
msgid "user profile"
msgstr "perfil usuario"
#: apps/member/models.py:173
#: apps/member/models.py:178
msgid "Activate your Note Kfet account"
msgstr "Active su cuenta Note Kfet"
#: apps/member/models.py:204
#: apps/member/models.py:209
#: apps/member/templates/member/includes/club_info.html:55
#: apps/member/templates/member/includes/profile_info.html:40
#: apps/registration/templates/registration/future_profile_detail.html:22
@ -789,87 +809,87 @@ msgstr "Active su cuenta Note Kfet"
msgid "email"
msgstr "correo electrónico"
#: apps/member/models.py:211
#: apps/member/models.py:216
msgid "parent club"
msgstr "club pariente"
#: apps/member/models.py:220
#: apps/member/models.py:225
msgid "require memberships"
msgstr "necesita afiliaciones"
#: apps/member/models.py:221
#: apps/member/models.py:226
msgid "Uncheck if this club don't require memberships."
msgstr "Desmarcar si este club no usa afiliaciones."
#: apps/member/models.py:237
#: apps/member/models.py:242
#: apps/member/templates/member/includes/club_info.html:26
msgid "membership duration"
msgstr "duración de la afiliación"
#: apps/member/models.py:238
#: apps/member/models.py:243
msgid "The longest time (in days) a membership can last (NULL = infinite)."
msgstr "La duración máxima (en días) de una afiliación (NULL = infinito)."
#: apps/member/models.py:245
#: apps/member/models.py:250
#: apps/member/templates/member/includes/club_info.html:16
msgid "membership start"
msgstr "inicio de la afiliación"
#: apps/member/models.py:246
#: apps/member/models.py:251
msgid "Date from which the members can renew their membership."
msgstr "Fecha a partir de la cual los miembros pueden prorrogar su afiliación."
#: apps/member/models.py:252
#: apps/member/models.py:257
#: apps/member/templates/member/includes/club_info.html:21
msgid "membership end"
msgstr "fin de la afiliación"
#: apps/member/models.py:253
#: apps/member/models.py:258
msgid "Maximal date of a membership, after which members must renew it."
msgstr ""
"Ultima fecha de una afiliación, después de la cual los miembros tienen que "
"prorrogarla."
#: apps/member/models.py:288 apps/member/models.py:313
#: apps/member/models.py:293 apps/member/models.py:318
#: apps/note/models/notes.py:176
msgid "club"
msgstr "club"
#: apps/member/models.py:289
#: apps/member/models.py:294
msgid "clubs"
msgstr "clubs"
#: apps/member/models.py:324
#: apps/member/models.py:329
msgid "membership starts on"
msgstr "afiliación empezá el"
#: apps/member/models.py:328
#: apps/member/models.py:333
msgid "membership ends on"
msgstr "afiliación termina el"
#: apps/member/models.py:430
#: apps/member/models.py:435
#, python-brace-format
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:439 apps/member/views.py:712
#: apps/member/models.py:444 apps/member/views.py:712
msgid "User is already a member of the club"
msgstr "Usuario ya esta un miembro del club"
#: apps/member/models.py:451 apps/member/views.py:721
#: apps/member/models.py:456 apps/member/views.py:721
msgid "User is not a member of the parent club"
msgstr "Usuario no es un miembro del club pariente"
#: apps/member/models.py:504
#: apps/member/models.py:509
#, python-brace-format
msgid "Membership of {user} for the club {club}"
msgstr "Afiliación of {user} for the club {club}"
#: apps/member/models.py:507 apps/note/models/transactions.py:389
#: apps/member/models.py:512 apps/note/models/transactions.py:389
msgid "membership"
msgstr "afiliación"
#: apps/member/models.py:508
#: apps/member/models.py:513
msgid "memberships"
msgstr "afiliaciones"
@ -1180,7 +1200,7 @@ msgstr "Guardar cambios"
msgid "Registrations"
msgstr "Registraciones"
#: apps/member/views.py:73 apps/registration/forms.py:23
#: apps/member/views.py:73 apps/registration/forms.py:24
msgid "This address must be valid."
msgstr "Este correo tiene que ser valido."
@ -1236,11 +1256,11 @@ msgstr "La afiliación tiene que empezar después del {:%d-%m-%Y}."
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:876
#: apps/member/views.py:880
msgid "Manage roles of an user in the club"
msgstr "Gestionar los papeles de un usuario en el club"
#: apps/member/views.py:901
#: apps/member/views.py:905
msgid "Members of the club"
msgstr "Miembros del club"
@ -1557,7 +1577,7 @@ msgstr "Transacciones especiales"
msgid "membership transaction"
msgstr "transacción de afiliación"
#: apps/note/models/transactions.py:385 apps/treasury/models.py:292
#: apps/note/models/transactions.py:385 apps/treasury/models.py:294
msgid "membership transactions"
msgstr "transacciones de afiliación"
@ -1676,7 +1696,7 @@ msgid "Amount"
msgstr "Monto"
#: apps/note/templates/note/transaction_form.html:132
#: apps/treasury/models.py:54
#: apps/treasury/models.py:56
msgid "Name"
msgstr "Nombre"
@ -1845,7 +1865,7 @@ msgstr ""
msgid "for club"
msgstr "interesa el club"
#: apps/permission/models.py:350 apps/permission/models.py:351
#: apps/permission/models.py:351 apps/permission/models.py:352
msgid "role permissions"
msgstr "permisos por papeles"
@ -1963,31 +1983,15 @@ msgstr "Todos los permisos"
msgid "registration"
msgstr "afiliación"
#: apps/registration/forms.py:39
#: apps/registration/forms.py:41
msgid "This email address is already used."
msgstr "Este correo electrónico ya esta utilizado."
#: apps/registration/forms.py:49
msgid ""
"I declare that I opened or I will open soon a bank account in the Société "
"générale with the BDE partnership."
msgstr ""
"Declaro que ya abrió una cuenta a la Société Générale en colaboración con el "
"BDE."
#: apps/registration/forms.py:51
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 ""
"Cuidado : esto le obliga abrir su cuenta bancaria. Si cambia de idea y no "
"abre su cuenta bancaria, tendrá que pagar su afiliación al BDE."
#: apps/registration/forms.py:59
#: apps/registration/forms.py:61
msgid "Register to the WEI"
msgstr "Registrarse en el WEI"
#: apps/registration/forms.py:61
#: apps/registration/forms.py:63
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."
@ -1995,14 +1999,18 @@ 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:106
#: apps/registration/forms.py:108
msgid "Join BDE Club"
msgstr "Afiliarse al club BDE"
#: apps/registration/forms.py:113
#: apps/registration/forms.py:115
msgid "Join Kfet Club"
msgstr "Afiliarse al club Kfet"
#: apps/registration/forms.py:124
msgid "Join BDA Club"
msgstr "Afiliarse al club BDA"
#: apps/registration/templates/registration/email_validation_complete.html:15
msgid "Your email have successfully been validated."
msgstr "Su correo electrónico fue validado con éxito."
@ -2051,12 +2059,12 @@ msgstr "Suprimir afiliación"
msgid "Validate account"
msgstr "Validar la cuenta"
#: apps/registration/templates/registration/future_profile_detail.html:62
#: apps/registration/templates/registration/future_profile_detail.html:63
msgid ""
"The user declared that he/she opened a bank account in the Société générale."
msgstr "El usuario declara que ya abrió una cuenta a la Société Générale."
#: apps/registration/templates/registration/future_profile_detail.html:71
#: apps/registration/templates/registration/future_profile_detail.html:73
#: apps/wei/templates/wei/weimembership_form.html:127
#: apps/wei/templates/wei/weimembership_form.html:186
msgid "Validate registration"
@ -2108,54 +2116,54 @@ msgstr "Gracias"
msgid "The Note Kfet team."
msgstr "El equipo Note Kfet."
#: apps/registration/views.py:40
#: apps/registration/views.py:41
msgid "Register new user"
msgstr "Registrar un nuevo usuario"
#: apps/registration/views.py:98
#: apps/registration/views.py:99
msgid "Email validation"
msgstr "Validación del correo electrónico"
#: apps/registration/views.py:100
#: apps/registration/views.py:101
msgid "Validate email"
msgstr "Validar el correo electrónico"
#: apps/registration/views.py:144
#: apps/registration/views.py:145
msgid "Email validation unsuccessful"
msgstr "La validación del correo electrónico fracasó"
#: apps/registration/views.py:155
#: apps/registration/views.py:156
msgid "Email validation email sent"
msgstr "Correo de validación enviado"
#: apps/registration/views.py:163
#: apps/registration/views.py:164
msgid "Resend email validation link"
msgstr "Reenviar el enlace de validación"
#: apps/registration/views.py:181
#: apps/registration/views.py:182
msgid "Pre-registered users list"
msgstr "Lista de los usuarios con afiliación pendiente"
#: apps/registration/views.py:205
#: apps/registration/views.py:206
msgid "Unregistered users"
msgstr "Usuarios con afiliación pendiente"
#: apps/registration/views.py:218
#: apps/registration/views.py:219
msgid "Registration detail"
msgstr "Detalles de la afiliación"
#: apps/registration/views.py:282
#: apps/registration/views.py:293
msgid "You must join the BDE."
msgstr "Usted tiene que afiliarse al BDE."
#: apps/registration/views.py:306
#: apps/registration/views.py:323
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:387
#: apps/registration/views.py:417
msgid "Invalidate pre-registration"
msgstr "Invalidar la afiliación"
@ -2163,7 +2171,7 @@ msgstr "Invalidar la afiliación"
msgid "Treasury"
msgstr "Tesorería"
#: apps/treasury/forms.py:26 apps/treasury/models.py:93
#: apps/treasury/forms.py:26 apps/treasury/models.py:95
#: apps/treasury/templates/treasury/invoice_form.html:22
msgid "This invoice is locked and can no longer be edited."
msgstr "Esta factura esta bloqueada y no puede ser modificada."
@ -2176,7 +2184,7 @@ msgstr "El descuento ya esta cerrado."
msgid "You can't change the type of the remittance."
msgstr "No puede cambiar el tipo de descuento."
#: apps/treasury/forms.py:125 apps/treasury/models.py:267
#: apps/treasury/forms.py:125 apps/treasury/models.py:269
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
#: apps/treasury/templates/treasury/invoice_list.html:16
#: apps/treasury/templates/treasury/remittance_list.html:16
@ -2192,116 +2200,116 @@ msgstr "No hay descuento relacionado"
msgid "Invoice identifier"
msgstr "Numero de factura"
#: apps/treasury/models.py:40
#: apps/treasury/models.py:42
msgid "BDE"
msgstr "BDE"
#: apps/treasury/models.py:45
#: apps/treasury/models.py:47
msgid "Object"
msgstr "Asunto"
#: apps/treasury/models.py:49
#: apps/treasury/models.py:51
msgid "Description"
msgstr "Descripción"
#: apps/treasury/models.py:58
#: apps/treasury/models.py:60
msgid "Address"
msgstr "Dirección"
#: apps/treasury/models.py:63 apps/treasury/models.py:193
#: apps/treasury/models.py:65 apps/treasury/models.py:195
msgid "Date"
msgstr "Fecha"
#: apps/treasury/models.py:67
#: apps/treasury/models.py:69
msgid "Acquitted"
msgstr "Pagada"
#: apps/treasury/models.py:72
#: apps/treasury/models.py:74
msgid "Locked"
msgstr "Bloqueada"
#: apps/treasury/models.py:73
#: apps/treasury/models.py:75
msgid "An invoice can't be edited when it is locked."
msgstr "Une factura no puede ser modificada cuando esta bloqueada."
#: apps/treasury/models.py:79
#: apps/treasury/models.py:81
msgid "tex source"
msgstr "código fuente TeX"
#: apps/treasury/models.py:113 apps/treasury/models.py:129
#: apps/treasury/models.py:115 apps/treasury/models.py:131
msgid "invoice"
msgstr "factura"
#: apps/treasury/models.py:114
#: apps/treasury/models.py:116
msgid "invoices"
msgstr "facturas"
#: apps/treasury/models.py:117
#: apps/treasury/models.py:119
#, python-brace-format
msgid "Invoice #{id}"
msgstr "Factura n°{id}"
#: apps/treasury/models.py:134
#: apps/treasury/models.py:136
msgid "Designation"
msgstr "Designación"
#: apps/treasury/models.py:140
#: apps/treasury/models.py:142
msgid "Quantity"
msgstr "Cantidad"
#: apps/treasury/models.py:145
#: apps/treasury/models.py:147
msgid "Unit price"
msgstr "Precio unitario"
#: apps/treasury/models.py:161
#: apps/treasury/models.py:163
msgid "product"
msgstr "producto"
#: apps/treasury/models.py:162
#: apps/treasury/models.py:164
msgid "products"
msgstr "productos"
#: apps/treasury/models.py:182
#: apps/treasury/models.py:184
msgid "remittance type"
msgstr "tipo de descuento"
#: apps/treasury/models.py:183
#: apps/treasury/models.py:185
msgid "remittance types"
msgstr "tipos de descuentos"
#: apps/treasury/models.py:204
#: apps/treasury/models.py:206
msgid "Comment"
msgstr "Comentario"
#: apps/treasury/models.py:209
#: apps/treasury/models.py:211
msgid "Closed"
msgstr "Cerrada"
#: apps/treasury/models.py:213
#: apps/treasury/models.py:215
msgid "remittance"
msgstr "descuento"
#: apps/treasury/models.py:214
#: apps/treasury/models.py:216
msgid "remittances"
msgstr "descuentos"
#: apps/treasury/models.py:247
#: apps/treasury/models.py:249
msgid "Remittance #{:d}: {}"
msgstr "Descuento n°{:d} : {}"
#: apps/treasury/models.py:271
#: apps/treasury/models.py:273
msgid "special transaction proxy"
msgstr "proxy de transacción especial"
#: apps/treasury/models.py:272
#: apps/treasury/models.py:274
msgid "special transaction proxies"
msgstr "proxys de transacciones especiales"
#: apps/treasury/models.py:298
#: apps/treasury/models.py:300
msgid "credit transaction"
msgstr "transacción de crédito"
#: apps/treasury/models.py:430
#: apps/treasury/models.py:432
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."
@ -2310,16 +2318,16 @@ msgstr ""
"afiliaciones. Por favor pídelo acreditar su note antes de invalidar este "
"crédito."
#: apps/treasury/models.py:451
#: apps/treasury/models.py:452
#: 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:452
#: apps/treasury/models.py:453
msgid "Credits from the Société générale"
msgstr "Créditos de la Société Générale"
#: apps/treasury/models.py:455
#: apps/treasury/models.py:456
#, python-brace-format
msgid "Soge credit for {user}"
msgstr "Crédito de la Société Générale para {user}"
@ -2574,7 +2582,7 @@ msgstr ""
"El usuario seleccionado no ha sido validado. Validar esta cuenta primero"
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
#: apps/wei/models.py:323
#: apps/wei/models.py:326
msgid "bus"
msgstr "bus"
@ -2612,7 +2620,8 @@ msgstr "Elegir los papeles que le interesa."
msgid "This team doesn't belong to the given bus."
msgstr "Este equipo no pertenece al bus dado."
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:35
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:38
#: apps/wei/forms/surveys/wei2023.py:38
msgid "Choose a word:"
msgstr "Elegir una palabra :"
@ -2699,40 +2708,48 @@ msgstr "No binari@"
msgid "gender"
msgstr "género"
#: apps/wei/models.py:213 apps/wei/templates/wei/weimembership_form.html:58
#: apps/wei/models.py:212
msgid "Unisex"
msgstr "Unisex"
#: apps/wei/models.py:215 apps/wei/templates/wei/weimembership_form.html:58
msgid "clothing cut"
msgstr "forma de ropa"
#: apps/wei/models.py:226 apps/wei/templates/wei/weimembership_form.html:61
#: apps/wei/models.py:228 apps/wei/templates/wei/weimembership_form.html:61
msgid "clothing size"
msgstr "medida de ropa"
#: apps/wei/models.py:232 apps/wei/templates/wei/attribute_bus_1A.html:28
#: apps/wei/models.py:234 apps/wei/templates/wei/attribute_bus_1A.html:28
#: apps/wei/templates/wei/weimembership_form.html:67
msgid "health issues"
msgstr "problemas de salud"
#: apps/wei/models.py:237 apps/wei/templates/wei/weimembership_form.html:70
#: apps/wei/models.py:239 apps/wei/templates/wei/weimembership_form.html:70
msgid "emergency contact name"
msgstr "nombre del contacto de emergencia"
#: apps/wei/models.py:242 apps/wei/templates/wei/weimembership_form.html:73
#: apps/wei/models.py:240
msgid "The emergency contact must not be a WEI participant"
msgstr "El contacto de emergencia no debe ser un participante de WEI"
#: apps/wei/models.py:245 apps/wei/templates/wei/weimembership_form.html:73
msgid "emergency contact phone"
msgstr "teléfono del contacto de emergencia"
#: apps/wei/models.py:247 apps/wei/templates/wei/weimembership_form.html:52
#: apps/wei/models.py:250 apps/wei/templates/wei/weimembership_form.html:52
msgid "first year"
msgstr "primer año"
#: apps/wei/models.py:248
#: apps/wei/models.py:251
msgid "Tells if the user is new in the school."
msgstr "Indica si el usuario es nuevo en la escuela."
#: apps/wei/models.py:253
#: apps/wei/models.py:256
msgid "registration information"
msgstr "informaciones sobre la afiliación"
#: apps/wei/models.py:254
#: apps/wei/models.py:257
msgid ""
"Information about the registration (buses for old members, survey for the "
"new members), encoded in JSON"
@ -2740,27 +2757,27 @@ msgstr ""
"Informaciones sobre la afiliacion (bus para miembros ancianos, cuestionario "
"para los nuevos miembros), registrado en JSON"
#: apps/wei/models.py:312
#: apps/wei/models.py:315
msgid "WEI User"
msgstr "Participante WEI"
#: apps/wei/models.py:313
#: apps/wei/models.py:316
msgid "WEI Users"
msgstr "Participantes WEI"
#: apps/wei/models.py:333
#: apps/wei/models.py:336
msgid "team"
msgstr "equipo"
#: apps/wei/models.py:343
#: apps/wei/models.py:346
msgid "WEI registration"
msgstr "Apuntación al WEI"
#: apps/wei/models.py:347
#: apps/wei/models.py:350
msgid "WEI membership"
msgstr "Afiliación al WEI"
#: apps/wei/models.py:348
#: apps/wei/models.py:351
msgid "WEI memberships"
msgstr "Afiliaciones al WEI"
@ -3316,6 +3333,10 @@ msgstr "Contactarnos"
msgid "Technical Support"
msgstr "Soporte técnico"
#: note_kfet/templates/base.html:198
msgid "FAQ (FR)"
msgstr "FAQ (FR)"
#: note_kfet/templates/base_search.html:15
msgid "Search by attribute such as name…"
msgstr "Buscar con atributo, como el nombre…"
@ -3537,6 +3558,21 @@ msgstr ""
"pagar su afiliación. Tambien tiene que validar su correo electronico con el "
"enlace que recibió."
#~ msgid ""
#~ "I declare that I opened or I will open soon a bank account in the Société "
#~ "générale with the BDE partnership."
#~ msgstr ""
#~ "Declaro que ya abrió una cuenta a la Société Générale en colaboración con "
#~ "el BDE."
#~ 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 ""
#~ "Cuidado : esto le obliga abrir su cuenta bancaria. Si cambia de idea y no "
#~ "abre su cuenta bancaria, tendrá que pagar su afiliación al BDE."
#~ msgid "You are not a Kfet member, so you can't use your note account."
#~ msgstr "Usted no es un miembro de la Kfet, no puede usar su cuenta note."

View File

@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-10 22:34+0200\n"
"POT-Creation-Date: 2023-08-31 13:25+0200\n"
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
"Last-Translator: bleizi <bleizi@crans.org>\n"
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
@ -53,7 +53,7 @@ msgid "You can't invite more than 3 people to this activity."
msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité."
#: apps/activity/models.py:28 apps/activity/models.py:63
#: apps/member/models.py:199
#: apps/member/models.py:204
#: apps/member/templates/member/includes/club_info.html:4
#: apps/member/templates/member/includes/profile_info.html:4
#: apps/note/models/notes.py:263 apps/note/models/transactions.py:26
@ -114,8 +114,8 @@ msgstr "Lieu où l'activité est organisée, par exemple la Kfet."
msgid "type"
msgstr "type"
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:312
#: apps/note/models/notes.py:148 apps/treasury/models.py:287
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
#: apps/wei/templates/wei/survey.html:15
msgid "user"
@ -258,19 +258,19 @@ msgstr "Entré le "
msgid "remove"
msgstr "supprimer"
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:199
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:201
msgid "Type"
msgstr "Type"
#: apps/activity/tables.py:84 apps/member/forms.py:186
#: apps/registration/forms.py:91 apps/treasury/forms.py:131
#: apps/activity/tables.py:84 apps/member/forms.py:193
#: apps/registration/forms.py:93 apps/treasury/forms.py:131
#: apps/wei/forms/registration.py:104
msgid "Last name"
msgstr "Nom de famille"
#: apps/activity/tables.py:86 apps/member/forms.py:191
#: apps/activity/tables.py:86 apps/member/forms.py:198
#: apps/note/templates/note/transaction_form.html:138
#: apps/registration/forms.py:96 apps/treasury/forms.py:133
#: apps/registration/forms.py:98 apps/treasury/forms.py:133
#: apps/wei/forms/registration.py:109
msgid "First name"
msgstr "Prénom"
@ -497,21 +497,21 @@ msgstr "journaux de modifications"
msgid "Changelog of type \"{action}\" for model {model} at {timestamp}"
msgstr "Changelog de type « {action} » pour le modèle {model} à {timestamp}"
#: apps/member/admin.py:50 apps/member/models.py:226
#: apps/member/admin.py:50 apps/member/models.py:231
#: apps/member/templates/member/includes/club_info.html:34
msgid "membership fee (paid students)"
msgstr "cotisation pour adhérer (normalien élève)"
#: apps/member/admin.py:51 apps/member/models.py:231
#: apps/member/admin.py:51 apps/member/models.py:236
#: apps/member/templates/member/includes/club_info.html:37
msgid "membership fee (unpaid students)"
msgstr "cotisation pour adhérer (normalien étudiant)"
#: apps/member/admin.py:65 apps/member/models.py:319
#: apps/member/admin.py:65 apps/member/models.py:324
msgid "roles"
msgstr "rôles"
#: apps/member/admin.py:66 apps/member/models.py:333
#: apps/member/admin.py:66 apps/member/models.py:338
msgid "fee"
msgstr "cotisation"
@ -531,65 +531,81 @@ msgstr "Fréquence des rapports (en jours)"
msgid "Last report date"
msgstr "Date de dernier rapport"
#: apps/member/forms.py:52
msgid ""
"Anti-VSS (<em>Violences Sexistes et Sexuelles</em>) charter read and approved"
msgstr ""
"Charte Anti-VSS (Violences Sexistes et Sexuelles) lue et approuvée"
#: apps/member/forms.py:53
msgid ""
"Tick after having read and accepted the anti-VSS charter <a "
"href=https://perso.crans.org/club-bde/Charte-anti-VSS.pdf target=_blank> "
"available here in pdf</a>"
msgstr ""
"Cochez après avoir lu la chartre anti-VSS <a "
"href=https://perso.crans.org/club-bde/Charte-anti-VSS.pdf target=_blank> "
"disponible en pdf ici</a>"
#: apps/member/forms.py:60
msgid "You can't register to the note if you come from the future."
msgstr "Vous ne pouvez pas vous inscrire à la note si vous venez du futur."
#: apps/member/forms.py:79
#: apps/member/forms.py:86
msgid "select an image"
msgstr "choisissez une image"
#: apps/member/forms.py:80
#: apps/member/forms.py:87
msgid "Maximal size: 2MB"
msgstr "Taille maximale : 2 Mo"
#: apps/member/forms.py:105
#: apps/member/forms.py:112
msgid "This image cannot be loaded."
msgstr "Cette image ne peut pas être chargée."
#: apps/member/forms.py:141 apps/member/views.py:103
#: apps/registration/forms.py:33 apps/registration/views.py:262
#: apps/member/forms.py:148 apps/member/views.py:103
#: apps/registration/forms.py:35 apps/registration/views.py:266
msgid "An alias with a similar name already exists."
msgstr "Un alias avec un nom similaire existe déjà."
#: apps/member/forms.py:165 apps/registration/forms.py:71
#: apps/member/forms.py:172
msgid "Inscription paid by Société Générale"
msgstr "Inscription payée par la Société générale"
#: apps/member/forms.py:167 apps/registration/forms.py:73
#: apps/member/forms.py:174
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:172 apps/registration/forms.py:78
#: apps/member/forms.py:179 apps/registration/forms.py:80
#: apps/wei/forms/registration.py:91
msgid "Credit type"
msgstr "Type de rechargement"
#: apps/member/forms.py:173 apps/registration/forms.py:79
#: apps/member/forms.py:180 apps/registration/forms.py:81
#: apps/wei/forms/registration.py:92
msgid "No credit"
msgstr "Pas de rechargement"
#: apps/member/forms.py:175
#: apps/member/forms.py:182
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:179 apps/registration/forms.py:84
#: apps/member/forms.py:186 apps/registration/forms.py:86
#: apps/wei/forms/registration.py:97
msgid "Credit amount"
msgstr "Montant à créditer"
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:144
#: apps/registration/forms.py:101 apps/treasury/forms.py:135
#: apps/member/forms.py:203 apps/note/templates/note/transaction_form.html:144
#: apps/registration/forms.py:103 apps/treasury/forms.py:135
#: apps/wei/forms/registration.py:114
msgid "Bank"
msgstr "Banque"
#: apps/member/forms.py:223
#: apps/member/forms.py:230
msgid "User"
msgstr "Utilisateur"
#: apps/member/forms.py:237
#: apps/member/forms.py:244
msgid "Roles"
msgstr "Rôles"
@ -774,15 +790,19 @@ msgstr "adresse email confirmée"
msgid "registration valid"
msgstr "inscription valide"
#: apps/member/models.py:162 apps/member/models.py:163
#: apps/member/models.py:138
msgid "VSS charter read"
msgstr "Charte VSS lue"
#: apps/member/models.py:167 apps/member/models.py:168
msgid "user profile"
msgstr "profil utilisateur"
#: apps/member/models.py:173
#: apps/member/models.py:178
msgid "Activate your Note Kfet account"
msgstr "Activez votre compte Note Kfet"
#: apps/member/models.py:204
#: apps/member/models.py:209
#: apps/member/templates/member/includes/club_info.html:55
#: apps/member/templates/member/includes/profile_info.html:40
#: apps/registration/templates/registration/future_profile_detail.html:22
@ -791,88 +811,88 @@ msgstr "Activez votre compte Note Kfet"
msgid "email"
msgstr "courriel"
#: apps/member/models.py:211
#: apps/member/models.py:216
msgid "parent club"
msgstr "club parent"
#: apps/member/models.py:220
#: apps/member/models.py:225
msgid "require memberships"
msgstr "nécessite des adhésions"
#: apps/member/models.py:221
#: apps/member/models.py:226
msgid "Uncheck if this club don't require memberships."
msgstr "Décochez si ce club n'utilise pas d'adhésions."
#: apps/member/models.py:237
#: apps/member/models.py:242
#: apps/member/templates/member/includes/club_info.html:26
msgid "membership duration"
msgstr "durée de l'adhésion"
#: apps/member/models.py:238
#: apps/member/models.py:243
msgid "The longest time (in days) a membership can last (NULL = infinite)."
msgstr "La durée maximale (en jours) d'une adhésion (NULL = infinie)."
#: apps/member/models.py:245
#: apps/member/models.py:250
#: apps/member/templates/member/includes/club_info.html:16
msgid "membership start"
msgstr "début de l'adhésion"
#: apps/member/models.py:246
#: apps/member/models.py:251
msgid "Date from which the members can renew their membership."
msgstr ""
"Date à partir de laquelle les adhérents peuvent renouveler leur adhésion."
#: apps/member/models.py:252
#: apps/member/models.py:257
#: apps/member/templates/member/includes/club_info.html:21
msgid "membership end"
msgstr "fin de l'adhésion"
#: apps/member/models.py:253
#: apps/member/models.py:258
msgid "Maximal date of a membership, after which members must renew it."
msgstr ""
"Date maximale d'une fin d'adhésion, après laquelle les adhérents doivent la "
"renouveler."
#: apps/member/models.py:288 apps/member/models.py:313
#: apps/member/models.py:293 apps/member/models.py:318
#: apps/note/models/notes.py:176
msgid "club"
msgstr "club"
#: apps/member/models.py:289
#: apps/member/models.py:294
msgid "clubs"
msgstr "clubs"
#: apps/member/models.py:324
#: apps/member/models.py:329
msgid "membership starts on"
msgstr "l'adhésion commence le"
#: apps/member/models.py:328
#: apps/member/models.py:333
msgid "membership ends on"
msgstr "l'adhésion finit le"
#: apps/member/models.py:430
#: apps/member/models.py:435
#, python-brace-format
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:439 apps/member/views.py:712
#: apps/member/models.py:444 apps/member/views.py:712
msgid "User is already a member of the club"
msgstr "L'utilisateur est déjà membre du club"
#: apps/member/models.py:451 apps/member/views.py:721
#: apps/member/models.py:456 apps/member/views.py:721
msgid "User is not a member of the parent club"
msgstr "L'utilisateur n'est pas membre du club parent"
#: apps/member/models.py:504
#: apps/member/models.py:509
#, python-brace-format
msgid "Membership of {user} for the club {club}"
msgstr "Adhésion de {user} pour le club {club}"
#: apps/member/models.py:507 apps/note/models/transactions.py:389
#: apps/member/models.py:512 apps/note/models/transactions.py:389
msgid "membership"
msgstr "adhésion"
#: apps/member/models.py:508
#: apps/member/models.py:513
msgid "memberships"
msgstr "adhésions"
@ -1183,7 +1203,7 @@ msgstr "Sauvegarder les changements"
msgid "Registrations"
msgstr "Inscriptions"
#: apps/member/views.py:73 apps/registration/forms.py:23
#: apps/member/views.py:73 apps/registration/forms.py:24
msgid "This address must be valid."
msgstr "Cette adresse doit être valide."
@ -1239,11 +1259,11 @@ msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}."
msgid "The membership must begin before {:%m-%d-%Y}."
msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
#: apps/member/views.py:876
#: apps/member/views.py:880
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:901
#: apps/member/views.py:905
msgid "Members of the club"
msgstr "Membres du club"
@ -1563,7 +1583,7 @@ msgstr "Transactions de crédit/retrait"
msgid "membership transaction"
msgstr "transaction d'adhésion"
#: apps/note/models/transactions.py:385 apps/treasury/models.py:292
#: apps/note/models/transactions.py:385 apps/treasury/models.py:294
msgid "membership transactions"
msgstr "transactions d'adhésion"
@ -1682,7 +1702,7 @@ msgid "Amount"
msgstr "Montant"
#: apps/note/templates/note/transaction_form.html:132
#: apps/treasury/models.py:54
#: apps/treasury/models.py:56
msgid "Name"
msgstr "Nom"
@ -1851,7 +1871,7 @@ msgstr ""
msgid "for club"
msgstr "s'applique au club"
#: apps/permission/models.py:350 apps/permission/models.py:351
#: apps/permission/models.py:351 apps/permission/models.py:352
msgid "role permissions"
msgstr "permissions par rôles"
@ -1972,31 +1992,15 @@ msgstr "Tous les droits"
msgid "registration"
msgstr "inscription"
#: apps/registration/forms.py:39
#: apps/registration/forms.py:41
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 or I will open soon a bank account in the Société "
"générale with the BDE partnership."
msgstr ""
"Je déclare avoir ouvert ou ouvrir prochainement un compte à la société "
"générale avec le partenariat du BDE."
#: apps/registration/forms.py:51
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:59
#: apps/registration/forms.py:61
msgid "Register to the WEI"
msgstr "S'inscrire au WEI"
#: apps/registration/forms.py:61
#: apps/registration/forms.py:63
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."
@ -2005,14 +2009,18 @@ msgstr ""
"pourrez toujours vous inscrire plus tard, après avoir validé votre compte à "
"la Kfet."
#: apps/registration/forms.py:106
#: apps/registration/forms.py:108
msgid "Join BDE Club"
msgstr "Adhérer au club BDE"
#: apps/registration/forms.py:113
#: apps/registration/forms.py:115
msgid "Join Kfet Club"
msgstr "Adhérer au club Kfet"
#: apps/registration/forms.py:124
msgid "Join BDA Club"
msgstr "Adhérer au club BDA"
#: apps/registration/templates/registration/email_validation_complete.html:15
msgid "Your email have successfully been validated."
msgstr "Votre adresse e-mail a bien été validée."
@ -2061,12 +2069,12 @@ msgstr "Supprimer l'inscription"
msgid "Validate account"
msgstr "Valider le compte"
#: apps/registration/templates/registration/future_profile_detail.html:62
#: apps/registration/templates/registration/future_profile_detail.html:63
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/registration/templates/registration/future_profile_detail.html:73
#: apps/wei/templates/wei/weimembership_form.html:127
#: apps/wei/templates/wei/weimembership_form.html:186
msgid "Validate registration"
@ -2116,54 +2124,54 @@ msgstr "Merci"
msgid "The Note Kfet team."
msgstr "L'équipe de la Note Kfet."
#: apps/registration/views.py:40
#: apps/registration/views.py:41
msgid "Register new user"
msgstr "Enregistrer un nouvel utilisateur"
#: apps/registration/views.py:98
#: apps/registration/views.py:99
msgid "Email validation"
msgstr "Validation de l'adresse mail"
#: apps/registration/views.py:100
#: apps/registration/views.py:101
msgid "Validate email"
msgstr "Valider l'adresse e-mail"
#: apps/registration/views.py:144
#: apps/registration/views.py:145
msgid "Email validation unsuccessful"
msgstr "La validation de l'adresse mail a échoué"
#: apps/registration/views.py:155
#: apps/registration/views.py:156
msgid "Email validation email sent"
msgstr "L'email de vérification de l'adresse email a bien été envoyé"
#: apps/registration/views.py:163
#: apps/registration/views.py:164
msgid "Resend email validation link"
msgstr "Renvoyer le lien de validation"
#: apps/registration/views.py:181
#: apps/registration/views.py:182
msgid "Pre-registered users list"
msgstr "Liste des utilisateurs en attente d'inscription"
#: apps/registration/views.py:205
#: apps/registration/views.py:206
msgid "Unregistered users"
msgstr "Utilisateurs en attente d'inscription"
#: apps/registration/views.py:218
#: apps/registration/views.py:219
msgid "Registration detail"
msgstr "Détails de l'inscription"
#: apps/registration/views.py:282
#: apps/registration/views.py:293
msgid "You must join the BDE."
msgstr "Vous devez adhérer au BDE."
#: apps/registration/views.py:306
#: apps/registration/views.py:323
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:387
#: apps/registration/views.py:417
msgid "Invalidate pre-registration"
msgstr "Invalider l'inscription"
@ -2171,7 +2179,7 @@ msgstr "Invalider l'inscription"
msgid "Treasury"
msgstr "Trésorerie"
#: apps/treasury/forms.py:26 apps/treasury/models.py:93
#: apps/treasury/forms.py:26 apps/treasury/models.py:95
#: apps/treasury/templates/treasury/invoice_form.html:22
msgid "This invoice is locked and can no longer be edited."
msgstr "Cette facture est verrouillée et ne peut plus être éditée."
@ -2184,7 +2192,7 @@ msgstr "La remise est déjà fermée."
msgid "You can't change the type of the remittance."
msgstr "Vous ne pouvez pas changer le type de la remise."
#: apps/treasury/forms.py:125 apps/treasury/models.py:267
#: apps/treasury/forms.py:125 apps/treasury/models.py:269
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
#: apps/treasury/templates/treasury/invoice_list.html:16
#: apps/treasury/templates/treasury/remittance_list.html:16
@ -2200,116 +2208,116 @@ msgstr "Pas de remise associée"
msgid "Invoice identifier"
msgstr "Numéro de facture"
#: apps/treasury/models.py:40
#: apps/treasury/models.py:42
msgid "BDE"
msgstr "BDE"
#: apps/treasury/models.py:45
#: apps/treasury/models.py:47
msgid "Object"
msgstr "Objet"
#: apps/treasury/models.py:49
#: apps/treasury/models.py:51
msgid "Description"
msgstr "Description"
#: apps/treasury/models.py:58
#: apps/treasury/models.py:60
msgid "Address"
msgstr "Adresse"
#: apps/treasury/models.py:63 apps/treasury/models.py:193
#: apps/treasury/models.py:65 apps/treasury/models.py:195
msgid "Date"
msgstr "Date"
#: apps/treasury/models.py:67
#: apps/treasury/models.py:69
msgid "Acquitted"
msgstr "Acquittée"
#: apps/treasury/models.py:72
#: apps/treasury/models.py:74
msgid "Locked"
msgstr "Verrouillée"
#: apps/treasury/models.py:73
#: apps/treasury/models.py:75
msgid "An invoice can't be edited when it is locked."
msgstr "Une facture ne peut plus être modifiée si elle est verrouillée."
#: apps/treasury/models.py:79
#: apps/treasury/models.py:81
msgid "tex source"
msgstr "fichier TeX source"
#: apps/treasury/models.py:113 apps/treasury/models.py:129
#: apps/treasury/models.py:115 apps/treasury/models.py:131
msgid "invoice"
msgstr "facture"
#: apps/treasury/models.py:114
#: apps/treasury/models.py:116
msgid "invoices"
msgstr "factures"
#: apps/treasury/models.py:117
#: apps/treasury/models.py:119
#, python-brace-format
msgid "Invoice #{id}"
msgstr "Facture n°{id}"
#: apps/treasury/models.py:134
#: apps/treasury/models.py:136
msgid "Designation"
msgstr "Désignation"
#: apps/treasury/models.py:140
#: apps/treasury/models.py:142
msgid "Quantity"
msgstr "Quantité"
#: apps/treasury/models.py:145
#: apps/treasury/models.py:147
msgid "Unit price"
msgstr "Prix unitaire"
#: apps/treasury/models.py:161
#: apps/treasury/models.py:163
msgid "product"
msgstr "produit"
#: apps/treasury/models.py:162
#: apps/treasury/models.py:164
msgid "products"
msgstr "produits"
#: apps/treasury/models.py:182
#: apps/treasury/models.py:184
msgid "remittance type"
msgstr "type de remise"
#: apps/treasury/models.py:183
#: apps/treasury/models.py:185
msgid "remittance types"
msgstr "types de remises"
#: apps/treasury/models.py:204
#: apps/treasury/models.py:206
msgid "Comment"
msgstr "Commentaire"
#: apps/treasury/models.py:209
#: apps/treasury/models.py:211
msgid "Closed"
msgstr "Fermée"
#: apps/treasury/models.py:213
#: apps/treasury/models.py:215
msgid "remittance"
msgstr "remise"
#: apps/treasury/models.py:214
#: apps/treasury/models.py:216
msgid "remittances"
msgstr "remises"
#: apps/treasury/models.py:247
#: apps/treasury/models.py:249
msgid "Remittance #{:d}: {}"
msgstr "Remise n°{:d} : {}"
#: apps/treasury/models.py:271
#: apps/treasury/models.py:273
msgid "special transaction proxy"
msgstr "proxy de transaction spéciale"
#: apps/treasury/models.py:272
#: apps/treasury/models.py:274
msgid "special transaction proxies"
msgstr "proxys de transactions spéciales"
#: apps/treasury/models.py:298
#: apps/treasury/models.py:300
msgid "credit transaction"
msgstr "transaction de crédit"
#: apps/treasury/models.py:430
#: apps/treasury/models.py:432
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."
@ -2317,16 +2325,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:451
#: apps/treasury/models.py:452
#: 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:452
#: apps/treasury/models.py:453
msgid "Credits from the Société générale"
msgstr "Crédits de la Société générale"
#: apps/treasury/models.py:455
#: apps/treasury/models.py:456
#, python-brace-format
msgid "Soge credit for {user}"
msgstr "Crédit de la société générale pour l'utilisateur {user}"
@ -2586,7 +2594,7 @@ msgstr ""
"compte"
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
#: apps/wei/models.py:323
#: apps/wei/models.py:326
msgid "bus"
msgstr "bus"
@ -2624,7 +2632,8 @@ msgstr "Sélectionnez les rôles qui vous intéressent."
msgid "This team doesn't belong to the given bus."
msgstr "Cette équipe n'appartient pas à ce bus."
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:35
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:38
#: apps/wei/forms/surveys/wei2023.py:38
msgid "Choose a word:"
msgstr "Choisissez un mot :"
@ -2711,40 +2720,50 @@ msgstr "Non-binaire"
msgid "gender"
msgstr "genre"
#: apps/wei/models.py:213 apps/wei/templates/wei/weimembership_form.html:58
#: apps/wei/models.py:212
msgid "Unisex"
msgstr "Unisexe"
#: apps/wei/models.py:215 apps/wei/templates/wei/weimembership_form.html:58
msgid "clothing cut"
msgstr "coupe de vêtement"
#: apps/wei/models.py:226 apps/wei/templates/wei/weimembership_form.html:61
#: apps/wei/models.py:228 apps/wei/templates/wei/weimembership_form.html:61
msgid "clothing size"
msgstr "taille de vêtement"
#: apps/wei/models.py:232 apps/wei/templates/wei/attribute_bus_1A.html:28
#: apps/wei/models.py:234 apps/wei/templates/wei/attribute_bus_1A.html:28
#: apps/wei/templates/wei/weimembership_form.html:67
msgid "health issues"
msgstr "problèmes de santé"
#: apps/wei/models.py:237 apps/wei/templates/wei/weimembership_form.html:70
#: apps/wei/models.py:239 apps/wei/templates/wei/weimembership_form.html:70
msgid "emergency contact name"
msgstr "nom du contact en cas d'urgence"
#: apps/wei/models.py:242 apps/wei/templates/wei/weimembership_form.html:73
#: apps/wei/models.py:240
msgid "The emergency contact must not be a WEI participant"
msgstr ""
"Le contact en cas d'urgence ne doit pas être une personne qui participe au "
"WEI"
#: apps/wei/models.py:245 apps/wei/templates/wei/weimembership_form.html:73
msgid "emergency contact phone"
msgstr "téléphone du contact en cas d'urgence"
#: apps/wei/models.py:247 apps/wei/templates/wei/weimembership_form.html:52
#: apps/wei/models.py:250 apps/wei/templates/wei/weimembership_form.html:52
msgid "first year"
msgstr "première année"
#: apps/wei/models.py:248
#: apps/wei/models.py:251
msgid "Tells if the user is new in the school."
msgstr "Indique si l'utilisateur est nouveau dans l'école."
#: apps/wei/models.py:253
#: apps/wei/models.py:256
msgid "registration information"
msgstr "informations sur l'inscription"
#: apps/wei/models.py:254
#: apps/wei/models.py:257
msgid ""
"Information about the registration (buses for old members, survey for the "
"new members), encoded in JSON"
@ -2752,27 +2771,27 @@ msgstr ""
"Informations sur l'inscription (bus pour les 2A+, questionnaire pour les "
"1A), encodées en JSON"
#: apps/wei/models.py:312
#: apps/wei/models.py:315
msgid "WEI User"
msgstr "Participant au WEI"
#: apps/wei/models.py:313
#: apps/wei/models.py:316
msgid "WEI Users"
msgstr "Participants au WEI"
#: apps/wei/models.py:333
#: apps/wei/models.py:336
msgid "team"
msgstr "équipe"
#: apps/wei/models.py:343
#: apps/wei/models.py:346
msgid "WEI registration"
msgstr "Inscription au WEI"
#: apps/wei/models.py:347
#: apps/wei/models.py:350
msgid "WEI membership"
msgstr "Adhésion au WEI"
#: apps/wei/models.py:348
#: apps/wei/models.py:351
msgid "WEI memberships"
msgstr "Adhésions au WEI"
@ -3335,6 +3354,10 @@ msgstr "Nous contacter"
msgid "Technical Support"
msgstr "Support technique"
#: note_kfet/templates/base.html:198
msgid "FAQ (FR)"
msgstr "FAQ (FR)"
#: note_kfet/templates/base_search.html:15
msgid "Search by attribute such as name…"
msgstr "Chercher par un attribut tel que le nom …"
@ -3562,3 +3585,18 @@ msgstr ""
"vous connecter. Vous devez vous rendre à la Kfet et payer les frais "
"d'adhésion. Vous devez également valider votre adresse email en suivant le "
"lien que vous avez reçu."
#~ msgid ""
#~ "I declare that I opened or I will open soon a bank account in the Société "
#~ "générale with the BDE partnership."
#~ msgstr ""
#~ "Je déclare avoir ouvert ou ouvrir prochainement un compte à la société "
#~ "générale avec le partenariat du BDE."
#~ 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."

View File

@ -18,7 +18,7 @@ MAILTO=notekfet2020@lists.crans.org
# Spammer les gens en négatif
00 5 * * 2 root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --spam --negative-amount 1 -v 0
# Envoyer le rapport mensuel aux trésoriers et respos info
00 8 6 * * root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --report --add-years 1 -v 0
00 8 * * 5 root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --report --add-years 1 -v 0
# Envoyer les rapports aux gens
55 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py send_reports -v 0
# Mettre à jour les boutons mis en avant

View File

@ -96,7 +96,7 @@ function displayStyle (note) {
if (!note) { return '' }
const balance = note.balance
var css = ''
if (balance < -5000) { css += ' text-danger bg-dark' }
if (balance < -2000) { css += ' text-danger bg-dark' }
else if (balance < -1000) { css += ' text-danger' }
else if (balance < 0) { css += ' text-warning' }
if (!note.email_confirmed) { css += ' bg-primary' }

View File

@ -194,6 +194,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
class="text-muted">{% trans "Contact us" %}</a> &mdash;
<a href="mailto:{{ "SUPPORT_EMAIL" | getenv }}"
class="text-muted">{% trans "Technical Support" %}</a> &mdash;
<a href="https://note.crans.org/doc/faq/"
class="text-muted">{% trans "FAQ (FR)" %}</a> &mdash;
</span>
{% csrf_token %}
<select title="language" name="language"

34
shell-static.nix Executable file
View File

@ -0,0 +1,34 @@
# This is a workaround meant for use with the nix package manager. If you don't know what it is or don't use it, please ignore this file.
#
# The nk20 javascript static location are hardcoded for imperative system.
# This make ./manage.py collectstatic hard to use with nixos.
#
# A workaround is to enter a FHSUserEnv with the static placed under /share/javascript/<static>.
# This emulate a debian like system and enable collecting static normally with ./manage.py collectstatics.
# The regular shell.nix should be enough for other configurations.
#
# Warning, you are still supposed to use pip package with a venv !
{ pkgs ? import <nixpkgs> {} }:
(pkgs.buildFHSUserEnv {
name = "pipzone";
targetPkgs = pkgs: (with pkgs;
let
fhs-static = stdenv.mkDerivation {
name = "fhs-static";
buildCommand = ''
mkdir -p $out/share/javascript/bootstrap4
mkdir -p $out/share/javascript/jquery
ln -s ${python39Packages.xstatic-bootstrap}/lib/python3.9/site-packages/xstatic/pkg/bootstrap/data/* $out/share/javascript/bootstrap4
ln -s ${python39Packages.xstatic-jquery}/lib/python3.9/site-packages/xstatic/pkg/jquery/data/* $out/share/javascript/jquery
'';
};
in [
fhs-static
python39
gettext
python39Packages.pip
python39Packages.virtualenv
python39Packages.setuptools
]);
runScript = "bash";
}).env

23
shell.nix Executable file
View File

@ -0,0 +1,23 @@
# This is meant for use with the nix package manager. If you don't know what it is or don't use it, please ignore this file.
#
# This shell.nix contains all dependencies require to create a venv and pip install -r requirements.txt.
#
# Please check shell-static.nix for running ./manage.py collectstatics.
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = with pkgs; [
python39
python39Packages.pip
python39Packages.setuptools
gettext
];
shellHook = ''
# Tells pip to put packages into $PIP_PREFIX instead of the usual locations.
# See https://pip.pypa.io/en/stable/user_guide/#environment-variables.
export PIP_PREFIX=$(pwd)/_build/pip_packages
export PYTHONPATH="$PIP_PREFIX/${pkgs.python39.sitePackages}:$PYTHONPATH"
export PATH="$PIP_PREFIX/bin:$PATH"
unset SOURCE_DATE_EPOCH
'';
}

View File

@ -35,7 +35,7 @@ commands =
flake8 apps --extend-exclude apps/scripts
[flake8]
ignore = W503, I100, I101
ignore = W503, I100, I101, B019
exclude =
.tox,
.git,