diff --git a/Back-End/Subscriptions/Configuration/automate.json b/Back-End/Subscriptions/Configuration/automate.json
index a142479..d14852d 100644
--- a/Back-End/Subscriptions/Configuration/automate.json
+++ b/Back-End/Subscriptions/Configuration/automate.json
@@ -1,68 +1,79 @@
{
"states": [
- "ABSENT",
- "CREE",
- "ENVOYE",
- "EN_VALIDATION",
- "A_RELANCER",
- "VALIDE",
- "ARCHIVE"
+ "IDLE",
+ "INITIALIZED",
+ "SENT",
+ "UNDER_REVIEW",
+ "TO_BE_FOLLOWED_UP",
+ "VALIDATED",
+ "ARCHIVED",
+ "SEPA_SENT"
],
"transitions": [
{
- "name": "creationDI",
- "from": "ABSENT",
- "to": "CREE"
+ "name": "EVENT_INIT",
+ "from": "IDLE",
+ "to": "INITIALIZED"
},
{
- "name": "envoiDI",
- "from": "CREE",
- "to": "ENVOYE"
+ "name": "EVENT_SEND",
+ "from": "INITIALIZED",
+ "to": "SENT"
},
{
- "name": "archiveDI",
- "from": "CREE",
- "to": "ARCHIVE"
+ "name": "EVENT_ARCHIVE",
+ "from": "INITIALIZED",
+ "to": "ARCHIVED"
},
{
- "name": "saisiDI",
- "from": "ENVOYE",
- "to": "EN_VALIDATION"
+ "name": "EVENT_SIGNATURE",
+ "from": "SENT",
+ "to": "UNDER_REVIEW"
},
{
- "name": "relanceDI",
- "from": "ENVOYE",
- "to": "A_RELANCER"
+ "name": "EVENT_FOLLOW_UP",
+ "from": "SENT",
+ "to": "TO_BE_FOLLOWED_UP"
},
{
- "name": "archiveDI",
- "from": "A_RELANCER",
- "to": "ARCHIVE"
+ "name": "EVENT_ARCHIVE",
+ "from": "SENT",
+ "to": "ARCHIVED"
},
{
- "name": "archiveDI",
- "from": "ENVOYE",
- "to": "ARCHIVE"
+ "name": "EVENT_ARCHIVE",
+ "from": "TO_BE_FOLLOWED_UP",
+ "to": "ARCHIVED"
},
{
- "name": "refuseDI",
- "from": "EN_VALIDATION",
- "to": "ENVOYE"
+ "name": "EVENT_REFUSE",
+ "from": "UNDER_REVIEW",
+ "to": "SENT"
},
{
- "name": "valideDI",
- "from": "EN_VALIDATION",
- "to": "VALIDE"
+ "name": "EVENT_VALIDATE",
+ "from": "UNDER_REVIEW",
+ "to": "VALIDATED"
},
{
- "name": "archiveDI",
- "from": "EN_VALIDATION",
- "to": "ARCHIVE"
+ "name": "EVENT_SEND_SEPA",
+ "from": "UNDER_REVIEW",
+ "to": "SEPA_SENT"
},
{
- "name": "archiveDI",
- "from": "VALIDE",
- "to": "ARCHIVE"
+ "name": "EVENT_ARCHIVE",
+ "from": "UNDER_REVIEW",
+ "to": "ARCHIVED"
+ },
+ {
+ "name": "EVENT_SIGNATURE_SEPA",
+ "from": "SEPA_SENT",
+ "to": "UNDER_REVIEW"
+ },
+ {
+ "name": "EVENT_ARCHIVE",
+ "from": "VALIDATED",
+ "to": "ARCHIVED"
}
]
}
diff --git a/Back-End/Subscriptions/automate.py b/Back-End/Subscriptions/automate.py
index 5b8180f..91b8fa2 100644
--- a/Back-End/Subscriptions/automate.py
+++ b/Back-End/Subscriptions/automate.py
@@ -3,13 +3,14 @@ import json
from Subscriptions.models import RegistrationForm
state_mapping = {
- "ABSENT": RegistrationForm.RegistrationFormStatus.RF_ABSENT,
- "CREE": RegistrationForm.RegistrationFormStatus.RF_CREATED,
- "ENVOYE": RegistrationForm.RegistrationFormStatus.RF_SENT,
- "EN_VALIDATION": RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW,
- "A_RELANCER": RegistrationForm.RegistrationFormStatus.RF_TO_BE_FOLLOWED_UP,
- "VALIDE": RegistrationForm.RegistrationFormStatus.RF_VALIDATED,
- "ARCHIVE": RegistrationForm.RegistrationFormStatus.RF_ARCHIVED
+ "IDLE": RegistrationForm.RegistrationFormStatus.RF_IDLE,
+ "INITIALIZED": RegistrationForm.RegistrationFormStatus.RF_INITIALIZED,
+ "SENT": RegistrationForm.RegistrationFormStatus.RF_SENT,
+ "UNDER_REVIEW": RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW,
+ "TO_BE_FOLLOWED_UP": RegistrationForm.RegistrationFormStatus.RF_TO_BE_FOLLOWED_UP,
+ "VALIDATED": RegistrationForm.RegistrationFormStatus.RF_VALIDATED,
+ "ARCHIVED": RegistrationForm.RegistrationFormStatus.RF_ARCHIVED,
+ "SEPA_SENT": RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT
}
def load_config(config_file):
diff --git a/Back-End/Subscriptions/mailManager.py b/Back-End/Subscriptions/mailManager.py
index 26342e3..3d393dc 100644
--- a/Back-End/Subscriptions/mailManager.py
+++ b/Back-End/Subscriptions/mailManager.py
@@ -65,7 +65,7 @@ def envoieRelanceDossierInscription(recipients, code):
return errorMessage
def isValid(message, fiche_inscription):
- # Est-ce que la référence du dossier est VALIDE
+ # Est-ce que la référence du dossier est VALIDATED
subject = message.subject
print ("++++ " + subject)
responsableMail = message.from_header
diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py
index d1ffd38..3b3703a 100644
--- a/Back-End/Subscriptions/models.py
+++ b/Back-End/Subscriptions/models.py
@@ -3,7 +3,7 @@ from django.utils.timezone import now
from django.conf import settings
from django.utils.translation import gettext_lazy as _
-from School.models import SchoolClass, Fee, Discount
+from School.models import SchoolClass, Fee, Discount, PaymentModeType
from Auth.models import ProfileRole
from Establishment.models import Establishment
@@ -62,11 +62,6 @@ class Student(models.Model):
MS = 3, _('MS - Moyenne Section')
GS = 4, _('GS - Grande Section')
- class PaymentMethod(models.IntegerChoices):
- NONE = 0, _('Sélection du mode de paiement')
- SEPA_DIRECT_DEBIT = 1, _('Prélèvement SEPA')
- CHECK = 2, _('Chèques')
-
last_name = models.CharField(max_length=200, default="")
first_name = models.CharField(max_length=200, default="")
gender = models.IntegerField(choices=StudentGender, default=StudentGender.NONE, blank=True)
@@ -77,7 +72,6 @@ class Student(models.Model):
birth_place = models.CharField(max_length=200, default="", blank=True)
birth_postal_code = models.IntegerField(default=0, blank=True)
attending_physician = models.CharField(max_length=200, default="", blank=True)
- payment_method = models.IntegerField(choices=PaymentMethod, default=PaymentMethod.NONE, blank=True)
# Many-to-Many Relationship
profiles = models.ManyToManyField('Auth.Profile', blank=True)
@@ -184,17 +178,18 @@ class RegistrationTemplateMaster(models.Model):
class RegistrationForm(models.Model):
class RegistrationFormStatus(models.IntegerChoices):
- RF_ABSENT = 0, _('Pas de dossier d\'inscription')
- RF_CREATED = 1, _('Dossier d\'inscription créé')
+ RF_IDLE = 0, _('Pas de dossier d\'inscription')
+ RF_INITIALIZED = 1, _('Dossier d\'inscription initialisé')
RF_SENT = 2, _('Dossier d\'inscription envoyé')
RF_UNDER_REVIEW = 3, _('Dossier d\'inscription en cours de validation')
RF_TO_BE_FOLLOWED_UP = 4, _('Dossier d\'inscription à relancer')
RF_VALIDATED = 5, _('Dossier d\'inscription validé')
RF_ARCHIVED = 6, _('Dossier d\'inscription archivé')
+ RF_SEPA_SENT = 7, _('Mandat SEPA envoyé')
# One-to-One Relationship
student = models.OneToOneField(Student, on_delete=models.CASCADE, primary_key=True)
- status = models.IntegerField(choices=RegistrationFormStatus, default=RegistrationFormStatus.RF_ABSENT)
+ status = models.IntegerField(choices=RegistrationFormStatus, default=RegistrationFormStatus.RF_IDLE)
last_update = models.DateTimeField(auto_now=True)
notes = models.CharField(max_length=200, blank=True)
registration_link_code = models.CharField(max_length=200, default="", blank=True)
@@ -217,6 +212,8 @@ class RegistrationForm(models.Model):
blank=True)
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='register_forms')
+ registration_payment = models.IntegerField(choices=PaymentModeType.choices, null=True, blank=True)
+ tuition_payment = models.IntegerField(choices=PaymentModeType.choices, null=True, blank=True)
def __str__(self):
return "RF_" + self.student.last_name + "_" + self.student.first_name
diff --git a/Back-End/Subscriptions/serializers.py b/Back-End/Subscriptions/serializers.py
index 1ed09df..c80357a 100644
--- a/Back-End/Subscriptions/serializers.py
+++ b/Back-End/Subscriptions/serializers.py
@@ -221,8 +221,8 @@ class RegistrationFormSerializer(serializers.ModelSerializer):
def update(self, instance, validated_data):
student_data = validated_data.pop('student', None)
- fees_data = validated_data.pop('fees', [])
- discounts_data = validated_data.pop('discounts', [])
+ fees_data = validated_data.pop('fees', None)
+ discounts_data = validated_data.pop('discounts', None)
if student_data:
student = instance.student
StudentSerializer.update(StudentSerializer(), student, student_data)
@@ -235,8 +235,11 @@ class RegistrationFormSerializer(serializers.ModelSerializer):
instance.save()
# Associer les IDs des objets Fee et Discount au RegistrationForm
- instance.fees.set([fee.id for fee in fees_data])
- instance.discounts.set([discount.id for discount in discounts_data])
+ if fees_data is not None:
+ instance.fees.set([fee.id for fee in fees_data])
+
+ if discounts_data is not None:
+ instance.discounts.set([discount.id for discount in discounts_data])
return instance
diff --git a/Back-End/Subscriptions/tasks.py b/Back-End/Subscriptions/tasks.py
index e89d2b7..cae3930 100644
--- a/Back-End/Subscriptions/tasks.py
+++ b/Back-End/Subscriptions/tasks.py
@@ -21,10 +21,10 @@ def check_for_signature_deadlines():
send_notification(dossier)
def send_notification(dossier):
- logger.debug(f'Dossier en attente.... {dossier} - Positionnement à l\'état A_RELANCER')
+ logger.debug(f'Dossier en attente.... {dossier} - Positionnement à l\'état TO_BE_FOLLOWED_UP')
# Changer l'état de l'automate
- updateStateMachine(dossier, 'relanceDI')
+ updateStateMachine(dossier, 'EVENT_FOLLOW_UP')
url = settings.URL_DJANGO + 'GestionMessagerie/message'
diff --git a/Back-End/Subscriptions/templates/pdfs/dossier_inscription.html b/Back-End/Subscriptions/templates/pdfs/dossier_inscription.html
index cb01e82..f23d394 100644
--- a/Back-End/Subscriptions/templates/pdfs/dossier_inscription.html
+++ b/Back-End/Subscriptions/templates/pdfs/dossier_inscription.html
@@ -194,8 +194,13 @@
MODALITÉS DE PAIEMENT
- {% with paymentMethod=student|getStudentPaymentMethod %}
-
{{ paymentMethod }}
+
Frais d'inscription
+ {% with registrationPayment=student|getRegistrationPaymentMethod %}
+
{{ registrationPayment }}
+ {% endwith %}
+
Frais de scolarité
+ {% with tuitionPayment=student|getTuitionPaymentMethod %}
+
{{ tuitionPayment }}
{% endwith %}
diff --git a/Back-End/Subscriptions/templatetags/myTemplateTag.py b/Back-End/Subscriptions/templatetags/myTemplateTag.py
index 67db926..da1a88e 100644
--- a/Back-End/Subscriptions/templatetags/myTemplateTag.py
+++ b/Back-End/Subscriptions/templatetags/myTemplateTag.py
@@ -1,11 +1,17 @@
from Subscriptions.models import RegistrationForm, Student
+from School.models import PaymentModeType
from django import template
register = template.Library()
@register.filter
-def getStudentPaymentMethod(pk):
+def getRegistrationPaymentMethod(pk):
registerForm = RegistrationForm.objects.get(student=pk)
- return Student.PaymentMethod(int(registerForm.student.payment_method)).label
+ return PaymentModeType(registerForm.registration_payment).label
+
+@register.filter
+def getTuitionPaymentMethod(pk):
+ registerForm = RegistrationForm.objects.get(student=pk)
+ return PaymentModeType(registerForm.tuition_payment).label
@register.filter
def getStudentLevel(pk):
diff --git a/Back-End/Subscriptions/util.py b/Back-End/Subscriptions/util.py
index 21e0a0a..b440d5d 100644
--- a/Back-End/Subscriptions/util.py
+++ b/Back-End/Subscriptions/util.py
@@ -93,42 +93,35 @@ def getArgFromRequest(_argument, _request):
resultat = data[_argument]
return resultat
-def merge_files_pdf(filenames, output_filename):
+from io import BytesIO
+from PyPDF2 import PdfMerger
+
+def merge_files_pdf(file_paths):
"""
- Fusionne plusieurs fichiers PDF en un seul document.
- Vérifie l'existence des fichiers sources avant la fusion.
+ Fusionne plusieurs fichiers PDF et retourne le contenu fusionné en mémoire.
"""
merger = PdfMerger()
- valid_files = []
-
- # Vérifier l'existence des fichiers et ne garder que ceux qui existent
- print(f'filenames : {filenames}')
- for filename in filenames:
- print(f'check exists filename : {filename}')
- if os.path.exists(filename):
- print(f'append filename : {filename}')
- valid_files.append(filename)
-
- if not valid_files:
- raise FileNotFoundError("Aucun fichier valide à fusionner.")
# Ajouter les fichiers valides au merger
- for filename in valid_files:
- merger.append(filename)
+ for file_path in file_paths:
+ merger.append(file_path)
- # S'assurer que le dossier de destination existe
- os.makedirs(os.path.dirname(output_filename), exist_ok=True)
-
- # Sauvegarder le fichier fusionné
- merger.write(output_filename)
+ # Sauvegarder le fichier fusionné en mémoire
+ merged_pdf = BytesIO()
+ merger.write(merged_pdf)
merger.close()
- return output_filename
+ # Revenir au début du fichier en mémoire
+ merged_pdf.seek(0)
+
+ return merged_pdf
def rfToPDF(registerForm, filename):
"""
Génère le PDF d'un dossier d'inscription et l'associe au RegistrationForm.
"""
+ filename = filename.replace(" ", "_")
+
data = {
'pdf_title': f"Dossier d'inscription de {registerForm.student.first_name}",
'signatureDate': convertToStr(_now(), '%d-%m-%Y'),
@@ -136,9 +129,6 @@ def rfToPDF(registerForm, filename):
'student': registerForm.student,
}
- # S'assurer que le dossier parent existe
- os.makedirs(os.path.dirname(filename), exist_ok=True)
-
# Générer le PDF
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
@@ -147,19 +137,14 @@ def rfToPDF(registerForm, filename):
os.remove(registerForm.registration_file.path)
registerForm.registration_file.delete(save=False)
- # Écrire le fichier directement
- with open(filename, 'wb') as f:
- f.write(pdf.content)
+ # Enregistrer directement le fichier dans le champ registration_file
+ registerForm.registration_file.save(
+ os.path.basename(filename),
+ File(BytesIO(pdf.content)), # Utilisation de BytesIO pour éviter l'écriture sur le disque
+ save=True
+ )
- # Mettre à jour le champ registration_file du registerForm
- with open(filename, 'rb') as f:
- registerForm.registration_file.save(
- os.path.basename(filename),
- File(f),
- save=True
- )
-
- return filename
+ return registerForm.registration_file.path
def delete_registration_files(registerForm):
"""
diff --git a/Back-End/Subscriptions/views/register_form_views.py b/Back-End/Subscriptions/views/register_form_views.py
index b9c304e..bb30d4c 100644
--- a/Back-End/Subscriptions/views/register_form_views.py
+++ b/Back-End/Subscriptions/views/register_form_views.py
@@ -155,7 +155,7 @@ class RegisterFormView(APIView):
di = registerForm_serializer.save()
# Mise à jour de l'automate
- updateStateMachine(di, 'creationDI')
+ updateStateMachine(di, 'EVENT_INIT')
# Récupération du reponsable associé
for guardianId in guardiansId:
@@ -231,14 +231,25 @@ class RegisterFormWithIdView(APIView):
Modifie un dossier d'inscription donné.
"""
studentForm_data = JSONParser().parse(request)
+
_status = studentForm_data.pop('status', 0)
studentForm_data["last_update"] = str(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'))
+
+ # Récupérer le dossier d'inscription
registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
+ if not registerForm:
+ return JsonResponse({"error": "Dossier d'inscription introuvable"}, status=status.HTTP_404_NOT_FOUND)
+
+ studentForm_serializer = RegistrationFormSerializer(registerForm, data=studentForm_data, partial=True)
+ if studentForm_serializer.is_valid():
+ studentForm_serializer.save()
+ else:
+ return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
try:
# Génération de la fiche d'inscription au format PDF
- base_dir = f"registration_files/dossier_rf_{registerForm.pk}"
+ base_dir = f"data/registration_files/dossier_rf_{registerForm.pk}"
os.makedirs(base_dir, exist_ok=True)
# Fichier PDF initial
@@ -248,46 +259,32 @@ class RegisterFormWithIdView(APIView):
# Récupération des fichiers d'inscription
fileNames = RegistrationTemplate.get_files_from_rf(registerForm.pk)
-
if registerForm.registration_file:
- fileNames.insert(0, registerForm.registration_file.path)
+ fileNames.insert(0, registerForm.registration_file.path)
# Création du fichier PDF Fusionné
- merged_pdf = f"{base_dir}/dossier_complet_{registerForm.pk}.pdf"
-
- util.merge_files_pdf(fileNames, merged_pdf)
+ merged_pdf_content = util.merge_files_pdf(fileNames)
# Mise à jour du champ registration_file avec le fichier fusionné
- with open(merged_pdf, 'rb') as f:
- registerForm.registration_file.save(
- os.path.basename(merged_pdf),
- File(f),
- save=True
- )
-
+ registerForm.registration_file.save(
+ f"dossier_complet_{registerForm.pk}.pdf",
+ File(merged_pdf_content),
+ save=True
+ )
# Mise à jour de l'automate
- updateStateMachine(registerForm, 'saisiDI')
+ updateStateMachine(registerForm, 'EVENT_SIGNATURE')
except Exception as e:
return JsonResponse({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
- elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED:
- # L'école a validé le dossier d'inscription
- # Mise à jour de l'automate
- updateStateMachine(registerForm, 'valideDI')
- elif _status == RegistrationForm.RegistrationFormStatus.RF_SENT:
- # Vérifier si l'étape précédente était RF_UNDER_REVIEW
- if registerForm.status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
- # Mise à jour de l'automate
- updateStateMachine(registerForm, 'refuseDI')
- # Supprimer le fichier et le dossier associés
+ elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED:
+ updateStateMachine(registerForm, 'EVENT_VALIDATE')
+ elif _status == RegistrationForm.RegistrationFormStatus.RF_SENT:
+ if registerForm.status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
+ updateStateMachine(registerForm, 'EVENT_REFUSE')
util.delete_registration_files(registerForm)
- studentForm_serializer = RegistrationFormSerializer(registerForm, data=studentForm_data)
- if studentForm_serializer.is_valid():
- studentForm_serializer.save()
- return JsonResponse(studentForm_serializer.data, safe=False)
-
- return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
+ # Retourner les données mises à jour
+ return JsonResponse(studentForm_serializer.data, safe=False)
@swagger_auto_schema(
responses={204: 'No Content'},
@@ -334,7 +331,7 @@ def send(request,id):
errorMessage = mailer.sendRegisterForm(email, register_form.establishment.pk)
if errorMessage == '':
register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
- updateStateMachine(register_form, 'envoiDI')
+ updateStateMachine(register_form, 'EVENT_SEND')
return JsonResponse({"message": f"Le dossier d'inscription a bien été envoyé à l'addresse {email}"}, safe=False)
return JsonResponse({"errorMessage":errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST)
return JsonResponse({"errorMessage":'Dossier d\'inscription non trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
@@ -356,7 +353,7 @@ def archive(request,id):
register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
if register_form != None:
register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
- updateStateMachine(register_form, 'archiveDI')
+ updateStateMachine(register_form, 'EVENT_ARCHIVE')
return JsonResponse({"message": "Le dossier a été archivé avec succès"}, safe=False)
return JsonResponse({"errorMessage":'Dossier d\'inscription non trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
diff --git a/Front-End/src/app/[locale]/admin/subscriptions/editInscription/page.js b/Front-End/src/app/[locale]/admin/subscriptions/editInscription/page.js
index 72afd98..4f7ecd7 100644
--- a/Front-End/src/app/[locale]/admin/subscriptions/editInscription/page.js
+++ b/Front-End/src/app/[locale]/admin/subscriptions/editInscription/page.js
@@ -4,27 +4,21 @@ import { useSearchParams, useRouter } from 'next/navigation';
import InscriptionFormShared from '@/components/Inscription/InscriptionFormShared';
import { FE_ADMIN_SUBSCRIPTIONS_URL } from '@/utils/Url';
import { useCsrfToken } from '@/context/CsrfContext';
+import { useEstablishment } from '@/context/EstablishmentContext';
import { editRegisterForm } from '@/app/actions/subscriptionAction';
import logger from '@/utils/logger';
-const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
export default function Page() {
const router = useRouter();
const searchParams = useSearchParams();
- const idProfil = searchParams.get('id');
const studentId = searchParams.get('studentId'); // Changé de codeDI à studentId
- const [initialData, setInitialData] = useState(null);
-
const [formErrors, setFormErrors] = useState({});
const csrfToken = useCsrfToken();
+ const { selectedEstablishmentId } = useEstablishment();
const handleSubmit = (data) => {
- if (useFakeData) {
- logger.debug('Fake submit:', data);
- return;
- }
editRegisterForm(studentId, data, csrfToken)
@@ -46,6 +40,7 @@ export default function Page() {
{
const actions = {
1: [
{
- icon: ,
- onClick: () => sendConfirmRegisterForm(row.student.id, row.student.last_name, row.student.first_name),
+ icon: ,
+ onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
},
{
- icon: ,
- onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
+ icon: ,
+ onClick: () => sendConfirmRegisterForm(row.student.id, row.student.last_name, row.student.first_name),
},
],
2: [
{
- icon: ,
+ icon: ,
onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
},
],
3: [
{
- icon: ,
+ icon: ,
onClick: () => openModalAssociationEleve(row.student),
},
{
- icon: ,
+ icon: ,
onClick: () => refuseRegistrationForm(row.student.id, row.student.last_name, row.student.first_name, row.student.guardians[0].associated_profile_email),
},
],
5: [
{
- icon: ,
+ icon: ,
onClick: () => openModalAssociationEleve(row.student),
},
],
default: [
{
- icon: ,
+ icon: ,
onClick: () => archiveFicheInscription(row.student.id, row.student.last_name, row.student.first_name),
},
],
@@ -703,14 +703,14 @@ const columnsSubscribed = [
items={[
{ label: (
<>
- Rattacher
+ Rattacher
>
),
onClick: () => openModalAssociationEleve(row.student)
},
{ label: (
<>
- Archiver
+ Archiver
>
),
onClick: () => archiveFicheInscription(row.student.id, row.student.last_name, row.student.first_name),
diff --git a/Front-End/src/components/FileStatusLabel.js b/Front-End/src/components/FileStatusLabel.js
index 29330c5..21a3a1a 100644
--- a/Front-End/src/components/FileStatusLabel.js
+++ b/Front-End/src/components/FileStatusLabel.js
@@ -6,7 +6,7 @@ const FileStatusLabel = ({ status }) => {
switch (status) {
case 'sent':
return {
- label: 'Envoyé',
+ label: 'En attente',
className: 'bg-green-50 text-green-600',
icon:
};
diff --git a/Front-End/src/components/Inscription/InscriptionFormShared.js b/Front-End/src/components/Inscription/InscriptionFormShared.js
index 8bbc4c3..7713b16 100644
--- a/Front-End/src/components/Inscription/InscriptionFormShared.js
+++ b/Front-End/src/components/Inscription/InscriptionFormShared.js
@@ -1,20 +1,17 @@
// Import des dépendances nécessaires
import React, { useState, useEffect } from 'react';
-import InputText from '@/components/InputText';
-import SelectChoice from '@/components/SelectChoice';
-import ResponsableInputFields from '@/components/Inscription/ResponsableInputFields';
import Loader from '@/components/Loader';
import Button from '@/components/Button';
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
-import Table from '@/components/Table';
import { fetchRegisterForm, fetchTemplatesFromRegistrationFiles } from '@/app/actions/subscriptionAction';
-import { fetchRegistrationFileFromGroup,
- fetchRegistrationTemplateMaster,
- downloadTemplate,
- createRegistrationTemplates,
- editRegistrationTemplates,
- deleteRegistrationTemplates
- } from '@/app/actions/registerFileGroupAction';
+import { downloadTemplate,
+ createRegistrationTemplates,
+ editRegistrationTemplates,
+ deleteRegistrationTemplates
+} from '@/app/actions/registerFileGroupAction';
+import {
+ fetchRegistrationPaymentModes,
+ fetchTuitionPaymentModes } from '@/app/actions/schoolAction';
import { Download, Upload, Trash2, Eye } from 'lucide-react';
import { BASE_URL } from '@/utils/Url';
import DraggableFileUpload from '@/components/DraggableFileUpload';
@@ -22,10 +19,8 @@ import Modal from '@/components/Modal';
import FileStatusLabel from '@/components/FileStatusLabel';
import logger from '@/utils/logger';
import StudentInfoForm, { validateStudentInfo } from '@/components/Inscription/StudentInfoForm';
-import FilesToSign from '@/components/Inscription/FilesToSign';
import FilesToUpload from '@/components/Inscription/FilesToUpload';
import { DocusealForm } from '@docuseal/react';
-import { ESTABLISHMENT_ID } from '@/utils/Url';
/**
* Composant de formulaire d'inscription partagé
@@ -38,6 +33,7 @@ import { ESTABLISHMENT_ID } from '@/utils/Url';
export default function InscriptionFormShared({
studentId,
csrfToken,
+ selectedEstablishmentId,
onSubmit,
cancelUrl,
errors = {} // Nouvelle prop pour les erreurs
@@ -54,11 +50,16 @@ export default function InscriptionFormShared({
birth_postal_code: '',
nationality: '',
attending_physician: '',
- level: ''
+ level: '',
+ registration_payment: '',
+ tuition_payment: ''
});
const [guardians, setGuardians] = useState([]);
+ const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]);
+ const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]);
+
// États pour la gestion des fichiers
const [uploadedFiles, setUploadedFiles] = useState([]);
const [fileTemplates, setFileTemplates] = useState([]);
@@ -95,7 +96,11 @@ export default function InscriptionFormShared({
birth_postal_code: data?.student?.birth_postal_code || '',
nationality: data?.student?.nationality || '',
attending_physician: data?.student?.attending_physician || '',
- level: data?.student?.level || ''
+ level: data?.student?.level || '',
+ registration_payment: data?.registration_payment || '',
+ tuition_payment: data?.tuition_payment || '',
+ totalRegistrationFees: data?.totalRegistrationFees,
+ totalTuitionFees: data?.totalTuitionFees,
});
setGuardians(data?.student?.guardians || []);
setUploadedFiles(data.registration_files || []);
@@ -111,6 +116,34 @@ export default function InscriptionFormShared({
})
}, []);
+ useEffect(() => {
+ if (selectedEstablishmentId) {
+ // Fetch data for registration payment modes
+ handleRegistrationPaymentModes();
+
+ // Fetch data for tuition payment modes
+ handleTuitionPaymentModes();
+ }
+ }, [selectedEstablishmentId]);
+
+ const handleRegistrationPaymentModes = () => {
+ fetchRegistrationPaymentModes(selectedEstablishmentId)
+ .then(data => {
+ const activePaymentModes = data.filter(mode => mode.is_active === true);
+ setRegistrationPaymentModes(activePaymentModes);
+ })
+ .catch(error => logger.error('Error fetching registration payment modes:', error));
+ };
+
+ const handleTuitionPaymentModes = () => {
+ fetchTuitionPaymentModes(selectedEstablishmentId)
+ .then(data => {
+ const activePaymentModes = data.filter(mode => mode.is_active === true);
+ setTuitionPaymentModes(activePaymentModes);
+ })
+ .catch(error => logger.error('Error fetching tuition payment modes:', error));
+ };
+
// Fonctions de gestion du formulaire et des fichiers
const updateFormField = (field, value) => {
setFormData(prev => ({...prev, [field]: value}));
@@ -186,8 +219,10 @@ export default function InscriptionFormShared({
...formData,
guardians
},
- establishment: 1,
- status:3
+ establishment: selectedEstablishmentId,
+ status:3,
+ tuition_payment:formData.tuition_payment,
+ registration_payment:formData.registration_payment
}
onSubmit(data);
};
@@ -200,16 +235,11 @@ export default function InscriptionFormShared({
...formData,
guardians
},
- establishment: 1
+ establishment: selectedEstablishmentId
}
onSubmit(data);
};
- // Récupération des messages d'erreur
- const getError = (field) => {
- return errors?.student?.[field]?.[0];
- };
-
const handleNextPage = () => {
setCurrentPage(currentPage + 1);
};
@@ -290,6 +320,8 @@ export default function InscriptionFormShared({
updateFormField={updateFormField}
guardians={guardians}
setGuardians={setGuardians}
+ registrationPaymentModes={registrationPaymentModes}
+ tuitionPaymentModes={tuitionPaymentModes}
errors={errors}
/>
)}
diff --git a/Front-End/src/components/Inscription/PaymentMethodSelector.js b/Front-End/src/components/Inscription/PaymentMethodSelector.js
new file mode 100644
index 0000000..4e84e53
--- /dev/null
+++ b/Front-End/src/components/Inscription/PaymentMethodSelector.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import SelectChoice from '@/components/SelectChoice';
+
+export default function PaymentMethodSelector({ formData, title, name, updateFormField, selected, paymentModes, paymentModesOptions, amount, getError }) {
+ console.log(paymentModes)
+ console.log(selected)
+ return (
+
+ {/* Titre */}
+
{title}
+
+ {/* Section d'information */}
+
+
+ Montant : {amount} €
+
+
+
+
updateFormField(name, e.target.value)}
+ choices={paymentModes.map((mode) => ({
+ value: mode.mode,
+ label: paymentModesOptions.find(option => option.id === mode.mode)?.name || 'Mode inconnu'
+ }))}
+ required
+ errorMsg={getError('payment_method')}
+ />
+
+ );
+}
\ No newline at end of file
diff --git a/Front-End/src/components/Inscription/StudentInfoForm.js b/Front-End/src/components/Inscription/StudentInfoForm.js
index a929ca5..8e92f21 100644
--- a/Front-End/src/components/Inscription/StudentInfoForm.js
+++ b/Front-End/src/components/Inscription/StudentInfoForm.js
@@ -2,6 +2,7 @@ import React from 'react';
import InputText from '@/components/InputText';
import SelectChoice from '@/components/SelectChoice';
import ResponsableInputFields from '@/components/Inscription/ResponsableInputFields';
+import PaymentMethodSelector from '@/components/Inscription/PaymentMethodSelector';
const levels = [
{ value:'1', label: 'TPS - Très Petite Section'},
@@ -10,6 +11,13 @@ const levels = [
{ value:'4', label: 'GS - Grande Section'},
];
+const paymentModesOptions = [
+ { id: 1, name: 'Prélèvement SEPA' },
+ { id: 2, name: 'Virement' },
+ { id: 3, name: 'Chèque' },
+ { id: 4, name: 'Espèce' },
+ ];
+
// Fonction de validation pour vérifier les champs requis
export function validateStudentInfo(formData) {
const requiredFields = [
@@ -32,7 +40,7 @@ export function validateStudentInfo(formData) {
return isValid;
}
-export default function StudentInfoForm({ formData, updateFormField, guardians, setGuardians, errors }) {
+export default function StudentInfoForm({ formData, updateFormField, guardians, setGuardians, registrationPaymentModes, tuitionPaymentModes, errors }) {
const getError = (field) => {
return errors?.student?.[field]?.[0];
};
@@ -143,6 +151,30 @@ export default function StudentInfoForm({ formData, updateFormField, guardians,
errors={errors?.student?.guardians || []}
/>
+
+
+
+
>
);
}
\ No newline at end of file
diff --git a/Front-End/src/components/StatusLabel.js b/Front-End/src/components/StatusLabel.js
index 142c0e2..c780089 100644
--- a/Front-End/src/components/StatusLabel.js
+++ b/Front-End/src/components/StatusLabel.js
@@ -5,9 +5,9 @@ import DropdownMenu from './DropdownMenu';
const StatusLabel = ({ status, onChange, showDropdown = true }) => {
const [dropdownOpen, setDropdownOpen] = useState(false);
const statusOptions = [
- { value: 1, label: 'Créé' },
- { value: 2, label: 'Envoyé' },
- { value: 3, label: 'En Validation' },
+ { value: 1, label: 'A envoyer' },
+ { value: 2, label: 'En attente' },
+ { value: 3, label: 'Signé' },
{ value: 4, label: 'A Relancer' },
{ value: 5, label: 'Validé' },
{ value: 6, label: 'Archivé' },
diff --git a/Front-End/src/components/Structure/Tarification/FeesManagement.js b/Front-End/src/components/Structure/Tarification/FeesManagement.js
index 685ec72..dfc2037 100644
--- a/Front-End/src/components/Structure/Tarification/FeesManagement.js
+++ b/Front-End/src/components/Structure/Tarification/FeesManagement.js
@@ -4,7 +4,6 @@ import DiscountsSection from '@/components/Structure/Tarification/DiscountsSecti
import PaymentPlanSelector from '@/components/PaymentPlanSelector';
import PaymentModeSelector from '@/components/PaymentModeSelector';
import { BE_SCHOOL_FEES_URL, BE_SCHOOL_DISCOUNTS_URL, BE_SCHOOL_PAYMENT_PLANS_URL, BE_SCHOOL_PAYMENT_MODES_URL } from '@/utils/Url';
-import { set } from 'lodash';
const FeesManagement = ({ registrationDiscounts,
setRegistrationDiscounts,
diff --git a/Front-End/src/context/EstablishmentContext.js b/Front-End/src/context/EstablishmentContext.js
index 33e3dd8..d2435fb 100644
--- a/Front-End/src/context/EstablishmentContext.js
+++ b/Front-End/src/context/EstablishmentContext.js
@@ -16,7 +16,6 @@ export const EstablishmentProvider = ({ children }) => {
if (session && session.user) {
setUser(session.user);
- console.log("getSession");
const userEstablishments = session.user.roles.map(role => ({
id: role.establishment__id,
name: role.establishment__name,