mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-07-22 16:56:48 +02:00
Compare commits
17 Commits
delete_act
...
16cfaa809a
Author | SHA1 | Date | |
---|---|---|---|
16cfaa809a | |||
f2cd0b6d36 | |||
a2e2ff5fa9 | |||
53d0480a12 | |||
ff812a028c | |||
136f636fda | |||
5a8acbde00 | |||
f60dc8cfa0 | |||
067dd6f9d1 | |||
7b1e32e514 | |||
e88dbfd597 | |||
3d34270959 | |||
3bb99671ec | |||
0d69383dfd | |||
7b9ff119e8 | |||
108a56745c | |||
9643d7652b |
@ -168,7 +168,8 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
template_name = "food/food_update.html"
|
template_name = "food/food_update.html"
|
||||||
|
|
||||||
def get_sample_object(self):
|
def get_sample_object(self):
|
||||||
return BasicFood(
|
# We choose a club which may work or BDE else
|
||||||
|
food = BasicFood(
|
||||||
name="",
|
name="",
|
||||||
owner_id=1,
|
owner_id=1,
|
||||||
expiry_date=timezone.now(),
|
expiry_date=timezone.now(),
|
||||||
@ -177,6 +178,14 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
date_type='DLC',
|
date_type='DLC',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for membership in self.request.user.memberships.all():
|
||||||
|
club_id = membership.club.id
|
||||||
|
food.owner_id = club_id
|
||||||
|
if PermissionBackend.check_perm(self.request, "food.add_basicfood", food):
|
||||||
|
return food
|
||||||
|
|
||||||
|
return food
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
if QRCode.objects.filter(qr_code_number=self.kwargs['slug']).count() > 0:
|
if QRCode.objects.filter(qr_code_number=self.kwargs['slug']).count() > 0:
|
||||||
@ -227,13 +236,22 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
template_name = "food/food_update.html"
|
template_name = "food/food_update.html"
|
||||||
|
|
||||||
def get_sample_object(self):
|
def get_sample_object(self):
|
||||||
return TransformedFood(
|
# We choose a club which may work or BDE else
|
||||||
|
food = TransformedFood(
|
||||||
name="",
|
name="",
|
||||||
owner_id=1,
|
owner_id=1,
|
||||||
expiry_date=timezone.now(),
|
expiry_date=timezone.now(),
|
||||||
is_ready=True,
|
is_ready=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for membership in self.request.user.memberships.all():
|
||||||
|
club_id = membership.club.id
|
||||||
|
food.owner_id = club_id
|
||||||
|
if PermissionBackend.check_perm(self.request, "food.add_transformedfood", food):
|
||||||
|
return food
|
||||||
|
|
||||||
|
return food
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.expiry_date = timezone.now() + timedelta(days=3)
|
form.instance.expiry_date = timezone.now() + timedelta(days=3)
|
||||||
@ -245,10 +263,10 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
return reverse_lazy('food:transformedfood_view', kwargs={"pk": self.object.pk})
|
return reverse_lazy('food:transformedfood_view', kwargs={"pk": self.object.pk})
|
||||||
|
|
||||||
|
|
||||||
MAX_FORMS = 10
|
MAX_FORMS = 100
|
||||||
|
|
||||||
|
|
||||||
class ManageIngredientsView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
class ManageIngredientsView(LoginRequiredMixin, UpdateView):
|
||||||
"""
|
"""
|
||||||
A view to manage ingredient for a transformed food
|
A view to manage ingredient for a transformed food
|
||||||
"""
|
"""
|
||||||
@ -279,6 +297,14 @@ class ManageIngredientsView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView
|
|||||||
ingredient.end_of_life = _('Fully used in {meal}'.format(
|
ingredient.end_of_life = _('Fully used in {meal}'.format(
|
||||||
meal=self.object.name))
|
meal=self.object.name))
|
||||||
ingredient.save()
|
ingredient.save()
|
||||||
|
# We recalculate new expiry date and allergens
|
||||||
|
self.object.expiry_date = self.object.creation_date + self.object.shelf_life
|
||||||
|
self.object.allergens.clear()
|
||||||
|
|
||||||
|
for ingredient in self.object.ingredients.iterator():
|
||||||
|
if not (ingredient.polymorphic_ctype.model == 'basicfood' and ingredient.date_type == 'DDM'):
|
||||||
|
self.object.expiry_date = min(self.object.expiry_date, ingredient.expiry_date)
|
||||||
|
self.object.allergens.set(self.object.allergens.union(ingredient.allergens.all()))
|
||||||
|
|
||||||
self.object.save(old_ingredients=old_ingredients, old_allergens=old_allergens)
|
self.object.save(old_ingredients=old_ingredients, old_allergens=old_allergens)
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
46
apps/member/migrations/0014_create_bda.py
Normal file
46
apps/member/migrations/0014_create_bda.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
def create_bda(apps, schema_editor):
|
||||||
|
"""
|
||||||
|
The club BDA is now pre-injected.
|
||||||
|
"""
|
||||||
|
Club = apps.get_model("member", "club")
|
||||||
|
NoteClub = apps.get_model("note", "noteclub")
|
||||||
|
Alias = apps.get_model("note", "alias")
|
||||||
|
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||||
|
polymorphic_ctype_id = ContentType.objects.get_for_model(NoteClub).id
|
||||||
|
|
||||||
|
Club.objects.get_or_create(
|
||||||
|
id=10,
|
||||||
|
name="BDA",
|
||||||
|
email="bda.ensparissaclay@gmail.com",
|
||||||
|
require_memberships=True,
|
||||||
|
membership_fee_paid=750,
|
||||||
|
membership_fee_unpaid=750,
|
||||||
|
membership_duration=396,
|
||||||
|
membership_start="2024-08-01",
|
||||||
|
membership_end="2025-09-30",
|
||||||
|
)
|
||||||
|
NoteClub.objects.get_or_create(
|
||||||
|
id=1937,
|
||||||
|
club_id=10,
|
||||||
|
polymorphic_ctype_id=polymorphic_ctype_id,
|
||||||
|
)
|
||||||
|
Alias.objects.get_or_create(
|
||||||
|
id=1937,
|
||||||
|
note_id=1937,
|
||||||
|
name="BDA",
|
||||||
|
normalized_name="bda",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('member', '0013_auto_20240801_1436'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(create_bda),
|
||||||
|
]
|
||||||
|
|
@ -3998,6 +3998,54 @@
|
|||||||
"description": "Créer une transaction de ou vers la note d'un club tant que la source reste au dessus de -50 €"
|
"description": "Créer une transaction de ou vers la note d'un club tant que la source reste au dessus de -50 €"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.permission",
|
||||||
|
"pk": 271,
|
||||||
|
"fields": {
|
||||||
|
"model": [
|
||||||
|
"wei",
|
||||||
|
"bus"
|
||||||
|
],
|
||||||
|
"query": "{\"wei\": [\"club\"]}",
|
||||||
|
"type": "change",
|
||||||
|
"mask": 3,
|
||||||
|
"field": "",
|
||||||
|
"permanent": false,
|
||||||
|
"description": "Modifier n'importe quel bus du wei"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.permission",
|
||||||
|
"pk": 272,
|
||||||
|
"fields": {
|
||||||
|
"model": [
|
||||||
|
"wei",
|
||||||
|
"bus"
|
||||||
|
],
|
||||||
|
"query": "{\"wei\": [\"club\"]}",
|
||||||
|
"type": "view",
|
||||||
|
"mask": 3,
|
||||||
|
"field": "",
|
||||||
|
"permanent": false,
|
||||||
|
"description": "Voir tous les bus du wei"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.permission",
|
||||||
|
"pk": 273,
|
||||||
|
"fields": {
|
||||||
|
"model": [
|
||||||
|
"wei",
|
||||||
|
"busteam"
|
||||||
|
],
|
||||||
|
"query": "{\"bus__wei\": [\"club\"], \"bus__wei__membership_end__gte\": [\"today\"]}",
|
||||||
|
"type": "view",
|
||||||
|
"mask": 3,
|
||||||
|
"field": "",
|
||||||
|
"permanent": false,
|
||||||
|
"description": "Voir toutes les équipes WEI"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "permission.role",
|
"model": "permission.role",
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
@ -4091,8 +4139,8 @@
|
|||||||
158,
|
158,
|
||||||
159,
|
159,
|
||||||
160,
|
160,
|
||||||
212,
|
212,
|
||||||
222
|
222
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4133,14 +4181,14 @@
|
|||||||
50,
|
50,
|
||||||
141,
|
141,
|
||||||
169,
|
169,
|
||||||
217,
|
217,
|
||||||
218,
|
218,
|
||||||
219,
|
219,
|
||||||
220,
|
220,
|
||||||
221,
|
221,
|
||||||
247,
|
247,
|
||||||
258,
|
258,
|
||||||
259
|
259
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4152,8 +4200,8 @@
|
|||||||
"name": "Pr\u00e9sident\u22c5e de club",
|
"name": "Pr\u00e9sident\u22c5e de club",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
62,
|
62,
|
||||||
142,
|
135,
|
||||||
135
|
142
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4382,7 +4430,10 @@
|
|||||||
112,
|
112,
|
||||||
113,
|
113,
|
||||||
128,
|
128,
|
||||||
130
|
130,
|
||||||
|
271,
|
||||||
|
272,
|
||||||
|
273
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4538,8 +4589,8 @@
|
|||||||
"name": "GC anti-VSS",
|
"name": "GC anti-VSS",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
42,
|
42,
|
||||||
135,
|
135,
|
||||||
150,
|
150,
|
||||||
163,
|
163,
|
||||||
164
|
164
|
||||||
]
|
]
|
||||||
@ -4555,13 +4606,140 @@
|
|||||||
137,
|
137,
|
||||||
211,
|
211,
|
||||||
212,
|
212,
|
||||||
213,
|
213,
|
||||||
214,
|
214,
|
||||||
215,
|
215,
|
||||||
216
|
216
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.role",
|
||||||
|
"pk": 23,
|
||||||
|
"fields": {
|
||||||
|
"for_club": 2,
|
||||||
|
"name": "Darbonne",
|
||||||
|
"permissions": [
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
32
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.role",
|
||||||
|
"pk": 24,
|
||||||
|
"fields": {
|
||||||
|
"for_club": null,
|
||||||
|
"name": "Staffeur⋅euse (S&L,Respo Tech,...)",
|
||||||
|
"permissions": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.role",
|
||||||
|
"pk": 25,
|
||||||
|
"fields": {
|
||||||
|
"for_club": null,
|
||||||
|
"name": "Référent⋅e Bus",
|
||||||
|
"permissions": [
|
||||||
|
22,
|
||||||
|
84,
|
||||||
|
115,
|
||||||
|
117,
|
||||||
|
118,
|
||||||
|
119,
|
||||||
|
120,
|
||||||
|
121,
|
||||||
|
122
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.role",
|
||||||
|
"pk": 28,
|
||||||
|
"fields": {
|
||||||
|
"for_club": 10,
|
||||||
|
"name": "Trésorièr⸱e BDA",
|
||||||
|
"permissions": [
|
||||||
|
55,
|
||||||
|
56,
|
||||||
|
57,
|
||||||
|
58,
|
||||||
|
135,
|
||||||
|
143,
|
||||||
|
176,
|
||||||
|
177,
|
||||||
|
178,
|
||||||
|
243,
|
||||||
|
260,
|
||||||
|
261,
|
||||||
|
262,
|
||||||
|
263,
|
||||||
|
264,
|
||||||
|
265,
|
||||||
|
266,
|
||||||
|
267,
|
||||||
|
268,
|
||||||
|
269
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.role",
|
||||||
|
"pk": 30,
|
||||||
|
"fields": {
|
||||||
|
"for_club": 10,
|
||||||
|
"name": "Respo sorties",
|
||||||
|
"permissions": [
|
||||||
|
49,
|
||||||
|
62,
|
||||||
|
141,
|
||||||
|
241,
|
||||||
|
242,
|
||||||
|
243
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.role",
|
||||||
|
"pk": 31,
|
||||||
|
"fields": {
|
||||||
|
"for_club": 1,
|
||||||
|
"name": "Respo comm",
|
||||||
|
"permissions": [
|
||||||
|
135,
|
||||||
|
244
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.role",
|
||||||
|
"pk": 32,
|
||||||
|
"fields": {
|
||||||
|
"for_club": 10,
|
||||||
|
"name": "Respo comm Art",
|
||||||
|
"permissions": [
|
||||||
|
135,
|
||||||
|
245
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "permission.role",
|
||||||
|
"pk": 33,
|
||||||
|
"fields": {
|
||||||
|
"for_club": 10,
|
||||||
|
"name": "Respo Jam",
|
||||||
|
"permissions": [
|
||||||
|
247,
|
||||||
|
250,
|
||||||
|
251,
|
||||||
|
252,
|
||||||
|
253,
|
||||||
|
254
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "wei.weirole",
|
"model": "wei.weirole",
|
||||||
"pk": 12,
|
"pk": 12,
|
||||||
@ -4596,5 +4774,15 @@
|
|||||||
"model": "wei.weirole",
|
"model": "wei.weirole",
|
||||||
"pk": 18,
|
"pk": 18,
|
||||||
"fields": {}
|
"fields": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "wei.weirole",
|
||||||
|
"pk": 24,
|
||||||
|
"fields": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "wei.weirole",
|
||||||
|
"pk": 25,
|
||||||
|
"fields": {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -39,7 +39,9 @@ class WEIRegistrationForm(forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WEIRegistration
|
model = WEIRegistration
|
||||||
exclude = ('wei', 'clothing_cut')
|
fields = ['user', 'soge_credit', 'birth_date', 'gender', 'clothing_size',
|
||||||
|
'health_issues', 'emergency_contact_name', 'emergency_contact_phone', 'first_year',
|
||||||
|
'information_json']
|
||||||
widgets = {
|
widgets = {
|
||||||
"user": Autocomplete(
|
"user": Autocomplete(
|
||||||
User,
|
User,
|
||||||
@ -50,7 +52,7 @@ class WEIRegistrationForm(forms.ModelForm):
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
"birth_date": DatePickerInput(options={'minDate': '1900-01-01',
|
"birth_date": DatePickerInput(options={'minDate': '1900-01-01',
|
||||||
'maxDate': '2100-01-01'}),
|
'maxDate': '2100-01-01'}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -81,11 +83,6 @@ class WEIChooseBusForm(forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class WEIMembershipForm(forms.ModelForm):
|
class WEIMembershipForm(forms.ModelForm):
|
||||||
caution_check = forms.BooleanField(
|
|
||||||
required=False,
|
|
||||||
label=_("Caution check given"),
|
|
||||||
)
|
|
||||||
|
|
||||||
roles = forms.ModelMultipleChoiceField(
|
roles = forms.ModelMultipleChoiceField(
|
||||||
queryset=WEIRole.objects,
|
queryset=WEIRole.objects,
|
||||||
label=_("WEI Roles"),
|
label=_("WEI Roles"),
|
||||||
@ -194,3 +191,4 @@ class BusTeamForm(forms.ModelForm):
|
|||||||
),
|
),
|
||||||
"color": ColorWidget(),
|
"color": ColorWidget(),
|
||||||
}
|
}
|
||||||
|
# "color": ColorWidget(),
|
||||||
|
18
apps/wei/migrations/0011_alter_weiclub_year.py
Normal file
18
apps/wei/migrations/0011_alter_weiclub_year.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.21 on 2025-05-25 12:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('wei', '0010_remove_weiregistration_specific_diet'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='weiclub',
|
||||||
|
name='year',
|
||||||
|
field=models.PositiveIntegerField(default=2025, unique=True, verbose_name='year'),
|
||||||
|
),
|
||||||
|
]
|
@ -98,7 +98,7 @@ class WEIRegistrationTable(tables.Table):
|
|||||||
if not hasperm:
|
if not hasperm:
|
||||||
return format_html("<span class='no-perm'></span>")
|
return format_html("<span class='no-perm'></span>")
|
||||||
|
|
||||||
url = reverse_lazy('wei:validate_registration', args=(record.pk,))
|
url = reverse_lazy('wei:wei_update_registration', args=(record.pk,)) + '?validate=true'
|
||||||
text = _('Validate')
|
text = _('Validate')
|
||||||
if record.fee > record.user.note.balance and not record.soge_credit:
|
if record.fee > record.user.note.balance and not record.soge_credit:
|
||||||
btn_class = 'btn-secondary'
|
btn_class = 'btn-secondary'
|
||||||
|
@ -18,6 +18,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<div class="card-footer text-center">
|
<div class="card-footer text-center">
|
||||||
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus' pk=bus.pk %}"
|
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:update_bus' pk=bus.pk %}"
|
||||||
data-turbolinks="false">{% trans "Edit" %}</a>
|
data-turbolinks="false">{% trans "Edit" %}</a>
|
||||||
|
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:manage_bus' pk=bus.pk %}"
|
||||||
|
data-turbolinks="false">{% trans "View" %}</a>
|
||||||
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:add_team' pk=bus.pk %}"
|
<a class="btn btn-primary btn-sm my-1" href="{% url 'wei:add_team' pk=bus.pk %}"
|
||||||
data-turbolinks="false">{% trans "Add team" %}</a>
|
data-turbolinks="false">{% trans "Add team" %}</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,9 +13,17 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
{{ form.media }}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
<button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
|
<button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
if (window.jscolor && jscolor.install) {
|
||||||
|
jscolor.install();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -8,18 +8,20 @@ from datetime import date, timedelta
|
|||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Q, Count
|
from django.db.models import Q, Count
|
||||||
from django.db.models.functions.text import Lower
|
from django.db.models.functions.text import Lower
|
||||||
|
from django import forms
|
||||||
from django.http import HttpResponse, Http404
|
from django.http import HttpResponse, Http404
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.generic import DetailView, UpdateView, RedirectView, TemplateView
|
from django.views.generic import DetailView, UpdateView, RedirectView, TemplateView, CreateView
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic.edit import BaseFormView, DeleteView
|
from django.views.generic.edit import BaseFormView, DeleteView
|
||||||
from django_tables2 import SingleTableView, MultiTableMixin
|
from django_tables2 import SingleTableView, MultiTableMixin
|
||||||
@ -37,6 +39,7 @@ from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMember
|
|||||||
WEIMembershipForm, CurrentSurvey
|
WEIMembershipForm, CurrentSurvey
|
||||||
from .tables import BusRepartitionTable, BusTable, BusTeamTable, WEITable, WEIRegistrationTable, \
|
from .tables import BusRepartitionTable, BusTable, BusTeamTable, WEITable, WEIRegistrationTable, \
|
||||||
WEIRegistration1ATable, WEIMembershipTable
|
WEIRegistration1ATable, WEIMembershipTable
|
||||||
|
from .forms.surveys import CurrentSurvey
|
||||||
|
|
||||||
|
|
||||||
class CurrentWEIDetailView(LoginRequiredMixin, RedirectView):
|
class CurrentWEIDetailView(LoginRequiredMixin, RedirectView):
|
||||||
@ -440,6 +443,13 @@ class BusTeamCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
self.object.refresh_from_db()
|
self.object.refresh_from_db()
|
||||||
return reverse_lazy("wei:manage_bus_team", kwargs={"pk": self.object.pk})
|
return reverse_lazy("wei:manage_bus_team", kwargs={"pk": self.object.pk})
|
||||||
|
|
||||||
|
def get_template_names(self):
|
||||||
|
names = super().get_template_names()
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BusTeamUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
class BusTeamUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
||||||
@ -472,6 +482,13 @@ class BusTeamUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
|||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
self.object.refresh_from_db()
|
self.object.refresh_from_db()
|
||||||
return reverse_lazy("wei:manage_bus_team", kwargs={"pk": self.object.pk})
|
return reverse_lazy("wei:manage_bus_team", kwargs={"pk": self.object.pk})
|
||||||
|
|
||||||
|
def get_template_names(self):
|
||||||
|
names = super().get_template_names()
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BusTeamManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
class BusTeamManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
||||||
@ -546,9 +563,15 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
def get_form(self, form_class=None):
|
def get_form(self, form_class=None):
|
||||||
form = super().get_form(form_class)
|
form = super().get_form(form_class)
|
||||||
form.fields["user"].initial = self.request.user
|
form.fields["user"].initial = self.request.user
|
||||||
del form.fields["first_year"]
|
|
||||||
del form.fields["caution_check"]
|
# Cacher les champs pendant l'inscription initiale
|
||||||
del form.fields["information_json"]
|
if "first_year" in form.fields:
|
||||||
|
del form.fields["first_year"]
|
||||||
|
if "caution_check" in form.fields:
|
||||||
|
del form.fields["caution_check"]
|
||||||
|
if "information_json" in form.fields:
|
||||||
|
del form.fields["information_json"]
|
||||||
|
|
||||||
return form
|
return form
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
@ -644,9 +667,13 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
form.fields["soge_credit"].disabled = True
|
form.fields["soge_credit"].disabled = True
|
||||||
form.fields["soge_credit"].help_text = _("You already opened an account in the Société générale.")
|
form.fields["soge_credit"].help_text = _("You already opened an account in the Société générale.")
|
||||||
|
|
||||||
del form.fields["caution_check"]
|
# Cacher les champs pendant l'inscription initiale
|
||||||
del form.fields["first_year"]
|
if "first_year" in form.fields:
|
||||||
del form.fields["information_json"]
|
del form.fields["first_year"]
|
||||||
|
if "caution_check" in form.fields:
|
||||||
|
del form.fields["caution_check"]
|
||||||
|
if "information_json" in form.fields:
|
||||||
|
del form.fields["information_json"]
|
||||||
|
|
||||||
return form
|
return form
|
||||||
|
|
||||||
@ -702,11 +729,15 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
|||||||
# We can't update a registration once the WEI is started and before the membership start date
|
# We can't update a registration once the WEI is started and before the membership start date
|
||||||
if today >= wei.date_start or today < wei.membership_start:
|
if today >= wei.date_start or today < wei.membership_start:
|
||||||
return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
|
return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
|
||||||
|
# Store the validate parameter in the view's state
|
||||||
|
self.should_validate = request.GET.get('validate', False)
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["club"] = self.object.wei
|
context["club"] = self.object.wei
|
||||||
|
# Pass the validate parameter to the template
|
||||||
|
context["should_validate"] = self.should_validate
|
||||||
|
|
||||||
if self.object.is_validated:
|
if self.object.is_validated:
|
||||||
membership_form = self.get_membership_form(instance=self.object.membership,
|
membership_form = self.get_membership_form(instance=self.object.membership,
|
||||||
@ -740,6 +771,9 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
|||||||
# The auto-json-format may cause issues with the default field remove
|
# The auto-json-format may cause issues with the default field remove
|
||||||
if not PermissionBackend.check_perm(self.request, 'wei.change_weiregistration_information_json', self.object):
|
if not PermissionBackend.check_perm(self.request, 'wei.change_weiregistration_information_json', self.object):
|
||||||
del form.fields["information_json"]
|
del form.fields["information_json"]
|
||||||
|
# Masquer le champ caution_check pour tout le monde dans le formulaire de modification
|
||||||
|
if "caution_check" in form.fields:
|
||||||
|
del form.fields["caution_check"]
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def get_membership_form(self, data=None, instance=None):
|
def get_membership_form(self, data=None, instance=None):
|
||||||
@ -759,10 +793,30 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
|||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
# If the membership is already validated, then we update the bus and the team (and the roles)
|
# If the membership is already validated, then we update the bus and the team (and the roles)
|
||||||
if form.instance.is_validated:
|
if form.instance.is_validated:
|
||||||
membership_form = self.get_membership_form(self.request.POST, form.instance.membership)
|
try:
|
||||||
if not membership_form.is_valid():
|
membership = form.instance.membership
|
||||||
|
if membership is None:
|
||||||
|
raise ValueError(_("No membership found for this registration"))
|
||||||
|
|
||||||
|
membership_form = self.get_membership_form(self.request.POST, instance=membership)
|
||||||
|
if not membership_form.is_valid():
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
# Vérifier que l'utilisateur a la permission de modifier le membership
|
||||||
|
# On vérifie d'abord si l'utilisateur a la permission générale de modification
|
||||||
|
if not self.request.user.has_perm("wei.change_weimembership"):
|
||||||
|
raise PermissionDenied(_("You don't have the permission to update memberships"))
|
||||||
|
|
||||||
|
# On vérifie ensuite les permissions spécifiques pour chaque champ modifié
|
||||||
|
for field_name in membership_form.changed_data:
|
||||||
|
perm = f"wei.change_weimembership_{field_name}"
|
||||||
|
if not self.request.user.has_perm(perm):
|
||||||
|
raise PermissionDenied(_("You don't have the permission to update the field %(field)s") % {'field': field_name})
|
||||||
|
|
||||||
|
membership_form.save()
|
||||||
|
except (WEIMembership.DoesNotExist, ValueError, PermissionDenied) as e:
|
||||||
|
form.add_error(None, str(e))
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
membership_form.save()
|
|
||||||
# If it is not validated and if this is an old member, then we update the choices
|
# If it is not validated and if this is an old member, then we update the choices
|
||||||
elif not form.instance.first_year and PermissionBackend.check_perm(
|
elif not form.instance.first_year and PermissionBackend.check_perm(
|
||||||
self.request, "wei.change_weiregistration_information_json", self.object):
|
self.request, "wei.change_weiregistration_information_json", self.object):
|
||||||
@ -787,14 +841,8 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
|
|||||||
survey = CurrentSurvey(self.object)
|
survey = CurrentSurvey(self.object)
|
||||||
if not survey.is_complete():
|
if not survey.is_complete():
|
||||||
return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
|
return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
|
||||||
if PermissionBackend.check_perm(self.request, "wei.add_weimembership", WEIMembership(
|
# On redirige vers la validation uniquement si c'est explicitement demandé (et stocké dans la vue)
|
||||||
club=self.object.wei,
|
if self.should_validate and self.request.user.has_perm("wei.add_weimembership"):
|
||||||
user=self.object.user,
|
|
||||||
date_start=date.today(),
|
|
||||||
date_end=date.today(),
|
|
||||||
fee=0,
|
|
||||||
registration=self.object,
|
|
||||||
)):
|
|
||||||
return reverse_lazy("wei:validate_registration", kwargs={"pk": self.object.pk})
|
return reverse_lazy("wei:validate_registration", kwargs={"pk": self.object.pk})
|
||||||
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk})
|
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk})
|
||||||
|
|
||||||
@ -828,26 +876,21 @@ class WEIDeleteRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Delete
|
|||||||
return reverse_lazy('wei:wei_detail', args=(self.object.wei.pk,))
|
return reverse_lazy('wei:wei_detail', args=(self.object.wei.pk,))
|
||||||
|
|
||||||
|
|
||||||
class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
class WEIValidateRegistrationView(LoginRequiredMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
Validate WEI Registration
|
Validate WEI Registration
|
||||||
"""
|
"""
|
||||||
model = WEIMembership
|
model = WEIMembership
|
||||||
extra_context = {"title": _("Validate WEI registration")}
|
extra_context = {"title": _("Validate WEI registration")}
|
||||||
|
|
||||||
def get_sample_object(self):
|
|
||||||
registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
|
|
||||||
return WEIMembership(
|
|
||||||
club=registration.wei,
|
|
||||||
user=registration.user,
|
|
||||||
date_start=date.today(),
|
|
||||||
date_end=date.today() + timedelta(days=1),
|
|
||||||
fee=0,
|
|
||||||
registration=registration,
|
|
||||||
)
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
wei = WEIRegistration.objects.get(pk=self.kwargs["pk"]).wei
|
# Vérifier d'abord si l'utilisateur a la permission générale
|
||||||
|
if not request.user.has_perm("wei.add_weimembership"):
|
||||||
|
raise PermissionDenied(_("You don't have the permission to validate registrations"))
|
||||||
|
|
||||||
|
registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
|
||||||
|
|
||||||
|
wei = registration.wei
|
||||||
today = date.today()
|
today = date.today()
|
||||||
# We can't validate anyone once the WEI is started and before the membership start date
|
# We can't validate anyone once the WEI is started and before the membership start date
|
||||||
if today >= wei.date_start or today < wei.membership_start:
|
if today >= wei.date_start or today < wei.membership_start:
|
||||||
@ -900,8 +943,14 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
form.fields["last_name"].initial = registration.user.last_name
|
form.fields["last_name"].initial = registration.user.last_name
|
||||||
form.fields["first_name"].initial = registration.user.first_name
|
form.fields["first_name"].initial = registration.user.first_name
|
||||||
|
|
||||||
if "caution_check" in form.fields:
|
# Ajouter le champ caution_check uniquement pour les non-première année et le rendre obligatoire
|
||||||
form.fields["caution_check"].initial = registration.caution_check
|
if not registration.first_year:
|
||||||
|
form.fields["caution_check"] = forms.BooleanField(
|
||||||
|
required=True,
|
||||||
|
initial=registration.caution_check,
|
||||||
|
label=_("Caution check given"),
|
||||||
|
help_text=_("Please make sure the check is given before validating the registration")
|
||||||
|
)
|
||||||
|
|
||||||
if registration.soge_credit:
|
if registration.soge_credit:
|
||||||
form.fields["credit_type"].disabled = True
|
form.fields["credit_type"].disabled = True
|
||||||
@ -1289,8 +1338,22 @@ class WEIAttributeBus1ANextView(LoginRequiredMixin, RedirectView):
|
|||||||
if not wei.exists():
|
if not wei.exists():
|
||||||
raise Http404
|
raise Http404
|
||||||
wei = wei.get()
|
wei = wei.get()
|
||||||
qs = WEIRegistration.objects.filter(wei=wei, membership__isnull=False, membership__bus__isnull=True)
|
|
||||||
qs = qs.filter(information_json__contains='selected_bus_pk') # not perfect, but works...
|
# On cherche d'abord les 1A qui ont une inscription validée (membership) mais pas de bus
|
||||||
if qs.exists():
|
qs = WEIRegistration.objects.filter(
|
||||||
return reverse_lazy('wei:wei_bus_1A', args=(qs.first().pk, ))
|
wei=wei,
|
||||||
return reverse_lazy('wei:wei_1A_list', args=(wei.pk, ))
|
first_year=True,
|
||||||
|
membership__isnull=False,
|
||||||
|
membership__bus__isnull=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Parmi eux, on prend ceux qui ont répondu au questionnaire (ont un bus préféré)
|
||||||
|
qs = qs.filter(information_json__contains='selected_bus_pk')
|
||||||
|
|
||||||
|
if not qs.exists():
|
||||||
|
# Si on ne trouve personne, on affiche un message et on retourne à la liste
|
||||||
|
messages.info(self.request, _("No first year student without a bus found. Either all of them have a bus, or none has filled the survey yet."))
|
||||||
|
return reverse_lazy('wei:wei_1A_list', args=(wei.pk,))
|
||||||
|
|
||||||
|
# On redirige vers la page d'attribution pour le premier étudiant trouvé
|
||||||
|
return reverse_lazy('wei:wei_bus_1A', args=(qs.first().pk,))
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -63,8 +63,16 @@ class ColorWidget(Widget):
|
|||||||
def format_value(self, value):
|
def format_value(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
value = 0xFFFFFF
|
value = 0xFFFFFF
|
||||||
return "#{:06X}".format(value)
|
if isinstance(value, str):
|
||||||
|
return value # Assume it's already a hex string like "#FFAA33"
|
||||||
|
try:
|
||||||
|
return "#{:06X}".format(value)
|
||||||
|
except Exception:
|
||||||
|
return "#FFFFFF"
|
||||||
|
|
||||||
|
|
||||||
def value_from_datadict(self, data, files, name):
|
def value_from_datadict(self, data, files, name):
|
||||||
val = super().value_from_datadict(data, files, name)
|
val = super().value_from_datadict(data, files, name)
|
||||||
return int(val[1:], 16)
|
if val:
|
||||||
|
return int(val[1:], 16)
|
||||||
|
return None
|
5
note_kfet/templates/colorfield/color.html
Normal file
5
note_kfet/templates/colorfield/color.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<input type="text"
|
||||||
|
name="{{ widget.name }}"
|
||||||
|
value="{{ widget.value }}"
|
||||||
|
class="jscolor"
|
||||||
|
{% include "django/forms/widgets/attrs.html" %}>
|
Reference in New Issue
Block a user