diff --git a/Back-End/Auth/serializers.py b/Back-End/Auth/serializers.py
index 98686d2..bee22ac 100644
--- a/Back-End/Auth/serializers.py
+++ b/Back-End/Auth/serializers.py
@@ -16,7 +16,24 @@ class ProfileSerializer(serializers.ModelSerializer):
def get_roles(self, obj):
roles = ProfileRole.objects.filter(profile=obj)
- return [{'role_type': role.role_type, 'establishment': role.establishment.id, 'establishment_name': role.establishment.name, 'is_active': role.is_active} for role in roles]
+ roles_data = []
+ for role in roles:
+ # Récupérer l'ID de l'associated_person en fonction du type de rôle
+ if role.role_type == ProfileRole.RoleType.PROFIL_PARENT:
+ guardian = Guardian.objects.filter(profile_role=role).first()
+ id_associated_person = guardian.id if guardian else None
+ else:
+ teacher = Teacher.objects.filter(profile_role=role).first()
+ id_associated_person = teacher.id if teacher else None
+
+ roles_data.append({
+ 'id_associated_person': id_associated_person,
+ 'role_type': role.role_type,
+ 'establishment': role.establishment.id,
+ 'establishment_name': role.establishment.name,
+ 'is_active': role.is_active,
+ })
+ return roles_data
def create(self, validated_data):
user = Profile(
@@ -107,6 +124,7 @@ class ProfileRoleSerializer(serializers.ModelSerializer):
"registration_status": registration_status
})
return {
+ "id": guardian.id,
"guardian_name": f"{guardian.last_name} {guardian.first_name}",
"students": students_list
}
@@ -118,6 +136,7 @@ class ProfileRoleSerializer(serializers.ModelSerializer):
specialities = teacher.specialities.all()
specialities_list = [{"name": speciality.name, "color_code": speciality.color_code} for speciality in specialities]
return {
+ "id": teacher.id,
"teacher_name": f"{teacher.last_name} {teacher.first_name}",
"classes": classes_list,
"specialities": specialities_list
diff --git a/Back-End/Auth/views.py b/Back-End/Auth/views.py
index bffc8d5..a1eb820 100644
--- a/Back-End/Auth/views.py
+++ b/Back-End/Auth/views.py
@@ -4,7 +4,6 @@ from django.http.response import JsonResponse
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator
from django.core.exceptions import ValidationError
-from django.core.cache import cache
from django.middleware.csrf import get_token
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser
@@ -24,7 +23,6 @@ from rest_framework.decorators import action, api_view
from Auth.serializers import ProfileSerializer, ProfileRoleSerializer
from Subscriptions.models import RegistrationForm
-from Subscriptions.signals import clear_cache
import Subscriptions.mailManager as mailer
import Subscriptions.util as util
import logging
@@ -204,7 +202,6 @@ class LoginView(APIView):
login(request, user)
user.save()
- clear_cache()
retour = ''
# Récupérer tous les rôles de l'utilisateur avec le type spécifié
@@ -423,7 +420,6 @@ class SubscribeView(APIView):
else:
return JsonResponse(role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
- clear_cache()
retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE]
retourErreur = ''
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": profil.id}, safe=False)
@@ -473,7 +469,6 @@ class NewPasswordView(APIView):
profil.code = util.genereRandomCode(12)
profil.datePeremption = util.calculeDatePeremption(util._now(), settings.EXPIRATION_URL_NB_DAYS)
profil.save()
- clear_cache()
retourErreur = ''
retour = error.returnMessage[error.MESSAGE_REINIT_PASSWORD] % (newProfilConnection.get('email'))
mailer.envoieReinitMotDePasse(newProfilConnection.get('email'), profil.code)
@@ -527,7 +522,6 @@ class ResetPasswordView(APIView):
profil.code = ''
profil.datePeremption = ''
profil.save()
- clear_cache()
retourErreur = ''
return JsonResponse({'message': retour, "errorMessage": retourErreur, "errorFields": errorFields}, safe=False)
diff --git a/Back-End/Subscriptions/apps.py b/Back-End/Subscriptions/apps.py
index 503b621..3ab723f 100644
--- a/Back-End/Subscriptions/apps.py
+++ b/Back-End/Subscriptions/apps.py
@@ -5,6 +5,3 @@ class GestioninscriptionsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'Subscriptions'
- def ready(self):
- from Subscriptions.signals import clear_cache
- clear_cache()
diff --git a/Back-End/Subscriptions/automate.py b/Back-End/Subscriptions/automate.py
index 3c457cb..6f98e79 100644
--- a/Back-End/Subscriptions/automate.py
+++ b/Back-End/Subscriptions/automate.py
@@ -1,7 +1,6 @@
# state_machine.py
import json
from Subscriptions.models import RegistrationForm
-from Subscriptions.signals import clear_cache
state_mapping = {
"ABSENT": RegistrationForm.RegistrationFormStatus.RF_ABSENT,
@@ -31,7 +30,6 @@ def updateStateMachine(rf, transition) :
if state_machine.trigger(transition, automateModel):
rf.status = state_machine.state
rf.save()
- clear_cache()
class Automate_RF_Register:
def __init__(self, initial_state):
diff --git a/Back-End/Subscriptions/serializers.py b/Back-End/Subscriptions/serializers.py
index 86d2870..1ed09df 100644
--- a/Back-End/Subscriptions/serializers.py
+++ b/Back-End/Subscriptions/serializers.py
@@ -110,6 +110,9 @@ class StudentSerializer(serializers.ModelSerializer):
profile_role = guardian_data.pop('profile_role', None)
if profile_role_data:
+ # Vérifiez si 'profile' est un objet ou une clé primaire
+ if isinstance(profile_role_data.get('profile'), Profile):
+ profile_role_data['profile'] = profile_role_data['profile'].id
establishment_id = profile_role_data.pop('establishment').id
profile_role_data['establishment'] = establishment_id
diff --git a/Back-End/Subscriptions/signals.py b/Back-End/Subscriptions/signals.py
index 0ce2f11..1da56dc 100644
--- a/Back-End/Subscriptions/signals.py
+++ b/Back-End/Subscriptions/signals.py
@@ -1,6 +1,5 @@
from django.db.models.signals import post_save, post_delete, m2m_changed
from django.dispatch import receiver
-from django.core.cache import cache
from .models import RegistrationForm, Student, Guardian
from Auth.models import Profile
from N3wtSchool import settings
@@ -8,23 +7,6 @@ from N3wtSchool.redis_client import redis_client
import logging
logger = logging.getLogger(__name__)
-def clear_cache():
- # Préfixes des clés à supprimer
- prefixes = ['N3WT_']
-
- for prefix in prefixes:
- # Utiliser le motif pour obtenir les clés correspondant au préfixe
- pattern = f'*{prefix}*'
- logger.debug(f'pattern : {pattern}')
- for key in redis_client.scan_iter(pattern):
- redis_client.delete(key)
- logger.debug(f'deleting : {key}')
-
-@receiver(post_save, sender=RegistrationForm)
-@receiver(post_delete, sender=RegistrationForm)
-def clear_cache_after_change(sender, instance, **kwargs):
- clear_cache()
-
@receiver(m2m_changed, sender=Student.guardians.through)
def check_orphan_reponsables(sender, **kwargs):
action = kwargs.pop('action', None)
diff --git a/Back-End/Subscriptions/views/register_form_views.py b/Back-End/Subscriptions/views/register_form_views.py
index ce72e85..1cecb01 100644
--- a/Back-End/Subscriptions/views/register_form_views.py
+++ b/Back-End/Subscriptions/views/register_form_views.py
@@ -1,7 +1,6 @@
from django.http.response import JsonResponse
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
from django.utils.decorators import method_decorator
-from django.core.cache import cache
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from rest_framework.decorators import action, api_view
@@ -18,7 +17,6 @@ import Subscriptions.util as util
from Subscriptions.serializers import RegistrationFormSerializer
from Subscriptions.pagination import CustomPagination
-from Subscriptions.signals import clear_cache
from Subscriptions.models import Student, Guardian, RegistrationForm, RegistrationTemplate, RegistrationFileGroup
from Subscriptions.automate import updateStateMachine
@@ -88,13 +86,6 @@ class RegisterFormView(APIView):
except ValueError:
page_size = settings.NB_RESULT_PER_PAGE
- # Définir le cache_key en fonction du filtre
- page_number = request.GET.get('page', 1)
- cache_key = f'N3WT_ficheInscriptions_{establishment_id}_{filter}_page_{page_number}_search_{search if filter == "pending" else ""}'
- cached_page = cache.get(cache_key)
- if cached_page:
- return JsonResponse(cached_page, safe=False)
-
# Récupérer les dossier d'inscriptions en fonction du filtre
registerForms_List = None
if filter == 'pending':
@@ -120,7 +111,6 @@ class RegisterFormView(APIView):
if page is not None:
registerForms_serializer = RegistrationFormSerializer(page, many=True)
response_data = paginator.get_paginated_response(registerForms_serializer.data)
- cache.set(cache_key, response_data, timeout=60 * 15)
return JsonResponse(response_data, safe=False)
return JsonResponse({'error': 'aucune donnée trouvée', 'count': 0}, safe=False)
@@ -308,7 +298,6 @@ class RegisterFormWithIdView(APIView):
student.profiles.clear()
student.registration_files.clear()
student.delete()
- clear_cache()
return JsonResponse("La suppression du dossier a été effectuée avec succès", safe=False)
diff --git a/Front-End/src/app/[locale]/admin/subscriptions/page.js b/Front-End/src/app/[locale]/admin/subscriptions/page.js
index 064dc2a..973e3d8 100644
--- a/Front-End/src/app/[locale]/admin/subscriptions/page.js
+++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js
@@ -43,7 +43,7 @@ import {
fetchRegistrationFees,
fetchTuitionFees } from '@/app/actions/schoolAction';
-import { createProfile, deleteProfile } from '@/app/actions/authAction';
+import { createProfile, deleteProfile, fetchProfiles } from '@/app/actions/authAction';
import {
BASE_URL,
@@ -86,6 +86,7 @@ export default function Page({ params: { locale } }) {
const [registrationFees, setRegistrationFees] = useState([]);
const [tuitionFees, setTuitionFees] = useState([]);
const [groups, setGroups] = useState([]);
+ const [profiles, setProfiles] = useState([]);
const csrfToken = useCsrfToken();
const { selectedEstablishmentId } = useEstablishment();
@@ -235,7 +236,14 @@ useEffect(() => {
})
.catch(error => {
logger.error('Error fetching file groups:', error);
+ }),
+ fetchProfiles()
+ .then(data => {
+ setProfiles(data);
})
+ .catch(error => {
+ logger.error('Error fetching profileRoles:', error);
+ }),
])
.then(() => {
setIsLoading(false);
@@ -373,24 +381,46 @@ useEffect(()=>{
student: {
last_name: updatedData.studentLastName,
first_name: updatedData.studentFirstName,
- guardians: updatedData.selectedGuardians.length !== 0 ? updatedData.selectedGuardians.map(guardianId => ({ id: guardianId })) : [{
- profile_role_data: {
- establishment: selectedEstablishmentId,
- role_type: 2,
- is_active: false,
- profile_data: {
- email: updatedData.guardianEmail,
- password: 'Provisoire01!',
- username: updatedData.guardianEmail,
- }
- },
- last_name: updatedData.guardianLastName,
- first_name: updatedData.guardianFirstName,
- birth_date: updatedData.guardianBirthDate,
- address: updatedData.guardianAddress,
- phone: updatedData.guardianPhone,
- profession: updatedData.guardianProfession
- }],
+ guardians: updatedData.selectedGuardians.length !== 0
+ ? updatedData.selectedGuardians.map(guardianId => ({ id: guardianId }))
+ : (() => {
+ if (updatedData.isExistingParentProfile) {
+ return [{
+ profile_role_data: {
+ establishment: selectedEstablishmentId,
+ role_type: 2,
+ is_active: false,
+ profile: updatedData.existingProfileId, // Associer au profil existant
+ },
+ last_name: updatedData.guardianLastName,
+ first_name: updatedData.guardianFirstName,
+ birth_date: updatedData.guardianBirthDate,
+ address: updatedData.guardianAddress,
+ phone: updatedData.guardianPhone,
+ profession: updatedData.guardianProfession
+ }];
+ }
+
+ // Si aucun profil existant n'est trouvé, créer un nouveau profil
+ return [{
+ profile_role_data: {
+ establishment: selectedEstablishmentId,
+ role_type: 2,
+ is_active: false,
+ profile_data: {
+ email: updatedData.guardianEmail,
+ password: 'Provisoire01!',
+ username: updatedData.guardianEmail,
+ }
+ },
+ last_name: updatedData.guardianLastName,
+ first_name: updatedData.guardianFirstName,
+ birth_date: updatedData.guardianBirthDate,
+ address: updatedData.guardianAddress,
+ phone: updatedData.guardianPhone,
+ profession: updatedData.guardianProfession
+ }];
+ })(),
sibling: []
},
fees: allFeesIds,
@@ -699,6 +729,7 @@ const columnsSubscribed = [
registrationFees={registrationFees.filter(fee => fee.is_active)}
tuitionFees={tuitionFees.filter(fee => fee.is_active)}
groups={groups}
+ profiles={profiles}
onSubmit={createRF}
/>
)}
diff --git a/Front-End/src/app/actions/authAction.js b/Front-End/src/app/actions/authAction.js
index 80a3a6a..377917f 100644
--- a/Front-End/src/app/actions/authAction.js
+++ b/Front-End/src/app/actions/authAction.js
@@ -113,6 +113,11 @@ export const deleteProfileRoles = (id, csrfToken) => {
return fetch(request).then(requestResponseHandler);
};
+export const fetchProfiles = () => {
+ return fetch(`${BE_AUTH_PROFILES_URL}`)
+ .then(requestResponseHandler)
+};
+
export const createProfile = (data, csrfToken) => {
const request = new Request(
`${BE_AUTH_PROFILES_URL}`,
diff --git a/Front-End/src/components/Inscription/InscriptionForm.js b/Front-End/src/components/Inscription/InscriptionForm.js
index df1e151..846f983 100644
--- a/Front-End/src/components/Inscription/InscriptionForm.js
+++ b/Front-End/src/components/Inscription/InscriptionForm.js
@@ -9,11 +9,14 @@ import DiscountsSection from '@/components/Structure/Tarification/DiscountsSecti
import SectionTitle from '@/components/SectionTitle';
import ProgressStep from '@/components/ProgressStep';
import logger from '@/utils/logger';
+import Popup from '@/components/Popup';
-const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, registrationFees, tuitionFees, onSubmit, currentStep, groups }) => {
+const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, registrationFees, tuitionFees, profiles, onSubmit, currentStep, groups }) => {
const [formData, setFormData] = useState({
studentLastName: '',
studentFirstName: '',
+ guardianLastName: '',
+ guardianFirstName: '',
guardianEmail: '',
guardianPhone: '',
selectedGuardians: [],
@@ -31,6 +34,10 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
const [existingGuardians, setExistingGuardians] = useState([]);
const [totalRegistrationAmount, setTotalRegistrationAmount] = useState(0);
const [totalTuitionAmount, setTotalTuitionAmount] = useState(0);
+ const [filteredStudents, setFilteredStudents] = useState(students);
+
+ const [popupVisible, setPopupVisible] = useState(false);
+ const [popupMessage, setPopupMessage] = useState("");
const stepTitles = {
1: 'Nouvel élève',
@@ -45,8 +52,8 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
const isStep1Valid = formData.studentLastName && formData.studentFirstName;
const isStep2Valid = (
- (formData.responsableType === "new" && formData.guardianEmail.length > 0) ||
- (formData.responsableType === "existing" && formData.selectedGuardians.length > 0)
+ formData.selectedGuardians.length > 0 ||
+ (!formData.emailError && formData.guardianEmail.length > 0 && filteredStudents.length === 0)
);
const isStep3Valid = formData.selectedRegistrationFees.length > 0;
const isStep4Valid = formData.selectedTuitionFees.length > 0;
@@ -99,9 +106,35 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
};
const nextStep = () => {
- if (step < steps.length) {
- setStep(step + 1);
- }
+ if (step === 2) {
+ // Vérifier si l'adresse email saisie est rattachée à un profil existant
+ const existingProfile = profiles.find(profile => profile.email === formData.guardianEmail);
+
+ if (existingProfile) {
+ // Vérifier si le profil a un rôle de type PARENT
+ const parentRole = existingProfile.roles.find(role => role.role_type === 2);
+
+ if (parentRole) {
+ console.log('Profil parent associé trouvé !', existingProfile);
+ setFormData((prevData) => ({
+ ...prevData,
+ guardianEmail: existingProfile.email, // Mettre à jour le champ guardianEmail avec l'email du profil
+ isExistingParentProfile: true, // Indiquer que le profil est un parent existant
+ existingProfileId: existingProfile.id
+ }));
+ } else {
+ console.log('Le profil existe mais n\'est pas de type PARENT.');
+ setFormData((prevData) => ({
+ ...prevData,
+ isExistingParentProfile: false, // Réinitialiser si le profil n'est pas de type PARENT
+ }));
+ }
+ }
+ }
+
+ if (step < steps.length) {
+ setStep(step + 1);
+ }
};
const prevStep = () => {
@@ -120,13 +153,27 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
};
const handleResponsableSelection = (guardianId, guardianEmail) => {
- setFormData((prevData) => {
- const isSelected = prevData.selectedGuardians.includes(guardianId);
- const selectedGuardians = isSelected
- ? prevData.selectedGuardians.filter(id => id !== guardianId)
- : [...prevData.selectedGuardians, guardianId];
- return { ...prevData, selectedGuardians, guardianEmail };
- });
+ setFormData((prevData) => {
+ const isSelected = prevData.selectedGuardians.includes(guardianId);
+ const selectedGuardians = isSelected
+ ? prevData.selectedGuardians.filter(id => id !== guardianId) // Retirer le guardian si déjà sélectionné
+ : [...prevData.selectedGuardians, guardianId]; // Ajouter le guardian s'il n'est pas sélectionné
+
+ // Mettre à jour l'email uniquement si un guardian est sélectionné
+ const updatedGuardianEmail = isSelected ? '' : guardianEmail;
+
+ // Si aucun guardian n'est sélectionné, réinitialiser le tableau des élèves
+ if (selectedGuardians.length === 0) {
+ setFilteredStudents(students); // Réafficher tous les élèves
+ setSelectedEleve(null);
+ }
+
+ return {
+ ...prevData,
+ selectedGuardians,
+ guardianEmail: updatedGuardianEmail,
+ };
+ });
};
const submit = () => {
@@ -265,117 +312,141 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
{step === 2 && (
-
-
-
-
- {formData.responsableType === 'new' && (
- <>
-
-
-
-
+
+
- >
- )}
- {formData.responsableType === 'existing' && (
-
-
-
-
-
- | Nom |
- Prénom |
+ {/* Email du responsable */}
+ {
+ const email = e.target.value;
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+
+ setFormData((prevData) => ({
+ ...prevData,
+ guardianEmail: email,
+ emailError:
+ email.length > 0 && // Le champ de mail est non null
+ (!emailRegex.test(email) && filteredStudents.length === 0) // Format invalide ou aucun résultat
+ ? "Format d'email invalide ou aucun élève trouvé"
+ : "",
+ }));
+
+ // Filtrer les responsables affichés dans le tableau
+ const filteredGuardians = students
+ .flatMap((student) => student.guardians) // Récupérer tous les guardians
+ .filter((guardian) =>
+ guardian.associated_profile_email.toLowerCase().includes(email.toLowerCase())
+ );
+
+ // Filtrer les élèves en fonction des responsables
+ const filteredStudents = students.filter((student) =>
+ student.guardians.some((guardian) =>
+ filteredGuardians.some((filteredGuardian) => filteredGuardian.id === guardian.id)
+ )
+ );
+
+ setFilteredStudents(filteredStudents); // Mettre à jour l'état des élèves filtrés
+
+ // Réinitialiser existingGuardians et selectedStudent si l'élève sélectionné n'est plus dans le tableau
+ if (!filteredStudents.some((student) => student.id === selectedStudent?.id)) {
+ setSelectedEleve(null); // Réinitialiser l'élève sélectionné
+ setExistingGuardians([]); // Réinitialiser les responsables associés
+ setFormData((prevData) => ({
+ ...prevData,
+ selectedGuardians: [] // Réinitialiser les responsables sélectionnés
+ }));
+ }
+ }}
+ errorMsg={formData.emailError}
+ className="w-full mt-4"
+ />
+
+ {/* Tableau des élèves */}
+
+
+
+
+
+ | Nom |
+ Prénom |
+
+
+
+ {filteredStudents.map((student, index) => ( // Utiliser les élèves filtrés
+ handleEleveSelection(student)}
+ >
+ | {student.last_name} |
+ {student.first_name} |
-
-
- {students.map((student, index) => (
- handleEleveSelection(student)}
- >
- | {student.last_name} |
- {student.first_name} |
-
- ))}
-
-
-
- {selectedStudent && (
-
-
Responsables associés à {selectedStudent.last_name} {selectedStudent.first_name} :
- {existingGuardians.map((guardian) => (
-
-
-
))}
-
- )}
+
+
- )}
+ {selectedStudent && (
+
+
+ Responsables associés à {selectedStudent.last_name} {selectedStudent.first_name} :
+
+ {existingGuardians.map((guardian) => (
+
+
+
+ ))}
+
+ )}
+
)}
@@ -641,6 +712,13 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
name="Create" />
)}
+
+ setPopupVisible(false)}
+ onCancel={() => setPopupVisible(false)}
+ />
);
}
diff --git a/Front-End/src/components/Popup.js b/Front-End/src/components/Popup.js
index ac7e04f..261d63a 100644
--- a/Front-End/src/components/Popup.js
+++ b/Front-End/src/components/Popup.js
@@ -28,14 +28,14 @@ const Popup = ({ visible, message, onConfirm, onCancel, uniqueConfirmButton = fa
{!uniqueConfirmButton && (
)}