mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
fix: Remplacement des enum par des modèles pour les payementModes et les
payementPlans
This commit is contained in:
@ -67,16 +67,16 @@ class EstablishmentSerializer(serializers.ModelSerializer):
|
||||
return list(Discount.objects.filter(establishment=obj).distinct().values_list('name', flat=True))
|
||||
|
||||
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):
|
||||
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):
|
||||
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):
|
||||
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):
|
||||
return RegistrationFileGroup.objects.filter(establishment=obj).distinct().count()
|
||||
|
||||
@ -157,16 +157,15 @@ class Command(BaseCommand):
|
||||
self.stdout.write(self.style.ERROR(f'Error in data for discount: {serializer.errors}'))
|
||||
|
||||
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]
|
||||
|
||||
for establishment in self.establishments:
|
||||
for mode in modes:
|
||||
for type in types:
|
||||
payment_mode_data = {
|
||||
"mode": mode,
|
||||
"mode": mode.pk,
|
||||
"type": type,
|
||||
"is_active": random.choice([True, False]),
|
||||
"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}'))
|
||||
|
||||
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]
|
||||
current_date = timezone.now().date()
|
||||
|
||||
@ -186,9 +185,8 @@ class Command(BaseCommand):
|
||||
for frequency in frequencies:
|
||||
for type in types:
|
||||
payment_plan_data = {
|
||||
"frequency": frequency,
|
||||
"frequency": frequency.pk,
|
||||
"type": type,
|
||||
"is_active": random.choice([True, False]),
|
||||
"establishment": establishment.id,
|
||||
"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}'))
|
||||
|
||||
def generate_due_dates(self, frequency, start_date):
|
||||
if frequency == PaymentPlanType.ONE_TIME:
|
||||
if frequency.code == "ONE_TIME":
|
||||
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)]
|
||||
elif frequency == PaymentPlanType.TEN_TIMES:
|
||||
elif frequency.code == "TEN_TIMES":
|
||||
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)]
|
||||
|
||||
def init_specialities(self):
|
||||
|
||||
@ -67,12 +67,6 @@ class Planning(models.Model):
|
||||
def __str__(self):
|
||||
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):
|
||||
CURRENCY = 0, 'Currency'
|
||||
PERCENT = 1, 'Percent'
|
||||
@ -81,11 +75,20 @@ class FeeType(models.IntegerChoices):
|
||||
REGISTRATION_FEE = 0, 'Registration Fee'
|
||||
TUITION_FEE = 1, 'Tuition Fee'
|
||||
|
||||
class PaymentModeType(models.IntegerChoices):
|
||||
SEPA = 1, 'Prélèvement SEPA'
|
||||
TRANSFER = 2, 'Virement'
|
||||
CHECK = 3, 'Chèque'
|
||||
CASH = 4, 'Espèce'
|
||||
class PaymentPlanType(models.Model):
|
||||
code = models.CharField(max_length=50, unique=True)
|
||||
label = models.CharField(max_length=255)
|
||||
|
||||
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):
|
||||
name = models.CharField(max_length=255, null=True, blank=True)
|
||||
@ -112,23 +115,21 @@ class Fee(models.Model):
|
||||
return self.name
|
||||
|
||||
class PaymentPlan(models.Model):
|
||||
frequency = models.IntegerField(choices=PaymentPlanType.choices, default=PaymentPlanType.ONE_TIME)
|
||||
due_dates = ArrayField(models.DateField(), blank=True)
|
||||
plan_type = models.ForeignKey(PaymentPlanType, on_delete=models.PROTECT, related_name='payment_plans')
|
||||
due_dates = ArrayField(models.DateField(), null=True, blank=True)
|
||||
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')
|
||||
|
||||
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):
|
||||
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)
|
||||
is_active = models.BooleanField(default=False)
|
||||
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='payment_modes')
|
||||
|
||||
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):
|
||||
name = models.CharField(max_length=255)
|
||||
|
||||
@ -2,11 +2,10 @@ import json
|
||||
import os
|
||||
from django.db.models.signals import post_migrate
|
||||
from django.dispatch import receiver
|
||||
from Establishment.models import Establishment
|
||||
from School.models import Domain, Category, Competency, EstablishmentCompetency
|
||||
from School.models import Domain, Category, Competency, PaymentModeType, PaymentPlanType
|
||||
|
||||
@receiver(post_migrate)
|
||||
def load_json_data(sender, **kwargs):
|
||||
def school_post_migrate(sender, **kwargs):
|
||||
if sender.name != "School":
|
||||
return
|
||||
|
||||
@ -48,3 +47,22 @@ def load_json_data(sender, **kwargs):
|
||||
category=category
|
||||
)
|
||||
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"]})
|
||||
@ -531,7 +531,6 @@ class CompetencyListCreateView(APIView):
|
||||
category__domain__cycle=cycle
|
||||
).distinct()
|
||||
serializer = CompetencySerializer(competencies_list, many=True)
|
||||
print(f'len : {competencies_list.count()}')
|
||||
return JsonResponse(serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
|
||||
@ -247,10 +247,10 @@ class RegistrationForm(models.Model):
|
||||
blank=True)
|
||||
|
||||
establishment = models.ForeignKey('Establishment.Establishment', on_delete=models.CASCADE, related_name='register_forms')
|
||||
registration_payment = models.IntegerField(choices=PaymentModeType.choices, null=True, blank=True)
|
||||
tuition_payment = models.IntegerField(choices=PaymentModeType.choices, null=True, blank=True)
|
||||
registration_payment_plan = models.IntegerField(choices=PaymentPlanType.choices, null=True, blank=True)
|
||||
tuition_payment_plan = models.IntegerField(choices=PaymentPlanType.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.ForeignKey('School.PaymentModeType', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_modes_forms')
|
||||
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.ForeignKey('School.PaymentPlanType', on_delete=models.SET_NULL, null=True, blank=True, related_name='tuition_payment_plans_forms')
|
||||
|
||||
def __str__(self):
|
||||
return "RF_" + self.student.last_name + "_" + self.student.first_name
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
from Subscriptions.models import RegistrationForm, Student
|
||||
from School.models import PaymentModeType, PaymentPlanType
|
||||
from django import template
|
||||
import re
|
||||
|
||||
@ -8,22 +7,30 @@ register = template.Library()
|
||||
@register.filter
|
||||
def getRegistrationPaymentPlan(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
|
||||
def getTuitionPaymentPlan(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
|
||||
def getRegistrationPaymentMethod(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
|
||||
def getTuitionPaymentMethod(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
|
||||
def getStudentLevel(pk):
|
||||
|
||||
@ -230,10 +230,7 @@ export default function InscriptionFormShared({
|
||||
const handleRegistrationPaymentModes = () => {
|
||||
fetchRegistrationPaymentModes(selectedEstablishmentId)
|
||||
.then((data) => {
|
||||
const activePaymentModes = data.filter(
|
||||
(mode) => mode.is_active === true
|
||||
);
|
||||
setRegistrationPaymentModes(activePaymentModes);
|
||||
setRegistrationPaymentModes(data);
|
||||
})
|
||||
.catch((error) =>
|
||||
logger.error('Error fetching registration payment modes:', error)
|
||||
@ -243,10 +240,7 @@ export default function InscriptionFormShared({
|
||||
const handleTuitionPaymentModes = () => {
|
||||
fetchTuitionPaymentModes(selectedEstablishmentId)
|
||||
.then((data) => {
|
||||
const activePaymentModes = data.filter(
|
||||
(mode) => mode.is_active === true
|
||||
);
|
||||
setTuitionPaymentModes(activePaymentModes);
|
||||
setTuitionPaymentModes(data);
|
||||
})
|
||||
.catch((error) =>
|
||||
logger.error('Error fetching tuition payment modes:', error)
|
||||
@ -256,10 +250,7 @@ export default function InscriptionFormShared({
|
||||
const handleRegistrationPaymentPlans = () => {
|
||||
fetchRegistrationPaymentPlans(selectedEstablishmentId)
|
||||
.then((data) => {
|
||||
const activePaymentPlans = data.filter(
|
||||
(mode) => mode.is_active === true
|
||||
);
|
||||
setRegistrationPaymentPlans(activePaymentPlans);
|
||||
setRegistrationPaymentPlans(data);
|
||||
})
|
||||
.catch((error) =>
|
||||
logger.error('Error fetching registration payment plans:', error)
|
||||
@ -269,10 +260,7 @@ export default function InscriptionFormShared({
|
||||
const handleTuitionnPaymentPlans = () => {
|
||||
fetchTuitionPaymentPlans(selectedEstablishmentId)
|
||||
.then((data) => {
|
||||
const activePaymentPlans = data.filter(
|
||||
(mode) => mode.is_active === true
|
||||
);
|
||||
setTuitionPaymentPlans(activePaymentPlans);
|
||||
setTuitionPaymentPlans(data);
|
||||
})
|
||||
.catch((error) =>
|
||||
logger.error('Error fetching registration tuition plans:', error)
|
||||
|
||||
@ -31,9 +31,9 @@ export default function PaymentMethodSelector({
|
||||
|
||||
const paymentPlansOptions = [
|
||||
{ id: 1, name: '1 fois' },
|
||||
{ id: 3, name: '3 fois' },
|
||||
{ id: 10, name: '10 fois' },
|
||||
{ id: 12, name: '12 fois' },
|
||||
{ id: 2, name: '3 fois' },
|
||||
{ id: 3, name: '10 fois' },
|
||||
{ id: 4, name: '12 fois' },
|
||||
];
|
||||
|
||||
const getError = (field) => {
|
||||
@ -105,7 +105,7 @@ export default function PaymentMethodSelector({
|
||||
items={paymentPlansOptions
|
||||
.filter((option) =>
|
||||
registrationPaymentPlans.some(
|
||||
(plan) => plan.frequency === option.id
|
||||
(plan) => plan.plan_type === option.id
|
||||
)
|
||||
)
|
||||
.map((option) => ({
|
||||
@ -167,7 +167,7 @@ export default function PaymentMethodSelector({
|
||||
required
|
||||
items={paymentPlansOptions
|
||||
.filter((option) =>
|
||||
tuitionPaymentPlans.some((plan) => plan.frequency === option.id)
|
||||
tuitionPaymentPlans.some((plan) => plan.plan_type === option.id)
|
||||
)
|
||||
.map((option) => ({
|
||||
id: option.id,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { DollarSign } from 'lucide-react';
|
||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||
|
||||
const paymentModesOptions = [
|
||||
{ id: 1, name: 'Prélèvement SEPA' },
|
||||
@ -11,36 +12,31 @@ const paymentModesOptions = [
|
||||
const PaymentModeSelector = ({
|
||||
paymentModes,
|
||||
setPaymentModes,
|
||||
handleEdit,
|
||||
handleCreate,
|
||||
handleDelete,
|
||||
type,
|
||||
}) => {
|
||||
const [activePaymentModes, setActivePaymentModes] = useState([]);
|
||||
const { selectedEstablishmentId } = useEstablishment();
|
||||
|
||||
useEffect(() => {
|
||||
// Initialiser activePaymentModes avec les modes dont is_active est à true
|
||||
const activeModes = paymentModes
|
||||
.filter((mode) => mode.is_active)
|
||||
.map((mode) => mode.mode);
|
||||
const activeModes = paymentModes.map((mode) => mode.mode);
|
||||
setActivePaymentModes(activeModes);
|
||||
}, [paymentModes]);
|
||||
|
||||
const handleModeToggle = (modeId) => {
|
||||
setActivePaymentModes((prevActiveModes) => {
|
||||
const newActiveModes = prevActiveModes.includes(modeId)
|
||||
? prevActiveModes.filter((mode) => mode !== modeId)
|
||||
: [...prevActiveModes, modeId];
|
||||
const updatedMode = paymentModes.find((mode) => mode.mode === modeId);
|
||||
const isActive = !!updatedMode;
|
||||
|
||||
// Mettre à jour le mode de paiement dans le backend
|
||||
const updatedMode = paymentModes.find((mode) => mode.mode === modeId);
|
||||
if (updatedMode) {
|
||||
handleEdit(updatedMode.id, {
|
||||
...updatedMode,
|
||||
is_active: !updatedMode.is_active,
|
||||
});
|
||||
}
|
||||
|
||||
return newActiveModes;
|
||||
});
|
||||
if (!isActive) {
|
||||
handleCreate({
|
||||
mode: modeId,
|
||||
type,
|
||||
establishment: selectedEstablishmentId,
|
||||
});
|
||||
} else {
|
||||
handleDelete(updatedMode.id, null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -55,7 +51,7 @@ const PaymentModeSelector = ({
|
||||
key={mode.id}
|
||||
type="button"
|
||||
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)
|
||||
? 'bg-emerald-100'
|
||||
: 'bg-stone-50'
|
||||
|
||||
@ -1,281 +1,62 @@
|
||||
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 DateTab from '@/components/DateTab';
|
||||
import InputTextIcon from '@/components/InputTextIcon';
|
||||
import Popup from '@/components/Popup';
|
||||
import logger from '@/utils/logger';
|
||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||
import CheckBox from '@/components/CheckBox';
|
||||
|
||||
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 },
|
||||
{ id: 1, name: '1 fois', frequency: 1 },
|
||||
{ id: 2, name: '3 fois', frequency: 3 },
|
||||
{ id: 3, name: '10 fois', frequency: 10 },
|
||||
{ id: 4, name: '12 fois', frequency: 12 },
|
||||
];
|
||||
|
||||
const PaymentPlanSelector = ({
|
||||
paymentPlans,
|
||||
setPaymentPlans,
|
||||
handleEdit,
|
||||
handleCreate,
|
||||
handleDelete,
|
||||
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 [popupMessage, setPopupMessage] = useState('');
|
||||
const [errorMsg, setErrorMsg] = useState('');
|
||||
const [resetModifiedDates, setResetModifiedDates] = useState(false);
|
||||
const { selectedEstablishmentId } = useEstablishment();
|
||||
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(() => {
|
||||
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);
|
||||
setCheckedPlans(
|
||||
paymentPlans.map((plan) =>
|
||||
typeof plan.plan_type === 'object'
|
||||
? plan.plan_type.id
|
||||
: plan.plan_type
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [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 (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center mb-4">
|
||||
@ -285,49 +66,34 @@ const PaymentPlanSelector = ({
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Table
|
||||
data={paymentPlansOptions}
|
||||
columns={columns}
|
||||
renderCell={renderCell}
|
||||
isSelectable={true}
|
||||
onRowClick={handleRowClick}
|
||||
selectedRows={selectedFrequency !== null ? [selectedFrequency] : []}
|
||||
columns={[
|
||||
{ name: 'OPTION', label: 'Option' },
|
||||
{ name: 'ACTIF', label: 'Actif' },
|
||||
]}
|
||||
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>
|
||||
<Popup
|
||||
isOpen={popupVisible}
|
||||
|
||||
@ -116,11 +116,17 @@ const FeesManagement = ({
|
||||
<PaymentPlanSelector
|
||||
paymentPlans={registrationPaymentPlans}
|
||||
setPaymentPlans={setRegistrationPaymentPlans}
|
||||
handleEdit={(id, updatedData) =>
|
||||
handleEdit(
|
||||
handleCreate={(newData) =>
|
||||
handleCreate(
|
||||
`${BE_SCHOOL_PAYMENT_PLANS_URL}`,
|
||||
newData,
|
||||
setRegistrationPaymentPlans
|
||||
)
|
||||
}
|
||||
handleDelete={(id) =>
|
||||
handleDelete(
|
||||
`${BE_SCHOOL_PAYMENT_PLANS_URL}`,
|
||||
id,
|
||||
updatedData,
|
||||
setRegistrationPaymentPlans
|
||||
)
|
||||
}
|
||||
@ -131,11 +137,17 @@ const FeesManagement = ({
|
||||
<PaymentModeSelector
|
||||
paymentModes={registrationPaymentModes}
|
||||
setPaymentModes={setRegistrationPaymentModes}
|
||||
handleEdit={(id, updatedData) =>
|
||||
handleEdit(
|
||||
handleCreate={(newData) =>
|
||||
handleCreate(
|
||||
`${BE_SCHOOL_PAYMENT_MODES_URL}`,
|
||||
newData,
|
||||
setRegistrationPaymentModes
|
||||
)
|
||||
}
|
||||
handleDelete={(id) =>
|
||||
handleDelete(
|
||||
`${BE_SCHOOL_PAYMENT_MODES_URL}`,
|
||||
id,
|
||||
updatedData,
|
||||
setRegistrationPaymentModes
|
||||
)
|
||||
}
|
||||
@ -200,12 +212,18 @@ const FeesManagement = ({
|
||||
<PaymentPlanSelector
|
||||
paymentPlans={tuitionPaymentPlans}
|
||||
setPaymentPlans={setTuitionPaymentPlans}
|
||||
handleEdit={(id, updatedData) =>
|
||||
handleEdit(
|
||||
handleCreate={(newData) =>
|
||||
handleCreate(
|
||||
`${BE_SCHOOL_PAYMENT_PLANS_URL}`,
|
||||
newData,
|
||||
setTuitionPaymentPlans
|
||||
)
|
||||
}
|
||||
handleDelete={(id) =>
|
||||
handleDelete(
|
||||
`${BE_SCHOOL_PAYMENT_PLANS_URL}`,
|
||||
id,
|
||||
updatedData,
|
||||
setRegistrationPaymentPlans
|
||||
setTuitionPaymentPlans
|
||||
)
|
||||
}
|
||||
type={1}
|
||||
@ -215,11 +233,17 @@ const FeesManagement = ({
|
||||
<PaymentModeSelector
|
||||
paymentModes={tuitionPaymentModes}
|
||||
setPaymentModes={setTuitionPaymentModes}
|
||||
handleEdit={(id, updatedData) =>
|
||||
handleEdit(
|
||||
handleCreate={(newData) =>
|
||||
handleCreate(
|
||||
`${BE_SCHOOL_PAYMENT_MODES_URL}`,
|
||||
newData,
|
||||
setTuitionPaymentModes
|
||||
)
|
||||
}
|
||||
handleDelete={(id) =>
|
||||
handleDelete(
|
||||
`${BE_SCHOOL_PAYMENT_MODES_URL}`,
|
||||
id,
|
||||
updatedData,
|
||||
setTuitionPaymentModes
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user