diff --git a/Back-End/School/management/commands/init_payment_plans.py b/Back-End/School/management/commands/init_payment_plans.py new file mode 100644 index 0000000..d2557d9 --- /dev/null +++ b/Back-End/School/management/commands/init_payment_plans.py @@ -0,0 +1,58 @@ +from django.core.management.base import BaseCommand +from django.utils import timezone +from dateutil.relativedelta import relativedelta +from School.models import PaymentPlan, PaymentPlanType, FeeType + +class Command(BaseCommand): + help = 'Initialize or update Payment Plans' + + def handle(self, *args, **kwargs): + self.create_or_update_payment_plans() + + def create_or_update_payment_plans(self): + current_date = timezone.now().date() + + for fee_type in FeeType.choices: + fee_type_value = fee_type[0] + + # 1 fois - échéance à 1 mois à partir de la date actuelle + PaymentPlan.objects.update_or_create( + frequency=PaymentPlanType.ONE_TIME, + type=fee_type_value, + defaults={ + 'due_dates': [current_date + relativedelta(months=1)], + 'is_active': True + } + ) + + # 3 fois - échéances espacées de 4 mois + PaymentPlan.objects.update_or_create( + frequency=PaymentPlanType.THREE_TIMES, + type=fee_type_value, + defaults={ + 'due_dates': [current_date + relativedelta(months=1+4*i) for i in range(3)], + 'is_active': False + } + ) + + # 10 fois - échéances espacées d'un mois + PaymentPlan.objects.update_or_create( + frequency=PaymentPlanType.TEN_TIMES, + type=fee_type_value, + defaults={ + 'due_dates': [current_date + relativedelta(months=1+i) for i in range(10)], + 'is_active': False + } + ) + + # 12 fois - échéances espacées d'un mois + PaymentPlan.objects.update_or_create( + frequency=PaymentPlanType.TWELVE_TIMES, + type=fee_type_value, + defaults={ + 'due_dates': [current_date + relativedelta(months=1+i) for i in range(12)], + 'is_active': False + } + ) + + self.stdout.write(self.style.SUCCESS('Payment Plans initialized or updated successfully')) \ No newline at end of file diff --git a/Back-End/School/models.py b/Back-End/School/models.py index 3bc84cf..4c8b5d9 100644 --- a/Back-End/School/models.py +++ b/Back-End/School/models.py @@ -108,4 +108,8 @@ class PaymentPlan(models.Model): frequency = models.IntegerField(choices=PaymentPlanType.choices, default=PaymentPlanType.ONE_TIME) due_dates = ArrayField(models.DateField(), blank=True) type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) + is_active = models.BooleanField(default=False) + + def __str__(self): + return f"{self.get_frequency_display()} - {self.get_type_display()}" diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index e7b2f9d..a2892c9 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -221,7 +221,7 @@ class RegistrationFileTemplate(models.Model): order = models.PositiveIntegerField(default=0) # Ajout du champ order date_added = models.DateTimeField(auto_now_add=True) is_required = models.BooleanField(default=False) - group = models.ForeignKey(RegistrationFileGroup, on_delete=models.CASCADE, related_name='file_templates') + group = models.ForeignKey(RegistrationFileGroup, on_delete=models.CASCADE, related_name='file_templates', null=True, blank=True) @property def formatted_date_added(self): diff --git a/Back-End/start.py b/Back-End/start.py index be242c4..99da02b 100644 --- a/Back-End/start.py +++ b/Back-End/start.py @@ -18,7 +18,8 @@ commands = [ ["python", "manage.py", "makemigrations", "GestionMessagerie", "--noinput"], ["python", "manage.py", "makemigrations", "Auth", "--noinput"], ["python", "manage.py", "makemigrations", "School", "--noinput"], - ["python", "manage.py", "migrate", "--noinput"] + ["python", "manage.py", "migrate", "--noinput"], + ["python", "manage.py", "init_payment_plans"] ] for command in commands: diff --git a/Front-End/src/app/[locale]/admin/structure/page.js b/Front-End/src/app/[locale]/admin/structure/page.js index 51a7722..2d4afde 100644 --- a/Front-End/src/app/[locale]/admin/structure/page.js +++ b/Front-End/src/app/[locale]/admin/structure/page.js @@ -9,15 +9,16 @@ import { ClassesProvider } from '@/context/ClassesContext'; import { createDatas, updateDatas, removeDatas, - fetchSpecialities, - fetchTeachers, - fetchClasses, - fetchSchedules, - fetchRegistrationDiscounts, - fetchTuitionDiscounts, - fetchRegistrationFees, + fetchSpecialities, + fetchTeachers, + fetchClasses, + fetchSchedules, + fetchRegistrationDiscounts, + fetchTuitionDiscounts, + fetchRegistrationFees, fetchTuitionFees, - } from '@/app/lib/schoolAction'; + fetchRregistrationPaymentPlans, + fetchTuitionPaymentPlans } from '@/app/lib/schoolAction'; import SidebarTabs from '@/components/SidebarTabs'; import FilesManagement from '@/components/Structure/Files/FilesManagement'; @@ -35,6 +36,8 @@ export default function Page() { const [registrationFees, setRegistrationFees] = useState([]); const [tuitionFees, setTuitionFees] = useState([]); const [fichiers, setFichiers] = useState([]); + const [registrationPaymentPlans, setRegistrationPaymentPlans] = useState([]); + const [tuitionPaymentPlans, setTuitionPaymentPlans] = useState([]); const csrfToken = useCsrfToken(); @@ -70,7 +73,11 @@ export default function Page() { }) .catch(error => console.error('Error fetching files:', error)); + // Fetch data for registration payment plans + handleRegistrationPaymentPlans(); + // Fetch data for tuition payment plans + handleTuitionPaymentPlans(); }, []); const handleSpecialities = () => { @@ -137,6 +144,22 @@ export default function Page() { .catch(error => console.error('Error fetching tuition fees', error)); }; + const handleRegistrationPaymentPlans = () => { + fetchRregistrationPaymentPlans() + .then(data => { + setRegistrationPaymentPlans(data); + }) + .catch(error => console.error('Error fetching registration payment plans:', error)); + }; + + const handleTuitionPaymentPlans = () => { + fetchTuitionPaymentPlans() + .then(data => { + setTuitionPaymentPlans(data); + }) + .catch(error => console.error('Error fetching tuition payment plans:', error)); + }; + const handleCreate = (url, newData, setDatas) => { return createDatas(url, newData, csrfToken) .then(data => { @@ -236,6 +259,11 @@ export default function Page() { setRegistrationFees={setRegistrationFees} tuitionFees={tuitionFees} setTuitionFees={setTuitionFees} + registrationPaymentPlans={registrationPaymentPlans} + setRegistrationPaymentPlans={setRegistrationPaymentPlans} + tuitionPaymentPlans={tuitionPaymentPlans} + setTuitionPaymentPlans={setTuitionPaymentPlans} + setRegist handleCreate={handleCreate} handleEdit={handleEdit} handleDelete={handleDelete} diff --git a/Front-End/src/app/lib/schoolAction.js b/Front-End/src/app/lib/schoolAction.js index 2c91893..9d84f91 100644 --- a/Front-End/src/app/lib/schoolAction.js +++ b/Front-End/src/app/lib/schoolAction.js @@ -4,7 +4,8 @@ import { BE_SCHOOL_SCHOOLCLASSES_URL, BE_SCHOOL_PLANNINGS_URL, BE_SCHOOL_FEES_URL, - BE_SCHOOL_DISCOUNTS_URL + BE_SCHOOL_DISCOUNTS_URL, + BE_SCHOOL_PAYMENT_PLANS_URL, } from '@/utils/Url'; const requestResponseHandler = async (response) => { @@ -60,6 +61,16 @@ export const fetchTuitionFees = () => { .then(requestResponseHandler) }; +export const fetchRregistrationPaymentPlans = () => { + return fetch(`${BE_SCHOOL_PAYMENT_PLANS_URL}/registration`) + .then(requestResponseHandler) +} + +export const fetchTuitionPaymentPlans = () => { + return fetch(`${BE_SCHOOL_PAYMENT_PLANS_URL}/tuition`) + .then(requestResponseHandler) +} + export const createDatas = (url, newData, csrfToken) => { return fetch(url, { method: 'POST', diff --git a/Front-End/src/components/CheckBox.js b/Front-End/src/components/CheckBox.js index 3818736..02f8fd5 100644 --- a/Front-End/src/components/CheckBox.js +++ b/Front-End/src/components/CheckBox.js @@ -3,7 +3,6 @@ import React from 'react'; const CheckBox = ({ item, formData, handleChange, fieldName, itemLabelFunc = () => null, labelAttenuated = () => false, horizontal }) => { const isChecked = formData[fieldName].includes(parseInt(item.id)); const isAttenuated = labelAttenuated(item) && !isChecked; - return (
{horizontal && ( diff --git a/Front-End/src/components/CheckBoxList.js b/Front-End/src/components/CheckBoxList.js index 8ff7a8c..0207e52 100644 --- a/Front-End/src/components/CheckBoxList.js +++ b/Front-End/src/components/CheckBoxList.js @@ -14,7 +14,7 @@ const CheckBoxList = ({ horizontal = false // Ajouter l'option horizontal }) => { return ( -
+