feat: Traitement de clonages des templates de documents dans le back

uniquement [#N3WTS-17]
This commit is contained in:
N3WT DE COMPET
2025-11-29 16:43:51 +01:00
parent 1e5bc6ccba
commit 7486f6c5ce
10 changed files with 598 additions and 172 deletions

View File

@ -1 +0,0 @@
# This file is intentionally left blank to make this directory a Python package.

View File

@ -277,6 +277,16 @@ class RegistrationForm(models.Model):
return "RF_" + self.student.last_name + "_" + self.student.first_name
def save(self, *args, **kwargs):
# Préparer le flag de création / changement de fileGroup
was_new = self.pk is None
old_fileGroup = None
if not was_new:
try:
old_instance = RegistrationForm.objects.get(pk=self.pk)
old_fileGroup = old_instance.fileGroup
except RegistrationForm.DoesNotExist:
old_fileGroup = None
# Vérifier si un fichier existant doit être remplacé
if self.pk: # Si l'objet existe déjà dans la base de données
try:
@ -290,6 +300,17 @@ class RegistrationForm(models.Model):
# Appeler la méthode save originale
super().save(*args, **kwargs)
# Après save : si nouveau ou changement de fileGroup -> créer les templates
fileGroup_changed = (self.fileGroup is not None) and (old_fileGroup is None or (old_fileGroup and old_fileGroup.id != self.fileGroup.id))
if was_new or fileGroup_changed:
try:
import Subscriptions.util as util
created = util.create_templates_for_registration_form(self)
if created:
logger.info("Created %d templates for RegistrationForm %s", len(created), self.pk)
except Exception as e:
logger.exception("Error creating templates for RegistrationForm %s: %s", self.pk, e)
#############################################################
####################### MASTER FILES ########################
#############################################################
@ -297,7 +318,6 @@ class RegistrationForm(models.Model):
####### Formulaires masters (documents école, à signer ou pas) #######
class RegistrationSchoolFileMaster(models.Model):
groups = models.ManyToManyField(RegistrationFileGroup, related_name='school_file_masters', blank=True)
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255, default="")
is_required = models.BooleanField(default=False)
formMasterData = models.JSONField(default=list, blank=True, null=True)
@ -325,7 +345,6 @@ def registration_parent_file_upload_to(instance, filename):
####### Formulaires templates (par dossier d'inscription) #######
class RegistrationSchoolFileTemplate(models.Model):
master = models.ForeignKey(RegistrationSchoolFileMaster, on_delete=models.CASCADE, related_name='school_file_templates', blank=True)
id = models.IntegerField(primary_key=True)
slug = models.CharField(max_length=255, default="")
name = models.CharField(max_length=255, default="")
registration_form = models.ForeignKey(RegistrationForm, on_delete=models.CASCADE, related_name='school_file_templates', blank=True)

View File

@ -24,7 +24,12 @@ from .views import (
)
from .views import RegistrationFileGroupView, RegistrationFileGroupSimpleView, get_registration_files_by_group
from .views import registration_file_views, get_school_file_templates_by_rf, get_parent_file_templates_by_rf
from .views import (
registration_school_file_masters_views,
registration_school_file_templates_views,
get_school_file_templates_by_rf,
get_parent_file_templates_by_rf
)
urlpatterns = [
re_path(r'^registerForms/(?P<id>[0-9]+)/archive$', archive, name="archive"),

View File

@ -21,8 +21,161 @@ from PyPDF2 import PdfMerger
import shutil
import logging
import json
from django.http import QueryDict
from rest_framework.response import Response
from rest_framework import status
logger = logging.getLogger(__name__)
def build_payload_from_request(request):
"""
Normalise la request en payload prêt à être donné au serializer.
- supporte multipart/form-data où le front envoie 'data' (JSON string) ou un fichier JSON + fichiers
- supporte application/json ou form-data simple
Retour: (payload_dict, None) ou (None, Response erreur)
"""
data_field = request.data.get('data') if hasattr(request.data, 'get') else None
if data_field:
try:
# Si 'data' est un fichier (InMemoryUploadedFile ou fichier similaire), lire et décoder
if hasattr(data_field, 'read'):
raw = data_field.read()
if isinstance(raw, (bytes, bytearray)):
text = raw.decode('utf-8')
else:
text = raw
payload = json.loads(text)
# Si 'data' est bytes déjà
elif isinstance(data_field, (bytes, bytearray)):
payload = json.loads(data_field.decode('utf-8'))
# Si 'data' est une string JSON
elif isinstance(data_field, str):
payload = json.loads(data_field)
else:
# type inattendu
raise ValueError(f"Unsupported 'data' type: {type(data_field)}")
except (json.JSONDecodeError, ValueError, UnicodeDecodeError) as e:
logger.error(f'Invalid JSON in "data": {e}')
return None, Response({'error': "Invalid JSON in 'data'", 'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
else:
payload = request.data.copy() if hasattr(request.data, 'copy') else dict(request.data)
if isinstance(payload, QueryDict):
payload = payload.dict()
# Attacher les fichiers présents (ex: photo, files.*, etc.), sauf 'data' (déjà traité)
for f_key, f_val in request.FILES.items():
if f_key == 'data':
# remettre le pointeur au début si besoin (déjà lu) — non indispensable ici mais sûr
try:
f_val.seek(0)
except Exception:
pass
# ne pas mettre le fichier 'data' dans le payload (c'est le JSON)
continue
payload[f_key] = f_val
return payload, None
def create_templates_for_registration_form(register_form):
"""
Idempotent:
- supprime les templates existants qui ne correspondent pas
aux masters du fileGroup courant du register_form (et supprime leurs fichiers).
- crée les templates manquants pour les masters du fileGroup courant.
Retourne la liste des templates créés.
"""
from Subscriptions.models import (
RegistrationSchoolFileMaster,
RegistrationSchoolFileTemplate,
# RegistrationParentFileMaster,
# RegistrationParentFileTemplate,
)
created = []
# Récupérer les masters du fileGroup courant
current_group = getattr(register_form, "fileGroup", None)
if not current_group:
# Si plus de fileGroup, supprimer tous les templates existants pour ce RF
school_existing = RegistrationSchoolFileTemplate.objects.filter(registration_form=register_form)
for t in school_existing:
try:
if getattr(t, "file", None):
t.file.delete(save=False)
except Exception:
logger.exception("Erreur suppression fichier school template %s", getattr(t, "pk", None))
t.delete()
# parent_existing = RegistrationParentFileTemplate.objects.filter(registration_form=register_form)
# for t in parent_existing:
# try:
# if getattr(t, "file", None):
# t.file.delete(save=False)
# except Exception:
# logger.exception("Erreur suppression fichier parent template %s", getattr(t, "pk", None))
# t.delete()
return created
school_masters = RegistrationSchoolFileMaster.objects.filter(groups=current_group).distinct()
# parent_masters = RegistrationParentFileMaster.objects.filter(groups=current_group).distinct()
school_master_ids = {m.pk for m in school_masters}
#parent_master_ids = {m.pk for m in parent_masters}
# Supprimer les school templates obsolètes
for tmpl in RegistrationSchoolFileTemplate.objects.filter(registration_form=register_form):
if not tmpl.master_id or tmpl.master_id not in school_master_ids:
try:
if getattr(tmpl, "file", None):
tmpl.file.delete(save=False)
except Exception:
logger.exception("Erreur suppression fichier school template obsolète %s", getattr(tmpl, "pk", None))
tmpl.delete()
logger.info("Deleted obsolete school template %s for RF %s", getattr(tmpl, "pk", None), register_form.pk)
# Supprimer les parent templates obsolètes
# for tmpl in RegistrationParentFileTemplate.objects.filter(registration_form=register_form):
# if not tmpl.master_id or tmpl.master_id not in parent_master_ids:
# try:
# if getattr(tmpl, "file", None):
# tmpl.file.delete(save=False)
# except Exception:
# logger.exception("Erreur suppression fichier parent template obsolète %s", getattr(tmpl, "pk", None))
# tmpl.delete()
# logger.info("Deleted obsolete parent template %s for RF %s", getattr(tmpl, "pk", None), register_form.pk)
# Créer les school templates manquants
for m in school_masters:
exists = RegistrationSchoolFileTemplate.objects.filter(master=m, registration_form=register_form).exists()
if exists:
continue
base_slug = (m.name or "master").strip().replace(" ", "_")[:40]
slug = f"{base_slug}_{register_form.pk}_{m.pk}"
tmpl = RegistrationSchoolFileTemplate.objects.create(
master=m,
registration_form=register_form,
name=m.name or "",
formTemplateData=m.formMasterData or [],
slug=slug,
)
created.append(tmpl)
logger.info("Created school template %s from master %s for RF %s", tmpl.pk, m.pk, register_form.pk)
# Créer les parent templates manquants
# for m in parent_masters:
# exists = RegistrationParentFileTemplate.objects.filter(master=m, registration_form=register_form).exists()
# if exists:
# continue
# tmpl = RegistrationParentFileTemplate.objects.create(
# master=m,
# registration_form=register_form,
# file=None,
# )
# created.append(tmpl)
# logger.info("Created parent template %s from master %s for RF %s", tmpl.pk, m.pk, register_form.pk)
return created
def recupereListeFichesInscription():
"""
Retourne la liste complète des fiches dinscription.

View File

@ -1,14 +1,24 @@
from .register_form_views import RegisterFormView, RegisterFormWithIdView, send, resend, archive, get_school_file_templates_by_rf, get_parent_file_templates_by_rf
from .registration_file_views import (
from .register_form_views import (
RegisterFormView,
RegisterFormWithIdView,
send,
resend,
archive,
get_school_file_templates_by_rf,
get_parent_file_templates_by_rf
)
from .registration_school_file_masters_views import (
RegistrationSchoolFileMasterView,
RegistrationSchoolFileMasterSimpleView,
RegistrationSchoolFileTemplateView,
RegistrationSchoolFileTemplateSimpleView,
RegistrationSchoolFileMasterSimpleView,
RegistrationParentFileMasterView,
RegistrationParentFileMasterSimpleView,
RegistrationParentFileTemplateSimpleView,
RegistrationParentFileTemplateView
)
from .registration_school_file_templates_views import (
RegistrationSchoolFileTemplateView,
RegistrationSchoolFileTemplateSimpleView,
)
from .registration_file_group_views import RegistrationFileGroupView, RegistrationFileGroupSimpleView, get_registration_files_by_group
from .student_views import StudentView, StudentListView, ChildrenListView, search_students
from .guardian_views import GuardianView, DissociateGuardianView
@ -33,7 +43,7 @@ __all__ = [
'RegistrationFileGroupSimpleView',
'get_registration_files_by_group',
'get_school_file_templates_by_rf',
'get_parent_file_templates_by_rf'
'get_parent_file_templates_by_rf',
'StudentView',
'StudentListView',
'ChildrenListView',

View File

@ -0,0 +1,363 @@
from django.http.response import JsonResponse
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
import json
from django.http import QueryDict
from Subscriptions.serializers import RegistrationSchoolFileMasterSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileMasterSerializer, RegistrationParentFileTemplateSerializer
from Subscriptions.models import (
RegistrationForm,
RegistrationSchoolFileMaster,
RegistrationSchoolFileTemplate,
RegistrationParentFileMaster,
RegistrationParentFileTemplate
)
from N3wtSchool import bdd
import logging
import Subscriptions.util as util
logger = logging.getLogger(__name__)
class RegistrationSchoolFileMasterView(APIView):
parser_classes = [MultiPartParser, FormParser]
@swagger_auto_schema(
operation_description="Récupère tous les masters de templates d'inscription pour un établissement donné",
manual_parameters=[
openapi.Parameter(
'establishment_id',
openapi.IN_QUERY,
description="ID de l'établissement",
type=openapi.TYPE_INTEGER,
required=True
)
],
responses={200: RegistrationSchoolFileMasterSerializer(many=True)}
)
def get(self, request):
establishment_id = request.GET.get('establishment_id')
if not establishment_id:
return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST)
# Filtrer les masters liés à l'établissement via groups.establishment
masters = RegistrationSchoolFileMaster.objects.filter(
groups__establishment__id=establishment_id
).distinct()
serializer = RegistrationSchoolFileMasterSerializer(masters, many=True)
return Response(serializer.data)
@swagger_auto_schema(
operation_description="Crée un nouveau master de template d'inscription",
request_body=RegistrationSchoolFileMasterSerializer,
responses={
201: RegistrationSchoolFileMasterSerializer,
400: "Données invalides"
}
)
def post(self, request):
logger.info(f"raw request.data: {request.data}")
payload, resp = util.build_payload_from_request(request)
if resp:
return resp
logger.info(f"payload for serializer: {payload}")
serializer = RegistrationSchoolFileMasterSerializer(data=payload, partial=True)
if serializer.is_valid():
obj = serializer.save()
# Propager la création des templates côté serveur pour les RegistrationForm
try:
groups_qs = obj.groups.all()
if groups_qs.exists():
# Tous les RegistrationForm dont fileGroup est dans les groups du master
rfs = RegistrationForm.objects.filter(fileGroup__in=groups_qs).distinct()
for rf in rfs:
try:
util.create_templates_for_registration_form(rf)
except Exception as e:
logger.exception("Error creating templates for RF %s from master %s: %s", getattr(rf, 'pk', None), getattr(obj, 'pk', None), e)
except Exception:
logger.exception("Error while propagating templates after master creation %s", getattr(obj, 'pk', None))
return Response(RegistrationSchoolFileMasterSerializer(obj).data, status=status.HTTP_201_CREATED)
logger.error(f"serializer errors: {serializer.errors}")
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class RegistrationSchoolFileMasterSimpleView(APIView):
@swagger_auto_schema(
operation_description="Récupère un master de template d'inscription spécifique",
responses={
200: RegistrationSchoolFileMasterSerializer,
404: "Master non trouvé"
}
)
def get(self, request, id):
master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id)
if master is None:
return JsonResponse({"errorMessage":'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
serializer = RegistrationSchoolFileMasterSerializer(master)
return JsonResponse(serializer.data, safe=False)
@swagger_auto_schema(
operation_description="Met à jour un master de template d'inscription existant",
request_body=RegistrationSchoolFileMasterSerializer,
responses={
200: RegistrationSchoolFileMasterSerializer,
400: "Données invalides",
404: "Master non trouvé"
}
)
def put(self, request, id):
master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id)
if master is None:
return JsonResponse({'erreur': "Le master de template n'a pas été trouvé"}, safe=False, status=status.HTTP_404_NOT_FOUND)
# snapshot des groups avant update
old_group_ids = set(master.groups.values_list('id', flat=True))
# Normaliser payload (supporte form-data avec champ 'data' JSON ou fichier JSON)
payload, resp = util.build_payload_from_request(request)
if resp:
return resp
logger.info(f"payload for update serializer: {payload}")
serializer = RegistrationSchoolFileMasterSerializer(master, data=payload, partial=True)
if serializer.is_valid():
obj = serializer.save()
# groups après update
new_group_ids = set(obj.groups.values_list('id', flat=True))
removed_group_ids = old_group_ids - new_group_ids
added_group_ids = new_group_ids - old_group_ids
# Pour chaque RF appartenant aux groupes retirés -> nettoyer les templates (idempotent)
if removed_group_ids:
try:
rfs_removed = RegistrationForm.objects.filter(fileGroup__in=list(removed_group_ids)).distinct()
for rf in rfs_removed:
try:
util.create_templates_for_registration_form(rf) # supprimera les templates obsolètes
except Exception as e:
logger.exception("Error cleaning templates for RF %s after master %s group removal: %s", getattr(rf, 'pk', None), getattr(obj, 'pk', None), e)
except Exception:
logger.exception("Error while processing RFs for removed groups after master update %s", getattr(obj, 'pk', None))
# Pour chaque RF appartenant aux groupes ajoutés -> créer les templates manquants
if added_group_ids:
try:
rfs_added = RegistrationForm.objects.filter(fileGroup__in=list(added_group_ids)).distinct()
for rf in rfs_added:
try:
util.create_templates_for_registration_form(rf) # créera les templates manquants
except Exception as e:
logger.exception("Error creating templates for RF %s after master %s group addition: %s", getattr(rf, 'pk', None), getattr(obj, 'pk', None), e)
except Exception:
logger.exception("Error while processing RFs for added groups after master update %s", getattr(obj, 'pk', None))
return Response(serializer.data, status=status.HTTP_200_OK)
logger.error(f"serializer errors on put: {serializer.errors}")
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@swagger_auto_schema(
operation_description="Supprime un master de template d'inscription",
responses={
204: "Suppression réussie",
404: "Master non trouvé"
}
)
def delete(self, request, id):
master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id)
if master is not None:
master.delete()
return JsonResponse({'message': 'La suppression du master de template a été effectuée avec succès'}, safe=False, status=status.HTTP_200_OK)
else:
return JsonResponse({'erreur': 'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
class RegistrationParentFileMasterView(APIView):
@swagger_auto_schema(
operation_description="Récupère tous les fichiers parents pour un établissement donné",
manual_parameters=[
openapi.Parameter(
'establishment_id',
openapi.IN_QUERY,
description="ID de l'établissement",
type=openapi.TYPE_INTEGER,
required=True
)
],
responses={200: RegistrationParentFileMasterSerializer(many=True)}
)
def get(self, request):
establishment_id = request.GET.get('establishment_id')
if not establishment_id:
return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST)
# Filtrer les fichiers parents liés à l'établissement
templates = RegistrationParentFileMaster.objects.filter(
groups__establishment__id=establishment_id
).distinct()
serializer = RegistrationParentFileMasterSerializer(templates, many=True)
return Response(serializer.data)
@swagger_auto_schema(
operation_description="Crée un nouveau fichier parent",
request_body=RegistrationParentFileMasterSerializer,
responses={
201: RegistrationParentFileMasterSerializer,
400: "Données invalides"
}
)
def post(self, request):
serializer = RegistrationParentFileMasterSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class RegistrationParentFileMasterSimpleView(APIView):
@swagger_auto_schema(
operation_description="Récupère un fichier parent spécifique",
responses={
200: RegistrationParentFileMasterSerializer,
404: "Fichier parent non trouvé"
}
)
def get(self, request, id):
template = bdd.getObject(_objectName=RegistrationParentFileMaster, _columnName='id', _value=id)
if template is None:
return JsonResponse({"errorMessage":'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
serializer = RegistrationParentFileMasterSerializer(template)
return JsonResponse(serializer.data, safe=False)
@swagger_auto_schema(
operation_description="Met à jour un fichier parent existant",
request_body=RegistrationParentFileMasterSerializer,
responses={
200: RegistrationParentFileMasterSerializer,
400: "Données invalides",
404: "Fichier parent non trouvé"
}
)
def put(self, request, id):
template = bdd.getObject(_objectName=RegistrationParentFileMaster, _columnName='id', _value=id)
if template is None:
return JsonResponse({'erreur': 'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
serializer = RegistrationParentFileMasterSerializer(template, data=request.data)
if serializer.is_valid():
serializer.save()
return Response({'message': 'Fichier parent mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@swagger_auto_schema(
operation_description="Supprime un fichier parent",
responses={
204: "Suppression réussie",
404: "Fichier parent non trouvé"
}
)
def delete(self, request, id):
template = bdd.getObject(_objectName=RegistrationParentFileMaster, _columnName='id', _value=id)
if template is not None:
template.delete()
return JsonResponse({'message': 'La suppression du fichier parent a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
else:
return JsonResponse({'erreur': 'Le fichier parent n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
class RegistrationParentFileTemplateView(APIView):
@swagger_auto_schema(
operation_description="Récupère tous les templates parents pour un établissement donné",
manual_parameters=[
openapi.Parameter(
'establishment_id',
openapi.IN_QUERY,
description="ID de l'établissement",
type=openapi.TYPE_INTEGER,
required=True
)
],
responses={200: RegistrationParentFileTemplateSerializer(many=True)}
)
def get(self, request):
establishment_id = request.GET.get('establishment_id')
if not establishment_id:
return Response({'error': "Paramètre 'establishment_id' requis"}, status=status.HTTP_400_BAD_REQUEST)
# Filtrer les templates parents liés à l'établissement via master.groups.establishment
templates = RegistrationParentFileTemplate.objects.filter(
master__groups__establishment__id=establishment_id
).distinct()
serializer = RegistrationParentFileTemplateSerializer(templates, many=True)
return Response(serializer.data)
@swagger_auto_schema(
operation_description="Crée un nouveau template d'inscription",
request_body=RegistrationParentFileTemplateSerializer,
responses={
201: RegistrationParentFileTemplateSerializer,
400: "Données invalides"
}
)
def post(self, request):
serializer = RegistrationParentFileTemplateSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class RegistrationParentFileTemplateSimpleView(APIView):
@swagger_auto_schema(
operation_description="Récupère un template d'inscription spécifique",
responses={
200: RegistrationParentFileTemplateSerializer,
404: "Template non trouvé"
}
)
def get(self, request, id):
template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id)
if template is None:
return JsonResponse({"errorMessage":'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
serializer = RegistrationParentFileTemplateSerializer(template)
return JsonResponse(serializer.data, safe=False)
@swagger_auto_schema(
operation_description="Met à jour un template d'inscription existant",
request_body=RegistrationParentFileTemplateSerializer,
responses={
200: RegistrationParentFileTemplateSerializer,
400: "Données invalides",
404: "Template non trouvé"
}
)
def put(self, request, id):
template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id)
if template is None:
return JsonResponse({'erreur': 'Le template d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
serializer = RegistrationParentFileTemplateSerializer(template, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response({'message': 'Template mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@swagger_auto_schema(
operation_description="Supprime un template d'inscription",
responses={
204: "Suppression réussie",
404: "Template non trouvé"
}
)
def delete(self, request, id):
template = bdd.getObject(_objectName=RegistrationParentFileTemplate, _columnName='id', _value=id)
if template is not None:
template.delete()
return JsonResponse({'message': 'La suppression du template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
else:
return JsonResponse({'erreur': 'Le template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)

View File

@ -5,12 +5,19 @@ from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
import json
from django.http import QueryDict
from Subscriptions.serializers import RegistrationSchoolFileMasterSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileMasterSerializer, RegistrationParentFileTemplateSerializer
from Subscriptions.models import RegistrationSchoolFileMaster, RegistrationSchoolFileTemplate, RegistrationParentFileMaster, RegistrationParentFileTemplate
from N3wtSchool import bdd
import logging
import Subscriptions.util as util
logger = logging.getLogger(__name__)
class RegistrationSchoolFileMasterView(APIView):
parser_classes = [MultiPartParser, FormParser]
@swagger_auto_schema(
operation_description="Récupère tous les masters de templates d'inscription pour un établissement donné",
manual_parameters=[
@ -45,10 +52,19 @@ class RegistrationSchoolFileMasterView(APIView):
}
)
def post(self, request):
serializer = RegistrationSchoolFileMasterSerializer(data=request.data)
logger.info(f"raw request.data: {request.data}")
payload, resp = util.build_payload_from_request(request)
if resp:
return resp
logger.info(f"payload for serializer: {payload}")
serializer = RegistrationSchoolFileMasterSerializer(data=payload, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
obj = serializer.save()
return Response(RegistrationSchoolFileMasterSerializer(obj).data, status=status.HTTP_201_CREATED)
logger.error(f"serializer errors: {serializer.errors}")
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class RegistrationSchoolFileMasterSimpleView(APIView):
@ -78,11 +94,19 @@ class RegistrationSchoolFileMasterSimpleView(APIView):
def put(self, request, id):
master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id)
if master is None:
return JsonResponse({'erreur': 'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
serializer = RegistrationSchoolFileMasterSerializer(master, data=request.data)
return JsonResponse({'erreur': "Le master de template n'a pas été trouvé"}, safe=False, status=status.HTTP_404_NOT_FOUND)
# Normaliser payload (supporte form-data avec champ 'data' JSON ou fichier JSON)
payload, resp = util.build_payload_from_request(request)
if resp:
return resp
logger.info(f"payload for update serializer: {payload}")
serializer = RegistrationSchoolFileMasterSerializer(master, data=payload, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
logger.error(f"serializer errors on put: {serializer.errors}")
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@swagger_auto_schema(
@ -96,7 +120,7 @@ class RegistrationSchoolFileMasterSimpleView(APIView):
master = bdd.getObject(_objectName=RegistrationSchoolFileMaster, _columnName='id', _value=id)
if master is not None:
master.delete()
return JsonResponse({'message': 'La suppression du master de template a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
return JsonResponse({'message': 'La suppression du master de template a été effectuée avec succès'}, safe=False, status=status.HTTP_200_OK)
else:
return JsonResponse({'erreur': 'Le master de template n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)

View File

@ -521,129 +521,23 @@ export default function CreateSubscriptionPage() {
} else {
// Création du dossier d'inscription
createRegisterForm(data, csrfToken)
.then((data) => {
// Clonage des schoolFileTemplates
const masters = schoolFileMasters.filter((file) =>
file.groups.includes(selectedFileGroup)
.then((response) => {
showNotification(
"Dossier d'inscription créé avec succès",
'success',
'Succès'
);
const parentMasters = parentFileMasters.filter((file) =>
file.groups.includes(selectedFileGroup)
);
createRegistrationSchoolFileTemplate(
cloneData,
csrfToken
)
.then((response) =>
logger.debug('Template enregistré avec succès:', response)
)
.catch((error) => {
setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du template:",
error
);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_03'
);
});
const clonePromises = masters.map((templateMaster) => {
const cloneData = {
name: `${templateMaster.name}_${formDataRef.current.studentFirstName}_${formDataRef.current.studentLastName}`,
slug: clonedDocument.slug,
id: clonedDocument.id,
master: templateMaster.id,
registration_form: data.student.id,
};
return createRegistrationSchoolFileTemplate(
cloneData,
csrfToken
)
.then((response) =>
logger.debug('Template enregistré avec succès:', response)
)
.catch((error) => {
setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du template:",
error
);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_03'
);
});
});
// Clonage des parentFileTemplates
const parentClonePromises = parentMasters.map((parentMaster) => {
const parentTemplateData = {
master: parentMaster.id,
registration_form: data.student.id,
};
return createRegistrationParentFileTemplate(
parentTemplateData,
csrfToken
)
.then((response) =>
logger.debug(
'Parent template enregistré avec succès:',
response
)
)
.catch((error) => {
setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du parent template:",
error
);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_02'
);
});
});
// Attendre que tous les clones soient créés
Promise.all([...clonePromises, ...parentClonePromises])
.then(() => {
// Redirection après succès
showNotification(
"Dossier d'inscription créé avec succès",
'success',
'Succès'
);
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
})
.catch((error) => {
setIsLoading(false);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_04'
);
logger.error('Error during cloning or sending:', error);
});
})
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
})
.catch((error) => {
setIsLoading(false);
logger.error('Erreur lors de la mise à jour du dossier:', error);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_01'
);
logger.error('Error during register form creation:', error);
});
}
};

View File

@ -19,13 +19,9 @@ export default function FileUploadDocuSeal({
const [templateMaster, setTemplateMaster] = useState(null);
const [uploadedFileName, setUploadedFileName] = useState('');
const [selectedGroups, setSelectedGroups] = useState([]);
const [guardianDetails, setGuardianDetails] = useState([]);
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState('');
const csrfToken = useCsrfToken();
const { selectedEstablishmentId, user } = useEstablishment();
useEffect(() => {
@ -47,18 +43,6 @@ export default function FileUploadDocuSeal({
const handleGroupChange = (selectedGroups) => {
setSelectedGroups(selectedGroups);
const details = selectedGroups.flatMap((group) =>
group.registration_forms.flatMap((form) =>
form.guardians.map((guardian) => ({
email: guardian.associated_profile_email,
last_name: form.last_name,
first_name: form.first_name,
registration_form: form.student_id,
}))
)
);
setGuardianDetails(details); // Mettre à jour la variable d'état avec les détails des guardians
};
const handleLoad = (detail) => {
@ -105,29 +89,6 @@ export default function FileUploadDocuSeal({
id: templateMaster?.id,
is_required: is_required,
});
guardianDetails.forEach((guardian, index) => {
logger.debug('creation du clone avec required : ', is_required);
const data = {
name: `${uploadedFileName}_${guardian.first_name}_${guardian.last_name}`,
slug: clonedDocument.slug,
id: clonedDocument.id,
master: templateMaster?.id,
registration_form: guardian.registration_form,
};
logger.debug('creation : ', data);
createRegistrationSchoolFileTemplate(data, csrfToken)
.then((response) => {
logger.debug('Template enregistré avec succès:', response);
onSuccess();
})
.catch((error) => {
logger.error(
"Erreur lors de l'enregistrement du template:",
error
);
});
});
}
};

View File

@ -20,9 +20,7 @@ import {
// DELETE
deleteRegistrationFileGroup,
deleteRegistrationSchoolFileMaster,
deleteRegistrationParentFileMaster,
removeTemplate
deleteRegistrationParentFileMaster
} from '@/app/actions/registerFileGroupAction';
import RegistrationFileGroupForm from '@/components/Structure/Files/RegistrationFileGroupForm';
import logger from '@/utils/logger';