diff --git a/noethysweb/core/decorators.py b/noethysweb/core/decorators.py index 2a3d24f2..42585211 100644 --- a/noethysweb/core/decorators.py +++ b/noethysweb/core/decorators.py @@ -2,12 +2,12 @@ # Copyright (c) 2019-2021 Ivan LUCAS. # Noethysweb, application de gestion multi-activités. # Distribué sous licence GNU GPL. - +import logging +logger = logging.getLogger(__name__) from django.urls import reverse_lazy, reverse from django.http import HttpResponseRedirect, HttpResponseBadRequest, HttpResponseForbidden -from django.contrib.auth import REDIRECT_FIELD_NAME -from django.contrib.auth.decorators import user_passes_test from reglements.utils import utils_ventilation +from core.models import PortailParametre def Verifie_ventilation(function): @@ -39,6 +39,10 @@ def _function(request, *args, **kwargs): def secure_ajax_portail(function): """ A associer aux requêtes AJAX """ def _function(request, *args, **kwargs): + compte_famille = PortailParametre.objects.filter(code="compte_famille").first() + compte_individu = PortailParametre.objects.filter(code="compte_individu").first() + # logger.debug("compte_famille: %s" % compte_famille.valeur) + # logger.debug("compte_individu: %s" % compte_individu.valeur) # Vérifie que c'est une requête AJAX if not request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest': return HttpResponseBadRequest() @@ -46,8 +50,11 @@ def _function(request, *args, **kwargs): if not request.user.is_authenticated: return HttpResponseForbidden() # Vérifie que c'est un user de type utilisateur - if request.user.categorie != "famille": + # et que si le paramètre de compte individu est true ( coché ) alors interdire l'accès ou l'utilisation des identifiants famille + if((request.user.categorie not in ["famille", "individu"]) # Vérification de la catégorie : Si l'utilisateur n'est ni "famille" ni "individu", l'accès est interdit. + or (compte_famille != "True" and request.user.categorie == "famille") #Compte famille non activé pour une famille: Si compte_famille n'est pas "True" et que la catégorie est "famille", l'accès est interdit. + or ( compte_individu == "True" and request.user.categorie == "famille")): #Compte individu activé pour une famille: Si compte_individu est "True" alors que l'utilisateur est de catégorie "famille", l'accès est interdit. + # if request.user.categorie != "individu": return HttpResponseForbidden() return function(request, *args, **kwargs) return _function - diff --git a/noethysweb/core/models.py b/noethysweb/core/models.py index 3075ab72..e33f605b 100644 --- a/noethysweb/core/models.py +++ b/noethysweb/core/models.py @@ -262,7 +262,7 @@ def get_storage(nom_champ=None): class AdresseMail(models.Model): idadresse = models.AutoField(verbose_name="ID", db_column='IDadresse', primary_key=True) - adresse = encrypt(models.EmailField(verbose_name="Adresse d'envoi", max_length=300, help_text="Saisissez l'adresse mail utilisée.")) + adresse = models.EmailField(verbose_name="Adresse d'envoi", max_length=300, help_text="Saisissez l'adresse mail utilisée.") motdepasse = encrypt(models.CharField(verbose_name="Mot de passe", max_length=300, blank=True, null=True, help_text="Saisissez le mot de passe de la messagerie.")) hote = encrypt(models.CharField(verbose_name="Hôte", max_length=300, blank=True, null=True, help_text="Saisissez le nom de l'hôte de la messagerie (Ex: smtp.orange.fr, smtp.gmail.com...).")) port = models.IntegerField(verbose_name="Port", blank=True, null=True, help_text="Saisissez le numéro de port (Ex: 995, 465...)") @@ -1538,7 +1538,7 @@ class ModelePrestation(models.Model): structure = models.ForeignKey(Structure, verbose_name="Structure", on_delete=models.PROTECT, blank=True, null=True) # type_quotient = models.ForeignKey(TypeQuotient, verbose_name="Type de QF", blank=True, null=True, on_delete=models.SET_NULL, help_text="Sélectionnez un type de quotient familial ou laissez le champ vide pour tenir compte de tous les types de quotients.") multiprestations = models.TextField(verbose_name="Multi-prestations", blank=True, null=True) - + class Meta: db_table = 'modeles_prestations' verbose_name = "modèle de prestation" @@ -1649,7 +1649,17 @@ class Meta: def __str__(self): return "Remplissage ID%d" % self.idremplissage if self.idremplissage else "Nouveau" +class CategorieCompteInternet(models.Model): + idcategorie = models.AutoField(verbose_name="ID", db_column='IDcategorie', primary_key=True) + nom = models.CharField(verbose_name="Nom", max_length=200) + + class Meta: + db_table = 'categories_compte_internet' + verbose_name = "catégorie de compte internet" + verbose_name_plural = "catégories de compte internet" + def __str__(self): + return self.nom class Individu(models.Model): idindividu = models.AutoField(verbose_name="ID", db_column='IDindividu', primary_key=True) @@ -1696,6 +1706,17 @@ class Individu(models.Model): type_garde_choix = [(1, "Mère"), (2, "Père"), (3, "Garde alternée"), (4, "Autre personne")] type_garde = models.IntegerField(verbose_name=_("Type de garde"), choices=type_garde_choix, blank=True, null=True) info_garde = models.TextField(verbose_name=_("Information sur la garde"), blank=True, null=True) + # new attributs + internet_categorie = models.ForeignKey(CategorieCompteInternet, verbose_name="Catégorie",related_name="internet_categori", on_delete=models.PROTECT, blank=True,null=True) + internet_actif = models.BooleanField(verbose_name="Compte internet activé", default=True) + internet_identifiant = encrypt(models.CharField(verbose_name="Identifiant", max_length=200, blank=True, null=True)) + internet_mdp = encrypt(models.CharField(verbose_name="Mot de passe", max_length=200, blank=True, null=True)) + internet_secquest = models.CharField(verbose_name="Question", max_length=200, blank=True, null=True) + internet_reservations = models.BooleanField(verbose_name="Autoriser les réservations sur le portail", default=True) + mobile = encrypt(models.CharField(verbose_name="Portable favori", max_length=100, blank=True, null=True)) + utilisateur = models.OneToOneField(Utilisateur, on_delete=models.CASCADE, null=True) + certification_date = models.DateTimeField(verbose_name="Date de certification", blank=True, null=True) + blocage_impayes_off = models.BooleanField(verbose_name="Ne jamais appliquer le blocage des réservations si impayés",default=False,help_text="En cochant cette case, vous permettez à cette famille d'accéder aux réservations du portail même s'il y a des impayés et que le paramètre 'blocage si impayés' a été activé dans les paramètres généraux du portail.") class Meta: db_table = 'individus' @@ -1767,6 +1788,9 @@ def Maj_infos(self): self.ville_resid = dict_adresse["ville"] self.secteur = dict_adresse["secteur"] self.save() + def save_individu(sender, instance, **kwargs): + if hasattr(instance, 'individu'): + instance.individu.save() class Scolarite(models.Model): @@ -1787,19 +1811,6 @@ def __str__(self): return "Etape de scolarité du %s au %s" % (self.date_debut.strftime('%d/%m/%Y'), self.date_fin.strftime('%d/%m/%Y')) -class CategorieCompteInternet(models.Model): - idcategorie = models.AutoField(verbose_name="ID", db_column='IDcategorie', primary_key=True) - nom = models.CharField(verbose_name="Nom", max_length=200) - - class Meta: - db_table = 'categories_compte_internet' - verbose_name = "catégorie de compte internet" - verbose_name_plural = "catégories de compte internet" - - def __str__(self): - return self.nom - - class Famille(models.Model): idfamille = models.AutoField(verbose_name="ID", db_column='IDfamille', primary_key=True) date_creation = models.DateTimeField(verbose_name="Date de création", auto_now_add=True) @@ -1848,7 +1859,8 @@ class Famille(models.Model): mobile_blocage = models.BooleanField(verbose_name="La famille ne souhaite pas recevoir de SMS groupés", default=False, help_text="L'éditeur de SMS groupés du menu Outils ne proposera pas cette famille dans les destinataires.") individus_masques = models.ManyToManyField(Individu, verbose_name="Individus masqués", related_name="individus_masques", blank=True) blocage_impayes_off = models.BooleanField(verbose_name="Ne jamais appliquer le blocage des réservations si impayés", default=False, help_text="En cochant cette case, vous permettez à cette famille d'accéder aux réservations du portail même s'il y a des impayés et que le paramètre 'blocage si impayés' a été activé dans les paramètres généraux du portail.") - + contact_facturation = models.ForeignKey(Individu, verbose_name="Contact facturation", related_name="contact_facturation", on_delete=models.SET_NULL, blank=True, null=True) + utilisateur = models.OneToOneField(Utilisateur, on_delete=models.CASCADE, null=True) class Meta: db_table = 'familles' verbose_name = "famille" @@ -1907,18 +1919,25 @@ def Maj_infos(self, maj_adresse=True, maj_mail=True, maj_mobile=True, maj_titula # Titulaire Hélios if maj_titulaire_helios: - if self.titulaire_helios: - # recherche si le titulaire est toujours dans la famille - found = False - for rattachement in rattachements: - if rattachement.individu == self.titulaire_helios: - found = True - if not found: - self.titulaire_helios = None - if not self.titulaire_helios: - # Recherche un individu valide parmi les titulaires de la famille - if rattachements: - self.titulaire_helios = rattachements.first().individu + try: + if self.titulaire_helios: + # Recherche si le titulaire est toujours dans la famille + found = False + for rattachement in rattachements: + if rattachement.individu == self.titulaire_helios: + found = True + break # Si trouvé, on sort de la boucle + if not found: + # Si le titulaire n'est pas trouvé dans la famille, on le met à None + self.titulaire_helios = None + except Individu.DoesNotExist: + # Si le titulaire a été supprimé, on le met à None + self.titulaire_helios = None + + # Assurez-vous qu'il y a un titulaire dans la famille si nécessaire + if not self.titulaire_helios and rattachements.exists(): + # Recherche un individu valide parmi les rattachements + self.titulaire_helios = rattachements.first().individu if maj_tiers_solidaire: if self.tiers_solidaire: @@ -1950,6 +1969,10 @@ def Maj_infos(self, maj_adresse=True, maj_mail=True, maj_mobile=True, maj_titula def Get_rue_resid(self): return self.rue_resid.replace("\n", "
") if self.rue_resid else None + def Get_nom(self): + texte = self.nom + return texte + def Get_infos(self, avec_civilite=False): """ Renvoie le nom des titulaires """ rattachements = Rattachement.objects.prefetch_related('individu').filter(famille=self, categorie=1, titulaire=True).order_by("individu__civilite") @@ -2012,6 +2035,9 @@ def Is_inscription_in_periode(self, date_min=None, date_max=None): else: return self.date_debut <= date_max + def Get_activite(self): + texte = self.activite + return texte class Information(models.Model): idinformation = models.AutoField(verbose_name="ID", db_column='IDinformation', primary_key=True) @@ -3042,6 +3068,8 @@ class Destinataire(models.Model): categorie = models.CharField(verbose_name="Catégorie", max_length=300, blank=True, null=True) individu = models.ForeignKey(Individu, verbose_name="Individu", blank=True, null=True, on_delete=models.CASCADE) famille = models.ForeignKey(Famille, verbose_name="Famille", blank=True, null=True, on_delete=models.CASCADE) + inscription = models.ForeignKey(Inscription, verbose_name="Inscription", blank=True, null=True,on_delete=models.CASCADE) + activites = models.ForeignKey(Activite, verbose_name="Activites", blank=True, null=True, on_delete=models.CASCADE) collaborateur = models.ForeignKey("Collaborateur", verbose_name="Collaborateur", blank=True, null=True, on_delete=models.CASCADE) contact = models.ForeignKey(Contact, verbose_name="Contact", blank=True, null=True, on_delete=models.CASCADE) liste_diffusion = models.ForeignKey(ListeDiffusion, verbose_name="Liste de diffusion", blank=True, null=True, on_delete=models.CASCADE) @@ -3129,22 +3157,40 @@ def Is_active_today(self): """ Vérifie si la période est active ce jour """ return self.affichage == "TOUJOURS" or (datetime.datetime.now() >= self.affichage_date_debut and datetime.datetime.now() <= self.affichage_date_fin) - def Is_famille_authorized(self, famille=None): + def Is_famille_authorized(self, famille=None, individu=None): """ Vérifie si une famille est autorisée à accéder à cette période """ # Vérification du compte internet - if (self.types_categories == "AUCUNE" and famille.internet_categorie) or (self.types_categories == "SELECTION" and famille.internet_categorie not in self.categories.all()): + if (self.types_categories == "AUCUNE" and famille.internet_categorie) or ( + self.types_categories == "SELECTION" and famille.internet_categorie not in self.categories.all()): return False # Vérification de la ville de résidence if self.types_villes != "TOUTES" and self.villes: liste_villes = [ville.strip().upper() for ville in self.villes.split(",")] - if not famille.ville_resid or (self.types_villes == "SELECTION" and famille.ville_resid.upper() not in liste_villes) or (self.types_villes == "SELECTION_INVERSE" and famille.ville_resid.upper() in liste_villes): + if not famille.ville_resid or ( + self.types_villes == "SELECTION" and famille.ville_resid.upper() not in liste_villes) or ( + self.types_villes == "SELECTION_INVERSE" and famille.ville_resid.upper() in liste_villes): + return False + return True + + def Is_individu_authorized(self, individu=None): + """ Vérifie si un individu est autorisée à accéder à cette période """ + # Vérification du compte internet + if (self.types_categories == "AUCUNE" and individu.internet_categorie) or ( + self.types_categories == "SELECTION" and individu.internet_categorie not in self.categories.all()): + return False + # Vérification de la ville de résidence + if self.types_villes != "TOUTES" and self.villes: + liste_villes = [ville.strip().upper() for ville in self.villes.split(",")] + if not individu.ville_resid or ( + self.types_villes == "SELECTION" and individu.ville_resid.upper() not in liste_villes) or ( + self.types_villes == "SELECTION_INVERSE" and individu.ville_resid.upper() in liste_villes): return False return True class PortailParametre(models.Model): idparametre = models.AutoField(verbose_name="ID", db_column='IDparametre', primary_key=True) - code = models.CharField(verbose_name="Code", max_length=200, blank=True, null=True) + code = models.CharField(verbose_name="Code", max_length=200, blank=True, null=True, unique=True) valeur = models.TextField(verbose_name="Valeur", blank=True, null=True) class Meta: @@ -3202,17 +3248,26 @@ def __str__(self): class PortailMessage(models.Model): idmessage = models.AutoField(verbose_name="ID", db_column='IDmessage', primary_key=True) - famille = models.ForeignKey(Famille, verbose_name="Famille", on_delete=models.CASCADE) - structure = models.ForeignKey(Structure, verbose_name="Structure", on_delete=models.CASCADE) - utilisateur = models.ForeignKey(Utilisateur, verbose_name="Utilisateur", blank=True, null=True, on_delete=models.PROTECT) + famille = models.ForeignKey(Famille, verbose_name="Famille", on_delete=models.CASCADE, db_index=True) + individu = models.ForeignKey(Individu, verbose_name="Individu", on_delete=models.CASCADE, null=True, db_index=True) + structure = models.ForeignKey(Structure, verbose_name="Structure", on_delete=models.CASCADE, db_index=True) + utilisateur = models.ForeignKey(Utilisateur, verbose_name="Utilisateur", blank=True, null=True,on_delete=models.PROTECT, db_index=True) texte = models.TextField(verbose_name="Texte") - date_creation = models.DateTimeField(verbose_name="Date de création", auto_now_add=True) - date_lecture = models.DateTimeField(verbose_name="Date de lecture", max_length=200, blank=True, null=True) + date_creation = models.DateTimeField(verbose_name="Date de création", auto_now_add=True, db_index=True) + date_lecture = models.DateTimeField(verbose_name="Date de lecture", max_length=200, blank=True, null=True, db_index=True) class Meta: db_table = 'portail_messages' verbose_name = "message" verbose_name_plural = "messages" + indexes = [ + models.Index(fields=["famille"]), + models.Index(fields=["individu"]), + models.Index(fields=["structure"]), + models.Index(fields=["utilisateur"]), + models.Index(fields=["date_creation"]), + models.Index(fields=["date_lecture"]), + ] def __str__(self): return "Message ID%d" % self.idmessage if self.idmessage else "Nouveau message" @@ -3435,6 +3490,7 @@ def __str__(self): class Consentement(models.Model): idconsentement = models.AutoField(verbose_name="ID", db_column='IDconsentement', primary_key=True) famille = models.ForeignKey(Famille, verbose_name="Famille", on_delete=models.PROTECT, blank=True, null=True) + individu = models.ForeignKey(Individu, verbose_name="Individu", on_delete=models.PROTECT, blank=True, null=True) unite_consentement = models.ForeignKey(UniteConsentement, verbose_name="Unité de consentement", on_delete=models.PROTECT) horodatage = models.DateTimeField(verbose_name="Date", auto_now_add=True) diff --git a/noethysweb/core/templates/core/header.html b/noethysweb/core/templates/core/header.html index 5b468fdf..3783812d 100644 --- a/noethysweb/core/templates/core/header.html +++ b/noethysweb/core/templates/core/header.html @@ -104,7 +104,7 @@ {% if perms.core.factures_generation %}{% endif %} {% if perms.core.rappels_generation %}{% endif %} {% endif %} - + {% if perms.core.famille_reglements_ajouter %}{% endif %} @@ -120,7 +120,6 @@ - {# Messagerie #} {% if liste_messages_non_lus %} {% endif %} @@ -166,7 +175,7 @@