mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Gestion multi-profil multi-école
This commit is contained in:
@ -2,25 +2,31 @@ from django.contrib.auth.models import AbstractUser
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.core.validators import EmailValidator
|
from django.core.validators import EmailValidator
|
||||||
|
from Establishment.models import Establishment
|
||||||
|
|
||||||
class Profile(AbstractUser):
|
class Profile(AbstractUser):
|
||||||
class Droits(models.IntegerChoices):
|
|
||||||
PROFIL_UNDEFINED = -1, _('NON DEFINI')
|
|
||||||
PROFIL_ECOLE = 0, _('ECOLE')
|
|
||||||
PROFIL_ADMIN = 1, _('ADMIN')
|
|
||||||
PROFIL_PARENT = 2, _('PARENT')
|
|
||||||
|
|
||||||
email = models.EmailField(max_length=255, unique=True, default="", validators=[EmailValidator()])
|
email = models.EmailField(max_length=255, unique=True, default="", validators=[EmailValidator()])
|
||||||
|
|
||||||
USERNAME_FIELD = 'email'
|
USERNAME_FIELD = 'email'
|
||||||
|
|
||||||
REQUIRED_FIELDS = ('password', )
|
REQUIRED_FIELDS = ('password', )
|
||||||
|
|
||||||
code = models.CharField(max_length=200, default="", blank=True)
|
code = models.CharField(max_length=200, default="", blank=True)
|
||||||
datePeremption = models.CharField(max_length=200, default="", blank=True)
|
datePeremption = models.CharField(max_length=200, default="", blank=True)
|
||||||
droit = models.IntegerField(choices=Droits, default=Droits.PROFIL_UNDEFINED)
|
|
||||||
estConnecte = models.BooleanField(default=False, blank=True)
|
|
||||||
establishment = models.ForeignKey('School.Establishment', on_delete=models.PROTECT, related_name='profile', null=True, blank=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.email + " - " + str(self.droit)
|
return self.email
|
||||||
|
|
||||||
|
class ProfileRole(models.Model):
|
||||||
|
class RoleType(models.IntegerChoices):
|
||||||
|
PROFIL_UNDEFINED = -1, _('NON DEFINI')
|
||||||
|
PROFIL_ECOLE = 0, _('ECOLE')
|
||||||
|
PROFIL_ADMIN = 1, _('ADMIN')
|
||||||
|
PROFIL_PARENT = 2, _('PARENT')
|
||||||
|
|
||||||
|
profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='roles')
|
||||||
|
role_type = models.IntegerField(choices=RoleType.choices, default=RoleType.PROFIL_UNDEFINED)
|
||||||
|
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='profile_roles')
|
||||||
|
is_active = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.profile.email} - {self.get_role_type_display()} - {self.establishment.name}"
|
||||||
@ -1,48 +1,97 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from Auth.models import Profile
|
from Auth.models import Profile, ProfileRole
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
class ProfileRoleSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = ProfileRole
|
||||||
|
fields = ['role_type', 'establishment', 'is_active', 'profile']
|
||||||
|
|
||||||
class ProfileSerializer(serializers.ModelSerializer):
|
class ProfileSerializer(serializers.ModelSerializer):
|
||||||
id = serializers.IntegerField(required=False)
|
id = serializers.IntegerField(required=False)
|
||||||
password = serializers.CharField(write_only=True)
|
password = serializers.CharField(write_only=True)
|
||||||
|
roles = ProfileRoleSerializer(many=True, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Profile
|
model = Profile
|
||||||
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'estConnecte', 'droit', 'username', 'is_active', 'establishment']
|
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'username', 'roles']
|
||||||
extra_kwargs = {'password': {'write_only': True}}
|
extra_kwargs = {'password': {'write_only': True}}
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
roles_data = validated_data.pop('roles', [])
|
||||||
user = Profile(
|
user = Profile(
|
||||||
username=validated_data['username'],
|
username=validated_data['username'],
|
||||||
email=validated_data['email'],
|
email=validated_data['email'],
|
||||||
is_active=validated_data['is_active'],
|
code=validated_data.get('code', ''),
|
||||||
droit=validated_data['droit'],
|
datePeremption=validated_data.get('datePeremption', '')
|
||||||
establishment=validated_data.get('establishment')
|
|
||||||
)
|
)
|
||||||
user.set_password(validated_data['password'])
|
user.set_password(validated_data['password'])
|
||||||
|
user.full_clean()
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
|
for role_data in roles_data:
|
||||||
|
ProfileRole.objects.create(profile=user, **role_data)
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def to_representation(self, instance):
|
|
||||||
ret = super().to_representation(instance)
|
|
||||||
ret['password'] = '********'
|
|
||||||
return ret
|
|
||||||
|
|
||||||
class ProfilUpdateSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Profile
|
|
||||||
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'estConnecte', 'droit', 'username', 'is_active']
|
|
||||||
extra_kwargs = {
|
|
||||||
'password': {'write_only': True, 'required': False}
|
|
||||||
}
|
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
|
roles_data = validated_data.pop('roles', [])
|
||||||
password = validated_data.pop('password', None)
|
password = validated_data.pop('password', None)
|
||||||
instance = super().update(instance, validated_data)
|
instance = super().update(instance, validated_data)
|
||||||
|
|
||||||
if password:
|
if password:
|
||||||
instance.set_password(password)
|
instance.set_password(password)
|
||||||
instance.save()
|
|
||||||
|
instance.full_clean()
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
for role_data in roles_data:
|
||||||
|
ProfileRole.objects.update_or_create(
|
||||||
|
profile=instance,
|
||||||
|
establishment_id=role_data.get('establishment_id'),
|
||||||
|
defaults={
|
||||||
|
'role_type': role_data.get('role_type'),
|
||||||
|
'is_active': role_data.get('is_active', True)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def to_representation(self, instance):
|
||||||
|
ret = super().to_representation(instance)
|
||||||
|
ret['password'] = '********'
|
||||||
|
return ret
|
||||||
|
|
||||||
|
class ProfilUpdateSerializer(serializers.ModelSerializer):
|
||||||
|
roles = ProfileRoleSerializer(many=True, required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Profile
|
||||||
|
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'username', 'roles']
|
||||||
|
extra_kwargs = {
|
||||||
|
'password': {'write_only': True, 'required': False}
|
||||||
|
}
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
roles_data = validated_data.pop('roles', [])
|
||||||
|
password = validated_data.pop('password', None)
|
||||||
|
instance = super().update(instance, validated_data)
|
||||||
|
|
||||||
|
if password:
|
||||||
|
instance.set_password(password)
|
||||||
|
|
||||||
|
instance.full_clean()
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
for role_data in roles_data:
|
||||||
|
ProfileRole.objects.update_or_create(
|
||||||
|
profile=instance,
|
||||||
|
establishment_id=role_data.get('establishment_id'),
|
||||||
|
defaults={
|
||||||
|
'role_type': role_data.get('role_type'),
|
||||||
|
'is_active': role_data.get('is_active', True)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ from jwt.exceptions import ExpiredSignatureError, InvalidTokenError
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from . import validator
|
from . import validator
|
||||||
from .models import Profile
|
from .models import Profile, ProfileRole
|
||||||
from rest_framework.decorators import action, api_view
|
from rest_framework.decorators import action, api_view
|
||||||
|
|
||||||
from Auth.serializers import ProfileSerializer, ProfilUpdateSerializer
|
from Auth.serializers import ProfileSerializer, ProfilUpdateSerializer
|
||||||
@ -56,7 +56,10 @@ class SessionView(APIView):
|
|||||||
'user': openapi.Schema(type=openapi.TYPE_OBJECT, properties={
|
'user': openapi.Schema(type=openapi.TYPE_OBJECT, properties={
|
||||||
'id': openapi.Schema(type=openapi.TYPE_INTEGER),
|
'id': openapi.Schema(type=openapi.TYPE_INTEGER),
|
||||||
'email': openapi.Schema(type=openapi.TYPE_STRING),
|
'email': openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
'role': openapi.Schema(type=openapi.TYPE_STRING)
|
'roles': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Items(type=openapi.TYPE_OBJECT, properties={
|
||||||
|
'role_type': openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
'establishment': openapi.Schema(type=openapi.TYPE_STRING)
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
})),
|
})),
|
||||||
401: openapi.Response('Session invalide')
|
401: openapi.Response('Session invalide')
|
||||||
@ -67,15 +70,16 @@ class SessionView(APIView):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
|
decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
|
||||||
print(f'decode : {decoded_token}')
|
userid = decoded_token.get('user_id')
|
||||||
userid = decoded_token.get('id')
|
|
||||||
user = Profile.objects.get(id=userid)
|
user = Profile.objects.get(id=userid)
|
||||||
|
|
||||||
|
roles = ProfileRole.objects.filter(profile=user).values('role_type', 'establishment__name')
|
||||||
|
|
||||||
response_data = {
|
response_data = {
|
||||||
'user': {
|
'user': {
|
||||||
'id': user.id,
|
'id': user.id,
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'role': user.droit, # Assure-toi que le champ 'droit' existe et contient le rôle
|
'roles': list(roles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return JsonResponse(response_data, status=status.HTTP_200_OK)
|
return JsonResponse(response_data, status=status.HTTP_200_OK)
|
||||||
@ -103,13 +107,11 @@ class ProfileView(APIView):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
profil_data=JSONParser().parse(request)
|
profil_data = JSONParser().parse(request)
|
||||||
print(f'{profil_data}')
|
|
||||||
profil_serializer = ProfileSerializer(data=profil_data)
|
profil_serializer = ProfileSerializer(data=profil_data)
|
||||||
|
|
||||||
if profil_serializer.is_valid():
|
if profil_serializer.is_valid():
|
||||||
profil_serializer.save()
|
profil = profil_serializer.save()
|
||||||
|
|
||||||
return JsonResponse(profil_serializer.data, safe=False)
|
return JsonResponse(profil_serializer.data, safe=False)
|
||||||
|
|
||||||
return JsonResponse(profil_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
return JsonResponse(profil_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
@ -122,8 +124,8 @@ class ProfileSimpleView(APIView):
|
|||||||
responses={200: ProfileSerializer}
|
responses={200: ProfileSerializer}
|
||||||
)
|
)
|
||||||
def get(self, request, id):
|
def get(self, request, id):
|
||||||
profil=bdd.getObject(Profile, "id", id)
|
profil = bdd.getObject(Profile, "id", id)
|
||||||
profil_serializer=ProfileSerializer(profil)
|
profil_serializer = ProfileSerializer(profil)
|
||||||
return JsonResponse(profil_serializer.data, safe=False)
|
return JsonResponse(profil_serializer.data, safe=False)
|
||||||
|
|
||||||
@swagger_auto_schema(
|
@swagger_auto_schema(
|
||||||
@ -135,12 +137,12 @@ class ProfileSimpleView(APIView):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
def put(self, request, id):
|
def put(self, request, id):
|
||||||
data=JSONParser().parse(request)
|
data = JSONParser().parse(request)
|
||||||
profil = Profile.objects.get(id=id)
|
profil = Profile.objects.get(id=id)
|
||||||
profil_serializer = ProfilUpdateSerializer(profil, data=data)
|
profil_serializer = ProfilUpdateSerializer(profil, data=data)
|
||||||
if profil_serializer.is_valid():
|
if profil_serializer.is_valid():
|
||||||
profil_serializer.save()
|
profil_serializer.save()
|
||||||
return JsonResponse("Updated Successfully", safe=False)
|
return JsonResponse(profil_serializer.data, safe=False)
|
||||||
|
|
||||||
return JsonResponse(profil_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
return JsonResponse(profil_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@ -157,10 +159,11 @@ class LoginView(APIView):
|
|||||||
operation_description="Connexion utilisateur",
|
operation_description="Connexion utilisateur",
|
||||||
request_body=openapi.Schema(
|
request_body=openapi.Schema(
|
||||||
type=openapi.TYPE_OBJECT,
|
type=openapi.TYPE_OBJECT,
|
||||||
required=['email', 'password'],
|
required=['email', 'password', 'role_type'],
|
||||||
properties={
|
properties={
|
||||||
'email': openapi.Schema(type=openapi.TYPE_STRING),
|
'email': openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
'password': openapi.Schema(type=openapi.TYPE_STRING)
|
'password': openapi.Schema(type=openapi.TYPE_STRING),
|
||||||
|
'role_type': openapi.Schema(type=openapi.TYPE_STRING)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
responses={
|
responses={
|
||||||
@ -193,40 +196,45 @@ class LoginView(APIView):
|
|||||||
password=data.get('password'),
|
password=data.get('password'),
|
||||||
)
|
)
|
||||||
if user is not None:
|
if user is not None:
|
||||||
if user.is_active:
|
role_type = data.get('role_type')
|
||||||
login(request, user)
|
primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type, is_active=True).first()
|
||||||
user.estConnecte = True
|
|
||||||
user.save()
|
|
||||||
clear_cache()
|
|
||||||
retour = ''
|
|
||||||
# Générer le JWT avec la bonne syntaxe datetime
|
|
||||||
access_payload = {
|
|
||||||
'user_id': user.id,
|
|
||||||
'email': user.email,
|
|
||||||
'droit': user.droit,
|
|
||||||
'establishment': user.establishment.id,
|
|
||||||
'type': 'access',
|
|
||||||
'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
|
|
||||||
'iat': datetime.utcnow(),
|
|
||||||
}
|
|
||||||
|
|
||||||
access_token = jwt.encode(access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
|
if not primary_role:
|
||||||
# Générer le Refresh Token (exp: 7 jours)
|
return JsonResponse({"errorMessage": "Role not assigned to the user"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||||
refresh_payload = {
|
|
||||||
'user_id': user.id,
|
|
||||||
'type': 'refresh',
|
|
||||||
'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
|
|
||||||
'iat': datetime.utcnow(),
|
|
||||||
}
|
|
||||||
refresh_token = jwt.encode(refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
|
|
||||||
|
|
||||||
return JsonResponse({
|
login(request, user)
|
||||||
'token': access_token,
|
user.save()
|
||||||
'refresh': refresh_token
|
clear_cache()
|
||||||
}, safe=False)
|
retour = ''
|
||||||
|
|
||||||
|
# Récupérer tous les rôles de l'utilisateur avec le type spécifié
|
||||||
|
roles = ProfileRole.objects.filter(profile=user, role_type=role_type).values('role_type', 'establishment__id', 'establishment__name')
|
||||||
|
|
||||||
|
# Générer le JWT avec la bonne syntaxe datetime
|
||||||
|
access_payload = {
|
||||||
|
'user_id': user.id,
|
||||||
|
'email': user.email,
|
||||||
|
'roles': list(roles),
|
||||||
|
'type': 'access',
|
||||||
|
'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
|
||||||
|
'iat': datetime.utcnow(),
|
||||||
|
}
|
||||||
|
|
||||||
|
access_token = jwt.encode(access_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
|
||||||
|
# Générer le Refresh Token (exp: 7 jours)
|
||||||
|
refresh_payload = {
|
||||||
|
'user_id': user.id,
|
||||||
|
'type': 'refresh',
|
||||||
|
'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
|
||||||
|
'iat': datetime.utcnow(),
|
||||||
|
}
|
||||||
|
refresh_token = jwt.encode(refresh_payload, settings.SIMPLE_JWT['SIGNING_KEY'], algorithm=settings.SIMPLE_JWT['ALGORITHM'])
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'token': access_token,
|
||||||
|
'refresh': refresh_token
|
||||||
|
}, safe=False)
|
||||||
|
|
||||||
else:
|
|
||||||
retour = error.returnMessage[error.PROFIL_INACTIVE]
|
|
||||||
else:
|
else:
|
||||||
retour = error.returnMessage[error.WRONG_ID]
|
retour = error.returnMessage[error.WRONG_ID]
|
||||||
|
|
||||||
@ -235,7 +243,6 @@ class LoginView(APIView):
|
|||||||
'errorMessage': retour,
|
'errorMessage': retour,
|
||||||
}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
class RefreshJWTView(APIView):
|
class RefreshJWTView(APIView):
|
||||||
@swagger_auto_schema(
|
@swagger_auto_schema(
|
||||||
operation_description="Rafraîchir le token d'accès",
|
operation_description="Rafraîchir le token d'accès",
|
||||||
@ -295,13 +302,20 @@ class RefreshJWTView(APIView):
|
|||||||
|
|
||||||
# Récupérer les informations utilisateur
|
# Récupérer les informations utilisateur
|
||||||
user = Profile.objects.get(id=payload['user_id'])
|
user = Profile.objects.get(id=payload['user_id'])
|
||||||
|
role_type = payload.get('role_type')
|
||||||
|
|
||||||
|
# Récupérer le rôle principal de l'utilisateur
|
||||||
|
primary_role = ProfileRole.objects.filter(profile=user, role_type=role_type).first()
|
||||||
|
|
||||||
|
if not primary_role:
|
||||||
|
return JsonResponse({'errorMessage': 'No role assigned to the user'}, status=400)
|
||||||
|
|
||||||
# Générer un nouveau Access Token avec les informations complètes
|
# Générer un nouveau Access Token avec les informations complètes
|
||||||
new_access_payload = {
|
new_access_payload = {
|
||||||
'user_id': user.id,
|
'user_id': user.id,
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'droit': user.droit,
|
'role_type': primary_role.get_role_type_display(),
|
||||||
'establishment': user.establishment.id,
|
'establishment': primary_role.establishment.id,
|
||||||
'type': 'access',
|
'type': 'access',
|
||||||
'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
|
'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
|
||||||
'iat': datetime.utcnow(),
|
'iat': datetime.utcnow(),
|
||||||
@ -311,6 +325,7 @@ class RefreshJWTView(APIView):
|
|||||||
|
|
||||||
new_refresh_payload = {
|
new_refresh_payload = {
|
||||||
'user_id': user.id,
|
'user_id': user.id,
|
||||||
|
'role_type': role_type,
|
||||||
'type': 'refresh',
|
'type': 'refresh',
|
||||||
'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
|
'exp': datetime.utcnow() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
|
||||||
'iat': datetime.utcnow(),
|
'iat': datetime.utcnow(),
|
||||||
@ -335,6 +350,14 @@ class RefreshJWTView(APIView):
|
|||||||
class SubscribeView(APIView):
|
class SubscribeView(APIView):
|
||||||
@swagger_auto_schema(
|
@swagger_auto_schema(
|
||||||
operation_description="Inscription utilisateur",
|
operation_description="Inscription utilisateur",
|
||||||
|
manual_parameters=[
|
||||||
|
openapi.Parameter(
|
||||||
|
'establishment_id', openapi.IN_QUERY,
|
||||||
|
description="ID de l'établissement",
|
||||||
|
type=openapi.TYPE_INTEGER,
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
],
|
||||||
request_body=openapi.Schema(
|
request_body=openapi.Schema(
|
||||||
type=openapi.TYPE_OBJECT,
|
type=openapi.TYPE_OBJECT,
|
||||||
required=['email', 'password1', 'password2'],
|
required=['email', 'password1', 'password2'],
|
||||||
@ -359,37 +382,54 @@ class SubscribeView(APIView):
|
|||||||
def post(self, request):
|
def post(self, request):
|
||||||
retourErreur = error.returnMessage[error.BAD_URL]
|
retourErreur = error.returnMessage[error.BAD_URL]
|
||||||
retour = ''
|
retour = ''
|
||||||
newProfilConnection=JSONParser().parse(request)
|
newProfilConnection = JSONParser().parse(request)
|
||||||
|
establishment_id = request.GET.get('establishment_id')
|
||||||
|
|
||||||
|
if not establishment_id:
|
||||||
|
return JsonResponse({'message': retour, 'errorMessage': 'establishment_id manquant', "errorFields": {}, "id": -1}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
validatorSubscription = validator.ValidatorSubscription(data=newProfilConnection)
|
validatorSubscription = validator.ValidatorSubscription(data=newProfilConnection)
|
||||||
validationOk, errorFields = validatorSubscription.validate()
|
validationOk, errorFields = validatorSubscription.validate()
|
||||||
|
|
||||||
|
|
||||||
if validationOk:
|
if validationOk:
|
||||||
|
|
||||||
# On vérifie que l'email existe : si ce n'est pas le cas, on retourne une erreur
|
# On vérifie que l'email existe : si ce n'est pas le cas, on retourne une erreur
|
||||||
profil = bdd.getProfile(Profile.objects.all(), newProfilConnection.get('email'))
|
profil = bdd.getProfile(Profile.objects.all(), newProfilConnection.get('email'))
|
||||||
if profil == None:
|
if profil is None:
|
||||||
retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS]
|
retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS]
|
||||||
else:
|
else:
|
||||||
if profil.is_active:
|
# Vérifier si le profil a déjà un rôle actif pour l'établissement donné
|
||||||
retourErreur=error.returnMessage[error.PROFIL_ACTIVE]
|
active_roles = ProfileRole.objects.filter(profile=profil, establishment_id=establishment_id, is_active=True)
|
||||||
return JsonResponse({'message':retour,'errorMessage':retourErreur, "errorFields":errorFields, "id":profil.id}, safe=False)
|
if active_roles.exists():
|
||||||
|
retourErreur = error.returnMessage[error.PROFIL_ACTIVE]
|
||||||
|
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": profil.id}, safe=False)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
profil.set_password(newProfilConnection.get('password1'))
|
profil.set_password(newProfilConnection.get('password1'))
|
||||||
profil.is_active = True
|
|
||||||
profil.full_clean()
|
profil.full_clean()
|
||||||
profil.save()
|
profil.save()
|
||||||
|
|
||||||
|
# Utiliser le sérialiseur ProfileRoleSerializer pour créer ou mettre à jour le rôle
|
||||||
|
role_data = {
|
||||||
|
'profile': profil.id,
|
||||||
|
'establishment_id': establishment_id,
|
||||||
|
'role_type': ProfileRole.RoleType.PROFIL_PARENT,
|
||||||
|
'is_active': True
|
||||||
|
}
|
||||||
|
role_serializer = ProfileRoleSerializer(data=role_data)
|
||||||
|
if role_serializer.is_valid():
|
||||||
|
role_serializer.save()
|
||||||
|
else:
|
||||||
|
return JsonResponse(role_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
clear_cache()
|
clear_cache()
|
||||||
retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE]
|
retour = error.returnMessage[error.MESSAGE_ACTIVATION_PROFILE]
|
||||||
retourErreur=''
|
retourErreur = ''
|
||||||
return JsonResponse({'message':retour,'errorMessage':retourErreur, "errorFields":errorFields, "id":profil.id}, safe=False)
|
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": profil.id}, safe=False)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
retourErreur = error.returnMessage[error.WRONG_MAIL_FORMAT]
|
retourErreur = error.returnMessage[error.WRONG_MAIL_FORMAT]
|
||||||
return JsonResponse({'message':retour,'errorMessage':retourErreur, "errorFields":errorFields}, safe=False)
|
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields}, safe=False)
|
||||||
|
|
||||||
return JsonResponse({'message':retour, 'errorMessage':retourErreur, "errorFields":errorFields, "id":-1}, safe=False)
|
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields, "id": -1}, safe=False)
|
||||||
|
|
||||||
@method_decorator(csrf_protect, name='dispatch')
|
@method_decorator(csrf_protect, name='dispatch')
|
||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
@ -417,26 +457,29 @@ class NewPasswordView(APIView):
|
|||||||
def post(self, request):
|
def post(self, request):
|
||||||
retourErreur = error.returnMessage[error.BAD_URL]
|
retourErreur = error.returnMessage[error.BAD_URL]
|
||||||
retour = ''
|
retour = ''
|
||||||
newProfilConnection=JSONParser().parse(request)
|
newProfilConnection = JSONParser().parse(request)
|
||||||
|
|
||||||
validatorNewPassword = validator.ValidatorNewPassword(data=newProfilConnection)
|
validatorNewPassword = validator.ValidatorNewPassword(data=newProfilConnection)
|
||||||
validationOk, errorFields = validatorNewPassword.validate()
|
validationOk, errorFields = validatorNewPassword.validate()
|
||||||
if validationOk:
|
if validationOk:
|
||||||
|
|
||||||
profil = bdd.getProfile(Profile.objects.all(), newProfilConnection.get('email'))
|
profil = bdd.getProfile(Profile.objects.all(), newProfilConnection.get('email'))
|
||||||
if profil == None:
|
if profil is None:
|
||||||
retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS]
|
retourErreur = error.returnMessage[error.PROFIL_NOT_EXISTS]
|
||||||
else:
|
else:
|
||||||
# Génération d'une URL provisoire pour modifier le mot de passe
|
try:
|
||||||
profil.code = util.genereRandomCode(12)
|
# Génération d'une URL provisoire pour modifier le mot de passe
|
||||||
profil.datePeremption = util.calculeDatePeremption(util._now(), settings.EXPIRATION_URL_NB_DAYS)
|
profil.code = util.genereRandomCode(12)
|
||||||
profil.save()
|
profil.datePeremption = util.calculeDatePeremption(util._now(), settings.EXPIRATION_URL_NB_DAYS)
|
||||||
clear_cache()
|
profil.save()
|
||||||
retourErreur = ''
|
clear_cache()
|
||||||
retour = error.returnMessage[error.MESSAGE_REINIT_PASSWORD]%(newProfilConnection.get('email'))
|
retourErreur = ''
|
||||||
mailer.envoieReinitMotDePasse(newProfilConnection.get('email'), profil.code)
|
retour = error.returnMessage[error.MESSAGE_REINIT_PASSWORD] % (newProfilConnection.get('email'))
|
||||||
|
mailer.envoieReinitMotDePasse(newProfilConnection.get('email'), profil.code)
|
||||||
|
except ValidationError as e:
|
||||||
|
retourErreur = error.returnMessage[error.WRONG_MAIL_FORMAT]
|
||||||
|
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields}, safe=False)
|
||||||
|
|
||||||
return JsonResponse({'message':retour, 'errorMessage':retourErreur, "errorFields":errorFields}, safe=False)
|
return JsonResponse({'message': retour, 'errorMessage': retourErreur, "errorFields": errorFields}, safe=False)
|
||||||
|
|
||||||
@method_decorator(csrf_protect, name='dispatch')
|
@method_decorator(csrf_protect, name='dispatch')
|
||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
@ -465,7 +508,7 @@ class ResetPasswordView(APIView):
|
|||||||
def post(self, request, code):
|
def post(self, request, code):
|
||||||
retourErreur = error.returnMessage[error.BAD_URL]
|
retourErreur = error.returnMessage[error.BAD_URL]
|
||||||
retour = ''
|
retour = ''
|
||||||
newProfilConnection=JSONParser().parse(request)
|
newProfilConnection = JSONParser().parse(request)
|
||||||
|
|
||||||
validatorResetPassword = validator.ValidatorResetPassword(data=newProfilConnection)
|
validatorResetPassword = validator.ValidatorResetPassword(data=newProfilConnection)
|
||||||
validationOk, errorFields = validatorResetPassword.validate()
|
validationOk, errorFields = validatorResetPassword.validate()
|
||||||
@ -474,16 +517,15 @@ class ResetPasswordView(APIView):
|
|||||||
if profil:
|
if profil:
|
||||||
|
|
||||||
if datetime.strptime(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'), '%d-%m-%Y %H:%M') > datetime.strptime(profil.datePeremption, '%d-%m-%Y %H:%M'):
|
if datetime.strptime(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'), '%d-%m-%Y %H:%M') > datetime.strptime(profil.datePeremption, '%d-%m-%Y %H:%M'):
|
||||||
retourErreur = error.returnMessage[error.EXPIRED_URL]%(_uuid)
|
retourErreur = error.returnMessage[error.EXPIRED_URL] % (_uuid)
|
||||||
elif validationOk:
|
elif validationOk:
|
||||||
retour = error.returnMessage[error.PASSWORD_CHANGED]
|
retour = error.returnMessage[error.PASSWORD_CHANGED]
|
||||||
|
|
||||||
profil.set_password(newProfilConnection.get('password1'))
|
profil.set_password(newProfilConnection.get('password1'))
|
||||||
profil.code = ''
|
profil.code = ''
|
||||||
profil.datePeremption = ''
|
profil.datePeremption = ''
|
||||||
profil.is_active = True
|
|
||||||
profil.save()
|
profil.save()
|
||||||
clear_cache()
|
clear_cache()
|
||||||
retourErreur=''
|
retourErreur = ''
|
||||||
|
|
||||||
return JsonResponse({'message':retour, "errorMessage":retourErreur, "errorFields":errorFields}, safe=False)
|
return JsonResponse({'message': retour, "errorMessage": retourErreur, "errorFields": errorFields}, safe=False)
|
||||||
|
|||||||
1
Back-End/Establishment/__init__.py
Normal file
1
Back-End/Establishment/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
default_app_config = 'Establishment.apps.EstablishmentConfig'
|
||||||
3
Back-End/Establishment/admin.py
Normal file
3
Back-End/Establishment/admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
7
Back-End/Establishment/apps.py
Normal file
7
Back-End/Establishment/apps.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
class EstablishmentConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'Establishment'
|
||||||
|
|
||||||
|
|
||||||
20
Back-End/Establishment/models.py
Normal file
20
Back-End/Establishment/models.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.contrib.postgres.fields import ArrayField
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
class StructureType(models.IntegerChoices):
|
||||||
|
MATERNELLE = 1, _('Maternelle')
|
||||||
|
PRIMAIRE = 2, _('Primaire')
|
||||||
|
SECONDAIRE = 3, _('Secondaire')
|
||||||
|
|
||||||
|
class Establishment(models.Model):
|
||||||
|
name = models.CharField(max_length=255, unique=True)
|
||||||
|
address = models.CharField(max_length=255)
|
||||||
|
total_capacity = models.IntegerField()
|
||||||
|
establishment_type = ArrayField(models.IntegerField(choices=StructureType.choices))
|
||||||
|
licence_code = models.CharField(max_length=100, blank=True)
|
||||||
|
is_active = models.BooleanField(default=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
91
Back-End/Establishment/serializers.py
Normal file
91
Back-End/Establishment/serializers.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from .models import Establishment
|
||||||
|
from School.models import SchoolClass, Teacher, Speciality, Fee, Discount, PaymentMode, PaymentPlan
|
||||||
|
from Subscriptions.models import RegistrationForm, RegistrationFileGroup
|
||||||
|
from Auth.models import Profile
|
||||||
|
|
||||||
|
class EstablishmentSerializer(serializers.ModelSerializer):
|
||||||
|
profile_count = serializers.SerializerMethodField()
|
||||||
|
profiles = serializers.SerializerMethodField()
|
||||||
|
school_class_count = serializers.SerializerMethodField()
|
||||||
|
school_classes = serializers.SerializerMethodField()
|
||||||
|
teacher_count = serializers.SerializerMethodField()
|
||||||
|
teachers = serializers.SerializerMethodField()
|
||||||
|
speciality_count = serializers.SerializerMethodField()
|
||||||
|
specialities = serializers.SerializerMethodField()
|
||||||
|
fee_count = serializers.SerializerMethodField()
|
||||||
|
fees = serializers.SerializerMethodField()
|
||||||
|
discount_count = serializers.SerializerMethodField()
|
||||||
|
discounts = serializers.SerializerMethodField()
|
||||||
|
active_payment_mode_count = serializers.SerializerMethodField()
|
||||||
|
active_payment_modes = serializers.SerializerMethodField()
|
||||||
|
active_payment_plan_count = serializers.SerializerMethodField()
|
||||||
|
active_payment_plans = serializers.SerializerMethodField()
|
||||||
|
file_group_count = serializers.SerializerMethodField()
|
||||||
|
file_groups = serializers.SerializerMethodField()
|
||||||
|
registration_form_count = serializers.SerializerMethodField()
|
||||||
|
registration_forms = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Establishment
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def get_profile_count(self, obj):
|
||||||
|
return Profile.objects.filter(roles__establishment=obj).distinct().count()
|
||||||
|
|
||||||
|
def get_profiles(self, obj):
|
||||||
|
return list(Profile.objects.filter(roles__establishment=obj).distinct().values_list('email', flat=True))
|
||||||
|
|
||||||
|
def get_school_class_count(self, obj):
|
||||||
|
return SchoolClass.objects.filter(establishment=obj).distinct().count()
|
||||||
|
|
||||||
|
def get_school_classes(self, obj):
|
||||||
|
return list(SchoolClass.objects.filter(establishment=obj).distinct().values_list('atmosphere_name', flat=True))
|
||||||
|
|
||||||
|
def get_teacher_count(self, obj):
|
||||||
|
return Teacher.objects.filter(profile_role__establishment=obj).distinct().count()
|
||||||
|
|
||||||
|
def get_teachers(self, obj):
|
||||||
|
return list(Teacher.objects.filter(profile_role__establishment=obj).distinct().values_list('last_name', 'first_name'))
|
||||||
|
|
||||||
|
def get_speciality_count(self, obj):
|
||||||
|
return Speciality.objects.filter(establishment=obj).distinct().count()
|
||||||
|
|
||||||
|
def get_specialities(self, obj):
|
||||||
|
return list(Speciality.objects.filter(establishment=obj).distinct().values_list('name', flat=True))
|
||||||
|
|
||||||
|
def get_fee_count(self, obj):
|
||||||
|
return Fee.objects.filter(establishment=obj).distinct().count()
|
||||||
|
|
||||||
|
def get_fees(self, obj):
|
||||||
|
return list(Fee.objects.filter(establishment=obj).distinct().values_list('name', flat=True))
|
||||||
|
|
||||||
|
def get_discount_count(self, obj):
|
||||||
|
return Discount.objects.filter(establishment=obj).distinct().count()
|
||||||
|
|
||||||
|
def get_discounts(self, obj):
|
||||||
|
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()
|
||||||
|
|
||||||
|
def get_active_payment_modes(self, obj):
|
||||||
|
return list(PaymentMode.objects.filter(establishment=obj, is_active=True).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()
|
||||||
|
|
||||||
|
def get_active_payment_plans(self, obj):
|
||||||
|
return list(PaymentPlan.objects.filter(establishment=obj, is_active=True).distinct().values_list('frequency', flat=True))
|
||||||
|
|
||||||
|
def get_file_group_count(self, obj):
|
||||||
|
return RegistrationFileGroup.objects.filter(establishment=obj).distinct().count()
|
||||||
|
|
||||||
|
def get_file_groups(self, obj):
|
||||||
|
return list(RegistrationFileGroup.objects.filter(establishment=obj).distinct().values_list('name', flat=True))
|
||||||
|
|
||||||
|
def get_registration_form_count(self, obj):
|
||||||
|
return RegistrationForm.objects.filter(establishment=obj).distinct().count()
|
||||||
|
|
||||||
|
def get_registration_forms(self, obj):
|
||||||
|
return list(RegistrationForm.objects.filter(establishment=obj).distinct().values_list('student__last_name', 'student__first_name'))
|
||||||
7
Back-End/Establishment/urls.py
Normal file
7
Back-End/Establishment/urls.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from django.urls import path, re_path
|
||||||
|
from .views import EstablishmentListCreateView, EstablishmentDetailView
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
re_path(r'^establishments$', EstablishmentListCreateView.as_view(), name='establishment_list_create'),
|
||||||
|
re_path(r'^establishments/(?P<id>[0-9]+)$', EstablishmentDetailView.as_view(), name="establishment_detail"),
|
||||||
|
]
|
||||||
51
Back-End/Establishment/views.py
Normal file
51
Back-End/Establishment/views.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
from django.http.response import JsonResponse
|
||||||
|
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from rest_framework.parsers import JSONParser
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework import status
|
||||||
|
from .models import Establishment
|
||||||
|
from .serializers import EstablishmentSerializer
|
||||||
|
from N3wtSchool.bdd import delete_object, getAllObjects, getObject
|
||||||
|
|
||||||
|
@method_decorator(csrf_protect, name='dispatch')
|
||||||
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
|
class EstablishmentListCreateView(APIView):
|
||||||
|
def get(self, request):
|
||||||
|
establishments = getAllObjects(Establishment)
|
||||||
|
establishments_serializer = EstablishmentSerializer(establishments, many=True)
|
||||||
|
return JsonResponse(establishments_serializer.data, safe=False, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
establishment_data = JSONParser().parse(request)
|
||||||
|
establishment_serializer = EstablishmentSerializer(data=establishment_data)
|
||||||
|
if establishment_serializer.is_valid():
|
||||||
|
establishment_serializer.save()
|
||||||
|
return JsonResponse(establishment_serializer.data, safe=False, status=status.HTTP_201_CREATED)
|
||||||
|
return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@method_decorator(csrf_protect, name='dispatch')
|
||||||
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
|
class EstablishmentDetailView(APIView):
|
||||||
|
def get(self, request, id=None):
|
||||||
|
try:
|
||||||
|
establishment = Establishment.objects.get(id=id)
|
||||||
|
establishment_serializer = EstablishmentSerializer(establishment)
|
||||||
|
return JsonResponse(establishment_serializer.data, safe=False)
|
||||||
|
except Establishment.DoesNotExist:
|
||||||
|
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
def put(self, request, id):
|
||||||
|
establishment_data = JSONParser().parse(request)
|
||||||
|
try:
|
||||||
|
establishment = Establishment.objects.get(id=id)
|
||||||
|
except Establishment.DoesNotExist:
|
||||||
|
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
establishment_serializer = EstablishmentSerializer(establishment, data=establishment_data, partial=True)
|
||||||
|
if establishment_serializer.is_valid():
|
||||||
|
establishment_serializer.save()
|
||||||
|
return JsonResponse(establishment_serializer.data, safe=False)
|
||||||
|
return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def delete(self, request, id):
|
||||||
|
return delete_object(Establishment, id)
|
||||||
@ -4,20 +4,20 @@ from .models import Notification, TypeNotif
|
|||||||
from GestionMessagerie.models import Messagerie
|
from GestionMessagerie.models import Messagerie
|
||||||
from Subscriptions.models import RegistrationForm
|
from Subscriptions.models import RegistrationForm
|
||||||
|
|
||||||
@receiver(post_save, sender=Messagerie)
|
# @receiver(post_save, sender=Messagerie)
|
||||||
def notification_MESSAGE(sender, instance, created, **kwargs):
|
# def notification_MESSAGE(sender, instance, created, **kwargs):
|
||||||
if created:
|
# if created:
|
||||||
Notification.objects.create(
|
# Notification.objects.create(
|
||||||
user=instance.destinataire,
|
# user=instance.destinataire,
|
||||||
message=(TypeNotif.NOTIF_MESSAGE).label,
|
# message=(TypeNotif.NOTIF_MESSAGE).label,
|
||||||
typeNotification=TypeNotif.NOTIF_MESSAGE
|
# typeNotification=TypeNotif.NOTIF_MESSAGE
|
||||||
)
|
# )
|
||||||
|
|
||||||
@receiver(post_save, sender=RegistrationForm)
|
# @receiver(post_save, sender=RegistrationForm)
|
||||||
def notification_DI(sender, instance, created, **kwargs):
|
# def notification_DI(sender, instance, created, **kwargs):
|
||||||
for responsable in instance.student.guardians.all():
|
# for responsable in instance.student.guardians.all():
|
||||||
Notification.objects.create(
|
# Notification.objects.create(
|
||||||
user=responsable.associated_profile,
|
# user=responsable.associated_profile,
|
||||||
message=(TypeNotif.NOTIF_DI).label,
|
# message=(TypeNotif.NOTIF_DI).label,
|
||||||
typeNotification=TypeNotif.NOTIF_DI
|
# typeNotification=TypeNotif.NOTIF_DI
|
||||||
)
|
# )
|
||||||
|
|||||||
@ -44,6 +44,7 @@ INSTALLED_APPS = [
|
|||||||
'GestionNotification.apps.GestionNotificationConfig',
|
'GestionNotification.apps.GestionNotificationConfig',
|
||||||
'School.apps.SchoolConfig',
|
'School.apps.SchoolConfig',
|
||||||
'Planning.apps.PlanningConfig',
|
'Planning.apps.PlanningConfig',
|
||||||
|
'Establishment.apps.EstablishmentConfig',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
|
|||||||
@ -46,6 +46,7 @@ urlpatterns = [
|
|||||||
path("School/", include(("School.urls", 'School'), namespace='School')),
|
path("School/", include(("School.urls", 'School'), namespace='School')),
|
||||||
path("DocuSeal/", include(("DocuSeal.urls", 'DocuSeal'), namespace='DocuSeal')),
|
path("DocuSeal/", include(("DocuSeal.urls", 'DocuSeal'), namespace='DocuSeal')),
|
||||||
path("Planning/", include(("Planning.urls", 'Planning'), namespace='Planning')),
|
path("Planning/", include(("Planning.urls", 'Planning'), namespace='Planning')),
|
||||||
|
path("Establishment/", include(("Establishment.urls", 'Establishment'), namespace='Establishment')),
|
||||||
# Documentation Api
|
# Documentation Api
|
||||||
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
|
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
|
||||||
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from django.db import models
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from School.models import Establishment
|
from Establishment.models import Establishment
|
||||||
|
|
||||||
class RecursionType(models.IntegerChoices):
|
class RecursionType(models.IntegerChoices):
|
||||||
RECURSION_NONE = 0, _('Aucune')
|
RECURSION_NONE = 0, _('Aucune')
|
||||||
|
|||||||
@ -1,14 +1,5 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.db.models.signals import post_migrate
|
|
||||||
|
|
||||||
def create_speciality(sender, **kwargs):
|
|
||||||
from .models import Speciality
|
|
||||||
if not Speciality.objects.filter(name='GROUPE').exists():
|
|
||||||
Speciality.objects.create(name='GROUPE', color_code='#FF0000')
|
|
||||||
|
|
||||||
class SchoolConfig(AppConfig):
|
class SchoolConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'School'
|
name = 'School'
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
post_migrate.connect(create_speciality, sender=self)
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from Subscriptions.models import (
|
from Subscriptions.models import (
|
||||||
RegistrationForm,
|
RegistrationForm,
|
||||||
Student,
|
Student,
|
||||||
@ -10,9 +9,8 @@ from Subscriptions.models import (
|
|||||||
RegistrationTemplateMaster,
|
RegistrationTemplateMaster,
|
||||||
RegistrationTemplate
|
RegistrationTemplate
|
||||||
)
|
)
|
||||||
from Auth.models import Profile
|
from Auth.models import Profile, ProfileRole
|
||||||
from School.models import (
|
from School.models import (
|
||||||
Establishment,
|
|
||||||
FeeType,
|
FeeType,
|
||||||
Speciality,
|
Speciality,
|
||||||
Teacher,
|
Teacher,
|
||||||
@ -21,7 +19,6 @@ from School.models import (
|
|||||||
PaymentModeType,
|
PaymentModeType,
|
||||||
PaymentPlan,
|
PaymentPlan,
|
||||||
PaymentPlanType,
|
PaymentPlanType,
|
||||||
StructureType,
|
|
||||||
DiscountType
|
DiscountType
|
||||||
)
|
)
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@ -34,6 +31,19 @@ from faker import Faker
|
|||||||
import random
|
import random
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from School.serializers import (
|
||||||
|
FeeSerializer,
|
||||||
|
DiscountSerializer,
|
||||||
|
PaymentModeSerializer,
|
||||||
|
PaymentPlanSerializer,
|
||||||
|
SpecialitySerializer,
|
||||||
|
TeacherSerializer,
|
||||||
|
SchoolClassSerializer
|
||||||
|
)
|
||||||
|
from Auth.serializers import ProfileSerializer, ProfileRoleSerializer
|
||||||
|
from Establishment.serializers import EstablishmentSerializer
|
||||||
|
from Subscriptions.serializers import RegistrationFormSerializer, GuardianSerializer
|
||||||
|
|
||||||
# Définir le chemin vers le dossier mock_datas
|
# Définir le chemin vers le dossier mock_datas
|
||||||
MOCK_DATAS_PATH = os.path.join(settings.BASE_DIR, 'School', 'management', 'mock_datas')
|
MOCK_DATAS_PATH = os.path.join(settings.BASE_DIR, 'School', 'management', 'mock_datas')
|
||||||
|
|
||||||
@ -41,176 +51,306 @@ class Command(BaseCommand):
|
|||||||
help = 'Initialise toutes les données mock'
|
help = 'Initialise toutes les données mock'
|
||||||
|
|
||||||
def handle(self, *args, **kwargs):
|
def handle(self, *args, **kwargs):
|
||||||
self.create_or_update_establishments()
|
self.init_establishments()
|
||||||
self.create_or_update_fees()
|
self.init_profiles()
|
||||||
self.create_or_update_discounts()
|
self.init_fees()
|
||||||
self.create_or_update_payment_modes()
|
self.init_discounts()
|
||||||
self.create_or_update_payment_plans()
|
self.init_payment_modes()
|
||||||
self.create_or_update_specialities()
|
self.init_payment_plans()
|
||||||
self.create_or_update_teachers()
|
self.init_specialities()
|
||||||
self.create_or_update_school_classes()
|
self.init_teachers()
|
||||||
self.create_or_update_registration_file_group()
|
self.init_guardians()
|
||||||
self.create_register_form()
|
self.init_school_classes()
|
||||||
|
self.init_file_group()
|
||||||
|
self.init_register_form()
|
||||||
|
|
||||||
def load_data(self, filename):
|
def load_data(self, filename):
|
||||||
with open(os.path.join(MOCK_DATAS_PATH, filename), 'r') as file:
|
with open(os.path.join(MOCK_DATAS_PATH, filename), 'r') as file:
|
||||||
return json.load(file)
|
return json.load(file)
|
||||||
|
|
||||||
def create_or_update_establishments(self):
|
def init_establishments(self):
|
||||||
establishments_data = self.load_data('establishments.json')
|
establishments_data = self.load_data('establishments.json')
|
||||||
|
|
||||||
self.establishments = []
|
self.establishments = []
|
||||||
for establishment_data in establishments_data:
|
for establishment_data in establishments_data:
|
||||||
establishment, created = Establishment.objects.update_or_create(
|
serializer = EstablishmentSerializer(data=establishment_data)
|
||||||
name=establishment_data["name"],
|
if serializer.is_valid():
|
||||||
defaults=establishment_data
|
establishment = serializer.save()
|
||||||
)
|
self.establishments.append(establishment)
|
||||||
self.establishments.append(establishment)
|
self.stdout.write(self.style.SUCCESS(f'Establishment {establishment.name} created or updated successfully'))
|
||||||
if created:
|
|
||||||
self.stdout.write(self.style.SUCCESS(f'Establishment {establishment.name} created successfully'))
|
|
||||||
else:
|
else:
|
||||||
self.stdout.write(self.style.SUCCESS(f'Establishment {establishment.name} updated successfully'))
|
self.stdout.write(self.style.ERROR(f'Error in data for establishment: {serializer.errors}'))
|
||||||
|
|
||||||
def create_or_update_fees(self):
|
def init_profiles(self):
|
||||||
|
profiles_data = self.load_data('profiles.json')
|
||||||
|
|
||||||
|
for profile_data in profiles_data:
|
||||||
|
# Randomize the number of roles to create (between 1 et 3)
|
||||||
|
num_roles = random.randint(1, 3)
|
||||||
|
selected_roles = []
|
||||||
|
|
||||||
|
for _ in range(num_roles):
|
||||||
|
establishment = random.choice(self.establishments)
|
||||||
|
role_type = random.choice([ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN, ProfileRole.RoleType.PROFIL_PARENT])
|
||||||
|
|
||||||
|
# Ensure no duplicate ADMIN role for the same establishment
|
||||||
|
if role_type == ProfileRole.RoleType.PROFIL_ADMIN:
|
||||||
|
if any(role['role_type'] == ProfileRole.RoleType.PROFIL_ADMIN and role['establishment'] == establishment.id for role in selected_roles):
|
||||||
|
continue
|
||||||
|
|
||||||
|
selected_roles.append({
|
||||||
|
"role_type": role_type,
|
||||||
|
"establishment": establishment.id,
|
||||||
|
"establishment_name": establishment.name
|
||||||
|
})
|
||||||
|
|
||||||
|
# Generate email based on the selected roles and establishment
|
||||||
|
role_types = '-'.join([f"{ProfileRole.RoleType(role['role_type']).name.replace('PROFIL_', '')}_{role['establishment_name'].replace(' ', '')}" for role in selected_roles])
|
||||||
|
email = f"{profile_data['username']}-{role_types}@exemple.com"
|
||||||
|
|
||||||
|
# Add email to profile data
|
||||||
|
profile_data['email'] = email
|
||||||
|
|
||||||
|
serializer = ProfileSerializer(data=profile_data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
profile = serializer.save()
|
||||||
|
profile.set_password(profile_data["password"])
|
||||||
|
profile.save()
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'Profile {profile.email} created successfully'))
|
||||||
|
|
||||||
|
# Create or update the profile role for each selected role using ProfileRoleSerializer
|
||||||
|
for role in selected_roles:
|
||||||
|
role_data = {
|
||||||
|
"profile": profile.id,
|
||||||
|
"establishment": role["establishment"],
|
||||||
|
"role_type": role["role_type"],
|
||||||
|
"is_active": True
|
||||||
|
}
|
||||||
|
role_serializer = ProfileRoleSerializer(data=role_data)
|
||||||
|
if role_serializer.is_valid():
|
||||||
|
role_serializer.save()
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.ERROR(f'Error in data for profile role: {role_serializer.errors}'))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.ERROR(f'Error in data for profile: {serializer.errors}'))
|
||||||
|
|
||||||
|
def init_fees(self):
|
||||||
fees_data = self.load_data('fees.json')
|
fees_data = self.load_data('fees.json')
|
||||||
|
|
||||||
for fee_data in fees_data:
|
for fee_data in fees_data:
|
||||||
establishment = random.choice(self.establishments)
|
establishment = random.choice(self.establishments)
|
||||||
|
print(f'establishment : {establishment}')
|
||||||
fee_data["name"] = f"{fee_data['name']} - {establishment.name}"
|
fee_data["name"] = f"{fee_data['name']} - {establishment.name}"
|
||||||
fee_data["establishment"] = establishment
|
fee_data["establishment"] = establishment.id
|
||||||
Fee.objects.update_or_create(
|
fee_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE])
|
||||||
name=fee_data["name"],
|
|
||||||
type=fee_data["type"],
|
|
||||||
defaults=fee_data
|
|
||||||
)
|
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS('Fees initialized or updated successfully'))
|
serializer = FeeSerializer(data=fee_data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
fee = serializer.save()
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'Fee {fee.name} created successfully'))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.ERROR(f'Error in data for fee: {serializer.errors}'))
|
||||||
|
|
||||||
def create_or_update_discounts(self):
|
def init_discounts(self):
|
||||||
discounts_data = self.load_data('discounts.json')
|
discounts_data = self.load_data('discounts.json')
|
||||||
|
|
||||||
for discount_data in discounts_data:
|
for discount_data in discounts_data:
|
||||||
establishment = random.choice(self.establishments)
|
establishment = random.choice(self.establishments)
|
||||||
discount_data["name"] = f"{discount_data['name']} - {establishment.name}"
|
discount_data["name"] = f"{discount_data['name']} - {establishment.name}"
|
||||||
discount_data["establishment"] = establishment
|
discount_data["establishment"] = establishment.id
|
||||||
Discount.objects.update_or_create(
|
discount_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE])
|
||||||
name=discount_data["name"],
|
discount_data["discount_type"] = random.choice([DiscountType.CURRENCY, DiscountType.PERCENT])
|
||||||
type=discount_data["type"],
|
|
||||||
discount_type=discount_data["discount_type"],
|
|
||||||
defaults=discount_data
|
|
||||||
)
|
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS('Discounts initialized or updated successfully'))
|
serializer = DiscountSerializer(data=discount_data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
discount = serializer.save()
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'Discount {discount.name} created successfully'))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.ERROR(f'Error in data for discount: {serializer.errors}'))
|
||||||
|
|
||||||
def create_or_update_payment_modes(self):
|
def init_payment_modes(self):
|
||||||
payment_modes_data = self.load_data('payment_modes.json')
|
modes = [PaymentModeType.SEPA, PaymentModeType.TRANSFER, PaymentModeType.CHECK, PaymentModeType.CASH]
|
||||||
|
types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]
|
||||||
|
|
||||||
for payment_mode_data in payment_modes_data:
|
for establishment in self.establishments:
|
||||||
establishment = random.choice(self.establishments)
|
for mode in modes:
|
||||||
payment_mode_data["establishment"] = establishment
|
for type in types:
|
||||||
PaymentMode.objects.update_or_create(
|
payment_mode_data = {
|
||||||
mode=payment_mode_data["mode"],
|
"mode": mode,
|
||||||
type=payment_mode_data["type"],
|
"type": type,
|
||||||
defaults=payment_mode_data
|
"is_active": random.choice([True, False]),
|
||||||
)
|
"establishment": establishment.id
|
||||||
|
}
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS('Payment Modes initialized or updated successfully'))
|
serializer = PaymentModeSerializer(data=payment_mode_data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
payment_mode = serializer.save()
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'Payment Mode {payment_mode} created successfully'))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.ERROR(f'Error in data for payment mode: {serializer.errors}'))
|
||||||
|
|
||||||
def create_or_update_payment_plans(self):
|
def init_payment_plans(self):
|
||||||
payment_plans_data = self.load_data('payment_plans.json')
|
frequencies = [PaymentPlanType.ONE_TIME, PaymentPlanType.THREE_TIMES, PaymentPlanType.TEN_TIMES, PaymentPlanType.TWELVE_TIMES]
|
||||||
|
types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]
|
||||||
current_date = timezone.now().date()
|
current_date = timezone.now().date()
|
||||||
|
|
||||||
for payment_plan_data in payment_plans_data:
|
for establishment in self.establishments:
|
||||||
establishment = random.choice(self.establishments)
|
for frequency in frequencies:
|
||||||
payment_plan_data["establishment"] = establishment
|
for type in types:
|
||||||
payment_plan_data["due_dates"] = [current_date + relativedelta(months=1)]
|
payment_plan_data = {
|
||||||
if payment_plan_data["frequency"] == PaymentPlanType.THREE_TIMES:
|
"frequency": frequency,
|
||||||
payment_plan_data["due_dates"] = [current_date + relativedelta(months=1+4*i) for i in range(3)]
|
"type": type,
|
||||||
elif payment_plan_data["frequency"] == PaymentPlanType.TEN_TIMES:
|
"is_active": random.choice([True, False]),
|
||||||
payment_plan_data["due_dates"] = [current_date + relativedelta(months=1+i) for i in range(10)]
|
"establishment": establishment.id,
|
||||||
elif payment_plan_data["frequency"] == PaymentPlanType.TWELVE_TIMES:
|
"due_dates": self.generate_due_dates(frequency, current_date)
|
||||||
payment_plan_data["due_dates"] = [current_date + relativedelta(months=1+i) for i in range(12)]
|
}
|
||||||
|
|
||||||
PaymentPlan.objects.update_or_create(
|
serializer = PaymentPlanSerializer(data=payment_plan_data)
|
||||||
frequency=payment_plan_data["frequency"],
|
if serializer.is_valid():
|
||||||
type=payment_plan_data["type"],
|
payment_plan = serializer.save()
|
||||||
defaults=payment_plan_data
|
self.stdout.write(self.style.SUCCESS(f'Payment Plan {payment_plan} created successfully'))
|
||||||
)
|
else:
|
||||||
|
self.stdout.write(self.style.ERROR(f'Error in data for payment plan: {serializer.errors}'))
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS('Payment Plans initialized or updated successfully'))
|
def generate_due_dates(self, frequency, start_date):
|
||||||
|
if frequency == PaymentPlanType.ONE_TIME:
|
||||||
|
return [start_date + relativedelta(months=1)]
|
||||||
|
elif frequency == PaymentPlanType.THREE_TIMES:
|
||||||
|
return [start_date + relativedelta(months=1+4*i) for i in range(3)]
|
||||||
|
elif frequency == PaymentPlanType.TEN_TIMES:
|
||||||
|
return [start_date + relativedelta(months=1+i) for i in range(10)]
|
||||||
|
elif frequency == PaymentPlanType.TWELVE_TIMES:
|
||||||
|
return [start_date + relativedelta(months=1+i) for i in range(12)]
|
||||||
|
|
||||||
def create_or_update_specialities(self):
|
def init_specialities(self):
|
||||||
specialities_data = self.load_data('specialities.json')
|
specialities_data = self.load_data('specialities.json')
|
||||||
|
|
||||||
for speciality_data in specialities_data:
|
for speciality_data in specialities_data:
|
||||||
Speciality.objects.update_or_create(
|
|
||||||
name=speciality_data["name"],
|
|
||||||
defaults=speciality_data
|
|
||||||
)
|
|
||||||
self.stdout.write(self.style.SUCCESS('Specialities initialized or updated successfully'))
|
|
||||||
|
|
||||||
def create_or_update_teachers(self):
|
|
||||||
teachers_data = self.load_data('teachers.json')
|
|
||||||
|
|
||||||
for teacher_data in teachers_data:
|
|
||||||
specialities = teacher_data.pop("specialities")
|
|
||||||
email = teacher_data["email"]
|
|
||||||
droit = teacher_data.pop("droit")
|
|
||||||
|
|
||||||
establishment = random.choice(self.establishments)
|
establishment = random.choice(self.establishments)
|
||||||
|
speciality_data["name"] = f"{speciality_data['name']} - {establishment.name}"
|
||||||
|
speciality_data["establishment"] = establishment.id
|
||||||
|
|
||||||
# Create or update the user profile
|
serializer = SpecialitySerializer(data=speciality_data)
|
||||||
user, created = Profile.objects.update_or_create(
|
if serializer.is_valid():
|
||||||
email=email,
|
speciality = serializer.save()
|
||||||
defaults={
|
self.stdout.write(self.style.SUCCESS(f'Speciality {speciality.name} created successfully'))
|
||||||
"username": email,
|
else:
|
||||||
"email": email,
|
self.stdout.write(self.style.ERROR(f'Error in data for speciality: {serializer.errors}'))
|
||||||
"is_active": True,
|
|
||||||
"password": "Provisoire01!",
|
def init_teachers(self):
|
||||||
"droit": droit,
|
fake = Faker()
|
||||||
"establishment": establishment
|
|
||||||
|
# Récupérer tous les profils dont le role_type est soit ECOLE soit ADMIN
|
||||||
|
profiles = Profile.objects.filter(roles__role_type__in=[ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN]).distinct()
|
||||||
|
|
||||||
|
for profile in profiles:
|
||||||
|
# Récupérer les rôles associés au profil
|
||||||
|
profile_roles = ProfileRole.objects.filter(profile=profile, role_type__in=[ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN])
|
||||||
|
|
||||||
|
for profile_role in profile_roles:
|
||||||
|
establishment = profile_role.establishment
|
||||||
|
teacher_data = {
|
||||||
|
"last_name": fake.last_name(),
|
||||||
|
"first_name": f"{fake.first_name()} - {establishment.name}",
|
||||||
|
"profile_role": profile_role.id
|
||||||
}
|
}
|
||||||
)
|
|
||||||
if created:
|
|
||||||
user.set_password("Provisoire01!")
|
|
||||||
user.save()
|
|
||||||
|
|
||||||
# Create or update the teacher
|
establishment_specialities = list(Speciality.objects.filter(establishment=establishment))
|
||||||
teacher, created = Teacher.objects.update_or_create(
|
num_specialities = min(random.randint(1, 3), len(establishment_specialities))
|
||||||
email=email,
|
selected_specialities = random.sample(establishment_specialities, num_specialities)
|
||||||
defaults={**teacher_data, "associated_profile_id": user.id}
|
|
||||||
)
|
# Créer l'enseignant si il n'existe pas
|
||||||
teacher.specialities.set(Speciality.objects.filter(name__in=specialities))
|
teacher_serializer = TeacherSerializer(data=teacher_data)
|
||||||
teacher.save()
|
if teacher_serializer.is_valid():
|
||||||
|
teacher = teacher_serializer.save()
|
||||||
|
# Associer les spécialités
|
||||||
|
teacher.specialities.set(selected_specialities)
|
||||||
|
teacher.save()
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'Teacher {teacher.last_name} created successfully for establishment {establishment.name}'))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.ERROR(f'Error in data for teacher: {teacher_serializer.errors}'))
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS('Teachers initialized or updated successfully'))
|
self.stdout.write(self.style.SUCCESS('Teachers initialized or updated successfully'))
|
||||||
|
|
||||||
def create_or_update_school_classes(self):
|
def init_guardians(self):
|
||||||
|
fake = Faker()
|
||||||
|
|
||||||
|
# Récupérer tous les profils dont le role_type est PROFIL_PARENT
|
||||||
|
profiles = Profile.objects.filter(roles__role_type=ProfileRole.RoleType.PROFIL_PARENT).distinct()
|
||||||
|
|
||||||
|
for profile in profiles:
|
||||||
|
# Récupérer les rôles associés au profil
|
||||||
|
profile_roles = ProfileRole.objects.filter(profile=profile, role_type=ProfileRole.RoleType.PROFIL_PARENT)
|
||||||
|
|
||||||
|
for profile_role in profile_roles:
|
||||||
|
establishment = profile_role.establishment
|
||||||
|
guardian_data = {
|
||||||
|
"last_name": fake.last_name(),
|
||||||
|
"first_name": f"{fake.first_name()} - {establishment.name}",
|
||||||
|
"profile_role": profile_role.id,
|
||||||
|
"birth_date": fake.date_of_birth().strftime('%Y-%m-%d'), # Convertir en chaîne de caractères valide
|
||||||
|
"address": fake.address(),
|
||||||
|
"phone": fake.phone_number(),
|
||||||
|
"profession": fake.job()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Créer le guardian si il n'existe pas
|
||||||
|
guardian_serializer = GuardianSerializer(data=guardian_data)
|
||||||
|
if guardian_serializer.is_valid():
|
||||||
|
guardian = guardian_serializer.save()
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'Guardian {guardian.last_name} created successfully for establishment {establishment.name}'))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.ERROR(f'Error in data for guardian: {guardian_serializer.errors}'))
|
||||||
|
|
||||||
|
self.stdout.write(self.style.SUCCESS('Guardians initialized or updated successfully'))
|
||||||
|
|
||||||
|
def init_school_classes(self):
|
||||||
school_classes_data = self.load_data('school_classes.json')
|
school_classes_data = self.load_data('school_classes.json')
|
||||||
|
|
||||||
for index, class_data in enumerate(school_classes_data, start=1):
|
for index, class_data in enumerate(school_classes_data, start=1):
|
||||||
teachers_ids = class_data.pop("teachers")
|
# Randomize establishment
|
||||||
establishment = random.choice(self.establishments)
|
establishment = random.choice(self.establishments)
|
||||||
class_data["atmosphere_name"] = f"Classe {index} - {establishment.name}"
|
class_data["atmosphere_name"] = f"Classe {index} - {establishment.name}"
|
||||||
class_data["establishment"] = establishment
|
class_data["establishment"] = establishment.id
|
||||||
school_class, created = SchoolClass.objects.update_or_create(
|
|
||||||
atmosphere_name=class_data["atmosphere_name"],
|
# Randomize levels
|
||||||
school_year=class_data["school_year"],
|
class_data["levels"] = random.sample(range(1, 10), random.randint(1, 5))
|
||||||
defaults=class_data
|
|
||||||
)
|
# Randomize teachers
|
||||||
school_class.teachers.set(teachers_ids)
|
establishment_teachers = list(Teacher.objects.filter(profile_role__establishment=establishment))
|
||||||
school_class.save()
|
num_teachers = min(random.randint(1, 10), len(establishment_teachers))
|
||||||
|
selected_teachers = random.sample(establishment_teachers, num_teachers)
|
||||||
|
teachers_ids = [teacher.id for teacher in selected_teachers]
|
||||||
|
|
||||||
|
# Use the serializer to create or update the school class
|
||||||
|
class_data["teachers"] = teachers_ids
|
||||||
|
serializer = SchoolClassSerializer(data=class_data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
school_class = serializer.save()
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'SchoolClass {school_class.atmosphere_name} created or updated successfully'))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.ERROR(f'Error in data for school class: {serializer.errors}'))
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS('SchoolClasses initialized or updated successfully'))
|
self.stdout.write(self.style.SUCCESS('SchoolClasses initialized or updated successfully'))
|
||||||
|
|
||||||
def create_or_update_registration_file_group(self):
|
def init_file_group(self):
|
||||||
file_groups_data = self.load_data('file_groups.json')
|
fake = Faker()
|
||||||
|
|
||||||
for group_data in file_groups_data:
|
for establishment in self.establishments:
|
||||||
RegistrationFileGroup.objects.update_or_create(name=group_data["name"], defaults=group_data)
|
for i in range(1, 4): # Créer 3 groupes de fichiers par établissement
|
||||||
self.stdout.write(self.style.SUCCESS(f'RegistrationFileGroup {group_data["name"]} initialized or updated successfully'))
|
name = f"Fichiers d'inscription - {fake.word()} - {establishment.name}"
|
||||||
|
description = fake.sentence()
|
||||||
|
group_data = {
|
||||||
|
"name": name,
|
||||||
|
"description": description,
|
||||||
|
"establishment": establishment
|
||||||
|
}
|
||||||
|
RegistrationFileGroup.objects.update_or_create(name=name, defaults=group_data)
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'RegistrationFileGroup {name} initialized or updated successfully'))
|
||||||
|
|
||||||
def create_register_form(self):
|
self.stdout.write(self.style.SUCCESS('All RegistrationFileGroups initialized or updated successfully'))
|
||||||
|
|
||||||
|
def init_register_form(self):
|
||||||
fake = Faker('fr_FR') # Utiliser le locale français pour Faker
|
fake = Faker('fr_FR') # Utiliser le locale français pour Faker
|
||||||
|
|
||||||
file_group_count = RegistrationFileGroup.objects.count()
|
file_group_count = RegistrationFileGroup.objects.count()
|
||||||
@ -218,40 +358,13 @@ class Command(BaseCommand):
|
|||||||
for _ in range(50):
|
for _ in range(50):
|
||||||
establishment = random.choice(self.establishments)
|
establishment = random.choice(self.establishments)
|
||||||
|
|
||||||
# Générer des données fictives pour le profil
|
# Récupérer un guardian aléatoire déjà créé
|
||||||
profile_data = {
|
guardian = Guardian.objects.order_by('?').first()
|
||||||
"email": fake.email(),
|
|
||||||
"droit": 2,
|
|
||||||
"username": fake.user_name(),
|
|
||||||
"is_active": True,
|
|
||||||
"password": "Provisoire01!",
|
|
||||||
"establishment": establishment
|
|
||||||
}
|
|
||||||
|
|
||||||
user, created = Profile.objects.update_or_create(
|
|
||||||
email=profile_data["email"],
|
|
||||||
defaults={
|
|
||||||
"username": profile_data["username"],
|
|
||||||
"email": profile_data["email"],
|
|
||||||
"is_active": profile_data["is_active"],
|
|
||||||
"droit": profile_data["droit"],
|
|
||||||
"establishment": profile_data["establishment"]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if created:
|
|
||||||
user.set_password(profile_data["password"])
|
|
||||||
user.save()
|
|
||||||
|
|
||||||
# Générer des données fictives pour le guardian
|
|
||||||
guardian_data = {
|
|
||||||
"associated_profile_id": user.id,
|
|
||||||
"email": profile_data["email"],
|
|
||||||
}
|
|
||||||
|
|
||||||
# Générer des données fictives pour l'étudiant
|
# Générer des données fictives pour l'étudiant
|
||||||
student_data = {
|
student_data = {
|
||||||
"last_name": f"{fake.last_name()} - {establishment.name}",
|
"last_name": fake.last_name(),
|
||||||
"first_name": fake.first_name(),
|
"first_name": f"{fake.first_name()} - {establishment.name}",
|
||||||
"address": fake.address(),
|
"address": fake.address(),
|
||||||
"birth_date": fake.date_of_birth(),
|
"birth_date": fake.date_of_birth(),
|
||||||
"birth_place": fake.city(),
|
"birth_place": fake.city(),
|
||||||
@ -261,16 +374,12 @@ class Command(BaseCommand):
|
|||||||
"level": fake.random_int(min=1, max=6)
|
"level": fake.random_int(min=1, max=6)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Créer ou mettre à jour l'étudiant et le guardian
|
# Créer ou mettre à jour l'étudiant
|
||||||
student, created = Student.objects.get_or_create(
|
student, created = Student.objects.get_or_create(
|
||||||
last_name=student_data["last_name"],
|
last_name=student_data["last_name"],
|
||||||
first_name=student_data["first_name"],
|
first_name=student_data["first_name"],
|
||||||
defaults=student_data
|
defaults=student_data
|
||||||
)
|
)
|
||||||
guardian, created = Guardian.objects.get_or_create(
|
|
||||||
last_name=guardian_data["email"],
|
|
||||||
defaults=guardian_data
|
|
||||||
)
|
|
||||||
student.guardians.add(guardian)
|
student.guardians.add(guardian)
|
||||||
|
|
||||||
# Récupérer les frais et les réductions
|
# Récupérer les frais et les réductions
|
||||||
@ -279,18 +388,23 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
# Créer les données du formulaire d'inscription
|
# Créer les données du formulaire d'inscription
|
||||||
register_form_data = {
|
register_form_data = {
|
||||||
"student": student,
|
|
||||||
"fileGroup": RegistrationFileGroup.objects.get(id=fake.random_int(min=1, max=file_group_count)),
|
"fileGroup": RegistrationFileGroup.objects.get(id=fake.random_int(min=1, max=file_group_count)),
|
||||||
"establishment": establishment,
|
"establishment": establishment,
|
||||||
"status": fake.random_int(min=1, max=3)
|
"status": fake.random_int(min=1, max=3)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Créer ou mettre à jour le formulaire d'inscription
|
# Créer ou mettre à jour le formulaire d'inscription
|
||||||
register_form, created = RegistrationForm.objects.get_or_create(student=student, defaults=register_form_data)
|
register_form, created = RegistrationForm.objects.get_or_create(
|
||||||
register_form.fees.set(fees)
|
student=student,
|
||||||
register_form.discounts.set(discounts)
|
establishment=establishment,
|
||||||
if not created:
|
defaults=register_form_data
|
||||||
register_form.fileGroup = file_group
|
)
|
||||||
register_form.save()
|
|
||||||
|
if created:
|
||||||
|
register_form.fees.set(fees)
|
||||||
|
register_form.discounts.set(discounts)
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'RegistrationForm for student {student.last_name} created successfully'))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.SUCCESS(f'RegistrationForm for student {student.last_name} already exists'))
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS('50 RegistrationForms initialized or updated successfully'))
|
self.stdout.write(self.style.SUCCESS('50 RegistrationForms initialized or updated successfully'))
|
||||||
@ -2,15 +2,41 @@
|
|||||||
{
|
{
|
||||||
"name": "Parrainage",
|
"name": "Parrainage",
|
||||||
"amount": "10.00",
|
"amount": "10.00",
|
||||||
"description": "Réduction pour parrainage",
|
"description": "Réduction pour parrainage"
|
||||||
"discount_type": 1,
|
|
||||||
"type": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Réinscription",
|
"name": "Réinscription",
|
||||||
"amount": "100.00",
|
"amount": "100.00",
|
||||||
"description": "Réduction pour Réinscription",
|
"description": "Réduction pour Réinscription"
|
||||||
"discount_type": 1,
|
},
|
||||||
"type": 0
|
{
|
||||||
|
"name": "Famille nombreuse",
|
||||||
|
"amount": "50.00",
|
||||||
|
"description": "Réduction pour les familles nombreuses"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Excellence académique",
|
||||||
|
"amount": "200.00",
|
||||||
|
"description": "Réduction pour les élèves ayant des résultats académiques exceptionnels"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sportif de haut niveau",
|
||||||
|
"amount": "150.00",
|
||||||
|
"description": "Réduction pour les élèves pratiquant un sport de haut niveau"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Artiste talentueux",
|
||||||
|
"amount": "100.00",
|
||||||
|
"description": "Réduction pour les élèves ayant des talents artistiques"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Bourse d'études",
|
||||||
|
"amount": "300.00",
|
||||||
|
"description": "Réduction pour les élèves bénéficiant d'une bourse d'études"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Réduction spéciale",
|
||||||
|
"amount": "75.00",
|
||||||
|
"description": "Réduction spéciale pour des occasions particulières"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -3,35 +3,30 @@
|
|||||||
"name": "Frais d'inscription",
|
"name": "Frais d'inscription",
|
||||||
"base_amount": "150.00",
|
"base_amount": "150.00",
|
||||||
"description": "Montant de base",
|
"description": "Montant de base",
|
||||||
"is_active": true,
|
"is_active": true
|
||||||
"type": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Matériel",
|
"name": "Matériel",
|
||||||
"base_amount": "85.00",
|
"base_amount": "85.00",
|
||||||
"description": "Livres / jouets",
|
"description": "Livres / jouets",
|
||||||
"is_active": true,
|
"is_active": true
|
||||||
"type": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Sorties périscolaires",
|
"name": "Sorties périscolaires",
|
||||||
"base_amount": "120.00",
|
"base_amount": "120.00",
|
||||||
"description": "Sorties",
|
"description": "Sorties",
|
||||||
"is_active": true,
|
"is_active": true
|
||||||
"type": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Les colibris",
|
"name": "Les colibris",
|
||||||
"base_amount": "4500.00",
|
"base_amount": "4500.00",
|
||||||
"description": "TPS / PS / MS / GS",
|
"description": "TPS / PS / MS / GS",
|
||||||
"is_active": true,
|
"is_active": true
|
||||||
"type": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Les butterflies",
|
"name": "Les butterflies",
|
||||||
"base_amount": "5000.00",
|
"base_amount": "5000.00",
|
||||||
"description": "CP / CE1 / CE2 / CM1 / CM2",
|
"description": "CP / CE1 / CE2 / CM1 / CM2",
|
||||||
"is_active": true,
|
"is_active": true
|
||||||
"type": 1
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -1,22 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"name": "Fichiers d'inscription - Classe 1 - Ecole A",
|
|
||||||
"description": "Fichiers d'inscription pour la Classe 1 de l'école Ecole A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Fichiers d'inscription - Classe 2 - Ecole B",
|
|
||||||
"description": "Fichiers d'inscription pour la Classe 2 de l'école Ecole B"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Fichiers d'inscription - Classe 3 - Ecole C",
|
|
||||||
"description": "Fichiers d'inscription pour la Classe 3 de l'école Ecole C"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Fichiers d'inscription - Classe 4 - Ecole A",
|
|
||||||
"description": "Fichiers d'inscription pour la Classe 4 de l'école Ecole A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Fichiers d'inscription - Classe 5 - Ecole B",
|
|
||||||
"description": "Fichiers d'inscription pour la Classe 5 de l'école Ecole B"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"mode": 4,
|
|
||||||
"type": 0,
|
|
||||||
"is_active": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mode": 2,
|
|
||||||
"type": 1,
|
|
||||||
"is_active": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"frequency": 1,
|
|
||||||
"type": 0,
|
|
||||||
"is_active": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"frequency": 3,
|
|
||||||
"type": 1,
|
|
||||||
"is_active": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"frequency": 10,
|
|
||||||
"type": 1,
|
|
||||||
"is_active": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"frequency": 12,
|
|
||||||
"type": 1,
|
|
||||||
"is_active": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
34
Back-End/School/management/mock_datas/profiles.json
Normal file
34
Back-End/School/management/mock_datas/profiles.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"username": "albus.dumbledore",
|
||||||
|
"password": "Provisoire01!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "severus.rogue",
|
||||||
|
"password": "Provisoire01!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "minerva.mcgonagall",
|
||||||
|
"password": "Provisoire01!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "pomona.chourave",
|
||||||
|
"password": "Provisoire01!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "rubeus.hagrid",
|
||||||
|
"password": "Provisoire01!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "filius.flitwick",
|
||||||
|
"password": "Provisoire01!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "pomona.sprout",
|
||||||
|
"password": "Provisoire01!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "aurora.sinistra",
|
||||||
|
"password": "Provisoire01!"
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -7,8 +7,7 @@
|
|||||||
"levels": [2, 3, 4],
|
"levels": [2, 3, 4],
|
||||||
"type": 1,
|
"type": 1,
|
||||||
"time_range": ["08:30", "17:30"],
|
"time_range": ["08:30", "17:30"],
|
||||||
"opening_days": [1, 2, 4, 5],
|
"opening_days": [1, 2, 4, 5]
|
||||||
"teachers": [2]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"age_range": "2-3",
|
"age_range": "2-3",
|
||||||
@ -18,8 +17,7 @@
|
|||||||
"levels": [1],
|
"levels": [1],
|
||||||
"type": 1,
|
"type": 1,
|
||||||
"time_range": ["08:30", "17:30"],
|
"time_range": ["08:30", "17:30"],
|
||||||
"opening_days": [1, 2, 4, 5],
|
"opening_days": [1, 2, 4, 5]
|
||||||
"teachers": [3]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"age_range": "6-12",
|
"age_range": "6-12",
|
||||||
@ -29,8 +27,7 @@
|
|||||||
"levels": [5, 6, 7, 8, 9],
|
"levels": [5, 6, 7, 8, 9],
|
||||||
"type": 1,
|
"type": 1,
|
||||||
"time_range": ["08:30", "17:30"],
|
"time_range": ["08:30", "17:30"],
|
||||||
"opening_days": [1, 2, 4, 5],
|
"opening_days": [1, 2, 4, 5]
|
||||||
"teachers": [4]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"age_range": "4-6",
|
"age_range": "4-6",
|
||||||
@ -40,8 +37,7 @@
|
|||||||
"levels": [4, 5],
|
"levels": [4, 5],
|
||||||
"type": 1,
|
"type": 1,
|
||||||
"time_range": ["08:30", "17:30"],
|
"time_range": ["08:30", "17:30"],
|
||||||
"opening_days": [1, 2, 4, 5],
|
"opening_days": [1, 2, 4, 5]
|
||||||
"teachers": [1]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"age_range": "7-9",
|
"age_range": "7-9",
|
||||||
@ -51,7 +47,6 @@
|
|||||||
"levels": [6, 7],
|
"levels": [6, 7],
|
||||||
"type": 1,
|
"type": 1,
|
||||||
"time_range": ["08:30", "17:30"],
|
"time_range": ["08:30", "17:30"],
|
||||||
"opening_days": [1, 2, 4, 5],
|
"opening_days": [1, 2, 4, 5]
|
||||||
"teachers": [2]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -1,58 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"last_name": "DUMBLEDORE",
|
|
||||||
"first_name": "Albus",
|
|
||||||
"email": "albus.dumbledore@gmail.com",
|
|
||||||
"specialities": ["GROUPE"],
|
|
||||||
"droit": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"last_name": "ROGUE",
|
|
||||||
"first_name": "Severus",
|
|
||||||
"email": "severus.rogue@gmail.com",
|
|
||||||
"specialities": ["ANGLAIS"],
|
|
||||||
"droit": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"last_name": "MC GONAGALL",
|
|
||||||
"first_name": "Minerva",
|
|
||||||
"email": "minerva.mcgonagall@gmail.com",
|
|
||||||
"specialities": ["MATHS", "HISTOIRE"],
|
|
||||||
"droit": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"last_name": "CHOURAVE",
|
|
||||||
"first_name": "Pomona",
|
|
||||||
"email": "pomona.chourave@gmail.com",
|
|
||||||
"specialities": ["MATHS", "FRANCAIS", "SPORT"],
|
|
||||||
"droit": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"last_name": "HAGRID",
|
|
||||||
"first_name": "Rubeus",
|
|
||||||
"email": "rubeus.hagrid@gmail.com",
|
|
||||||
"specialities": ["SCIENCES"],
|
|
||||||
"droit": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"last_name": "FLITWICK",
|
|
||||||
"first_name": "Filius",
|
|
||||||
"email": "filius.flitwick@gmail.com",
|
|
||||||
"specialities": ["MUSIQUE"],
|
|
||||||
"droit": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"last_name": "SPROUT",
|
|
||||||
"first_name": "Pomona",
|
|
||||||
"email": "pomona.sprout@gmail.com",
|
|
||||||
"specialities": ["ART"],
|
|
||||||
"droit": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"last_name": "SINISTRA",
|
|
||||||
"first_name": "Aurora",
|
|
||||||
"email": "aurora.sinistra@gmail.com",
|
|
||||||
"specialities": ["INFORMATIQUE"],
|
|
||||||
"droit": 2
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from Auth.models import Profile
|
from Auth.models import ProfileRole
|
||||||
|
from Establishment.models import Establishment
|
||||||
from django.db.models import JSONField
|
from django.db.models import JSONField
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
@ -18,27 +19,11 @@ LEVEL_CHOICES = [
|
|||||||
(9, 'Cours Moyen 2 (CM2)')
|
(9, 'Cours Moyen 2 (CM2)')
|
||||||
]
|
]
|
||||||
|
|
||||||
class StructureType(models.IntegerChoices):
|
|
||||||
MATERNELLE = 1, _('Maternelle')
|
|
||||||
PRIMAIRE = 2, _('Primaire')
|
|
||||||
SECONDAIRE = 3, _('Secondaire')
|
|
||||||
|
|
||||||
class Establishment(models.Model):
|
|
||||||
name = models.CharField(max_length=255, unique=True)
|
|
||||||
address = models.CharField(max_length=255)
|
|
||||||
total_capacity = models.IntegerField()
|
|
||||||
establishment_type = ArrayField(models.IntegerField(choices=StructureType.choices))
|
|
||||||
licence_code = models.CharField(max_length=100, blank=True)
|
|
||||||
is_active = models.BooleanField(default=True)
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
class Speciality(models.Model):
|
class Speciality(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
updated_date = models.DateTimeField(auto_now=True)
|
updated_date = models.DateTimeField(auto_now=True)
|
||||||
color_code = models.CharField(max_length=7, default='#FFFFFF')
|
color_code = models.CharField(max_length=7, default='#FFFFFF')
|
||||||
|
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='specialities')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -46,9 +31,8 @@ class Speciality(models.Model):
|
|||||||
class Teacher(models.Model):
|
class Teacher(models.Model):
|
||||||
last_name = models.CharField(max_length=100)
|
last_name = models.CharField(max_length=100)
|
||||||
first_name = models.CharField(max_length=100)
|
first_name = models.CharField(max_length=100)
|
||||||
email = models.EmailField(unique=True)
|
|
||||||
specialities = models.ManyToManyField(Speciality, blank=True)
|
specialities = models.ManyToManyField(Speciality, blank=True)
|
||||||
associated_profile = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True, blank=True)
|
profile_role = models.OneToOneField(ProfileRole, on_delete=models.CASCADE, related_name='teacher_profile', null=True, blank=True)
|
||||||
updated_date = models.DateTimeField(auto_now=True)
|
updated_date = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES, Discount, Fee, PaymentPlan, PaymentMode, Establishment
|
from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES, Discount, Fee, PaymentPlan, PaymentMode
|
||||||
from Auth.models import Profile
|
from Auth.models import Profile, ProfileRole
|
||||||
from Subscriptions.models import Student
|
from Subscriptions.models import Student
|
||||||
|
from Establishment.models import Establishment
|
||||||
from N3wtSchool import settings, bdd
|
from N3wtSchool import settings, bdd
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
import pytz
|
import pytz
|
||||||
@ -30,9 +31,10 @@ class TeacherDetailSerializer(serializers.ModelSerializer):
|
|||||||
class TeacherSerializer(serializers.ModelSerializer):
|
class TeacherSerializer(serializers.ModelSerializer):
|
||||||
specialities = serializers.PrimaryKeyRelatedField(queryset=Speciality.objects.all(), many=True, required=False)
|
specialities = serializers.PrimaryKeyRelatedField(queryset=Speciality.objects.all(), many=True, required=False)
|
||||||
specialities_details = serializers.SerializerMethodField()
|
specialities_details = serializers.SerializerMethodField()
|
||||||
associated_profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=True)
|
profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=True)
|
||||||
updated_date_formatted = serializers.SerializerMethodField()
|
updated_date_formatted = serializers.SerializerMethodField()
|
||||||
droit = serializers.SerializerMethodField()
|
role_type = serializers.SerializerMethodField()
|
||||||
|
associated_profile_email = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Teacher
|
model = Teacher
|
||||||
@ -40,12 +42,12 @@ class TeacherSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
specialities_data = validated_data.pop('specialities', None)
|
specialities_data = validated_data.pop('specialities', None)
|
||||||
associated_profile = validated_data.pop('associated_profile', None)
|
profile_role = validated_data.pop('profile_role', None)
|
||||||
teacher = Teacher.objects.create(**validated_data)
|
teacher = Teacher.objects.create(**validated_data)
|
||||||
if specialities_data:
|
if specialities_data:
|
||||||
teacher.specialities.set(specialities_data)
|
teacher.specialities.set(specialities_data)
|
||||||
if associated_profile:
|
if profile_role:
|
||||||
teacher.associated_profile = associated_profile
|
teacher.profile_role = profile_role
|
||||||
teacher.save()
|
teacher.save()
|
||||||
return teacher
|
return teacher
|
||||||
|
|
||||||
@ -54,7 +56,7 @@ class TeacherSerializer(serializers.ModelSerializer):
|
|||||||
instance.last_name = validated_data.get('last_name', instance.last_name)
|
instance.last_name = validated_data.get('last_name', instance.last_name)
|
||||||
instance.first_name = validated_data.get('first_name', instance.first_name)
|
instance.first_name = validated_data.get('first_name', instance.first_name)
|
||||||
instance.email = validated_data.get('email', instance.email)
|
instance.email = validated_data.get('email', instance.email)
|
||||||
instance.associated_profile = validated_data.get('associated_profile', instance.associated_profile)
|
instance.profile_role = validated_data.get('profile_role', instance.profile_role)
|
||||||
instance.save()
|
instance.save()
|
||||||
if specialities_data:
|
if specialities_data:
|
||||||
instance.specialities.set(specialities_data)
|
instance.specialities.set(specialities_data)
|
||||||
@ -64,17 +66,18 @@ class TeacherSerializer(serializers.ModelSerializer):
|
|||||||
utc_time = timezone.localtime(obj.updated_date) # Convert to local time
|
utc_time = timezone.localtime(obj.updated_date) # Convert to local time
|
||||||
local_tz = pytz.timezone(settings.TZ_APPLI)
|
local_tz = pytz.timezone(settings.TZ_APPLI)
|
||||||
local_time = utc_time.astimezone(local_tz)
|
local_time = utc_time.astimezone(local_tz)
|
||||||
|
|
||||||
return local_time.strftime("%d-%m-%Y %H:%M")
|
return local_time.strftime("%d-%m-%Y %H:%M")
|
||||||
|
|
||||||
def get_droit(self, obj):
|
def get_role_type(self, obj):
|
||||||
if obj.associated_profile:
|
profile_role = obj.profile_role
|
||||||
return obj.associated_profile.droit
|
return {'role_type': profile_role.role_type, 'establishment': profile_role.establishment.name}
|
||||||
return None
|
|
||||||
|
|
||||||
def get_specialities_details(self, obj):
|
def get_specialities_details(self, obj):
|
||||||
return [{'id': speciality.id, 'name': speciality.name, 'color_code': speciality.color_code} for speciality in obj.specialities.all()]
|
return [{'id': speciality.id, 'name': speciality.name, 'color_code': speciality.color_code} for speciality in obj.specialities.all()]
|
||||||
|
|
||||||
|
def get_associated_profile_email(self, obj):
|
||||||
|
return obj.profile_role.profile.email
|
||||||
|
|
||||||
class PlanningSerializer(serializers.ModelSerializer):
|
class PlanningSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Planning
|
model = Planning
|
||||||
@ -208,8 +211,3 @@ class PaymentModeSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = PaymentMode
|
model = PaymentMode
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
class EstablishmentSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Establishment
|
|
||||||
fields = '__all__'
|
|
||||||
@ -16,9 +16,7 @@ from .views import (
|
|||||||
PaymentPlanListCreateView,
|
PaymentPlanListCreateView,
|
||||||
PaymentPlanDetailView,
|
PaymentPlanDetailView,
|
||||||
PaymentModeListCreateView,
|
PaymentModeListCreateView,
|
||||||
PaymentModeDetailView,
|
PaymentModeDetailView
|
||||||
EstablishmentListCreateView,
|
|
||||||
EstablishmentDetailView
|
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -44,8 +42,5 @@ urlpatterns = [
|
|||||||
re_path(r'^paymentPlans/(?P<id>[0-9]+)$', PaymentPlanDetailView.as_view(), name="payment_plan_detail"),
|
re_path(r'^paymentPlans/(?P<id>[0-9]+)$', PaymentPlanDetailView.as_view(), name="payment_plan_detail"),
|
||||||
|
|
||||||
re_path(r'^paymentModes$', PaymentModeListCreateView.as_view(), name="payment_mode_list_create"),
|
re_path(r'^paymentModes$', PaymentModeListCreateView.as_view(), name="payment_mode_list_create"),
|
||||||
re_path(r'^paymentModes/(?P<id>[0-9]+)$', PaymentModeDetailView.as_view(), name="payment_mode_detail"),
|
re_path(r'^paymentModes/(?P<id>[0-9]+)$', PaymentModeDetailView.as_view(), name="payment_mode_detail")
|
||||||
|
|
||||||
re_path(r'^establishments$', EstablishmentListCreateView.as_view(), name='establishment_list_create'),
|
|
||||||
re_path(r'^establishments/(?P<id>[0-9]+)$', EstablishmentDetailView.as_view(), name="establishment_detail"),
|
|
||||||
]
|
]
|
||||||
@ -12,8 +12,7 @@ from .models import (
|
|||||||
Discount,
|
Discount,
|
||||||
Fee,
|
Fee,
|
||||||
PaymentPlan,
|
PaymentPlan,
|
||||||
PaymentMode,
|
PaymentMode
|
||||||
Establishment
|
|
||||||
)
|
)
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
TeacherSerializer,
|
TeacherSerializer,
|
||||||
@ -23,8 +22,7 @@ from .serializers import (
|
|||||||
DiscountSerializer,
|
DiscountSerializer,
|
||||||
FeeSerializer,
|
FeeSerializer,
|
||||||
PaymentPlanSerializer,
|
PaymentPlanSerializer,
|
||||||
PaymentModeSerializer,
|
PaymentModeSerializer
|
||||||
EstablishmentSerializer
|
|
||||||
)
|
)
|
||||||
from N3wtSchool.bdd import delete_object, getAllObjects, getObject
|
from N3wtSchool.bdd import delete_object, getAllObjects, getObject
|
||||||
|
|
||||||
@ -32,8 +30,14 @@ from N3wtSchool.bdd import delete_object, getAllObjects, getObject
|
|||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
class SpecialityListCreateView(APIView):
|
class SpecialityListCreateView(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
specialitiesList = getAllObjects(Speciality)
|
establishment_id = request.GET.get('establishment_id', None)
|
||||||
specialities_serializer = SpecialitySerializer(specialitiesList, many=True)
|
if establishment_id is None:
|
||||||
|
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
specialities_list = getAllObjects(Speciality)
|
||||||
|
if establishment_id:
|
||||||
|
specialities_list = specialities_list.filter(establishment__id=establishment_id).distinct()
|
||||||
|
specialities_serializer = SpecialitySerializer(specialities_list, many=True)
|
||||||
return JsonResponse(specialities_serializer.data, safe=False)
|
return JsonResponse(specialities_serializer.data, safe=False)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
@ -51,7 +55,7 @@ class SpecialityListCreateView(APIView):
|
|||||||
class SpecialityDetailView(APIView):
|
class SpecialityDetailView(APIView):
|
||||||
def get(self, request, id):
|
def get(self, request, id):
|
||||||
speciality = getObject(_objectName=Speciality, _columnName='id', _value=id)
|
speciality = getObject(_objectName=Speciality, _columnName='id', _value=id)
|
||||||
speciality_serializer = SpecialitySerializer(speciality)
|
speciality_serializer=SpecialitySerializer(speciality)
|
||||||
return JsonResponse(speciality_serializer.data, safe=False)
|
return JsonResponse(speciality_serializer.data, safe=False)
|
||||||
|
|
||||||
def put(self, request, id):
|
def put(self, request, id):
|
||||||
@ -71,9 +75,14 @@ class SpecialityDetailView(APIView):
|
|||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
class TeacherListCreateView(APIView):
|
class TeacherListCreateView(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
teachersList=getAllObjects(Teacher)
|
establishment_id = request.GET.get('establishment_id', None)
|
||||||
teachers_serializer=TeacherSerializer(teachersList, many=True)
|
if establishment_id is None:
|
||||||
|
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
teachers_list = getAllObjects(Teacher)
|
||||||
|
if teachers_list:
|
||||||
|
teachers_list = teachers_list.filter(profile_role__establishment_id=establishment_id)
|
||||||
|
teachers_serializer = TeacherSerializer(teachers_list, many=True)
|
||||||
return JsonResponse(teachers_serializer.data, safe=False)
|
return JsonResponse(teachers_serializer.data, safe=False)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
@ -107,14 +116,20 @@ class TeacherDetailView(APIView):
|
|||||||
return JsonResponse(teacher_serializer.errors, safe=False)
|
return JsonResponse(teacher_serializer.errors, safe=False)
|
||||||
|
|
||||||
def delete(self, request, id):
|
def delete(self, request, id):
|
||||||
return delete_object(Teacher, id, related_field='associated_profile')
|
return delete_object(Teacher, id, related_field='profile')
|
||||||
|
|
||||||
@method_decorator(csrf_protect, name='dispatch')
|
@method_decorator(csrf_protect, name='dispatch')
|
||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
class SchoolClassListCreateView(APIView):
|
class SchoolClassListCreateView(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
classesList=getAllObjects(SchoolClass)
|
establishment_id = request.GET.get('establishment_id', None)
|
||||||
classes_serializer=SchoolClassSerializer(classesList, many=True)
|
if establishment_id is None:
|
||||||
|
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
school_classes_list = getAllObjects(SchoolClass)
|
||||||
|
if school_classes_list:
|
||||||
|
school_classes_list = school_classes_list.filter(establishment=establishment_id).distinct()
|
||||||
|
classes_serializer = SchoolClassSerializer(school_classes_list, many=True)
|
||||||
return JsonResponse(classes_serializer.data, safe=False)
|
return JsonResponse(classes_serializer.data, safe=False)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
@ -201,9 +216,14 @@ class PlanningDetailView(APIView):
|
|||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
class FeeListCreateView(APIView):
|
class FeeListCreateView(APIView):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
establishment_id = request.GET.get('establishment_id', None)
|
||||||
|
if establishment_id is None:
|
||||||
|
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
filter = request.GET.get('filter', '').strip()
|
filter = request.GET.get('filter', '').strip()
|
||||||
fee_type_value = 0 if filter == 'registration' else 1
|
fee_type_value = 0 if filter == 'registration' else 1
|
||||||
fees = Fee.objects.filter(type=fee_type_value)
|
|
||||||
|
fees = Fee.objects.filter(type=fee_type_value, establishment_id=establishment_id).distinct()
|
||||||
fee_serializer = FeeSerializer(fees, many=True)
|
fee_serializer = FeeSerializer(fees, many=True)
|
||||||
|
|
||||||
return JsonResponse(fee_serializer.data, safe=False, status=status.HTTP_200_OK)
|
return JsonResponse(fee_serializer.data, safe=False, status=status.HTTP_200_OK)
|
||||||
@ -246,9 +266,14 @@ class FeeDetailView(APIView):
|
|||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
class DiscountListCreateView(APIView):
|
class DiscountListCreateView(APIView):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
establishment_id = request.GET.get('establishment_id', None)
|
||||||
|
if establishment_id is None:
|
||||||
|
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
filter = request.GET.get('filter', '').strip()
|
filter = request.GET.get('filter', '').strip()
|
||||||
discount_type_value = 0 if filter == 'registration' else 1
|
discount_type_value = 0 if filter == 'registration' else 1
|
||||||
discounts = Discount.objects.filter(type=discount_type_value)
|
|
||||||
|
discounts = Discount.objects.filter(type=discount_type_value, establishment_id=establishment_id).distinct()
|
||||||
discounts_serializer = DiscountSerializer(discounts, many=True)
|
discounts_serializer = DiscountSerializer(discounts, many=True)
|
||||||
|
|
||||||
return JsonResponse(discounts_serializer.data, safe=False, status=status.HTTP_200_OK)
|
return JsonResponse(discounts_serializer.data, safe=False, status=status.HTTP_200_OK)
|
||||||
@ -291,10 +316,15 @@ class DiscountDetailView(APIView):
|
|||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
class PaymentPlanListCreateView(APIView):
|
class PaymentPlanListCreateView(APIView):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
establishment_id = request.GET.get('establishment_id', None)
|
||||||
|
if establishment_id is None:
|
||||||
|
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
filter = request.GET.get('filter', '').strip()
|
filter = request.GET.get('filter', '').strip()
|
||||||
type_value = 0 if filter == 'registration' else 1
|
type_value = 0 if filter == 'registration' else 1
|
||||||
paymentPlans = PaymentPlan.objects.filter(type=type_value)
|
|
||||||
payment_plans_serializer = PaymentPlanSerializer(paymentPlans, many=True)
|
payment_plans = PaymentPlan.objects.filter(type=type_value, establishment_id=establishment_id).distinct()
|
||||||
|
payment_plans_serializer = PaymentPlanSerializer(payment_plans, many=True)
|
||||||
|
|
||||||
return JsonResponse(payment_plans_serializer.data, safe=False, status=status.HTTP_200_OK)
|
return JsonResponse(payment_plans_serializer.data, safe=False, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@ -336,10 +366,15 @@ class PaymentPlanDetailView(APIView):
|
|||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
class PaymentModeListCreateView(APIView):
|
class PaymentModeListCreateView(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
establishment_id = request.GET.get('establishment_id', None)
|
||||||
|
if establishment_id is None:
|
||||||
|
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
filter = request.GET.get('filter', '').strip()
|
filter = request.GET.get('filter', '').strip()
|
||||||
type_value = 0 if filter == 'registration' else 1
|
type_value = 0 if filter == 'registration' else 1
|
||||||
paymentModes = PaymentMode.objects.filter(type=type_value)
|
|
||||||
payment_modes_serializer = PaymentModeSerializer(paymentModes, many=True)
|
payment_modes = PaymentMode.objects.filter(type=type_value, establishment_id=establishment_id).distinct()
|
||||||
|
payment_modes_serializer = PaymentModeSerializer(payment_modes, many=True)
|
||||||
|
|
||||||
return JsonResponse(payment_modes_serializer.data, safe=False, status=status.HTTP_200_OK)
|
return JsonResponse(payment_modes_serializer.data, safe=False, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@ -376,45 +411,3 @@ class PaymentModeDetailView(APIView):
|
|||||||
|
|
||||||
def delete(self, request, id):
|
def delete(self, request, id):
|
||||||
return delete_object(PaymentMode, id)
|
return delete_object(PaymentMode, id)
|
||||||
|
|
||||||
@method_decorator(csrf_protect, name='dispatch')
|
|
||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
|
||||||
class EstablishmentListCreateView(APIView):
|
|
||||||
def get(self, request):
|
|
||||||
establishments=getAllObjects(Establishment)
|
|
||||||
establishments_serializer=EstablishmentSerializer(establishments, many=True)
|
|
||||||
return JsonResponse(establishments_serializer.data, safe=False, status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
establishment_data = JSONParser().parse(request)
|
|
||||||
establishment_serializer = EstablishmentSerializer(data=establishment_data)
|
|
||||||
if establishment_serializer.is_valid():
|
|
||||||
establishment_serializer.save()
|
|
||||||
return JsonResponse(establishment_serializer.data, safe=False, status=status.HTTP_201_CREATED)
|
|
||||||
return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
@method_decorator(csrf_protect, name='dispatch')
|
|
||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
|
||||||
class EstablishmentDetailView(APIView):
|
|
||||||
def get(self, request, id=None):
|
|
||||||
try:
|
|
||||||
establishment = Establishment.objects.get(id=id)
|
|
||||||
establishment_serializer = EstablishmentSerializer(establishment)
|
|
||||||
return JsonResponse(establishment_serializer.data, safe=False)
|
|
||||||
except Establishment.DoesNotExist:
|
|
||||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
|
|
||||||
def put(self, request, id):
|
|
||||||
establishment_data = JSONParser().parse(request)
|
|
||||||
try:
|
|
||||||
establishment = Establishment.objects.get(id=id)
|
|
||||||
except Establishment.DoesNotExist:
|
|
||||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
|
||||||
establishment_serializer = EstablishmentSerializer(establishment, data=establishment_data, partial=True)
|
|
||||||
if establishment_serializer.is_valid():
|
|
||||||
establishment_serializer.save()
|
|
||||||
return JsonResponse(establishment_serializer.data, safe=False)
|
|
||||||
return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
def delete(self, request, id):
|
|
||||||
return delete_object(Establishment, id)
|
|
||||||
@ -4,6 +4,8 @@ from django.conf import settings
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from School.models import SchoolClass, Fee, Discount
|
from School.models import SchoolClass, Fee, Discount
|
||||||
|
from Auth.models import ProfileRole
|
||||||
|
from Establishment.models import Establishment
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@ -25,10 +27,9 @@ class Guardian(models.Model):
|
|||||||
first_name = models.CharField(max_length=200, default="")
|
first_name = models.CharField(max_length=200, default="")
|
||||||
birth_date = models.CharField(max_length=200, default="", blank=True)
|
birth_date = models.CharField(max_length=200, default="", blank=True)
|
||||||
address = models.CharField(max_length=200, default="", blank=True)
|
address = models.CharField(max_length=200, default="", blank=True)
|
||||||
email = models.CharField(max_length=200, default="", blank=True)
|
|
||||||
phone = models.CharField(max_length=200, default="", blank=True)
|
phone = models.CharField(max_length=200, default="", blank=True)
|
||||||
profession = models.CharField(max_length=200, default="", blank=True)
|
profession = models.CharField(max_length=200, default="", blank=True)
|
||||||
associated_profile = models.ForeignKey('Auth.Profile', on_delete=models.CASCADE)
|
profile_role = models.OneToOneField(ProfileRole, on_delete=models.CASCADE, related_name='guardian_profile', null=True, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.last_name + "_" + self.first_name
|
return self.last_name + "_" + self.first_name
|
||||||
@ -163,6 +164,7 @@ class Student(models.Model):
|
|||||||
class RegistrationFileGroup(models.Model):
|
class RegistrationFileGroup(models.Model):
|
||||||
name = models.CharField(max_length=255, default="")
|
name = models.CharField(max_length=255, default="")
|
||||||
description = models.TextField(blank=True, null=True)
|
description = models.TextField(blank=True, null=True)
|
||||||
|
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='file_group', null=True, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -214,7 +216,7 @@ class RegistrationForm(models.Model):
|
|||||||
null=True,
|
null=True,
|
||||||
blank=True)
|
blank=True)
|
||||||
|
|
||||||
establishment = models.ForeignKey('School.Establishment', on_delete=models.CASCADE, related_name='register_forms')
|
establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='register_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
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from rest_framework import serializers
|
|||||||
from .models import RegistrationFileGroup, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationTemplateMaster, RegistrationTemplate
|
from .models import RegistrationFileGroup, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationTemplateMaster, RegistrationTemplate
|
||||||
from School.models import SchoolClass, Fee, Discount, FeeType
|
from School.models import SchoolClass, Fee, Discount, FeeType
|
||||||
from School.serializers import FeeSerializer, DiscountSerializer
|
from School.serializers import FeeSerializer, DiscountSerializer
|
||||||
from Auth.models import Profile
|
from Auth.models import ProfileRole
|
||||||
from Auth.serializers import ProfileSerializer
|
from Auth.serializers import ProfileSerializer
|
||||||
from GestionMessagerie.models import Messagerie
|
from GestionMessagerie.models import Messagerie
|
||||||
from GestionNotification.models import Notification
|
from GestionNotification.models import Notification
|
||||||
@ -68,7 +68,7 @@ class SiblingSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class GuardianSerializer(serializers.ModelSerializer):
|
class GuardianSerializer(serializers.ModelSerializer):
|
||||||
id = serializers.IntegerField(required=False)
|
id = serializers.IntegerField(required=False)
|
||||||
associated_profile = serializers.PrimaryKeyRelatedField(queryset=Profile.objects.all(), required=True)
|
profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=True)
|
||||||
associated_profile_email = serializers.SerializerMethodField()
|
associated_profile_email = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -76,7 +76,7 @@ class GuardianSerializer(serializers.ModelSerializer):
|
|||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
def get_associated_profile_email(self, obj):
|
def get_associated_profile_email(self, obj):
|
||||||
return obj.associated_profile.email
|
return obj.profile_role.profile.email
|
||||||
|
|
||||||
|
|
||||||
class StudentSerializer(serializers.ModelSerializer):
|
class StudentSerializer(serializers.ModelSerializer):
|
||||||
@ -248,7 +248,7 @@ class GuardianByDICreationSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Guardian
|
model = Guardian
|
||||||
fields = ['id', 'last_name', 'first_name', 'email', 'associated_profile']
|
fields = ['id', 'last_name', 'first_name', 'email', 'profile']
|
||||||
|
|
||||||
class StudentByRFCreationSerializer(serializers.ModelSerializer):
|
class StudentByRFCreationSerializer(serializers.ModelSerializer):
|
||||||
id = serializers.IntegerField(required=False)
|
id = serializers.IntegerField(required=False)
|
||||||
|
|||||||
@ -90,6 +90,12 @@ class ChildrenListView(APIView):
|
|||||||
# Récupération des élèves d'un parent
|
# Récupération des élèves d'un parent
|
||||||
# idProfile : identifiant du profil connecté rattaché aux fiches d'élèves
|
# idProfile : identifiant du profil connecté rattaché aux fiches d'élèves
|
||||||
def get(self, request, id):
|
def get(self, request, id):
|
||||||
students = bdd.getObjects(_objectName=RegistrationForm, _columnName='student__guardians__associated_profile__id', _value=id)
|
establishment_id = request.GET.get('establishment_id', None)
|
||||||
|
if establishment_id is None:
|
||||||
|
return JsonResponse({'error': 'establishment_id est requis'}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
students = bdd.getObjects(_objectName=RegistrationForm, _columnName='student__guardians__profile_role__profile__id', _value=id)
|
||||||
|
if students:
|
||||||
|
students = students.filter(establishment=establishment_id).distinct()
|
||||||
students_serializer = RegistrationFormByParentSerializer(students, many=True)
|
students_serializer = RegistrationFormByParentSerializer(students, many=True)
|
||||||
return JsonResponse(students_serializer.data, safe=False)
|
return JsonResponse(students_serializer.data, safe=False)
|
||||||
|
|||||||
@ -15,6 +15,7 @@ test_mode = os.getenv('TEST_MODE', 'False') == 'True'
|
|||||||
commands = [
|
commands = [
|
||||||
["python", "manage.py", "collectstatic", "--noinput"],
|
["python", "manage.py", "collectstatic", "--noinput"],
|
||||||
["python", "manage.py", "flush", "--noinput"],
|
["python", "manage.py", "flush", "--noinput"],
|
||||||
|
["python", "manage.py", "makemigrations", "Establishment", "--noinput"],
|
||||||
["python", "manage.py", "makemigrations", "Subscriptions", "--noinput"],
|
["python", "manage.py", "makemigrations", "Subscriptions", "--noinput"],
|
||||||
["python", "manage.py", "makemigrations", "Planning", "--noinput"],
|
["python", "manage.py", "makemigrations", "Planning", "--noinput"],
|
||||||
["python", "manage.py", "makemigrations", "GestionNotification", "--noinput"],
|
["python", "manage.py", "makemigrations", "GestionNotification", "--noinput"],
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import Sidebar from '@/components/Sidebar';
|
import Sidebar from '@/components/Sidebar';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import {useTranslations} from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import {
|
import {
|
||||||
Users,
|
Users,
|
||||||
@ -35,6 +35,9 @@ import ProtectedRoute from '@/components/ProtectedRoute';
|
|||||||
import { getGravatarUrl } from '@/utils/gravatar';
|
import { getGravatarUrl } from '@/utils/gravatar';
|
||||||
import Footer from '@/components/Footer';
|
import Footer from '@/components/Footer';
|
||||||
import { getRightStr, RIGHTS } from '@/utils/rights';
|
import { getRightStr, RIGHTS } from '@/utils/rights';
|
||||||
|
import { getSession } from 'next-auth/react';
|
||||||
|
import logger from '@/utils/logger';
|
||||||
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||||
|
|
||||||
export default function Layout({
|
export default function Layout({
|
||||||
children,
|
children,
|
||||||
@ -51,11 +54,12 @@ export default function Layout({
|
|||||||
"settings": { "id": "settings", "name": t('settings'), "url": FE_ADMIN_SETTINGS_URL, "icon": Settings }
|
"settings": { "id": "settings", "name": t('settings'), "url": FE_ADMIN_SETTINGS_URL, "icon": Settings }
|
||||||
};
|
};
|
||||||
|
|
||||||
const [establishment, setEstablishment] = useState(null);
|
const [establishments, setEstablishments] = useState([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isPopupVisible, setIsPopupVisible] = useState(false);
|
const [isPopupVisible, setIsPopupVisible] = useState(false);
|
||||||
const [user, setUser] = useState(null);
|
const [user, setUser] = useState(null);
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
|
const { selectedEstablishmentId, setSelectedEstablishmentId, profileRole, setProfileRole } = useEstablishment();
|
||||||
|
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const currentPage = pathname.split('/').pop();
|
const currentPage = pathname.split('/').pop();
|
||||||
@ -80,7 +84,7 @@ export default function Layout({
|
|||||||
content: (
|
content: (
|
||||||
<div className="px-4 py-2">
|
<div className="px-4 py-2">
|
||||||
<div className="font-medium">{user?.email || 'Utilisateur'}</div>
|
<div className="font-medium">{user?.email || 'Utilisateur'}</div>
|
||||||
<div className="text-xs text-gray-400">{getRightStr(user?.role) || ''}</div>
|
<div className="text-xs text-gray-400">{getRightStr(profileRole) || ''}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -106,17 +110,34 @@ export default function Layout({
|
|||||||
}, [pathname]);
|
}, [pathname]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoading(true);
|
getSession()
|
||||||
fetchEstablishment()
|
.then(session => {
|
||||||
.then(data => {
|
if (session && session.user) {
|
||||||
setEstablishment(data);
|
setUser(session.user);
|
||||||
|
setEstablishments(session.user.roles.map(role => ({
|
||||||
|
id: role.establishment__id,
|
||||||
|
name: role.establishment__name,
|
||||||
|
role_type: role.role_type
|
||||||
|
})));
|
||||||
|
// Sélectionner l'établissement depuis le localStorage ou le premier établissement par défaut
|
||||||
|
const storedEstablishmentId = localStorage.getItem('selectedEstablishmentId');
|
||||||
|
if (storedEstablishmentId) {
|
||||||
|
setSelectedEstablishmentId(storedEstablishmentId);
|
||||||
|
const storedProfileRole = session.user.roles.find(role => role.establishment__id === parseInt(storedEstablishmentId))?.role_type;
|
||||||
|
setProfileRole(storedProfileRole);
|
||||||
|
} else if (session.user.roles.length > 0) {
|
||||||
|
setSelectedEstablishmentId(session.user.roles[0].establishment__id);
|
||||||
|
setProfileRole(session.user.roles[0].role_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error fetching establishment : ', error))
|
.catch(err => {
|
||||||
.finally(() => setIsLoading(false));
|
logger.error('Error fetching session:', err);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
if (session) { // Vérifier que la session existe
|
if (session) { // Vérifier que la session existe
|
||||||
const userData = await getUser();
|
const userData = await getUser();
|
||||||
setUser(userData);
|
setUser(userData);
|
||||||
@ -126,8 +147,6 @@ export default function Layout({
|
|||||||
fetchUser();
|
fetchUser();
|
||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProtectedRoute requiredRight={RIGHTS.ADMIN}>
|
<ProtectedRoute requiredRight={RIGHTS.ADMIN}>
|
||||||
{!isLoading && (
|
{!isLoading && (
|
||||||
@ -138,12 +157,16 @@ export default function Layout({
|
|||||||
style={{ height: '100vh' }} // Force la hauteur à 100% de la hauteur de la vue
|
style={{ height: '100vh' }} // Force la hauteur à 100% de la hauteur de la vue
|
||||||
>
|
>
|
||||||
<Sidebar
|
<Sidebar
|
||||||
establishment={establishment}
|
establishments={establishments}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
items={Object.values(sidebarItems)}
|
items={Object.values(sidebarItems)}
|
||||||
|
|
||||||
onCloseMobile={toggleSidebar}
|
onCloseMobile={toggleSidebar}
|
||||||
|
onEstablishmentChange={(establishmentId) => {
|
||||||
|
const parsedEstablishmentId = parseInt(establishmentId, 10);
|
||||||
|
setSelectedEstablishmentId(parsedEstablishmentId);
|
||||||
|
const role = session.user.roles.find(role => role.establishment__id === parsedEstablishmentId)?.role_type;
|
||||||
|
setProfileRole(role);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -39,47 +39,61 @@ export default function DashboardPage() {
|
|||||||
|
|
||||||
|
|
||||||
const [classes, setClasses] = useState([]);
|
const [classes, setClasses] = useState([]);
|
||||||
|
const [establishmentId, setEstablishmentId] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Fetch data for classes
|
getSession()
|
||||||
fetchClasses().then(data => {
|
.then(session => {
|
||||||
setClasses(data);
|
if (session && session.user) {
|
||||||
logger.info('Classes fetched:', data);
|
setEstablishmentId(session.user.establishment);
|
||||||
const nbMaxStudents = data.reduce((acc, classe) => acc + classe.number_of_students, 0);
|
}
|
||||||
const nbStudents = data.reduce((acc, classe) => acc + classe.students.length, 0);
|
|
||||||
setStructureCapacity(nbMaxStudents);
|
|
||||||
setTotalStudents(nbStudents);
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(err => {
|
||||||
logger.error('Error fetching classes:', error);
|
logger.error('Error fetching session:', err);
|
||||||
});
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
fetchRegisterForms().then(data => {
|
useEffect(() => {
|
||||||
logger.info('Pending registrations fetched:', data);
|
if (establishmentId) {
|
||||||
setPendingRegistration(data.count);
|
// Fetch data for classes
|
||||||
})
|
fetchClasses(establishmentId).then(data => {
|
||||||
.catch(error => {
|
setClasses(data);
|
||||||
logger.error('Error fetching pending registrations:', error);
|
logger.info('Classes fetched:', data);
|
||||||
});
|
const nbMaxStudents = data.reduce((acc, classe) => acc + classe.number_of_students, 0);
|
||||||
|
const nbStudents = data.reduce((acc, classe) => acc + classe.students.length, 0);
|
||||||
|
setStructureCapacity(nbMaxStudents);
|
||||||
|
setTotalStudents(nbStudents);
|
||||||
|
|
||||||
fetchUpcomingEvents().then(data => {
|
})
|
||||||
setUpcomingEvents(data);
|
.catch(error => {
|
||||||
}).catch(error => {
|
logger.error('Error fetching classes:', error);
|
||||||
logger.error('Error fetching upcoming events:', error);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// Simulation de chargement des données
|
fetchRegisterForms().then(data => {
|
||||||
setTimeout(() => {
|
logger.info('Pending registrations fetched:', data);
|
||||||
setMonthlyStats({
|
setPendingRegistration(data.count);
|
||||||
inscriptions: [150, 180, 210, 245],
|
})
|
||||||
completionRate: 78
|
.catch(error => {
|
||||||
});
|
logger.error('Error fetching pending registrations:', error);
|
||||||
setIsLoading(false);
|
});
|
||||||
}, 1000);
|
|
||||||
}
|
fetchUpcomingEvents().then(data => {
|
||||||
, []);
|
setUpcomingEvents(data);
|
||||||
|
}).catch(error => {
|
||||||
|
logger.error('Error fetching upcoming events:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Simulation de chargement des données
|
||||||
|
setTimeout(() => {
|
||||||
|
setMonthlyStats({
|
||||||
|
inscriptions: [150, 180, 210, 245],
|
||||||
|
completionRate: 78
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, [establishmentId]);
|
||||||
|
|
||||||
|
|
||||||
if (isLoading) return <Loader />;
|
if (isLoading) return <Loader />;
|
||||||
|
|||||||
@ -27,13 +27,13 @@ import {
|
|||||||
fetchRegistrationTemplateMaster
|
fetchRegistrationTemplateMaster
|
||||||
} from "@/app/actions/registerFileGroupAction";
|
} from "@/app/actions/registerFileGroupAction";
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const [specialities, setSpecialities] = useState([]);
|
const [specialities, setSpecialities] = useState([]);
|
||||||
const [classes, setClasses] = useState([]);
|
const [classes, setClasses] = useState([]);
|
||||||
const [teachers, setTeachers] = useState([]);
|
const [teachers, setTeachers] = useState([]);
|
||||||
const [schedules, setSchedules] = useState([]); // Add this line
|
const [schedules, setSchedules] = useState([]);
|
||||||
const [registrationDiscounts, setRegistrationDiscounts] = useState([]);
|
const [registrationDiscounts, setRegistrationDiscounts] = useState([]);
|
||||||
const [tuitionDiscounts, setTuitionDiscounts] = useState([]);
|
const [tuitionDiscounts, setTuitionDiscounts] = useState([]);
|
||||||
const [registrationFees, setRegistrationFees] = useState([]);
|
const [registrationFees, setRegistrationFees] = useState([]);
|
||||||
@ -45,54 +45,57 @@ export default function Page() {
|
|||||||
const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]);
|
const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]);
|
||||||
|
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
|
const { selectedEstablishmentId } = useEstablishment();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Fetch data for specialities
|
if (selectedEstablishmentId) {
|
||||||
handleSpecialities();
|
// Fetch data for specialities
|
||||||
|
handleSpecialities();
|
||||||
|
|
||||||
// Fetch data for teachers
|
// Fetch data for teachers
|
||||||
handleTeachers();
|
handleTeachers();
|
||||||
|
|
||||||
// Fetch data for classes
|
// Fetch data for classes
|
||||||
handleClasses();
|
handleClasses();
|
||||||
|
|
||||||
// Fetch data for schedules
|
// Fetch data for schedules
|
||||||
handleSchedules();
|
handleSchedules();
|
||||||
|
|
||||||
// Fetch data for registration discounts
|
// Fetch data for registration discounts
|
||||||
handleRegistrationDiscounts();
|
handleRegistrationDiscounts();
|
||||||
|
|
||||||
// Fetch data for tuition discounts
|
// Fetch data for tuition discounts
|
||||||
handleTuitionDiscounts();
|
handleTuitionDiscounts();
|
||||||
|
|
||||||
// Fetch data for registration fees
|
// Fetch data for registration fees
|
||||||
handleRegistrationFees();
|
handleRegistrationFees();
|
||||||
|
|
||||||
// Fetch data for tuition fees
|
// Fetch data for tuition fees
|
||||||
handleTuitionFees();
|
handleTuitionFees();
|
||||||
|
|
||||||
// Fetch data for registration file templates
|
// Fetch data for registration file templates
|
||||||
fetchRegistrationTemplateMaster()
|
fetchRegistrationTemplateMaster()
|
||||||
.then((data)=> {
|
.then((data)=> {
|
||||||
setFichiers(data)
|
setFichiers(data)
|
||||||
})
|
})
|
||||||
.catch(error => logger.error('Error fetching files:', error));
|
.catch(error => logger.error('Error fetching files:', error));
|
||||||
|
|
||||||
// Fetch data for registration payment plans
|
// Fetch data for registration payment plans
|
||||||
handleRegistrationPaymentPlans();
|
handleRegistrationPaymentPlans();
|
||||||
|
|
||||||
// Fetch data for tuition payment plans
|
// Fetch data for tuition payment plans
|
||||||
handleTuitionPaymentPlans();
|
handleTuitionPaymentPlans();
|
||||||
|
|
||||||
// Fetch data for registration payment modes
|
// Fetch data for registration payment modes
|
||||||
handleRegistrationPaymentModes();
|
handleRegistrationPaymentModes();
|
||||||
|
|
||||||
// Fetch data for tuition payment modes
|
// Fetch data for tuition payment modes
|
||||||
handleTuitionPaymentModes();
|
handleTuitionPaymentModes();
|
||||||
}, []);
|
}
|
||||||
|
}, [selectedEstablishmentId]);
|
||||||
|
|
||||||
const handleSpecialities = () => {
|
const handleSpecialities = () => {
|
||||||
fetchSpecialities()
|
fetchSpecialities(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setSpecialities(data);
|
setSpecialities(data);
|
||||||
})
|
})
|
||||||
@ -100,7 +103,7 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleTeachers = () => {
|
const handleTeachers = () => {
|
||||||
fetchTeachers()
|
fetchTeachers(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setTeachers(data);
|
setTeachers(data);
|
||||||
})
|
})
|
||||||
@ -108,7 +111,7 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleClasses = () => {
|
const handleClasses = () => {
|
||||||
fetchClasses()
|
fetchClasses(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setClasses(data);
|
setClasses(data);
|
||||||
})
|
})
|
||||||
@ -124,7 +127,7 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleRegistrationDiscounts = () => {
|
const handleRegistrationDiscounts = () => {
|
||||||
fetchRegistrationDiscounts()
|
fetchRegistrationDiscounts(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setRegistrationDiscounts(data);
|
setRegistrationDiscounts(data);
|
||||||
})
|
})
|
||||||
@ -132,7 +135,7 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleTuitionDiscounts = () => {
|
const handleTuitionDiscounts = () => {
|
||||||
fetchTuitionDiscounts()
|
fetchTuitionDiscounts(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setTuitionDiscounts(data);
|
setTuitionDiscounts(data);
|
||||||
})
|
})
|
||||||
@ -140,7 +143,7 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleRegistrationFees = () => {
|
const handleRegistrationFees = () => {
|
||||||
fetchRegistrationFees()
|
fetchRegistrationFees(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setRegistrationFees(data);
|
setRegistrationFees(data);
|
||||||
})
|
})
|
||||||
@ -148,7 +151,7 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleTuitionFees = () => {
|
const handleTuitionFees = () => {
|
||||||
fetchTuitionFees()
|
fetchTuitionFees(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setTuitionFees(data);
|
setTuitionFees(data);
|
||||||
})
|
})
|
||||||
@ -156,7 +159,7 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleRegistrationPaymentPlans = () => {
|
const handleRegistrationPaymentPlans = () => {
|
||||||
fetchRegistrationPaymentPlans()
|
fetchRegistrationPaymentPlans(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setRegistrationPaymentPlans(data);
|
setRegistrationPaymentPlans(data);
|
||||||
})
|
})
|
||||||
@ -164,7 +167,7 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleTuitionPaymentPlans = () => {
|
const handleTuitionPaymentPlans = () => {
|
||||||
fetchTuitionPaymentPlans()
|
fetchTuitionPaymentPlans(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setTuitionPaymentPlans(data);
|
setTuitionPaymentPlans(data);
|
||||||
})
|
})
|
||||||
@ -172,7 +175,7 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleRegistrationPaymentModes = () => {
|
const handleRegistrationPaymentModes = () => {
|
||||||
fetchRegistrationPaymentModes()
|
fetchRegistrationPaymentModes(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setRegistrationPaymentModes(data);
|
setRegistrationPaymentModes(data);
|
||||||
})
|
})
|
||||||
@ -180,7 +183,7 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleTuitionPaymentModes = () => {
|
const handleTuitionPaymentModes = () => {
|
||||||
fetchTuitionPaymentModes()
|
fetchTuitionPaymentModes(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setTuitionPaymentModes(data);
|
setTuitionPaymentModes(data);
|
||||||
})
|
})
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import Modal from '@/components/Modal';
|
|||||||
import InscriptionForm from '@/components/Inscription/InscriptionForm'
|
import InscriptionForm from '@/components/Inscription/InscriptionForm'
|
||||||
import AffectationClasseForm from '@/components/AffectationClasseForm'
|
import AffectationClasseForm from '@/components/AffectationClasseForm'
|
||||||
import { getSession } from 'next-auth/react';
|
import { getSession } from 'next-auth/react';
|
||||||
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PENDING,
|
PENDING,
|
||||||
@ -50,7 +51,6 @@ import {
|
|||||||
|
|
||||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
|
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
|
||||||
import { useCsrfToken } from '@/context/CsrfContext';
|
import { useCsrfToken } from '@/context/CsrfContext';
|
||||||
import { ESTABLISHMENT_ID } from '@/utils/Url';
|
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
|
|
||||||
export default function Page({ params: { locale } }) {
|
export default function Page({ params: { locale } }) {
|
||||||
@ -87,9 +87,8 @@ export default function Page({ params: { locale } }) {
|
|||||||
const [tuitionFees, setTuitionFees] = useState([]);
|
const [tuitionFees, setTuitionFees] = useState([]);
|
||||||
const [groups, setGroups] = useState([]);
|
const [groups, setGroups] = useState([]);
|
||||||
|
|
||||||
const [establishmentId, setEstablishmentId] = useState(null);
|
|
||||||
|
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
|
const { selectedEstablishmentId } = useEstablishment();
|
||||||
|
|
||||||
const openModal = () => {
|
const openModal = () => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
@ -167,23 +166,11 @@ const registerFormArchivedDataHandler = (data) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getSession()
|
if (selectedEstablishmentId) {
|
||||||
.then(session => {
|
|
||||||
if (session && session.user) {
|
|
||||||
setEstablishmentId(session.user.establishment);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
logger.error('Error fetching session:', err);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (establishmentId) {
|
|
||||||
const fetchInitialData = () => {
|
const fetchInitialData = () => {
|
||||||
Promise.all([
|
Promise.all([
|
||||||
fetchClasses(),
|
fetchClasses(selectedEstablishmentId),
|
||||||
fetchStudents(establishmentId) // Utiliser l'ID de l'établissement ici
|
fetchStudents(selectedEstablishmentId) // Utiliser l'ID de l'établissement ici
|
||||||
])
|
])
|
||||||
.then(([classesData, studentsData]) => {
|
.then(([classesData, studentsData]) => {
|
||||||
setClasses(classesData);
|
setClasses(classesData);
|
||||||
@ -198,21 +185,21 @@ useEffect(() => {
|
|||||||
|
|
||||||
fetchInitialData();
|
fetchInitialData();
|
||||||
}
|
}
|
||||||
}, [establishmentId]);
|
}, [selectedEstablishmentId]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (establishmentId) {
|
if (selectedEstablishmentId) {
|
||||||
const fetchDataAndSetState = () => {
|
const fetchDataAndSetState = () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
Promise.all([
|
Promise.all([
|
||||||
fetchRegisterForms(establishmentId, PENDING, currentPage, itemsPerPage, searchTerm)
|
fetchRegisterForms(selectedEstablishmentId, PENDING, currentPage, itemsPerPage, searchTerm)
|
||||||
.then(registerFormPendingDataHandler)
|
.then(registerFormPendingDataHandler)
|
||||||
.catch(requestErrorHandler),
|
.catch(requestErrorHandler),
|
||||||
fetchRegisterForms(establishmentId, SUBSCRIBED)
|
fetchRegisterForms(selectedEstablishmentId, SUBSCRIBED)
|
||||||
.then(registerFormSubscribedDataHandler)
|
.then(registerFormSubscribedDataHandler)
|
||||||
.catch(requestErrorHandler),
|
.catch(requestErrorHandler),
|
||||||
fetchRegisterForms(establishmentId, ARCHIVED)
|
fetchRegisterForms(selectedEstablishmentId, ARCHIVED)
|
||||||
.then(registerFormArchivedDataHandler)
|
.then(registerFormArchivedDataHandler)
|
||||||
.catch(requestErrorHandler),
|
.catch(requestErrorHandler),
|
||||||
fetchRegistrationTemplateMaster()
|
fetchRegistrationTemplateMaster()
|
||||||
@ -222,27 +209,27 @@ useEffect(() => {
|
|||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.debug(err.message);
|
logger.debug(err.message);
|
||||||
}),
|
}),
|
||||||
fetchRegistrationDiscounts()
|
fetchRegistrationDiscounts(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setRegistrationDiscounts(data);
|
setRegistrationDiscounts(data);
|
||||||
})
|
})
|
||||||
.catch(requestErrorHandler),
|
.catch(requestErrorHandler),
|
||||||
fetchTuitionDiscounts()
|
fetchTuitionDiscounts(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setTuitionDiscounts(data);
|
setTuitionDiscounts(data);
|
||||||
})
|
})
|
||||||
.catch(requestErrorHandler),
|
.catch(requestErrorHandler),
|
||||||
fetchRegistrationFees()
|
fetchRegistrationFees(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setRegistrationFees(data);
|
setRegistrationFees(data);
|
||||||
})
|
})
|
||||||
.catch(requestErrorHandler),
|
.catch(requestErrorHandler),
|
||||||
fetchTuitionFees()
|
fetchTuitionFees(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setTuitionFees(data);
|
setTuitionFees(data);
|
||||||
})
|
})
|
||||||
.catch(requestErrorHandler),
|
.catch(requestErrorHandler),
|
||||||
fetchRegistrationFileGroups()
|
fetchRegistrationFileGroups(selectedEstablishmentId)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setGroups(data);
|
setGroups(data);
|
||||||
})
|
})
|
||||||
@ -263,20 +250,20 @@ useEffect(() => {
|
|||||||
|
|
||||||
fetchDataAndSetState();
|
fetchDataAndSetState();
|
||||||
}
|
}
|
||||||
}, [establishmentId, reloadFetch, currentPage, searchTerm]);
|
}, [selectedEstablishmentId, reloadFetch, currentPage, searchTerm]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (establishmentId) {
|
if (selectedEstablishmentId) {
|
||||||
const fetchDataAndSetState = () => {
|
const fetchDataAndSetState = () => {
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
fetchRegisterForms(establishmentId, PENDING, currentPage, itemsPerPage, searchTerm)
|
fetchRegisterForms(selectedEstablishmentId, PENDING, currentPage, itemsPerPage, searchTerm)
|
||||||
.then(registerFormPendingDataHandler)
|
.then(registerFormPendingDataHandler)
|
||||||
.catch(requestErrorHandler)
|
.catch(requestErrorHandler)
|
||||||
fetchRegisterForms(establishmentId, SUBSCRIBED)
|
fetchRegisterForms(selectedEstablishmentId, SUBSCRIBED)
|
||||||
.then(registerFormSubscribedDataHandler)
|
.then(registerFormSubscribedDataHandler)
|
||||||
.catch(requestErrorHandler)
|
.catch(requestErrorHandler)
|
||||||
fetchRegisterForms(establishmentId, ARCHIVED)
|
fetchRegisterForms(selectedEstablishmentId, ARCHIVED)
|
||||||
.then(registerFormArchivedDataHandler)
|
.then(registerFormArchivedDataHandler)
|
||||||
.catch(requestErrorHandler)
|
.catch(requestErrorHandler)
|
||||||
fetchRegistrationTemplateMaster()
|
fetchRegistrationTemplateMaster()
|
||||||
@ -545,7 +532,7 @@ useEffect(()=>{
|
|||||||
const columns = [
|
const columns = [
|
||||||
{ name: t('studentName'), transform: (row) => row.student.last_name },
|
{ name: t('studentName'), transform: (row) => row.student.last_name },
|
||||||
{ name: t('studentFistName'), transform: (row) => row.student.first_name },
|
{ name: t('studentFistName'), transform: (row) => row.student.first_name },
|
||||||
{ name: t('mainContactMail'), transform: (row) => row.student.guardians[0].email },
|
{ name: t('mainContactMail'), transform: (row) => row.student.guardians[0].associated_profile_email },
|
||||||
{ name: t('phone'), transform: (row) => formatPhoneNumber(row.student.guardians[0].phone) },
|
{ name: t('phone'), transform: (row) => formatPhoneNumber(row.student.guardians[0].phone) },
|
||||||
{ name: t('lastUpdateDate'), transform: (row) => row.formatted_last_update},
|
{ name: t('lastUpdateDate'), transform: (row) => row.formatted_last_update},
|
||||||
{ name: t('registrationFileStatus'), transform: (row) => (
|
{ name: t('registrationFileStatus'), transform: (row) => (
|
||||||
|
|||||||
@ -37,8 +37,9 @@ export default function Layout({
|
|||||||
setIsPopupVisible(false);
|
setIsPopupVisible(false);
|
||||||
disconnect();
|
disconnect();
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
|
||||||
const fetchUser = async () => {
|
useEffect(() => {
|
||||||
|
const fetchUser = async () => {
|
||||||
if (session) { // Vérifier que la session existe
|
if (session) { // Vérifier que la session existe
|
||||||
const userData = await getUser();
|
const userData = await getUser();
|
||||||
setUser(userData);
|
setUser(userData);
|
||||||
@ -48,6 +49,7 @@ export default function Layout({
|
|||||||
fetchUser();
|
fetchUser();
|
||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (status === 'loading') return;
|
// if (status === 'loading') return;
|
||||||
// if (!session) {
|
// if (!session) {
|
||||||
@ -81,7 +83,7 @@ const dropdownItems = [
|
|||||||
content: (
|
content: (
|
||||||
<div className="px-4 py-2">
|
<div className="px-4 py-2">
|
||||||
<div className="font-medium">{user?.email || 'Utilisateur'}</div>
|
<div className="font-medium">{user?.email || 'Utilisateur'}</div>
|
||||||
<div className="text-xs text-gray-400">{getRightStr(user?.role) || ''}</div>
|
<div className="text-xs text-gray-400">{getRightStr(user?.roles[0]?.role_type) || ''}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@ -9,13 +9,15 @@ import { fetchChildren } from '@/app/actions/subscriptionAction';
|
|||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
import { FE_USERS_LOGIN_URL } from '@/utils/Url';
|
import { FE_USERS_LOGIN_URL } from '@/utils/Url';
|
||||||
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||||
|
|
||||||
export default function ParentHomePage() {
|
export default function ParentHomePage() {
|
||||||
|
|
||||||
const [children, setChildren] = useState([]);
|
const [children, setChildren] = useState([]);
|
||||||
const { data: session, status } = useSession();
|
const { data: session, status } = useSession();
|
||||||
const [userId, setUserId] = useState(null);
|
const [userId, setUserId] = useState(null);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const [establishments, setEstablishments] = useState([]);
|
||||||
|
const { selectedEstablishmentId, setSelectedEstablishmentId } = useEstablishment();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -24,21 +26,42 @@ export default function ParentHomePage() {
|
|||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
router.push(`${FE_USERS_LOGIN_URL}`);
|
router.push(`${FE_USERS_LOGIN_URL}`);
|
||||||
}
|
} else {
|
||||||
console.log(session);
|
const userIdFromSession = session.user.user_id;
|
||||||
const userIdFromSession = session.user.user_id;
|
setUserId(userIdFromSession);
|
||||||
setUserId(userIdFromSession);
|
|
||||||
|
|
||||||
fetchChildren(userIdFromSession).then(data => {
|
const userEstablishments = session.user.roles.map(role => ({
|
||||||
setChildren(data);
|
id: role.establishment__id,
|
||||||
});
|
name: role.establishment__name,
|
||||||
}, [userId]);
|
role_type: role.role_type
|
||||||
|
}));
|
||||||
|
setEstablishments(userEstablishments);
|
||||||
|
|
||||||
|
const storedEstablishmentId = localStorage.getItem('selectedEstablishmentId');
|
||||||
|
if (storedEstablishmentId) {
|
||||||
|
setSelectedEstablishmentId(storedEstablishmentId);
|
||||||
|
} else if (userEstablishments.length > 0) {
|
||||||
|
setSelectedEstablishmentId(userEstablishments[0].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchChildren(userIdFromSession, storedEstablishmentId).then(data => {
|
||||||
|
setChildren(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [status, session, selectedEstablishmentId]);
|
||||||
|
|
||||||
|
const handleEstablishmentChange = (e) => {
|
||||||
|
const establishmentId = parseInt(e.target.value, 10);
|
||||||
|
setSelectedEstablishmentId(establishmentId);
|
||||||
|
localStorage.setItem('selectedEstablishmentId', establishmentId);
|
||||||
|
};
|
||||||
|
|
||||||
function handleEdit(eleveId) {
|
function handleEdit(eleveId) {
|
||||||
// Logique pour éditer le dossier de l'élève
|
// Logique pour éditer le dossier de l'élève
|
||||||
logger.debug(`Edit dossier for student id: ${eleveId}`);
|
logger.debug(`Edit dossier for student id: ${eleveId}`);
|
||||||
router.push(`${FE_PARENTS_EDIT_INSCRIPTION_URL}?id=${userId}&studentId=${eleveId}`);
|
router.push(`${FE_PARENTS_EDIT_INSCRIPTION_URL}?id=${userId}&studentId=${eleveId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionColumns = [
|
const actionColumns = [
|
||||||
{ name: 'Action', transform: (row) => row.action },
|
{ name: 'Action', transform: (row) => row.action },
|
||||||
];
|
];
|
||||||
@ -106,6 +129,22 @@ export default function ParentHomePage() {
|
|||||||
<Users className="h-6 w-6 text-emerald-600" />
|
<Users className="h-6 w-6 text-emerald-600" />
|
||||||
Enfants
|
Enfants
|
||||||
</h2>
|
</h2>
|
||||||
|
{establishments.length > 1 && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="block text-gray-700 text-sm font-bold mb-2">Sélectionnez un établissement :</label>
|
||||||
|
<select
|
||||||
|
value={selectedEstablishmentId}
|
||||||
|
onChange={handleEstablishmentChange}
|
||||||
|
className="block w-full mt-1 p-2 border border-gray-300 rounded-md"
|
||||||
|
>
|
||||||
|
{establishments.map(establishment => (
|
||||||
|
<option key={establishment.id} value={establishment.id}>
|
||||||
|
{establishment.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<Table
|
<Table
|
||||||
data={children}
|
data={children}
|
||||||
|
|||||||
@ -13,18 +13,20 @@ import {
|
|||||||
FE_PARENTS_HOME_URL
|
FE_PARENTS_HOME_URL
|
||||||
} from '@/utils/Url';
|
} from '@/utils/Url';
|
||||||
import { login } from '@/app/actions/authAction';
|
import { login } from '@/app/actions/authAction';
|
||||||
import { getSession } from 'next-auth/react';
|
import { getSession } from 'next-auth/react';
|
||||||
import { useCsrfToken } from '@/context/CsrfContext'; // Importez le hook useCsrfToken
|
import { useCsrfToken } from '@/context/CsrfContext'; // Importez le hook useCsrfToken
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
|
import ProfileSelector from '@/components/ProfileSelector'; // Importez le composant ProfileSelector
|
||||||
|
|
||||||
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
|
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const [errorMessage, setErrorMessage] = useState("");
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
const [userFieldError,setUserFieldError] = useState("")
|
const [userFieldError, setUserFieldError] = useState("")
|
||||||
const [passwordFieldError,setPasswordFieldError] = useState("")
|
const [passwordFieldError, setPasswordFieldError] = useState("")
|
||||||
|
const [selectedProfile, setSelectedProfile] = useState(1); // Par défaut, sélectionnez ADMIN
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@ -35,52 +37,54 @@ export default function Page() {
|
|||||||
return data.errorMessage === ""
|
return data.errorMessage === ""
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFormLogin(formData) {
|
function handleFormLogin(formData) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
login({
|
login({
|
||||||
email: formData.get('login'),
|
email: formData.get('login'),
|
||||||
password: formData.get('password'),
|
password: formData.get('password'),
|
||||||
}).then(result => {
|
role_type: selectedProfile // Utilisez le profil sélectionné
|
||||||
logger.debug('Sign In Result', result);
|
}).then(result => {
|
||||||
setIsLoading(false);
|
logger.debug('Sign In Result', result);
|
||||||
if (result.error) {
|
setIsLoading(false);
|
||||||
setErrorMessage(result.error);
|
if (result.error) {
|
||||||
} else {
|
setErrorMessage(result.error);
|
||||||
getSession().then(session => {
|
} else {
|
||||||
if (!session || !session.user) {
|
getSession().then(session => {
|
||||||
throw new Error('Session not found');
|
if (!session || !session.user) {
|
||||||
}
|
throw new Error('Session not found');
|
||||||
const user = session.user;
|
}
|
||||||
logger.debug('User Session:', user);
|
const user = session.user;
|
||||||
|
logger.debug('User Session:', user);
|
||||||
|
|
||||||
if (user.establishment_id) {
|
const roles = user.roles.filter(role => role.role_type === selectedProfile);
|
||||||
localStorage.setItem('establishment_id', user.establishment_id);
|
if (roles.length > 0) {
|
||||||
}
|
// const establishment = roles[0].establishment;
|
||||||
|
// localStorage.setItem('establishment_id', establishment);
|
||||||
|
|
||||||
if (user.droit === 0) {
|
// Redirection en fonction du rôle
|
||||||
// Vue ECOLE
|
if (roles[0].role_type === 1) {
|
||||||
} else if (user.droit === 1) {
|
|
||||||
// Vue ADMIN
|
|
||||||
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
|
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
|
||||||
} else if (user.droit === 2) {
|
} else if (roles[0].role_type === 2) {
|
||||||
// Vue PARENT
|
|
||||||
router.push(FE_PARENTS_HOME_URL);
|
router.push(FE_PARENTS_HOME_URL);
|
||||||
} else {
|
} else {
|
||||||
// Cas anormal
|
// Cas anormal
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
} else {
|
||||||
logger.error('Error during session retrieval:', error);
|
setErrorMessage('No roles found for the specified role type.');
|
||||||
setIsLoading(false);
|
}
|
||||||
setErrorMessage('An error occurred during session retrieval.');
|
}).catch(error => {
|
||||||
});
|
logger.error('Error during session retrieval:', error);
|
||||||
}
|
setIsLoading(false);
|
||||||
}).catch(error => {
|
setErrorMessage('An error occurred during session retrieval.');
|
||||||
logger.error('Error during sign in:', error);
|
});
|
||||||
setIsLoading(false);
|
}
|
||||||
setErrorMessage('An error occurred during sign in.');
|
}).catch(error => {
|
||||||
});
|
logger.error('Error during sign in:', error);
|
||||||
}
|
setIsLoading(false);
|
||||||
|
setErrorMessage('An error occurred during sign in.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (isLoading === true) {
|
if (isLoading === true) {
|
||||||
return <Loader /> // Affichez le composant Loader
|
return <Loader /> // Affichez le composant Loader
|
||||||
@ -94,7 +98,8 @@ export default function Page() {
|
|||||||
<form className="max-w-md mx-auto" onSubmit={(e) => { e.preventDefault(); handleFormLogin(new FormData(e.target)); }}>
|
<form className="max-w-md mx-auto" onSubmit={(e) => { e.preventDefault(); handleFormLogin(new FormData(e.target)); }}>
|
||||||
<DjangoCSRFToken csrfToken={csrfToken} />
|
<DjangoCSRFToken csrfToken={csrfToken} />
|
||||||
<InputTextIcon name="login" type="text" IconItem={User} label="Identifiant" placeholder="Identifiant" errorMsg={userFieldError} className="w-full mb-5" />
|
<InputTextIcon name="login" type="text" IconItem={User} label="Identifiant" placeholder="Identifiant" errorMsg={userFieldError} className="w-full mb-5" />
|
||||||
<InputTextIcon name="password" type="password" IconItem={KeySquare} label="Mot de passe" placeholder="Mot de passe" errorMsg={passwordFieldError} className="w-full" />
|
<InputTextIcon name="password" type="password" IconItem={KeySquare} label="Mot de passe" placeholder="Mot de passe" errorMsg={passwordFieldError} className="w-full mb-5" />
|
||||||
|
<ProfileSelector selectedProfile={selectedProfile} setSelectedProfile={setSelectedProfile} />
|
||||||
<div className="input-group mb-4">
|
<div className="input-group mb-4">
|
||||||
</div>
|
</div>
|
||||||
<label className="text-red-500">{errorMessage}</label>
|
<label className="text-red-500">{errorMessage}</label>
|
||||||
|
|||||||
@ -30,6 +30,7 @@ export const login = (data) => {
|
|||||||
redirect: false,
|
redirect: false,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
password: data.password,
|
password: data.password,
|
||||||
|
role_type: data.role_type,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -194,7 +195,11 @@ export const getUser = async () => {
|
|||||||
return {
|
return {
|
||||||
id: session.user.user_id,
|
id: session.user.user_id,
|
||||||
email: session.user.email,
|
email: session.user.email,
|
||||||
role: session.user.droit
|
roles: session.user.roles.map(role => ({
|
||||||
|
role_type: role.role_type,
|
||||||
|
establishment_id: role.establishment__id,
|
||||||
|
establishment_name: role.establishment__name
|
||||||
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -7,8 +7,7 @@ import {
|
|||||||
BE_SCHOOL_DISCOUNTS_URL,
|
BE_SCHOOL_DISCOUNTS_URL,
|
||||||
BE_SCHOOL_PAYMENT_PLANS_URL,
|
BE_SCHOOL_PAYMENT_PLANS_URL,
|
||||||
BE_SCHOOL_PAYMENT_MODES_URL,
|
BE_SCHOOL_PAYMENT_MODES_URL,
|
||||||
BE_SCHOOL_ESTABLISHMENT_URL,
|
BE_SCHOOL_ESTABLISHMENT_URL
|
||||||
ESTABLISHMENT_ID
|
|
||||||
} from '@/utils/Url';
|
} from '@/utils/Url';
|
||||||
|
|
||||||
const requestResponseHandler = async (response) => {
|
const requestResponseHandler = async (response) => {
|
||||||
@ -24,18 +23,18 @@ const requestResponseHandler = async (response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const fetchSpecialities = () => {
|
export const fetchSpecialities = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_SPECIALITIES_URL}`)
|
return fetch(`${BE_SCHOOL_SPECIALITIES_URL}?establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchTeachers = () => {
|
export const fetchTeachers = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_TEACHERS_URL}`)
|
return fetch(`${BE_SCHOOL_TEACHERS_URL}?establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchClasses = () => {
|
export const fetchClasses = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_SCHOOLCLASSES_URL}`)
|
return fetch(`${BE_SCHOOL_SCHOOLCLASSES_URL}?establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,48 +43,48 @@ export const fetchSchedules = () => {
|
|||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchRegistrationDiscounts = () => {
|
export const fetchRegistrationDiscounts = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_DISCOUNTS_URL}?filter=registration`)
|
return fetch(`${BE_SCHOOL_DISCOUNTS_URL}?filter=registration&establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchTuitionDiscounts = () => {
|
export const fetchTuitionDiscounts = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_DISCOUNTS_URL}?filter=tuition`)
|
return fetch(`${BE_SCHOOL_DISCOUNTS_URL}?filter=tuition&establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchRegistrationFees = () => {
|
export const fetchRegistrationFees = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_FEES_URL}?filter=registration`)
|
return fetch(`${BE_SCHOOL_FEES_URL}?filter=registration&establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchTuitionFees = () => {
|
export const fetchTuitionFees = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_FEES_URL}?filter=tuition`)
|
return fetch(`${BE_SCHOOL_FEES_URL}?filter=tuition&establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchRegistrationPaymentPlans = () => {
|
export const fetchRegistrationPaymentPlans = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_PAYMENT_PLANS_URL}?filter=registration`)
|
return fetch(`${BE_SCHOOL_PAYMENT_PLANS_URL}?filter=registration&establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchTuitionPaymentPlans = () => {
|
export const fetchTuitionPaymentPlans = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_PAYMENT_PLANS_URL}?filter=tuition`)
|
return fetch(`${BE_SCHOOL_PAYMENT_PLANS_URL}?filter=tuition&establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchRegistrationPaymentModes = () => {
|
export const fetchRegistrationPaymentModes = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_PAYMENT_MODES_URL}?filter=registration`)
|
return fetch(`${BE_SCHOOL_PAYMENT_MODES_URL}?filter=registration&establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchTuitionPaymentModes = () => {
|
export const fetchTuitionPaymentModes = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_PAYMENT_MODES_URL}?filter=tuition`)
|
return fetch(`${BE_SCHOOL_PAYMENT_MODES_URL}?filter=tuition&establishment_id=${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchEstablishment = () => {
|
export const fetchEstablishment = (establishment) => {
|
||||||
return fetch(`${BE_SCHOOL_ESTABLISHMENT_URL}/${ESTABLISHMENT_ID}`)
|
return fetch(`${BE_SCHOOL_ESTABLISHMENT_URL}/${establishment}`)
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -100,7 +100,7 @@ export const archiveRegisterForm = (id) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const fetchStudents = (id=null, establishment) => {
|
export const fetchStudents = (id=null, establishment) => {
|
||||||
const url = (id)?`${BE_SUBSCRIPTION_STUDENTS_URL}/${id}`:`${BE_SUBSCRIPTION_STUDENTS_URL}?establishment_id=${establishment}`;
|
const url = (id)?`${BE_SUBSCRIPTION_STUDENTS_URL}/${id}?establishment_id=${establishment}`:`${BE_SUBSCRIPTION_STUDENTS_URL}?establishment_id=${establishment}`;
|
||||||
const request = new Request(
|
const request = new Request(
|
||||||
url,
|
url,
|
||||||
{
|
{
|
||||||
@ -114,9 +114,9 @@ export const fetchStudents = (id=null, establishment) => {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchChildren = (id) =>{
|
export const fetchChildren = (id, establishment) =>{
|
||||||
const request = new Request(
|
const request = new Request(
|
||||||
`${BE_SUBSCRIPTION_CHILDRENS_URL}/${id}`,
|
`${BE_SUBSCRIPTION_CHILDRENS_URL}/${id}?establishment_id=${establishment}`,
|
||||||
{
|
{
|
||||||
method:'GET',
|
method:'GET',
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
31
Front-End/src/components/ProfileSelector.js
Normal file
31
Front-End/src/components/ProfileSelector.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const ProfileSelector = ({ selectedProfile, setSelectedProfile }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex space-x-4">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`px-4 py-2 rounded-lg shadow-md focus:outline-none focus:ring-2 focus:ring-blue-300 ${selectedProfile === 1 ? 'bg-blue-500 text-white ring-2 ring-blue-500' : 'bg-gray-200'}`}
|
||||||
|
onClick={() => setSelectedProfile(1)}
|
||||||
|
>
|
||||||
|
ADMINISTRATEUR
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`px-4 py-2 rounded-lg shadow-md focus:outline-none focus:ring-2 focus:ring-blue-300 ${selectedProfile === 0 ? 'bg-blue-500 text-white ring-2 ring-blue-500' : 'bg-gray-200'}`}
|
||||||
|
onClick={() => setSelectedProfile(0)}
|
||||||
|
>
|
||||||
|
PROFESSEUR
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`px-4 py-2 rounded-lg shadow-md focus:outline-none focus:ring-2 focus:ring-blue-300 ${selectedProfile === 2 ? 'bg-blue-500 text-white ring-2 ring-blue-500' : 'bg-gray-200'}`}
|
||||||
|
onClick={() => setSelectedProfile(2)}
|
||||||
|
>
|
||||||
|
PARENT
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProfileSelector;
|
||||||
@ -18,8 +18,10 @@ const ProtectedRoute = ({ children, requiredRight }) => {
|
|||||||
return <Loader />;
|
return <Loader />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vérifier le rôle de l'utilisateur
|
// Vérifier si l'utilisateur a au moins un rôle correspondant au requiredRight
|
||||||
if (session && requiredRight && session.user.droit !== requiredRight) {
|
const hasRequiredRight = session?.user?.roles?.some(role => role.role_type === requiredRight);
|
||||||
|
|
||||||
|
if (session && requiredRight && !hasRequiredRight) {
|
||||||
router.push(`${FE_USERS_LOGIN_URL}`);
|
router.push(`${FE_USERS_LOGIN_URL}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
import { SessionProvider } from "next-auth/react"
|
import { SessionProvider } from "next-auth/react"
|
||||||
import { CsrfProvider } from '@/context/CsrfContext'
|
import { CsrfProvider } from '@/context/CsrfContext'
|
||||||
import { NextIntlClientProvider } from 'next-intl'
|
import { NextIntlClientProvider } from 'next-intl'
|
||||||
|
import { EstablishmentProvider } from '@/context/EstablishmentContext';
|
||||||
|
|
||||||
|
|
||||||
export default function Providers({ children, messages, locale, session }) {
|
export default function Providers({ children, messages, locale, session }) {
|
||||||
if (!locale) {
|
if (!locale) {
|
||||||
@ -12,9 +14,11 @@ export default function Providers({ children, messages, locale, session }) {
|
|||||||
return (
|
return (
|
||||||
<SessionProvider session={session}>
|
<SessionProvider session={session}>
|
||||||
<CsrfProvider>
|
<CsrfProvider>
|
||||||
<NextIntlClientProvider messages={messages} locale={locale}>
|
<EstablishmentProvider>
|
||||||
{children}
|
<NextIntlClientProvider messages={messages} locale={locale}>
|
||||||
</NextIntlClientProvider>
|
{children}
|
||||||
|
</NextIntlClientProvider>
|
||||||
|
</EstablishmentProvider>
|
||||||
</CsrfProvider>
|
</CsrfProvider>
|
||||||
</SessionProvider>
|
</SessionProvider>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||||
|
|
||||||
const SidebarItem = ({ icon: Icon, text, active, url, onClick }) => (
|
const SidebarItem = ({ icon: Icon, text, active, url, onClick }) => (
|
||||||
<div
|
<div
|
||||||
@ -14,8 +15,9 @@ const SidebarItem = ({ icon: Icon, text, active, url, onClick }) => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
function Sidebar({ establishment, currentPage, items }) {
|
function Sidebar({ establishments, currentPage, items, onCloseMobile, onEstablishmentChange }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { selectedEstablishmentId, setSelectedEstablishmentId, setProfileRole } = useEstablishment();
|
||||||
const [selectedItem, setSelectedItem] = useState(currentPage);
|
const [selectedItem, setSelectedItem] = useState(currentPage);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -25,31 +27,49 @@ function Sidebar({ establishment, currentPage, items }) {
|
|||||||
const handleItemClick = (url) => {
|
const handleItemClick = (url) => {
|
||||||
setSelectedItem(url);
|
setSelectedItem(url);
|
||||||
router.push(url);
|
router.push(url);
|
||||||
|
if (onCloseMobile) {
|
||||||
|
onCloseMobile();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return <>
|
const handleEstablishmentChange = (e) => {
|
||||||
{/* Sidebar */}
|
const establishmentId = parseInt(e.target.value, 10);
|
||||||
|
setSelectedEstablishmentId(establishmentId);
|
||||||
|
const role = establishments.find(est => est.id === establishmentId)?.role_type;
|
||||||
|
setProfileRole(role);
|
||||||
|
onEstablishmentChange(establishmentId);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="w-64 bg-white border-r h-full border-gray-200 py-6 px-4">
|
<div className="w-64 bg-white border-r h-full border-gray-200 py-6 px-4">
|
||||||
<div className="flex items-center mb-8 px-2">
|
<div className="flex items-center mb-8 px-2">
|
||||||
<div className="text-xl font-semibold">{establishment?.name}</div>
|
<select
|
||||||
|
value={selectedEstablishmentId}
|
||||||
|
onChange={handleEstablishmentChange}
|
||||||
|
className="form-select block w-full mt-1"
|
||||||
|
>
|
||||||
|
{establishments.map(establishment => (
|
||||||
|
<option key={establishment.id} value={establishment.id}>
|
||||||
|
{establishment.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav className="space-y-1">
|
<nav className="space-y-1">
|
||||||
{
|
{items.map((item) => (
|
||||||
items.map((item) => (
|
<SidebarItem
|
||||||
<SidebarItem
|
key={item.id}
|
||||||
key={item.id}
|
icon={item.icon}
|
||||||
icon={item.icon}
|
text={item.name}
|
||||||
text={item.name}
|
active={item.id === selectedItem}
|
||||||
active={item.id === selectedItem}
|
url={item.url}
|
||||||
url={item.url}
|
onClick={() => handleItemClick(item.url)}
|
||||||
onClick={() => handleItemClick(item.url)}
|
/>
|
||||||
/>
|
))}
|
||||||
))
|
|
||||||
}
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</>
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Sidebar;
|
export default Sidebar;
|
||||||
@ -329,7 +329,7 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
|
|||||||
<TeacherItem key={teacher.id} teacher={teacher} />
|
<TeacherItem key={teacher.id} teacher={teacher} />
|
||||||
);
|
);
|
||||||
case 'EMAIL':
|
case 'EMAIL':
|
||||||
return teacher.email;
|
return teacher.associated_profile_email;
|
||||||
case 'SPECIALITES':
|
case 'SPECIALITES':
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center space-x-2 flex-wrap">
|
<div className="flex justify-center space-x-2 flex-wrap">
|
||||||
|
|||||||
341
Front-End/src/components/Structure/Files/FilesManagement.js
Normal file
341
Front-End/src/components/Structure/Files/FilesManagement.js
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Plus, Download, Edit, Trash2, FolderPlus, Signature } from 'lucide-react';
|
||||||
|
import Modal from '@/components/Modal';
|
||||||
|
import Table from '@/components/Table';
|
||||||
|
import FileUpload from '@/components/FileUpload';
|
||||||
|
import { formatDate } from '@/utils/Date';
|
||||||
|
import { BASE_URL } from '@/utils/Url';
|
||||||
|
import {
|
||||||
|
fetchRegisterFormFileTemplate,
|
||||||
|
createRegistrationFormFileTemplate,
|
||||||
|
editRegistrationFormFileTemplate,
|
||||||
|
deleteRegisterFormFileTemplate,
|
||||||
|
getRegisterFormFileTemplate
|
||||||
|
} from '@/app/actions/subscriptionAction';
|
||||||
|
import {
|
||||||
|
fetchRegistrationFileGroups,
|
||||||
|
createRegistrationFileGroup,
|
||||||
|
deleteRegistrationFileGroup,
|
||||||
|
editRegistrationFileGroup
|
||||||
|
} from '@/app/actions/registerFileGroupAction';
|
||||||
|
import RegistrationFileGroupForm from '@/components/RegistrationFileGroupForm';
|
||||||
|
|
||||||
|
export default function FilesManagement({ csrfToken }) {
|
||||||
|
const [fichiers, setFichiers] = useState([]);
|
||||||
|
const [groups, setGroups] = useState([]);
|
||||||
|
const [selectedGroup, setSelectedGroup] = useState(null);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [fileToEdit, setFileToEdit] = useState(null);
|
||||||
|
const [isGroupModalOpen, setIsGroupModalOpen] = useState(false);
|
||||||
|
const [groupToEdit, setGroupToEdit] = useState(null);
|
||||||
|
const [token]
|
||||||
|
|
||||||
|
// Fonction pour transformer les données des fichiers avec les informations complètes du groupe
|
||||||
|
const transformFileData = (file, groups) => {
|
||||||
|
if (!file.group) return file;
|
||||||
|
|
||||||
|
const groupInfo = groups.find(g => g.id === file.group);
|
||||||
|
return {
|
||||||
|
...file,
|
||||||
|
group: groupInfo || { id: file.group, name: 'Groupe inconnu' }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Promise.all([
|
||||||
|
fetchRegisterFormFileTemplate(),
|
||||||
|
fetchRegistrationFileGroups()
|
||||||
|
]).then(([filesData, groupsData]) => {
|
||||||
|
setGroups(groupsData);
|
||||||
|
// Sélectionner automatiquement le premier groupe s'il existe
|
||||||
|
if (groupsData.length > 0) {
|
||||||
|
setSelectedGroup(groupsData[0].id.toString());
|
||||||
|
}
|
||||||
|
// Transformer chaque fichier pour inclure les informations complètes du groupe
|
||||||
|
const transformedFiles = filesData.map(file => transformFileData(file, groupsData));
|
||||||
|
setFichiers(transformedFiles);
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(err.message);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleFileDelete = (fileId) => {
|
||||||
|
deleteRegisterFormFileTemplate(fileId, csrfToken)
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
setFichiers(fichiers.filter(fichier => fichier.id !== fileId));
|
||||||
|
alert('Fichier supprimé avec succès.');
|
||||||
|
} else {
|
||||||
|
alert('Erreur lors de la suppression du fichier.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error deleting file:', error);
|
||||||
|
alert('Erreur lors de la suppression du fichier.');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileEdit = (file) => {
|
||||||
|
setIsEditing(true);
|
||||||
|
setFileToEdit(file);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileUpload = ({file, name, is_required, order, groupId}) => {
|
||||||
|
if (!name) {
|
||||||
|
alert('Veuillez entrer un nom de fichier.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
if(file) {
|
||||||
|
formData.append('file', file);
|
||||||
|
}
|
||||||
|
formData.append('name', name);
|
||||||
|
formData.append('is_required', is_required);
|
||||||
|
formData.append('order', order);
|
||||||
|
|
||||||
|
// Modification ici : vérifier si groupId existe et n'est pas vide
|
||||||
|
if (groupId && groupId !== '') {
|
||||||
|
formData.append('group', groupId); // Notez que le nom du champ est 'group' et non 'group_id'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEditing && fileToEdit) {
|
||||||
|
editRegistrationFormFileTemplate(fileToEdit.id, formData, csrfToken)
|
||||||
|
.then(data => {
|
||||||
|
// Transformer le fichier mis à jour avec les informations du groupe
|
||||||
|
const transformedFile = transformFileData(data, groups);
|
||||||
|
setFichiers(prevFichiers =>
|
||||||
|
prevFichiers.map(f => f.id === fileToEdit.id ? transformedFile : f)
|
||||||
|
);
|
||||||
|
setIsModalOpen(false);
|
||||||
|
setFileToEdit(null);
|
||||||
|
setIsEditing(false);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error editing file:', error);
|
||||||
|
alert('Erreur lors de la modification du fichier');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
createRegistrationFormFileTemplate(formData, csrfToken)
|
||||||
|
.then(data => {
|
||||||
|
// Transformer le nouveau fichier avec les informations du groupe
|
||||||
|
const transformedFile = transformFileData(data, groups);
|
||||||
|
setFichiers(prevFiles => [...prevFiles, transformedFile]);
|
||||||
|
setIsModalOpen(false);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error uploading file:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGroupSubmit = (groupData) => {
|
||||||
|
if (groupToEdit) {
|
||||||
|
editRegistrationFileGroup(groupToEdit.id, groupData, csrfToken)
|
||||||
|
.then(updatedGroup => {
|
||||||
|
setGroups(groups.map(group => group.id === groupToEdit.id ? updatedGroup : group));
|
||||||
|
setGroupToEdit(null);
|
||||||
|
setIsGroupModalOpen(false);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error handling group:', error);
|
||||||
|
alert('Erreur lors de l\'opération sur le groupe');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
createRegistrationFileGroup(groupData, csrfToken)
|
||||||
|
.then(newGroup => {
|
||||||
|
setGroups([...groups, newGroup]);
|
||||||
|
setIsGroupModalOpen(false);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error handling group:', error);
|
||||||
|
alert('Erreur lors de l\'opération sur le groupe');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGroupEdit = (group) => {
|
||||||
|
setGroupToEdit(group);
|
||||||
|
setIsGroupModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGroupDelete = (groupId) => {
|
||||||
|
// Vérifier si des fichiers utilisent ce groupe
|
||||||
|
const filesInGroup = fichiers.filter(file => file.group && file.group.id === groupId);
|
||||||
|
if (filesInGroup.length > 0) {
|
||||||
|
alert('Impossible de supprimer ce groupe car il contient des fichiers. Veuillez d\'abord retirer tous les fichiers de ce groupe.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.confirm('Êtes-vous sûr de vouloir supprimer ce groupe ?')) {
|
||||||
|
deleteRegistrationFileGroup(groupId, csrfToken)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status === 409) {
|
||||||
|
throw new Error('Ce groupe est lié à des inscriptions existantes.');
|
||||||
|
}
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Erreur lors de la suppression du groupe.');
|
||||||
|
}
|
||||||
|
setGroups(groups.filter(group => group.id !== groupId));
|
||||||
|
alert('Groupe supprimé avec succès.');
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error deleting group:', error);
|
||||||
|
alert(error.message || 'Erreur lors de la suppression du groupe. Vérifiez qu\'aucune inscription n\'utilise ce groupe.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ajouter cette fonction de filtrage
|
||||||
|
const filteredFiles = fichiers.filter(file => {
|
||||||
|
if (!selectedGroup) return true;
|
||||||
|
return file.group && file.group.id === parseInt(selectedGroup);
|
||||||
|
});
|
||||||
|
|
||||||
|
const columnsFiles = [
|
||||||
|
{ name: 'Nom du fichier', transform: (row) => row.name },
|
||||||
|
{ name: 'Groupe', transform: (row) => row.group ? row.group.name : 'Aucun' },
|
||||||
|
{ name: 'Date de création', transform: (row) => formatDate(new Date (row.date_added),"DD/MM/YYYY hh:mm:ss") },
|
||||||
|
{ name: 'Fichier Obligatoire', transform: (row) => row.is_required ? 'Oui' : 'Non' },
|
||||||
|
{ name: 'Ordre de fusion', transform: (row) => row.order },
|
||||||
|
{ name: 'Actions', transform: (row) => (
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
{row.file && (
|
||||||
|
<a href={`${BASE_URL}${row.file}`} target='_blank' className="text-blue-500 hover:text-blue-700">
|
||||||
|
<Download size={16} />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
<button onClick={() => handleFileEdit(row)} className="text-blue-500 hover:text-blue-700">
|
||||||
|
<Edit size={16} />
|
||||||
|
</button>
|
||||||
|
<button onClick={() => handleFileDelete(row.id)} className="text-red-500 hover:text-red-700">
|
||||||
|
<Trash2 size={16} />
|
||||||
|
</button>
|
||||||
|
<button onClick={() => handleSignatureRequest(row)} className="text-green-500 hover:text-green-700">
|
||||||
|
<Signature size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
];
|
||||||
|
|
||||||
|
const columnsGroups = [
|
||||||
|
{ name: 'Nom du groupe', transform: (row) => row.name },
|
||||||
|
{ name: 'Description', transform: (row) => row.description },
|
||||||
|
{ name: 'Actions', transform: (row) => (
|
||||||
|
<div className="flex items-center justify-center gap-2">
|
||||||
|
<button onClick={() => handleGroupEdit(row)} className="text-blue-500 hover:text-blue-700">
|
||||||
|
<Edit size={16} />
|
||||||
|
</button>
|
||||||
|
<button onClick={() => handleGroupDelete(row.id)} className="text-red-500 hover:text-red-700">
|
||||||
|
<Trash2 size={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Fonction pour gérer la demande de signature
|
||||||
|
const handleSignatureRequest = (file) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
console.log('Demande de signature pour le fichier :', file);
|
||||||
|
|
||||||
|
fetch('http://localhost:8080:/DocuSeal/generateToken', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer NFPZy6BBGvYs1BwTuXMQ3XAu5N1kLFiXWftGQhkiz2A',
|
||||||
|
},
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Erreur lors du téléversement du document : ' + response.statusText);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
const documentId = data.documentId;
|
||||||
|
console.log('Document téléversé avec succès, ID :', documentId);
|
||||||
|
onUpload(documentId);
|
||||||
|
});
|
||||||
|
.catch((error) => console.error(error));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Modal
|
||||||
|
isOpen={isModalOpen}
|
||||||
|
setIsOpen={setIsModalOpen}
|
||||||
|
title={isEditing ? 'Modifier un fichier' : 'Ajouter un fichier'}
|
||||||
|
ContentComponent={() => (
|
||||||
|
<FileUpload
|
||||||
|
onFileUpload={handleFileUpload}
|
||||||
|
fileToEdit={fileToEdit}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Modal
|
||||||
|
isOpen={isGroupModalOpen}
|
||||||
|
setIsOpen={setIsGroupModalOpen}
|
||||||
|
title={groupToEdit ? "Modifier le groupe" : "Ajouter un groupe de fichiers"}
|
||||||
|
ContentComponent={() => (
|
||||||
|
<RegistrationFileGroupForm
|
||||||
|
onSubmit={handleGroupSubmit}
|
||||||
|
initialData={groupToEdit}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="mt-8 mb-4">
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h2 className="text-xl font-bold">Groupes de fichiers</h2>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsGroupModalOpen(true)}
|
||||||
|
className="flex items-center bg-blue-600 text-white p-2 rounded-full shadow hover:bg-blue-900 transition duration-200"
|
||||||
|
>
|
||||||
|
<FolderPlus className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<Table
|
||||||
|
data={groups}
|
||||||
|
columns={columnsGroups}
|
||||||
|
itemsPerPage={5}
|
||||||
|
currentPage={1}
|
||||||
|
totalPages={Math.ceil(groups.length / 5)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{groups.length > 0 && (
|
||||||
|
<div className="mt-8">
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h2 className="text-xl font-bold">Fichiers</h2>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<select
|
||||||
|
className="border rounded p-2"
|
||||||
|
value={selectedGroup || ''}
|
||||||
|
onChange={(e) => setSelectedGroup(e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="">Tous les groupes</option>
|
||||||
|
{groups.map(group => (
|
||||||
|
<option key={group.id} value={group.id}>{group.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<button
|
||||||
|
onClick={() => { setIsModalOpen(true); setIsEditing(false); }}
|
||||||
|
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-900 transition duration-200"
|
||||||
|
>
|
||||||
|
<Plus className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Table
|
||||||
|
data={filteredFiles}
|
||||||
|
columns={columnsFiles}
|
||||||
|
itemsPerPage={10}
|
||||||
|
currentPage={1}
|
||||||
|
totalPages={Math.ceil(filteredFiles.length / 10)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
45
Front-End/src/context/EstablishmentContext.js
Normal file
45
Front-End/src/context/EstablishmentContext.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
const EstablishmentContext = createContext();
|
||||||
|
|
||||||
|
export const EstablishmentProvider = ({ children }) => {
|
||||||
|
const [selectedEstablishmentId, setSelectedEstablishmentId] = useState(() => {
|
||||||
|
// Récupérer l'ID de l'établissement depuis le localStorage
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
return localStorage.getItem('selectedEstablishmentId') || null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const [profileRole, setProfileRole] = useState(() => {
|
||||||
|
// Récupérer le rôle du profil depuis le localStorage
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
return localStorage.getItem('profileRole') || null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Sauvegarder l'ID de l'établissement dans le localStorage
|
||||||
|
if (selectedEstablishmentId) {
|
||||||
|
localStorage.setItem('selectedEstablishmentId', selectedEstablishmentId);
|
||||||
|
}
|
||||||
|
}, [selectedEstablishmentId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Sauvegarder le rôle du profil dans le localStorage
|
||||||
|
if (profileRole) {
|
||||||
|
localStorage.setItem('profileRole', profileRole);
|
||||||
|
}
|
||||||
|
}, [profileRole]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EstablishmentContext.Provider value={{ selectedEstablishmentId, setSelectedEstablishmentId, profileRole, setProfileRole }}>
|
||||||
|
{children}
|
||||||
|
</EstablishmentContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useEstablishment = () => {
|
||||||
|
return useContext(EstablishmentContext);
|
||||||
|
};
|
||||||
@ -11,24 +11,23 @@ const options = {
|
|||||||
name: 'Credentials',
|
name: 'Credentials',
|
||||||
credentials: {
|
credentials: {
|
||||||
email: { label: 'Email', type: 'email' },
|
email: { label: 'Email', type: 'email' },
|
||||||
password: { label: 'Password', type: 'password' }
|
password: { label: 'Password', type: 'password' },
|
||||||
|
role_type: { label: 'Role Type', type: 'text' }
|
||||||
},
|
},
|
||||||
authorize: async (credentials, req) => {
|
authorize: async (credentials, req) => {
|
||||||
try {
|
try {
|
||||||
const data = {
|
const data = {
|
||||||
email: credentials.email,
|
email: credentials.email,
|
||||||
password: credentials.password
|
password: credentials.password,
|
||||||
|
role_type: credentials.role_type
|
||||||
};
|
};
|
||||||
|
|
||||||
const user = await getJWT(data);
|
const user = await getJWT(data);
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
logger.debug("API response:", user);
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
logger.error('Invalid credentials')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Invalid credentials')
|
|
||||||
throw new Error(error.message || 'Invalid credentials');
|
throw new Error(error.message || 'Invalid credentials');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,16 +87,15 @@ const options = {
|
|||||||
},
|
},
|
||||||
async session({ session, token }) {
|
async session({ session, token }) {
|
||||||
if (token && token?.token) {
|
if (token && token?.token) {
|
||||||
const {user_id, droit, email, establishment} = jwt_decode.decode(token.token);
|
const { user_id, email, roles } = jwt_decode.decode(token.token);
|
||||||
session.user = {
|
session.user = {
|
||||||
...session.user,
|
...session.user,
|
||||||
token: token.token,
|
token: token.token,
|
||||||
refresh: token.refresh
|
refresh: token.refresh,
|
||||||
|
user_id: user_id,
|
||||||
|
email: email,
|
||||||
|
roles: roles
|
||||||
};
|
};
|
||||||
session.user.user_id = user_id;
|
|
||||||
session.user.droit = droit;
|
|
||||||
session.user.email = email;
|
|
||||||
session.user.establishment = establishment;
|
|
||||||
}
|
}
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,9 @@ export const BE_SCHOOL_FEES_URL = `${BASE_URL}/School/fees`;
|
|||||||
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_PLANS_URL = `${BASE_URL}/School/paymentPlans`;
|
export const BE_SCHOOL_PAYMENT_PLANS_URL = `${BASE_URL}/School/paymentPlans`;
|
||||||
export const BE_SCHOOL_PAYMENT_MODES_URL = `${BASE_URL}/School/paymentModes`;
|
export const BE_SCHOOL_PAYMENT_MODES_URL = `${BASE_URL}/School/paymentModes`;
|
||||||
export const BE_SCHOOL_ESTABLISHMENT_URL = `${BASE_URL}/School/establishments`;
|
|
||||||
|
// ESTABLISHMENT
|
||||||
|
export const BE_SCHOOL_ESTABLISHMENT_URL = `${BASE_URL}/Establishment/establishments`;
|
||||||
|
|
||||||
|
|
||||||
// GESTION PLANNING
|
// GESTION PLANNING
|
||||||
|
|||||||
Reference in New Issue
Block a user