mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-05 12:41:27 +00:00
feat(backend,frontend): régénération et visualisation inline de la fiche élève PDF
This commit is contained in:
@ -1,228 +1,319 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>Fiche élève de {{ student.last_name }} {{ student.first_name }}</title>
|
<title>
|
||||||
|
Fiche élève — {{ student.last_name }} {{ student.first_name }}
|
||||||
|
</title>
|
||||||
<style>
|
<style>
|
||||||
@page {
|
@page {
|
||||||
size: A4;
|
size: A4;
|
||||||
margin: 2cm;
|
margin: 1.5cm 2cm;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
font-family: 'Arial', sans-serif;
|
font-family: "Helvetica", "Arial", sans-serif;
|
||||||
font-size: 12pt;
|
font-size: 10pt;
|
||||||
color: #222;
|
color: #1e293b;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
line-height: 1.4;
|
||||||
.container {
|
}
|
||||||
width: 100%;
|
|
||||||
padding: 0;
|
/* ── Header ── */
|
||||||
background: #fff;
|
.header-table {
|
||||||
}
|
width: 100%;
|
||||||
.header {
|
border: none;
|
||||||
text-align: center;
|
margin-bottom: 16px;
|
||||||
margin-bottom: 24px;
|
}
|
||||||
border-bottom: 2px solid #4CAF50;
|
.header-table td {
|
||||||
padding-bottom: 12px;
|
border: none;
|
||||||
position: relative;
|
padding: 0;
|
||||||
}
|
vertical-align: middle;
|
||||||
.title {
|
}
|
||||||
font-size: 22pt;
|
.header-left {
|
||||||
font-weight: bold;
|
width: 80%;
|
||||||
color: #4CAF50;
|
}
|
||||||
margin: 0;
|
.header-right {
|
||||||
}
|
width: 20%;
|
||||||
.photo {
|
text-align: right;
|
||||||
position: absolute;
|
}
|
||||||
top: 0;
|
.school-name {
|
||||||
right: 0;
|
font-size: 10pt;
|
||||||
width: 90px;
|
color: #64748b;
|
||||||
height: 90px;
|
margin: 0 0 4px 0;
|
||||||
object-fit: cover;
|
letter-spacing: 0.5px;
|
||||||
border: 1px solid #4CAF50;
|
}
|
||||||
border-radius: 8px;
|
.title {
|
||||||
}
|
font-size: 20pt;
|
||||||
.section {
|
font-weight: bold;
|
||||||
margin-bottom: 32px; /* Espacement augmenté entre les sections */
|
color: #064e3b;
|
||||||
}
|
margin: 0 0 2px 0;
|
||||||
.section-title {
|
}
|
||||||
font-size: 15pt;
|
.subtitle {
|
||||||
font-weight: bold;
|
font-size: 11pt;
|
||||||
color: #4CAF50;
|
color: #059669;
|
||||||
margin-bottom: 18px; /* Espacement sous le titre de section */
|
margin: 0;
|
||||||
border-bottom: 1px solid #4CAF50;
|
font-weight: normal;
|
||||||
padding-bottom: 2px;
|
}
|
||||||
}
|
.header-line {
|
||||||
table {
|
border: none;
|
||||||
width: 100%;
|
border-top: 3px solid #059669;
|
||||||
border-collapse: collapse;
|
margin: 12px 0 20px 0;
|
||||||
margin-bottom: 8px;
|
}
|
||||||
}
|
.photo {
|
||||||
th, td {
|
width: 80px;
|
||||||
border: 1px solid #bbb;
|
height: 80px;
|
||||||
padding: 6px 8px;
|
object-fit: cover;
|
||||||
text-align: left;
|
border: 2px solid #059669;
|
||||||
}
|
border-radius: 4px;
|
||||||
th {
|
}
|
||||||
background: #f3f3f3;
|
|
||||||
font-weight: bold;
|
/* ── Sections ── */
|
||||||
}
|
.section {
|
||||||
tr:nth-child(even) {
|
margin-bottom: 20px;
|
||||||
background: #fafafa;
|
}
|
||||||
}
|
.section-header {
|
||||||
.label-cell {
|
background-color: #059669;
|
||||||
font-weight: bold;
|
color: #ffffff;
|
||||||
width: 30%;
|
font-size: 11pt;
|
||||||
background: #f3f3f3;
|
font-weight: bold;
|
||||||
}
|
padding: 6px 12px;
|
||||||
.value-cell {
|
margin-bottom: 0;
|
||||||
width: 70%;
|
letter-spacing: 0.5px;
|
||||||
}
|
border-radius: 2px 2px 0 0;
|
||||||
.signature {
|
}
|
||||||
margin-top: 30px;
|
.subsection-title {
|
||||||
text-align: right;
|
font-size: 10pt;
|
||||||
font-style: italic;
|
color: #064e3b;
|
||||||
color: #555;
|
font-weight: bold;
|
||||||
}
|
padding: 6px 0 2px 0;
|
||||||
.signature-text {
|
margin: 8px 0 4px 0;
|
||||||
font-weight: bold;
|
border-bottom: 1px solid #d1d5db;
|
||||||
color: #333;
|
}
|
||||||
}
|
|
||||||
.subsection-title {
|
/* ── Tables ── */
|
||||||
font-size: 12pt;
|
table.data {
|
||||||
color: #333;
|
width: 100%;
|
||||||
margin: 8px 0 4px 0;
|
border-collapse: collapse;
|
||||||
font-weight: bold;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
table.data td {
|
||||||
|
padding: 5px 8px;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
font-size: 10pt;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
table.data .label {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #064e3b;
|
||||||
|
background-color: #f0fdf4;
|
||||||
|
width: 25%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
table.data .value {
|
||||||
|
color: #1e293b;
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Paiement ── */
|
||||||
|
table.payment {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
table.payment td {
|
||||||
|
padding: 5px 8px;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
table.payment .label {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #064e3b;
|
||||||
|
background-color: #f0fdf4;
|
||||||
|
width: 35%;
|
||||||
|
}
|
||||||
|
table.payment .value {
|
||||||
|
width: 65%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Footer / Signature ── */
|
||||||
|
.signature-block {
|
||||||
|
margin-top: 24px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background-color: #f8fafc;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.signature-block p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 10pt;
|
||||||
|
color: #475569;
|
||||||
|
}
|
||||||
|
.signature-date {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #064e3b;
|
||||||
|
}
|
||||||
|
.footer-line {
|
||||||
|
border: none;
|
||||||
|
border-top: 2px solid #059669;
|
||||||
|
margin: 20px 0 8px 0;
|
||||||
|
}
|
||||||
|
.footer-text {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 8pt;
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% load myTemplateTag %}
|
{% load myTemplateTag %}
|
||||||
<div class="container">
|
|
||||||
<!-- Header Section -->
|
|
||||||
<div class="header">
|
|
||||||
<h1 class="title">Fiche élève de {{ student.last_name }} {{ student.first_name }}</h1>
|
|
||||||
{% if student.photo %}
|
|
||||||
<img src="{{ student.get_photo_url }}" alt="Photo de l'élève" class="photo" />
|
|
||||||
{% else %}
|
|
||||||
<img src="/static/img/default-photo.jpg" alt="Photo par défaut" class="photo" />
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Élève -->
|
<!-- ═══════ HEADER ═══════ -->
|
||||||
<div class="section">
|
<table class="header-table">
|
||||||
<div class="section-title">ÉLÈVE</div>
|
<tr>
|
||||||
<table>
|
<td class="header-left">
|
||||||
<tr>
|
{% if establishment %}
|
||||||
<td class="label-cell">Nom</td>
|
<p class="school-name">{{ establishment.name }}</p>
|
||||||
<td class="value-cell">{{ student.last_name }}</td>
|
{% endif %}
|
||||||
<td class="label-cell">Prénom</td>
|
<h1 class="title">Fiche Élèves</h1>
|
||||||
<td class="value-cell">{{ student.first_name }}</td>
|
<!-- prettier-ignore -->
|
||||||
</tr>
|
<p class="subtitle">{{ student.last_name }} {{ student.first_name }}{% if school_year %} — {{ school_year }}{% endif %}</p>
|
||||||
<tr>
|
</td>
|
||||||
<td class="label-cell">Adresse</td>
|
<td class="header-right">
|
||||||
<td class="value-cell" colspan="3">{{ student.address }}</td>
|
{% if student.photo %}
|
||||||
</tr>
|
<img src="{{ student.get_photo_url }}" alt="Photo" class="photo" />
|
||||||
<tr>
|
{% endif %}
|
||||||
<td class="label-cell">Genre</td>
|
</td>
|
||||||
<td class="value-cell">{{ student|getStudentGender }}</td>
|
</tr>
|
||||||
<td class="label-cell">Né(e) le</td>
|
</table>
|
||||||
<td class="value-cell">{{ student.birth_date }}</td>
|
<hr class="header-line" />
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="label-cell">À</td>
|
|
||||||
<td class="value-cell">{{ student.birth_place }} ({{ student.birth_postal_code }})</td>
|
|
||||||
<td class="label-cell">Nationalité</td>
|
|
||||||
<td class="value-cell">{{ student.nationality }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="label-cell">Niveau</td>
|
|
||||||
<td class="value-cell">{{ student|getStudentLevel }}</td>
|
|
||||||
<td class="label-cell"></td>
|
|
||||||
<td class="value-cell"></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Responsables -->
|
<!-- ═══════ ÉLÈVE ═══════ -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section-title">RESPONSABLES</div>
|
<div class="section-header">INFORMATIONS DE L'ÉLÈVE</div>
|
||||||
{% for guardian in student.getGuardians %}
|
<table class="data">
|
||||||
<div>
|
<tr>
|
||||||
<div class="subsection-title">Responsable {{ forloop.counter }}</div>
|
<td class="label">Nom</td>
|
||||||
<table>
|
<td class="value">{{ student.last_name }}</td>
|
||||||
<tr>
|
<td class="label">Prénom</td>
|
||||||
<td class="label-cell">Nom</td>
|
<td class="value">{{ student.first_name }}</td>
|
||||||
<td class="value-cell">{{ guardian.last_name }}</td>
|
</tr>
|
||||||
<td class="label-cell">Prénom</td>
|
<tr>
|
||||||
<td class="value-cell">{{ guardian.first_name }}</td>
|
<td class="label">Genre</td>
|
||||||
</tr>
|
<td class="value">{{ student|getStudentGender }}</td>
|
||||||
<tr>
|
<td class="label">Niveau</td>
|
||||||
<td class="label-cell">Adresse</td>
|
<td class="value">{{ student|getStudentLevel }}</td>
|
||||||
<td class="value-cell" colspan="3">{{ guardian.address }}</td>
|
</tr>
|
||||||
</tr>
|
<tr>
|
||||||
<tr>
|
<td class="label">Date de naissance</td>
|
||||||
<td class="label-cell">Email</td>
|
<td class="value">{{ student.formatted_birth_date }}</td>
|
||||||
<td class="value-cell" colspan="3">{{ guardian.email }}</td>
|
<td class="label">Lieu de naissance</td>
|
||||||
</tr>
|
<!-- prettier-ignore -->
|
||||||
<tr>
|
<td class="value">{{ student.birth_place }}{% if student.birth_postal_code %} ({{ student.birth_postal_code }}){% endif %}</td>
|
||||||
<td class="label-cell">Né(e) le</td>
|
</tr>
|
||||||
<td class="value-cell">{{ guardian.birth_date }}</td>
|
<tr>
|
||||||
<td class="label-cell">Téléphone</td>
|
<td class="label">Nationalité</td>
|
||||||
<td class="value-cell">{{ guardian.phone|phone_format }}</td>
|
<td class="value">{{ student.nationality }}</td>
|
||||||
</tr>
|
<td class="label">Médecin traitant</td>
|
||||||
<tr>
|
<td class="value">{{ student.attending_physician }}</td>
|
||||||
<td class="label-cell">Profession</td>
|
</tr>
|
||||||
<td class="value-cell" colspan="3">{{ guardian.profession }}</td>
|
<tr>
|
||||||
</tr>
|
<td class="label">Adresse</td>
|
||||||
</table>
|
<td class="value" colspan="3">{{ student.address }}</td>
|
||||||
</div>
|
</tr>
|
||||||
{% endfor %}
|
</table>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Fratrie -->
|
|
||||||
<div class="section">
|
|
||||||
<div class="section-title">FRATRIE</div>
|
|
||||||
{% for sibling in student.getSiblings %}
|
|
||||||
<div>
|
|
||||||
<div class="subsection-title">Frère/Soeur {{ forloop.counter }}</div>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td class="label-cell">Nom</td>
|
|
||||||
<td class="value-cell">{{ sibling.last_name }}</td>
|
|
||||||
<td class="label-cell">Prénom</td>
|
|
||||||
<td class="value-cell">{{ sibling.first_name }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="label-cell">Né(e) le</td>
|
|
||||||
<td class="value-cell" colspan="3">{{ sibling.birth_date }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Paiement -->
|
|
||||||
<div class="section">
|
|
||||||
<div class="section-title">MODALITÉS DE PAIEMENT</div>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td class="label-cell">Frais d'inscription</td>
|
|
||||||
<td class="value-cell">{{ student|getRegistrationPaymentMethod }} en {{ student|getRegistrationPaymentPlan }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="label-cell">Frais de scolarité</td>
|
|
||||||
<td class="value-cell">{{ student|getTuitionPaymentMethod }} en {{ student|getTuitionPaymentPlan }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Signature -->
|
|
||||||
<div class="signature">
|
|
||||||
Fait le <span class="signature-text">{{ signatureDate }}</span> à <span class="signature-text">{{ signatureTime }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
</html>
|
<!-- ═══════ RESPONSABLES ═══════ -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">RESPONSABLES LÉGAUX</div>
|
||||||
|
{% for guardian in student.getGuardians %}
|
||||||
|
<div class="subsection-title">Responsable {{ forloop.counter }}</div>
|
||||||
|
<table class="data">
|
||||||
|
<tr>
|
||||||
|
<td class="label">Nom</td>
|
||||||
|
<td class="value">{{ guardian.last_name }}</td>
|
||||||
|
<td class="label">Prénom</td>
|
||||||
|
<td class="value">{{ guardian.first_name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label">Date de naissance</td>
|
||||||
|
<td class="value">{{ guardian.birth_date }}</td>
|
||||||
|
<td class="label">Téléphone</td>
|
||||||
|
<td class="value">{{ guardian.phone|phone_format }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label">Email</td>
|
||||||
|
<td class="value" colspan="3">{{ guardian.email }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label">Adresse</td>
|
||||||
|
<td class="value" colspan="3">{{ guardian.address }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label">Profession</td>
|
||||||
|
<td class="value" colspan="3">{{ guardian.profession }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
{% empty %}
|
||||||
|
<p style="color: #94a3b8; font-style: italic; padding: 8px">
|
||||||
|
Aucun responsable renseigné.
|
||||||
|
</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════ FRATRIE ═══════ -->
|
||||||
|
{% if student.getSiblings %}
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">FRATRIE</div>
|
||||||
|
{% for sibling in student.getSiblings %}
|
||||||
|
<div class="subsection-title">Frère / Sœur {{ forloop.counter }}</div>
|
||||||
|
<table class="data">
|
||||||
|
<tr>
|
||||||
|
<td class="label">Nom</td>
|
||||||
|
<td class="value">{{ sibling.last_name }}</td>
|
||||||
|
<td class="label">Prénom</td>
|
||||||
|
<td class="value">{{ sibling.first_name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label">Date de naissance</td>
|
||||||
|
<td class="value" colspan="3">{{ sibling.birth_date }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- ═══════ PAIEMENT ═══════ -->
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">MODALITÉS DE PAIEMENT</div>
|
||||||
|
<table class="payment">
|
||||||
|
<tr>
|
||||||
|
<td class="label">Frais d'inscription</td>
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
<td class="value">{{ student|getRegistrationPaymentMethod }} — {{ student|getRegistrationPaymentPlan }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label">Frais de scolarité</td>
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
<td class="value">{{ student|getTuitionPaymentMethod }} — {{ student|getTuitionPaymentPlan }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════ SIGNATURE ═══════ -->
|
||||||
|
<div class="signature-block">
|
||||||
|
<p>
|
||||||
|
Document généré le
|
||||||
|
<span class="signature-date">{{ signatureDate }}</span> à
|
||||||
|
<span class="signature-date">{{ signatureTime }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="footer-line" />
|
||||||
|
<p class="footer-text">
|
||||||
|
Ce document est généré automatiquement et fait office de fiche
|
||||||
|
d'inscription.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
@ -3,16 +3,16 @@ from django.urls import path, re_path
|
|||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
# RF
|
# RF
|
||||||
from .views import RegisterFormView, RegisterFormWithIdView, send, resend, archive
|
from .views import RegisterFormView, RegisterFormWithIdView, send, resend, archive, generate_registration_pdf
|
||||||
# SubClasses
|
# SubClasses
|
||||||
from .views import StudentView, GuardianView, ChildrenListView, StudentListView, DissociateGuardianView
|
from .views import StudentView, GuardianView, ChildrenListView, StudentListView, DissociateGuardianView
|
||||||
# Files
|
# Files
|
||||||
from .views import (
|
from .views import (
|
||||||
RegistrationSchoolFileMasterView,
|
RegistrationSchoolFileMasterView,
|
||||||
RegistrationSchoolFileMasterSimpleView,
|
RegistrationSchoolFileMasterSimpleView,
|
||||||
RegistrationSchoolFileTemplateView,
|
RegistrationSchoolFileTemplateView,
|
||||||
RegistrationSchoolFileTemplateSimpleView,
|
RegistrationSchoolFileTemplateSimpleView,
|
||||||
RegistrationParentFileMasterSimpleView,
|
RegistrationParentFileMasterSimpleView,
|
||||||
RegistrationParentFileMasterView,
|
RegistrationParentFileMasterView,
|
||||||
RegistrationParentFileTemplateSimpleView,
|
RegistrationParentFileTemplateSimpleView,
|
||||||
RegistrationParentFileTemplateView,
|
RegistrationParentFileTemplateView,
|
||||||
@ -25,11 +25,12 @@ from .views import (
|
|||||||
|
|
||||||
from .views import RegistrationFileGroupView, RegistrationFileGroupSimpleView, get_registration_files_by_group
|
from .views import RegistrationFileGroupView, RegistrationFileGroupSimpleView, get_registration_files_by_group
|
||||||
from .views import (
|
from .views import (
|
||||||
get_school_file_templates_by_rf,
|
get_school_file_templates_by_rf,
|
||||||
get_parent_file_templates_by_rf
|
get_parent_file_templates_by_rf
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
re_path(r'^registerForms/(?P<id>[0-9]+)/pdf$', generate_registration_pdf, name="generate_registration_pdf"),
|
||||||
re_path(r'^registerForms/(?P<id>[0-9]+)/archive$', archive, name="archive"),
|
re_path(r'^registerForms/(?P<id>[0-9]+)/archive$', archive, name="archive"),
|
||||||
re_path(r'^registerForms/(?P<id>[0-9]+)/resend$', resend, name="resend"),
|
re_path(r'^registerForms/(?P<id>[0-9]+)/resend$', resend, name="resend"),
|
||||||
re_path(r'^registerForms/(?P<id>[0-9]+)/send$', send, name="send"),
|
re_path(r'^registerForms/(?P<id>[0-9]+)/send$', send, name="send"),
|
||||||
@ -52,7 +53,7 @@ urlpatterns = [
|
|||||||
re_path(r'^registrationFileGroups/(?P<id>[0-9]+)$', RegistrationFileGroupSimpleView.as_view(), name='registrationFileGroupDetail'),
|
re_path(r'^registrationFileGroups/(?P<id>[0-9]+)$', RegistrationFileGroupSimpleView.as_view(), name='registrationFileGroupDetail'),
|
||||||
re_path(r'^registrationFileGroups/(?P<id>[0-9]+)/templates$', get_registration_files_by_group, name="get_registration_files_by_group"),
|
re_path(r'^registrationFileGroups/(?P<id>[0-9]+)/templates$', get_registration_files_by_group, name="get_registration_files_by_group"),
|
||||||
re_path(r'^registrationFileGroups$', RegistrationFileGroupView.as_view(), name='registrationFileGroups'),
|
re_path(r'^registrationFileGroups$', RegistrationFileGroupView.as_view(), name='registrationFileGroups'),
|
||||||
|
|
||||||
re_path(r'^registrationSchoolFileMasters/(?P<id>[0-9]+)$', RegistrationSchoolFileMasterSimpleView.as_view(), name='registrationSchoolFileMasters'),
|
re_path(r'^registrationSchoolFileMasters/(?P<id>[0-9]+)$', RegistrationSchoolFileMasterSimpleView.as_view(), name='registrationSchoolFileMasters'),
|
||||||
re_path(r'^registrationSchoolFileMasters$', RegistrationSchoolFileMasterView.as_view(), name='registrationSchoolFileMasters'),
|
re_path(r'^registrationSchoolFileMasters$', RegistrationSchoolFileMasterView.as_view(), name='registrationSchoolFileMasters'),
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ def save_file_replacing_existing(file_field, filename, content, save=True):
|
|||||||
"""
|
"""
|
||||||
Sauvegarde un fichier en écrasant l'existant s'il porte le même nom.
|
Sauvegarde un fichier en écrasant l'existant s'il porte le même nom.
|
||||||
Évite les suffixes automatiques Django (ex: fichier_N5QdZpk.pdf).
|
Évite les suffixes automatiques Django (ex: fichier_N5QdZpk.pdf).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
file_field: Le FileField Django (ex: registerForm.registration_file)
|
file_field: Le FileField Django (ex: registerForm.registration_file)
|
||||||
filename: Le nom du fichier à sauvegarder
|
filename: Le nom du fichier à sauvegarder
|
||||||
@ -51,7 +51,7 @@ def save_file_replacing_existing(file_field, filename, content, save=True):
|
|||||||
logger.debug(f"[save_file] Ancien fichier supprimé: {file_field.path}")
|
logger.debug(f"[save_file] Ancien fichier supprimé: {file_field.path}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[save_file] Erreur suppression ancien fichier: {e}")
|
logger.error(f"[save_file] Erreur suppression ancien fichier: {e}")
|
||||||
|
|
||||||
# Sauvegarder le nouveau fichier
|
# Sauvegarder le nouveau fichier
|
||||||
file_field.save(filename, content, save=save)
|
file_field.save(filename, content, save=save)
|
||||||
|
|
||||||
@ -207,19 +207,21 @@ def create_templates_for_registration_form(register_form):
|
|||||||
logger.error(f"Erreur lors de la génération du PDF pour le template: {e}")
|
logger.error(f"Erreur lors de la génération du PDF pour le template: {e}")
|
||||||
file_name = None
|
file_name = None
|
||||||
|
|
||||||
from django.core.files.base import ContentFile
|
|
||||||
upload_rel_path = registration_school_file_upload_to(
|
|
||||||
type("Tmp", (), {
|
|
||||||
"registration_form": register_form,
|
|
||||||
"establishment": getattr(register_form, "establishment", None),
|
|
||||||
"student": getattr(register_form, "student", None)
|
|
||||||
})(),
|
|
||||||
file_name
|
|
||||||
)
|
|
||||||
abs_path = os.path.join(settings.MEDIA_ROOT, upload_rel_path)
|
|
||||||
master_file_path = m.file.path if m.file and hasattr(m.file, 'path') else None
|
master_file_path = m.file.path if m.file and hasattr(m.file, 'path') else None
|
||||||
|
|
||||||
|
def _build_upload_path(template_pk):
|
||||||
|
"""Génère le chemin relatif et absolu pour un template avec un pk connu."""
|
||||||
|
rel = registration_school_file_upload_to(
|
||||||
|
type("Tmp", (), {
|
||||||
|
"registration_form": register_form,
|
||||||
|
"pk": template_pk,
|
||||||
|
})(),
|
||||||
|
file_name,
|
||||||
|
)
|
||||||
|
return rel, os.path.join(settings.MEDIA_ROOT, rel)
|
||||||
|
|
||||||
if tmpl:
|
if tmpl:
|
||||||
|
upload_rel_path, abs_path = _build_upload_path(tmpl.pk)
|
||||||
template_file_name = os.path.basename(tmpl.file.name) if tmpl.file and tmpl.file.name else None
|
template_file_name = os.path.basename(tmpl.file.name) if tmpl.file and tmpl.file.name else None
|
||||||
master_file_changed = template_file_name != file_name
|
master_file_changed = template_file_name != file_name
|
||||||
# --- GESTION FORM EXISTANT : suppression ancien template si nom ou contenu master changé ---
|
# --- GESTION FORM EXISTANT : suppression ancien template si nom ou contenu master changé ---
|
||||||
@ -254,7 +256,7 @@ def create_templates_for_registration_form(register_form):
|
|||||||
logger.info("util.create_templates_for_registration_form - Mise à jour school template %s from master %s for RF %s", tmpl.pk, m.pk, register_form.pk)
|
logger.info("util.create_templates_for_registration_form - Mise à jour school template %s from master %s for RF %s", tmpl.pk, m.pk, register_form.pk)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Sinon, création du template comme avant
|
# Sinon, création du template — sauvegarder d'abord pour obtenir un pk
|
||||||
tmpl = RegistrationSchoolFileTemplate(
|
tmpl = RegistrationSchoolFileTemplate(
|
||||||
master=m,
|
master=m,
|
||||||
registration_form=register_form,
|
registration_form=register_form,
|
||||||
@ -262,8 +264,10 @@ def create_templates_for_registration_form(register_form):
|
|||||||
formTemplateData=m.formMasterData or [],
|
formTemplateData=m.formMasterData or [],
|
||||||
slug=slug,
|
slug=slug,
|
||||||
)
|
)
|
||||||
|
tmpl.save() # pk attribué ici
|
||||||
if file_name:
|
if file_name:
|
||||||
# Copier le fichier du master si besoin (form existant)
|
upload_rel_path, abs_path = _build_upload_path(tmpl.pk)
|
||||||
|
# Copier le fichier du master si besoin
|
||||||
if master_file_path and not os.path.exists(abs_path):
|
if master_file_path and not os.path.exists(abs_path):
|
||||||
try:
|
try:
|
||||||
import shutil
|
import shutil
|
||||||
@ -273,7 +277,7 @@ def create_templates_for_registration_form(register_form):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Erreur lors de la copie du fichier master pour le template: {e}")
|
logger.error(f"Erreur lors de la copie du fichier master pour le template: {e}")
|
||||||
tmpl.file.name = upload_rel_path
|
tmpl.file.name = upload_rel_path
|
||||||
tmpl.save()
|
tmpl.save()
|
||||||
created.append(tmpl)
|
created.append(tmpl)
|
||||||
logger.info("util.create_templates_for_registration_form - Created school template %s from master %s for RF %s", tmpl.pk, m.pk, register_form.pk)
|
logger.info("util.create_templates_for_registration_form - Created school template %s from master %s for RF %s", tmpl.pk, m.pk, register_form.pk)
|
||||||
|
|
||||||
@ -453,6 +457,8 @@ def rfToPDF(registerForm, filename):
|
|||||||
'signatureDate': convertToStr(_now(), '%d-%m-%Y'),
|
'signatureDate': convertToStr(_now(), '%d-%m-%Y'),
|
||||||
'signatureTime': convertToStr(_now(), '%H:%M'),
|
'signatureTime': convertToStr(_now(), '%H:%M'),
|
||||||
'student': registerForm.student,
|
'student': registerForm.student,
|
||||||
|
'establishment': registerForm.establishment,
|
||||||
|
'school_year': registerForm.school_year,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Générer le PDF
|
# Générer le PDF
|
||||||
@ -474,6 +480,24 @@ def rfToPDF(registerForm, filename):
|
|||||||
|
|
||||||
return registerForm.registration_file
|
return registerForm.registration_file
|
||||||
|
|
||||||
|
def generateRegistrationPDF(registerForm):
|
||||||
|
"""
|
||||||
|
Génère le PDF d'un dossier d'inscription à la volée et retourne le contenu binaire.
|
||||||
|
Ne sauvegarde pas le fichier sur disque.
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'pdf_title': f"Dossier d'inscription de {registerForm.student.first_name}",
|
||||||
|
'signatureDate': convertToStr(_now(), '%d-%m-%Y'),
|
||||||
|
'signatureTime': convertToStr(_now(), '%H:%M'),
|
||||||
|
'student': registerForm.student,
|
||||||
|
'establishment': registerForm.establishment,
|
||||||
|
'school_year': registerForm.school_year,
|
||||||
|
}
|
||||||
|
pdf = renderers.render_to_pdf('pdfs/fiche_eleve.html', data)
|
||||||
|
if not pdf:
|
||||||
|
raise ValueError("Erreur lors de la génération du PDF.")
|
||||||
|
return pdf.content
|
||||||
|
|
||||||
def delete_registration_files(registerForm):
|
def delete_registration_files(registerForm):
|
||||||
"""
|
"""
|
||||||
Supprime le fichier et le dossier associés à un RegistrationForm.
|
Supprime le fichier et le dossier associés à un RegistrationForm.
|
||||||
|
|||||||
@ -1,22 +1,23 @@
|
|||||||
from .register_form_views import (
|
from .register_form_views import (
|
||||||
RegisterFormView,
|
RegisterFormView,
|
||||||
RegisterFormWithIdView,
|
RegisterFormWithIdView,
|
||||||
send,
|
send,
|
||||||
resend,
|
resend,
|
||||||
archive,
|
archive,
|
||||||
get_school_file_templates_by_rf,
|
get_school_file_templates_by_rf,
|
||||||
get_parent_file_templates_by_rf
|
get_parent_file_templates_by_rf,
|
||||||
|
generate_registration_pdf
|
||||||
)
|
)
|
||||||
from .registration_school_file_masters_views import (
|
from .registration_school_file_masters_views import (
|
||||||
RegistrationSchoolFileMasterView,
|
RegistrationSchoolFileMasterView,
|
||||||
RegistrationSchoolFileMasterSimpleView,
|
RegistrationSchoolFileMasterSimpleView,
|
||||||
)
|
)
|
||||||
from .registration_school_file_templates_views import (
|
from .registration_school_file_templates_views import (
|
||||||
RegistrationSchoolFileTemplateView,
|
RegistrationSchoolFileTemplateView,
|
||||||
RegistrationSchoolFileTemplateSimpleView
|
RegistrationSchoolFileTemplateSimpleView
|
||||||
)
|
)
|
||||||
from .registration_parent_file_masters_views import (
|
from .registration_parent_file_masters_views import (
|
||||||
RegistrationParentFileMasterView,
|
RegistrationParentFileMasterView,
|
||||||
RegistrationParentFileMasterSimpleView
|
RegistrationParentFileMasterSimpleView
|
||||||
)
|
)
|
||||||
from .registration_parent_file_templates_views import (
|
from .registration_parent_file_templates_views import (
|
||||||
@ -48,6 +49,7 @@ __all__ = [
|
|||||||
'get_registration_files_by_group',
|
'get_registration_files_by_group',
|
||||||
'get_school_file_templates_by_rf',
|
'get_school_file_templates_by_rf',
|
||||||
'get_parent_file_templates_by_rf',
|
'get_parent_file_templates_by_rf',
|
||||||
|
'generate_registration_pdf',
|
||||||
'StudentView',
|
'StudentView',
|
||||||
'StudentListView',
|
'StudentListView',
|
||||||
'ChildrenListView',
|
'ChildrenListView',
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from django.http.response import JsonResponse
|
from django.http.response import JsonResponse
|
||||||
|
from django.http import HttpResponse
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
|
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
@ -365,7 +366,7 @@ class RegisterFormWithIdView(APIView):
|
|||||||
student = registerForm.student
|
student = registerForm.student
|
||||||
student_name = f"{student.first_name} {student.last_name}"
|
student_name = f"{student.first_name} {student.last_name}"
|
||||||
notes = registerForm.notes or "Aucun motif spécifié"
|
notes = registerForm.notes or "Aucun motif spécifié"
|
||||||
|
|
||||||
guardians = student.guardians.all()
|
guardians = student.guardians.all()
|
||||||
for guardian in guardians:
|
for guardian in guardians:
|
||||||
email = None
|
email = None
|
||||||
@ -373,13 +374,13 @@ class RegisterFormWithIdView(APIView):
|
|||||||
email = guardian.profile_role.profile.email
|
email = guardian.profile_role.profile.email
|
||||||
if not email:
|
if not email:
|
||||||
email = getattr(guardian, "email", None)
|
email = getattr(guardian, "email", None)
|
||||||
|
|
||||||
if email:
|
if email:
|
||||||
logger.info(f"[RF_SENT] Envoi email de refus à {email} pour l'élève {student_name}")
|
logger.info(f"[RF_SENT] Envoi email de refus à {email} pour l'élève {student_name}")
|
||||||
mailer.sendRefusDossier(email, registerForm.establishment.pk, student_name, notes)
|
mailer.sendRefusDossier(email, registerForm.establishment.pk, student_name, notes)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[RF_SENT] Erreur lors de l'envoi de l'email de refus: {e}")
|
logger.error(f"[RF_SENT] Erreur lors de l'envoi de l'email de refus: {e}")
|
||||||
|
|
||||||
updateStateMachine(registerForm, 'EVENT_REFUSE')
|
updateStateMachine(registerForm, 'EVENT_REFUSE')
|
||||||
util.delete_registration_files(registerForm)
|
util.delete_registration_files(registerForm)
|
||||||
elif _status == RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT:
|
elif _status == RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT:
|
||||||
@ -411,6 +412,17 @@ class RegisterFormWithIdView(APIView):
|
|||||||
# Initialisation de la liste des fichiers à fusionner
|
# Initialisation de la liste des fichiers à fusionner
|
||||||
fileNames = []
|
fileNames = []
|
||||||
|
|
||||||
|
# Régénérer la fiche élève avec le nouveau template avant fusion
|
||||||
|
try:
|
||||||
|
base_dir = os.path.join(settings.MEDIA_ROOT, f"registration_files/dossier_rf_{registerForm.pk}")
|
||||||
|
os.makedirs(base_dir, exist_ok=True)
|
||||||
|
initial_pdf = f"{base_dir}/Inscription_{registerForm.student.last_name}_{registerForm.student.first_name}.pdf"
|
||||||
|
registerForm.registration_file = util.rfToPDF(registerForm, initial_pdf)
|
||||||
|
registerForm.save()
|
||||||
|
logger.debug(f"[RF_VALIDATED] Fiche élève régénérée avant fusion")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[RF_VALIDATED] Erreur lors de la régénération de la fiche élève: {e}")
|
||||||
|
|
||||||
# Ajout du fichier registration_file en première position
|
# Ajout du fichier registration_file en première position
|
||||||
if registerForm.registration_file:
|
if registerForm.registration_file:
|
||||||
fileNames.append(registerForm.registration_file.path)
|
fileNames.append(registerForm.registration_file.path)
|
||||||
@ -488,7 +500,7 @@ class RegisterFormWithIdView(APIView):
|
|||||||
class_name = None
|
class_name = None
|
||||||
if student.associated_class:
|
if student.associated_class:
|
||||||
class_name = student.associated_class.atmosphere_name
|
class_name = student.associated_class.atmosphere_name
|
||||||
|
|
||||||
guardians = student.guardians.all()
|
guardians = student.guardians.all()
|
||||||
for guardian in guardians:
|
for guardian in guardians:
|
||||||
email = None
|
email = None
|
||||||
@ -496,13 +508,13 @@ class RegisterFormWithIdView(APIView):
|
|||||||
email = guardian.profile_role.profile.email
|
email = guardian.profile_role.profile.email
|
||||||
if not email:
|
if not email:
|
||||||
email = getattr(guardian, "email", None)
|
email = getattr(guardian, "email", None)
|
||||||
|
|
||||||
if email:
|
if email:
|
||||||
logger.info(f"[RF_VALIDATED] Envoi email de validation à {email} pour l'élève {student_name}")
|
logger.info(f"[RF_VALIDATED] Envoi email de validation à {email} pour l'élève {student_name}")
|
||||||
mailer.sendValidationDossier(email, registerForm.establishment.pk, student_name, class_name)
|
mailer.sendValidationDossier(email, registerForm.establishment.pk, student_name, class_name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[RF_VALIDATED] Erreur lors de l'envoi de l'email de validation: {e}")
|
logger.error(f"[RF_VALIDATED] Erreur lors de l'envoi de l'email de validation: {e}")
|
||||||
|
|
||||||
# Lancer l'envoi d'email dans un thread séparé pour ne pas bloquer la réponse
|
# Lancer l'envoi d'email dans un thread séparé pour ne pas bloquer la réponse
|
||||||
email_thread = threading.Thread(target=send_validation_emails)
|
email_thread = threading.Thread(target=send_validation_emails)
|
||||||
email_thread.start()
|
email_thread.start()
|
||||||
@ -518,7 +530,7 @@ class RegisterFormWithIdView(APIView):
|
|||||||
student = registerForm.student
|
student = registerForm.student
|
||||||
student_name = f"{student.first_name} {student.last_name}"
|
student_name = f"{student.first_name} {student.last_name}"
|
||||||
notes = data.get('notes', '') or "Aucun motif spécifié"
|
notes = data.get('notes', '') or "Aucun motif spécifié"
|
||||||
|
|
||||||
guardians = student.guardians.all()
|
guardians = student.guardians.all()
|
||||||
for guardian in guardians:
|
for guardian in guardians:
|
||||||
email = None
|
email = None
|
||||||
@ -526,17 +538,17 @@ class RegisterFormWithIdView(APIView):
|
|||||||
email = guardian.profile_role.profile.email
|
email = guardian.profile_role.profile.email
|
||||||
if not email:
|
if not email:
|
||||||
email = getattr(guardian, "email", None)
|
email = getattr(guardian, "email", None)
|
||||||
|
|
||||||
if email:
|
if email:
|
||||||
logger.info(f"[RF_ARCHIVED] Envoi email de refus définitif à {email} pour l'élève {student_name}")
|
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)
|
mailer.sendRefusDefinitif(email, registerForm.establishment.pk, student_name, notes)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[RF_ARCHIVED] Erreur lors de l'envoi de l'email de refus définitif: {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
|
# 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 = threading.Thread(target=send_refus_definitif_emails)
|
||||||
email_thread.start()
|
email_thread.start()
|
||||||
|
|
||||||
updateStateMachine(registerForm, 'EVENT_ARCHIVE')
|
updateStateMachine(registerForm, 'EVENT_ARCHIVE')
|
||||||
|
|
||||||
# Retourner les données mises à jour
|
# Retourner les données mises à jour
|
||||||
@ -946,3 +958,26 @@ def get_parent_file_templates_by_rf(request, id):
|
|||||||
return JsonResponse(serializer.data, safe=False)
|
return JsonResponse(serializer.data, safe=False)
|
||||||
except RegistrationParentFileTemplate.DoesNotExist:
|
except RegistrationParentFileTemplate.DoesNotExist:
|
||||||
return JsonResponse({'error': 'Aucune pièce à fournir trouvée pour ce dossier d\'inscription'}, status=status.HTTP_404_NOT_FOUND)
|
return JsonResponse({'error': 'Aucune pièce à fournir trouvée pour ce dossier d\'inscription'}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
method='get',
|
||||||
|
responses={200: openapi.Response('PDF file', schema=openapi.Schema(type=openapi.TYPE_FILE))},
|
||||||
|
operation_description="Génère et retourne le PDF de la fiche élève à la volée",
|
||||||
|
operation_summary="Télécharger la fiche élève (régénérée)"
|
||||||
|
)
|
||||||
|
@api_view(['GET'])
|
||||||
|
def generate_registration_pdf(request, id):
|
||||||
|
try:
|
||||||
|
registerForm = RegistrationForm.objects.select_related('student', 'establishment').get(student__id=id)
|
||||||
|
except RegistrationForm.DoesNotExist:
|
||||||
|
return JsonResponse({"error": "Dossier d'inscription introuvable"}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
try:
|
||||||
|
pdf_content = util.generateRegistrationPDF(registerForm)
|
||||||
|
except ValueError as e:
|
||||||
|
return JsonResponse({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
filename = f"Inscription_{registerForm.student.last_name}_{registerForm.student.first_name}.pdf"
|
||||||
|
response = HttpResponse(pdf_content, content_type='application/pdf')
|
||||||
|
response['Content-Disposition'] = f'inline; filename="{filename}"'
|
||||||
|
return response
|
||||||
|
|||||||
@ -53,10 +53,10 @@ const FilesModal = ({
|
|||||||
.then((parentFiles) => {
|
.then((parentFiles) => {
|
||||||
// Construct the categorized files list
|
// Construct the categorized files list
|
||||||
const categorizedFiles = {
|
const categorizedFiles = {
|
||||||
registrationFile: selectedRegisterForm.registration_file
|
registrationFile: selectedRegisterForm.student?.id
|
||||||
? {
|
? {
|
||||||
name: 'Fiche élève',
|
name: 'Fiche élève',
|
||||||
url: getSecureFileUrl(selectedRegisterForm.registration_file),
|
url: `/api/generate-pdf?studentId=${selectedRegisterForm.student.id}`,
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
fusionFile: selectedRegisterForm.fusion_file
|
fusionFile: selectedRegisterForm.fusion_file
|
||||||
|
|||||||
@ -152,7 +152,11 @@ export default function ValidateSubscription({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const allTemplates = [
|
const allTemplates = [
|
||||||
{ name: 'Fiche élève', file: student_file, type: 'main' },
|
{
|
||||||
|
name: 'Fiche élève',
|
||||||
|
file: `/api/generate-pdf?studentId=${studentId}`,
|
||||||
|
type: 'main',
|
||||||
|
},
|
||||||
...schoolFileTemplates.map((template) => ({
|
...schoolFileTemplates.map((template) => ({
|
||||||
name: template.name || 'Document scolaire',
|
name: template.name || 'Document scolaire',
|
||||||
file: template.file,
|
file: template.file,
|
||||||
@ -213,7 +217,11 @@ export default function ValidateSubscription({
|
|||||||
{allTemplates[currentTemplateIndex].name || 'Document sans nom'}
|
{allTemplates[currentTemplateIndex].name || 'Document sans nom'}
|
||||||
</h3>
|
</h3>
|
||||||
<iframe
|
<iframe
|
||||||
src={getSecureFileUrl(allTemplates[currentTemplateIndex].file)}
|
src={
|
||||||
|
allTemplates[currentTemplateIndex].type === 'main'
|
||||||
|
? allTemplates[currentTemplateIndex].file
|
||||||
|
: getSecureFileUrl(allTemplates[currentTemplateIndex].file)
|
||||||
|
}
|
||||||
title={
|
title={
|
||||||
allTemplates[currentTemplateIndex].type === 'main'
|
allTemplates[currentTemplateIndex].type === 'main'
|
||||||
? 'Document Principal'
|
? 'Document Principal'
|
||||||
|
|||||||
58
Front-End/src/pages/api/generate-pdf.js
Normal file
58
Front-End/src/pages/api/generate-pdf.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { getToken } from 'next-auth/jwt';
|
||||||
|
|
||||||
|
const BACKEND_URL = process.env.NEXT_PUBLIC_API_URL;
|
||||||
|
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
if (req.method !== 'GET') {
|
||||||
|
return res.status(405).json({ error: 'Method not allowed' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = await getToken({
|
||||||
|
req,
|
||||||
|
secret: process.env.AUTH_SECRET,
|
||||||
|
cookieName: 'n3wtschool_session_token',
|
||||||
|
});
|
||||||
|
if (!token?.token) {
|
||||||
|
return res.status(401).json({ error: 'Non authentifié' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { studentId } = req.query;
|
||||||
|
if (!studentId) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ error: 'Le paramètre "studentId" est requis' });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const backendUrl = `${BACKEND_URL}/Subscriptions/registerForms/${encodeURIComponent(studentId)}/pdf`;
|
||||||
|
|
||||||
|
const backendRes = await fetch(backendUrl, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token.token}`,
|
||||||
|
Connection: 'close',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!backendRes.ok) {
|
||||||
|
return res.status(backendRes.status).json({
|
||||||
|
error: `Erreur backend: ${backendRes.status}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType =
|
||||||
|
backendRes.headers.get('content-type') || 'application/pdf';
|
||||||
|
const contentDisposition = backendRes.headers.get('content-disposition');
|
||||||
|
|
||||||
|
res.setHeader('Content-Type', contentType);
|
||||||
|
if (contentDisposition) {
|
||||||
|
res.setHeader('Content-Disposition', contentDisposition);
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = Buffer.from(await backendRes.arrayBuffer());
|
||||||
|
return res.send(buffer);
|
||||||
|
} catch {
|
||||||
|
return res
|
||||||
|
.status(500)
|
||||||
|
.json({ error: 'Erreur lors de la génération du PDF' });
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user