diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py
index c3de2b3..a9d51d2 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, PaymentModeType
+from School.models import SchoolClass, Fee, Discount, PaymentModeType, PaymentPlanType
from Auth.models import ProfileRole
from Establishment.models import Establishment
@@ -229,6 +229,8 @@ class RegistrationForm(models.Model):
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)
+ registration_payment_plan = models.IntegerField(choices=PaymentPlanType.choices, null=True, blank=True)
+ tuition_payment_plan = models.IntegerField(choices=PaymentPlanType.choices, null=True, blank=True)
def __str__(self):
return "RF_" + self.student.last_name + "_" + self.student.first_name
diff --git a/Back-End/Subscriptions/views/register_form_views.py b/Back-End/Subscriptions/views/register_form_views.py
index 83c5bd9..ae61ae0 100644
--- a/Back-End/Subscriptions/views/register_form_views.py
+++ b/Back-End/Subscriptions/views/register_form_views.py
@@ -231,6 +231,7 @@ class RegisterFormWithIdView(APIView):
"""
studentForm_data = request.data.get('data', '{}')
+ print(f'studentForm_data : {studentForm_data}')
try:
data = json.loads(studentForm_data)
except json.JSONDecodeError:
@@ -239,10 +240,16 @@ class RegisterFormWithIdView(APIView):
# Extraire le fichier photo
photo_file = request.FILES.get('photo')
+ # Extraire le fichier photo
+ sepa_file = request.FILES.get('sepa_file')
+
# Ajouter la photo aux données de l'étudiant
if photo_file:
data['student']['photo'] = photo_file
+ if sepa_file:
+ data['sepa_file'] = sepa_file
+
# Gérer le champ `_status`
_status = data.pop('status', 0)
_status = int(_status)
@@ -307,7 +314,7 @@ class RegisterFormWithIdView(APIView):
elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED:
# Vérifier si le paramètre fusion est activé via l'URL
- fusion = studentForm_data.get('fusion', False)
+ fusion = data.get('fusion', False)
if fusion:
# Fusion des documents
# Récupération des fichiers schoolFileTemplates
diff --git a/Front-End/src/app/[locale]/admin/subscriptions/page.js b/Front-End/src/app/[locale]/admin/subscriptions/page.js
index 903a497..606f684 100644
--- a/Front-End/src/app/[locale]/admin/subscriptions/page.js
+++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js
@@ -375,8 +375,15 @@ export default function Page({ params: { locale } }) {
return;
}
+ // Préparer les données JSON
+ const jsonData = {
+ status: 7,
+ };
+
const formData = new FormData();
- formData.append('status', 7);
+
+ // Ajouter les données JSON sous forme de chaîne
+ formData.append('data', JSON.stringify(jsonData));
formData.append('sepa_file', file);
// Appeler l'API pour uploader le fichier SEPA
@@ -868,7 +875,7 @@ export default function Page({ params: { locale } }) {
) : (
diff --git a/Front-End/src/app/[locale]/admin/subscriptions/validateSubscription/page.js b/Front-End/src/app/[locale]/admin/subscriptions/validateSubscription/page.js
index b9e14a6..e58b4d6 100644
--- a/Front-End/src/app/[locale]/admin/subscriptions/validateSubscription/page.js
+++ b/Front-End/src/app/[locale]/admin/subscriptions/validateSubscription/page.js
@@ -23,10 +23,8 @@ export default function Page() {
const csrfToken = useCsrfToken();
const handleAcceptRF = (data) => {
- const { status, fusionParam } = data;
const formData = new FormData();
- formData.append('status', status); // Ajoute le statut
- formData.append('fusion', fusionParam);
+ formData.append('data', JSON.stringify(data));
setIsLoading(true);
// Appeler l'API pour mettre à jour le RF
diff --git a/Front-End/src/app/[locale]/parents/editInscription/page.js b/Front-End/src/app/[locale]/parents/editInscription/page.js
index 80dca19..1a90772 100644
--- a/Front-End/src/app/[locale]/parents/editInscription/page.js
+++ b/Front-End/src/app/[locale]/parents/editInscription/page.js
@@ -5,7 +5,7 @@ import { useSearchParams, useRouter } from 'next/navigation';
import { useCsrfToken } from '@/context/CsrfContext';
import { useEstablishment } from '@/context/EstablishmentContext';
import { FE_PARENTS_HOME_URL } from '@/utils/Url';
-import { editRegisterForm } from '@/app/actions/subscriptionAction';
+import { editRegisterFormWithBinaryFile } from '@/app/actions/subscriptionAction';
import logger from '@/utils/logger';
export default function Page() {
@@ -18,7 +18,11 @@ export default function Page() {
const handleSubmit = async (data) => {
try {
- const result = await editRegisterForm(studentId, data, csrfToken);
+ const result = await editRegisterFormWithBinaryFile(
+ studentId,
+ data,
+ csrfToken
+ );
logger.debug('Success:', result);
router.push(FE_PARENTS_HOME_URL);
} catch (error) {
diff --git a/Front-End/src/app/[locale]/parents/page.js b/Front-End/src/app/[locale]/parents/page.js
index 86841a2..363edf1 100644
--- a/Front-End/src/app/[locale]/parents/page.js
+++ b/Front-End/src/app/[locale]/parents/page.js
@@ -66,9 +66,12 @@ export default function ParentHomePage() {
return;
}
+ const jsonData = {
+ status: 3,
+ };
const formData = new FormData();
+ formData.append('data', JSON.stringify(jsonData));
formData.append('sepa_file', uploadedFile); // Ajoute le fichier SEPA
- formData.append('status', 3); // Statut à envoyer
editRegisterFormWithBinaryFile(uploadingStudentId, formData, csrfToken)
.then((response) => {
diff --git a/Front-End/src/components/FileUpload.js b/Front-End/src/components/FileUpload.js
index 5cc49bb..a461a9b 100644
--- a/Front-End/src/components/FileUpload.js
+++ b/Front-End/src/components/FileUpload.js
@@ -6,6 +6,9 @@ export default function FileUpload({
selectionMessage,
onFileSelect,
uploadedFileName,
+ existingFile,
+ required,
+ errorMsg,
}) {
const [localFileName, setLocalFileName] = useState(uploadedFileName || '');
const fileInputRef = useRef(null); // Utilisation de useRef pour cibler l'input
@@ -31,7 +34,10 @@ export default function FileUpload({
return (
-
{`${selectionMessage}`}
+
+ {`${selectionMessage}`}
+ {required && *}
+
fileInputRef.current.click()} // Utilisation de la référence pour ouvrir l'explorateur
@@ -52,10 +58,24 @@ export default function FileUpload({
Déposez votre fichier ici
- ou cliquez pour sélectionner un fichier PDF
+ ou cliquez pour sélectionner un fichier
+
+ {/* Affichage du fichier existant */}
+ {existingFile && !localFileName && (
+
+
+
+
+ {existingFile.split('/').pop()}
+
+
+
+ )}
+
+ {/* Affichage du fichier sélectionné */}
{localFileName && (
@@ -64,6 +84,9 @@ export default function FileUpload({
)}
+
+ {/* Message d'erreur */}
+ {errorMsg &&
{errorMsg}
}
);
}
diff --git a/Front-End/src/components/Inscription/InscriptionFormShared.js b/Front-End/src/components/Inscription/InscriptionFormShared.js
index afacb30..914b626 100644
--- a/Front-End/src/components/Inscription/InscriptionFormShared.js
+++ b/Front-End/src/components/Inscription/InscriptionFormShared.js
@@ -14,6 +14,8 @@ import {
import {
fetchRegistrationPaymentModes,
fetchTuitionPaymentModes,
+ fetchRegistrationPaymentPlans,
+ fetchTuitionPaymentPlans,
} from '@/app/actions/schoolAction';
import { BASE_URL } from '@/utils/Url';
import logger from '@/utils/logger';
@@ -53,13 +55,14 @@ export default function InscriptionFormShared({
nationality: '',
attending_physician: '',
level: '',
- registration_payment: '',
- tuition_payment: '',
+ photo: '',
});
const [guardians, setGuardians] = useState([]);
const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]);
const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]);
+ const [registrationPaymentPlans, setRegistrationPaymentPlans] = useState([]);
+ const [tuitionPaymentPlans, setTuitionPaymentPlans] = useState([]);
// États pour la gestion des fichiers
const [uploadedFiles, setUploadedFiles] = useState([]);
@@ -194,6 +197,12 @@ export default function InscriptionFormShared({
// Fetch data for tuition payment modes
handleTuitionPaymentModes();
+
+ // Fetch data for registration payment plans
+ handleRegistrationPaymentPlans();
+
+ // Fetch data for tuition payment plans
+ handleTuitionnPaymentPlans();
}
}, [selectedEstablishmentId]);
@@ -223,6 +232,32 @@ export default function InscriptionFormShared({
);
};
+ const handleRegistrationPaymentPlans = () => {
+ fetchRegistrationPaymentPlans(selectedEstablishmentId)
+ .then((data) => {
+ const activePaymentPlans = data.filter(
+ (mode) => mode.is_active === true
+ );
+ setRegistrationPaymentPlans(activePaymentPlans);
+ })
+ .catch((error) =>
+ logger.error('Error fetching registration payment plans:', error)
+ );
+ };
+
+ const handleTuitionnPaymentPlans = () => {
+ fetchTuitionPaymentPlans(selectedEstablishmentId)
+ .then((data) => {
+ const activePaymentPlans = data.filter(
+ (mode) => mode.is_active === true
+ );
+ setTuitionPaymentPlans(activePaymentPlans);
+ })
+ .catch((error) =>
+ logger.error('Error fetching registration tuition plans:', error)
+ );
+ };
+
const handleFileUpload = (file, selectedFile) => {
if (!file || !selectedFile) {
logger.error('Données manquantes pour le téléversement.');
@@ -344,8 +379,12 @@ export default function InscriptionFormShared({
status: isSepaPayment ? 8 : 3,
tuition_payment: formData.tuition_payment,
registration_payment: formData.registration_payment,
+ tuition_payment_plan: formData.tuition_payment_plan,
+ registration_payment_plan: formData.registration_payment_plan,
};
+ console.log('jsonData : ', jsonData);
+
// Créer un objet FormData
const formDataToSend = new FormData();
@@ -450,6 +489,8 @@ export default function InscriptionFormShared({
setFormData={setFormData}
registrationPaymentModes={registrationPaymentModes}
tuitionPaymentModes={tuitionPaymentModes}
+ registrationPaymentPlans={registrationPaymentPlans}
+ tuitionPaymentPlans={tuitionPaymentPlans}
errors={errors}
setIsPageValid={setIsPage3Valid}
/>
diff --git a/Front-End/src/components/Inscription/PaymentMethodSelector.js b/Front-End/src/components/Inscription/PaymentMethodSelector.js
index 719adfe..796465d 100644
--- a/Front-End/src/components/Inscription/PaymentMethodSelector.js
+++ b/Front-End/src/components/Inscription/PaymentMethodSelector.js
@@ -1,11 +1,14 @@
import React, { useEffect } from 'react';
import SelectChoice from '@/components/SelectChoice';
+import RadioList from '@/components/RadioList';
export default function PaymentMethodSelector({
formData,
setFormData,
registrationPaymentModes,
tuitionPaymentModes,
+ registrationPaymentPlans,
+ tuitionPaymentPlans,
errors,
setIsPageValid,
}) {
@@ -14,6 +17,7 @@ export default function PaymentMethodSelector({
(field) => getLocalError(field) !== ''
);
setIsPageValid(isValid);
+ console.log('formdata : ', formData);
}, [formData, setIsPageValid]);
const paymentModesOptions = [
@@ -23,19 +27,31 @@ export default function PaymentMethodSelector({
{ id: 4, name: 'Espèce' },
];
+ const paymentPlansOptions = [
+ { id: 1, name: '1 fois' },
+ { id: 3, name: '3 fois' },
+ { id: 10, name: '10 fois' },
+ { id: 12, name: '12 fois' },
+ ];
+
const getError = (field) => {
return errors?.student?.[field]?.[0];
};
const getLocalError = (field) => {
if (
- // Student Form
(field === 'registration_payment' &&
(!formData.registration_payment ||
String(formData.registration_payment).trim() === '')) ||
(field === 'tuition_payment' &&
(!formData.tuition_payment ||
- String(formData.tuition_payment).trim() === ''))
+ String(formData.tuition_payment).trim() === '')) ||
+ (field === 'registration_payment_plan' &&
+ (!formData.registration_payment_plan ||
+ String(formData.registration_payment_plan).trim() === '')) ||
+ (field === 'tuition_payment_plan' &&
+ (!formData.tuition_payment_plan ||
+ String(formData.tuition_payment_plan).trim() === ''))
) {
return 'Champs requis';
}
@@ -48,13 +64,12 @@ export default function PaymentMethodSelector({
return (
<>
+ {/* Frais d'inscription */}
- {/* Titre */}
Frais d'inscription
- {/* Section d'information */}
Montant :{' '}
@@ -80,15 +95,42 @@ export default function PaymentMethodSelector({
getLocalError('registration_payment')
}
/>
+
+
+ registrationPaymentPlans.some(
+ (plan) => plan.frequency === option.id
+ )
+ )
+ .map((option) => ({
+ id: option.id,
+ label: option.name,
+ }))}
+ formData={{
+ ...formData,
+ registration_payment_plan: parseInt(
+ formData.registration_payment_plan,
+ 10
+ ), // S'assurer que la valeur est un entier
+ }}
+ handleChange={(e) => {
+ const value = parseInt(e.target.value, 10);
+ onChange('registration_payment_plan', value); // Convertir la valeur en entier
+ }}
+ fieldName="registration_payment_plan"
+ className="mt-4"
+ />
+ {/* Frais de scolarité */}
- {/* Titre */}
Frais de scolarité
- {/* Section d'information */}
Montant :{' '}
@@ -113,6 +155,26 @@ export default function PaymentMethodSelector({
getError('tuition_payment') || getLocalError('tuition_payment')
}
/>
+
+
+ tuitionPaymentPlans.some((plan) => plan.frequency === option.id)
+ )
+ .map((option) => ({
+ id: option.id,
+ label: option.name,
+ }))}
+ formData={formData}
+ handleChange={(e) => onChange('tuition_payment_plan', e.target.value)}
+ fieldName="tuition_payment_plan"
+ className="mt-4"
+ errorMsg={
+ getError('tuition_payment_plan') ||
+ getLocalError('tuition_payment_plan')
+ }
+ />
>
);
diff --git a/Front-End/src/components/Inscription/StudentInfoForm.js b/Front-End/src/components/Inscription/StudentInfoForm.js
index cc78cdc..8f5694e 100644
--- a/Front-End/src/components/Inscription/StudentInfoForm.js
+++ b/Front-End/src/components/Inscription/StudentInfoForm.js
@@ -51,6 +51,8 @@ export default function StudentInfoForm({
level: data?.student?.level || '',
registration_payment: data?.registration_payment || '',
tuition_payment: data?.tuition_payment || '',
+ registration_payment_plan: data?.registration_payment_plan || '',
+ tuition_payment_plan: data?.tuition_payment_plan || '',
totalRegistrationFees: data?.totalRegistrationFees,
totalTuitionFees: data?.totalTuitionFees,
});
@@ -96,7 +98,8 @@ export default function StudentInfoForm({
(!formData.attending_physician ||
formData.attending_physician.trim() === '')) ||
(field === 'level' &&
- (!formData.level || String(formData.level).trim() === ''))
+ (!formData.level || String(formData.level).trim() === '')) ||
+ (field === 'photo' && !formData.photo)
) {
return 'Champs requis';
}
@@ -230,6 +233,9 @@ export default function StudentInfoForm({
handlePhotoUpload(file)}
+ existingFile={formData.photo}
+ required
+ errorMsg={getError('photo') || getLocalError('photo')}
/>
>
diff --git a/Front-End/src/components/RadioList.js b/Front-End/src/components/RadioList.js
index fb8b72f..90df256 100644
--- a/Front-End/src/components/RadioList.js
+++ b/Front-End/src/components/RadioList.js
@@ -7,9 +7,17 @@ const RadioList = ({
fieldName,
icon: Icon,
className,
+ sectionLabel,
+ required,
}) => {
return (
+ {sectionLabel && (
+
+ {sectionLabel}
+ {required && *}
+
+ )}