diff --git a/Back-End/Subscriptions/Configuration/automate.json b/Back-End/Subscriptions/Configuration/automate.json
index d14852d..1dc13ad 100644
--- a/Back-End/Subscriptions/Configuration/automate.json
+++ b/Back-End/Subscriptions/Configuration/automate.json
@@ -30,6 +30,16 @@
"from": "SENT",
"to": "UNDER_REVIEW"
},
+ {
+ "name": "EVENT_WAITING_FOR_SEPA",
+ "from": "SENT",
+ "to": "SEPA_TO_SEND"
+ },
+ {
+ "name": "EVENT_SEND_SEPA",
+ "from": "SEPA_TO_SEND",
+ "to": "SEPA_SENT"
+ },
{
"name": "EVENT_FOLLOW_UP",
"from": "SENT",
@@ -55,11 +65,6 @@
"from": "UNDER_REVIEW",
"to": "VALIDATED"
},
- {
- "name": "EVENT_SEND_SEPA",
- "from": "UNDER_REVIEW",
- "to": "SEPA_SENT"
- },
{
"name": "EVENT_ARCHIVE",
"from": "UNDER_REVIEW",
diff --git a/Back-End/Subscriptions/automate.py b/Back-End/Subscriptions/automate.py
index 91b8fa2..1c28ad9 100644
--- a/Back-End/Subscriptions/automate.py
+++ b/Back-End/Subscriptions/automate.py
@@ -10,7 +10,8 @@ state_mapping = {
"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
+ "SEPA_SENT": RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT,
+ "SEPA_TO_SEND": RegistrationForm.RegistrationFormStatus.RF_SEPA_TO_SEND
}
def load_config(config_file):
diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py
index a0dd4af..e119336 100644
--- a/Back-End/Subscriptions/models.py
+++ b/Back-End/Subscriptions/models.py
@@ -182,6 +182,7 @@ class RegistrationForm(models.Model):
RF_VALIDATED = 5, _('Dossier d\'inscription validé')
RF_ARCHIVED = 6, _('Dossier d\'inscription archivé')
RF_SEPA_SENT = 7, _('Mandat SEPA envoyé')
+ RF_SEPA_TO_SEND = 8, _('Mandat SEPA à envoyer')
# One-to-One Relationship
student = models.OneToOneField(Student, on_delete=models.CASCADE, primary_key=True)
diff --git a/Back-End/Subscriptions/views/register_form_views.py b/Back-End/Subscriptions/views/register_form_views.py
index ab1da49..50d7d74 100644
--- a/Back-End/Subscriptions/views/register_form_views.py
+++ b/Back-End/Subscriptions/views/register_form_views.py
@@ -252,6 +252,8 @@ class RegisterFormWithIdView(APIView):
return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
+ # Le parent a rempli le dossier d'inscription sans sélectionner "Prélèvement par Mandat SEPA"
+ # L'école doit désormais valider le dossier d'inscription
try:
# Génération de la fiche d'inscription au format PDF
base_dir = os.path.join(settings.MEDIA_ROOT, f"registration_files/dossier_rf_{registerForm.pk}")
@@ -304,11 +306,14 @@ class RegisterFormWithIdView(APIView):
guardian = student.getMainGuardian()
email = guardian.profile_role.profile.email
errorMessage = mailer.sendMandatSEPA(email, registerForm.establishment.pk)
- if errorMessage == '':
- registerForm.last_update = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
- updateStateMachine(registerForm, 'EVENT_SEND_SEPA')
- return JsonResponse({"message": f"Le mandat SEPA a bien été envoyé à l'adresse {email}"}, safe=False)
- return JsonResponse({"errorMessage": errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST)
+ if errorMessage != '':
+ return JsonResponse({"errorMessage": errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST)
+ registerForm.last_update = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
+ updateStateMachine(registerForm, 'EVENT_SEND_SEPA')
+ elif _status == RegistrationForm.RegistrationFormStatus.RF_SEPA_TO_SEND:
+ # Le parent a rempli le dossier d'inscription en sélectionnant "Prélèvement par Mandat SEPA"
+ # L'école doit désormais envoyer le mandat SEPA pour poursuivre l'inscription
+ updateStateMachine(registerForm, 'EVENT_WAITING_FOR_SEPA')
# Retourner les données mises à jour
return JsonResponse(studentForm_serializer.data, safe=False)
diff --git a/Back-End/Subscriptions/views/student_views.py b/Back-End/Subscriptions/views/student_views.py
index 8abcd99..70c590d 100644
--- a/Back-End/Subscriptions/views/student_views.py
+++ b/Back-End/Subscriptions/views/student_views.py
@@ -101,7 +101,8 @@ class ChildrenListView(APIView):
status__in=[
RegistrationForm.RegistrationFormStatus.RF_SENT,
RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW,
- RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT
+ RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT,
+ RegistrationForm.RegistrationFormStatus.RF_SEPA_TO_SEND
]
).distinct()
students_serializer = RegistrationFormByParentSerializer(students, many=True)
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 b4eb1d0..a968c10 100644
--- a/Front-End/src/app/[locale]/admin/subscriptions/editInscription/page.js
+++ b/Front-End/src/app/[locale]/admin/subscriptions/editInscription/page.js
@@ -7,6 +7,7 @@ import { useCsrfToken } from '@/context/CsrfContext';
import { useEstablishment } from '@/context/EstablishmentContext';
import { editRegisterForm } from '@/app/actions/subscriptionAction';
import logger from '@/utils/logger';
+import Loader from '@/components/Loader';
export default function Page() {
const router = useRouter();
@@ -16,14 +17,18 @@ export default function Page() {
const [formErrors, setFormErrors] = useState({});
const csrfToken = useCsrfToken();
const { selectedEstablishmentId } = useEstablishment();
+ const [isLoading, setIsLoading] = useState(false);
const handleSubmit = (data) => {
+ setIsLoading(true);
editRegisterForm(studentId, data, csrfToken)
.then((result) => {
+ setIsLoading(false);
logger.debug('Success:', result);
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
})
.catch((error) => {
+ setIsLoading(false);
logger.error('Error:', error.message);
if (error.details) {
logger.error('Form errors:', error.details);
@@ -32,6 +37,10 @@ export default function Page() {
});
};
+ if (isLoading) {
+ return ;
+ }
+
return (
{});
+
+ const [isSepaUploadModalOpen, setIsSepaUploadModalOpen] = useState(false);
+ const [selectedRowForUpload, setSelectedRowForUpload] = useState(null);
+
const csrfToken = useCsrfToken();
const router = useRouter();
const { selectedEstablishmentId } = useEstablishment();
+ const openSepaUploadModal = (row) => {
+ setSelectedRowForUpload(row);
+ setIsSepaUploadModalOpen(true);
+ };
+
+ const closeSepaUploadModal = () => {
+ setSelectedRowForUpload(null);
+ setIsSepaUploadModalOpen(false);
+ };
+
const openModal = () => {
setIsOpen(true);
};
@@ -221,28 +237,6 @@ export default function Page({ params: { locale } }) {
}
};
- useEffect(() => {
- if (selectedEstablishmentId) {
- const fetchInitialData = () => {
- Promise.all([
- fetchClasses(selectedEstablishmentId),
- fetchStudents(selectedEstablishmentId),
- ])
- .then(([classesData, studentsData]) => {
- setClasses(classesData);
- setEleves(studentsData);
- logger.debug('Success - Classes:', classesData);
- logger.debug('Success - Students:', studentsData);
- })
- .catch((error) => {
- logger.error('Error fetching initial data:', error);
- });
- };
-
- fetchInitialData();
- }
- }, [selectedEstablishmentId]);
-
useEffect(() => {
if (selectedEstablishmentId) {
const fetchDataAndSetState = () => {
@@ -257,6 +251,19 @@ export default function Page({ params: { locale } }) {
)
.then(registerFormPendingDataHandler)
.catch(requestErrorHandler),
+
+ fetchClasses(selectedEstablishmentId)
+ .then((classesData) => {
+ setClasses(classesData);
+ })
+ .catch(requestErrorHandler),
+
+ fetchStudents(selectedEstablishmentId)
+ .then((studentsData) => {
+ setEleves(studentsData);
+ })
+ .catch(requestErrorHandler),
+
fetchRegisterForms(selectedEstablishmentId, SUBSCRIBED)
.then(registerFormSubscribedDataHandler)
.catch(requestErrorHandler),
@@ -378,6 +385,33 @@ export default function Page({ params: { locale } }) {
setTotalPages(Math.ceil(totalArchives / itemsPerPage));
}
}, [currentPage]);
+
+ const handleSepaFileUpload = (file, row) => {
+ if (!file || !row) {
+ logger.error("Aucun fichier ou ligne sélectionnée pour l'upload.");
+ return;
+ }
+
+ const formData = new FormData();
+ formData.append('status', 7);
+ formData.append('sepa_file', file);
+
+ // Appeler l'API pour uploader le fichier SEPA
+ sendSEPARegisterForm(row.student.id, formData, csrfToken)
+ .then((response) => {
+ logger.debug('Mandat SEPA uploadé avec succès :', response);
+ setPopupMessage('Le mandat SEPA a été uploadé avec succès.');
+ setPopupVisible(true);
+ setReloadFetch(true);
+ closeSepaUploadModal();
+ })
+ .catch((error) => {
+ logger.error("Erreur lors de l'upload du mandat SEPA :", error);
+ setPopupMessage("Erreur lors de l'upload du mandat SEPA.");
+ setPopupVisible(true);
+ });
+ };
+
/**
* Archives a registration form after user confirmation.
*
@@ -386,44 +420,56 @@ export default function Page({ params: { locale } }) {
* @param {string} prenom - The first name of the person whose registration form is being archived.
*/
const archiveFicheInscription = (id, nom, prenom) => {
- setPopup({
- visible: true,
- message: `Attentions ! \nVous êtes sur le point d'archiver le dossier d'inscription de ${nom} ${prenom}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`,
- onConfirm: () => {
- archiveRegisterForm(id)
- .then((data) => {
- logger.debug('Success:', data);
- setRegistrationForms(
- registrationForms.filter((fiche) => fiche.id !== id)
- );
- setReloadFetch(true);
- alert("Le dossier d'inscription a été correctement archivé");
- })
- .catch((error) => {
- logger.error('Error archiving data:', error);
- alert(
- "Erreur lors de l'archivage du dossier d'inscription.\nContactez l'administrateur."
- );
- });
- },
+ setConfirmPopupMessage(
+ `Attentions ! \nVous êtes sur le point d'archiver le dossier d'inscription de ${nom} ${prenom}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`
+ );
+ setConfirmPopupOnConfirm(() => () => {
+ archiveRegisterForm(id)
+ .then((data) => {
+ logger.debug('Success:', data);
+ setPopupMessage(
+ `Le dossier d'inscription a été correctement archivé`
+ );
+ setPopupVisible(true);
+ setRegistrationForms(
+ registrationForms.filter((fiche) => fiche.id !== id)
+ );
+ setReloadFetch(true);
+ })
+ .catch((error) => {
+ logger.error('Error archiving data:', error);
+ setPopupMessage(
+ `Erreur lors de l'archivage du dossier d'inscription.\nContactez l'administrateur.`
+ );
+ setPopupVisible(true);
+ });
+ setConfirmPopupVisible(false);
});
+ setConfirmPopupVisible(true);
};
const sendConfirmRegisterForm = (id, nom, prenom) => {
- setPopup({
- visible: true,
- message: `Avertissement ! \nVous êtes sur le point d'envoyer un dossier d'inscription à ${nom} ${prenom}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`,
- onConfirm: () => {
- sendRegisterForm(id)
- .then((data) => {
- logger.debug('Success:', data);
- setReloadFetch(true);
- })
- .catch((error) => {
- logger.error('Error fetching data:', error);
- });
- },
+ setConfirmPopupMessage(
+ `Avertissement ! \nVous êtes sur le point d'envoyer un dossier d'inscription à ${nom} ${prenom}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`
+ );
+ setConfirmPopupOnConfirm(() => () => {
+ sendRegisterForm(id)
+ .then((data) => {
+ logger.debug('Success:', data);
+ setPopupMessage(`Le dossier d'inscription a été envoyé avec succès`);
+ setPopupVisible(true);
+ setReloadFetch(true);
+ })
+ .catch((error) => {
+ logger.error('Error archiving data:', error);
+ setPopupMessage(
+ `Erreur lors de l'envoi du dossier d'inscription.\nContactez l'administrateur.`
+ );
+ setPopupVisible(true);
+ });
+ setConfirmPopupVisible(false);
});
+ setConfirmPopupVisible(true);
};
const affectationClassFormSubmitHandler = (formdata) => {
@@ -437,25 +483,6 @@ export default function Page({ params: { locale } }) {
});
};
- const refuseRegistrationForm = (id, lastname, firstname, guardianEmail) => {
- const data = { status: 2, establishment: selectedEstablishmentId };
-
- setPopup({
- visible: true,
- message: `Avertissement ! \nVous êtes sur le point de refuser le dossier d'inscription de ${lastname} ${firstname}\nUne notification va être envoyée à l'adresse ${guardianEmail}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`,
- onConfirm: () => {
- editRegisterForm(id, data, csrfToken)
- .then((data) => {
- logger.debug('Success:', data);
- setReloadFetch(true);
- })
- .catch((error) => {
- logger.error('Error refusing RF:', error);
- });
- },
- });
- };
-
const updateStatusAction = (id, newStatus) => {
logger.debug(
`Mise à jour du statut du dossier d'inscription avec l'ID : ${id} vers le statut : ${newStatus}`
@@ -551,6 +578,7 @@ export default function Page({ params: { locale } }) {
establishment: selectedEstablishmentId,
};
+ setIsLoading(true);
createRegisterForm(data, csrfToken)
.then((data) => {
// Cloner les schoolFileTemplates pour chaque templateMaster du fileGroup
@@ -582,6 +610,7 @@ export default function Page({ params: { locale } }) {
logger.debug('Template enregistré avec succès:', response);
})
.catch((error) => {
+ setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du template:",
error
@@ -589,6 +618,7 @@ export default function Page({ params: { locale } }) {
});
})
.catch((error) => {
+ setIsLoading(false);
logger.error('Error during cloning or sending:', error);
});
});
@@ -608,6 +638,7 @@ export default function Page({ params: { locale } }) {
logger.debug('Parent template enregistré avec succès:', response);
})
.catch((error) => {
+ setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du parent template:",
error
@@ -634,12 +665,15 @@ export default function Page({ params: { locale } }) {
closeModal(); // Appeler closeModal ici après que tout soit terminé
// Forcer le rechargement complet des données
setReloadFetch(true);
+ setIsLoading(false);
})
.catch((error) => {
+ setIsLoading(false);
logger.error('Error during cloning or sending:', error);
});
})
.catch((error) => {
+ setIsLoading(false);
logger.error('Error:', error);
});
};
@@ -810,6 +844,24 @@ export default function Page({ params: { locale } }) {
onClick: () => openFilesModal(row),
},
],
+ 8: [
+ {
+ icon: (
+
+
+
+ ),
+ onClick: () => openFilesModal(row),
+ },
+ {
+ icon: (
+
+
+
+ ),
+ onClick: () => openSepaUploadModal(row),
+ },
+ ],
default: [
{
icon: (
@@ -879,37 +931,6 @@ export default function Page({ params: { locale } }) {
),
},
- {
- name: t('files'),
- transform: (row) => (
-
- ),
- },
{
name: 'Actions',
transform: (row) => (
@@ -1116,13 +1137,16 @@ export default function Page({ params: { locale } }) {
) : null}
{
- popup.onConfirm();
- setPopup({ ...popup, visible: false });
- }}
- onCancel={() => setPopup({ ...popup, visible: false })}
+ visible={popupVisible}
+ message={popupMessage}
+ onConfirm={() => setPopupVisible(false)}
+ uniqueConfirmButton={true}
+ />
+ setConfirmPopupVisible(false)}
/>
{isOpen && (
@@ -1176,6 +1200,21 @@ export default function Page({ params: { locale } }) {
)}
/>
)}
+ {isSepaUploadModalOpen && (
+ (
+
+ handleSepaFileUpload(file, selectedRowForUpload)
+ }
+ />
+ )}
+ />
+ )}
{isFilesModalOpen && (
{
const userIdFromSession = user.user_id;
setUserId(userIdFromSession);
- console.log(selectedEstablishmentId);
fetchChildren(userIdFromSession, selectedEstablishmentId).then((data) => {
setChildren(data);
});
- }, [selectedEstablishmentId]);
+ setReloadFetch(false);
+ }, [selectedEstablishmentId, reloadFetch]);
function handleView(eleveId) {
logger.debug(`View dossier for student id: ${eleveId}`);
@@ -70,7 +71,8 @@ export default function ParentHomePage() {
sendSEPARegisterForm(uploadingStudentId, formData, csrfToken)
.then((response) => {
logger.debug('RF mis à jour avec succès:', response);
- // Logique supplémentaire après la mise à jour (par exemple, redirection ou notification)
+ setReloadFetch(true);
+ setUploadState('off');
})
.catch((error) => {
logger.error('Erreur lors de la mise à jour du RF:', error);
@@ -118,7 +120,7 @@ export default function ParentHomePage() {
)}
- {row.status === 3 && (
+ {(row.status === 3 || row.status === 8) && (