feat: ADD choix type de compte famille/individu sur le portail#11
Open
Abderrahmane01Rhiabi wants to merge 11 commits into
Open
Conversation
There was a problem hiding this comment.
Pull request overview
Cette PR introduit la possibilité de basculer le portail entre deux modes d’authentification/compte (Compte Famille vs Compte Individu) et ajoute les éléments nécessaires côté backoffice pour gérer les identifiants individuels.
Changes:
- Ajout d’un paramètre global
type_compte(paramètres généraux) et intégration au menu/chargement de contexte. - Ajout d’un onglet/page “Portail” sur la fiche individu (widgets, formulaire, vues, routes) pour gérer identifiant/mot de passe/options.
- Adaptations transverses (middleware, historiques, éditeurs email/SMS express, utilitaires internet) pour supporter la catégorie
individu.
Reviewed changes
Copilot reviewed 31 out of 31 changed files in this pull request and generated 25 comments.
Show a summary per file
| File | Description |
|---|---|
| noethysweb/parametrage/views/parametres_generaux.py | Nouvelle vue d’édition des paramètres généraux (dont type_compte). |
| noethysweb/parametrage/urls.py | Route vers la page “Paramètres généraux”. |
| noethysweb/parametrage/forms/parametres_generaux.py | Formulaire crispy pour type_compte. |
| noethysweb/outils/views/historique.py | Historique : prise en compte de la catégorie individu dans filtres/formatage. |
| noethysweb/outils/views/editeur_sms_express.py | Éditeur SMS express : support destinataire individu en plus de famille. |
| noethysweb/outils/views/editeur_emails_express.py | Éditeur emails express : support individu (et refactor imports). |
| noethysweb/noethysweb/middleware.py | Middleware : initialise type_compte en session + gestion reset password pour individu. |
| noethysweb/individus/views/effacer_familles.py | Ajustements d’anonymisation/suppression de données famille. |
| noethysweb/fiche_individu/widgets.py | Ajout widgets identifiant/mdp portail pour individu. |
| noethysweb/fiche_individu/views/individu.py | Filtrage des onglets individu selon permissions/paramètres/type de compte. |
| noethysweb/fiche_individu/views/individu_portail.py | Nouvelle page Portail sur fiche individu (consult/modif + AJAX). |
| noethysweb/fiche_individu/utils/utils_individu.py | Ajout onglet “portail” à la liste des onglets individu. |
| noethysweb/fiche_individu/urls.py | Routes vers la page portail individu + endpoints AJAX associés. |
| noethysweb/fiche_individu/templates/fiche_individu/widgets/internet_mdp.html | Template widget mot de passe pour individu. |
| noethysweb/fiche_individu/templates/fiche_individu/widgets/internet_identifiant.html | Template widget identifiant pour individu. |
| noethysweb/fiche_individu/templates/fiche_individu/individu_portail.html | Template page “Portail” individu + JS envoi codes. |
| noethysweb/fiche_individu/forms/individu_portail.py | Formulaire modèle Individu pour paramétrage portail (codes/options). |
| noethysweb/fiche_famille/views/famille.py | Filtrage onglets famille selon type_compte et paramètres. |
| noethysweb/fiche_famille/views/famille_ajouter.py | Génération de credentials + création user individu lors de la création d’un individu. |
| noethysweb/fiche_famille/utils/utils_internet.py | Extensions utilitaires : identifiant individu + réinit/purge/fix pour individu. |
| noethysweb/fiche_famille/utils/utils_famille.py | Ajout onglet “messagerie” + helper de filtrage par paramètres. |
| noethysweb/core/views/menu.py | Menu backoffice : ajout “Paramètres généraux” + option force_permissions. |
| noethysweb/core/views/base.py | Injection parametres_generaux en contexte + passage au menu principal. |
| noethysweb/core/utils/utils_portail.py | Ajustement mineur de commentaire. |
| noethysweb/core/utils/utils_permissions.py | Génération permissions : support du menu avec force_permissions/paramètres généraux. |
| noethysweb/core/utils/utils_parametres_generaux.py | Nouveau module : définition/lecture des paramètres généraux (dont type_compte). |
| noethysweb/core/utils/utils_historique.py | Historique : garde contre individu supprimé avant création de trace. |
| noethysweb/core/models.py | Modèle : ajout champs portail sur Individu + ajout/déplacement de CategorieCompteInternet. |
| noethysweb/core/decorators.py | Décorateur AJAX portail : tentative d’alignement sur type_compte. |
| noethysweb/core/constants.py | Constantes TYPE_COMPTE_FAMILLE / TYPE_COMPTE_INDIVIDU. |
| noethysweb/core/admin.py | Début de support admin pour utilisateurs individu (proxy/admin). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+20
to
+26
| def get_context_data(self, **kwargs): | ||
| context = super(Modifier, self).get_context_data(**kwargs) | ||
| context['page_titre'] = "Paramètres Généraux" | ||
| context['box_titre'] = "Paramètres" | ||
| context['box_introduction'] = "Ajustez les paramètres de Portail utilisateur et cliquez sur le bouton Enregistrer." | ||
| context['form'] = Formulaire() | ||
| return context |
Comment on lines
+45
to
+48
| # Stocker le type de compte dans la session | ||
| request.session['type_compte'] = form.cleaned_data.get("type_compte", TYPE_COMPTE_FAMILLE) | ||
| cache.delete("parametres_portail") | ||
|
|
| from core.utils import utils_parametres , utils_parametres_generaux | ||
| from noethysweb.version import GetVersion | ||
|
|
||
| from django.db.models import Q |
Comment on lines
+124
to
+129
| # Paramètres du portail | ||
| parametres_generaux = cache.get('parametres_generaux') | ||
| if not parametres_generaux: | ||
| parametres_generaux = utils_parametres_generaux.Get_dict_parametres() | ||
| cache.set('parametres_generaux', parametres_generaux, 1) | ||
| context['parametres_generaux'] = parametres_generaux |
Comment on lines
40
to
57
| def secure_ajax_portail(function): | ||
| """ A associer aux requêtes AJAX """ | ||
| def _function(request, *args, **kwargs): | ||
| parametre_type_compte = PortailParametre.objects.filter(code="type_compte").first() | ||
| type_compte = parametre_type_compte.valeur if parametre_type_compte else TYPE_COMPTE_FAMILLE | ||
|
|
||
| # Vérifie que c'est une requête AJAX | ||
| if not request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest': | ||
| return HttpResponseBadRequest() | ||
| # Vérifie que l'utilisateur est authentifié | ||
| 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 la catégorie correspond au type de compte configuré | ||
| 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 (type_compte != TYPE_COMPTE_FAMILLE and request.user.categorie == "famille") #Compte famille non activé pour une famille: Si type_compte n'est pas "famille" et que la catégorie est "famille", l'accès est interdit. | ||
| or (type_compte == TYPE_COMPTE_INDIVIDU and request.user.categorie == "famille")): #Compte individu activé pour une famille: Si type_compte est "individu" alors que l'utilisateur est de catégorie "famille", l'accès est interdit. | ||
| return HttpResponseForbidden() |
Comment on lines
+1804
to
+1806
| def save_individu(sender, instance, **kwargs): | ||
| if hasattr(instance, 'individu'): | ||
| instance.individu.save() |
Comment on lines
1663
to
+1732
| @@ -1709,6 +1719,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.") | |||
Comment on lines
+75
to
+83
| # Réinitialisation pour les familles | ||
| if Famille.objects.select_related("utilisateur"): | ||
| for famille in Famille.objects.select_related("utilisateur").all(): | ||
| _reinit_mdp_objet(famille, "famille") | ||
|
|
||
| # Réinitialisation pour les individus | ||
| if Individu.objects.select_related("utilisateur"): | ||
| for individu in Individu.objects.select_related("utilisateur").all(): | ||
| _reinit_mdp_objet(individu, "individu") |
Comment on lines
+97
to
+113
| # Purge pour les familles | ||
| if Utilisateur.objects.select_related("famille"): | ||
| utilisateurs = Utilisateur.objects.select_related("famille").filter( | ||
| categorie="famille", | ||
| date_expiration_mdp__lte=datetime.datetime.now() - datetime.timedelta(days=3) | ||
| ) | ||
| for utilisateur in utilisateurs: | ||
| _purge_mdp_utilisateur(utilisateur, "famille") | ||
|
|
||
| # Purge pour les individus | ||
| if Utilisateur.objects.select_related("individu"): | ||
| utilisateurs = Utilisateur.objects.select_related("individu").filter( | ||
| categorie="individu", | ||
| date_expiration_mdp__lte=datetime.datetime.now() - datetime.timedelta(days=3) | ||
| ) | ||
| for utilisateur in utilisateurs: | ||
| _purge_mdp_utilisateur(utilisateur, "individu") |
Comment on lines
+157
to
+173
| """ Affiche uniquement les utilisateurs de type famille """ | ||
| qs = super().get_queryset(request) | ||
| return qs.filter(categorie="individu") | ||
|
|
||
| def has_add_permission(self, request): | ||
| """ Empêche l'ajout d'un utilisateur famille """ | ||
| return False | ||
|
|
||
| def has_delete_permission(self, request, obj=None): | ||
| """ Empêche la suppression d'un utilisateur famille """ | ||
| return False | ||
|
|
||
|
|
||
| class Utilisateur_Individu(Utilisateur): | ||
| class Meta: | ||
| proxy = True | ||
| verbose_name = "Individu" No newline at end of file |
…rmée (accolade fermante manquante)
…— bloque désormais l'accès selon type_compte configuré
… à date_expiration_mdp
…teur_email - évite KeyError idindividu pour envoi au restaurateur
…n type_compte à chaque requête
… ajout garde utilisateur None, sauvegarde internet_reservations, get_form_kwargs avec idfamille
…tré par famille_id, garde date_expiration_mdp si utilisateur None
- Correction validation login pour accepter categorie 'individu' selon type_compte configuré - Ajout vérification type_compte dans base.py pour autoriser accès portail individu - Correction login.py: gestion famille_pk=None pour utilisateurs individu dans historique - Correction change_password.py: gestion secquest uniquement pour comptes famille - Correction accueil.py: méthode get_famille() pour gérer utilisateurs sans famille - Page accueil portail fonctionne pour utilisateurs individu (affichage contexte vide) LIMITATION: Les autres pages du portail (renseignements, activités, réservations, etc.) ne sont pas encore adaptées pour les utilisateurs individu et génèrent une erreur 'Utilisateur has no famille'. Ces pages nécessitent des modifications supplémentaires pour récupérer la famille via le modèle Rattachement. Testé: - Mode famille: login famille ✓, accès portail ✓ - Mode individu: login individu ✓, accès page accueil ✓, autres pages KO (limitation connue)
…vidu Quand un utilisateur individu change son mot de passe sur le portail, le champ internet_mdp doit être masqué (*****) dans l'admin pour ne plus afficher le mot de passe en clair. Modifié: - portail/views/change_password.py: ajout du masquage de internet_mdp pour les comptes individu (comme déjà fait pour les comptes famille)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 Pull Request Description
Brief description of changes applied
Cette PR ajoute la possibilité de choisir le type de compte utilisateur
sur le portail : Compte Famille ou Compte Individu.
Concrètement :
type_comptedans les Paramètres Généraux(identifiant, mot de passe)
Changes removed
(toujours_afficher=parametres_generaux.get(...)) pour rester conforme
à Ivan et éviter des conflits avec PR_feat/parametres-generaux.