diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index 8cfb355..d43dbe5 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -8,27 +8,6 @@ from School.models import SchoolClass, Fee, Discount from datetime import datetime -class RegistrationFee(models.Model): - """ - Représente un tarif ou frais d’inscription avec différentes options de paiement. - """ - class PaymentOptions(models.IntegerChoices): - SINGLE_PAYMENT = 0, _('Paiement en une seule fois') - MONTHLY_PAYMENT = 1, _('Paiement mensuel') - QUARTERLY_PAYMENT = 2, _('Paiement trimestriel') - - name = models.CharField(max_length=255, unique=True) - description = models.TextField(blank=True) - base_amount = models.DecimalField(max_digits=10, decimal_places=2) - discounts = models.JSONField(blank=True, null=True) - supplements = models.JSONField(blank=True, null=True) - validity_start_date = models.DateField() - validity_end_date = models.DateField() - payment_option = models.IntegerField(choices=PaymentOptions, default=PaymentOptions.SINGLE_PAYMENT) - - def __str__(self): - return self.name - class Language(models.Model): """ Représente une langue parlée par l’élève. diff --git a/Back-End/Subscriptions/serializers.py b/Back-End/Subscriptions/serializers.py index 2e916a7..b11c482 100644 --- a/Back-End/Subscriptions/serializers.py +++ b/Back-End/Subscriptions/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import RegistrationFileTemplate, RegistrationFile, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationFee -from School.models import SchoolClass, Fee, Discount +from .models import RegistrationFileTemplate, RegistrationFile, RegistrationForm, Student, Guardian, Sibling, Language +from School.models import SchoolClass, Fee, Discount, FeeType from School.serializers import FeeSerializer, DiscountSerializer from Auth.models import Profile from Auth.serializers import ProfileSerializer @@ -11,12 +11,6 @@ from django.utils import timezone import pytz from datetime import datetime -class RegistrationFeeSerializer(serializers.ModelSerializer): - id = serializers.IntegerField(required=False) - class Meta: - model = RegistrationFee - fields = '__all__' - class RegistrationFileSerializer(serializers.ModelSerializer): class Meta: model = RegistrationFile @@ -136,6 +130,8 @@ class RegistrationFormSerializer(serializers.ModelSerializer): registration_files = RegistrationFileSerializer(many=True, required=False) fees = serializers.PrimaryKeyRelatedField(queryset=Fee.objects.all(), many=True, required=False) discounts = serializers.PrimaryKeyRelatedField(queryset=Discount.objects.all(), many=True, required=False) + totalRegistrationFees = serializers.SerializerMethodField() + totalTuitionFees = serializers.SerializerMethodField() class Meta: model = RegistrationForm @@ -184,6 +180,14 @@ class RegistrationFormSerializer(serializers.ModelSerializer): return local_time.strftime("%d-%m-%Y %H:%M") + def get_totalRegistrationFees(self, obj): + for fee in obj.fees.filter(type=FeeType.REGISTRATION_FEE): + print(fee.base_amount) + return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.REGISTRATION_FEE)) + + def get_totalTuitionFees(self, obj): + return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.TUITION_FEE)) + class StudentByParentSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) diff --git a/Back-End/Subscriptions/urls.py b/Back-End/Subscriptions/urls.py index 0e7fbf8..9def08b 100644 --- a/Back-End/Subscriptions/urls.py +++ b/Back-End/Subscriptions/urls.py @@ -1,7 +1,7 @@ from django.urls import path, re_path from . import views -from .views import RegistrationFileTemplateView, RegisterFormListView, RegisterFormView, StudentView, GuardianView, ChildrenListView, StudentListView, RegistrationFeeView, RegistrationFileView +from .views import RegistrationFileTemplateView, RegisterFormListView, RegisterFormView, StudentView, GuardianView, ChildrenListView, StudentListView, RegistrationFileView urlpatterns = [ re_path(r'^registerForms/(?P<_filter>[a-zA-z]+)$', RegisterFormListView.as_view(), name="registerForms"), @@ -30,9 +30,6 @@ urlpatterns = [ # Page INSCRIPTION - Liste des élèves re_path(r'^students$', StudentListView.as_view(), name="students"), - # Frais d'inscription - re_path(r'^registrationFees$', RegistrationFeeView.as_view(), name="registrationFees"), - # modèles de fichiers d'inscription re_path(r'^registrationFileTemplates$', RegistrationFileTemplateView.as_view(), name='registrationFileTemplates'), re_path(r'^registrationFileTemplates/(?P<_id>[0-9]+)$', RegistrationFileTemplateView.as_view(), name="registrationFileTemplate"), diff --git a/Back-End/Subscriptions/views.py b/Back-End/Subscriptions/views.py index 4e0ae69..4b48e4f 100644 --- a/Back-End/Subscriptions/views.py +++ b/Back-End/Subscriptions/views.py @@ -22,10 +22,10 @@ import Subscriptions.mailManager as mailer import Subscriptions.util as util from Subscriptions.automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine -from .serializers import RegistrationFormSerializer, StudentSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFileSerializer, RegistrationFileTemplateSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFeeSerializer +from .serializers import RegistrationFormSerializer, StudentSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFileSerializer, RegistrationFileTemplateSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer from .pagination import CustomPagination from .signals import clear_cache -from .models import Student, Guardian, RegistrationForm, RegistrationFee, RegistrationFileTemplate, RegistrationFile +from .models import Student, Guardian, RegistrationForm, RegistrationFileTemplate, RegistrationFile from .automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine from Auth.models import Profile @@ -335,16 +335,6 @@ class StudentListView(APIView): students_serializer = StudentByRFCreationSerializer(students, many=True) return JsonResponse(students_serializer.data, safe=False) -# API utilisée pour la vue de personnalisation des frais d'inscription pour la structure -class RegistrationFeeView(APIView): - """ - Liste les frais d’inscription. - """ - def get(self, request): - tarifs = bdd.getAllObjects(RegistrationFee) - tarifs_serializer = RegistrationFeeSerializer(tarifs, many=True) - return JsonResponse(tarifs_serializer.data, safe=False) - class RegistrationFileTemplateView(APIView): """ Gère les fichiers templates pour les dossiers d’inscription. diff --git a/Front-End/src/app/[locale]/admin/subscriptions/page.js b/Front-End/src/app/[locale]/admin/subscriptions/page.js index 6cb654f..9de4858 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js @@ -410,7 +410,9 @@ useEffect(()=>{ associated_profile: response.id }], sibling: [] - } + }, + fees: allFeesIds, + discounts: allDiscountsds }; createRegisterForm(data, csrfToken) @@ -422,6 +424,7 @@ useEffect(()=>{ sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName); } closeModal(); + console.log('Success:', data); // Forcer le rechargement complet des données setReloadFetch(true); }) @@ -810,8 +813,8 @@ const handleFileUpload = ({file, name, is_required, order}) => { fee.is_active)} + tuitionFees={tuitionFees.filter(fee => fee.is_active)} onSubmit={createRF} /> )} diff --git a/Front-End/src/components/Inscription/InscriptionForm.js b/Front-End/src/components/Inscription/InscriptionForm.js index 0f7bab6..22a23fb 100644 --- a/Front-End/src/components/Inscription/InscriptionForm.js +++ b/Front-End/src/components/Inscription/InscriptionForm.js @@ -123,6 +123,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r }; const submit = () => { + console.log('Submitting form data:', formData); onSubmit(formData); } diff --git a/Front-End/src/components/Popup.js b/Front-End/src/components/Popup.js index 809d4f6..cbb4e72 100644 --- a/Front-End/src/components/Popup.js +++ b/Front-End/src/components/Popup.js @@ -4,10 +4,15 @@ import ReactDOM from 'react-dom'; const Popup = ({ visible, message, onConfirm, onCancel, uniqueConfirmButton = false }) => { if (!visible) return null; + // Diviser le message en lignes + const messageLines = message.split('\n'); + return ReactDOM.createPortal(
-

{message}

+ {messageLines.map((line, index) => ( +

{line}

+ ))}
{!uniqueConfirmButton && ( diff --git a/Front-End/src/components/Structure/Configuration/ClassesSection.js b/Front-End/src/components/Structure/Configuration/ClassesSection.js index e7af1ad..385ccd6 100644 --- a/Front-End/src/components/Structure/Configuration/ClassesSection.js +++ b/Front-End/src/components/Structure/Configuration/ClassesSection.js @@ -94,6 +94,9 @@ const ClassesSection = ({ classes, setClasses, teachers, handleCreate, handleEdi const [localErrors, setLocalErrors] = useState({}); const [popupVisible, setPopupVisible] = useState(false); const [popupMessage, setPopupMessage] = useState(""); + const [removePopupVisible, setRemovePopupVisible] = useState(false); + const [removePopupMessage, setRemovePopupMessage] = useState(""); + const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {}); const niveauxPremierCycle = [ { id: 1, name: 'TPS', age: 2 }, @@ -377,7 +380,25 @@ const ClassesSection = ({ classes, setClasses, teachers, handleCreate, handleEdi
); diff --git a/Front-End/src/components/Structure/Configuration/SpecialitiesSection.js b/Front-End/src/components/Structure/Configuration/SpecialitiesSection.js index b3ae3bd..81a8f6b 100644 --- a/Front-End/src/components/Structure/Configuration/SpecialitiesSection.js +++ b/Front-End/src/components/Structure/Configuration/SpecialitiesSection.js @@ -16,6 +16,10 @@ const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, hand const [popupVisible, setPopupVisible] = useState(false); const [popupMessage, setPopupMessage] = useState(""); + const [removePopupVisible, setRemovePopupVisible] = useState(false); + const [removePopupMessage, setRemovePopupMessage] = useState(""); + const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {}); + // Récupération des messages d'erreur const getError = (field) => { return localErrors?.[field]?.[0]; @@ -26,7 +30,7 @@ const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, hand }; const handleRemoveSpeciality = (id) => { - handleDelete(id) + return handleDelete(id) .then(() => { setSpecialities(prevSpecialities => prevSpecialities.filter(speciality => speciality.id !== id)); }) @@ -161,7 +165,25 @@ const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, hand
); diff --git a/Front-End/src/components/Structure/Configuration/TeachersSection.js b/Front-End/src/components/Structure/Configuration/TeachersSection.js index 801ea06..01d979c 100644 --- a/Front-End/src/components/Structure/Configuration/TeachersSection.js +++ b/Front-End/src/components/Structure/Configuration/TeachersSection.js @@ -98,6 +98,10 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha const [popupVisible, setPopupVisible] = useState(false); const [popupMessage, setPopupMessage] = useState(""); + const [removePopupVisible, setRemovePopupVisible] = useState(false); + const [removePopupMessage, setRemovePopupMessage] = useState(""); + const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {}); + // Récupération des messages d'erreur const getError = (field) => { return localErrors?.[field]?.[0]; @@ -109,7 +113,7 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha }; const handleRemoveTeacher = (id) => { - handleDelete(id) + return handleDelete(id) .then(() => { setTeachers(prevTeachers => prevTeachers.filter(teacher => teacher.id !== id)); }) @@ -361,7 +365,25 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
); diff --git a/Front-End/src/components/Structure/Tarification/DiscountsSection.js b/Front-End/src/components/Structure/Tarification/DiscountsSection.js index 177ab05..b38357c 100644 --- a/Front-End/src/components/Structure/Tarification/DiscountsSection.js +++ b/Front-End/src/components/Structure/Tarification/DiscountsSection.js @@ -12,13 +12,16 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h const [localErrors, setLocalErrors] = useState({}); const [popupVisible, setPopupVisible] = useState(false); const [popupMessage, setPopupMessage] = useState(""); + const [removePopupVisible, setRemovePopupVisible] = useState(false); + const [removePopupMessage, setRemovePopupMessage] = useState(""); + const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {}); const handleAddDiscount = () => { setNewDiscount({ id: Date.now(), name: '', amount: '', description: '', discount_type: 0, type: type }); }; const handleRemoveDiscount = (id) => { - handleDelete(id) + return handleDelete(id) .then(() => { setDiscounts(prevDiscounts => prevDiscounts.filter(discount => discount.id !== id)); }) @@ -204,7 +207,25 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h