Merge pull request 'Application des changement statuts et RI' (#846) from status-change-2024-nov-13 into master
Reviewed-on: #846
This commit is contained in:
commit
5c4b752363
31 changed files with 748 additions and 165 deletions
|
@ -130,15 +130,33 @@ class UserProfileAdmin(UserAdmin):
|
|||
is_buro.short_description = "Membre du Buro"
|
||||
is_buro.boolean = True
|
||||
|
||||
def is_chef(self, obj):
|
||||
try:
|
||||
return obj.profile.is_chef
|
||||
except CofProfile.DoesNotExist:
|
||||
return False
|
||||
|
||||
is_chef.short_description = "Chef K-Fêt"
|
||||
is_chef.boolean = True
|
||||
|
||||
def is_cof(self, obj):
|
||||
try:
|
||||
return obj.profile.is_cof
|
||||
except CofProfile.DoesNotExist:
|
||||
return False
|
||||
|
||||
is_cof.short_description = "Membre du COF"
|
||||
is_cof.short_description = "Membre COF"
|
||||
is_cof.boolean = True
|
||||
|
||||
def is_kfet(self, obj):
|
||||
try:
|
||||
return obj.profile.is_kfet
|
||||
except CofProfile.DoesNotExist:
|
||||
return False
|
||||
|
||||
is_kfet.short_description = "Membre K-Fêt"
|
||||
is_kfet.boolean = True
|
||||
|
||||
list_display = UserAdmin.list_display + (
|
||||
"profile_phone",
|
||||
"profile_occupation",
|
||||
|
@ -146,7 +164,9 @@ class UserProfileAdmin(UserAdmin):
|
|||
"profile_mailing_bda",
|
||||
"profile_mailing_bda_revente",
|
||||
"is_cof",
|
||||
"is_kfet",
|
||||
"is_buro",
|
||||
"is_chef",
|
||||
)
|
||||
list_display_links = ("username", "email", "first_name", "last_name")
|
||||
list_filter = UserAdmin.list_filter + (
|
||||
|
|
|
@ -9,7 +9,7 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
def cof_required(view_func):
|
||||
"""Décorateur qui vérifie que l'utilisateur est connecté et membre du COF.
|
||||
"""Décorateur qui vérifie que l'utilisateur est connecté et membre COF.
|
||||
|
||||
- Si l'utilisteur n'est pas connecté, il est redirigé vers la page de
|
||||
connexion
|
||||
|
@ -33,6 +33,31 @@ def cof_required(view_func):
|
|||
return login_required(_wrapped_view)
|
||||
|
||||
|
||||
def kfet_required(view_func):
|
||||
"""Décorateur qui vérifie que l'utilisateur est connecté et membre K-Fêt.
|
||||
|
||||
- Si l'utilisteur n'est pas connecté, il est redirigé vers la page de
|
||||
connexion
|
||||
- Si l'utilisateur est connecté mais pas membre K-Fêt, il obtient une
|
||||
page d'erreur lui demandant de s'inscrire à la K-Fêt
|
||||
"""
|
||||
|
||||
def is_kfet(user):
|
||||
try:
|
||||
return user.profile.is_cof or user.profile.is_kfet
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(request, *args, **kwargs):
|
||||
if is_kfet(request.user):
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
||||
return render(request, "kfet-denied.html", status=403)
|
||||
|
||||
return login_required(_wrapped_view)
|
||||
|
||||
|
||||
def buro_required(view_func):
|
||||
"""Décorateur qui vérifie que l'utilisateur est connecté et membre du burô.
|
||||
|
||||
|
@ -58,6 +83,33 @@ def buro_required(view_func):
|
|||
return login_required(_wrapped_view)
|
||||
|
||||
|
||||
def chef_required(view_func):
|
||||
"""Décorateur qui vérifie que l'utilisateur est connecté et membre du burô ou chef K-fêt.
|
||||
|
||||
- Si l'utilisateur n'est pas connecté, il est redirigé vers la page de
|
||||
connexion
|
||||
- Si l'utilisateur est connecté mais pas membre du burô ou chef, il obtient une
|
||||
page d'erreur 403 Forbidden
|
||||
"""
|
||||
|
||||
def is_chef(user):
|
||||
try:
|
||||
return user.profile.is_chef or user.profile.is_buro
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(request, *args, **kwargs):
|
||||
if is_chef(request.user):
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
||||
return render(
|
||||
request, "buro-denied.html", status=403
|
||||
) # TODO: reservé au burô ou au chef
|
||||
|
||||
return login_required(_wrapped_view)
|
||||
|
||||
|
||||
class CofRequiredMixin(PermissionRequiredMixin):
|
||||
def has_permission(self):
|
||||
if not self.request.user.is_authenticated:
|
||||
|
@ -79,3 +131,18 @@ class BuroRequiredMixin(PermissionRequiredMixin):
|
|||
"L'utilisateur %s n'a pas de profil !", self.request.user.username
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
class ChefRequiredMixin(PermissionRequiredMixin):
|
||||
def has_permission(self):
|
||||
if not self.request.user.is_authenticated:
|
||||
return False
|
||||
try:
|
||||
return (
|
||||
self.request.user.profile.is_chef or self.request.user.profile.is_buro
|
||||
)
|
||||
except AttributeError:
|
||||
logger.error(
|
||||
"L'utilisateur %s n'a pas de profil !", self.request.user.username
|
||||
)
|
||||
return False
|
||||
|
|
|
@ -284,6 +284,7 @@ class RegistrationProfileForm(forms.ModelForm):
|
|||
"occupation",
|
||||
"departement",
|
||||
"is_cof",
|
||||
"is_kfet",
|
||||
"type_cotiz",
|
||||
"mailing_cof",
|
||||
"mailing_bda",
|
||||
|
@ -293,6 +294,19 @@ class RegistrationProfileForm(forms.ModelForm):
|
|||
]
|
||||
|
||||
|
||||
class RegistrationKFProfileForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CofProfile
|
||||
fields = [
|
||||
"login_clipper",
|
||||
"phone",
|
||||
"occupation",
|
||||
"departement",
|
||||
"is_kfet",
|
||||
"comments",
|
||||
]
|
||||
|
||||
|
||||
STATUS_CHOICES = (
|
||||
("no", "Non"),
|
||||
("wait", "Oui mais attente paiement"),
|
||||
|
@ -444,3 +458,20 @@ class GestioncofConfigForm(ConfigForm):
|
|||
max_length=2048,
|
||||
required=False,
|
||||
)
|
||||
|
||||
|
||||
# ----
|
||||
# Formulaire pour les adhésions self-service
|
||||
# ----
|
||||
|
||||
|
||||
class SubscribForm(forms.Form):
|
||||
accept_ri = forms.BooleanField(
|
||||
label="Lu et accepte le réglement intérieur de l'AEENS (COF).", required=True
|
||||
)
|
||||
accept_status = forms.BooleanField(
|
||||
label="Lu et accepte les status de l'AEENS (COF).", required=True
|
||||
)
|
||||
accept_charte_kf = forms.BooleanField(
|
||||
label="Lu et accepte la charte de la K-Fêt.", required=True
|
||||
)
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 4.2.16 on 2024-12-24 10:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("gestioncof", "0020_merge_20241218_2240"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="cofprofile",
|
||||
name="is_kfet",
|
||||
field=models.BooleanField(default=False, verbose_name="Membre K-Fêt"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="cofprofile",
|
||||
name="is_cof",
|
||||
field=models.BooleanField(default=False, verbose_name="Membre COF"),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.16 on 2024-12-30 14:19
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("gestioncof", "0021_cofprofile_is_kfet_alter_cofprofile_is_cof"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="cofprofile",
|
||||
name="date_adhesion",
|
||||
field=models.DateField(
|
||||
blank=True, null=True, verbose_name="Date d'adhésion COF"
|
||||
),
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="cofprofile",
|
||||
old_name="date_adhesion",
|
||||
new_name="date_adhesion_cof",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="cofprofile",
|
||||
name="date_adhesion_kfet",
|
||||
field=models.DateField(
|
||||
blank=True, null=True, verbose_name="Date d'adhésion K-Fêt"
|
||||
),
|
||||
),
|
||||
]
|
18
gestioncof/migrations/0023_cofprofile_is_chef.py
Normal file
18
gestioncof/migrations/0023_cofprofile_is_chef.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.16 on 2025-01-21 10:06
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("gestioncof", "0022_rename_cofprofile_date_adhesion_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="cofprofile",
|
||||
name="is_chef",
|
||||
field=models.BooleanField(default=False, verbose_name="Chef K-Fêt"),
|
||||
),
|
||||
]
|
|
@ -1,7 +1,13 @@
|
|||
from datetime import date
|
||||
from smtplib import SMTPRecipientsRefused
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.mail import send_mail
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_delete, post_save
|
||||
from django.dispatch import receiver
|
||||
from django.template import loader
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from bda.models import Spectacle
|
||||
|
@ -49,8 +55,12 @@ class CofProfile(models.Model):
|
|||
login_clipper = models.CharField(
|
||||
"Login clipper", max_length=32, blank=True, unique=True, null=True
|
||||
)
|
||||
is_cof = models.BooleanField("Membre du COF", default=False)
|
||||
date_adhesion = models.DateField("Date d'adhésion", blank=True, null=True)
|
||||
is_cof = models.BooleanField("Membre COF", default=False)
|
||||
is_kfet = models.BooleanField("Membre K-Fêt", default=False)
|
||||
date_adhesion_cof = models.DateField("Date d'adhésion COF", blank=True, null=True)
|
||||
date_adhesion_kfet = models.DateField(
|
||||
"Date d'adhésion K-Fêt", blank=True, null=True
|
||||
)
|
||||
phone = models.CharField("Téléphone", max_length=20, blank=True)
|
||||
occupation = models.CharField(
|
||||
_("Occupation"),
|
||||
|
@ -75,6 +85,7 @@ class CofProfile(models.Model):
|
|||
)
|
||||
comments = models.TextField("Commentaires visibles par l'utilisateur", blank=True)
|
||||
is_buro = models.BooleanField("Membre du Burô", default=False)
|
||||
is_chef = models.BooleanField("Chef K-Fêt", default=False)
|
||||
petits_cours_accept = models.BooleanField(
|
||||
"Recevoir des petits cours", default=False
|
||||
)
|
||||
|
@ -89,6 +100,46 @@ class CofProfile(models.Model):
|
|||
def __str__(self):
|
||||
return self.user.username
|
||||
|
||||
def make_adh_cof(self, request, was_cof):
|
||||
if self.is_cof and not was_cof:
|
||||
notify_new_member(request, self.user)
|
||||
self.date_adhesion_cof = date.today()
|
||||
self.save()
|
||||
|
||||
def make_adh_kfet(self, request, was_kfet):
|
||||
if self.is_kfet and not was_kfet:
|
||||
notify_new_member(request, self.user)
|
||||
self.date_adhesion_kfet = date.today()
|
||||
self.save()
|
||||
|
||||
|
||||
def notify_new_member(request, member: User):
|
||||
if not member.email:
|
||||
messages.warning(
|
||||
request,
|
||||
"GestioCOF n'a pas d'adresse mail pour {}, ".format(member)
|
||||
+ "aucun email de bienvenue n'a été envoyé",
|
||||
)
|
||||
return
|
||||
|
||||
# Try to send a welcome email and report SMTP errors
|
||||
try:
|
||||
send_mail(
|
||||
"Bienvenue au COF",
|
||||
loader.render_to_string(
|
||||
"gestioncof/mails/welcome.txt", context={"member": member}
|
||||
),
|
||||
"cof@ens.fr",
|
||||
[member.email],
|
||||
)
|
||||
except SMTPRecipientsRefused:
|
||||
messages.error(
|
||||
request,
|
||||
"Error lors de l'envoi de l'email de bienvenue à {} ({})".format(
|
||||
member, member.email
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def create_user_profile(sender, instance, created, **kwargs):
|
||||
|
|
|
@ -701,6 +701,9 @@ header a:active {
|
|||
.user-is-cof {
|
||||
color : #ADE297;
|
||||
}
|
||||
.user-is-kfet {
|
||||
color : #FF8C00;
|
||||
}
|
||||
.user-is-not-cof {
|
||||
color: #EE8585;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Section réservée aux membres du COF -- merci de vous inscrire au COF ou de passer au COF/nous envoyer un mail si vous êtes déjà membre :)</h2>
|
||||
<h2>Section réservée aux membres COF -- merci de vous inscrire au COF ou de passer au COF/nous envoyer un mail si vous êtes déjà membre :)</h2>
|
||||
{% endblock %}
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
{% endblock %}
|
||||
</a>
|
||||
<div class="secondary">
|
||||
{% if user.is_authenticated %}
|
||||
<span class="hidden-xxs"> | </span>
|
||||
<span><a href="{% url "cof-logout" %}">Se déconnecter <span class="glyphicon glyphicon-log-out"></span></a></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<h2 class="member-status">{% if user.first_name %}{{ user.first_name }}{% else %}<tt>{{ user.username }}</tt>{% endif %}, {% if user.profile.is_cof %}<tt class="user-is-cof">au COF{% else %}<tt class="user-is-not-cof">non-COF{% endif %}</tt></h2>
|
||||
<h2 class="member-status">{%if user.is_authenticated %}{% if user.first_name %}{{ user.first_name }}{% else %}<tt>{{ user.username }}</tt>{% endif %}, {% endif %}{% if user.profile.is_cof %}<tt class="user-is-cof">au COF{% elif user.profile.is_kfet %}<tt class="user-is-kfet">membre K-Fêt{% else %}<tt class="user-is-not-cof">non-COF{% endif %}</tt></h2>
|
||||
</div><!-- /.container -->
|
||||
</header>
|
||||
|
||||
|
|
19
gestioncof/templates/gestioncof/carte_kf.html
Normal file
19
gestioncof/templates/gestioncof/carte_kf.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load bootstrap %}
|
||||
|
||||
{% block page_size %}col-sm-8{%endblock%}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Pass K-Fêt</h2>
|
||||
|
||||
<center style="font-size: 20pt;">
|
||||
<p>Profil de {{ user.first_name }} {{ user.last_name }}</p>
|
||||
|
||||
{% if user.profile.is_cof %}
|
||||
<p>Membre COF depuis le {{ user.profile.date_adhesion_cof }}</p>
|
||||
{% else %}
|
||||
<p>Membre K-Fêt depuis le {{ user.profile.date_adhesion_kfet }}</p>
|
||||
{% endif %}
|
||||
</center>
|
||||
|
||||
{% endblock %}
|
|
@ -8,7 +8,7 @@
|
|||
<div class="container hidden-xs espace"></div>
|
||||
<div class="container">
|
||||
<div class="home-menu row">
|
||||
<div class="{% if user.profile.is_buro %}col-sm-6 {% else %}col-sm-8 col-sm-offset-2 col-xs-12 {%endif%}normal-user-hm">
|
||||
<div class="{% if user.profile.is_buro or user.profile.is_chef %}col-sm-6 {% else %}col-sm-8 col-sm-offset-2 col-xs-12 {%endif%}normal-user-hm">
|
||||
{% if open_surveys %}
|
||||
<h3 class="block-title">Sondages en cours<span class="pull-right glyphicon glyphicon-stats"></span></h3>
|
||||
<div class="hm-block">
|
||||
|
@ -50,6 +50,9 @@
|
|||
<ul>
|
||||
{# TODO: Since Django 1.9, we can store result with "as", allowing proper value management (if None) #}
|
||||
<li><a href="{% slugurl "k-fet" %}">Page d'accueil</a></li>
|
||||
{% if user.profile.is_cof or user.profile.is_kfet %}
|
||||
<li><a href="{% url "profile.carte" %}">Carte K-Fêt</a></li>
|
||||
{% endif %}
|
||||
<li><a href="https://cof.ens.fr/k-fet/le-calendrier/">Calendrier</a></li>
|
||||
{% if perms.kfet.is_team %}
|
||||
<li><a href="{% url 'kfet.kpsul' %}">K-Psul</a></li>
|
||||
|
@ -68,9 +71,23 @@
|
|||
{% if not user.profile.login_clipper %}
|
||||
<li><a href="{% url "password_change" %}">Changer mon mot de passe</a></li>
|
||||
{% endif %}
|
||||
{% if not user.profile.is_cof and not user.profile.is_kfet %}
|
||||
<li><a href="{% url "self.kf_registration" %}">Adhérer à la K-Fêt</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% if user.profile.is_chef and not user.profile.is_buro %}
|
||||
<div class="col-sm-6 buro-user-hm">
|
||||
<h3 class="block-title">Administration<span class="pull-right glyphicon glyphicon-cog"></span></h3>
|
||||
<div class="hm-block">
|
||||
<ul>
|
||||
<h4>Général</h4>
|
||||
<li><a href="{% url "registration" %}">Inscription d'un nouveau membre</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if user.profile.is_buro %}
|
||||
<div class="col-sm-6 buro-user-hm">
|
||||
<h3 class="block-title">Administration<span class="pull-right glyphicon glyphicon-cog"></span></h3>
|
||||
|
|
21
gestioncof/templates/gestioncof/registration_kf_form.html
Normal file
21
gestioncof/templates/gestioncof/registration_kf_form.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
{% load bootstrap %}
|
||||
|
||||
{% if login_clipper %}
|
||||
<h3>Inscription associée au compte clipper <tt>{{ login_clipper }}</tt></h3>
|
||||
{% elif member %}
|
||||
<h3>Inscription du compte GestioCOF existant <tt>{{ member.username }}</tt></h3>
|
||||
{% else %}
|
||||
<h3>Inscription d'un nouveau compte (extérieur ?)</h3>
|
||||
{% endif %}
|
||||
<form role="form" id="profile" method="post" action="{% url 'registration' %}">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
{{ user_form | bootstrap }}
|
||||
{{ profile_form | bootstrap }}
|
||||
</table>
|
||||
<hr />
|
||||
{% if login_clipper or member %}
|
||||
<input type="hidden" name="user_exists" value="1" />
|
||||
{% endif %}
|
||||
<input type="submit" class="btn btn-primary pull-right" value="Enregistrer l'inscription" />
|
||||
</form>
|
|
@ -0,0 +1,8 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Inscription d'un nouveau membre</h2>
|
||||
<div id="form-placeholder">
|
||||
{% include "gestioncof/registration_kf_form.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
28
gestioncof/templates/gestioncof/self_registration.html
Normal file
28
gestioncof/templates/gestioncof/self_registration.html
Normal file
|
@ -0,0 +1,28 @@
|
|||
{% extends "base_title.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block page_size %}col-sm-8{% endblock %}
|
||||
|
||||
{% load bootstrap %}
|
||||
|
||||
{% block realcontent %}
|
||||
|
||||
{% if member %}
|
||||
<h3>Inscription K-Fêt du compte GestioCOF existant <tt>{{ member.username }}</tt></h3>
|
||||
{% else %}
|
||||
<h3>Inscription K-Fêt d'un nouveau compte (extérieur ?)</h3>
|
||||
{% endif %}
|
||||
<form role="form" id="profile" method="post" action="{% url 'self.kf_registration' %}">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
{{ user_form | bootstrap }}
|
||||
{{ profile_form | bootstrap }}
|
||||
{{ agreement_form | bootstrap }}
|
||||
</table>
|
||||
<hr />
|
||||
{% if login_clipper or member %}
|
||||
<input type="hidden" name="user_exists" value="1" />
|
||||
{% endif %}
|
||||
<input type="submit" class="btn btn-primary pull-right" value="Adhérer" />
|
||||
</form>
|
||||
{% endblock %}
|
5
gestioncof/templates/kfet-denied.html
Normal file
5
gestioncof/templates/kfet-denied.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% extends "base_title.html" %}
|
||||
|
||||
{% block realcontent %}
|
||||
<h2>Section réservée aux membres K-Fêt -- merci de vous inscrire au COF ou de passer au COF/nous envoyer un mail si vous êtes déjà membre :)</h2>
|
||||
{% endblock %}
|
|
@ -486,7 +486,7 @@ class ExportMembersViewTests(CSVResponseMixin, ViewTestCaseMixin, TestCase):
|
|||
u1.last_name = "last"
|
||||
u1.email = "user@mail.net"
|
||||
u1.save()
|
||||
u1.profile.date_adhesion = date(2023, 5, 22)
|
||||
u1.profile.date_adhesion_cof = date(2023, 5, 22)
|
||||
u1.profile.phone = "0123456789"
|
||||
u1.profile.departement = "Dept"
|
||||
u1.profile.save()
|
||||
|
|
|
@ -80,6 +80,7 @@ registration_patterns = [
|
|||
views.RegistrationAutocompleteView.as_view(),
|
||||
name="cof.registration.autocomplete",
|
||||
),
|
||||
path("self_kf", views.self_kf_registration, name="self.kf_registration"),
|
||||
]
|
||||
|
||||
urlpatterns = [
|
||||
|
@ -99,6 +100,7 @@ urlpatterns = [
|
|||
name="cof-user-autocomplete",
|
||||
),
|
||||
path("config", views.ConfigUpdate.as_view(), name="config.edit"),
|
||||
path("carte", views.carte_kf, name="profile.carte"),
|
||||
# -----
|
||||
# Authentification
|
||||
# -----
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import csv
|
||||
import uuid
|
||||
from datetime import date, timedelta
|
||||
from smtplib import SMTPRecipientsRefused
|
||||
from datetime import timedelta
|
||||
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
|
||||
|
||||
from django.contrib import messages
|
||||
|
@ -14,11 +13,10 @@ from django.contrib.auth.views import (
|
|||
redirect_to_login,
|
||||
)
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.mail import send_mail
|
||||
from django.db.models import Q
|
||||
from django.http import Http404, HttpResponse, HttpResponseForbidden
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.template import loader
|
||||
from django.urls import reverse_lazy
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import FormView, TemplateView
|
||||
|
@ -27,7 +25,14 @@ from icalendar import Calendar, Event as Vevent
|
|||
|
||||
from bda.models import Spectacle, Tirage
|
||||
from gestioncof.autocomplete import cof_autocomplete
|
||||
from gestioncof.decorators import BuroRequiredMixin, buro_required, cof_required
|
||||
from gestioncof.decorators import (
|
||||
BuroRequiredMixin,
|
||||
ChefRequiredMixin,
|
||||
buro_required,
|
||||
chef_required,
|
||||
cof_required,
|
||||
kfet_required,
|
||||
)
|
||||
from gestioncof.forms import (
|
||||
CalendarForm,
|
||||
ClubsForm,
|
||||
|
@ -38,9 +43,11 @@ from gestioncof.forms import (
|
|||
GestioncofConfigForm,
|
||||
PhoneForm,
|
||||
ProfileForm,
|
||||
RegistrationKFProfileForm,
|
||||
RegistrationPassUserForm,
|
||||
RegistrationProfileForm,
|
||||
RegistrationUserForm,
|
||||
SubscribForm,
|
||||
SurveyForm,
|
||||
SurveyStatusFilterForm,
|
||||
UserForm,
|
||||
|
@ -86,7 +93,9 @@ class ResetComptes(BuroRequiredMixin, TemplateView):
|
|||
nb_adherents = CofProfile.objects.filter(is_cof=True).count()
|
||||
CofProfile.objects.update(
|
||||
is_cof=False,
|
||||
date_adhesion=None,
|
||||
is_kfet=False,
|
||||
date_adhesion_cof=None,
|
||||
date_adhesion_kfet=None,
|
||||
mailing_cof=False,
|
||||
mailing_bda=False,
|
||||
mailing_bda_revente=False,
|
||||
|
@ -421,13 +430,20 @@ def profile(request):
|
|||
return render(request, "gestioncof/profile.html", context)
|
||||
|
||||
|
||||
@kfet_required
|
||||
def carte_kf(request):
|
||||
user = request.user
|
||||
return render(request, "gestioncof/carte_kf.html", {"user": user})
|
||||
|
||||
|
||||
def registration_set_ro_fields(user_form, profile_form):
|
||||
user_form.fields["username"].widget.attrs["readonly"] = True
|
||||
profile_form.fields["login_clipper"].widget.attrs["readonly"] = True
|
||||
|
||||
|
||||
@buro_required
|
||||
@chef_required
|
||||
def registration_form2(request, login_clipper=None, username=None, fullname=None):
|
||||
is_buro = request.user.profile.is_buro
|
||||
events = Event.objects.filter(old=False).all()
|
||||
member = None
|
||||
if login_clipper:
|
||||
|
@ -449,21 +465,27 @@ def registration_form2(request, login_clipper=None, username=None, fullname=None
|
|||
user_form.fields["first_name"].initial = bits[0]
|
||||
if len(bits) > 1:
|
||||
user_form.fields["last_name"].initial = " ".join(bits[1:])
|
||||
if is_buro:
|
||||
# profile
|
||||
profile_form = RegistrationProfileForm(
|
||||
initial={"login_clipper": login_clipper}
|
||||
)
|
||||
registration_set_ro_fields(user_form, profile_form)
|
||||
# events & clubs
|
||||
event_formset = EventFormset(events=events, prefix="events")
|
||||
clubs_form = ClubsForm()
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm(
|
||||
initial={"login_clipper": login_clipper}
|
||||
)
|
||||
|
||||
registration_set_ro_fields(user_form, profile_form)
|
||||
if username:
|
||||
member = get_object_or_404(User, username=username)
|
||||
(profile, _) = CofProfile.objects.get_or_create(user=member)
|
||||
# already existing, prefill
|
||||
user_form = RegistrationUserForm(instance=member)
|
||||
if is_buro:
|
||||
profile_form = RegistrationProfileForm(instance=profile)
|
||||
registration_set_ro_fields(user_form, profile_form)
|
||||
# events
|
||||
current_registrations = []
|
||||
for event in events:
|
||||
|
@ -474,16 +496,25 @@ def registration_form2(request, login_clipper=None, username=None, fullname=None
|
|||
except EventRegistration.DoesNotExist:
|
||||
current_registrations.append(None)
|
||||
event_formset = EventFormset(
|
||||
events=events, prefix="events", current_registrations=current_registrations
|
||||
events=events,
|
||||
prefix="events",
|
||||
current_registrations=current_registrations,
|
||||
)
|
||||
# Clubs
|
||||
clubs_form = ClubsForm(initial={"clubs": member.clubs.all()})
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm(instance=profile)
|
||||
registration_set_ro_fields(user_form, profile_form)
|
||||
elif not login_clipper:
|
||||
# new user
|
||||
user_form = RegistrationPassUserForm()
|
||||
if is_buro:
|
||||
profile_form = RegistrationProfileForm()
|
||||
event_formset = EventFormset(events=events, prefix="events")
|
||||
clubs_form = ClubsForm()
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm()
|
||||
if is_buro:
|
||||
return render(
|
||||
request,
|
||||
"gestioncof/registration_form.html",
|
||||
|
@ -496,38 +527,22 @@ def registration_form2(request, login_clipper=None, username=None, fullname=None
|
|||
"clubs_form": clubs_form,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def notify_new_member(request, member: User):
|
||||
if not member.email:
|
||||
messages.warning(
|
||||
else:
|
||||
return render(
|
||||
request,
|
||||
"GestioCOF n'a pas d'adresse mail pour {}, ".format(member)
|
||||
+ "aucun email de bienvenue n'a été envoyé",
|
||||
)
|
||||
return
|
||||
|
||||
# Try to send a welcome email and report SMTP errors
|
||||
try:
|
||||
send_mail(
|
||||
"Bienvenue au COF",
|
||||
loader.render_to_string(
|
||||
"gestioncof/mails/welcome.txt", context={"member": member}
|
||||
),
|
||||
"cof@ens.fr",
|
||||
[member.email],
|
||||
)
|
||||
except SMTPRecipientsRefused:
|
||||
messages.error(
|
||||
request,
|
||||
"Error lors de l'envoi de l'email de bienvenue à {} ({})".format(
|
||||
member, member.email
|
||||
),
|
||||
"gestioncof/registration_kf_form.html",
|
||||
{
|
||||
"member": member,
|
||||
"login_clipper": login_clipper,
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@buro_required
|
||||
@chef_required
|
||||
def registration(request):
|
||||
is_buro = request.user.profile.is_buro
|
||||
if request.POST:
|
||||
request_dict = request.POST.copy()
|
||||
member = None
|
||||
|
@ -541,10 +556,15 @@ def registration(request):
|
|||
user_form = RegistrationPassUserForm(request_dict)
|
||||
else:
|
||||
user_form = RegistrationUserForm(request_dict)
|
||||
if is_buro:
|
||||
profile_form = RegistrationProfileForm(request_dict)
|
||||
clubs_form = ClubsForm(request_dict)
|
||||
events = Event.objects.filter(old=False).all()
|
||||
event_formset = EventFormset(events=events, data=request_dict, prefix="events")
|
||||
event_formset = EventFormset(
|
||||
events=events, data=request_dict, prefix="events"
|
||||
)
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm(request_dict)
|
||||
if "user_exists" in request_dict and request_dict["user_exists"]:
|
||||
username = request_dict["username"]
|
||||
try:
|
||||
|
@ -565,20 +585,25 @@ def registration(request):
|
|||
member = user_form.save()
|
||||
profile, _ = CofProfile.objects.get_or_create(user=member)
|
||||
was_cof = profile.is_cof
|
||||
was_kfet = profile.is_kfet
|
||||
# Maintenant on remplit le formulaire de profil
|
||||
if is_buro:
|
||||
profile_form = RegistrationProfileForm(request_dict, instance=profile)
|
||||
if (
|
||||
profile_form.is_valid()
|
||||
and event_formset.is_valid()
|
||||
and clubs_form.is_valid()
|
||||
else:
|
||||
profile_form = RegistrationKFProfileForm(request_dict, instance=profile)
|
||||
if profile_form.is_valid() and (
|
||||
not is_buro or (event_formset.is_valid() and clubs_form.is_valid())
|
||||
):
|
||||
# Enregistrement du profil
|
||||
profile = profile_form.save()
|
||||
if profile.is_cof and not was_cof:
|
||||
notify_new_member(request, member)
|
||||
profile.date_adhesion = date.today()
|
||||
profile.save()
|
||||
if is_buro:
|
||||
if profile.is_cof:
|
||||
profile.make_adh_cof(request, was_cof)
|
||||
|
||||
if profile.is_kfet:
|
||||
profile.make_adh_kfet(request, was_kfet)
|
||||
|
||||
if is_buro:
|
||||
# Enregistrement des inscriptions aux événements
|
||||
for form in event_formset:
|
||||
if "status" not in form.cleaned_data:
|
||||
|
@ -599,9 +624,13 @@ def registration(request):
|
|||
) = EventRegistration.objects.get_or_create(
|
||||
user=member, event=form.event
|
||||
)
|
||||
update_event_form_comments(form.event, form, current_registration)
|
||||
update_event_form_comments(
|
||||
form.event, form, current_registration
|
||||
)
|
||||
current_registration.options.set(all_choices)
|
||||
current_registration.paid = form.cleaned_data["status"] == "paid"
|
||||
current_registration.paid = (
|
||||
form.cleaned_data["status"] == "paid"
|
||||
)
|
||||
current_registration.save()
|
||||
# if form.event.title == "Mega 15" and created_reg:
|
||||
# field = EventCommentField.objects.get(
|
||||
|
@ -633,11 +662,12 @@ def registration(request):
|
|||
member.get_full_name(), member.email
|
||||
)
|
||||
)
|
||||
if profile.is_cof:
|
||||
if is_buro and profile.is_cof:
|
||||
msg += "\nIl est désormais membre du COF n°{:d} !".format(
|
||||
member.profile.id
|
||||
)
|
||||
messages.success(request, msg, extra_tags="safe")
|
||||
if is_buro:
|
||||
return render(
|
||||
request,
|
||||
"gestioncof/registration_post.html",
|
||||
|
@ -650,10 +680,73 @@ def registration(request):
|
|||
"clubs_form": clubs_form,
|
||||
},
|
||||
)
|
||||
else:
|
||||
return render(
|
||||
request,
|
||||
"gestioncof/registration_kf_post.html",
|
||||
{
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
"member": member,
|
||||
"login_clipper": login_clipper,
|
||||
},
|
||||
)
|
||||
else:
|
||||
return render(request, "registration.html")
|
||||
|
||||
|
||||
# TODO: without login
|
||||
@login_required
|
||||
def self_kf_registration(request):
|
||||
member = request.user
|
||||
(profile, _) = CofProfile.objects.get_or_create(user=member)
|
||||
|
||||
if profile.is_kfet or profile.is_cof:
|
||||
msg = "Vous êtes déjà adhérent du COF !"
|
||||
messages.success(request, msg)
|
||||
response = HttpResponse(content="", status=303)
|
||||
response["Location"] = reverse("profile")
|
||||
return response
|
||||
|
||||
was_kfet = profile.is_kfet
|
||||
if request.POST:
|
||||
user_form = RegistrationUserForm(request.POST, instance=member)
|
||||
profile_form = PhoneForm(request.POST, instance=profile)
|
||||
agreement_form = SubscribForm(request.POST)
|
||||
if (
|
||||
user_form.is_valid()
|
||||
and profile_form.is_valid()
|
||||
and agreement_form.is_valid()
|
||||
):
|
||||
member = user_form.save()
|
||||
profile = profile_form.save()
|
||||
profile.is_kfet = True
|
||||
profile.save()
|
||||
profile.make_adh_kfet(request, was_kfet)
|
||||
|
||||
msg = "Votre adhésion a été enregistrée avec succès."
|
||||
messages.success(request, msg, extra_tags="safe")
|
||||
response = HttpResponse(content="", status=303)
|
||||
response["Location"] = reverse("profile")
|
||||
return response
|
||||
else:
|
||||
user_form = RegistrationUserForm(instance=member)
|
||||
profile_form = PhoneForm(instance=profile)
|
||||
agreement_form = SubscribForm()
|
||||
|
||||
user_form.fields["username"].widget.attrs["readonly"] = True
|
||||
return render(
|
||||
request,
|
||||
"gestioncof/self_registration.html",
|
||||
{
|
||||
"user_form": user_form,
|
||||
"profile_form": profile_form,
|
||||
"agreement_form": agreement_form,
|
||||
"member": member,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# -----
|
||||
# Clubs
|
||||
# -----
|
||||
|
@ -707,7 +800,7 @@ def export_members(request):
|
|||
response["Content-Disposition"] = "attachment; filename=membres_cof.csv"
|
||||
|
||||
writer = csv.writer(response)
|
||||
for profile in CofProfile.objects.filter(is_cof=True).all():
|
||||
for profile in CofProfile.objects.filter(Q(is_cof=True) | Q(is_kfet=True)).all():
|
||||
user = profile.user
|
||||
bits = [
|
||||
user.id,
|
||||
|
@ -718,8 +811,10 @@ def export_members(request):
|
|||
profile.phone,
|
||||
profile.occupation,
|
||||
profile.departement,
|
||||
"COF" if profile.is_cof else "K-Fêt",
|
||||
profile.type_cotiz,
|
||||
profile.date_adhesion,
|
||||
profile.date_adhesion_cof,
|
||||
profile.date_adhesion_kfet,
|
||||
]
|
||||
writer.writerow([str(bit) for bit in bits])
|
||||
|
||||
|
@ -975,6 +1070,6 @@ class UserAutocompleteView(BuroRequiredMixin, Select2QuerySetView):
|
|||
search_fields = ("username", "first_name", "last_name")
|
||||
|
||||
|
||||
class RegistrationAutocompleteView(BuroRequiredMixin, AutocompleteView):
|
||||
class RegistrationAutocompleteView(ChefRequiredMixin, AutocompleteView):
|
||||
template_name = "gestioncof/search_results.html"
|
||||
search_composer = cof_autocomplete
|
||||
|
|
|
@ -195,7 +195,13 @@ class CofForm(forms.ModelForm):
|
|||
|
||||
class Meta:
|
||||
model = CofProfile
|
||||
fields = ["login_clipper", "is_cof", "departement"]
|
||||
fields = ["login_clipper", "is_cof", "is_kfet", "departement"]
|
||||
|
||||
|
||||
class CofKFForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CofProfile
|
||||
fields = ["is_kfet"]
|
||||
|
||||
|
||||
class UserForm(forms.ModelForm):
|
||||
|
@ -350,6 +356,7 @@ class ArticleForm(forms.ModelForm):
|
|||
fields = [
|
||||
"name",
|
||||
"is_sold",
|
||||
"no_exte",
|
||||
"hidden",
|
||||
"price",
|
||||
"stock",
|
||||
|
@ -364,6 +371,7 @@ class ArticleRestrictForm(ArticleForm):
|
|||
fields = [
|
||||
"name",
|
||||
"is_sold",
|
||||
"no_exte",
|
||||
"hidden",
|
||||
"price",
|
||||
"category",
|
||||
|
|
20
kfet/migrations/0081_article_no_exte.py
Normal file
20
kfet/migrations/0081_article_no_exte.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 4.2.16 on 2025-01-06 16:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("kfet", "0080_accountnegative_last_rappel"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="article",
|
||||
name="no_exte",
|
||||
field=models.BooleanField(
|
||||
default=False, verbose_name="Réservé au adhérents"
|
||||
),
|
||||
),
|
||||
]
|
34
kfet/migrations/0082_alter_operation_options.py
Normal file
34
kfet/migrations/0082_alter_operation_options.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Generated by Django 4.2.16 on 2025-01-18 10:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("kfet", "0081_article_no_exte"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="operation",
|
||||
options={
|
||||
"permissions": (
|
||||
("perform_deposit", "Effectuer une charge"),
|
||||
(
|
||||
"perform_negative_operations",
|
||||
"Enregistrer des commandes en négatif",
|
||||
),
|
||||
(
|
||||
"perform_liq_reserved",
|
||||
"Effectuer une opération réservé aux adhérents sur LIQ",
|
||||
),
|
||||
("cancel_old_operations", "Annuler des commandes non récentes"),
|
||||
(
|
||||
"perform_commented_operations",
|
||||
"Enregistrer des commandes avec commentaires",
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
]
|
18
kfet/migrations/0083_operationgroup_is_kfet.py
Normal file
18
kfet/migrations/0083_operationgroup_is_kfet.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.16 on 2025-03-18 10:25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("kfet", "0082_alter_operation_options"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="operationgroup",
|
||||
name="is_kfet",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
|
@ -119,6 +119,10 @@ class Account(models.Model):
|
|||
def is_cof(self):
|
||||
return self.cofprofile.is_cof
|
||||
|
||||
@property
|
||||
def is_kfet(self):
|
||||
return self.cofprofile.is_kfet
|
||||
|
||||
# Propriétés supplémentaires
|
||||
@property
|
||||
def balance_ukf(self):
|
||||
|
@ -494,6 +498,7 @@ class ArticleCategory(models.Model):
|
|||
class Article(models.Model):
|
||||
name = models.CharField("nom", max_length=45)
|
||||
is_sold = models.BooleanField("en vente", default=True)
|
||||
no_exte = models.BooleanField("Réservé au adhérents", default=False)
|
||||
hidden = models.BooleanField(
|
||||
"caché",
|
||||
default=False,
|
||||
|
@ -682,6 +687,7 @@ class OperationGroup(models.Model):
|
|||
at = models.DateTimeField(default=timezone.now)
|
||||
amount = models.DecimalField(max_digits=6, decimal_places=2, default=0)
|
||||
is_cof = models.BooleanField(default=False)
|
||||
is_kfet = models.BooleanField(default=False)
|
||||
# Optional
|
||||
comment = models.CharField(max_length=255, blank=True, default="")
|
||||
valid_by = models.ForeignKey(
|
||||
|
@ -754,6 +760,10 @@ class Operation(models.Model):
|
|||
permissions = (
|
||||
("perform_deposit", "Effectuer une charge"),
|
||||
("perform_negative_operations", "Enregistrer des commandes en négatif"),
|
||||
(
|
||||
"perform_liq_reserved",
|
||||
"Effectuer une opération réservé aux adhérents sur LIQ",
|
||||
),
|
||||
("cancel_old_operations", "Annuler des commandes non récentes"),
|
||||
(
|
||||
"perform_commented_operations",
|
||||
|
|
|
@ -5,6 +5,7 @@ var Account = Backbone.Model.extend({
|
|||
'name': '',
|
||||
'email': '',
|
||||
'is_cof': '',
|
||||
'is_kfet': '',
|
||||
'promo': '',
|
||||
'balance': '',
|
||||
'is_frozen': false,
|
||||
|
@ -69,7 +70,7 @@ var AccountView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
get_is_cof: function () {
|
||||
return this.model.get("is_cof") ? 'COF' : 'Non-COF';
|
||||
return this.model.get("is_cof") ? 'Membre COF' : (this.model.get("is_kfet") ? 'Membre K-Fêt' : 'Non-COF');
|
||||
},
|
||||
|
||||
get_balance: function () {
|
||||
|
|
|
@ -34,6 +34,7 @@ Modification de mes informations
|
|||
{% include 'kfet/form_snippet.html' with form=account_form %}
|
||||
{% include 'kfet/form_snippet.html' with form=frozen_form %}
|
||||
{% include 'kfet/form_snippet.html' with form=group_form %}
|
||||
{% include 'kfet/form_snippet.html' with form=cof_form %}
|
||||
{% include 'kfet/form_snippet.html' with form=pwd_form %}
|
||||
|
||||
{% include 'kfet/form_authentication_snippet.html' %}
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
<td class="text-right">Prix</td>
|
||||
<td class="text-right">Stock</td>
|
||||
<td class="text-right" data-sorter="article__is_sold">En vente</td>
|
||||
<td class="text-right" data-sorter="article__is_no_exte">Reservé aux adhérents</td>
|
||||
<td class="text-right" data-sorter="article__hidden">Affiché</td>
|
||||
<td class="text-right" data-sorter="shortDate">Dernier inventaire</td>
|
||||
</tr>
|
||||
|
@ -63,6 +64,7 @@
|
|||
<td class="text-right">{{ article.price }}€</td>
|
||||
<td class="text-right">{{ article.stock }}</td>
|
||||
<td class="text-right">{{ article.is_sold | yesno:"En vente,Non vendu"}}</td>
|
||||
<td class="text-right">{{ article.no_exte | yesno:"Réservé,Non réservé"}}</td>
|
||||
<td class="text-right">{{ article.hidden | yesno:"Caché,Affiché" }}</td>
|
||||
{% with last_inventory=article.inventory.0 %}
|
||||
<td class="text-right" title="{{ last_inventory.at }}">
|
||||
|
@ -88,6 +90,7 @@
|
|||
<td class="text-right">Prix</td>
|
||||
<td class="text-right">Stock</td>
|
||||
<td class="text-right" data-sorter="article__is_sold">En vente</td>
|
||||
<td class="text-right" data-sorter="article__is_no_exte">Reservé aux adhérents</td>
|
||||
<td class="text-right" data-sorter="article__hidden">Affiché</td>
|
||||
<td class="text-right" data-sorter="shortDate">Dernier inventaire</td>
|
||||
</tr>
|
||||
|
@ -111,6 +114,7 @@
|
|||
<td class="text-right">{{ article.price }}€</td>
|
||||
<td class="text-right">{{ article.stock }}</td>
|
||||
<td class="text-right">{{ article.is_sold | yesno:"En vente,Non vendu"}}</td>
|
||||
<td class="text-right">{{ article.no_exte | yesno:"Réservé,Non réservé"}}</td>
|
||||
<td class="text-right">{{ article.hidden | yesno:"Caché,Affiché" }}</td>
|
||||
{% with last_inventory=article.inventory.0 %}
|
||||
<td class="text-right" title="{{ last_inventory.at }}">
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<li><b>Stock:</b> {{ article.stock }}</li>
|
||||
<li><b>En vente:</b> {{ article.is_sold|yesno|title }}</li>
|
||||
<li><b>Affiché:</b> {{ article.hidden|yesno|title }}</li>
|
||||
<li><b>Réservé aux adhérents:</b> {{ article.no_exte|yesno|title }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
|
|
|
@ -397,8 +397,8 @@ $(document).ready(function() {
|
|||
// -----
|
||||
|
||||
var articles_container = $('#articles_data tbody');
|
||||
var article_category_default_html = '<tr class="category"><td colspan="3"></td></tr>';
|
||||
var article_default_html = '<tr class="article"><td class="name"></td><td class="price"></td><td class="stock"></td></tr>';
|
||||
var article_category_default_html = '<tr class="category"><td colspan="4"></td></tr>';
|
||||
var article_default_html = '<tr class="article"><td class="name"></td><td class="price"></td><td class="no_exte"></td><td class="stock" style="width: 0;white-space: nowrap;"></td></tr>';
|
||||
|
||||
function addArticle(article) {
|
||||
var article_html = $(article_default_html);
|
||||
|
@ -411,6 +411,7 @@ $(document).ready(function() {
|
|||
article_html.addClass('low-stock');
|
||||
}
|
||||
article_html.find('.price').text(amountToUKF(article['price'], false, false)+' UKF');
|
||||
article_html.find('.no_exte').text(article['no_exte'] ? "Réservé aux adhérents" : "");
|
||||
var category_html = articles_container
|
||||
.find('#data-category-'+article['category_id']);
|
||||
if (category_html.length == 0) {
|
||||
|
|
|
@ -43,7 +43,9 @@
|
|||
<li>
|
||||
{% if account.is_cof %}
|
||||
<span title="Réduction de {{ kfet_config.reduction_cof }} % sur tes commandes" data-toggle="tooltip"
|
||||
data-placement="right">Adhérent COF</span>
|
||||
data-placement="right">Membre COF</span>
|
||||
{% elif account.is_kfet %}
|
||||
Membre K-Fêt
|
||||
{% else %}
|
||||
Non-COF
|
||||
{% endif %}
|
||||
|
|
|
@ -66,6 +66,7 @@ from kfet.forms import (
|
|||
CheckoutStatementCreateForm,
|
||||
CheckoutStatementUpdateForm,
|
||||
CofForm,
|
||||
CofKFForm,
|
||||
ContactForm,
|
||||
DemandeSoireeForm,
|
||||
FilterHistoryForm,
|
||||
|
@ -187,13 +188,17 @@ def account(request):
|
|||
positive_accounts = Account.objects.filter(balance__gte=0).exclude(trigramme="#13")
|
||||
negative_accounts = Account.objects.filter(balance__lt=0).exclude(trigramme="#13")
|
||||
|
||||
return render(request, "kfet/account.html", {
|
||||
return render(
|
||||
request,
|
||||
"kfet/account.html",
|
||||
{
|
||||
"accounts": accounts,
|
||||
"positive_count": positive_accounts.count(),
|
||||
"positives_sum": sum(acc.balance for acc in positive_accounts),
|
||||
"negative_count": negative_accounts.count(),
|
||||
"negatives_sum": sum(acc.balance for acc in negative_accounts),
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -252,6 +257,11 @@ def account_create(request):
|
|||
account = trigramme_form.save(data=data)
|
||||
account_form = AccountNoTriForm(request.POST, instance=account)
|
||||
account_form.save()
|
||||
was_kfet = account.is_kfet
|
||||
account.cofprofile.is_kfet = cof_form.cleaned_data["is_kfet"]
|
||||
account.cofprofile.save()
|
||||
if account.cofprofile.is_kfet:
|
||||
account.cofprofile.make_adh_kfet(request, was_kfet)
|
||||
messages.success(request, "Compte créé : %s" % account.trigramme)
|
||||
account.send_creation_email()
|
||||
return redirect("kfet.account.create")
|
||||
|
@ -432,6 +442,7 @@ def account_update(request, trigramme):
|
|||
account_form = AccountForm(instance=account)
|
||||
group_form = UserGroupForm(instance=account.user)
|
||||
frozen_form = AccountFrozenForm(instance=account)
|
||||
cof_form = CofKFForm(instance=account.cofprofile)
|
||||
pwd_form = AccountPwdForm()
|
||||
|
||||
if request.method == "POST":
|
||||
|
@ -439,6 +450,7 @@ def account_update(request, trigramme):
|
|||
account_form = AccountForm(request.POST, instance=account)
|
||||
group_form = UserGroupForm(request.POST, instance=account.user)
|
||||
frozen_form = AccountFrozenForm(request.POST, instance=account)
|
||||
cof_form = CofKFForm(request.POST, instance=account.cofprofile)
|
||||
pwd_form = AccountPwdForm(request.POST, account=account)
|
||||
|
||||
forms = []
|
||||
|
@ -455,6 +467,11 @@ def account_update(request, trigramme):
|
|||
elif group_form.has_changed():
|
||||
warnings.append("statut d'équipe")
|
||||
|
||||
if request.user.has_perm("kfet.change_account"):
|
||||
forms.append(cof_form)
|
||||
elif cof_form.has_changed():
|
||||
warnings.append("adhésion kfet")
|
||||
|
||||
# Il ne faut pas valider `pwd_form` si elle est inchangée
|
||||
if pwd_form.has_changed():
|
||||
if self_update or request.user.has_perm("kfet.change_account_password"):
|
||||
|
@ -471,8 +488,11 @@ def account_update(request, trigramme):
|
|||
)
|
||||
else:
|
||||
if all(form.is_valid() for form in forms):
|
||||
was_kfet = account.is_kfet
|
||||
for form in forms:
|
||||
form.save()
|
||||
if account.is_kfet:
|
||||
account.cofprofile.make_adh_kfet(request, was_kfet)
|
||||
|
||||
if len(warnings):
|
||||
messages.warning(
|
||||
|
@ -504,6 +524,7 @@ def account_update(request, trigramme):
|
|||
"frozen_form": frozen_form,
|
||||
"group_form": group_form,
|
||||
"pwd_form": pwd_form,
|
||||
"cof_form": cof_form,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -988,6 +1009,7 @@ def account_read_json(request, trigramme):
|
|||
"name": account.name,
|
||||
"email": account.email,
|
||||
"is_cof": account.is_cof,
|
||||
"is_kfet": account.is_kfet,
|
||||
"promo": account.promo,
|
||||
"balance": account.balance,
|
||||
"is_frozen": account.is_frozen,
|
||||
|
@ -1164,6 +1186,22 @@ def kpsul_perform_operations(request):
|
|||
if is_addcost and operation.article.category.has_addcost:
|
||||
operation.addcost_amount /= cof_grant_divisor
|
||||
operation.amount = operation.amount / cof_grant_divisor
|
||||
if not on_acc.is_cof and not on_acc.is_kfet and operation.article.no_exte:
|
||||
if on_acc.is_cash:
|
||||
required_perms.add("kfet.perform_liq_reserved")
|
||||
else:
|
||||
data["errors"].append(
|
||||
{
|
||||
"code": "reserved",
|
||||
"message": (
|
||||
"L'article "
|
||||
+ operation.article.name
|
||||
+ " est réservé aux adhérents du COF, or "
|
||||
+ on_acc.trigramme
|
||||
+ " ne l'est pas"
|
||||
),
|
||||
}
|
||||
)
|
||||
to_articles_stocks[operation.article] -= operation.article_nb
|
||||
else:
|
||||
if on_acc.is_cash:
|
||||
|
@ -1221,6 +1259,7 @@ def kpsul_perform_operations(request):
|
|||
operationgroup.valid_by = request.user.profile.account_kfet
|
||||
# Filling cof status for statistics
|
||||
operationgroup.is_cof = on_acc.is_cof
|
||||
operationgroup.is_kfet = on_acc.is_kfet
|
||||
|
||||
# Starting transaction to ensure data consistency
|
||||
with transaction.atomic():
|
||||
|
@ -1271,6 +1310,7 @@ def kpsul_perform_operations(request):
|
|||
"checkout__name": operationgroup.checkout.name,
|
||||
"at": operationgroup.at,
|
||||
"is_cof": operationgroup.is_cof,
|
||||
"is_kfet": operationgroup.is_kfet,
|
||||
"comment": operationgroup.comment,
|
||||
"valid_by__trigramme": (
|
||||
operationgroup.valid_by and operationgroup.valid_by.trigramme or None
|
||||
|
@ -1486,7 +1526,7 @@ def cancel_operations(request):
|
|||
# Sort objects by pk to get deterministic responses.
|
||||
opegroups_pk = [opegroup.pk for opegroup in to_groups_amounts]
|
||||
opegroups = (
|
||||
OperationGroup.objects.values("id", "amount", "is_cof")
|
||||
OperationGroup.objects.values("id", "amount", "is_cof", "is_kfet")
|
||||
.filter(pk__in=opegroups_pk)
|
||||
.order_by("pk")
|
||||
)
|
||||
|
@ -1715,6 +1755,7 @@ def kpsul_articles_data(request):
|
|||
"id",
|
||||
"name",
|
||||
"price",
|
||||
"no_exte",
|
||||
"stock",
|
||||
"category_id",
|
||||
"category__name",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue