feat: Ajout des frais d'inscription lors de la création d'un RF [#18]

This commit is contained in:
N3WT DE COMPET
2025-01-25 16:40:08 +01:00
parent 799e1c6717
commit ece23deb19
12 changed files with 333 additions and 136 deletions

View File

@ -96,7 +96,6 @@ class Fee(models.Model):
name = models.CharField(max_length=255, unique=True)
base_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
description = models.TextField(blank=True)
discounts = models.ManyToManyField('Discount', blank=True)
is_active = models.BooleanField(default=True)
updated_at = models.DateTimeField(auto_now=True)
type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE)

View File

@ -1,8 +1,5 @@
from rest_framework import serializers
from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES, Discount, Fee
from Subscriptions.models import RegistrationForm
from Subscriptions.serializers import StudentSerializer
from Auth.serializers import ProfileSerializer
from Auth.models import Profile
from N3wtSchool import settings, bdd
from django.utils import timezone
@ -187,41 +184,12 @@ class DiscountSerializer(serializers.ModelSerializer):
return local_time.strftime("%d-%m-%Y %H:%M")
class FeeSerializer(serializers.ModelSerializer):
discounts = serializers.PrimaryKeyRelatedField(queryset=Discount.objects.all(), many=True)
updated_at_formatted = serializers.SerializerMethodField()
class Meta:
model = Fee
fields = '__all__'
def create(self, validated_data):
discounts_data = validated_data.pop('discounts', [])
# Create the Fee instance
fee = Fee.objects.create(**validated_data)
# Add discounts if provided
fee.discounts.set(discounts_data)
return fee
def update(self, instance, validated_data):
discounts_data = validated_data.pop('discounts', [])
# Update the Fee instance
instance.name = validated_data.get('name', instance.name)
instance.description = validated_data.get('description', instance.description)
instance.base_amount = validated_data.get('base_amount', instance.base_amount)
instance.is_active = validated_data.get('is_active', instance.is_active)
instance.updated_at = validated_data.get('updated_at', instance.updated_at)
instance.type = validated_data.get('type', instance.type)
instance.save()
# Update discounts if provided
instance.discounts.set(discounts_data)
return instance
def get_updated_at_formatted(self, obj):
utc_time = timezone.localtime(obj.updated_at)
local_tz = pytz.timezone(settings.TZ_APPLI)

View File

@ -4,7 +4,7 @@ from django.conf import settings
from django.utils.translation import gettext_lazy as _
from Auth.models import Profile
from School.models import SchoolClass
from School.models import SchoolClass, Fee, Discount
from datetime import datetime
@ -204,6 +204,12 @@ class RegistrationForm(models.Model):
registration_file = models.FileField(upload_to=settings.DOCUMENT_DIR, default="", blank=True)
associated_rf = models.CharField(max_length=200, default="", blank=True)
# Many-to-Many Relationship
fees = models.ManyToManyField(Fee, blank=True, related_name='register_forms')
# Many-to-Many Relationship
discounts = models.ManyToManyField(Discount, blank=True, related_name='register_forms')
def __str__(self):
return "RF_" + self.student.last_name + "_" + self.student.first_name

View File

@ -1,6 +1,7 @@
from rest_framework import serializers
from .models import RegistrationFileTemplate, RegistrationFile, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationFee
from School.models import SchoolClass
from School.models import SchoolClass, Fee, Discount
from School.serializers import FeeSerializer, DiscountSerializer
from Auth.models import Profile
from Auth.serializers import ProfileSerializer
from GestionMessagerie.models import Messagerie
@ -133,6 +134,9 @@ class RegistrationFormSerializer(serializers.ModelSerializer):
status_label = serializers.SerializerMethodField()
formatted_last_update = serializers.SerializerMethodField()
registration_files = RegistrationFileSerializer(many=True, required=False)
fees = serializers.PrimaryKeyRelatedField(queryset=Fee.objects.all(), many=True, required=False)
discounts = serializers.PrimaryKeyRelatedField(queryset=Discount.objects.all(), many=True, required=False)
class Meta:
model = RegistrationForm
fields = '__all__'
@ -140,11 +144,19 @@ class RegistrationFormSerializer(serializers.ModelSerializer):
def create(self, validated_data):
student_data = validated_data.pop('student')
student = StudentSerializer.create(StudentSerializer(), student_data)
fees_data = validated_data.pop('fees', [])
discounts_data = validated_data.pop('discounts', [])
registrationForm = RegistrationForm.objects.create(student=student, **validated_data)
# Associer les IDs des objets Fee et Discount au RegistrationForm
registrationForm.fees.set([fee.id for fee in fees_data])
registrationForm.discounts.set([discount.id for discount in discounts_data])
return registrationForm
def update(self, instance, validated_data):
student_data = validated_data.pop('student', None)
fees_data = validated_data.pop('fees', [])
discounts_data = validated_data.pop('discounts', [])
if student_data:
student = instance.student
StudentSerializer.update(StudentSerializer(), student, student_data)
@ -156,6 +168,10 @@ class RegistrationFormSerializer(serializers.ModelSerializer):
pass
instance.save()
# Associer les IDs des objets Fee et Discount au RegistrationForm
instance.fees.set([fee.id for fee in fees_data])
instance.discounts.set([discount.id for discount in discounts_data])
return instance
def get_status_label(self, obj):

View File

@ -2,7 +2,7 @@
import React, { useState, useEffect } from 'react';
import StructureManagement from '@/components/Structure/Configuration/StructureManagement';
import ScheduleManagement from '@/components/Structure/Planning/ScheduleManagement';
import FeesManagement from '@/components/Structure/Configuration/FeesManagement';
import FeesManagement from '@/components/Structure/Tarification/FeesManagement';
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import useCsrfToken from '@/hooks/useCsrfToken';
import { ClassesProvider } from '@/context/ClassesContext';

View File

@ -32,7 +32,13 @@ import {
fetchStudents,
editRegisterForm } from "@/app/lib/subscriptionAction"
import { fetchClasses } from '@/app/lib/schoolAction';
import {
fetchClasses,
fetchRegistrationDiscounts,
fetchTuitionDiscounts,
fetchRegistrationFees,
fetchTuitionFees } from '@/app/lib/schoolAction';
import { createProfile } from '@/app/lib/authAction';
import {
@ -75,6 +81,11 @@ export default function Page({ params: { locale } }) {
const [isEditing, setIsEditing] = useState(false);
const [fileToEdit, setFileToEdit] = useState(null);
const [registrationDiscounts, setRegistrationDiscounts] = useState([]);
const [tuitionDiscounts, setTuitionDiscounts] = useState([]);
const [registrationFees, setRegistrationFees] = useState([]);
const [tuitionFees, setTuitionFees] = useState([]);
const csrfToken = useCsrfToken();
const openModal = () => {
@ -151,6 +162,7 @@ const registerFormArchivedDataHandler = (data) => {
}
}
}
// TODO: revoir le système de pagination et de UseEffect
useEffect(() => {
@ -195,7 +207,27 @@ const registerFormArchivedDataHandler = (data) => {
setFichiers(data)
})
.catch((err)=>{ err = err.message; console.log(err);});
.catch((err)=>{ err = err.message; console.log(err);})
fetchRegistrationDiscounts()
.then(data => {
setRegistrationDiscounts(data);
})
.catch(requestErrorHandler)
fetchTuitionDiscounts()
.then(data => {
setTuitionDiscounts(data);
})
.catch(requestErrorHandler)
fetchRegistrationFees()
.then(data => {
setRegistrationFees(data);
})
.catch(requestErrorHandler)
fetchTuitionFees()
.then(data => {
setTuitionFees(data);
})
.catch(requestErrorHandler);
} else {
setTimeout(() => {
setRegistrationFormsDataPending(mockFicheInscription);
@ -321,6 +353,8 @@ useEffect(()=>{
const createRF = (updatedData) => {
console.log('createRF updatedData:', updatedData);
const selectedRegistrationFeesIds = updatedData.selectedRegistrationFees.map(feeId => feeId)
const selectedRegistrationDiscountsIds = updatedData.selectedRegistrationDiscounts.map(discountId => discountId)
if (updatedData.selectedGuardians.length !== 0) {
const selectedGuardiansIds = updatedData.selectedGuardians.map(guardianId => guardianId)
@ -330,7 +364,9 @@ useEffect(()=>{
last_name: updatedData.studentLastName,
first_name: updatedData.studentFirstName,
},
idGuardians: selectedGuardiansIds
idGuardians: selectedGuardiansIds,
fees: selectedRegistrationFeesIds,
discounts: selectedRegistrationDiscountsIds
};
createRegisterForm(data,csrfToken)
@ -379,7 +415,9 @@ useEffect(()=>{
}
],
sibling: []
}
},
fees: selectedRegistrationFeesIds,
discounts: selectedRegistrationDiscountsIds
};
createRegisterForm(data,csrfToken)
@ -784,6 +822,10 @@ const handleFileUpload = ({file, name, is_required, order}) => {
size='sm:w-1/4'
ContentComponent={() => (
<InscriptionForm students={students}
registrationDiscounts={registrationDiscounts}
tuitionDiscounts={tuitionDiscounts}
registrationFees={registrationFees}
tuitionFees={tuitionFees}
onSubmit={createRF}
/>
)}

View File

@ -0,0 +1,39 @@
import React from 'react';
const CheckBox = ({ item, formData, handleChange, fieldName, itemLabelFunc = () => null, labelAttenuated = () => false, horizontal }) => {
const isChecked = formData[fieldName].includes(parseInt(item.id));
const isAttenuated = labelAttenuated(item) && !isChecked;
return (
<div key={item.id} className={`flex ${horizontal ? 'flex-col items-center' : 'flex-row items-center'}`}>
{horizontal && (
<label
htmlFor={`${fieldName}-${item.id}`}
className={`block text-sm text-center mb-1 ${isAttenuated ? 'text-gray-300' : 'font-bold text-emerald-600'}`}
>
{itemLabelFunc(item)}
</label>
)}
<input
type="checkbox"
id={`${fieldName}-${item.id}`}
name={fieldName}
value={item.id}
checked={isChecked}
onChange={handleChange}
className={`form-checkbox h-4 w-4 rounded-mg text-emerald-600 hover:ring-emerald-400 checked:bg-emerald-600 hover:border-emerald-500 hover:bg-emerald-500 cursor-pointer ${horizontal ? 'mt-1' : 'mr-2'}`}
style={{ borderRadius: '6px', outline: 'none', boxShadow: 'none' }}
/>
{!horizontal && (
<label
htmlFor={`${fieldName}-${item.id}`}
className={`block text-sm ${isAttenuated ? 'text-gray-300' : 'font-bold text-emerald-600'}`}
>
{itemLabelFunc(item)}
</label>
)}
</div>
);
};
export default CheckBox;

View File

@ -1,4 +1,5 @@
import React from 'react';
import CheckBox from '@/components/CheckBox';
const CheckBoxList = ({
items,
@ -12,10 +13,6 @@ const CheckBoxList = ({
labelAttenuated = () => false,
horizontal = false // Ajouter l'option horizontal
}) => {
const handleCheckboxChange = (e) => {
handleChange(e);
};
return (
<div className={`mb-4 ${className}`}>
<label className="block text-sm font-medium text-gray-700 flex items-center">
@ -23,45 +20,18 @@ const CheckBoxList = ({
{label}
</label>
<div className={`mt-2 grid ${horizontal ? 'grid-cols-6 gap-2' : 'grid-cols-1 gap-4'}`}>
{items.map(item => {
const isChecked = formData[fieldName].includes(parseInt(item.id));
const isAttenuated = labelAttenuated(item) && !isChecked;
return (
<div key={item.id} className={`flex ${horizontal ? 'flex-col items-center' : 'flex-row items-center'}`}>
{horizontal && (
<label
htmlFor={`${fieldName}-${item.id}`}
className={`block text-sm text-center mb-1 ${
isAttenuated ? 'text-gray-300' : 'font-bold text-emerald-600'
}`}
>
{itemLabelFunc(item)}
</label>
)}
<input
key={`${item.id}-${Math.random()}`}
type="checkbox"
id={`${fieldName}-${item.id}`}
name={fieldName}
value={item.id}
checked={isChecked}
onChange={handleCheckboxChange}
className={`form-checkbox h-4 w-4 rounded-mg text-emerald-600 hover:ring-emerald-400 checked:bg-emerald-600 hover:border-emerald-500 hover:bg-emerald-500 cursor-pointer ${horizontal ? 'mt-1' : 'mr-2'}`}
style={{ borderRadius: '6px', outline: 'none', boxShadow: 'none' }}
/>
{!horizontal && (
<label
htmlFor={`${fieldName}-${item.id}`}
className={`block text-sm ${
isAttenuated ? 'text-gray-300' : 'font-bold text-emerald-600'
}`}
>
{itemLabelFunc(item)}
</label>
)}
</div>
);
})}
{items.map(item => (
<CheckBox
key={item.id}
item={item}
formData={formData}
handleChange={handleChange}
fieldName={fieldName}
itemLabelFunc={itemLabelFunc}
labelAttenuated={labelAttenuated}
horizontal={horizontal}
/>
))}
</div>
</div>
);

View File

@ -1,10 +1,13 @@
import { useState } from 'react';
import { User, Mail, Phone, UserCheck } from 'lucide-react';
import { useState, useEffect } from 'react';
import { User, Mail, Phone, UserCheck, DollarSign, Percent } from 'lucide-react';
import InputTextIcon from '@/components/InputTextIcon';
import ToggleSwitch from '@/components/ToggleSwitch';
import Button from '@/components/Button';
import Table from '@/components/Table';
import FeesSection from '@/components/Structure/Tarification/FeesSection';
import DiscountsSection from '../Structure/Tarification/DiscountsSection';
const InscriptionForm = ( { students, onSubmit }) => {
const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, registrationFees, tuitionFees, onSubmit }) => {
const [formData, setFormData] = useState({
studentLastName: '',
studentFirstName: '',
@ -12,14 +15,26 @@ const InscriptionForm = ( { students, onSubmit }) => {
guardianPhone: '',
selectedGuardians: [],
responsableType: 'new',
autoMail: false
autoMail: false,
selectedRegistrationDiscounts: [],
selectedRegistrationFees: registrationFees.map(fee => fee.id)
});
const [step, setStep] = useState(1);
const [step, setStep] = useState(0);
const [selectedStudent, setSelectedEleve] = useState('');
const [existingGuardians, setExistingGuardians] = useState([]);
const [totalRegistrationAmount, setTotalRegistrationAmount] = useState(0);
const maxStep = 4
useEffect(() => {
// Calcul du montant total lors de l'initialisation
const initialTotalAmount = calculateFinalRegistrationAmount(
registrationFees.map(fee => fee.id),
[]
);
setTotalRegistrationAmount(initialTotalAmount);
}, [registrationDiscounts, registrationFees]);
const handleToggleChange = () => {
setFormData({ ...formData, autoMail: !formData.autoMail });
};
@ -39,7 +54,7 @@ const InscriptionForm = ( { students, onSubmit }) => {
};
const prevStep = () => {
if (step > 1) {
if (step >= 1) {
setStep(step - 1);
}
};
@ -66,8 +81,122 @@ const InscriptionForm = ( { students, onSubmit }) => {
onSubmit(formData);
}
const handleFeeSelection = (feeId) => {
setFormData((prevData) => {
const selectedRegistrationFees = prevData.selectedRegistrationFees.includes(feeId)
? prevData.selectedRegistrationFees.filter(id => id !== feeId)
: [...prevData.selectedRegistrationFees, feeId];
const finalAmount = calculateFinalRegistrationAmount(selectedRegistrationFees, prevData.selectedRegistrationDiscounts);
setTotalRegistrationAmount(finalAmount);
return { ...prevData, selectedRegistrationFees };
});
};
const handleDiscountSelection = (discountId) => {
setFormData((prevData) => {
const selectedRegistrationDiscounts = prevData.selectedRegistrationDiscounts.includes(discountId)
? prevData.selectedRegistrationDiscounts.filter(id => id !== discountId)
: [...prevData.selectedRegistrationDiscounts, discountId];
const finalAmount = calculateFinalRegistrationAmount(prevData.selectedRegistrationFees, selectedRegistrationDiscounts);
setTotalRegistrationAmount(finalAmount);
return { ...prevData, selectedRegistrationDiscounts };
});
};
const calculateFinalRegistrationAmount = (selectedRegistrationFees, selectedRegistrationDiscounts) => {
const totalFees = selectedRegistrationFees.reduce((sum, feeId) => {
const fee = registrationFees.find(f => f.id === feeId);
if (fee && !isNaN(parseFloat(fee.base_amount))) {
return sum + parseFloat(fee.base_amount);
}
return sum;
}, 0);
console.log(totalFees);
const totalDiscounts = selectedRegistrationDiscounts.reduce((sum, discountId) => {
const discount = registrationDiscounts.find(d => d.id === discountId);
if (discount) {
if (discount.discount_type === 0 && !isNaN(parseFloat(discount.amount))) { // Currency
return sum + parseFloat(discount.amount);
} else if (discount.discount_type === 1 && !isNaN(parseFloat(discount.amount))) { // Percent
return sum + (totalFees * parseFloat(discount.amount) / 100);
}
}
return sum;
}, 0);
const finalAmount = totalFees - totalDiscounts;
return finalAmount.toFixed(2);
};
const isLabelAttenuated = (item) => {
return !formData.selectedRegistrationDiscounts.includes(parseInt(item.id));
};
const isLabelFunction = (item) => {
return item.name + ' : ' + item.amount
};
return (
<div className="space-y-4 mt-8">
{step === 0 && (
<div>
<h2 className="text-l font-bold mb-4">Frais d'inscription</h2>
{registrationFees.length > 0 ? (
<>
<div className="mb-4">
<FeesSection
fees={registrationFees}
type={0}
subscriptionMode={true}
selectedFees={formData.selectedRegistrationFees}
handleFeeSelection={handleFeeSelection}
/>
</div>
<h2 className="text-l font-bold mb-4">Réductions</h2>
<div className="mb-4">
{registrationDiscounts.length > 0 ? (
<DiscountsSection
discounts={registrationDiscounts}
type={0}
subscriptionMode={true}
selectedDiscounts={formData.selectedRegistrationDiscounts}
handleDiscountSelection={handleDiscountSelection}
/>
) : (
<p className="bg-orange-100 border border-orange-400 text-orange-700 px-4 py-3 rounded relative" role="alert">
<strong className="font-bold">Information</strong>
<span className="block sm:inline"> Aucune réduction n'a été créée sur les frais d'inscription.</span>
</p>
)}
</div>
<Table
data={[ {id: 1}]}
columns={[
{
name: 'LIBELLE',
transform: () => <span>MONTANT TOTAL</span>
},
{
name: 'TOTAL',
transform: () => <b>{totalRegistrationAmount} €</b>
}
]}
defaultTheme='bg-cyan-100'
/>
</>
) : (
<p className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
<strong className="font-bold">Attention!</strong>
<span className="block sm:inline"> Aucun frais d'inscription n'a été créé.</span>
</p>
)}
</div>
)}
{step === 1 && (
<div>
<h2 className="text-l font-bold mb-4">Nouvel élève</h2>
@ -270,7 +399,7 @@ const InscriptionForm = ( { students, onSubmit }) => {
)}
<div className="flex justify-end mt-4 space-x-4">
{step > 1 && (
{step >= 1 && (
<Button text="Précédent"
onClick={prevStep}
className="px-4 py-2 bg-gray-300 text-gray-700 rounded-md shadow-sm hover:bg-gray-400 focus:outline-none"

View File

@ -3,8 +3,9 @@ import { Plus, Trash, Edit3, Check, X, Percent, EuroIcon, Tag } from 'lucide-rea
import Table from '@/components/Table';
import InputTextIcon from '@/components/InputTextIcon';
import Popup from '@/components/Popup';
import CheckBox from '@/components/CheckBox';
const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, handleDelete, type }) => {
const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, handleDelete, type, subscriptionMode = false, selectedDiscounts, handleDiscountSelection }) => {
const [editingDiscount, setEditingDiscount] = useState(null);
const [newDiscount, setNewDiscount] = useState(null);
const [formData, setFormData] = useState({});
@ -154,7 +155,7 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
return discount.name;
case 'REMISE':
return discount.discount_type === 0 ? `${discount.amount}` : `${discount.amount} %`;
case 'DESCRIPTION':
case 'DESCRIPTION':
return discount.description;
case 'MISE A JOUR':
return discount.updated_at_formatted;
@ -184,32 +185,54 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
</button>
</div>
);
case '':
return (
<div className="flex justify-center">
<CheckBox
item={discount}
formData={{ selectedDiscounts }}
handleChange={() => handleDiscountSelection(discount.id)}
fieldName="selectedDiscounts"
/>
</div>
);
default:
return null;
}
}
};
const columns = subscriptionMode
? [
{ name: 'LIBELLE', label: 'Libellé' },
{ name: 'DESCRIPTION', label: 'Description' },
{ name: 'REMISE', label: 'Remise' },
{ name: '', label: 'Sélection' }
]
: [
{ name: 'LIBELLE', label: 'Libellé' },
{ name: 'REMISE', label: 'Remise' },
{ name: 'DESCRIPTION', label: 'Description' },
{ name: 'MISE A JOUR', label: 'Date mise à jour' },
{ name: 'ACTIONS', label: 'Actions' }
];
return (
<div className="space-y-4">
<div className="flex justify-between items-center">
<div className="flex items-center mb-4">
<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>
{!subscriptionMode && (
<div className="flex justify-between items-center">
<div className="flex items-center mb-4">
<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>
</div>
<button type="button" onClick={handleAddDiscount} className="text-emerald-500 hover:text-emerald-700">
<Plus className="w-5 h-5" />
</button>
</div>
<button type="button" onClick={handleAddDiscount} className="text-emerald-500 hover:text-emerald-700">
<Plus className="w-5 h-5" />
</button>
</div>
)}
<Table
data={newDiscount ? [newDiscount, ...discounts] : discounts}
columns={[
{ name: 'LIBELLE', label: 'Libellé' },
{ name: 'REMISE', label: 'Valeur' },
{ name: 'DESCRIPTION', label: 'Description' },
{ name: 'MISE A JOUR', label: 'date mise à jour' },
{ name: 'ACTIONS', label: 'Actions' }
]}
columns={columns}
renderCell={renderDiscountCell}
defaultTheme='bg-yellow-100'
/>

View File

@ -1,6 +1,6 @@
import React from 'react';
import FeesSection from '@/components/Structure/Configuration/FeesSection';
import DiscountsSection from '@/components/Structure/Configuration/DiscountsSection';
import FeesSection from '@/components/Structure/Tarification/FeesSection';
import DiscountsSection from '@/components/Structure/Tarification/DiscountsSection';
import { BE_SCHOOL_FEE_URL, BE_SCHOOL_DISCOUNT_URL } from '@/utils/Url';
const FeesManagement = ({ registrationDiscounts, setRegistrationDiscounts, tuitionDiscounts, setTuitionDiscounts, registrationFees, setRegistrationFees, tuitionFees, setTuitionFees, handleCreate, handleEdit, handleDelete }) => {

View File

@ -1,10 +1,11 @@
import React, { useState } from 'react';
import { Plus, Trash, Edit3, Check, X, EyeOff, Eye, CreditCard } from 'lucide-react';
import { Plus, Trash, Edit3, Check, X, EyeOff, Eye, CreditCard, BookOpen } from 'lucide-react';
import Table from '@/components/Table';
import InputTextIcon from '@/components/InputTextIcon';
import Popup from '@/components/Popup';
import CheckBox from '@/components/CheckBox';
const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handleDelete, type }) => {
const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handleDelete, type, subscriptionMode = false, selectedFees, handleFeeSelection }) => {
const [editingFee, setEditingFee] = useState(null);
const [newFee, setNewFee] = useState(null);
const [formData, setFormData] = useState({});
@ -122,24 +123,6 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
</div>
);
const calculateFinalAmount = (baseAmount, discountIds) => {
const totalDiscounts = discountIds.reduce((sum, discountId) => {
const discount = discounts.find(d => d.id === discountId);
if (discount) {
if (discount.discount_type === 0) { // Currency
return sum + parseFloat(discount.amount);
} else if (discount.discount_type === 1) { // Percent
return sum + (parseFloat(baseAmount) * parseFloat(discount.amount) / 100);
}
}
return sum;
}, 0);
const finalAmount = parseFloat(baseAmount) - totalDiscounts;
return finalAmount.toFixed(2);
};
const renderFeeCell = (fee, column) => {
const isEditing = editingFee === fee.id;
const isCreating = newFee && newFee.id === fee.id;
@ -211,14 +194,41 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
</button>
</div>
);
case '':
return (
<div className="flex justify-center">
<CheckBox
item={fee}
formData={{ selectedFees }}
handleChange={() => handleFeeSelection(fee.id)}
fieldName="selectedFees"
/>
</div>
);
default:
return null;
}
}
};
const columns = subscriptionMode
? [
{ name: 'NOM', label: 'Nom' },
{ name: 'DESCRIPTION', label: 'Description' },
{ name: 'MONTANT', label: 'Montant de base' },
{ name: '', label: 'Sélection' }
]
: [
{ name: 'NOM', label: 'Nom' },
{ name: 'MONTANT', label: 'Montant de base' },
{ name: 'DESCRIPTION', label: 'Description' },
{ name: 'MISE A JOUR', label: 'Date mise à jour' },
{ name: 'ACTIONS', label: 'Actions' }
];
return (
<div className="space-y-4">
{!subscriptionMode && (
<div className="flex justify-between items-center">
<div className="flex items-center mb-4">
<CreditCard className="w-6 h-6 text-emerald-500 mr-2" />
@ -228,15 +238,10 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
<Plus className="w-5 h-5" />
</button>
</div>
)}
<Table
data={newFee ? [newFee, ...fees] : fees}
columns={[
{ name: 'NOM', label: 'Nom' },
{ name: 'MONTANT', label: 'Montant de base' },
{ name: 'DESCRIPTION', label: 'Description' },
{ name: 'MISE A JOUR', label: 'date mise à jour' },
{ name: 'ACTIONS', label: 'Actions' }
]}
columns={columns}
renderCell={renderFeeCell}
/>
<Popup