From 2fef6d61a4d38f82c28587a9642aef65b5fd387a Mon Sep 17 00:00:00 2001 From: N3WT DE COMPET Date: Sat, 14 Mar 2026 11:35:19 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20Gestion=20du=20refus=20d=C3=A9finitif?= =?UTF-8?q?=20d'un=20dossier=20[N3WTS-2]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Back-End/N3wtSchool/mailManager.py | 34 ++++++++++ .../templates/emails/refus_definitif.html | 66 +++++++++++++++++++ .../views/register_form_views.py | 30 +++++++++ .../app/[locale]/admin/subscriptions/page.js | 12 ++-- 4 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 Back-End/Subscriptions/templates/emails/refus_definitif.html diff --git a/Back-End/N3wtSchool/mailManager.py b/Back-End/N3wtSchool/mailManager.py index be5c2db..54c8a47 100644 --- a/Back-End/N3wtSchool/mailManager.py +++ b/Back-End/N3wtSchool/mailManager.py @@ -293,4 +293,38 @@ def sendValidationDossier(recipients, establishment_id, student_name, class_name except Exception as e: errorMessage = str(e) logger.error(f"Erreur lors de l'envoi de l'email de validation: {errorMessage}") + return errorMessage + + +def sendRefusDefinitif(recipients, establishment_id, student_name, notes): + """ + Envoie un email au parent pour l'informer que le dossier d'inscription + a été définitivement refusé. + + Args: + recipients: Email du destinataire (parent) + establishment_id: ID de l'établissement + student_name: Nom complet de l'élève + notes: Motifs du refus + + Returns: + str: Message d'erreur si échec, chaîne vide sinon + """ + errorMessage = '' + try: + EMAIL_REFUS_DEFINITIF_SUBJECT = '[N3WT-SCHOOL] Dossier d\'inscription refusé' + context = { + 'BASE_URL': settings.BASE_URL, + 'URL_DJANGO': settings.URL_DJANGO, + 'student_name': student_name, + 'notes': notes + } + connection = getConnection(establishment_id) + subject = EMAIL_REFUS_DEFINITIF_SUBJECT + html_message = render_to_string('emails/refus_definitif.html', context) + sendMail(subject=subject, message=html_message, recipients=recipients, connection=connection) + logger.info(f"Email de refus définitif envoyé à {recipients} pour l'élève {student_name}") + except Exception as e: + errorMessage = str(e) + logger.error(f"Erreur lors de l'envoi de l'email de refus définitif: {errorMessage}") return errorMessage \ No newline at end of file diff --git a/Back-End/Subscriptions/templates/emails/refus_definitif.html b/Back-End/Subscriptions/templates/emails/refus_definitif.html new file mode 100644 index 0000000..41fa6d1 --- /dev/null +++ b/Back-End/Subscriptions/templates/emails/refus_definitif.html @@ -0,0 +1,66 @@ + + + + + Dossier d'inscription refusé + + + +
+
+ +

Dossier d'inscription refusé

+
+
+

Bonjour,

+

Nous avons le regret de vous informer que le dossier d'inscription de {{ student_name }} n'a pas été retenu.

+ +
+ Motif(s) :
+ {{ notes }} +
+ +

Nous vous remercions de l'intérêt que vous avez porté à notre établissement et restons à votre disposition pour tout renseignement complémentaire.

+ +

Cordialement,

+

L'équipe N3wt School

+
+ +
+ + diff --git a/Back-End/Subscriptions/views/register_form_views.py b/Back-End/Subscriptions/views/register_form_views.py index 5441703..e86916d 100644 --- a/Back-End/Subscriptions/views/register_form_views.py +++ b/Back-End/Subscriptions/views/register_form_views.py @@ -509,6 +509,36 @@ class RegisterFormWithIdView(APIView): updateStateMachine(registerForm, 'EVENT_VALIDATE') + elif _status == RegistrationForm.RegistrationFormStatus.RF_ARCHIVED: + # Vérifier si on vient de l'état "À valider" (RF_UNDER_REVIEW) pour un refus définitif + if registerForm.status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW: + # Envoi de l'email de refus définitif aux responsables légaux (en arrière-plan) + def send_refus_definitif_emails(): + try: + student = registerForm.student + student_name = f"{student.first_name} {student.last_name}" + notes = data.get('notes', '') or "Aucun motif spécifié" + + guardians = student.guardians.all() + for guardian in guardians: + email = None + if hasattr(guardian, "profile_role") and guardian.profile_role and hasattr(guardian.profile_role, "profile") and guardian.profile_role.profile: + email = guardian.profile_role.profile.email + if not email: + email = getattr(guardian, "email", None) + + if email: + logger.info(f"[RF_ARCHIVED] Envoi email de refus définitif à {email} pour l'élève {student_name}") + mailer.sendRefusDefinitif(email, registerForm.establishment.pk, student_name, notes) + except Exception as e: + logger.error(f"[RF_ARCHIVED] Erreur lors de l'envoi de l'email de refus définitif: {e}") + + # Lancer l'envoi d'email dans un thread séparé pour ne pas bloquer la réponse + email_thread = threading.Thread(target=send_refus_definitif_emails) + email_thread.start() + + updateStateMachine(registerForm, 'EVENT_ARCHIVE') + # Retourner les données mises à jour return JsonResponse(studentForm_serializer.data, 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 4254a21..5192317 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js @@ -18,6 +18,7 @@ import { Plus, Upload, Eye, + XCircle, } from 'lucide-react'; import Modal from '@/components/Modal'; import { useEstablishment } from '@/context/EstablishmentContext'; @@ -114,11 +115,10 @@ export default function Page({ params: { locale } }) { showNotification('Merci de préciser la raison du refus.', 'error', 'Erreur'); return; } - editRegisterForm( - rowToRefuse.student.id, - { status: RegistrationFormStatus.STATUS_ARCHIVED, notes: refuseReason }, - csrfToken - ) + const formData = new FormData(); + formData.append('data', JSON.stringify({ status: RegistrationFormStatus.STATUS_ARCHIVED, notes: refuseReason })); + + editRegisterForm(rowToRefuse.student.id, formData, csrfToken) .then(() => { showNotification('Le dossier a été refusé et archivé.', 'success', 'Succès'); setReloadFetch(true); @@ -527,7 +527,7 @@ export default function Page({ params: { locale } }) { { icon: ( - + ), onClick: () => openRefusePopup(row),