mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Mise en place des paiements en plusieurs fois (partie BACK) [#25]
This commit is contained in:
58
Back-End/School/management/commands/init_payment_plans.py
Normal file
58
Back-End/School/management/commands/init_payment_plans.py
Normal file
@ -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'))
|
||||||
@ -108,4 +108,8 @@ class PaymentPlan(models.Model):
|
|||||||
frequency = models.IntegerField(choices=PaymentPlanType.choices, default=PaymentPlanType.ONE_TIME)
|
frequency = models.IntegerField(choices=PaymentPlanType.choices, default=PaymentPlanType.ONE_TIME)
|
||||||
due_dates = ArrayField(models.DateField(), blank=True)
|
due_dates = ArrayField(models.DateField(), blank=True)
|
||||||
type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE)
|
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()}"
|
||||||
|
|
||||||
|
|||||||
@ -221,7 +221,7 @@ class RegistrationFileTemplate(models.Model):
|
|||||||
order = models.PositiveIntegerField(default=0) # Ajout du champ order
|
order = models.PositiveIntegerField(default=0) # Ajout du champ order
|
||||||
date_added = models.DateTimeField(auto_now_add=True)
|
date_added = models.DateTimeField(auto_now_add=True)
|
||||||
is_required = models.BooleanField(default=False)
|
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
|
@property
|
||||||
def formatted_date_added(self):
|
def formatted_date_added(self):
|
||||||
|
|||||||
@ -18,7 +18,8 @@ commands = [
|
|||||||
["python", "manage.py", "makemigrations", "GestionMessagerie", "--noinput"],
|
["python", "manage.py", "makemigrations", "GestionMessagerie", "--noinput"],
|
||||||
["python", "manage.py", "makemigrations", "Auth", "--noinput"],
|
["python", "manage.py", "makemigrations", "Auth", "--noinput"],
|
||||||
["python", "manage.py", "makemigrations", "School", "--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:
|
for command in commands:
|
||||||
|
|||||||
@ -17,7 +17,8 @@ import { createDatas,
|
|||||||
fetchTuitionDiscounts,
|
fetchTuitionDiscounts,
|
||||||
fetchRegistrationFees,
|
fetchRegistrationFees,
|
||||||
fetchTuitionFees,
|
fetchTuitionFees,
|
||||||
} from '@/app/lib/schoolAction';
|
fetchRregistrationPaymentPlans,
|
||||||
|
fetchTuitionPaymentPlans } from '@/app/lib/schoolAction';
|
||||||
import SidebarTabs from '@/components/SidebarTabs';
|
import SidebarTabs from '@/components/SidebarTabs';
|
||||||
import FilesManagement from '@/components/Structure/Files/FilesManagement';
|
import FilesManagement from '@/components/Structure/Files/FilesManagement';
|
||||||
|
|
||||||
@ -35,6 +36,8 @@ export default function Page() {
|
|||||||
const [registrationFees, setRegistrationFees] = useState([]);
|
const [registrationFees, setRegistrationFees] = useState([]);
|
||||||
const [tuitionFees, setTuitionFees] = useState([]);
|
const [tuitionFees, setTuitionFees] = useState([]);
|
||||||
const [fichiers, setFichiers] = useState([]);
|
const [fichiers, setFichiers] = useState([]);
|
||||||
|
const [registrationPaymentPlans, setRegistrationPaymentPlans] = useState([]);
|
||||||
|
const [tuitionPaymentPlans, setTuitionPaymentPlans] = useState([]);
|
||||||
|
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
|
|
||||||
@ -70,7 +73,11 @@ export default function Page() {
|
|||||||
})
|
})
|
||||||
.catch(error => console.error('Error fetching files:', error));
|
.catch(error => console.error('Error fetching files:', error));
|
||||||
|
|
||||||
|
// Fetch data for registration payment plans
|
||||||
|
handleRegistrationPaymentPlans();
|
||||||
|
|
||||||
|
// Fetch data for tuition payment plans
|
||||||
|
handleTuitionPaymentPlans();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSpecialities = () => {
|
const handleSpecialities = () => {
|
||||||
@ -137,6 +144,22 @@ export default function Page() {
|
|||||||
.catch(error => console.error('Error fetching tuition fees', error));
|
.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) => {
|
const handleCreate = (url, newData, setDatas) => {
|
||||||
return createDatas(url, newData, csrfToken)
|
return createDatas(url, newData, csrfToken)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
@ -236,6 +259,11 @@ export default function Page() {
|
|||||||
setRegistrationFees={setRegistrationFees}
|
setRegistrationFees={setRegistrationFees}
|
||||||
tuitionFees={tuitionFees}
|
tuitionFees={tuitionFees}
|
||||||
setTuitionFees={setTuitionFees}
|
setTuitionFees={setTuitionFees}
|
||||||
|
registrationPaymentPlans={registrationPaymentPlans}
|
||||||
|
setRegistrationPaymentPlans={setRegistrationPaymentPlans}
|
||||||
|
tuitionPaymentPlans={tuitionPaymentPlans}
|
||||||
|
setTuitionPaymentPlans={setTuitionPaymentPlans}
|
||||||
|
setRegist
|
||||||
handleCreate={handleCreate}
|
handleCreate={handleCreate}
|
||||||
handleEdit={handleEdit}
|
handleEdit={handleEdit}
|
||||||
handleDelete={handleDelete}
|
handleDelete={handleDelete}
|
||||||
|
|||||||
@ -4,7 +4,8 @@ import {
|
|||||||
BE_SCHOOL_SCHOOLCLASSES_URL,
|
BE_SCHOOL_SCHOOLCLASSES_URL,
|
||||||
BE_SCHOOL_PLANNINGS_URL,
|
BE_SCHOOL_PLANNINGS_URL,
|
||||||
BE_SCHOOL_FEES_URL,
|
BE_SCHOOL_FEES_URL,
|
||||||
BE_SCHOOL_DISCOUNTS_URL
|
BE_SCHOOL_DISCOUNTS_URL,
|
||||||
|
BE_SCHOOL_PAYMENT_PLANS_URL,
|
||||||
} from '@/utils/Url';
|
} from '@/utils/Url';
|
||||||
|
|
||||||
const requestResponseHandler = async (response) => {
|
const requestResponseHandler = async (response) => {
|
||||||
@ -60,6 +61,16 @@ export const fetchTuitionFees = () => {
|
|||||||
.then(requestResponseHandler)
|
.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) => {
|
export const createDatas = (url, newData, csrfToken) => {
|
||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import React from 'react';
|
|||||||
const CheckBox = ({ item, formData, handleChange, fieldName, itemLabelFunc = () => null, labelAttenuated = () => false, horizontal }) => {
|
const CheckBox = ({ item, formData, handleChange, fieldName, itemLabelFunc = () => null, labelAttenuated = () => false, horizontal }) => {
|
||||||
const isChecked = formData[fieldName].includes(parseInt(item.id));
|
const isChecked = formData[fieldName].includes(parseInt(item.id));
|
||||||
const isAttenuated = labelAttenuated(item) && !isChecked;
|
const isAttenuated = labelAttenuated(item) && !isChecked;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={item.id} className={`flex ${horizontal ? 'flex-col items-center' : 'flex-row items-center'}`}>
|
<div key={item.id} className={`flex ${horizontal ? 'flex-col items-center' : 'flex-row items-center'}`}>
|
||||||
{horizontal && (
|
{horizontal && (
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const CheckBoxList = ({
|
|||||||
horizontal = false // Ajouter l'option horizontal
|
horizontal = false // Ajouter l'option horizontal
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={`mb-4 ${className}`}>
|
<div className={`mb-4 w-full ${className}`}>
|
||||||
<label className="block text-sm font-medium text-gray-700 flex items-center">
|
<label className="block text-sm font-medium text-gray-700 flex items-center">
|
||||||
{Icon && <Icon className="w-5 h-5 mr-2" />}
|
{Icon && <Icon className="w-5 h-5 mr-2" />}
|
||||||
{label}
|
{label}
|
||||||
|
|||||||
66
Front-End/src/components/DateTab.js
Normal file
66
Front-End/src/components/DateTab.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Check } from 'lucide-react';
|
||||||
|
import Popup from '@/components/Popup';
|
||||||
|
|
||||||
|
const DateTab = ({ dates, activeTab, handleDateChange, handleEdit, type, paymentPlanId }) => {
|
||||||
|
const [popupVisible, setPopupVisible] = useState(false);
|
||||||
|
const [popupMessage, setPopupMessage] = useState("");
|
||||||
|
const [modifiedDates, setModifiedDates] = useState({});
|
||||||
|
|
||||||
|
const submit = (updatedData) => {
|
||||||
|
const dataWithType = {
|
||||||
|
...updatedData,
|
||||||
|
type: type
|
||||||
|
};
|
||||||
|
handleEdit(paymentPlanId, dataWithType)
|
||||||
|
.then(() => {
|
||||||
|
setPopupMessage(`Mise à jour de la date d'échéance effectuée avec succès`);
|
||||||
|
setPopupVisible(true);
|
||||||
|
setModifiedDates({});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDateChangeWithModification = (tab, index, value) => {
|
||||||
|
handleDateChange(tab, index, value);
|
||||||
|
setModifiedDates(prev => ({ ...prev, [`${tab}-${index}`]: true }));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white p-4 rounded-lg h-auto max-h-96 overflow-y-auto">
|
||||||
|
<div className="flex flex-col space-y-3">
|
||||||
|
{dates[activeTab]?.map((date, index) => (
|
||||||
|
<div key={index} className="flex items-center space-x-3">
|
||||||
|
<span className="text-emerald-700 font-semibold">Échéance {index + 1}</span>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={date}
|
||||||
|
onChange={(e) => handleDateChangeWithModification(activeTab, index, e.target.value)}
|
||||||
|
className="p-2 border border-emerald-300 rounded focus:outline-none focus:ring-2 focus:ring-emerald-500 cursor-pointer"
|
||||||
|
/>
|
||||||
|
{modifiedDates[`${activeTab}-${index}`] && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => submit({ frequency: dates[activeTab].length, due_dates: dates[activeTab] })}
|
||||||
|
className="text-emerald-500 hover:text-emerald-800"
|
||||||
|
>
|
||||||
|
<Check className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<Popup
|
||||||
|
visible={popupVisible}
|
||||||
|
message={popupMessage}
|
||||||
|
onConfirm={() => setPopupVisible(false)}
|
||||||
|
onCancel={() => setPopupVisible(false)}
|
||||||
|
uniqueConfirmButton={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DateTab;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
export default function InputTextIcon({name, type, IconItem, label, value, onChange, errorMsg, placeholder, className}) {
|
export default function InputTextIcon({name, type, IconItem, label, value, onChange, errorMsg, placeholder, className, min, max}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={`${className}`}>
|
<div className={`${className}`}>
|
||||||
@ -15,6 +15,8 @@ export default function InputTextIcon({name, type, IconItem, label, value, onCha
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="flex-1 px-3 py-2 block w-full rounded-r-md sm:text-sm border-none focus:ring-0 outline-none"
|
className="flex-1 px-3 py-2 block w-full rounded-r-md sm:text-sm border-none focus:ring-0 outline-none"
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{errorMsg && <p className="mt-2 text-sm text-red-600">{errorMsg}</p>}
|
{errorMsg && <p className="mt-2 text-sm text-red-600">{errorMsg}</p>}
|
||||||
|
|||||||
44
Front-End/src/components/PaymentModeSelector.js
Normal file
44
Front-End/src/components/PaymentModeSelector.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import CheckBoxList from '@/components/CheckBoxList';
|
||||||
|
import { CreditCard } from 'lucide-react';
|
||||||
|
|
||||||
|
const paymentModes = [
|
||||||
|
{ id: 1, name: 'Prélèvement SEPA' },
|
||||||
|
{ id: 2, name: 'Virement' },
|
||||||
|
{ id: 3, name: 'Chèque' },
|
||||||
|
{ id: 4, name: 'Espèce' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const PaymentModeSelector = ({ formData, setFormData, fieldName }) => {
|
||||||
|
const handleCheckboxChange = (event) => {
|
||||||
|
const value = parseInt(event.target.value, 10);
|
||||||
|
setFormData((prevFormData) => {
|
||||||
|
const selectedModes = prevFormData[fieldName] || [];
|
||||||
|
const newSelectedModes = selectedModes.includes(value)
|
||||||
|
? selectedModes.filter((mode) => mode !== value)
|
||||||
|
: [...selectedModes, value];
|
||||||
|
return { ...prevFormData, [fieldName]: newSelectedModes };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-4 w-full">
|
||||||
|
<div className="flex justify-center bg-gray p-4 rounded-lg shadow-md w-full">
|
||||||
|
<CreditCard className="w-6 h-6 text-emerald-500 mr-2" />
|
||||||
|
<h2 className="text-xl font-semibold">Mode de paiement</h2>
|
||||||
|
<CheckBoxList
|
||||||
|
items={paymentModes}
|
||||||
|
formData={formData}
|
||||||
|
handleChange={handleCheckboxChange}
|
||||||
|
fieldName={fieldName}
|
||||||
|
itemLabelFunc={(item) => (
|
||||||
|
<span className="text-sm font-medium">{item.name}</span>
|
||||||
|
)}
|
||||||
|
horizontal={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PaymentModeSelector;
|
||||||
248
Front-End/src/components/PaymentPlanSelector.js
Normal file
248
Front-End/src/components/PaymentPlanSelector.js
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Calendar, Eye, EyeOff, Clock, Check } from 'lucide-react';
|
||||||
|
import Table from '@/components/Table';
|
||||||
|
import DateTab from '@/components/DateTab';
|
||||||
|
import InputTextIcon from '@/components/InputTextIcon';
|
||||||
|
import Popup from '@/components/Popup';
|
||||||
|
|
||||||
|
const paymentPlansOptions = [
|
||||||
|
{ id: 0, name: '1 fois', frequency: 1 },
|
||||||
|
{ id: 1, name: '3 fois', frequency: 3 },
|
||||||
|
{ id: 2, name: '10 fois', frequency: 10 },
|
||||||
|
{ id: 3, name: '12 fois', frequency: 12 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const PaymentPlanSelector = ({ paymentPlans, setPaymentPlans, handleEdit, type }) => {
|
||||||
|
const [dates, setDates] = useState({});
|
||||||
|
const [selectedFrequency, setSelectedFrequency] = useState(null);
|
||||||
|
const [activeFrequencies, setActiveFrequencies] = useState([]);
|
||||||
|
const [defaultDay, setDefaultDay] = useState(new Date().getDate());
|
||||||
|
const [isDefaultDayModified, setIsDefaultDayModified] = useState(false);
|
||||||
|
const [popupVisible, setPopupVisible] = useState(false);
|
||||||
|
const [popupMessage, setPopupMessage] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (paymentPlans && paymentPlans.length > 0) {
|
||||||
|
const activePlans = paymentPlans.filter(plan => plan.is_active);
|
||||||
|
const frequencies = activePlans.map(plan => {
|
||||||
|
const paymentPlanOption = paymentPlansOptions.find(p => p.frequency === plan.frequency);
|
||||||
|
return paymentPlanOption ? paymentPlanOption.id : null;
|
||||||
|
}).filter(id => id !== null);
|
||||||
|
setActiveFrequencies(frequencies);
|
||||||
|
|
||||||
|
if (activePlans.length > 0) {
|
||||||
|
const firstDueDate = new Date(activePlans[0].due_dates[0]);
|
||||||
|
setDefaultDay(firstDueDate.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialDates = {};
|
||||||
|
paymentPlans.forEach(plan => {
|
||||||
|
const paymentPlanOption = paymentPlansOptions.find(p => p.frequency === plan.frequency);
|
||||||
|
if (paymentPlanOption) {
|
||||||
|
initialDates[paymentPlanOption.id] = plan.due_dates;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setDates(initialDates);
|
||||||
|
}
|
||||||
|
}, [paymentPlans]);
|
||||||
|
|
||||||
|
const handleActivationChange = (value) => {
|
||||||
|
const selectedPlan = paymentPlans.find(plan => plan.frequency === paymentPlansOptions.find(p => p.id === value)?.frequency);
|
||||||
|
if (!selectedPlan) return;
|
||||||
|
|
||||||
|
const updatedData = {
|
||||||
|
...selectedPlan,
|
||||||
|
is_active: !selectedPlan.is_active
|
||||||
|
};
|
||||||
|
|
||||||
|
handleEdit(selectedPlan.id, updatedData)
|
||||||
|
.then(() => {
|
||||||
|
setPaymentPlans(prevPlans => prevPlans.map(plan =>
|
||||||
|
plan.id === selectedPlan.id ? { ...plan, is_active: updatedData.is_active } : plan
|
||||||
|
));
|
||||||
|
setActiveFrequencies(prevFrequencies => {
|
||||||
|
if (updatedData.is_active) {
|
||||||
|
setPopupMessage(`L'option de paiement en ${paymentPlansOptions.find(p => p.id === value).name} a été activée.`);
|
||||||
|
setPopupVisible(true);
|
||||||
|
return [...prevFrequencies, value];
|
||||||
|
} else {
|
||||||
|
setPopupMessage(`L'option de paiement en ${paymentPlansOptions.find(p => p.id === value).name} a été désactivée.`);
|
||||||
|
setPopupVisible(true);
|
||||||
|
return prevFrequencies.filter(item => item !== value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRowClick = (row) => {
|
||||||
|
const value = row.id;
|
||||||
|
if (selectedFrequency === value) {
|
||||||
|
setSelectedFrequency(null); // Désélectionner l'onglet si la ligne est déjà sélectionnée
|
||||||
|
} else {
|
||||||
|
setSelectedFrequency(value);
|
||||||
|
if (!dates[value]) {
|
||||||
|
const frequencyValue = paymentPlansOptions.find(plan => plan.id === value)?.frequency || 1;
|
||||||
|
const newDates = Array(frequencyValue).fill('').map((_, index) => {
|
||||||
|
const newDate = new Date();
|
||||||
|
newDate.setDate(defaultDay);
|
||||||
|
if (value === 1) {
|
||||||
|
newDate.setMonth(newDate.getMonth() + index * 4); // Espacer de 4 mois pour le paiement en 3 fois
|
||||||
|
} else {
|
||||||
|
newDate.setMonth(newDate.getMonth() + index);
|
||||||
|
}
|
||||||
|
return newDate.toISOString().split('T')[0];
|
||||||
|
});
|
||||||
|
setDates(prevDates => ({ ...prevDates, [value]: newDates }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDateChange = (planId, index, date) => {
|
||||||
|
setDates((prevDates) => {
|
||||||
|
const newDates = { ...prevDates };
|
||||||
|
newDates[planId][index] = date;
|
||||||
|
return newDates;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDefaultDayChange = (e) => {
|
||||||
|
const newDefaultDay = parseInt(e.target.value, 10);
|
||||||
|
if (newDefaultDay >= 1 && newDefaultDay <= 31) {
|
||||||
|
setDefaultDay(newDefaultDay);
|
||||||
|
setIsDefaultDayModified(true);
|
||||||
|
|
||||||
|
if (selectedFrequency !== null) {
|
||||||
|
const frequencyValue = paymentPlansOptions.find(plan => plan.id === selectedFrequency)?.frequency || 1;
|
||||||
|
const newDates = Array(frequencyValue).fill('').map((_, index) => {
|
||||||
|
const newDate = new Date();
|
||||||
|
newDate.setDate(newDefaultDay);
|
||||||
|
if (selectedFrequency === 1) {
|
||||||
|
newDate.setMonth(newDate.getMonth() + index * 4); // Espacer de 4 mois pour le paiement en 3 fois
|
||||||
|
} else {
|
||||||
|
newDate.setMonth(newDate.getMonth() + index);
|
||||||
|
}
|
||||||
|
return newDate.toISOString().split('T')[0];
|
||||||
|
});
|
||||||
|
setDates(prevDates => ({ ...prevDates, [selectedFrequency]: newDates }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmitDefaultDay = () => {
|
||||||
|
const selectedPlan = paymentPlans.find(plan => plan.frequency === paymentPlansOptions.find(p => p.id === selectedFrequency)?.frequency);
|
||||||
|
if (!selectedPlan) return;
|
||||||
|
|
||||||
|
const updatedData = {
|
||||||
|
...selectedPlan,
|
||||||
|
due_dates: dates[selectedFrequency]
|
||||||
|
};
|
||||||
|
|
||||||
|
handleEdit(selectedPlan.id, updatedData)
|
||||||
|
.then(() => {
|
||||||
|
setPopupMessage(`Mise à jour des dates d'échéances effectuée avec succès`);
|
||||||
|
setPopupVisible(true);
|
||||||
|
setIsDefaultDayModified(false); // Réinitialiser l'état de modification après la soumission
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ name: 'OPTIONS', label: 'Option' },
|
||||||
|
{ name: 'ACTIONS', label: 'Action' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const renderCell = (row, column) => {
|
||||||
|
switch (column) {
|
||||||
|
case 'OPTIONS':
|
||||||
|
return <span className="text-sm font-medium text-gray-900">{row.name}</span>;
|
||||||
|
case 'ACTIONS':
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleActivationChange(row.id);
|
||||||
|
}}
|
||||||
|
className={activeFrequencies.includes(row.id) ? 'text-emerald-500 hover:text-emerald-700' : 'text-orange-500 hover:text-orange-700'}
|
||||||
|
>
|
||||||
|
{activeFrequencies.includes(row.id) ? <Eye className="w-5 h-5" /> : <EyeOff className="w-5 h-5" />}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedPaymentPlan = paymentPlans.find(plan => plan.frequency === paymentPlansOptions.find(p => p.id === selectedFrequency)?.frequency);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center mb-4">
|
||||||
|
<Calendar className="w-6 h-6 text-emerald-500 mr-2" />
|
||||||
|
<h2 className="text-xl font-semibold">Paiement en plusieurs fois</h2>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<Table
|
||||||
|
data={paymentPlansOptions}
|
||||||
|
columns={columns}
|
||||||
|
renderCell={renderCell}
|
||||||
|
isSelectable={true}
|
||||||
|
onRowClick={handleRowClick}
|
||||||
|
selectedRows={selectedFrequency !== null ? [selectedFrequency] : []}
|
||||||
|
/>
|
||||||
|
{selectedFrequency !== null && selectedPaymentPlan && (
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="flex-grow">
|
||||||
|
<InputTextIcon
|
||||||
|
name="defaultDay"
|
||||||
|
type="number"
|
||||||
|
IconItem={Clock}
|
||||||
|
label="Jour d'échéance"
|
||||||
|
value={defaultDay}
|
||||||
|
onChange={handleDefaultDayChange}
|
||||||
|
placeholder="Jour d'échéance"
|
||||||
|
errorMsg=""
|
||||||
|
min={1}
|
||||||
|
max={31}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{isDefaultDayModified && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleSubmitDefaultDay}
|
||||||
|
className="text-emerald-500 hover:text-emerald-700 ml-2 cursor-pointer"
|
||||||
|
style={{ marginTop: '1.75rem' }} // Adjust this value to align with the input
|
||||||
|
>
|
||||||
|
<Check className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<DateTab
|
||||||
|
dates={dates}
|
||||||
|
activeTab={selectedFrequency}
|
||||||
|
handleDateChange={handleDateChange}
|
||||||
|
handleEdit={handleEdit}
|
||||||
|
type={type}
|
||||||
|
paymentPlanId={selectedPaymentPlan.id}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Popup
|
||||||
|
visible={popupVisible}
|
||||||
|
message={popupMessage}
|
||||||
|
onConfirm={() => setPopupVisible(false)}
|
||||||
|
onCancel={() => setPopupVisible(false)}
|
||||||
|
uniqueConfirmButton={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PaymentPlanSelector;
|
||||||
@ -119,7 +119,8 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderInputField = (field, value, onChange, placeholder) => (
|
const renderInputField = (field, value, onChange, placeholder) => (
|
||||||
<div>
|
<div className="flex justify-center space-x-2">
|
||||||
|
<div className="w-full max-w-xs">
|
||||||
<InputText
|
<InputText
|
||||||
name={field}
|
name={field}
|
||||||
type={field === 'amount' ? 'number' : 'text'}
|
type={field === 'amount' ? 'number' : 'text'}
|
||||||
@ -129,6 +130,7 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
|
|||||||
errorMsg={localErrors && localErrors[field] && Array.isArray(localErrors[field]) ? localErrors[field][0] : ''}
|
errorMsg={localErrors && localErrors[field] && Array.isArray(localErrors[field]) ? localErrors[field][0] : ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderDiscountCell = (discount, column) => {
|
const renderDiscountCell = (discount, column) => {
|
||||||
@ -142,7 +144,7 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
|
|||||||
return renderInputField('name', currentData.name, handleChange, 'Libellé de la réduction');
|
return renderInputField('name', currentData.name, handleChange, 'Libellé de la réduction');
|
||||||
case 'REMISE':
|
case 'REMISE':
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex justify-center space-x-2">
|
||||||
{renderInputField('amount', currentData.amount, handleChange,'Montant')}
|
{renderInputField('amount', currentData.amount, handleChange,'Montant')}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@ -270,7 +272,7 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
|
|||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="flex items-center mb-4">
|
<div className="flex items-center mb-4">
|
||||||
<Tag className="w-6 h-6 text-emerald-500 mr-2" />
|
<Tag className="w-6 h-6 text-emerald-500 mr-2" />
|
||||||
<h2 className="text-xl font-semibold">Réductions {type === 0 ? 'd\'inscription' : 'de scolarité'}</h2>
|
<h2 className="text-xl font-semibold">Liste des réductions</h2>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" onClick={handleAddDiscount} className="text-emerald-500 hover:text-emerald-700">
|
<button type="button" onClick={handleAddDiscount} className="text-emerald-500 hover:text-emerald-700">
|
||||||
<Plus className="w-5 h-5" />
|
<Plus className="w-5 h-5" />
|
||||||
|
|||||||
@ -1,9 +1,26 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import FeesSection from '@/components/Structure/Tarification/FeesSection';
|
import FeesSection from '@/components/Structure/Tarification/FeesSection';
|
||||||
import DiscountsSection from '@/components/Structure/Tarification/DiscountsSection';
|
import DiscountsSection from '@/components/Structure/Tarification/DiscountsSection';
|
||||||
import { BE_SCHOOL_FEE_URL, BE_SCHOOL_DISCOUNT_URL } from '@/utils/Url';
|
import PaymentPlanSelector from '@/components/PaymentPlanSelector';
|
||||||
|
import PaymentModeSelector from '@/components/PaymentModeSelector';
|
||||||
|
import { BE_SCHOOL_FEE_URL, BE_SCHOOL_DISCOUNT_URL, BE_SCHOOL_PAYMENT_PLAN_URL } from '@/utils/Url';
|
||||||
|
import { set } from 'lodash';
|
||||||
|
|
||||||
const FeesManagement = ({ registrationDiscounts, setRegistrationDiscounts, tuitionDiscounts, setTuitionDiscounts, registrationFees, setRegistrationFees, tuitionFees, setTuitionFees, handleCreate, handleEdit, handleDelete }) => {
|
const FeesManagement = ({ registrationDiscounts,
|
||||||
|
setRegistrationDiscounts,
|
||||||
|
tuitionDiscounts,
|
||||||
|
setTuitionDiscounts,
|
||||||
|
registrationFees,
|
||||||
|
setRegistrationFees,
|
||||||
|
tuitionFees,
|
||||||
|
setTuitionFees,
|
||||||
|
registrationPaymentPlans,
|
||||||
|
setRegistrationPaymentPlans,
|
||||||
|
tuitionPaymentPlans,
|
||||||
|
setTuitionPaymentPlans,
|
||||||
|
handleCreate,
|
||||||
|
handleEdit,
|
||||||
|
handleDelete }) => {
|
||||||
|
|
||||||
const handleDiscountDelete = (id, type) => {
|
const handleDiscountDelete = (id, type) => {
|
||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
@ -24,9 +41,11 @@ const FeesManagement = ({ registrationDiscounts, setRegistrationDiscounts, tuiti
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-8xl mx-auto p-4 mt-6 space-y-6">
|
<div className="w-full mx-auto p-2 mt-6 space-y-6">
|
||||||
|
<div className="bg-white p-2 rounded-lg shadow-md">
|
||||||
|
<h2 className="text-2xl font-semibold mb-4">Frais d'inscription</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md">
|
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
|
||||||
<FeesSection
|
<FeesSection
|
||||||
fees={registrationFees}
|
fees={registrationFees}
|
||||||
setFees={setRegistrationFees}
|
setFees={setRegistrationFees}
|
||||||
@ -37,7 +56,7 @@ const FeesManagement = ({ registrationDiscounts, setRegistrationDiscounts, tuiti
|
|||||||
type={0}
|
type={0}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md">
|
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
|
||||||
<DiscountsSection
|
<DiscountsSection
|
||||||
discounts={registrationDiscounts}
|
discounts={registrationDiscounts}
|
||||||
setDiscounts={setRegistrationDiscounts}
|
setDiscounts={setRegistrationDiscounts}
|
||||||
@ -48,9 +67,27 @@ const FeesManagement = ({ registrationDiscounts, setRegistrationDiscounts, tuiti
|
|||||||
type={0}
|
type={0}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
|
||||||
|
<PaymentPlanSelector
|
||||||
|
paymentPlans={registrationPaymentPlans}
|
||||||
|
setPaymentPlans={setRegistrationPaymentPlans}
|
||||||
|
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_PAYMENT_PLAN_URL}`, id, updatedData, setRegistrationPaymentPlans)}
|
||||||
|
type={0}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{/* <div className="col-span-1">
|
||||||
|
<PaymentModeSelector
|
||||||
|
formData={formRegistrationData}
|
||||||
|
setFormData={setFormRegistrationData}
|
||||||
|
fieldName="paymentRegistrationMode"
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white p-2 rounded-lg shadow-md">
|
||||||
|
<h2 className="text-2xl font-semibold mb-4">Frais de scolarité</h2>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md">
|
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
|
||||||
<FeesSection
|
<FeesSection
|
||||||
fees={tuitionFees}
|
fees={tuitionFees}
|
||||||
setFees={setTuitionFees}
|
setFees={setTuitionFees}
|
||||||
@ -61,7 +98,7 @@ const FeesManagement = ({ registrationDiscounts, setRegistrationDiscounts, tuiti
|
|||||||
type={1}
|
type={1}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white p-6 rounded-lg shadow-md">
|
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
|
||||||
<DiscountsSection
|
<DiscountsSection
|
||||||
discounts={tuitionDiscounts}
|
discounts={tuitionDiscounts}
|
||||||
setDiscounts={setTuitionDiscounts}
|
setDiscounts={setTuitionDiscounts}
|
||||||
@ -72,6 +109,15 @@ const FeesManagement = ({ registrationDiscounts, setRegistrationDiscounts, tuiti
|
|||||||
type={1}
|
type={1}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
|
||||||
|
<PaymentPlanSelector
|
||||||
|
paymentPlans={tuitionPaymentPlans}
|
||||||
|
setPaymentPlans={setTuitionPaymentPlans}
|
||||||
|
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_PAYMENT_PLAN_URL}`, id, updatedData, setRegistrationPaymentPlans)}
|
||||||
|
type={1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -120,7 +120,8 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderInputField = (field, value, onChange, placeholder) => (
|
const renderInputField = (field, value, onChange, placeholder) => (
|
||||||
<div className="flex justify-center items-center h-full">
|
<div className="flex justify-center space-x-2">
|
||||||
|
<div className="w-full max-w-xs">
|
||||||
<InputText
|
<InputText
|
||||||
name={field}
|
name={field}
|
||||||
type={field === 'base_amount' ? 'number' : 'text'}
|
type={field === 'base_amount' ? 'number' : 'text'}
|
||||||
@ -130,6 +131,7 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
|
|||||||
errorMsg={getError(field)}
|
errorMsg={getError(field)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderFeeCell = (fee, column) => {
|
const renderFeeCell = (fee, column) => {
|
||||||
@ -259,7 +261,7 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
|
|||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="flex items-center mb-4">
|
<div className="flex items-center mb-4">
|
||||||
<CreditCard className="w-6 h-6 text-emerald-500 mr-2" />
|
<CreditCard className="w-6 h-6 text-emerald-500 mr-2" />
|
||||||
<h2 className="text-xl font-semibold">{labelTypeFrais}</h2>
|
<h2 className="text-xl font-semibold">Liste des frais</h2>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" onClick={handleAddFee} className="text-emerald-500 hover:text-emerald-700">
|
<button type="button" onClick={handleAddFee} className="text-emerald-500 hover:text-emerald-700">
|
||||||
<Plus className="w-5 h-5" />
|
<Plus className="w-5 h-5" />
|
||||||
|
|||||||
@ -25,8 +25,8 @@ const Table = ({ data, columns, renderCell, itemsPerPage = 0, currentPage, total
|
|||||||
key={rowIndex}
|
key={rowIndex}
|
||||||
className={`
|
className={`
|
||||||
${isSelectable ? 'cursor-pointer' : ''}
|
${isSelectable ? 'cursor-pointer' : ''}
|
||||||
${selectedRows?.includes(row.id) ? 'bg-emerald-500 text-white' : rowIndex % 2 === 0 ? `${defaultTheme}` : ''}
|
${selectedRows?.includes(row.id) ? 'bg-emerald-300 text-white' : rowIndex % 2 === 0 ? `${defaultTheme}` : ''}
|
||||||
${isSelectable ? 'hover:bg-emerald-600' : ''}
|
${isSelectable ? 'hover:bg-emerald-200' : ''}
|
||||||
`}
|
`}
|
||||||
onClick={() => isSelectable && onRowClick && onRowClick(row)}
|
onClick={() => isSelectable && onRowClick && onRowClick(row)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export const BE_SUBSCRIPTION_REGISTRATIONFILE_GROUPS_URL = `${BASE_URL}/Subscrip
|
|||||||
export const BE_SUBSCRIPTION_LAST_GUARDIAN_ID_URL = `${BASE_URL}/Subscriptions/lastGuardianId`
|
export const BE_SUBSCRIPTION_LAST_GUARDIAN_ID_URL = `${BASE_URL}/Subscriptions/lastGuardianId`
|
||||||
|
|
||||||
|
|
||||||
//GESTION ENSEIGNANT
|
//GESTION ECOLE
|
||||||
export const BE_SCHOOL_SPECIALITY_URL = `${BASE_URL}/School/speciality`
|
export const BE_SCHOOL_SPECIALITY_URL = `${BASE_URL}/School/speciality`
|
||||||
export const BE_SCHOOL_SPECIALITIES_URL = `${BASE_URL}/School/specialities`
|
export const BE_SCHOOL_SPECIALITIES_URL = `${BASE_URL}/School/specialities`
|
||||||
export const BE_SCHOOL_SCHOOLCLASS_URL = `${BASE_URL}/School/schoolClass`
|
export const BE_SCHOOL_SCHOOLCLASS_URL = `${BASE_URL}/School/schoolClass`
|
||||||
@ -37,6 +37,8 @@ export const BE_SCHOOL_FEE_URL = `${BASE_URL}/School/fee`;
|
|||||||
export const BE_SCHOOL_FEES_URL = `${BASE_URL}/School/fees`;
|
export const BE_SCHOOL_FEES_URL = `${BASE_URL}/School/fees`;
|
||||||
export const BE_SCHOOL_DISCOUNT_URL = `${BASE_URL}/School/discount`;
|
export const BE_SCHOOL_DISCOUNT_URL = `${BASE_URL}/School/discount`;
|
||||||
export const BE_SCHOOL_DISCOUNTS_URL = `${BASE_URL}/School/discounts`;
|
export const BE_SCHOOL_DISCOUNTS_URL = `${BASE_URL}/School/discounts`;
|
||||||
|
export const BE_SCHOOL_PAYMENT_PLAN_URL = `${BASE_URL}/School/paymentPlan`;
|
||||||
|
export const BE_SCHOOL_PAYMENT_PLANS_URL = `${BASE_URL}/School/paymentPlans`;
|
||||||
|
|
||||||
// GESTION MESSAGERIE
|
// GESTION MESSAGERIE
|
||||||
export const BE_GESTIONMESSAGERIE_MESSAGES_URL = `${BASE_URL}/GestionMessagerie/messagerie`
|
export const BE_GESTIONMESSAGERIE_MESSAGES_URL = `${BASE_URL}/GestionMessagerie/messagerie`
|
||||||
|
|||||||
Reference in New Issue
Block a user