diff --git a/apps/activity/templates/activity/activity_detail.html b/apps/activity/templates/activity/activity_detail.html
index 1a8d01ee..bb0fc57a 100644
--- a/apps/activity/templates/activity/activity_detail.html
+++ b/apps/activity/templates/activity/activity_detail.html
@@ -37,6 +37,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% render_table guests %}
+
{% endif %}
{% endblock %}
diff --git a/apps/activity/templates/activity/activity_list.html b/apps/activity/templates/activity/activity_list.html
index bf5ba70f..79da6d97 100644
--- a/apps/activity/templates/activity/activity_list.html
+++ b/apps/activity/templates/activity/activity_list.html
@@ -1,4 +1,4 @@
-{% extends "base.html" %}
+{% extends "base_search.html" %}
{% comment %}
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
@@ -44,6 +44,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
- {% render_table table %}
+ {% render_table all %}
+
+{{ block.super }}
{% endblock %}
diff --git a/apps/activity/templates/activity/includes/activity_info.html b/apps/activity/templates/activity/includes/activity_info.html
index f9ea634b..4565a086 100644
--- a/apps/activity/templates/activity/includes/activity_info.html
+++ b/apps/activity/templates/activity/includes/activity_info.html
@@ -1,7 +1,7 @@
{% comment %}
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
-{% load i18n perms pretty_money %}
+{% load i18n perms pretty_money dict_get %}
{% url 'activity:activity_detail' activity.pk as activity_detail_url %}
@@ -53,6 +53,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% trans 'opened'|capfirst %}
{{ activity.open|yesno }}
+ {% if show_entries|dict_get:activity %}
+
+ {{ entries_count|dict_get:activity }}
+ {% if entries_count|dict_get:activity >= 2 %}{% trans "entries" %}{% else %}{% trans "entry" %}{% endif %}
+
+ {% endif %}
@@ -114,7 +120,26 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endif %}
+
{% endblock %}
\ No newline at end of file
diff --git a/apps/member/views.py b/apps/member/views.py
index bf6245f5..72ad446e 100644
--- a/apps/member/views.py
+++ b/apps/member/views.py
@@ -17,6 +17,7 @@ from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, UpdateView, TemplateView
from django.views.generic.edit import FormMixin
from django_tables2.views import MultiTableMixin, SingleTableMixin, SingleTableView
+from django_tables2.export.views import ExportMixin
from rest_framework.authtoken.models import Token
from api.viewsets import is_regex
from note.models import Alias, NoteClub, NoteUser, Trust
@@ -950,11 +951,12 @@ class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
return reverse_lazy('member:user_detail', kwargs={'pk': self.object.user.id})
-class ClubMembersListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
+class ClubMembersListView(ExportMixin, ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
model = Membership
table_class = MembershipTable
template_name = "member/club_members.html"
extra_context = {"title": _("Members of the club")}
+ export_formats = ["csv"]
def get_queryset(self, **kwargs):
qs = super().get_queryset().filter(club_id=self.kwargs["pk"])
@@ -986,6 +988,14 @@ class ClubMembersListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableV
return qs.distinct()
+ def get_export_filename(self, export_format):
+ return "members.csv"
+
+ def get_export_content_type(self, export_format):
+ if export_format == "csv":
+ return "text/csv"
+ return super().get_export_content_type(export_format)
+
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
club = Club.objects.filter(
diff --git a/apps/note/static/note/js/consos.js b/apps/note/static/note/js/consos.js
index d08d93bd..99bdf610 100644
--- a/apps/note/static/note/js/consos.js
+++ b/apps/note/static/note/js/consos.js
@@ -228,7 +228,7 @@ function consume (source, source_alias, dest, quantity, amount, reason, type, ca
addMsg(interpolate(gettext('Warning, the transaction from the note %s succeed, ' +
'but the emitter note %s is negative.'), [source_alias, source_alias]), 'warning', 30000)
}
- if (source.membership && source.membership.date_end < new Date().toISOString()) {
+ if (source.membership && source.membership.date_end <= new Date().toISOString()) {
addMsg(interpolate(gettext('Warning, the emitter note %s is no more a BDE member.'), [source_alias]),
'danger', 30000)
}
diff --git a/apps/note/static/note/js/transfer.js b/apps/note/static/note/js/transfer.js
index 509d9b48..1c8797f4 100644
--- a/apps/note/static/note/js/transfer.js
+++ b/apps/note/static/note/js/transfer.js
@@ -66,6 +66,8 @@ $(document).ready(function () {
arr.push(last)
last.quantity = 1
+
+
if (last.note.club) {
$('#last_name').val(last.note.name)
@@ -111,7 +113,8 @@ $(document).ready(function () {
dest.removeClass('d-none')
$('#dest_note_list').removeClass('d-none')
$('#debit_type').addClass('d-none')
-
+ $('#reason').val('')
+
$('#source_note_label').text(select_emitters_label)
$('#dest_note_label').text(select_receveirs_label)
@@ -134,6 +137,7 @@ $(document).ready(function () {
dest.val('')
dest.tooltip('hide')
$('#debit_type').addClass('d-none')
+ $('#reason').val('Rechargement note')
$('#source_note_label').text(transfer_type_label)
$('#dest_note_label').text(select_receveir_label)
@@ -162,6 +166,7 @@ $(document).ready(function () {
dest.addClass('d-none')
dest.tooltip('hide')
$('#debit_type').removeClass('d-none')
+ $('#reason').val('')
$('#source_note_label').text(select_emitter_label)
$('#dest_note_label').text(transfer_type_label)
@@ -305,10 +310,10 @@ $('#btn_transfer').click(function () {
destination: dest.note.id,
destination_alias: dest.name
}).done(function () {
- if (source.note.membership && source.note.membership.date_end < new Date().toISOString()) {
+ if (source.note.membership && source.note.membership.date_end <= new Date().toISOString()) {
addMsg(interpolate(gettext('Warning, the emitter note %s is no more a BDE member.'), [source.name]), 'danger', 30000)
}
- if (dest.note.membership && dest.note.membership.date_end < new Date().toISOString()) {
+ if (dest.note.membership && dest.note.membership.date_end <= new Date().toISOString()) {
addMsg(interpolate(gettext('Warning, the destination note %s is no more a BDE member.'), [dest.name]), 'danger', 30000)
}
@@ -409,7 +414,7 @@ $('#btn_transfer').click(function () {
bank: $('#bank').val()
}).done(function () {
addMsg(gettext('Credit/debit succeed!'), 'success', 10000)
- if (user_note.membership && user_note.membership.date_end < new Date().toISOString()) { addMsg(gettext('Warning, the emitter note %s is no more a BDE member.'), 'danger', 10000) }
+ if (user_note.membership && user_note.membership.date_end <= new Date().toISOString()) { addMsg(gettext('Warning, the emitter note %s is no more a BDE member.'), 'danger', 10000) }
reset()
}).fail(function (err) {
const errObj = JSON.parse(err.responseText)
diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json
index e642c4e6..0acd11c4 100644
--- a/apps/permission/fixtures/initial.json
+++ b/apps/permission/fixtures/initial.json
@@ -4430,6 +4430,22 @@
"description": "Modifier le type de caution de mon inscription WEI tant qu'elle n'est pas validée"
}
},
+ {
+ "model": "permission.permission",
+ "pk": 298,
+ "fields": {
+ "model": [
+ "wei",
+ "bus"
+ ],
+ "query": "{\"pk\": [\"membership\", \"weimembership\", \"bus\", \"pk\"], \"wei__date_end__gte\": [\"today\"]}",
+ "type": "change",
+ "mask": 2,
+ "field": "information_json",
+ "permanent": false,
+ "description": "Modifier les informations du bus"
+ }
+ },
{
"model": "permission.permission",
"pk": 311,
@@ -4686,6 +4702,22 @@
"description": "Supprimer un succès"
}
},
+ {
+ "model": "permission.permission",
+ "pk": 330,
+ "fields": {
+ "model": [
+ "auth",
+ "user"
+ ],
+ "query": "{\"memberships__club\": [\"club\"]}",
+ "type": "view",
+ "mask": 2,
+ "field": "email",
+ "permanent": false,
+ "description": "Voir l'adresse mail des membres de son club"
+ }
+ },
{
"model": "permission.role",
"pk": 1,
@@ -4833,7 +4865,11 @@
221,
247,
258,
- 259
+ 259,
+ 260,
+ 263,
+ 265,
+ 330
]
}
},
@@ -4845,7 +4881,6 @@
"name": "Pr\u00e9sident\u22c5e de club",
"permissions": [
62,
- 135,
142
]
}
@@ -5122,7 +5157,8 @@
289,
290,
291,
- 293
+ 293,
+ 298
]
}
},
@@ -5182,6 +5218,7 @@
"permissions": [
37,
41,
+ 42,
53,
54,
55,
@@ -5233,7 +5270,9 @@
168,
176,
177,
- 197
+ 197,
+ 311,
+ 319
]
}
},
@@ -5313,7 +5352,8 @@
289,
290,
291,
- 293
+ 293,
+ 298
]
}
},
diff --git a/apps/treasury/migrations/0011_sogecredit_valid.py b/apps/treasury/migrations/0011_sogecredit_valid.py
new file mode 100644
index 00000000..44ef6c90
--- /dev/null
+++ b/apps/treasury/migrations/0011_sogecredit_valid.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.2.6 on 2025-09-28 20:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('treasury', '0010_alter_invoice_bde'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='sogecredit',
+ name='valid',
+ field=models.BooleanField(blank=True, default=False, verbose_name='Valid'),
+ ),
+ ]
diff --git a/apps/treasury/models.py b/apps/treasury/models.py
index 48709801..63e1d7cd 100644
--- a/apps/treasury/models.py
+++ b/apps/treasury/models.py
@@ -308,6 +308,12 @@ class SogeCredit(models.Model):
null=True,
)
+ valid = models.BooleanField(
+ default=False,
+ verbose_name=_("Valid"),
+ blank=True,
+ )
+
class Meta:
verbose_name = _("Credit from the Société générale")
verbose_name_plural = _("Credits from the Société générale")
@@ -338,7 +344,7 @@ class SogeCredit(models.Model):
credit_transaction.save()
credit_transaction.refresh_from_db()
self.credit_transaction = credit_transaction
- elif not self.valid:
+ elif not self.valid_legacy:
self.credit_transaction.amount = self.amount
self.credit_transaction._force_save = True
self.credit_transaction.save()
@@ -346,12 +352,12 @@ class SogeCredit(models.Model):
return super().save(*args, **kwargs)
@property
- def valid(self):
+ def valid_legacy(self):
return self.credit_transaction and self.credit_transaction.valid
@property
def amount(self):
- if self.valid:
+ if self.valid_legacy:
return self.credit_transaction.total
amount = 0
transactions_wei = self.transactions.filter(membership__club__weiclub__isnull=False)
@@ -365,7 +371,7 @@ class SogeCredit(models.Model):
The Sogé credit may be created after the user already paid its memberships.
We query transactions and update the credit, if it is unvalid.
"""
- if self.valid or not self.pk:
+ if self.valid_legacy or not self.pk:
return
# Soge do not pay BDE and kfet memberships since 2022
@@ -405,7 +411,7 @@ class SogeCredit(models.Model):
Invalidating a Société générale delete the transaction of the bank if it was already created.
Treasurers must know what they do, With Great Power Comes Great Responsibility...
"""
- if self.valid:
+ if self.valid_legacy:
self.credit_transaction.valid = False
self.credit_transaction.save()
for tr in self.transactions.all():
@@ -414,7 +420,7 @@ class SogeCredit(models.Model):
tr.save()
def validate(self, force=False):
- if self.valid and not force:
+ if self.valid_legacy and not force:
# The credit is already done
return
diff --git a/apps/treasury/static/img/Diolistos_bg.jpg b/apps/treasury/static/img/Diolistos_bg.jpg
index e9453dbd..3bbc7b2e 100755
Binary files a/apps/treasury/static/img/Diolistos_bg.jpg and b/apps/treasury/static/img/Diolistos_bg.jpg differ
diff --git a/apps/treasury/tests/test_treasury.py b/apps/treasury/tests/test_treasury.py
index 8feb5485..d1d5a414 100644
--- a/apps/treasury/tests/test_treasury.py
+++ b/apps/treasury/tests/test_treasury.py
@@ -359,7 +359,7 @@ class TestSogeCredits(TestCase):
))
self.assertRedirects(response, reverse("treasury:manage_soge_credit", args=(soge_credit.pk,)), 302, 200)
soge_credit.refresh_from_db()
- self.assertTrue(soge_credit.valid)
+ self.assertTrue(soge_credit.valid_legacy)
self.user.note.refresh_from_db()
self.assertEqual(
Transaction.objects.filter(Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
diff --git a/apps/wei/forms/surveys/wei2025.py b/apps/wei/forms/surveys/wei2025.py
index 67439b6e..758776b8 100644
--- a/apps/wei/forms/surveys/wei2025.py
+++ b/apps/wei/forms/surveys/wei2025.py
@@ -17,7 +17,7 @@ from ...models import WEIMembership, Bus
WORDS = {
'list': [
- 'Fiesta', 'Graillance', 'Move it move it', 'Calme', 'Nert et geek', 'Jeux de rôles et danse rock',
+ 'Fiesta', 'Graillance', 'Move it move it', 'Calme', 'Nerd et geek', 'Jeux de rôles et danse rock',
'Strass et paillettes', 'Spectaculaire', 'Splendide', 'Flow inégalable', 'Rap', 'Battles légendaires',
'Techno', 'Alcool', 'Kiffeur·euse', 'Rugby', 'Médiéval', 'Festif',
'Stylé', 'Chipie', 'Rétro', 'Vache', 'Farfadet', 'Fanfare',
@@ -57,7 +57,7 @@ WORDS = {
42: "Un burgouzz de valouzz",
47: "Un ocarina (pour me téléporter hors de ce bourbier)",
48: "Des paillettes, un micro de karaoké et une enceinte bluetooth",
- 45: "",
+ 45: "Un kebab",
44: "Une 86 et un caisson pour taper du pied",
46: "Une épée, un ballon et une tireuse",
43: "Des lunettes de soleil",
@@ -176,7 +176,33 @@ WORDS = {
49: "Soirée raclette !"
}
]
- }
+ },
+ 'stats': [
+ {
+ "question": """Le WEI est structuré par bus, et au sein de chaque bus, par équipes.
+ Pour toi, être dans une équipe où tout le monde reste sobre (primo-entrants comme encadrants) c'est :""",
+ "answers": [
+ (1, "Inenvisageable"),
+ (2, "À contre cœur"),
+ (3, "Pourquoi pas"),
+ (4, "Souhaitable"),
+ (5, "Nécessaire"),
+ ],
+ "help_text": "(De toute façon aucun alcool n'est consommé pendant les trajets du bus, ni aller, ni retour.)",
+ },
+ {
+ "question": "Faire partie d'un bus qui n'apporte pas de boisson alcoolisée pour ses membres, pour toi c'est :",
+ "answers": [
+ (1, "Inenvisageable"),
+ (2, "À contre cœur"),
+ (3, "Pourquoi pas"),
+ (4, "Souhaitable"),
+ (5, "Nécessaire"),
+ ],
+ "help_text": """(Tout les bus apportent de l'alcool cette année, cette question sert à l'organisation pour l'année prochaine.
+ De plus il y aura de toute façon de l'alcool commun au WEI et aucun alcool n'est consommé pendant les trajets en bus.)""",
+ },
+ ]
}
IMAGES = {
@@ -235,7 +261,7 @@ class WEISurveyForm2025(forms.Form):
all_preferred_words = WORDS['list']
rng.shuffle(all_preferred_words)
self.fields["words"].choices = [(w, w) for w in all_preferred_words]
- else:
+ elif information.step <= len(WORDS['questions']):
questions = list(WORDS['questions'].items())
idx = information.step - 1
if idx < len(questions):
@@ -251,6 +277,15 @@ class WEISurveyForm2025(forms.Form):
widget=OptionalImageRadioSelect(images=IMAGES.get(q, {})),
required=True,
)
+ elif information.step == len(WORDS['questions']) + 1:
+ for i, v in enumerate(WORDS['stats']):
+ self.fields[f'stat_{i}'] = forms.ChoiceField(
+ label=v['question'],
+ choices=v['answers'],
+ widget=forms.RadioSelect(),
+ required=False,
+ help_text=_(v.get('help_text', ''))
+ )
def clean_words(self):
data = self.cleaned_data['words']
@@ -377,7 +412,7 @@ class WEISurvey2025(WEISurvey):
setattr(self.information, "word" + str(i), word)
self.information.step += 1
self.save()
- else:
+ elif 1 <= self.information.step <= len(WORDS['questions']):
questions = list(WORDS['questions'].keys())
idx = self.information.step - 1
if idx < len(questions):
@@ -385,6 +420,13 @@ class WEISurvey2025(WEISurvey):
setattr(self.information, q, form.cleaned_data[q])
self.information.step += 1
self.save()
+ else:
+ for i, __ in enumerate(WORDS['stats']):
+ ans = form.cleaned_data.get(f'stat_{i}')
+ if ans is not None:
+ setattr(self.information, f'stat_{i}', ans)
+ self.information.step += 1
+ self.save()
@classmethod
def get_algorithm_class(cls):
@@ -394,7 +436,7 @@ class WEISurvey2025(WEISurvey):
"""
The survey is complete once the bus is chosen.
"""
- return self.information.step > len(WORDS['questions'])
+ return self.information.step > len(WORDS['questions']) + 1
@classmethod
@lru_cache()
diff --git a/apps/wei/static/wei/img/logo_auvergne_rhone_alpes.jpg b/apps/wei/static/wei/img/logo_auvergne_rhone_alpes.jpg
new file mode 100644
index 00000000..d95f496b
Binary files /dev/null and b/apps/wei/static/wei/img/logo_auvergne_rhone_alpes.jpg differ
diff --git a/apps/wei/templates/wei/weiregistration_form.html b/apps/wei/templates/wei/weiregistration_form.html
index fae85e0f..dc5f66e5 100644
--- a/apps/wei/templates/wei/weiregistration_form.html
+++ b/apps/wei/templates/wei/weiregistration_form.html
@@ -11,7 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{{ title }}
-
-