From 256f995698e79572eb3d51ea60b96b6fad47d553 Mon Sep 17 00:00:00 2001 From: N3WT DE COMPET Date: Sat, 3 May 2025 17:34:36 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Am=C3=A9lioration=20de=20la=20fiche=20?= =?UTF-8?q?=C3=A9l=C3=A8ve=20pour=20y=20ajouter=20la=20fratrie=20et=20les?= =?UTF-8?q?=20modalit=C3=A9s=20de=20paimenet=20(probl=C3=A8me=20affichage?= =?UTF-8?q?=20photo)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Back-End/Subscriptions/models.py | 18 ++ .../templates/pdfs/dossier_inscription.html | 230 ----------------- .../templates/pdfs/fiche_eleve.html | 233 ++++++++++++++++++ .../templatetags/myTemplateTag.py | 22 +- Back-End/Subscriptions/util.py | 2 +- .../app/[locale]/admin/subscriptions/page.js | 6 +- Front-End/src/app/actions/authAction.js | 20 +- .../Inscription/PaymentMethodSelector.js | 1 + .../Inscription/ResponsableInputFields.js | 2 +- .../Inscription/SiblingInputFields.js | 7 +- 10 files changed, 289 insertions(+), 252 deletions(-) delete mode 100644 Back-End/Subscriptions/templates/pdfs/dossier_inscription.html create mode 100644 Back-End/Subscriptions/templates/pdfs/fiche_eleve.html diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index 0f768f7..cad45a7 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -33,6 +33,15 @@ class Guardian(models.Model): profession = models.CharField(max_length=200, default="", blank=True) profile_role = models.OneToOneField(ProfileRole, on_delete=models.CASCADE, related_name='guardian_profile', blank=True) + @property + def email(self): + """ + Retourne l'email du profil associé via le ProfileRole. + """ + if self.profile_role and self.profile_role.profile: + return self.profile_role.profile.email + return None + def __str__(self): return self.last_name + "_" + self.first_name @@ -144,6 +153,15 @@ class Student(models.Model): """ return self.siblings.count() + def get_photo_url(self): + """ + Retourne le chemin correct de la photo pour le template HTML. + Si la photo n'existe pas, retourne le chemin de l'image par défaut. + """ + if self.photo and hasattr(self.photo, 'url'): + # Retourne l'URL complète de la photo + return self.photo.url + @property def age(self): if self.birth_date: diff --git a/Back-End/Subscriptions/templates/pdfs/dossier_inscription.html b/Back-End/Subscriptions/templates/pdfs/dossier_inscription.html deleted file mode 100644 index 4b2c683..0000000 --- a/Back-End/Subscriptions/templates/pdfs/dossier_inscription.html +++ /dev/null @@ -1,230 +0,0 @@ - - - - - {{ pdf_title }} - - - - {% load myTemplateTag %} -
-
-
-

{{ pdf_title }}

- {% if student.photo %} - Photo de l'élève - {% endif %} -
-
- -
-

ÉLÈVE

- {% with level=student|getStudentLevel %} - {% with gender=student|getStudentGender %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOM :{{ student.last_name }}PRÉNOM :{{ student.first_name }}
ADRESSE :{{ student.address }}
GENRE :{{ gender }}NÉ(E) LE :{{ student.birth_date }}
À :{{ student.birth_place }} ({{ student.birth_postal_code }})
NATIONALITÉ :{{ student.nationality }}NIVEAU :{{ level }}
MÉDECIN TRAITANT :{{ student.attending_physician }}
- {% endwith %} - {% endwith %} -
- -
-

RESPONSABLES

- {% with guardians=student.getGuardians %} - {% for guardian in guardians%} -
-

Responsable {{ forloop.counter }}

- - - - - - - - - - - - - - - - - - - - - - - -
NOM :{{ guardian.last_name }}PRÉNOM :{{ guardian.first_name }}
ADRESSE :{{ guardian.address }}
NÉ(E) LE :{{ guardian.birth_date }}EMAIL :{{ guardian.email }}
TÉLÉPHONE :{{ guardian.phone }}PROFESSION :{{ guardian.profession }}
-
- {% endfor %} - {% endwith %} -
- -
-

FRATRIE

- {% with siblings=student.getGuardians %} - {% for sibling in siblings%} -
-

Frère/Sœur {{ forloop.counter }}

- - - - - - - - - - - -
NOM :{{ sibling.last_name }}PRÉNOM :{{ sibling.first_name }}
NÉ(E) LE :{{ sibling.birth_date }}
-
- {% endfor %} - {% endwith %} -
- -
-

MODALITÉS DE PAIEMENT

-

Frais d'inscription

- {% with registrationPayment=student|getRegistrationPaymentMethod %} -

{{ registrationPayment }}

- {% endwith %} -

Frais de scolarité

- {% with tuitionPayment=student|getTuitionPaymentMethod %} -

{{ tuitionPayment }}

- {% endwith %} -
- -
- Fait le {{ signatureDate }} à {{ signatureTime }} -
-
- - \ No newline at end of file diff --git a/Back-End/Subscriptions/templates/pdfs/fiche_eleve.html b/Back-End/Subscriptions/templates/pdfs/fiche_eleve.html new file mode 100644 index 0000000..52baef9 --- /dev/null +++ b/Back-End/Subscriptions/templates/pdfs/fiche_eleve.html @@ -0,0 +1,233 @@ + + + + + Fiche élève de {{ student.last_name }} {{ student.first_name }} + + + + {% load myTemplateTag %} +
+ +
+

Fiche élève de {{ student.last_name }} {{ student.first_name }}

+ {% if student.photo %} + Photo de l'élève + {% else %} + Photo par défaut + {% endif %} +
+ + +
+

ÉLÈVE

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nom :{{ student.last_name }}Prénom :{{ student.first_name }}
Adresse :{{ student.address }}
Genre :{{ student|getStudentGender }}Né(e) le :{{ student.birth_date }}
À :{{ student.birth_place }} ({{ student.birth_postal_code }})
Nationalité :{{ student.nationality }}Niveau :{{ student|getStudentLevel }}
+
+ + +
+

RESPONSABLES

+ {% for guardian in student.getGuardians %} +
+

Responsable {{ forloop.counter }}

+ + + + + + + + + + + + + + + + + + + + + + + +
Nom :{{ guardian.last_name }}Prénom :{{ guardian.first_name }}
Adresse :{{ guardian.address }}
Né(e) le :{{ guardian.birth_date }}Email :{{ guardian.email }}
Téléphone :{{ guardian.phone|phone_format }}Profession :{{ guardian.profession }}
+
+ {% endfor %} +
+ + +
+

FRATRIE

+ {% for sibling in student.getSiblings %} +
+

Frère/Soeur {{ forloop.counter }}

+ + + + + + + + + + + +
Nom :{{ sibling.last_name }}Prénom :{{ sibling.first_name }}
Né(e) le :{{ sibling.birth_date }}
+
+ {% endfor %} +
+ + +
+

MODALITÉS DE PAIEMENT

+ + + + + + + + + +
Frais d'inscription :{{ student|getRegistrationPaymentMethod }} en {{ student|getRegistrationPaymentPlan }}
Frais de scolarité :{{ student|getTuitionPaymentMethod }} en {{ student|getTuitionPaymentPlan }}
+
+ + +
+ Fait le {{ signatureDate }} à {{ signatureTime }} +
+
+ + \ No newline at end of file diff --git a/Back-End/Subscriptions/templatetags/myTemplateTag.py b/Back-End/Subscriptions/templatetags/myTemplateTag.py index da1a88e..cc47171 100644 --- a/Back-End/Subscriptions/templatetags/myTemplateTag.py +++ b/Back-End/Subscriptions/templatetags/myTemplateTag.py @@ -1,8 +1,20 @@ from Subscriptions.models import RegistrationForm, Student -from School.models import PaymentModeType +from School.models import PaymentModeType, PaymentPlanType from django import template +import re + register = template.Library() +@register.filter +def getRegistrationPaymentPlan(pk): + registerForm = RegistrationForm.objects.get(student=pk) + return PaymentPlanType(registerForm.registration_payment_plan).label + +@register.filter +def getTuitionPaymentPlan(pk): + registerForm = RegistrationForm.objects.get(student=pk) + return PaymentPlanType(registerForm.tuition_payment_plan).label + @register.filter def getRegistrationPaymentMethod(pk): registerForm = RegistrationForm.objects.get(student=pk) @@ -21,4 +33,10 @@ def getStudentLevel(pk): @register.filter def getStudentGender(pk): registerForm = RegistrationForm.objects.get(student=pk) - return Student.StudentGender(int(registerForm.student.gender)).label \ No newline at end of file + return Student.StudentGender(int(registerForm.student.gender)).label + +@register.filter +def phone_format(value): + if value.startswith("+33"): + value = value.replace("+33", "+33 ") + return re.sub(r"(\d{2})", r"\1 ", value).strip() \ No newline at end of file diff --git a/Back-End/Subscriptions/util.py b/Back-End/Subscriptions/util.py index 68b02a8..f97d5b8 100644 --- a/Back-End/Subscriptions/util.py +++ b/Back-End/Subscriptions/util.py @@ -132,7 +132,7 @@ def rfToPDF(registerForm, filename): } # Générer le PDF - pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data) + pdf = renderers.render_to_pdf('pdfs/fiche_eleve.html', data) if not pdf: raise ValueError("Erreur lors de la génération du PDF.") diff --git a/Front-End/src/app/[locale]/admin/subscriptions/page.js b/Front-End/src/app/[locale]/admin/subscriptions/page.js index 5e017b3..632d648 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js @@ -885,9 +885,9 @@ export default function Page({ params: { locale } }) { { name: t('mainContactMail'), transform: (row) => - row.student.guardians && row.student.guardians.length > 0 && ( - row.student.guardians[0].associated_profile_email - ) + row.student.guardians && + row.student.guardians.length > 0 && + row.student.guardians[0].associated_profile_email, }, { name: t('phone'), diff --git a/Front-End/src/app/actions/authAction.js b/Front-End/src/app/actions/authAction.js index e9955ba..4ff2815 100644 --- a/Front-End/src/app/actions/authAction.js +++ b/Front-End/src/app/actions/authAction.js @@ -93,22 +93,20 @@ export const updateProfileRoles = (id, data, csrfToken) => { }; export const deleteProfileRoles = async (id, csrfToken) => { - const response = await fetch( - `${BE_AUTH_PROFILES_ROLES_URL}/${id}`, - { - method: 'DELETE', - headers: { - 'X-CSRFToken': csrfToken, - }, - credentials: 'include', - } - ); + const response = await fetch(`${BE_AUTH_PROFILES_ROLES_URL}/${id}`, { + method: 'DELETE', + headers: { + 'X-CSRFToken': csrfToken, + }, + credentials: 'include', + }); if (!response.ok) { // Extraire le message d'erreur du backend const errorData = await response.json(); const errorMessage = - errorData?.error || 'Une erreur est survenue lors de la suppression du profil.'; + errorData?.error || + 'Une erreur est survenue lors de la suppression du profil.'; // Jeter une erreur avec le message spécifique throw new Error(errorMessage); diff --git a/Front-End/src/components/Inscription/PaymentMethodSelector.js b/Front-End/src/components/Inscription/PaymentMethodSelector.js index 21f4daf..4dbe83a 100644 --- a/Front-End/src/components/Inscription/PaymentMethodSelector.js +++ b/Front-End/src/components/Inscription/PaymentMethodSelector.js @@ -159,6 +159,7 @@ export default function PaymentMethodSelector({ tuitionPaymentPlans.some((plan) => plan.frequency === option.id) diff --git a/Front-End/src/components/Inscription/ResponsableInputFields.js b/Front-End/src/components/Inscription/ResponsableInputFields.js index 2723aaa..6fa55aa 100644 --- a/Front-End/src/components/Inscription/ResponsableInputFields.js +++ b/Front-End/src/components/Inscription/ResponsableInputFields.js @@ -150,7 +150,7 @@ export default function ResponsableInputFields({ name="prenomResponsable" type="text" label={t('firstname')} - value={item.firstname || ''} + value={item.first_name || ''} onChange={(event) => { onGuardiansChange(item.id, 'first_name', event.target.value); }} diff --git a/Front-End/src/components/Inscription/SiblingInputFields.js b/Front-End/src/components/Inscription/SiblingInputFields.js index 21512f9..656a9f0 100644 --- a/Front-End/src/components/Inscription/SiblingInputFields.js +++ b/Front-End/src/components/Inscription/SiblingInputFields.js @@ -11,7 +11,6 @@ export default function SiblingInputFields({ errors, setIsPageValid, }) { - const getError = (index, field) => { return errors[index]?.[field]?.[0]; }; @@ -43,7 +42,7 @@ export default function SiblingInputFields({ } return; // Sortir pour éviter de continuer l'exécution inutilement } - + // Synchroniser siblings avec formData uniquement si nécessaire setFormData((prevFormData) => { if (JSON.stringify(prevFormData.siblings) !== JSON.stringify(siblings)) { @@ -54,14 +53,14 @@ export default function SiblingInputFields({ } return prevFormData; // Évitez une mise à jour inutile }); - + // Valider les siblings const isValid = siblings.every((sibling, index) => { return !Object.keys(sibling).some( (field) => getLocalError(index, field) !== '' ); }); - + setIsPageValid(isValid); }, [siblings, getLocalError, setFormData, setIsPageValid]);