mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Ajout de la sélection des modes de paiements / refactoring de
l'automate
This commit is contained in:
@ -1,68 +1,79 @@
|
|||||||
{
|
{
|
||||||
"states": [
|
"states": [
|
||||||
"ABSENT",
|
"IDLE",
|
||||||
"CREE",
|
"INITIALIZED",
|
||||||
"ENVOYE",
|
"SENT",
|
||||||
"EN_VALIDATION",
|
"UNDER_REVIEW",
|
||||||
"A_RELANCER",
|
"TO_BE_FOLLOWED_UP",
|
||||||
"VALIDE",
|
"VALIDATED",
|
||||||
"ARCHIVE"
|
"ARCHIVED",
|
||||||
|
"SEPA_SENT"
|
||||||
],
|
],
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{
|
{
|
||||||
"name": "creationDI",
|
"name": "EVENT_INIT",
|
||||||
"from": "ABSENT",
|
"from": "IDLE",
|
||||||
"to": "CREE"
|
"to": "INITIALIZED"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "envoiDI",
|
"name": "EVENT_SEND",
|
||||||
"from": "CREE",
|
"from": "INITIALIZED",
|
||||||
"to": "ENVOYE"
|
"to": "SENT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "archiveDI",
|
"name": "EVENT_ARCHIVE",
|
||||||
"from": "CREE",
|
"from": "INITIALIZED",
|
||||||
"to": "ARCHIVE"
|
"to": "ARCHIVED"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "saisiDI",
|
"name": "EVENT_SIGNATURE",
|
||||||
"from": "ENVOYE",
|
"from": "SENT",
|
||||||
"to": "EN_VALIDATION"
|
"to": "UNDER_REVIEW"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "relanceDI",
|
"name": "EVENT_FOLLOW_UP",
|
||||||
"from": "ENVOYE",
|
"from": "SENT",
|
||||||
"to": "A_RELANCER"
|
"to": "TO_BE_FOLLOWED_UP"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "archiveDI",
|
"name": "EVENT_ARCHIVE",
|
||||||
"from": "A_RELANCER",
|
"from": "SENT",
|
||||||
"to": "ARCHIVE"
|
"to": "ARCHIVED"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "archiveDI",
|
"name": "EVENT_ARCHIVE",
|
||||||
"from": "ENVOYE",
|
"from": "TO_BE_FOLLOWED_UP",
|
||||||
"to": "ARCHIVE"
|
"to": "ARCHIVED"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "refuseDI",
|
"name": "EVENT_REFUSE",
|
||||||
"from": "EN_VALIDATION",
|
"from": "UNDER_REVIEW",
|
||||||
"to": "ENVOYE"
|
"to": "SENT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "valideDI",
|
"name": "EVENT_VALIDATE",
|
||||||
"from": "EN_VALIDATION",
|
"from": "UNDER_REVIEW",
|
||||||
"to": "VALIDE"
|
"to": "VALIDATED"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "archiveDI",
|
"name": "EVENT_SEND_SEPA",
|
||||||
"from": "EN_VALIDATION",
|
"from": "UNDER_REVIEW",
|
||||||
"to": "ARCHIVE"
|
"to": "SEPA_SENT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "archiveDI",
|
"name": "EVENT_ARCHIVE",
|
||||||
"from": "VALIDE",
|
"from": "UNDER_REVIEW",
|
||||||
"to": "ARCHIVE"
|
"to": "ARCHIVED"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "EVENT_SIGNATURE_SEPA",
|
||||||
|
"from": "SEPA_SENT",
|
||||||
|
"to": "UNDER_REVIEW"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "EVENT_ARCHIVE",
|
||||||
|
"from": "VALIDATED",
|
||||||
|
"to": "ARCHIVED"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,13 +3,14 @@ import json
|
|||||||
from Subscriptions.models import RegistrationForm
|
from Subscriptions.models import RegistrationForm
|
||||||
|
|
||||||
state_mapping = {
|
state_mapping = {
|
||||||
"ABSENT": RegistrationForm.RegistrationFormStatus.RF_ABSENT,
|
"IDLE": RegistrationForm.RegistrationFormStatus.RF_IDLE,
|
||||||
"CREE": RegistrationForm.RegistrationFormStatus.RF_CREATED,
|
"INITIALIZED": RegistrationForm.RegistrationFormStatus.RF_INITIALIZED,
|
||||||
"ENVOYE": RegistrationForm.RegistrationFormStatus.RF_SENT,
|
"SENT": RegistrationForm.RegistrationFormStatus.RF_SENT,
|
||||||
"EN_VALIDATION": RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW,
|
"UNDER_REVIEW": RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW,
|
||||||
"A_RELANCER": RegistrationForm.RegistrationFormStatus.RF_TO_BE_FOLLOWED_UP,
|
"TO_BE_FOLLOWED_UP": RegistrationForm.RegistrationFormStatus.RF_TO_BE_FOLLOWED_UP,
|
||||||
"VALIDE": RegistrationForm.RegistrationFormStatus.RF_VALIDATED,
|
"VALIDATED": RegistrationForm.RegistrationFormStatus.RF_VALIDATED,
|
||||||
"ARCHIVE": RegistrationForm.RegistrationFormStatus.RF_ARCHIVED
|
"ARCHIVED": RegistrationForm.RegistrationFormStatus.RF_ARCHIVED,
|
||||||
|
"SEPA_SENT": RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT
|
||||||
}
|
}
|
||||||
|
|
||||||
def load_config(config_file):
|
def load_config(config_file):
|
||||||
|
|||||||
@ -65,7 +65,7 @@ def envoieRelanceDossierInscription(recipients, code):
|
|||||||
return errorMessage
|
return errorMessage
|
||||||
|
|
||||||
def isValid(message, fiche_inscription):
|
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
|
subject = message.subject
|
||||||
print ("++++ " + subject)
|
print ("++++ " + subject)
|
||||||
responsableMail = message.from_header
|
responsableMail = message.from_header
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from django.utils.timezone import now
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import gettext_lazy as _
|
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 Auth.models import ProfileRole
|
||||||
from Establishment.models import Establishment
|
from Establishment.models import Establishment
|
||||||
|
|
||||||
@ -62,11 +62,6 @@ class Student(models.Model):
|
|||||||
MS = 3, _('MS - Moyenne Section')
|
MS = 3, _('MS - Moyenne Section')
|
||||||
GS = 4, _('GS - Grande 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="")
|
last_name = models.CharField(max_length=200, default="")
|
||||||
first_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)
|
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_place = models.CharField(max_length=200, default="", blank=True)
|
||||||
birth_postal_code = models.IntegerField(default=0, blank=True)
|
birth_postal_code = models.IntegerField(default=0, blank=True)
|
||||||
attending_physician = models.CharField(max_length=200, default="", 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
|
# Many-to-Many Relationship
|
||||||
profiles = models.ManyToManyField('Auth.Profile', blank=True)
|
profiles = models.ManyToManyField('Auth.Profile', blank=True)
|
||||||
@ -184,17 +178,18 @@ class RegistrationTemplateMaster(models.Model):
|
|||||||
|
|
||||||
class RegistrationForm(models.Model):
|
class RegistrationForm(models.Model):
|
||||||
class RegistrationFormStatus(models.IntegerChoices):
|
class RegistrationFormStatus(models.IntegerChoices):
|
||||||
RF_ABSENT = 0, _('Pas de dossier d\'inscription')
|
RF_IDLE = 0, _('Pas de dossier d\'inscription')
|
||||||
RF_CREATED = 1, _('Dossier d\'inscription créé')
|
RF_INITIALIZED = 1, _('Dossier d\'inscription initialisé')
|
||||||
RF_SENT = 2, _('Dossier d\'inscription envoyé')
|
RF_SENT = 2, _('Dossier d\'inscription envoyé')
|
||||||
RF_UNDER_REVIEW = 3, _('Dossier d\'inscription en cours de validation')
|
RF_UNDER_REVIEW = 3, _('Dossier d\'inscription en cours de validation')
|
||||||
RF_TO_BE_FOLLOWED_UP = 4, _('Dossier d\'inscription à relancer')
|
RF_TO_BE_FOLLOWED_UP = 4, _('Dossier d\'inscription à relancer')
|
||||||
RF_VALIDATED = 5, _('Dossier d\'inscription validé')
|
RF_VALIDATED = 5, _('Dossier d\'inscription validé')
|
||||||
RF_ARCHIVED = 6, _('Dossier d\'inscription archivé')
|
RF_ARCHIVED = 6, _('Dossier d\'inscription archivé')
|
||||||
|
RF_SEPA_SENT = 7, _('Mandat SEPA envoyé')
|
||||||
|
|
||||||
# One-to-One Relationship
|
# One-to-One Relationship
|
||||||
student = models.OneToOneField(Student, on_delete=models.CASCADE, primary_key=True)
|
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)
|
last_update = models.DateTimeField(auto_now=True)
|
||||||
notes = models.CharField(max_length=200, blank=True)
|
notes = models.CharField(max_length=200, blank=True)
|
||||||
registration_link_code = models.CharField(max_length=200, default="", blank=True)
|
registration_link_code = models.CharField(max_length=200, default="", blank=True)
|
||||||
@ -217,6 +212,8 @@ class RegistrationForm(models.Model):
|
|||||||
blank=True)
|
blank=True)
|
||||||
|
|
||||||
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='register_forms')
|
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):
|
def __str__(self):
|
||||||
return "RF_" + self.student.last_name + "_" + self.student.first_name
|
return "RF_" + self.student.last_name + "_" + self.student.first_name
|
||||||
|
|||||||
@ -221,8 +221,8 @@ class RegistrationFormSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
student_data = validated_data.pop('student', None)
|
student_data = validated_data.pop('student', None)
|
||||||
fees_data = validated_data.pop('fees', [])
|
fees_data = validated_data.pop('fees', None)
|
||||||
discounts_data = validated_data.pop('discounts', [])
|
discounts_data = validated_data.pop('discounts', None)
|
||||||
if student_data:
|
if student_data:
|
||||||
student = instance.student
|
student = instance.student
|
||||||
StudentSerializer.update(StudentSerializer(), student, student_data)
|
StudentSerializer.update(StudentSerializer(), student, student_data)
|
||||||
@ -235,7 +235,10 @@ class RegistrationFormSerializer(serializers.ModelSerializer):
|
|||||||
instance.save()
|
instance.save()
|
||||||
|
|
||||||
# Associer les IDs des objets Fee et Discount au RegistrationForm
|
# Associer les IDs des objets Fee et Discount au RegistrationForm
|
||||||
|
if fees_data is not None:
|
||||||
instance.fees.set([fee.id for fee in fees_data])
|
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])
|
instance.discounts.set([discount.id for discount in discounts_data])
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|||||||
@ -21,10 +21,10 @@ def check_for_signature_deadlines():
|
|||||||
send_notification(dossier)
|
send_notification(dossier)
|
||||||
|
|
||||||
def 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
|
# Changer l'état de l'automate
|
||||||
updateStateMachine(dossier, 'relanceDI')
|
updateStateMachine(dossier, 'EVENT_FOLLOW_UP')
|
||||||
|
|
||||||
url = settings.URL_DJANGO + 'GestionMessagerie/message'
|
url = settings.URL_DJANGO + 'GestionMessagerie/message'
|
||||||
|
|
||||||
|
|||||||
@ -194,8 +194,13 @@
|
|||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2 class="section-title">MODALITÉS DE PAIEMENT</h2>
|
<h2 class="section-title">MODALITÉS DE PAIEMENT</h2>
|
||||||
{% with paymentMethod=student|getStudentPaymentMethod %}
|
<h3 class="subsection-title">Frais d'inscription</h3>
|
||||||
<p>{{ paymentMethod }}</p>
|
{% with registrationPayment=student|getRegistrationPaymentMethod %}
|
||||||
|
<p>{{ registrationPayment }}</p>
|
||||||
|
{% endwith %}
|
||||||
|
<h3 class="subsection-title">Frais de scolarité</h3>
|
||||||
|
{% with tuitionPayment=student|getTuitionPaymentMethod %}
|
||||||
|
<p>{{ tuitionPayment }}</p>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,17 @@
|
|||||||
from Subscriptions.models import RegistrationForm, Student
|
from Subscriptions.models import RegistrationForm, Student
|
||||||
|
from School.models import PaymentModeType
|
||||||
from django import template
|
from django import template
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def getStudentPaymentMethod(pk):
|
def getRegistrationPaymentMethod(pk):
|
||||||
registerForm = RegistrationForm.objects.get(student=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
|
@register.filter
|
||||||
def getStudentLevel(pk):
|
def getStudentLevel(pk):
|
||||||
|
|||||||
@ -93,42 +93,35 @@ def getArgFromRequest(_argument, _request):
|
|||||||
resultat = data[_argument]
|
resultat = data[_argument]
|
||||||
return resultat
|
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.
|
Fusionne plusieurs fichiers PDF et retourne le contenu fusionné en mémoire.
|
||||||
Vérifie l'existence des fichiers sources avant la fusion.
|
|
||||||
"""
|
"""
|
||||||
merger = PdfMerger()
|
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
|
# Ajouter les fichiers valides au merger
|
||||||
for filename in valid_files:
|
for file_path in file_paths:
|
||||||
merger.append(filename)
|
merger.append(file_path)
|
||||||
|
|
||||||
# S'assurer que le dossier de destination existe
|
# Sauvegarder le fichier fusionné en mémoire
|
||||||
os.makedirs(os.path.dirname(output_filename), exist_ok=True)
|
merged_pdf = BytesIO()
|
||||||
|
merger.write(merged_pdf)
|
||||||
# Sauvegarder le fichier fusionné
|
|
||||||
merger.write(output_filename)
|
|
||||||
merger.close()
|
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):
|
def rfToPDF(registerForm, filename):
|
||||||
"""
|
"""
|
||||||
Génère le PDF d'un dossier d'inscription et l'associe au RegistrationForm.
|
Génère le PDF d'un dossier d'inscription et l'associe au RegistrationForm.
|
||||||
"""
|
"""
|
||||||
|
filename = filename.replace(" ", "_")
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'pdf_title': f"Dossier d'inscription de {registerForm.student.first_name}",
|
'pdf_title': f"Dossier d'inscription de {registerForm.student.first_name}",
|
||||||
'signatureDate': convertToStr(_now(), '%d-%m-%Y'),
|
'signatureDate': convertToStr(_now(), '%d-%m-%Y'),
|
||||||
@ -136,9 +129,6 @@ def rfToPDF(registerForm, filename):
|
|||||||
'student': registerForm.student,
|
'student': registerForm.student,
|
||||||
}
|
}
|
||||||
|
|
||||||
# S'assurer que le dossier parent existe
|
|
||||||
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
|
||||||
|
|
||||||
# Générer le PDF
|
# Générer le PDF
|
||||||
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
|
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
|
||||||
|
|
||||||
@ -147,19 +137,14 @@ def rfToPDF(registerForm, filename):
|
|||||||
os.remove(registerForm.registration_file.path)
|
os.remove(registerForm.registration_file.path)
|
||||||
registerForm.registration_file.delete(save=False)
|
registerForm.registration_file.delete(save=False)
|
||||||
|
|
||||||
# Écrire le fichier directement
|
# Enregistrer directement le fichier dans le champ registration_file
|
||||||
with open(filename, 'wb') as f:
|
|
||||||
f.write(pdf.content)
|
|
||||||
|
|
||||||
# Mettre à jour le champ registration_file du registerForm
|
|
||||||
with open(filename, 'rb') as f:
|
|
||||||
registerForm.registration_file.save(
|
registerForm.registration_file.save(
|
||||||
os.path.basename(filename),
|
os.path.basename(filename),
|
||||||
File(f),
|
File(BytesIO(pdf.content)), # Utilisation de BytesIO pour éviter l'écriture sur le disque
|
||||||
save=True
|
save=True
|
||||||
)
|
)
|
||||||
|
|
||||||
return filename
|
return registerForm.registration_file.path
|
||||||
|
|
||||||
def delete_registration_files(registerForm):
|
def delete_registration_files(registerForm):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -155,7 +155,7 @@ class RegisterFormView(APIView):
|
|||||||
di = registerForm_serializer.save()
|
di = registerForm_serializer.save()
|
||||||
|
|
||||||
# Mise à jour de l'automate
|
# Mise à jour de l'automate
|
||||||
updateStateMachine(di, 'creationDI')
|
updateStateMachine(di, 'EVENT_INIT')
|
||||||
|
|
||||||
# Récupération du reponsable associé
|
# Récupération du reponsable associé
|
||||||
for guardianId in guardiansId:
|
for guardianId in guardiansId:
|
||||||
@ -231,14 +231,25 @@ class RegisterFormWithIdView(APIView):
|
|||||||
Modifie un dossier d'inscription donné.
|
Modifie un dossier d'inscription donné.
|
||||||
"""
|
"""
|
||||||
studentForm_data = JSONParser().parse(request)
|
studentForm_data = JSONParser().parse(request)
|
||||||
|
|
||||||
_status = studentForm_data.pop('status', 0)
|
_status = studentForm_data.pop('status', 0)
|
||||||
studentForm_data["last_update"] = str(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'))
|
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)
|
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:
|
if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
|
||||||
try:
|
try:
|
||||||
# Génération de la fiche d'inscription au format PDF
|
# 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)
|
os.makedirs(base_dir, exist_ok=True)
|
||||||
|
|
||||||
# Fichier PDF initial
|
# Fichier PDF initial
|
||||||
@ -248,47 +259,33 @@ class RegisterFormWithIdView(APIView):
|
|||||||
|
|
||||||
# Récupération des fichiers d'inscription
|
# Récupération des fichiers d'inscription
|
||||||
fileNames = RegistrationTemplate.get_files_from_rf(registerForm.pk)
|
fileNames = RegistrationTemplate.get_files_from_rf(registerForm.pk)
|
||||||
|
|
||||||
if registerForm.registration_file:
|
if registerForm.registration_file:
|
||||||
fileNames.insert(0, registerForm.registration_file.path)
|
fileNames.insert(0, registerForm.registration_file.path)
|
||||||
|
|
||||||
# Création du fichier PDF Fusionné
|
# Création du fichier PDF Fusionné
|
||||||
merged_pdf = f"{base_dir}/dossier_complet_{registerForm.pk}.pdf"
|
merged_pdf_content = util.merge_files_pdf(fileNames)
|
||||||
|
|
||||||
util.merge_files_pdf(fileNames, merged_pdf)
|
|
||||||
|
|
||||||
# Mise à jour du champ registration_file avec le fichier fusionné
|
# Mise à jour du champ registration_file avec le fichier fusionné
|
||||||
with open(merged_pdf, 'rb') as f:
|
|
||||||
registerForm.registration_file.save(
|
registerForm.registration_file.save(
|
||||||
os.path.basename(merged_pdf),
|
f"dossier_complet_{registerForm.pk}.pdf",
|
||||||
File(f),
|
File(merged_pdf_content),
|
||||||
save=True
|
save=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mise à jour de l'automate
|
# Mise à jour de l'automate
|
||||||
updateStateMachine(registerForm, 'saisiDI')
|
updateStateMachine(registerForm, 'EVENT_SIGNATURE')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return JsonResponse({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
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)
|
util.delete_registration_files(registerForm)
|
||||||
|
|
||||||
studentForm_serializer = RegistrationFormSerializer(registerForm, data=studentForm_data)
|
# Retourner les données mises à jour
|
||||||
if studentForm_serializer.is_valid():
|
|
||||||
studentForm_serializer.save()
|
|
||||||
return JsonResponse(studentForm_serializer.data, safe=False)
|
return JsonResponse(studentForm_serializer.data, safe=False)
|
||||||
|
|
||||||
return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
@swagger_auto_schema(
|
@swagger_auto_schema(
|
||||||
responses={204: 'No Content'},
|
responses={204: 'No Content'},
|
||||||
operation_description="Supprime un dossier d'inscription donné.",
|
operation_description="Supprime un dossier d'inscription donné.",
|
||||||
@ -334,7 +331,7 @@ def send(request,id):
|
|||||||
errorMessage = mailer.sendRegisterForm(email, register_form.establishment.pk)
|
errorMessage = mailer.sendRegisterForm(email, register_form.establishment.pk)
|
||||||
if errorMessage == '':
|
if errorMessage == '':
|
||||||
register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
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({"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":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)
|
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)
|
register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
|
||||||
if register_form != None:
|
if register_form != None:
|
||||||
register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
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({"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)
|
return JsonResponse({"errorMessage":'Dossier d\'inscription non trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
|||||||
@ -4,27 +4,21 @@ import { useSearchParams, useRouter } from 'next/navigation';
|
|||||||
import InscriptionFormShared from '@/components/Inscription/InscriptionFormShared';
|
import InscriptionFormShared from '@/components/Inscription/InscriptionFormShared';
|
||||||
import { FE_ADMIN_SUBSCRIPTIONS_URL } from '@/utils/Url';
|
import { FE_ADMIN_SUBSCRIPTIONS_URL } from '@/utils/Url';
|
||||||
import { useCsrfToken } from '@/context/CsrfContext';
|
import { useCsrfToken } from '@/context/CsrfContext';
|
||||||
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||||
import { editRegisterForm } from '@/app/actions/subscriptionAction';
|
import { editRegisterForm } from '@/app/actions/subscriptionAction';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const idProfil = searchParams.get('id');
|
|
||||||
const studentId = searchParams.get('studentId'); // Changé de codeDI à studentId
|
const studentId = searchParams.get('studentId'); // Changé de codeDI à studentId
|
||||||
|
|
||||||
const [initialData, setInitialData] = useState(null);
|
|
||||||
|
|
||||||
const [formErrors, setFormErrors] = useState({});
|
const [formErrors, setFormErrors] = useState({});
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
|
const { selectedEstablishmentId } = useEstablishment();
|
||||||
|
|
||||||
|
|
||||||
const handleSubmit = (data) => {
|
const handleSubmit = (data) => {
|
||||||
if (useFakeData) {
|
|
||||||
logger.debug('Fake submit:', data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
editRegisterForm(studentId, data, csrfToken)
|
editRegisterForm(studentId, data, csrfToken)
|
||||||
|
|
||||||
@ -46,6 +40,7 @@ export default function Page() {
|
|||||||
<InscriptionFormShared
|
<InscriptionFormShared
|
||||||
studentId={studentId}
|
studentId={studentId}
|
||||||
csrfToken={csrfToken}
|
csrfToken={csrfToken}
|
||||||
|
selectedEstablishmentId={selectedEstablishmentId}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
cancelUrl={FE_ADMIN_SUBSCRIPTIONS_URL}
|
cancelUrl={FE_ADMIN_SUBSCRIPTIONS_URL}
|
||||||
errors={formErrors}
|
errors={formErrors}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import Loader from '@/components/Loader';
|
|||||||
import AlertWithModal from '@/components/AlertWithModal';
|
import AlertWithModal from '@/components/AlertWithModal';
|
||||||
import DropdownMenu from "@/components/DropdownMenu";
|
import DropdownMenu from "@/components/DropdownMenu";
|
||||||
import { formatPhoneNumber } from '@/utils/Telephone';
|
import { formatPhoneNumber } from '@/utils/Telephone';
|
||||||
import { MoreVertical, Send, Edit, Trash2, FileText, CheckCircle, Plus, TicketX } from 'lucide-react';
|
import { MoreVertical, Send, Edit, Archive, FileText, CircleCheck, Plus, XCircle } from 'lucide-react';
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
import InscriptionForm from '@/components/Inscription/InscriptionForm'
|
import InscriptionForm from '@/components/Inscription/InscriptionForm'
|
||||||
import AffectationClasseForm from '@/components/AffectationClasseForm'
|
import AffectationClasseForm from '@/components/AffectationClasseForm'
|
||||||
@ -68,7 +68,7 @@ export default function Page({ params: { locale } }) {
|
|||||||
const [totalPending, setTotalPending] = useState(0);
|
const [totalPending, setTotalPending] = useState(0);
|
||||||
const [totalSubscribed, setTotalSubscribed] = useState(0);
|
const [totalSubscribed, setTotalSubscribed] = useState(0);
|
||||||
const [totalArchives, setTotalArchives] = useState(0);
|
const [totalArchives, setTotalArchives] = useState(0);
|
||||||
const [itemsPerPage, setItemsPerPage] = useState(5); // Définir le nombre d'éléments par page
|
const [itemsPerPage, setItemsPerPage] = useState(10); // Définir le nombre d'éléments par page
|
||||||
|
|
||||||
const [templateMasters, setTemplateMasters] = useState([]);
|
const [templateMasters, setTemplateMasters] = useState([]);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
@ -577,39 +577,39 @@ useEffect(()=>{
|
|||||||
const actions = {
|
const actions = {
|
||||||
1: [
|
1: [
|
||||||
{
|
{
|
||||||
icon: <Send className="w-5 h-5 text-blue-500 hover:text-blue-700" />,
|
icon: <Edit className="w-5 h-5 text-blue-500 hover:text-blue-700" />,
|
||||||
onClick: () => sendConfirmRegisterForm(row.student.id, row.student.last_name, row.student.first_name),
|
onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Edit className="w-5 h-5 text-green-500 hover:text-green-700" />,
|
icon: <Send className="w-5 h-5 text-green-500 hover:text-green-700" />,
|
||||||
onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
|
onClick: () => sendConfirmRegisterForm(row.student.id, row.student.last_name, row.student.first_name),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
2: [
|
2: [
|
||||||
{
|
{
|
||||||
icon: <Edit className="w-5 h-5 text-green-500 hover:text-green-700" />,
|
icon: <Edit className="w-5 h-5 text-blue-500 hover:text-blue-700" />,
|
||||||
onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
|
onClick: () => window.location.href = `${FE_ADMIN_SUBSCRIPTIONS_EDIT_URL}?studentId=${row.student.id}&id=1`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
3: [
|
3: [
|
||||||
{
|
{
|
||||||
icon: <CheckCircle className="w-5 h-5 text-emerald-500 hover:text-emerald-700" />,
|
icon: <CircleCheck className="w-5 h-5 text-green-500 hover:text-green-700" />,
|
||||||
onClick: () => openModalAssociationEleve(row.student),
|
onClick: () => openModalAssociationEleve(row.student),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <TicketX className="w-5 h-5 text-red-500 hover:text-red-700" />,
|
icon: <XCircle className="w-5 h-5 text-red-500 hover:text-red-700" />,
|
||||||
onClick: () => refuseRegistrationForm(row.student.id, row.student.last_name, row.student.first_name, row.student.guardians[0].associated_profile_email),
|
onClick: () => refuseRegistrationForm(row.student.id, row.student.last_name, row.student.first_name, row.student.guardians[0].associated_profile_email),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
5: [
|
5: [
|
||||||
{
|
{
|
||||||
icon: <CheckCircle className="w-5 h-5 text-emerald-500 hover:text-emerald-700" />,
|
icon: <CircleCheck className="w-5 h-5 text-green-500 hover:text-green-700" />,
|
||||||
onClick: () => openModalAssociationEleve(row.student),
|
onClick: () => openModalAssociationEleve(row.student),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
default: [
|
default: [
|
||||||
{
|
{
|
||||||
icon: <Trash2 className="w-5 h-5 text-red-500 hover:text-red-700" />,
|
icon: <Archive className="w-5 h-5 text-gray-500 hover:text-gray-700" />,
|
||||||
onClick: () => archiveFicheInscription(row.student.id, row.student.last_name, row.student.first_name),
|
onClick: () => archiveFicheInscription(row.student.id, row.student.last_name, row.student.first_name),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -703,14 +703,14 @@ const columnsSubscribed = [
|
|||||||
items={[
|
items={[
|
||||||
{ label: (
|
{ label: (
|
||||||
<>
|
<>
|
||||||
<CheckCircle size={16} className="mr-2" /> Rattacher
|
<CircleCheck size={16} className="mr-2" /> Rattacher
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
onClick: () => openModalAssociationEleve(row.student)
|
onClick: () => openModalAssociationEleve(row.student)
|
||||||
},
|
},
|
||||||
{ label: (
|
{ label: (
|
||||||
<>
|
<>
|
||||||
<Trash2 size={16} className="mr-2 text-red-700" /> Archiver
|
<Archive size={16} className="mr-2 text-red-700" /> Archiver
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
onClick: () => archiveFicheInscription(row.student.id, row.student.last_name, row.student.first_name),
|
onClick: () => archiveFicheInscription(row.student.id, row.student.last_name, row.student.first_name),
|
||||||
|
|||||||
@ -6,7 +6,7 @@ const FileStatusLabel = ({ status }) => {
|
|||||||
switch (status) {
|
switch (status) {
|
||||||
case 'sent':
|
case 'sent':
|
||||||
return {
|
return {
|
||||||
label: 'Envoyé',
|
label: 'En attente',
|
||||||
className: 'bg-green-50 text-green-600',
|
className: 'bg-green-50 text-green-600',
|
||||||
icon: <Check size={16} className="text-green-600" />
|
icon: <Check size={16} className="text-green-600" />
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,20 +1,17 @@
|
|||||||
// Import des dépendances nécessaires
|
// Import des dépendances nécessaires
|
||||||
import React, { useState, useEffect } from 'react';
|
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 Loader from '@/components/Loader';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
||||||
import Table from '@/components/Table';
|
|
||||||
import { fetchRegisterForm, fetchTemplatesFromRegistrationFiles } from '@/app/actions/subscriptionAction';
|
import { fetchRegisterForm, fetchTemplatesFromRegistrationFiles } from '@/app/actions/subscriptionAction';
|
||||||
import { fetchRegistrationFileFromGroup,
|
import { downloadTemplate,
|
||||||
fetchRegistrationTemplateMaster,
|
|
||||||
downloadTemplate,
|
|
||||||
createRegistrationTemplates,
|
createRegistrationTemplates,
|
||||||
editRegistrationTemplates,
|
editRegistrationTemplates,
|
||||||
deleteRegistrationTemplates
|
deleteRegistrationTemplates
|
||||||
} from '@/app/actions/registerFileGroupAction';
|
} from '@/app/actions/registerFileGroupAction';
|
||||||
|
import {
|
||||||
|
fetchRegistrationPaymentModes,
|
||||||
|
fetchTuitionPaymentModes } from '@/app/actions/schoolAction';
|
||||||
import { Download, Upload, Trash2, Eye } from 'lucide-react';
|
import { Download, Upload, Trash2, Eye } from 'lucide-react';
|
||||||
import { BASE_URL } from '@/utils/Url';
|
import { BASE_URL } from '@/utils/Url';
|
||||||
import DraggableFileUpload from '@/components/DraggableFileUpload';
|
import DraggableFileUpload from '@/components/DraggableFileUpload';
|
||||||
@ -22,10 +19,8 @@ import Modal from '@/components/Modal';
|
|||||||
import FileStatusLabel from '@/components/FileStatusLabel';
|
import FileStatusLabel from '@/components/FileStatusLabel';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
import StudentInfoForm, { validateStudentInfo } from '@/components/Inscription/StudentInfoForm';
|
import StudentInfoForm, { validateStudentInfo } from '@/components/Inscription/StudentInfoForm';
|
||||||
import FilesToSign from '@/components/Inscription/FilesToSign';
|
|
||||||
import FilesToUpload from '@/components/Inscription/FilesToUpload';
|
import FilesToUpload from '@/components/Inscription/FilesToUpload';
|
||||||
import { DocusealForm } from '@docuseal/react';
|
import { DocusealForm } from '@docuseal/react';
|
||||||
import { ESTABLISHMENT_ID } from '@/utils/Url';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Composant de formulaire d'inscription partagé
|
* Composant de formulaire d'inscription partagé
|
||||||
@ -38,6 +33,7 @@ import { ESTABLISHMENT_ID } from '@/utils/Url';
|
|||||||
export default function InscriptionFormShared({
|
export default function InscriptionFormShared({
|
||||||
studentId,
|
studentId,
|
||||||
csrfToken,
|
csrfToken,
|
||||||
|
selectedEstablishmentId,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
cancelUrl,
|
cancelUrl,
|
||||||
errors = {} // Nouvelle prop pour les erreurs
|
errors = {} // Nouvelle prop pour les erreurs
|
||||||
@ -54,11 +50,16 @@ export default function InscriptionFormShared({
|
|||||||
birth_postal_code: '',
|
birth_postal_code: '',
|
||||||
nationality: '',
|
nationality: '',
|
||||||
attending_physician: '',
|
attending_physician: '',
|
||||||
level: ''
|
level: '',
|
||||||
|
registration_payment: '',
|
||||||
|
tuition_payment: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const [guardians, setGuardians] = useState([]);
|
const [guardians, setGuardians] = useState([]);
|
||||||
|
|
||||||
|
const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]);
|
||||||
|
const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]);
|
||||||
|
|
||||||
// États pour la gestion des fichiers
|
// États pour la gestion des fichiers
|
||||||
const [uploadedFiles, setUploadedFiles] = useState([]);
|
const [uploadedFiles, setUploadedFiles] = useState([]);
|
||||||
const [fileTemplates, setFileTemplates] = useState([]);
|
const [fileTemplates, setFileTemplates] = useState([]);
|
||||||
@ -95,7 +96,11 @@ export default function InscriptionFormShared({
|
|||||||
birth_postal_code: data?.student?.birth_postal_code || '',
|
birth_postal_code: data?.student?.birth_postal_code || '',
|
||||||
nationality: data?.student?.nationality || '',
|
nationality: data?.student?.nationality || '',
|
||||||
attending_physician: data?.student?.attending_physician || '',
|
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 || []);
|
setGuardians(data?.student?.guardians || []);
|
||||||
setUploadedFiles(data.registration_files || []);
|
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
|
// Fonctions de gestion du formulaire et des fichiers
|
||||||
const updateFormField = (field, value) => {
|
const updateFormField = (field, value) => {
|
||||||
setFormData(prev => ({...prev, [field]: value}));
|
setFormData(prev => ({...prev, [field]: value}));
|
||||||
@ -186,8 +219,10 @@ export default function InscriptionFormShared({
|
|||||||
...formData,
|
...formData,
|
||||||
guardians
|
guardians
|
||||||
},
|
},
|
||||||
establishment: 1,
|
establishment: selectedEstablishmentId,
|
||||||
status:3
|
status:3,
|
||||||
|
tuition_payment:formData.tuition_payment,
|
||||||
|
registration_payment:formData.registration_payment
|
||||||
}
|
}
|
||||||
onSubmit(data);
|
onSubmit(data);
|
||||||
};
|
};
|
||||||
@ -200,16 +235,11 @@ export default function InscriptionFormShared({
|
|||||||
...formData,
|
...formData,
|
||||||
guardians
|
guardians
|
||||||
},
|
},
|
||||||
establishment: 1
|
establishment: selectedEstablishmentId
|
||||||
}
|
}
|
||||||
onSubmit(data);
|
onSubmit(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Récupération des messages d'erreur
|
|
||||||
const getError = (field) => {
|
|
||||||
return errors?.student?.[field]?.[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNextPage = () => {
|
const handleNextPage = () => {
|
||||||
setCurrentPage(currentPage + 1);
|
setCurrentPage(currentPage + 1);
|
||||||
};
|
};
|
||||||
@ -290,6 +320,8 @@ export default function InscriptionFormShared({
|
|||||||
updateFormField={updateFormField}
|
updateFormField={updateFormField}
|
||||||
guardians={guardians}
|
guardians={guardians}
|
||||||
setGuardians={setGuardians}
|
setGuardians={setGuardians}
|
||||||
|
registrationPaymentModes={registrationPaymentModes}
|
||||||
|
tuitionPaymentModes={tuitionPaymentModes}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -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 (
|
||||||
|
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||||
|
{/* Titre */}
|
||||||
|
<h2 className="text-2xl font-semibold mb-6 text-gray-800 border-b pb-2">{title}</h2>
|
||||||
|
|
||||||
|
{/* Section d'information */}
|
||||||
|
<div className="mb-6 bg-gray-50 p-4 rounded-lg border border-gray-100">
|
||||||
|
<p className="text-gray-700 text-sm mb-2">
|
||||||
|
<strong className="text-gray-900">Montant :</strong> {amount} €
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SelectChoice
|
||||||
|
name={name}
|
||||||
|
label="Mode de Paiement"
|
||||||
|
placeHolder="Sélectionner un mode de paiement"
|
||||||
|
selected={selected || ''}
|
||||||
|
callback={(e) => 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')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import InputText from '@/components/InputText';
|
import InputText from '@/components/InputText';
|
||||||
import SelectChoice from '@/components/SelectChoice';
|
import SelectChoice from '@/components/SelectChoice';
|
||||||
import ResponsableInputFields from '@/components/Inscription/ResponsableInputFields';
|
import ResponsableInputFields from '@/components/Inscription/ResponsableInputFields';
|
||||||
|
import PaymentMethodSelector from '@/components/Inscription/PaymentMethodSelector';
|
||||||
|
|
||||||
const levels = [
|
const levels = [
|
||||||
{ value:'1', label: 'TPS - Très Petite Section'},
|
{ value:'1', label: 'TPS - Très Petite Section'},
|
||||||
@ -10,6 +11,13 @@ const levels = [
|
|||||||
{ value:'4', label: 'GS - Grande Section'},
|
{ 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
|
// Fonction de validation pour vérifier les champs requis
|
||||||
export function validateStudentInfo(formData) {
|
export function validateStudentInfo(formData) {
|
||||||
const requiredFields = [
|
const requiredFields = [
|
||||||
@ -32,7 +40,7 @@ export function validateStudentInfo(formData) {
|
|||||||
return isValid;
|
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) => {
|
const getError = (field) => {
|
||||||
return errors?.student?.[field]?.[0];
|
return errors?.student?.[field]?.[0];
|
||||||
};
|
};
|
||||||
@ -143,6 +151,30 @@ export default function StudentInfoForm({ formData, updateFormField, guardians,
|
|||||||
errors={errors?.student?.guardians || []}
|
errors={errors?.student?.guardians || []}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<PaymentMethodSelector
|
||||||
|
formData={formData}
|
||||||
|
title="Frais d'inscription"
|
||||||
|
name="registration_payment"
|
||||||
|
updateFormField={updateFormField}
|
||||||
|
selected={formData.registration_payment}
|
||||||
|
paymentModes={registrationPaymentModes}
|
||||||
|
paymentModesOptions={paymentModesOptions}
|
||||||
|
amount={formData.totalRegistrationFees}
|
||||||
|
getError={getError}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PaymentMethodSelector
|
||||||
|
formData={formData}
|
||||||
|
title="Frais de scolarité"
|
||||||
|
name="tuition_payment"
|
||||||
|
updateFormField={updateFormField}
|
||||||
|
selected={formData.tuition_payment}
|
||||||
|
paymentModes={tuitionPaymentModes}
|
||||||
|
paymentModesOptions={paymentModesOptions}
|
||||||
|
amount={formData.totalTuitionFees}
|
||||||
|
getError={getError}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -5,9 +5,9 @@ import DropdownMenu from './DropdownMenu';
|
|||||||
const StatusLabel = ({ status, onChange, showDropdown = true }) => {
|
const StatusLabel = ({ status, onChange, showDropdown = true }) => {
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
{ value: 1, label: 'Créé' },
|
{ value: 1, label: 'A envoyer' },
|
||||||
{ value: 2, label: 'Envoyé' },
|
{ value: 2, label: 'En attente' },
|
||||||
{ value: 3, label: 'En Validation' },
|
{ value: 3, label: 'Signé' },
|
||||||
{ value: 4, label: 'A Relancer' },
|
{ value: 4, label: 'A Relancer' },
|
||||||
{ value: 5, label: 'Validé' },
|
{ value: 5, label: 'Validé' },
|
||||||
{ value: 6, label: 'Archivé' },
|
{ value: 6, label: 'Archivé' },
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import DiscountsSection from '@/components/Structure/Tarification/DiscountsSecti
|
|||||||
import PaymentPlanSelector from '@/components/PaymentPlanSelector';
|
import PaymentPlanSelector from '@/components/PaymentPlanSelector';
|
||||||
import PaymentModeSelector from '@/components/PaymentModeSelector';
|
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 { 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,
|
const FeesManagement = ({ registrationDiscounts,
|
||||||
setRegistrationDiscounts,
|
setRegistrationDiscounts,
|
||||||
|
|||||||
@ -16,7 +16,6 @@ export const EstablishmentProvider = ({ children }) => {
|
|||||||
|
|
||||||
if (session && session.user) {
|
if (session && session.user) {
|
||||||
setUser(session.user);
|
setUser(session.user);
|
||||||
console.log("getSession");
|
|
||||||
const userEstablishments = session.user.roles.map(role => ({
|
const userEstablishments = session.user.roles.map(role => ({
|
||||||
id: role.establishment__id,
|
id: role.establishment__id,
|
||||||
name: role.establishment__name,
|
name: role.establishment__name,
|
||||||
|
|||||||
Reference in New Issue
Block a user