fix: Remplacement des enum par des modèles pour les payementModes et les

payementPlans
This commit is contained in:
N3WT DE COMPET
2025-05-18 10:45:00 +02:00
parent 00f7bfde4a
commit 7fe53465ac
12 changed files with 198 additions and 401 deletions

View File

@ -67,16 +67,16 @@ class EstablishmentSerializer(serializers.ModelSerializer):
return list(Discount.objects.filter(establishment=obj).distinct().values_list('name', flat=True)) return list(Discount.objects.filter(establishment=obj).distinct().values_list('name', flat=True))
def get_active_payment_mode_count(self, obj): def get_active_payment_mode_count(self, obj):
return PaymentMode.objects.filter(establishment=obj, is_active=True).distinct().count() return PaymentMode.objects.filter(establishment=obj).distinct().count()
def get_active_payment_modes(self, obj): def get_active_payment_modes(self, obj):
return list(PaymentMode.objects.filter(establishment=obj, is_active=True).distinct().values_list('mode', flat=True)) return list(PaymentMode.objects.filter(establishment=obj).distinct().values_list('mode', flat=True))
def get_active_payment_plan_count(self, obj): def get_active_payment_plan_count(self, obj):
return PaymentPlan.objects.filter(establishment=obj, is_active=True).distinct().count() return PaymentPlan.objects.filter(establishment=obj).distinct().count()
def get_active_payment_plans(self, obj): def get_active_payment_plans(self, obj):
return list(PaymentPlan.objects.filter(establishment=obj, is_active=True).distinct().values_list('frequency', flat=True)) return list(PaymentPlan.objects.filter(establishment=obj).distinct().values_list('plan_type', flat=True))
def get_file_group_count(self, obj): def get_file_group_count(self, obj):
return RegistrationFileGroup.objects.filter(establishment=obj).distinct().count() return RegistrationFileGroup.objects.filter(establishment=obj).distinct().count()

View File

@ -157,16 +157,15 @@ class Command(BaseCommand):
self.stdout.write(self.style.ERROR(f'Error in data for discount: {serializer.errors}')) self.stdout.write(self.style.ERROR(f'Error in data for discount: {serializer.errors}'))
def init_payment_modes(self): def init_payment_modes(self):
modes = [PaymentModeType.SEPA, PaymentModeType.TRANSFER, PaymentModeType.CHECK, PaymentModeType.CASH] modes = list(PaymentModeType.objects.filter(code__in=["SEPA", "TRANSFER", "CHECK", "CASH"]))
types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE] types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]
for establishment in self.establishments: for establishment in self.establishments:
for mode in modes: for mode in modes:
for type in types: for type in types:
payment_mode_data = { payment_mode_data = {
"mode": mode, "mode": mode.pk,
"type": type, "type": type,
"is_active": random.choice([True, False]),
"establishment": establishment.id "establishment": establishment.id
} }
@ -178,7 +177,7 @@ class Command(BaseCommand):
self.stdout.write(self.style.ERROR(f'Error in data for payment mode: {serializer.errors}')) self.stdout.write(self.style.ERROR(f'Error in data for payment mode: {serializer.errors}'))
def init_payment_plans(self): def init_payment_plans(self):
frequencies = [PaymentPlanType.ONE_TIME, PaymentPlanType.THREE_TIMES, PaymentPlanType.TEN_TIMES, PaymentPlanType.TWELVE_TIMES] frequencies = list(PaymentPlanType.objects.filter(code__in=["ONE_TIME", "THREE_TIMES", "TEN_TIMES", "TWELVE_TIMES"]))
types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE] types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]
current_date = timezone.now().date() current_date = timezone.now().date()
@ -186,9 +185,8 @@ class Command(BaseCommand):
for frequency in frequencies: for frequency in frequencies:
for type in types: for type in types:
payment_plan_data = { payment_plan_data = {
"frequency": frequency, "frequency": frequency.pk,
"type": type, "type": type,
"is_active": random.choice([True, False]),
"establishment": establishment.id, "establishment": establishment.id,
"due_dates": self.generate_due_dates(frequency, current_date) "due_dates": self.generate_due_dates(frequency, current_date)
} }
@ -201,13 +199,13 @@ class Command(BaseCommand):
self.stdout.write(self.style.ERROR(f'Error in data for payment plan: {serializer.errors}')) self.stdout.write(self.style.ERROR(f'Error in data for payment plan: {serializer.errors}'))
def generate_due_dates(self, frequency, start_date): def generate_due_dates(self, frequency, start_date):
if frequency == PaymentPlanType.ONE_TIME: if frequency.code == "ONE_TIME":
return [start_date + relativedelta(months=1)] return [start_date + relativedelta(months=1)]
elif frequency == PaymentPlanType.THREE_TIMES: elif frequency.code == "THREE_TIMES":
return [start_date + relativedelta(months=1+4*i) for i in range(3)] return [start_date + relativedelta(months=1+4*i) for i in range(3)]
elif frequency == PaymentPlanType.TEN_TIMES: elif frequency.code == "TEN_TIMES":
return [start_date + relativedelta(months=1+i) for i in range(10)] return [start_date + relativedelta(months=1+i) for i in range(10)]
elif frequency == PaymentPlanType.TWELVE_TIMES: elif frequency.code == "TWELVE_TIMES":
return [start_date + relativedelta(months=1+i) for i in range(12)] return [start_date + relativedelta(months=1+i) for i in range(12)]
def init_specialities(self): def init_specialities(self):

View File

@ -67,12 +67,6 @@ class Planning(models.Model):
def __str__(self): def __str__(self):
return f'Planning for {self.level} of {self.school_class.atmosphere_name}' return f'Planning for {self.level} of {self.school_class.atmosphere_name}'
class PaymentPlanType(models.IntegerChoices):
ONE_TIME = 1, '1 fois'
THREE_TIMES = 3, '3 fois'
TEN_TIMES = 10, '10 fois'
TWELVE_TIMES = 12, '12 fois'
class DiscountType(models.IntegerChoices): class DiscountType(models.IntegerChoices):
CURRENCY = 0, 'Currency' CURRENCY = 0, 'Currency'
PERCENT = 1, 'Percent' PERCENT = 1, 'Percent'
@ -81,11 +75,20 @@ class FeeType(models.IntegerChoices):
REGISTRATION_FEE = 0, 'Registration Fee' REGISTRATION_FEE = 0, 'Registration Fee'
TUITION_FEE = 1, 'Tuition Fee' TUITION_FEE = 1, 'Tuition Fee'
class PaymentModeType(models.IntegerChoices): class PaymentPlanType(models.Model):
SEPA = 1, 'Prélèvement SEPA' code = models.CharField(max_length=50, unique=True)
TRANSFER = 2, 'Virement' label = models.CharField(max_length=255)
CHECK = 3, 'Chèque'
CASH = 4, 'Espèce' def __str__(self):
return self.label
class PaymentModeType(models.Model):
code = models.CharField(max_length=50, unique=True)
label = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.label
class Discount(models.Model): class Discount(models.Model):
name = models.CharField(max_length=255, null=True, blank=True) name = models.CharField(max_length=255, null=True, blank=True)
@ -112,23 +115,21 @@ class Fee(models.Model):
return self.name return self.name
class PaymentPlan(models.Model): class PaymentPlan(models.Model):
frequency = models.IntegerField(choices=PaymentPlanType.choices, default=PaymentPlanType.ONE_TIME) plan_type = models.ForeignKey(PaymentPlanType, on_delete=models.PROTECT, related_name='payment_plans')
due_dates = ArrayField(models.DateField(), blank=True) due_dates = ArrayField(models.DateField(), null=True, 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)
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_plans') establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_plans')
def __str__(self): def __str__(self):
return f"{self.get_frequency_display()} - {self.get_type_display()}" return f"{self.plan_type.label} - {self.get_type_display()}"
class PaymentMode(models.Model): class PaymentMode(models.Model):
mode = models.IntegerField(choices=PaymentModeType.choices, default=PaymentModeType.SEPA) mode = models.ForeignKey(PaymentModeType, on_delete=models.PROTECT, related_name='payment_modes')
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)
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_modes') establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_modes')
def __str__(self): def __str__(self):
return f"{self.get_mode_display()} - {self.get_type_display()}" return f"{self.mode.label} - {self.get_type_display()}"
class Domain(models.Model): class Domain(models.Model):
name = models.CharField(max_length=255) name = models.CharField(max_length=255)

View File

@ -2,11 +2,10 @@ import json
import os import os
from django.db.models.signals import post_migrate from django.db.models.signals import post_migrate
from django.dispatch import receiver from django.dispatch import receiver
from Establishment.models import Establishment from School.models import Domain, Category, Competency, PaymentModeType, PaymentPlanType
from School.models import Domain, Category, Competency, EstablishmentCompetency
@receiver(post_migrate) @receiver(post_migrate)
def load_json_data(sender, **kwargs): def school_post_migrate(sender, **kwargs):
if sender.name != "School": if sender.name != "School":
return return
@ -47,4 +46,23 @@ def load_json_data(sender, **kwargs):
level=competency_data.get('niveau'), level=competency_data.get('niveau'),
category=category category=category
) )
print(f"Données importées depuis : {json_file_path}") print(f"Données importées depuis : {json_file_path}")
payment_mode_types = [
{"code": "SEPA", "label": "Prélèvement SEPA"},
{"code": "TRANSFER", "label": "Virement"},
{"code": "CHECK", "label": "Chèque"},
{"code": "CASH", "label": "Espèce"},
]
for mode in payment_mode_types:
PaymentModeType.objects.get_or_create(code=mode["code"], defaults={"label": mode["label"]})
# ... après la création des PaymentModeType ...
payment_plan_types = [
{"code": "ONE_TIME", "label": "1 fois"},
{"code": "THREE_TIMES", "label": "3 fois"},
{"code": "TEN_TIMES", "label": "10 fois"},
{"code": "TWELVE_TIMES", "label": "12 fois"},
]
for plan in payment_plan_types:
PaymentPlanType.objects.get_or_create(code=plan["code"], defaults={"label": plan["label"]})

View File

@ -531,7 +531,6 @@ class CompetencyListCreateView(APIView):
category__domain__cycle=cycle category__domain__cycle=cycle
).distinct() ).distinct()
serializer = CompetencySerializer(competencies_list, many=True) serializer = CompetencySerializer(competencies_list, many=True)
print(f'len : {competencies_list.count()}')
return JsonResponse(serializer.data, safe=False) return JsonResponse(serializer.data, safe=False)
def post(self, request): def post(self, request):

View File

@ -247,10 +247,10 @@ class RegistrationForm(models.Model):
blank=True) blank=True)
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='register_forms') establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='register_forms')
registration_payment = models.IntegerField(choices=PaymentModeType.choices, null=True, blank=True) registration_payment = models.ForeignKey('School.PaymentModeType', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_modes_forms')
tuition_payment = models.IntegerField(choices=PaymentModeType.choices, null=True, blank=True) tuition_payment = models.ForeignKey('School.PaymentModeType', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_modes_forms')
registration_payment_plan = models.IntegerField(choices=PaymentPlanType.choices, null=True, blank=True) registration_payment_plan = models.ForeignKey('School.PaymentPlanType', on_delete=models.SET_NULL, null=True, blank=True, related_name='registration_payment_plans_forms')
tuition_payment_plan = models.IntegerField(choices=PaymentPlanType.choices, null=True, blank=True) tuition_payment_plan = models.ForeignKey('School.PaymentPlanType', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_plans_forms')
def __str__(self): def __str__(self):
return "RF_" + self.student.last_name + "_" + self.student.first_name return "RF_" + self.student.last_name + "_" + self.student.first_name

View File

@ -1,5 +1,4 @@
from Subscriptions.models import RegistrationForm, Student from Subscriptions.models import RegistrationForm, Student
from School.models import PaymentModeType, PaymentPlanType
from django import template from django import template
import re import re
@ -8,22 +7,30 @@ register = template.Library()
@register.filter @register.filter
def getRegistrationPaymentPlan(pk): def getRegistrationPaymentPlan(pk):
registerForm = RegistrationForm.objects.get(student=pk) registerForm = RegistrationForm.objects.get(student=pk)
return PaymentPlanType(registerForm.registration_payment_plan).label if registerForm.registration_payment_plan:
return registerForm.registration_payment_plan.label
return ""
@register.filter @register.filter
def getTuitionPaymentPlan(pk): def getTuitionPaymentPlan(pk):
registerForm = RegistrationForm.objects.get(student=pk) registerForm = RegistrationForm.objects.get(student=pk)
return PaymentPlanType(registerForm.tuition_payment_plan).label if registerForm.tuition_payment_plan:
return registerForm.tuition_payment_plan.label
return ""
@register.filter @register.filter
def getRegistrationPaymentMethod(pk): def getRegistrationPaymentMethod(pk):
registerForm = RegistrationForm.objects.get(student=pk) registerForm = RegistrationForm.objects.get(student=pk)
return PaymentModeType(registerForm.registration_payment).label if registerForm.registration_payment:
return registerForm.registration_payment.label
return ""
@register.filter @register.filter
def getTuitionPaymentMethod(pk): def getTuitionPaymentMethod(pk):
registerForm = RegistrationForm.objects.get(student=pk) registerForm = RegistrationForm.objects.get(student=pk)
return PaymentModeType(registerForm.tuition_payment).label if registerForm.tuition_payment:
return registerForm.tuition_payment.label
return ""
@register.filter @register.filter
def getStudentLevel(pk): def getStudentLevel(pk):

View File

@ -230,10 +230,7 @@ export default function InscriptionFormShared({
const handleRegistrationPaymentModes = () => { const handleRegistrationPaymentModes = () => {
fetchRegistrationPaymentModes(selectedEstablishmentId) fetchRegistrationPaymentModes(selectedEstablishmentId)
.then((data) => { .then((data) => {
const activePaymentModes = data.filter( setRegistrationPaymentModes(data);
(mode) => mode.is_active === true
);
setRegistrationPaymentModes(activePaymentModes);
}) })
.catch((error) => .catch((error) =>
logger.error('Error fetching registration payment modes:', error) logger.error('Error fetching registration payment modes:', error)
@ -243,10 +240,7 @@ export default function InscriptionFormShared({
const handleTuitionPaymentModes = () => { const handleTuitionPaymentModes = () => {
fetchTuitionPaymentModes(selectedEstablishmentId) fetchTuitionPaymentModes(selectedEstablishmentId)
.then((data) => { .then((data) => {
const activePaymentModes = data.filter( setTuitionPaymentModes(data);
(mode) => mode.is_active === true
);
setTuitionPaymentModes(activePaymentModes);
}) })
.catch((error) => .catch((error) =>
logger.error('Error fetching tuition payment modes:', error) logger.error('Error fetching tuition payment modes:', error)
@ -256,10 +250,7 @@ export default function InscriptionFormShared({
const handleRegistrationPaymentPlans = () => { const handleRegistrationPaymentPlans = () => {
fetchRegistrationPaymentPlans(selectedEstablishmentId) fetchRegistrationPaymentPlans(selectedEstablishmentId)
.then((data) => { .then((data) => {
const activePaymentPlans = data.filter( setRegistrationPaymentPlans(data);
(mode) => mode.is_active === true
);
setRegistrationPaymentPlans(activePaymentPlans);
}) })
.catch((error) => .catch((error) =>
logger.error('Error fetching registration payment plans:', error) logger.error('Error fetching registration payment plans:', error)
@ -269,10 +260,7 @@ export default function InscriptionFormShared({
const handleTuitionnPaymentPlans = () => { const handleTuitionnPaymentPlans = () => {
fetchTuitionPaymentPlans(selectedEstablishmentId) fetchTuitionPaymentPlans(selectedEstablishmentId)
.then((data) => { .then((data) => {
const activePaymentPlans = data.filter( setTuitionPaymentPlans(data);
(mode) => mode.is_active === true
);
setTuitionPaymentPlans(activePaymentPlans);
}) })
.catch((error) => .catch((error) =>
logger.error('Error fetching registration tuition plans:', error) logger.error('Error fetching registration tuition plans:', error)

View File

@ -31,9 +31,9 @@ export default function PaymentMethodSelector({
const paymentPlansOptions = [ const paymentPlansOptions = [
{ id: 1, name: '1 fois' }, { id: 1, name: '1 fois' },
{ id: 3, name: '3 fois' }, { id: 2, name: '3 fois' },
{ id: 10, name: '10 fois' }, { id: 3, name: '10 fois' },
{ id: 12, name: '12 fois' }, { id: 4, name: '12 fois' },
]; ];
const getError = (field) => { const getError = (field) => {
@ -105,7 +105,7 @@ export default function PaymentMethodSelector({
items={paymentPlansOptions items={paymentPlansOptions
.filter((option) => .filter((option) =>
registrationPaymentPlans.some( registrationPaymentPlans.some(
(plan) => plan.frequency === option.id (plan) => plan.plan_type === option.id
) )
) )
.map((option) => ({ .map((option) => ({
@ -167,7 +167,7 @@ export default function PaymentMethodSelector({
required required
items={paymentPlansOptions items={paymentPlansOptions
.filter((option) => .filter((option) =>
tuitionPaymentPlans.some((plan) => plan.frequency === option.id) tuitionPaymentPlans.some((plan) => plan.plan_type === option.id)
) )
.map((option) => ({ .map((option) => ({
id: option.id, id: option.id,

View File

@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { DollarSign } from 'lucide-react'; import { DollarSign } from 'lucide-react';
import { useEstablishment } from '@/context/EstablishmentContext';
const paymentModesOptions = [ const paymentModesOptions = [
{ id: 1, name: 'Prélèvement SEPA' }, { id: 1, name: 'Prélèvement SEPA' },
@ -11,36 +12,31 @@ const paymentModesOptions = [
const PaymentModeSelector = ({ const PaymentModeSelector = ({
paymentModes, paymentModes,
setPaymentModes, setPaymentModes,
handleEdit, handleCreate,
handleDelete,
type, type,
}) => { }) => {
const [activePaymentModes, setActivePaymentModes] = useState([]); const [activePaymentModes, setActivePaymentModes] = useState([]);
const { selectedEstablishmentId } = useEstablishment();
useEffect(() => { useEffect(() => {
// Initialiser activePaymentModes avec les modes dont is_active est à true const activeModes = paymentModes.map((mode) => mode.mode);
const activeModes = paymentModes
.filter((mode) => mode.is_active)
.map((mode) => mode.mode);
setActivePaymentModes(activeModes); setActivePaymentModes(activeModes);
}, [paymentModes]); }, [paymentModes]);
const handleModeToggle = (modeId) => { const handleModeToggle = (modeId) => {
setActivePaymentModes((prevActiveModes) => { const updatedMode = paymentModes.find((mode) => mode.mode === modeId);
const newActiveModes = prevActiveModes.includes(modeId) const isActive = !!updatedMode;
? prevActiveModes.filter((mode) => mode !== modeId)
: [...prevActiveModes, modeId];
// Mettre à jour le mode de paiement dans le backend if (!isActive) {
const updatedMode = paymentModes.find((mode) => mode.mode === modeId); handleCreate({
if (updatedMode) { mode: modeId,
handleEdit(updatedMode.id, { type,
...updatedMode, establishment: selectedEstablishmentId,
is_active: !updatedMode.is_active, });
}); } else {
} handleDelete(updatedMode.id, null);
}
return newActiveModes;
});
}; };
return ( return (
@ -55,7 +51,7 @@ const PaymentModeSelector = ({
key={mode.id} key={mode.id}
type="button" type="button"
onClick={() => handleModeToggle(mode.id)} onClick={() => handleModeToggle(mode.id)}
className={`p-4 rounded-lg shadow-md text-center text-gray-700' ${ className={`p-4 rounded-lg shadow-md text-center text-gray-700 ${
activePaymentModes.includes(mode.id) activePaymentModes.includes(mode.id)
? 'bg-emerald-100' ? 'bg-emerald-100'
: 'bg-stone-50' : 'bg-stone-50'

View File

@ -1,281 +1,62 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Calendar, Eye, EyeOff, Clock, Check } from 'lucide-react'; import { Calendar } from 'lucide-react';
import Table from '@/components/Table'; import Table from '@/components/Table';
import DateTab from '@/components/DateTab';
import InputTextIcon from '@/components/InputTextIcon';
import Popup from '@/components/Popup'; import Popup from '@/components/Popup';
import logger from '@/utils/logger'; import logger from '@/utils/logger';
import { useEstablishment } from '@/context/EstablishmentContext';
import CheckBox from '@/components/CheckBox';
const paymentPlansOptions = [ const paymentPlansOptions = [
{ id: 0, name: '1 fois', frequency: 1 }, { id: 1, name: '1 fois', frequency: 1 },
{ id: 1, name: '3 fois', frequency: 3 }, { id: 2, name: '3 fois', frequency: 3 },
{ id: 2, name: '10 fois', frequency: 10 }, { id: 3, name: '10 fois', frequency: 10 },
{ id: 3, name: '12 fois', frequency: 12 }, { id: 4, name: '12 fois', frequency: 12 },
]; ];
const PaymentPlanSelector = ({ const PaymentPlanSelector = ({
paymentPlans, paymentPlans,
setPaymentPlans, handleCreate,
handleEdit, handleDelete,
type, type,
}) => { }) => {
const [dates, setDates] = useState({});
const [selectedFrequency, setSelectedFrequency] = useState(null);
const [activeFrequencies, setActiveFrequencies] = useState([]);
const [defaultDay, setDefaultDay] = useState('-');
const [isDefaultDayModified, setIsDefaultDayModified] = useState(false);
const [popupVisible, setPopupVisible] = useState(false); const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState(''); const [popupMessage, setPopupMessage] = useState('');
const [errorMsg, setErrorMsg] = useState(''); const { selectedEstablishmentId } = useEstablishment();
const [resetModifiedDates, setResetModifiedDates] = useState(false); const [checkedPlans, setCheckedPlans] = useState([]);
// Vérifie si un plan existe pour ce type (par id)
const isChecked = (planOption) => checkedPlans.includes(planOption.id);
// Création ou suppression du plan
const handlePlanToggle = (planOption) => {
const updatedPlan = paymentPlans.find(
(plan) => plan.plan_type === planOption.id
);
if (isChecked(planOption)) {
setCheckedPlans((prev) => prev.filter((id) => id !== planOption.id));
handleDelete(updatedPlan.id, null);
} else {
setCheckedPlans((prev) => [...prev, planOption.id]);
handleCreate({
plan_type: planOption.id,
type,
establishment: selectedEstablishmentId,
});
}
};
useEffect(() => { useEffect(() => {
if (paymentPlans && paymentPlans.length > 0) { if (paymentPlans && paymentPlans.length > 0) {
const activePlans = paymentPlans.filter((plan) => plan.is_active); setCheckedPlans(
const frequencies = activePlans paymentPlans.map((plan) =>
.map((plan) => { typeof plan.plan_type === 'object'
const paymentPlanOption = paymentPlansOptions.find( ? plan.plan_type.id
(p) => p.frequency === plan.frequency : plan.plan_type
); )
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]); }, [paymentPlans]);
useEffect(() => {
updateDefaultDay();
}, [dates, selectedFrequency]);
const updateDefaultDay = () => {
const currentDates = dates[selectedFrequency];
if (currentDates && currentDates.length > 0) {
const days = currentDates.map((date) => new Date(date).getDate());
const allSameDay = days.every((day) => day === days[0]);
if (allSameDay) {
setDefaultDay(days[0]);
} else {
setDefaultDay('-');
setIsDefaultDayModified(false);
}
} else {
setDefaultDay('-');
}
};
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) => {
logger.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();
// Validate defaultDay
const day =
typeof defaultDay === 'number' &&
defaultDay >= 1 &&
defaultDay <= 31
? defaultDay
: 1; // Fallback to 1 if defaultDay is invalid
newDate.setDate(day);
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 value = e.target.value;
if (value === '') {
setDefaultDay('-');
setErrorMsg('');
setIsDefaultDayModified(false);
return;
}
const day = parseInt(value, 10);
setDefaultDay(day);
if (day < 1 || day > 31) {
setErrorMsg('Le jour doit être compris entre 1 et 31.');
setIsDefaultDayModified(false);
return;
}
setErrorMsg('');
setIsDefaultDayModified(true);
setResetModifiedDates(true);
setTimeout(() => setResetModifiedDates(false), 0);
// Mettre à jour les dates d'échéance en fonction du jour sélectionné
const updatedDates = dates[selectedFrequency].map((date) => {
const newDate = new Date(date);
newDate.setDate(day);
return newDate.toISOString().split('T')[0];
});
setDates((prevDates) => ({
...prevDates,
[selectedFrequency]: updatedDates,
}));
};
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);
})
.catch((error) => {
logger.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 ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center mb-4"> <div className="flex items-center mb-4">
@ -285,49 +66,34 @@ const PaymentPlanSelector = ({
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<Table <Table
data={paymentPlansOptions} data={paymentPlansOptions}
columns={columns} columns={[
renderCell={renderCell} { name: 'OPTION', label: 'Option' },
isSelectable={true} { name: 'ACTIF', label: 'Actif' },
onRowClick={handleRowClick} ]}
selectedRows={selectedFrequency !== null ? [selectedFrequency] : []} renderCell={(row, column) => {
switch (column) {
case 'OPTION':
return (
<span className="text-sm font-medium text-gray-900">
{row.name}
</span>
);
case 'ACTIF':
return (
<CheckBox
item={{ id: row.id }}
formData={{ checked: isChecked(row) }}
handleChange={() => handlePlanToggle(row)}
fieldName="checked"
itemLabelFunc={() => ''}
horizontal={true}
/>
);
default:
return null;
}
}}
/> />
{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={errorMsg}
/>
</div>
{isDefaultDayModified && defaultDay && (
<button
type="button"
onClick={handleSubmitDefaultDay}
className="text-emerald-500 hover:text-emerald-700 ml-2 cursor-pointer"
style={{ marginTop: errorMsg ? '3.5rem' : '1.75rem' }}
>
<Check className="w-5 h-5" />
</button>
)}
</div>
<DateTab
dates={dates}
activeTab={selectedFrequency}
handleDateChange={handleDateChange}
handleEdit={handleEdit}
type={type}
paymentPlanId={selectedPaymentPlan.id}
resetModifiedDates={resetModifiedDates}
/>
</div>
)}
</div> </div>
<Popup <Popup
isOpen={popupVisible} isOpen={popupVisible}

View File

@ -116,11 +116,17 @@ const FeesManagement = ({
<PaymentPlanSelector <PaymentPlanSelector
paymentPlans={registrationPaymentPlans} paymentPlans={registrationPaymentPlans}
setPaymentPlans={setRegistrationPaymentPlans} setPaymentPlans={setRegistrationPaymentPlans}
handleEdit={(id, updatedData) => handleCreate={(newData) =>
handleEdit( handleCreate(
`${BE_SCHOOL_PAYMENT_PLANS_URL}`,
newData,
setRegistrationPaymentPlans
)
}
handleDelete={(id) =>
handleDelete(
`${BE_SCHOOL_PAYMENT_PLANS_URL}`, `${BE_SCHOOL_PAYMENT_PLANS_URL}`,
id, id,
updatedData,
setRegistrationPaymentPlans setRegistrationPaymentPlans
) )
} }
@ -131,11 +137,17 @@ const FeesManagement = ({
<PaymentModeSelector <PaymentModeSelector
paymentModes={registrationPaymentModes} paymentModes={registrationPaymentModes}
setPaymentModes={setRegistrationPaymentModes} setPaymentModes={setRegistrationPaymentModes}
handleEdit={(id, updatedData) => handleCreate={(newData) =>
handleEdit( handleCreate(
`${BE_SCHOOL_PAYMENT_MODES_URL}`,
newData,
setRegistrationPaymentModes
)
}
handleDelete={(id) =>
handleDelete(
`${BE_SCHOOL_PAYMENT_MODES_URL}`, `${BE_SCHOOL_PAYMENT_MODES_URL}`,
id, id,
updatedData,
setRegistrationPaymentModes setRegistrationPaymentModes
) )
} }
@ -200,12 +212,18 @@ const FeesManagement = ({
<PaymentPlanSelector <PaymentPlanSelector
paymentPlans={tuitionPaymentPlans} paymentPlans={tuitionPaymentPlans}
setPaymentPlans={setTuitionPaymentPlans} setPaymentPlans={setTuitionPaymentPlans}
handleEdit={(id, updatedData) => handleCreate={(newData) =>
handleEdit( handleCreate(
`${BE_SCHOOL_PAYMENT_PLANS_URL}`,
newData,
setTuitionPaymentPlans
)
}
handleDelete={(id) =>
handleDelete(
`${BE_SCHOOL_PAYMENT_PLANS_URL}`, `${BE_SCHOOL_PAYMENT_PLANS_URL}`,
id, id,
updatedData, setTuitionPaymentPlans
setRegistrationPaymentPlans
) )
} }
type={1} type={1}
@ -215,11 +233,17 @@ const FeesManagement = ({
<PaymentModeSelector <PaymentModeSelector
paymentModes={tuitionPaymentModes} paymentModes={tuitionPaymentModes}
setPaymentModes={setTuitionPaymentModes} setPaymentModes={setTuitionPaymentModes}
handleEdit={(id, updatedData) => handleCreate={(newData) =>
handleEdit( handleCreate(
`${BE_SCHOOL_PAYMENT_MODES_URL}`,
newData,
setTuitionPaymentModes
)
}
handleDelete={(id) =>
handleDelete(
`${BE_SCHOOL_PAYMENT_MODES_URL}`, `${BE_SCHOOL_PAYMENT_MODES_URL}`,
id, id,
updatedData,
setTuitionPaymentModes setTuitionPaymentModes
) )
} }