mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
feat: Ajout de la configuration des tarifs de l'école [#18]
This commit is contained in:
committed by
Luc SORIGNET
parent
147a70135d
commit
5a0e65bb75
@ -7,6 +7,7 @@ FROM python:3.12.7
|
||||
# Allows docker to cache installed dependencies between builds
|
||||
COPY requirements.txt requirements.txt
|
||||
RUN pip install -r requirements.txt
|
||||
RUN pip install pymupdf
|
||||
|
||||
# Mounts the application code to the image
|
||||
COPY . .
|
||||
|
||||
@ -92,6 +92,7 @@ def searchObjects(_objectName, _searchTerm=None, _excludeStates=None):
|
||||
def delete_object(model_class, object_id, related_field=None):
|
||||
try:
|
||||
obj = model_class.objects.get(id=object_id)
|
||||
|
||||
if related_field and hasattr(obj, related_field):
|
||||
related_obj = getattr(obj, related_field)
|
||||
if related_obj:
|
||||
@ -103,5 +104,3 @@ def delete_object(model_class, object_id, related_field=None):
|
||||
return JsonResponse({'error': f'L\'objet {model_class.__name__} n\'existe pas avec cet ID'}, status=404, safe=False)
|
||||
except Exception as e:
|
||||
return JsonResponse({'error': f'Une erreur est survenue : {str(e)}'}, status=500, safe=False)
|
||||
|
||||
|
||||
|
||||
@ -3,6 +3,9 @@ from Auth.models import Profile
|
||||
from django.db.models import JSONField
|
||||
from django.dispatch import receiver
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
||||
LEVEL_CHOICES = [
|
||||
(1, 'Très Petite Section (TPS)'),
|
||||
@ -47,7 +50,7 @@ class SchoolClass(models.Model):
|
||||
number_of_students = models.PositiveIntegerField(blank=True)
|
||||
teaching_language = models.CharField(max_length=255, blank=True)
|
||||
school_year = models.CharField(max_length=9, blank=True)
|
||||
updated_date = models.DateTimeField(auto_now_add=True)
|
||||
updated_date = models.DateTimeField(auto_now=True)
|
||||
teachers = models.ManyToManyField(Teacher, blank=True)
|
||||
levels = ArrayField(models.IntegerField(choices=LEVEL_CHOICES), default=list)
|
||||
type = models.IntegerField(choices=PLANNING_TYPE_CHOICES, default=1)
|
||||
@ -64,3 +67,56 @@ class Planning(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f'Planning for {self.level} of {self.school_class.atmosphere_name}'
|
||||
|
||||
class Discount(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
amount = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
description = models.TextField(blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Fee(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
amount = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
description = models.TextField(blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class TuitionFee(models.Model):
|
||||
class PaymentOptions(models.IntegerChoices):
|
||||
SINGLE_PAYMENT = 0, _('Paiement en une seule fois')
|
||||
FOUR_TIME_PAYMENT = 1, _('Paiement en 4 fois')
|
||||
TEN_TIME_PAYMENT = 2, _('Paiement en 10 fois')
|
||||
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
description = models.TextField(blank=True)
|
||||
base_amount = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
currency = models.CharField(max_length=3, default='EUR')
|
||||
discounts = models.ManyToManyField('Discount', blank=True)
|
||||
validity_start_date = models.DateField()
|
||||
validity_end_date = models.DateField()
|
||||
payment_option = models.IntegerField(choices=PaymentOptions, default=PaymentOptions.SINGLE_PAYMENT)
|
||||
is_active = models.BooleanField(default=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def clean(self):
|
||||
if self.validity_end_date <= self.validity_start_date:
|
||||
raise ValidationError(_('La date de fin de validité doit être après la date de début de validité.'))
|
||||
|
||||
def calculate_final_amount(self):
|
||||
amount = self.base_amount
|
||||
|
||||
# Apply fees (supplements and taxes)
|
||||
# for fee in self.fees.all():
|
||||
# amount += fee.amount
|
||||
|
||||
# Apply discounts
|
||||
for discount in self.discounts.all():
|
||||
amount -= discount.amount
|
||||
|
||||
return amount
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
from rest_framework import serializers
|
||||
from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES
|
||||
from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES, Discount, TuitionFee, Fee
|
||||
from Subscriptions.models import RegistrationForm
|
||||
from Subscriptions.serializers import StudentSerializer
|
||||
from Auth.serializers import ProfileSerializer
|
||||
@ -172,4 +172,57 @@ class SchoolClassSerializer(serializers.ModelSerializer):
|
||||
utc_time = timezone.localtime(obj.updated_date)
|
||||
local_tz = pytz.timezone(settings.TZ_APPLI)
|
||||
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")
|
||||
|
||||
class DiscountSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Discount
|
||||
fields = '__all__'
|
||||
|
||||
class FeeSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Fee
|
||||
fields = '__all__'
|
||||
|
||||
class TuitionFeeSerializer(serializers.ModelSerializer):
|
||||
discounts = serializers.PrimaryKeyRelatedField(queryset=Discount.objects.all(), many=True)
|
||||
final_amount = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = TuitionFee
|
||||
fields = '__all__'
|
||||
|
||||
def get_final_amount(self, obj):
|
||||
return obj.calculate_final_amount()
|
||||
|
||||
def create(self, validated_data):
|
||||
discounts_data = validated_data.pop('discounts', [])
|
||||
|
||||
# Create the TuitionFee instance
|
||||
tuition_fee = TuitionFee.objects.create(**validated_data)
|
||||
|
||||
# Add discounts if provided
|
||||
for discount in discounts_data:
|
||||
tuition_fee.discounts.add(discount)
|
||||
|
||||
return tuition_fee
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
discounts_data = validated_data.pop('discounts', [])
|
||||
|
||||
# Update the TuitionFee instance
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.description = validated_data.get('description', instance.description)
|
||||
instance.base_amount = validated_data.get('base_amount', instance.base_amount)
|
||||
instance.currency = validated_data.get('currency', instance.currency)
|
||||
instance.validity_start_date = validated_data.get('validity_start_date', instance.validity_start_date)
|
||||
instance.validity_end_date = validated_data.get('validity_end_date', instance.validity_end_date)
|
||||
instance.payment_option = validated_data.get('payment_option', instance.payment_option)
|
||||
instance.is_active = validated_data.get('is_active', instance.is_active)
|
||||
instance.save()
|
||||
|
||||
# Update discounts if provided
|
||||
if discounts_data:
|
||||
instance.discounts.set(discounts_data)
|
||||
|
||||
return instance
|
||||
@ -1,6 +1,21 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from School.views import TeachersView, TeacherView, SpecialitiesView, SpecialityView, ClassesView, ClasseView, PlanningsView, PlanningView
|
||||
from School.views import (
|
||||
TeachersView,
|
||||
TeacherView,
|
||||
SpecialitiesView,
|
||||
SpecialityView,
|
||||
ClassesView,
|
||||
ClasseView,
|
||||
PlanningsView,
|
||||
PlanningView,
|
||||
FeesView,
|
||||
FeeView,
|
||||
TuitionFeesView,
|
||||
TuitionFeeView,
|
||||
DiscountsView,
|
||||
DiscountView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^specialities$', SpecialitiesView.as_view(), name="specialities"),
|
||||
@ -18,4 +33,16 @@ urlpatterns = [
|
||||
re_path(r'^plannings$', PlanningsView.as_view(), name="plannings"),
|
||||
re_path(r'^planning$', PlanningView.as_view(), name="planning"),
|
||||
re_path(r'^planning/([0-9]+)$', PlanningView.as_view(), name="planning"),
|
||||
|
||||
re_path(r'^fees$', FeesView.as_view(), name="fees"),
|
||||
re_path(r'^fee$', FeeView.as_view(), name="fee"),
|
||||
re_path(r'^fee/([0-9]+)$', FeeView.as_view(), name="fee"),
|
||||
|
||||
re_path(r'^tuitionFees$', TuitionFeesView.as_view(), name="tuitionFees"),
|
||||
re_path(r'^tuitionFee$', TuitionFeeView.as_view(), name="tuitionFee"),
|
||||
re_path(r'^tuitionFee/([0-9]+)$', TuitionFeeView.as_view(), name="tuitionFee"),
|
||||
|
||||
re_path(r'^discounts$', DiscountsView.as_view(), name="discounts"),
|
||||
re_path(r'^discount$', DiscountView.as_view(), name="discount"),
|
||||
re_path(r'^discount/([0-9]+)$', DiscountView.as_view(), name="discount"),
|
||||
]
|
||||
@ -5,46 +5,41 @@ from rest_framework.parsers import JSONParser
|
||||
from rest_framework.views import APIView
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from .models import Teacher, Speciality, SchoolClass, Planning
|
||||
from .serializers import TeacherSerializer, SpecialitySerializer, SchoolClassSerializer, PlanningSerializer
|
||||
from .models import Teacher, Speciality, SchoolClass, Planning, Discount, TuitionFee, Fee
|
||||
from .serializers import TeacherSerializer, SpecialitySerializer, SchoolClassSerializer, PlanningSerializer, DiscountSerializer, TuitionFeeSerializer, FeeSerializer
|
||||
from N3wtSchool import bdd
|
||||
from N3wtSchool.bdd import delete_object, getAllObjects, getObject
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class SpecialitiesView(APIView):
|
||||
def get(self, request):
|
||||
specialitiesList=bdd.getAllObjects(Speciality)
|
||||
specialities_serializer=SpecialitySerializer(specialitiesList, many=True)
|
||||
|
||||
specialitiesList = getAllObjects(Speciality)
|
||||
specialities_serializer = SpecialitySerializer(specialitiesList, many=True)
|
||||
return JsonResponse(specialities_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
specialities_data=JSONParser().parse(request)
|
||||
specialities_data = JSONParser().parse(request)
|
||||
all_valid = True
|
||||
for speciality_data in specialities_data:
|
||||
speciality_serializer = SpecialitySerializer(data=speciality_data)
|
||||
|
||||
if speciality_serializer.is_valid():
|
||||
speciality_serializer.save()
|
||||
else:
|
||||
all_valid = False
|
||||
break
|
||||
if all_valid:
|
||||
specialitiesList = bdd.getAllObjects(Speciality)
|
||||
specialitiesList = getAllObjects(Speciality)
|
||||
specialities_serializer = SpecialitySerializer(specialitiesList, many=True)
|
||||
|
||||
return JsonResponse(specialities_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(speciality_serializer.errors, safe=False)
|
||||
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class SpecialityView(APIView):
|
||||
def get (self, request, _id):
|
||||
speciality = bdd.getObject(_objectName=Speciality, _columnName='id', _value=_id)
|
||||
speciality_serializer=SpecialitySerializer(speciality)
|
||||
|
||||
def get(self, request, _id):
|
||||
speciality = getObject(_objectName=Speciality, _columnName='id', _value=_id)
|
||||
speciality_serializer = SpecialitySerializer(speciality)
|
||||
return JsonResponse(speciality_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
@ -59,7 +54,7 @@ class SpecialityView(APIView):
|
||||
|
||||
def put(self, request, _id):
|
||||
speciality_data=JSONParser().parse(request)
|
||||
speciality = bdd.getObject(_objectName=Speciality, _columnName='id', _value=_id)
|
||||
speciality = getObject(_objectName=Speciality, _columnName='id', _value=_id)
|
||||
speciality_serializer = SpecialitySerializer(speciality, data=speciality_data)
|
||||
if speciality_serializer.is_valid():
|
||||
speciality_serializer.save()
|
||||
@ -68,11 +63,62 @@ class SpecialityView(APIView):
|
||||
return JsonResponse(speciality_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, _id):
|
||||
return bdd.delete_object(Speciality, _id)
|
||||
return delete_object(Speciality, _id)
|
||||
|
||||
# Vues pour les réductions (Discount)
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class DiscountsView(APIView):
|
||||
def get(self, request):
|
||||
discountsList = Discount.objects.all()
|
||||
discounts_serializer = DiscountSerializer(discountsList, many=True)
|
||||
return JsonResponse(discounts_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
discount_data = JSONParser().parse(request)
|
||||
discount_serializer = DiscountSerializer(data=discount_data)
|
||||
if discount_serializer.is_valid():
|
||||
discount_serializer.save()
|
||||
return JsonResponse(discount_serializer.data, safe=False, status=201)
|
||||
return JsonResponse(discount_serializer.errors, safe=False, status=400)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class DiscountView(APIView):
|
||||
def get(self, request, _id):
|
||||
try:
|
||||
discount = Discount.objects.get(id=_id)
|
||||
discount_serializer = DiscountSerializer(discount)
|
||||
return JsonResponse(discount_serializer.data, safe=False)
|
||||
except Discount.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=404)
|
||||
|
||||
def post(self, request):
|
||||
discount_data = JSONParser().parse(request)
|
||||
discount_serializer = DiscountSerializer(data=discount_data)
|
||||
if discount_serializer.is_valid():
|
||||
discount_serializer.save()
|
||||
return JsonResponse(discount_serializer.data, safe=False, status=201)
|
||||
return JsonResponse(discount_serializer.errors, safe=False, status=400)
|
||||
|
||||
def put(self, request, _id):
|
||||
discount_data = JSONParser().parse(request)
|
||||
try:
|
||||
discount = Discount.objects.get(id=_id)
|
||||
except Discount.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=404)
|
||||
discount_serializer = DiscountSerializer(discount, data=discount_data, partial=True) # Utilisation de partial=True
|
||||
if discount_serializer.is_valid():
|
||||
discount_serializer.save()
|
||||
return JsonResponse(discount_serializer.data, safe=False)
|
||||
return JsonResponse(discount_serializer.errors, safe=False, status=400)
|
||||
|
||||
def delete(self, request, _id):
|
||||
return delete_object(Discount, _id)
|
||||
|
||||
class TeachersView(APIView):
|
||||
def get(self, request):
|
||||
teachersList=bdd.getAllObjects(Teacher)
|
||||
teachersList=getAllObjects(Teacher)
|
||||
teachers_serializer=TeacherSerializer(teachersList, many=True)
|
||||
|
||||
return JsonResponse(teachers_serializer.data, safe=False)
|
||||
@ -81,7 +127,7 @@ class TeachersView(APIView):
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class TeacherView(APIView):
|
||||
def get (self, request, _id):
|
||||
teacher = bdd.getObject(_objectName=Teacher, _columnName='id', _value=_id)
|
||||
teacher = getObject(_objectName=Teacher, _columnName='id', _value=_id)
|
||||
teacher_serializer=TeacherSerializer(teacher)
|
||||
|
||||
return JsonResponse(teacher_serializer.data, safe=False)
|
||||
@ -99,7 +145,7 @@ class TeacherView(APIView):
|
||||
|
||||
def put(self, request, _id):
|
||||
teacher_data=JSONParser().parse(request)
|
||||
teacher = bdd.getObject(_objectName=Teacher, _columnName='id', _value=_id)
|
||||
teacher = getObject(_objectName=Teacher, _columnName='id', _value=_id)
|
||||
teacher_serializer = TeacherSerializer(teacher, data=teacher_data)
|
||||
if teacher_serializer.is_valid():
|
||||
teacher_serializer.save()
|
||||
@ -108,13 +154,13 @@ class TeacherView(APIView):
|
||||
return JsonResponse(teacher_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, _id):
|
||||
return bdd.delete_object(Teacher, _id, related_field='associated_profile')
|
||||
return delete_object(Teacher, _id, related_field='associated_profile')
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class ClassesView(APIView):
|
||||
def get(self, request):
|
||||
classesList=bdd.getAllObjects(SchoolClass)
|
||||
classesList=getAllObjects(SchoolClass)
|
||||
classes_serializer=SchoolClassSerializer(classesList, many=True)
|
||||
return JsonResponse(classes_serializer.data, safe=False)
|
||||
|
||||
@ -131,7 +177,7 @@ class ClassesView(APIView):
|
||||
break
|
||||
|
||||
if all_valid:
|
||||
classesList = bdd.getAllObjects(SchoolClass)
|
||||
classesList = getAllObjects(SchoolClass)
|
||||
classes_serializer = SchoolClassSerializer(classesList, many=True)
|
||||
|
||||
return JsonResponse(classes_serializer.data, safe=False)
|
||||
@ -142,7 +188,7 @@ class ClassesView(APIView):
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class ClasseView(APIView):
|
||||
def get (self, request, _id):
|
||||
schoolClass = bdd.getObject(_objectName=SchoolClass, _columnName='id', _value=_id)
|
||||
schoolClass = getObject(_objectName=SchoolClass, _columnName='id', _value=_id)
|
||||
classe_serializer=SchoolClassSerializer(schoolClass)
|
||||
|
||||
return JsonResponse(classe_serializer.data, safe=False)
|
||||
@ -159,7 +205,7 @@ class ClasseView(APIView):
|
||||
|
||||
def put(self, request, _id):
|
||||
classe_data=JSONParser().parse(request)
|
||||
schoolClass = bdd.getObject(_objectName=SchoolClass, _columnName='id', _value=_id)
|
||||
schoolClass = getObject(_objectName=SchoolClass, _columnName='id', _value=_id)
|
||||
classe_serializer = SchoolClassSerializer(schoolClass, data=classe_data)
|
||||
if classe_serializer.is_valid():
|
||||
classe_serializer.save()
|
||||
@ -168,14 +214,14 @@ class ClasseView(APIView):
|
||||
return JsonResponse(classe_serializer.errors, safe=False)
|
||||
|
||||
def delete(self, request, _id):
|
||||
return bdd.delete_object(SchoolClass, _id)
|
||||
return delete_object(SchoolClass, _id)
|
||||
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class PlanningsView(APIView):
|
||||
def get(self, request):
|
||||
schedulesList=bdd.getAllObjects(Planning)
|
||||
schedulesList=getAllObjects(Planning)
|
||||
schedules_serializer=PlanningSerializer(schedulesList, many=True)
|
||||
return JsonResponse(schedules_serializer.data, safe=False)
|
||||
|
||||
@ -183,7 +229,7 @@ class PlanningsView(APIView):
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class PlanningView(APIView):
|
||||
def get (self, request, _id):
|
||||
planning = bdd.getObject(_objectName=Planning, _columnName='classe__id', _value=_id)
|
||||
planning = getObject(_objectName=Planning, _columnName='classe__id', _value=_id)
|
||||
planning_serializer=PlanningSerializer(planning)
|
||||
|
||||
return JsonResponse(planning_serializer.data, safe=False)
|
||||
@ -215,3 +261,98 @@ class PlanningView(APIView):
|
||||
return JsonResponse(planning_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(planning_serializer.errors, safe=False)
|
||||
|
||||
|
||||
# Vues pour les frais (Fee)
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class FeesView(APIView):
|
||||
def get(self, request):
|
||||
feesList = Fee.objects.all()
|
||||
fees_serializer = FeeSerializer(feesList, many=True)
|
||||
return JsonResponse(fees_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
fee_data = JSONParser().parse(request)
|
||||
fee_serializer = FeeSerializer(data=fee_data)
|
||||
if fee_serializer.is_valid():
|
||||
fee_serializer.save()
|
||||
return JsonResponse(fee_serializer.data, safe=False, status=201)
|
||||
return JsonResponse(fee_serializer.errors, safe=False, status=400)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class FeeView(APIView):
|
||||
def get(self, request, _id):
|
||||
try:
|
||||
fee = Fee.objects.get(id=_id)
|
||||
fee_serializer = FeeSerializer(fee)
|
||||
return JsonResponse(fee_serializer.data, safe=False)
|
||||
except Fee.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=404)
|
||||
|
||||
def post(self, request):
|
||||
fee_data = JSONParser().parse(request)
|
||||
fee_serializer = FeeSerializer(data=fee_data)
|
||||
if fee_serializer.is_valid():
|
||||
fee_serializer.save()
|
||||
return JsonResponse(fee_serializer.data, safe=False, status=201)
|
||||
return JsonResponse(fee_serializer.errors, safe=False, status=400)
|
||||
|
||||
def put(self, request, _id):
|
||||
fee_data = JSONParser().parse(request)
|
||||
try:
|
||||
fee = Fee.objects.get(id=_id)
|
||||
except Fee.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=404)
|
||||
fee_serializer = FeeSerializer(fee, data=fee_data, partial=True) # Utilisation de partial=True
|
||||
if fee_serializer.is_valid():
|
||||
fee_serializer.save()
|
||||
return JsonResponse(fee_serializer.data, safe=False)
|
||||
return JsonResponse(fee_serializer.errors, safe=False, status=400)
|
||||
|
||||
def delete(self, request, _id):
|
||||
return delete_object(Fee, _id)
|
||||
|
||||
# Vues pour les frais de scolarité (TuitionFee)
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class TuitionFeesView(APIView):
|
||||
def get(self, request):
|
||||
tuitionFeesList = TuitionFee.objects.all()
|
||||
tuitionFees_serializer = TuitionFeeSerializer(tuitionFeesList, many=True)
|
||||
return JsonResponse(tuitionFees_serializer.data, safe=False)
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class TuitionFeeView(APIView):
|
||||
def get(self, request, _id):
|
||||
try:
|
||||
tuitionFee = TuitionFee.objects.get(id=_id)
|
||||
tuitionFee_serializer = TuitionFeeSerializer(tuitionFee)
|
||||
return JsonResponse(tuitionFee_serializer.data, safe=False)
|
||||
except TuitionFee.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=404)
|
||||
|
||||
def post(self, request):
|
||||
tuitionFee_data = JSONParser().parse(request)
|
||||
tuitionFee_serializer = TuitionFeeSerializer(data=tuitionFee_data)
|
||||
if tuitionFee_serializer.is_valid():
|
||||
tuitionFee_serializer.save()
|
||||
return JsonResponse(tuitionFee_serializer.data, safe=False, status=201)
|
||||
return JsonResponse(tuitionFee_serializer.errors, safe=False, status=400)
|
||||
|
||||
def put(self, request, _id):
|
||||
tuitionFee_data = JSONParser().parse(request)
|
||||
try:
|
||||
tuitionFee = TuitionFee.objects.get(id=_id)
|
||||
except TuitionFee.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=404)
|
||||
tuitionFee_serializer = TuitionFeeSerializer(tuitionFee, data=tuitionFee_data, partial=True) # Utilisation de partial=True
|
||||
if tuitionFee_serializer.is_valid():
|
||||
tuitionFee_serializer.save()
|
||||
return JsonResponse(tuitionFee_serializer.data, safe=False)
|
||||
return JsonResponse(tuitionFee_serializer.errors, safe=False, status=400)
|
||||
|
||||
def delete(self, request, _id):
|
||||
return delete_object(TuitionFee, _id)
|
||||
@ -9,6 +9,9 @@ from School.models import SchoolClass
|
||||
from datetime import datetime
|
||||
|
||||
class RegistrationFee(models.Model):
|
||||
"""
|
||||
Représente un tarif ou frais d’inscription avec différentes options de paiement.
|
||||
"""
|
||||
class PaymentOptions(models.IntegerChoices):
|
||||
SINGLE_PAYMENT = 0, _('Paiement en une seule fois')
|
||||
MONTHLY_PAYMENT = 1, _('Paiement mensuel')
|
||||
@ -27,6 +30,9 @@ class RegistrationFee(models.Model):
|
||||
return self.name
|
||||
|
||||
class Language(models.Model):
|
||||
"""
|
||||
Représente une langue parlée par l’élève.
|
||||
"""
|
||||
id = models.AutoField(primary_key=True)
|
||||
label = models.CharField(max_length=200, default="")
|
||||
|
||||
@ -34,6 +40,9 @@ class Language(models.Model):
|
||||
return "LANGUAGE"
|
||||
|
||||
class Guardian(models.Model):
|
||||
"""
|
||||
Représente un responsable légal (parent/tuteur) d’un élève.
|
||||
"""
|
||||
last_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)
|
||||
@ -47,6 +56,9 @@ class Guardian(models.Model):
|
||||
return self.last_name + "_" + self.first_name
|
||||
|
||||
class Sibling(models.Model):
|
||||
"""
|
||||
Représente un frère ou une sœur d’un élève.
|
||||
"""
|
||||
id = models.AutoField(primary_key=True)
|
||||
last_name = models.CharField(max_length=200, default="")
|
||||
first_name = models.CharField(max_length=200, default="")
|
||||
@ -56,7 +68,9 @@ class Sibling(models.Model):
|
||||
return "SIBLING"
|
||||
|
||||
class Student(models.Model):
|
||||
|
||||
"""
|
||||
Représente l’élève inscrit ou en cours d’inscription.
|
||||
"""
|
||||
class StudentGender(models.IntegerChoices):
|
||||
NONE = 0, _('Sélection du genre')
|
||||
MALE = 1, _('Garçon')
|
||||
@ -95,6 +109,9 @@ class Student(models.Model):
|
||||
# Many-to-Many Relationship
|
||||
siblings = models.ManyToManyField(Sibling, blank=True)
|
||||
|
||||
# Many-to-Many Relationship
|
||||
registration_files = models.ManyToManyField('RegistrationFile', blank=True, related_name='students')
|
||||
|
||||
# Many-to-Many Relationship
|
||||
spoken_languages = models.ManyToManyField(Language, blank=True)
|
||||
|
||||
@ -105,21 +122,39 @@ class Student(models.Model):
|
||||
return self.last_name + "_" + self.first_name
|
||||
|
||||
def getSpokenLanguages(self):
|
||||
"""
|
||||
Retourne la liste des langues parlées par l’élève.
|
||||
"""
|
||||
return self.spoken_languages.all()
|
||||
|
||||
def getMainGuardian(self):
|
||||
"""
|
||||
Retourne le responsable légal principal de l’élève.
|
||||
"""
|
||||
return self.guardians.all()[0]
|
||||
|
||||
def getGuardians(self):
|
||||
"""
|
||||
Retourne tous les responsables légaux de l’élève.
|
||||
"""
|
||||
return self.guardians.all()
|
||||
|
||||
def getProfiles(self):
|
||||
"""
|
||||
Retourne les profils utilisateurs liés à l’élève.
|
||||
"""
|
||||
return self.profiles.all()
|
||||
|
||||
def getSiblings(self):
|
||||
"""
|
||||
Retourne les frères et sœurs de l’élève.
|
||||
"""
|
||||
return self.siblings.all()
|
||||
|
||||
def getNumberOfSiblings(self):
|
||||
"""
|
||||
Retourne le nombre de frères et sœurs.
|
||||
"""
|
||||
return self.siblings.count()
|
||||
|
||||
@property
|
||||
@ -148,7 +183,9 @@ class Student(models.Model):
|
||||
return None
|
||||
|
||||
class RegistrationForm(models.Model):
|
||||
|
||||
"""
|
||||
Gère le dossier d’inscription lié à un élève donné.
|
||||
"""
|
||||
class RegistrationFormStatus(models.IntegerChoices):
|
||||
RF_ABSENT = 0, _('Pas de dossier d\'inscription')
|
||||
RF_CREATED = 1, _('Dossier d\'inscription créé')
|
||||
@ -171,9 +208,53 @@ class RegistrationForm(models.Model):
|
||||
return "RF_" + self.student.last_name + "_" + self.student.first_name
|
||||
|
||||
class RegistrationFileTemplate(models.Model):
|
||||
"""
|
||||
Modèle pour stocker les fichiers "templates" d’inscription.
|
||||
"""
|
||||
name = models.CharField(max_length=255)
|
||||
file = models.FileField(upload_to='registration_files/')
|
||||
file = models.FileField(upload_to='templates_files/', blank=True, null=True)
|
||||
order = models.PositiveIntegerField(default=0) # Ajout du champ order
|
||||
date_added = models.DateTimeField(auto_now_add=True)
|
||||
is_required = models.BooleanField(default=False)
|
||||
|
||||
@property
|
||||
def formatted_date_added(self):
|
||||
if self.date_added:
|
||||
return self.date_added.strftime('%d-%m-%Y')
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def registration_file_upload_to(instance, filename):
|
||||
return f"registration_files/dossier_rf_{instance.register_form.pk}/{filename}"
|
||||
|
||||
class RegistrationFile(models.Model):
|
||||
"""
|
||||
Fichier lié à un dossier d’inscription particulier.
|
||||
"""
|
||||
name = models.CharField(max_length=255)
|
||||
file = models.FileField(upload_to=registration_file_upload_to)
|
||||
date_added = models.DateTimeField(auto_now_add=True)
|
||||
template = models.OneToOneField(RegistrationFileTemplate, on_delete=models.CASCADE)
|
||||
register_form = models.ForeignKey('RegistrationForm', on_delete=models.CASCADE, related_name='registration_files')
|
||||
|
||||
@property
|
||||
def formatted_date_added(self):
|
||||
if self.date_added:
|
||||
return self.date_added.strftime('%d-%m-%Y')
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@staticmethod
|
||||
def get_files_from_rf(register_form_id):
|
||||
"""
|
||||
Récupère tous les fichiers liés à un dossier d’inscription donné.
|
||||
"""
|
||||
registration_files = RegistrationFile.objects.filter(register_form_id=register_form_id).order_by('template__order')
|
||||
filenames = []
|
||||
for reg_file in registration_files:
|
||||
filenames.append(reg_file.file.path)
|
||||
return filenames
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
from rest_framework import serializers
|
||||
from .models import RegistrationFileTemplate, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationFee
|
||||
from .models import RegistrationFileTemplate, RegistrationFile, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationFee
|
||||
from School.models import SchoolClass
|
||||
from Auth.models import Profile
|
||||
from Auth.serializers import ProfileSerializer
|
||||
@ -10,17 +10,22 @@ from django.utils import timezone
|
||||
import pytz
|
||||
from datetime import datetime
|
||||
|
||||
class RegistrationFileTemplateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = RegistrationFileTemplate
|
||||
fields = '__all__'
|
||||
|
||||
class RegistrationFeeSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
class Meta:
|
||||
model = RegistrationFee
|
||||
fields = '__all__'
|
||||
|
||||
class RegistrationFileSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = RegistrationFile
|
||||
fields = '__all__'
|
||||
|
||||
class RegistrationFileTemplateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = RegistrationFileTemplate
|
||||
fields = '__all__'
|
||||
|
||||
class LanguageSerializer(serializers.ModelSerializer):
|
||||
id = serializers.IntegerField(required=False)
|
||||
class Meta:
|
||||
@ -47,6 +52,7 @@ class GuardianSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class StudentSerializer(serializers.ModelSerializer):
|
||||
|
||||
id = serializers.IntegerField(required=False)
|
||||
guardians = GuardianSerializer(many=True, required=False)
|
||||
siblings = SiblingSerializer(many=True, required=False)
|
||||
@ -126,7 +132,7 @@ class RegistrationFormSerializer(serializers.ModelSerializer):
|
||||
registration_file = serializers.FileField(required=False)
|
||||
status_label = serializers.SerializerMethodField()
|
||||
formatted_last_update = serializers.SerializerMethodField()
|
||||
|
||||
registration_files = RegistrationFileSerializer(many=True, required=False)
|
||||
class Meta:
|
||||
model = RegistrationForm
|
||||
fields = '__all__'
|
||||
|
||||
@ -1,36 +1,44 @@
|
||||
from django.urls import path, re_path
|
||||
|
||||
from . import views
|
||||
from Subscriptions.views import RegistrationFileTemplateView, RegisterFormListView, RegisterFormView, StudentView, GuardianView, ChildrenListView, StudentListView, RegistrationFeeView
|
||||
from .views import RegistrationFileTemplateView, RegisterFormListView, RegisterFormView, StudentView, GuardianView, ChildrenListView, StudentListView, RegistrationFeeView, RegistrationFileView
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^registerForms/([a-zA-z]+)$', RegisterFormListView.as_view(), name="registerForms"),
|
||||
re_path(r'^registerForms/(?P<_filter>[a-zA-z]+)$', RegisterFormListView.as_view(), name="registerForms"),
|
||||
|
||||
re_path(r'^registerForm$', RegisterFormView.as_view(), name="registerForm"),
|
||||
re_path(r'^registerForm/([0-9]+)$', RegisterFormView.as_view(), name="registerForm"),
|
||||
re_path(r'^registerForm/(?P<_id>[0-9]+)$', RegisterFormView.as_view(), name="registerForm"),
|
||||
|
||||
# Page de formulaire d'inscription - ELEVE
|
||||
re_path(r'^student/([0-9]+)$', StudentView.as_view(), name="students"),
|
||||
re_path(r'^student/(?P<_id>[0-9]+)$', StudentView.as_view(), name="students"),
|
||||
|
||||
# Page de formulaire d'inscription - RESPONSABLE
|
||||
re_path(r'^lastGuardian$', GuardianView.as_view(), name="lastGuardian"),
|
||||
|
||||
# Envoi d'un dossier d'inscription
|
||||
re_path(r'^send/([0-9]+)$', views.send, name="send"),
|
||||
re_path(r'^send/(?P<_id>[0-9]+)$', views.send, name="send"),
|
||||
|
||||
# Archivage d'un dossier d'inscription
|
||||
re_path(r'^archive/([0-9]+)$', views.archive, name="archive"),
|
||||
re_path(r'^archive/(?P<_id>[0-9]+)$', views.archive, name="archive"),
|
||||
|
||||
# Envoi d'une relance de dossier d'inscription
|
||||
re_path(r'^sendRelance/([0-9]+)$', views.relance, name="sendRelance"),
|
||||
re_path(r'^sendRelance/(?P<_id>[0-9]+)$', views.relance, name="sendRelance"),
|
||||
|
||||
# Page PARENT - Liste des children
|
||||
re_path(r'^children/([0-9]+)$', ChildrenListView.as_view(), name="children"),
|
||||
re_path(r'^children/(?P<_id>[0-9]+)$', ChildrenListView.as_view(), name="children"),
|
||||
|
||||
# Page INSCRIPTION - Liste des élèves
|
||||
re_path(r'^students$', StudentListView.as_view(), name="students"),
|
||||
|
||||
# Frais d'inscription
|
||||
re_path(r'^registrationFees$', RegistrationFeeView.as_view(), name="registrationFees"),
|
||||
|
||||
# modèles de fichiers d'inscription
|
||||
re_path(r'^registrationFileTemplates$', RegistrationFileTemplateView.as_view(), name='registrationFileTemplates'),
|
||||
re_path(r'^registrationFileTemplates/([0-9]+)$', RegistrationFileTemplateView.as_view(), name="registrationFileTemplate"),
|
||||
re_path(r'^registrationFileTemplates/(?P<_id>[0-9]+)$', RegistrationFileTemplateView.as_view(), name="registrationFileTemplate"),
|
||||
|
||||
# fichiers d'inscription
|
||||
re_path(r'^registrationFiles/(?P<_id>[0-9]+)$', RegistrationFileView.as_view(), name='registrationFiles'),
|
||||
re_path(r'^registrationFiles', RegistrationFileView.as_view(), name="registrationFiles"),
|
||||
|
||||
]
|
||||
@ -16,52 +16,95 @@ from enum import Enum
|
||||
import random
|
||||
import string
|
||||
from rest_framework.parsers import JSONParser
|
||||
import pymupdf
|
||||
|
||||
def recupereListeFichesInscription():
|
||||
"""
|
||||
Retourne la liste complète des fiches d’inscription.
|
||||
"""
|
||||
context = {
|
||||
"ficheInscriptions_list": bdd.getAllObjects(RegistrationForm),
|
||||
}
|
||||
return context
|
||||
|
||||
def recupereListeFichesInscriptionEnAttenteSEPA():
|
||||
|
||||
"""
|
||||
Retourne les fiches d’inscription avec paiement SEPA en attente.
|
||||
"""
|
||||
ficheInscriptionsSEPA_list = RegistrationForm.objects.filter(modePaiement="Prélèvement SEPA").filter(etat=RegistrationForm.RegistrationFormStatus['SEPA_ENVOYE'])
|
||||
return ficheInscriptionsSEPA_list
|
||||
|
||||
def _now():
|
||||
"""
|
||||
Retourne la date et l’heure en cours, avec fuseau.
|
||||
"""
|
||||
return datetime.now(ZoneInfo(settings.TZ_APPLI))
|
||||
|
||||
def convertToStr(dateValue, dateFormat):
|
||||
"""
|
||||
Convertit un objet datetime en chaîne selon un format donné.
|
||||
"""
|
||||
return dateValue.strftime(dateFormat)
|
||||
|
||||
def convertToDate(date_time):
|
||||
"""
|
||||
Convertit une chaîne en objet datetime selon le format '%d-%m-%Y %H:%M'.
|
||||
"""
|
||||
format = '%d-%m-%Y %H:%M'
|
||||
datetime_str = datetime.strptime(date_time, format)
|
||||
|
||||
return datetime_str
|
||||
|
||||
def convertTelephone(telephoneValue, separator='-'):
|
||||
"""
|
||||
Reformate un numéro de téléphone en y insérant un séparateur donné.
|
||||
"""
|
||||
return f"{telephoneValue[:2]}{separator}{telephoneValue[2:4]}{separator}{telephoneValue[4:6]}{separator}{telephoneValue[6:8]}{separator}{telephoneValue[8:10]}"
|
||||
|
||||
def genereRandomCode(length):
|
||||
"""
|
||||
Génère un code aléatoire de longueur spécifiée.
|
||||
"""
|
||||
return ''.join(random.choice(string.ascii_letters) for i in range(length))
|
||||
|
||||
def calculeDatePeremption(_start, nbDays):
|
||||
"""
|
||||
Calcule la date de fin à partir d’un point de départ et d’un nombre de jours.
|
||||
"""
|
||||
return convertToStr(_start + timedelta(days=nbDays), settings.DATE_FORMAT)
|
||||
|
||||
# Fonction permettant de retourner la valeur du QueryDict
|
||||
# QueryDict [ index ] -> Dernière valeur d'une liste
|
||||
# dict (QueryDict [ index ]) -> Toutes les valeurs de la liste
|
||||
def _(liste):
|
||||
"""
|
||||
Retourne la première valeur d’une liste extraite d’un QueryDict.
|
||||
"""
|
||||
return liste[0]
|
||||
|
||||
def getArgFromRequest(_argument, _request):
|
||||
"""
|
||||
Extrait la valeur d’un argument depuis la requête (JSON).
|
||||
"""
|
||||
resultat = None
|
||||
data=JSONParser().parse(_request)
|
||||
resultat = data[_argument]
|
||||
return resultat
|
||||
|
||||
def rfToPDF(registerForm):
|
||||
def merge_files_pdf(filenames, output_filename):
|
||||
"""
|
||||
Insère plusieurs fichiers PDF dans un seul document de sortie.
|
||||
"""
|
||||
merger = pymupdf.open()
|
||||
for filename in filenames:
|
||||
merger.insert_file(filename)
|
||||
merger.save(output_filename)
|
||||
merger.close()
|
||||
|
||||
def rfToPDF(registerForm,filename):
|
||||
"""
|
||||
Génère le PDF d’un dossier d’inscription et l’associe au RegistrationForm.
|
||||
"""
|
||||
# Ajout du fichier d'inscriptions
|
||||
data = {
|
||||
'pdf_title': "Dossier d'inscription de %s"%registerForm.student.first_name,
|
||||
@ -69,14 +112,12 @@ def rfToPDF(registerForm):
|
||||
'signatureTime': convertToStr(_now(), '%H:%M'),
|
||||
'student':registerForm.student,
|
||||
}
|
||||
|
||||
PDFFileName = filename
|
||||
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
|
||||
|
||||
PDFFileName = "Dossier_Inscription_%s_%s.pdf"%(registerForm.student.last_name, registerForm.student.first_name)
|
||||
pathFichier = Path(settings.DOCUMENT_DIR + "/" + PDFFileName)
|
||||
pathFichier = Path(filename)
|
||||
if os.path.exists(str(pathFichier)):
|
||||
print(f'File exists : {str(pathFichier)}')
|
||||
os.remove(str(pathFichier))
|
||||
|
||||
receipt_file = BytesIO(pdf.content)
|
||||
registerForm.fichierInscription = File(receipt_file, PDFFileName)
|
||||
registerForm.fichierInscription = File(receipt_file, PDFFileName)
|
||||
registerForm.fichierInscription.save()
|
||||
@ -10,6 +10,8 @@ from rest_framework.parsers import JSONParser,MultiPartParser, FormParser
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework import status
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
@ -18,18 +20,22 @@ from io import BytesIO
|
||||
|
||||
import Subscriptions.mailManager as mailer
|
||||
import Subscriptions.util as util
|
||||
from Subscriptions.serializers import RegistrationFormSerializer, RegistrationFileTemplateSerializer, StudentSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFeeSerializer
|
||||
from Subscriptions.pagination import CustomPagination
|
||||
from Subscriptions.signals import clear_cache
|
||||
from .models import Student, Guardian, RegistrationForm, RegistrationFee, RegistrationFileTemplate
|
||||
|
||||
from Subscriptions.automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine
|
||||
from .serializers import RegistrationFormSerializer, StudentSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFileSerializer, RegistrationFileTemplateSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFeeSerializer
|
||||
from .pagination import CustomPagination
|
||||
from .signals import clear_cache
|
||||
from .models import Student, Guardian, RegistrationForm, RegistrationFee, RegistrationFileTemplate, RegistrationFile
|
||||
from .automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine
|
||||
|
||||
from Auth.models import Profile
|
||||
|
||||
from N3wtSchool import settings, renderers, bdd
|
||||
|
||||
class RegisterFormListView(APIView):
|
||||
"""
|
||||
Gère la liste des dossiers d’inscription, lecture et création.
|
||||
"""
|
||||
pagination_class = CustomPagination
|
||||
|
||||
def get_register_form(self, _filter, search=None):
|
||||
@ -47,8 +53,18 @@ class RegisterFormListView(APIView):
|
||||
return bdd.getObjects(RegistrationForm, 'status', RegistrationForm.RegistrationFormStatus.RF_VALIDATED)
|
||||
return None
|
||||
|
||||
@swagger_auto_schema(
|
||||
manual_parameters=[
|
||||
openapi.Parameter('_filter', openapi.IN_PATH, description="filtre", type=openapi.TYPE_STRING, enum=['pending', 'archived', 'subscribed'], required=True),
|
||||
openapi.Parameter('search', openapi.IN_QUERY, description="search", type=openapi.TYPE_STRING, required=False),
|
||||
openapi.Parameter('page_size', openapi.IN_QUERY, description="limite de page lors de la pagination", type=openapi.TYPE_INTEGER, required=False),
|
||||
],
|
||||
responses={200: RegistrationFormSerializer(many=True)}
|
||||
)
|
||||
def get(self, request, _filter):
|
||||
|
||||
"""
|
||||
Récupère les fiches d'inscriptions en fonction du filtre passé.
|
||||
"""
|
||||
# Récupération des paramètres
|
||||
search = request.GET.get('search', '').strip()
|
||||
page_size = request.GET.get('page_size', None)
|
||||
@ -84,6 +100,11 @@ class RegisterFormListView(APIView):
|
||||
|
||||
return JsonResponse({'error' : 'aucune donnée trouvée', 'count' :0}, safe=False)
|
||||
|
||||
@swagger_auto_schema(
|
||||
manual_parameters=[
|
||||
],
|
||||
responses={200: RegistrationFormSerializer(many=True)}
|
||||
)
|
||||
def post(self, request):
|
||||
studentFormList_serializer=JSONParser().parse(request)
|
||||
for studentForm_data in studentFormList_serializer:
|
||||
@ -104,14 +125,23 @@ class RegisterFormListView(APIView):
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class RegisterFormView(APIView):
|
||||
"""
|
||||
Gère la lecture, création, modification et suppression d’un dossier d’inscription.
|
||||
"""
|
||||
pagination_class = CustomPagination
|
||||
|
||||
def get(self, request, _id):
|
||||
"""
|
||||
Récupère un dossier d'inscription donné.
|
||||
"""
|
||||
registerForm=bdd.getObject(RegistrationForm, "student__id", _id)
|
||||
registerForm_serializer=RegistrationFormSerializer(registerForm)
|
||||
return JsonResponse(registerForm_serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
"""
|
||||
Crée un dossier d'inscription.
|
||||
"""
|
||||
studentForm_data=JSONParser().parse(request)
|
||||
# Ajout de la date de mise à jour
|
||||
studentForm_data["last_update"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
||||
@ -137,21 +167,33 @@ class RegisterFormView(APIView):
|
||||
|
||||
return JsonResponse(studentForm_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(studentForm_serializer.errors, safe=False, status=400)
|
||||
return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def put(self, request, id):
|
||||
def put(self, request, _id):
|
||||
"""
|
||||
Modifie un dossier d'inscription donné.
|
||||
"""
|
||||
studentForm_data=JSONParser().parse(request)
|
||||
status = studentForm_data.pop('status', 0)
|
||||
_status = studentForm_data.pop('status', 0)
|
||||
studentForm_data["last_update"] = str(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'))
|
||||
registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
|
||||
registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id)
|
||||
|
||||
if status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
|
||||
if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
|
||||
# Le parent a complété le dossier d'inscription, il est soumis à validation par l'école
|
||||
json.dumps(studentForm_data)
|
||||
util.rfToPDF(registerForm)
|
||||
#Génération de la fiche d'inscription au format PDF
|
||||
PDFFileName = "rf_%s_%s.pdf"%(registerForm.student.last_name, registerForm.student.first_name)
|
||||
path = Path(f"registration_files/dossier_rf_{registerForm.pk}/{PDFFileName}")
|
||||
registerForm.fichierInscription = util.rfToPDF(registerForm, path)
|
||||
# Récupération des fichiers d'inscription
|
||||
fileNames = RegistrationFile.get_files_from_rf(registerForm.pk)
|
||||
fileNames.insert(0,path)
|
||||
# Création du fichier PDF Fusionné avec le dossier complet
|
||||
output_path = f"registration_files/dossier_rf_{registerForm.pk}/dossier_{registerForm.pk}.pdf"
|
||||
util.merge_files_pdf(fileNames, output_path)
|
||||
# Mise à jour de l'automate
|
||||
updateStateMachine(registerForm, 'saisiDI')
|
||||
elif status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED:
|
||||
elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED:
|
||||
# L'école a validé le dossier d'inscription
|
||||
# Mise à jour de l'automate
|
||||
updateStateMachine(registerForm, 'valideDI')
|
||||
@ -162,34 +204,49 @@ class RegisterFormView(APIView):
|
||||
studentForm_serializer.save()
|
||||
return JsonResponse(studentForm_serializer.data, safe=False)
|
||||
|
||||
return JsonResponse(studentForm_serializer.errors, safe=False, status=400)
|
||||
return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, id):
|
||||
"""
|
||||
Supprime un dossier d'inscription donné.
|
||||
"""
|
||||
register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
|
||||
if register_form != None:
|
||||
student = register_form.student
|
||||
student.guardians.clear()
|
||||
student.profiles.clear()
|
||||
student.registration_files.clear()
|
||||
student.delete()
|
||||
clear_cache()
|
||||
|
||||
return JsonResponse("La suppression du dossier a été effectuée avec succès", safe=False)
|
||||
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=400)
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
class StudentView(APIView):
|
||||
"""
|
||||
Gère la lecture d’un élève donné.
|
||||
"""
|
||||
def get(self, request, _id):
|
||||
student = bdd.getObject(_objectName=Student, _columnName='id', _value=_id)
|
||||
if student is None:
|
||||
return JsonResponse({"errorMessage":'Aucun élève trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
student_serializer = StudentSerializer(student)
|
||||
return JsonResponse(student_serializer.data, safe=False)
|
||||
|
||||
class GuardianView(APIView):
|
||||
"""
|
||||
Récupère le dernier ID de responsable légal créé.
|
||||
"""
|
||||
def get(self, request):
|
||||
lastGuardian = bdd.getLastId(Guardian)
|
||||
return JsonResponse({"lastid":lastGuardian}, safe=False)
|
||||
|
||||
def send(request, id):
|
||||
register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
|
||||
def send(request, _id):
|
||||
"""
|
||||
Envoie le dossier d’inscription par e-mail.
|
||||
"""
|
||||
register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id)
|
||||
if register_form != None:
|
||||
student = register_form.student
|
||||
guardian = student.getMainGuardian()
|
||||
@ -199,24 +256,31 @@ def send(request, id):
|
||||
register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
||||
# Mise à jour de l'automate
|
||||
updateStateMachine(register_form, 'envoiDI')
|
||||
return JsonResponse({"message": f"Le dossier d'inscription a bien été envoyé à l'addresse {email}"}, safe=False)
|
||||
|
||||
return JsonResponse({"errorMessage":errorMessage}, safe=False, status=400)
|
||||
return JsonResponse({"errorMessage":errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=400)
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def archive(request, id):
|
||||
register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
|
||||
def archive(request, _id):
|
||||
"""
|
||||
Archive le dossier d’inscription visé.
|
||||
"""
|
||||
register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id)
|
||||
if register_form != None:
|
||||
register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
||||
# Mise à jour de l'automate
|
||||
updateStateMachine(register_form, 'archiveDI')
|
||||
|
||||
return JsonResponse({"errorMessage":''}, safe=False, status=400)
|
||||
return JsonResponse({"errorMessage":''}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=400)
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def relance(request, id):
|
||||
register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
|
||||
def relance(request, _id):
|
||||
"""
|
||||
Relance un dossier d’inscription par e-mail.
|
||||
"""
|
||||
register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id)
|
||||
if register_form != None:
|
||||
student = register_form.student
|
||||
guardian = student.getMainGuardian()
|
||||
@ -227,12 +291,15 @@ def relance(request, id):
|
||||
register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
||||
register_form.save()
|
||||
|
||||
return JsonResponse({"errorMessage":errorMessage}, safe=False, status=400)
|
||||
return JsonResponse({"errorMessage":errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=400)
|
||||
return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
# API utilisée pour la vue parent
|
||||
class ChildrenListView(APIView):
|
||||
"""
|
||||
Pour la vue parent : liste les élèves rattachés à un profil donné.
|
||||
"""
|
||||
# Récupération des élèves d'un parent
|
||||
# idProfile : identifiant du profil connecté rattaché aux fiches d'élèves
|
||||
def get(self, request, _idProfile):
|
||||
@ -242,6 +309,9 @@ class ChildrenListView(APIView):
|
||||
|
||||
# API utilisée pour la vue de création d'un DI
|
||||
class StudentListView(APIView):
|
||||
"""
|
||||
Pour la vue de création d’un dossier d’inscription : liste les élèves disponibles.
|
||||
"""
|
||||
# Récupération de la liste des élèves inscrits ou en cours d'inscriptions
|
||||
def get(self, request):
|
||||
students = bdd.getAllObjects(_objectName=Student)
|
||||
@ -250,20 +320,52 @@ class StudentListView(APIView):
|
||||
|
||||
# API utilisée pour la vue de personnalisation des frais d'inscription pour la structure
|
||||
class RegistrationFeeView(APIView):
|
||||
"""
|
||||
Liste les frais d’inscription.
|
||||
"""
|
||||
def get(self, request):
|
||||
tarifs = bdd.getAllObjects(RegistrationFee)
|
||||
tarifs_serializer = RegistrationFeeSerializer(tarifs, many=True)
|
||||
return JsonResponse(tarifs_serializer.data, safe=False)
|
||||
|
||||
class RegistrationFileTemplateView(APIView):
|
||||
"""
|
||||
Gère les fichiers templates pour les dossiers d’inscription.
|
||||
"""
|
||||
parser_classes = (MultiPartParser, FormParser)
|
||||
|
||||
def get(self, request):
|
||||
fichiers = RegistrationFileTemplate.objects.all()
|
||||
serializer = RegistrationFileTemplateSerializer(fichiers, many=True)
|
||||
return Response(serializer.data)
|
||||
def get(self, request, _id=None):
|
||||
"""
|
||||
Récupère les fichiers templates pour les dossiers d’inscription.
|
||||
"""
|
||||
if _id is None:
|
||||
files = RegistrationFileTemplate.objects.all()
|
||||
serializer = RegistrationFileTemplateSerializer(files, many=True)
|
||||
return Response(serializer.data)
|
||||
else :
|
||||
registationFileTemplate = bdd.getObject(_objectName=RegistrationFileTemplate, _columnName='id', _value=_id)
|
||||
if registationFileTemplate is None:
|
||||
return JsonResponse({"errorMessage":'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
serializer = RegistrationFileTemplateSerializer(registationFileTemplate)
|
||||
return JsonResponse(serializer.data, safe=False)
|
||||
|
||||
def put(self, request, _id):
|
||||
"""
|
||||
Met à jour un fichier template existant.
|
||||
"""
|
||||
registationFileTemplate = bdd.getObject(_objectName=RegistrationFileTemplate, _columnName='id', _value=_id)
|
||||
if registationFileTemplate is None:
|
||||
return JsonResponse({'erreur': 'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
serializer = RegistrationFileTemplateSerializer(registationFileTemplate,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)
|
||||
|
||||
def post(self, request):
|
||||
"""
|
||||
Crée un fichier template pour les dossiers d’inscription.
|
||||
"""
|
||||
serializer = RegistrationFileTemplateSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
@ -271,10 +373,71 @@ class RegistrationFileTemplateView(APIView):
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, _id):
|
||||
"""
|
||||
Supprime un fichier template existant.
|
||||
"""
|
||||
registrationFileTemplate = bdd.getObject(_objectName=RegistrationFileTemplate, _columnName='id', _value=_id)
|
||||
if registrationFileTemplate is not None:
|
||||
registrationFileTemplate.file.delete() # Supprimer le fichier uploadé
|
||||
registrationFileTemplate.delete()
|
||||
return JsonResponse({'message': 'La suppression du fichier d\'inscription a été effectuée avec succès'}, safe=False)
|
||||
return JsonResponse({'message': 'La suppression du fichier d\'inscription a été effectuée avec succès'}, safe=False, status=status.HTTP_204_NO_CONTENT)
|
||||
else:
|
||||
return JsonResponse({'erreur': 'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=400)
|
||||
return JsonResponse({'erreur': 'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
class RegistrationFileView(APIView):
|
||||
"""
|
||||
Gère la création, mise à jour et suppression de fichiers liés à un dossier d’inscription.
|
||||
"""
|
||||
parser_classes = (MultiPartParser, FormParser)
|
||||
|
||||
def get(self, request, _id=None):
|
||||
"""
|
||||
Récupère les fichiers liés à un dossier d’inscription donné.
|
||||
"""
|
||||
if (_id is None):
|
||||
files = RegistrationFile.objects.all()
|
||||
serializer = RegistrationFileSerializer(files, many=True)
|
||||
return Response(serializer.data)
|
||||
else:
|
||||
registationFile = bdd.getObject(_objectName=RegistrationFile, _columnName='id', _value=_id)
|
||||
if registationFile is None:
|
||||
return JsonResponse({"errorMessage":'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
serializer = RegistrationFileSerializer(registationFile)
|
||||
return JsonResponse(serializer.data, safe=False)
|
||||
|
||||
def post(self, request):
|
||||
"""
|
||||
Crée un RegistrationFile pour le RegistrationForm associé.
|
||||
"""
|
||||
serializer = RegistrationFileSerializer(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)
|
||||
|
||||
|
||||
def put(self, request, fileId):
|
||||
"""
|
||||
Met à jour un RegistrationFile existant.
|
||||
"""
|
||||
registrationFile = bdd.getObject(_objectName=RegistrationFile, _columnName='id', _value=fileId)
|
||||
if registrationFile is None:
|
||||
return JsonResponse({'erreur': 'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
serializer = RegistrationFileSerializer(registrationFile, data=request.data)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response({'message': 'Fichier mis à jour avec succès', 'data': serializer.data}, status=status.HTTP_200_OK)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, _id):
|
||||
"""
|
||||
Supprime un RegistrationFile existant.
|
||||
"""
|
||||
registrationFile = bdd.getObject(_objectName=RegistrationFile, _columnName='id', _value=_id)
|
||||
if registrationFile is not None:
|
||||
registrationFile.file.delete() # Supprimer le fichier uploadé
|
||||
registrationFile.delete()
|
||||
return JsonResponse({'message': 'La suppression du fichier a été effectuée avec succès'}, safe=False)
|
||||
else:
|
||||
return JsonResponse({'erreur': 'Le fichier n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
@ -13,12 +13,12 @@ def run_command(command):
|
||||
commands = [
|
||||
["python", "manage.py", "collectstatic", "--noinput"],
|
||||
["python", "manage.py", "flush", "--noinput"],
|
||||
["python", "manage.py", "makemigrations", "Subscriptions"],
|
||||
["python", "manage.py", "makemigrations", "GestionNotification"],
|
||||
["python", "manage.py", "makemigrations", "GestionMessagerie"],
|
||||
["python", "manage.py", "makemigrations", "Auth"],
|
||||
["python", "manage.py", "makemigrations", "School"],
|
||||
["python", "manage.py", "migrate"]
|
||||
["python", "manage.py", "makemigrations", "Subscriptions", "--noinput"],
|
||||
["python", "manage.py", "makemigrations", "GestionNotification", "--noinput"],
|
||||
["python", "manage.py", "makemigrations", "GestionMessagerie", "--noinput"],
|
||||
["python", "manage.py", "makemigrations", "Auth", "--noinput"],
|
||||
["python", "manage.py", "makemigrations", "School", "--noinput"],
|
||||
["python", "manage.py", "migrate", "--noinput"]
|
||||
]
|
||||
|
||||
for command in commands:
|
||||
|
||||
Reference in New Issue
Block a user