From cce78355a3836379e3b33ba1bf5ee008449e03ac Mon Sep 17 00:00:00 2001 From: N3WT DE COMPET Date: Thu, 13 Feb 2025 17:13:31 +0100 Subject: [PATCH] =?UTF-8?q?chore:=20commit=20qui=20sert=20=C3=A0=20rien?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../management/commands/init_establishment.py | 24 +++++++ .../management/commands/init_payment_modes.py | 6 +- .../management/commands/init_payment_plans.py | 15 ++-- .../commands/init_school_configuration.py | 12 ++-- .../management/commands/init_school_fees.py | 25 ++++--- Back-End/School/models.py | 23 +++++- Back-End/School/serializers.py | 7 +- Back-End/School/urls.py | 9 ++- Back-End/School/views.py | 48 ++++++++++++- Back-End/Subscriptions/models.py | 4 +- Back-End/start.py | 9 +-- Front-End/src/app/[locale]/admin/layout.js | 72 +++++++++++-------- Front-End/src/app/lib/schoolAction.js | 9 ++- Front-End/src/components/Sidebar.js | 4 +- Front-End/src/utils/Url.js | 4 ++ README.md | 62 ---------------- package.json | 20 ------ 17 files changed, 210 insertions(+), 143 deletions(-) create mode 100644 Back-End/School/management/commands/init_establishment.py delete mode 100644 README.md delete mode 100644 package.json diff --git a/Back-End/School/management/commands/init_establishment.py b/Back-End/School/management/commands/init_establishment.py new file mode 100644 index 0000000..874ee15 --- /dev/null +++ b/Back-End/School/management/commands/init_establishment.py @@ -0,0 +1,24 @@ +from django.core.management.base import BaseCommand +from School.models import Establishment, StructureType + +class Command(BaseCommand): + help = 'Initialize the establishment' + + def handle(self, *args, **kwargs): + establishment_data = { + "name": "N3WT", + "address": "Société n3wt-innov 69 Chez LANA", + "total_capacity": 69, + "establishment_type": [StructureType.MATERNELLE, StructureType.PRIMAIRE], + "licence_code": "" + } + + establishment, created = Establishment.objects.update_or_create( + name=establishment_data["name"], + defaults=establishment_data + ) + + if created: + self.stdout.write(self.style.SUCCESS('Establishment created successfully')) + else: + self.stdout.write(self.style.SUCCESS('Establishment updated successfully')) \ No newline at end of file diff --git a/Back-End/School/management/commands/init_payment_modes.py b/Back-End/School/management/commands/init_payment_modes.py index 8d4abf5..844a2f6 100644 --- a/Back-End/School/management/commands/init_payment_modes.py +++ b/Back-End/School/management/commands/init_payment_modes.py @@ -1,5 +1,5 @@ from django.core.management.base import BaseCommand -from School.models import PaymentMode, PaymentModeType, FeeType +from School.models import PaymentMode, PaymentModeType, FeeType, Establishment class Command(BaseCommand): help = 'Initialize or update Payment Modes' @@ -8,6 +8,7 @@ class Command(BaseCommand): self.create_or_update_payment_modes() def create_or_update_payment_modes(self): + establishment = Establishment.objects.get(name="N3WT") for fee_type in FeeType.choices: fee_type_value = fee_type[0] @@ -18,7 +19,8 @@ class Command(BaseCommand): mode=mode_value, type=fee_type_value, defaults={ - 'is_active': False + 'is_active': False, + 'establishment': establishment } ) diff --git a/Back-End/School/management/commands/init_payment_plans.py b/Back-End/School/management/commands/init_payment_plans.py index d2557d9..919e902 100644 --- a/Back-End/School/management/commands/init_payment_plans.py +++ b/Back-End/School/management/commands/init_payment_plans.py @@ -1,7 +1,7 @@ from django.core.management.base import BaseCommand from django.utils import timezone from dateutil.relativedelta import relativedelta -from School.models import PaymentPlan, PaymentPlanType, FeeType +from School.models import PaymentPlan, PaymentPlanType, FeeType, Establishment class Command(BaseCommand): help = 'Initialize or update Payment Plans' @@ -11,6 +11,7 @@ class Command(BaseCommand): def create_or_update_payment_plans(self): current_date = timezone.now().date() + establishment = Establishment.objects.get(name="N3WT") for fee_type in FeeType.choices: fee_type_value = fee_type[0] @@ -21,7 +22,8 @@ class Command(BaseCommand): type=fee_type_value, defaults={ 'due_dates': [current_date + relativedelta(months=1)], - 'is_active': True + 'is_active': True, + 'establishment': establishment } ) @@ -31,7 +33,8 @@ class Command(BaseCommand): type=fee_type_value, defaults={ 'due_dates': [current_date + relativedelta(months=1+4*i) for i in range(3)], - 'is_active': False + 'is_active': False, + 'establishment': establishment } ) @@ -41,7 +44,8 @@ class Command(BaseCommand): type=fee_type_value, defaults={ 'due_dates': [current_date + relativedelta(months=1+i) for i in range(10)], - 'is_active': False + 'is_active': False, + 'establishment': establishment } ) @@ -51,7 +55,8 @@ class Command(BaseCommand): type=fee_type_value, defaults={ 'due_dates': [current_date + relativedelta(months=1+i) for i in range(12)], - 'is_active': False + 'is_active': False, + 'establishment': establishment } ) diff --git a/Back-End/School/management/commands/init_school_configuration.py b/Back-End/School/management/commands/init_school_configuration.py index 4402808..4a5a298 100644 --- a/Back-End/School/management/commands/init_school_configuration.py +++ b/Back-End/School/management/commands/init_school_configuration.py @@ -1,6 +1,6 @@ from django.core.management.base import BaseCommand from Auth.models import Profile -from School.models import Speciality, Teacher, SchoolClass +from School.models import Speciality, Teacher, SchoolClass, Establishment class Command(BaseCommand): help = 'Initialize or update Fees and Discounts' @@ -108,6 +108,7 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS('Teachers initialized or updated successfully')) def create_or_update_schoolClasses(self): + establishment = Establishment.objects.get(name="N3WT") school_classes_data = [ { "atmosphere_name": "Classe A", @@ -119,7 +120,8 @@ class Command(BaseCommand): "type": 1, "time_range": ["08:30", "17:30"], "opening_days": [1, 2, 4, 5], - "teachers": [2] # ID of Severus Rogue + "teachers": [2], # ID of Severus Rogue + "establishment": establishment }, { "atmosphere_name": "Classe B", @@ -131,7 +133,8 @@ class Command(BaseCommand): "type": 1, "time_range": ["08:30", "17:30"], "opening_days": [1, 2, 4, 5], - "teachers": [3] # ID of Minerva McGonagall + "teachers": [3], # ID of Minerva McGonagall + "establishment": establishment }, { "atmosphere_name": "Classe C", @@ -143,7 +146,8 @@ class Command(BaseCommand): "type": 1, "time_range": ["08:30", "17:30"], "opening_days": [1, 2, 4, 5], - "teachers": [4] # ID of Pomona Chourave + "teachers": [4], # ID of Pomona Chourave + "establishment": establishment } ] diff --git a/Back-End/School/management/commands/init_school_fees.py b/Back-End/School/management/commands/init_school_fees.py index 9c36f6e..c26c79b 100644 --- a/Back-End/School/management/commands/init_school_fees.py +++ b/Back-End/School/management/commands/init_school_fees.py @@ -1,6 +1,6 @@ from django.core.management.base import BaseCommand from Auth.models import Profile -from School.models import Fee, Discount, FeeType, DiscountType +from School.models import Fee, Discount, FeeType, DiscountType, Establishment class Command(BaseCommand): help = 'Initialize or update Fees and Discounts' @@ -10,41 +10,47 @@ class Command(BaseCommand): self.create_or_update_discounts() def create_or_update_fees(self): + establishment = Establishment.objects.get(name="N3WT") fees_data = [ { "name": "Frais d'inscription", "base_amount": "150.00", "description": "Montant de base", "is_active": True, - "type": FeeType.REGISTRATION_FEE + "type": FeeType.REGISTRATION_FEE, + "establishment": establishment }, { "name": "Matériel", "base_amount": "85.00", "description": "Livres / jouets", "is_active": True, - "type": FeeType.REGISTRATION_FEE + "type": FeeType.REGISTRATION_FEE, + "establishment": establishment }, { "name": "Sorties périscolaires", "base_amount": "120.00", "description": "Sorties", "is_active": True, - "type": FeeType.REGISTRATION_FEE + "type": FeeType.REGISTRATION_FEE, + "establishment": establishment }, { "name": "Les colibris", "base_amount": "4500.00", "description": "TPS / PS / MS / GS", "is_active": True, - "type": FeeType.TUITION_FEE + "type": FeeType.TUITION_FEE, + "establishment": establishment }, { "name": "Les butterflies", "base_amount": "5000.00", "description": "CP / CE1 / CE2 / CM1 / CM2", "is_active": True, - "type": FeeType.TUITION_FEE + "type": FeeType.TUITION_FEE, + "establishment": establishment } ] @@ -58,20 +64,23 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS('Fees initialized or updated successfully')) def create_or_update_discounts(self): + establishment = Establishment.objects.get(name="N3WT") discounts_data = [ { "name": "Parrainage", "amount": "10.00", "description": "Réduction pour parrainage", "discount_type": DiscountType.PERCENT, - "type": FeeType.TUITION_FEE + "type": FeeType.TUITION_FEE, + "establishment": establishment }, { "name": "Réinscription", "amount": "100.00", "description": "Réduction pour Réinscription", "discount_type": DiscountType.PERCENT, - "type": FeeType.REGISTRATION_FEE + "type": FeeType.REGISTRATION_FEE, + "establishment": establishment } ] diff --git a/Back-End/School/models.py b/Back-End/School/models.py index 345a8e8..9615db9 100644 --- a/Back-End/School/models.py +++ b/Back-End/School/models.py @@ -6,7 +6,6 @@ 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)'), (2, 'Petite Section (PS)'), @@ -19,6 +18,23 @@ LEVEL_CHOICES = [ (9, 'Cours Moyen 2 (CM2)') ] +class StructureType(models.IntegerChoices): + MATERNELLE = 1, _('Maternelle') + PRIMAIRE = 2, _('Primaire') + SECONDAIRE = 3, _('Secondaire') + +class Establishment(models.Model): + name = models.CharField(max_length=255, unique=True) + address = models.CharField(max_length=255) + total_capacity = models.IntegerField() + establishment_type = ArrayField(models.IntegerField(choices=StructureType.choices)) + licence_code = models.CharField(max_length=100, blank=True) + is_active = models.BooleanField(default=True) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return self.name + class Speciality(models.Model): name = models.CharField(max_length=100) updated_date = models.DateTimeField(auto_now=True) @@ -56,6 +72,7 @@ class SchoolClass(models.Model): type = models.IntegerField(choices=PLANNING_TYPE_CHOICES, default=1) time_range = models.JSONField(default=list) opening_days = ArrayField(models.IntegerField(), default=list) + establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='school_classes') def __str__(self): return self.atmosphere_name @@ -95,6 +112,7 @@ class Discount(models.Model): discount_type = models.IntegerField(choices=DiscountType.choices, default=DiscountType.CURRENCY) type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) updated_at = models.DateTimeField(auto_now=True) + establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='discounts') def __str__(self): return self.name @@ -106,6 +124,7 @@ class Fee(models.Model): is_active = models.BooleanField(default=True) updated_at = models.DateTimeField(auto_now=True) type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) + establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='fees') def __str__(self): return self.name @@ -115,6 +134,7 @@ class PaymentPlan(models.Model): due_dates = ArrayField(models.DateField(), blank=True) type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) is_active = models.BooleanField(default=False) + establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='payment_plans') def __str__(self): return f"{self.get_frequency_display()} - {self.get_type_display()}" @@ -123,6 +143,7 @@ class PaymentMode(models.Model): mode = models.IntegerField(choices=PaymentModeType.choices, default=PaymentModeType.SEPA) type = models.IntegerField(choices=FeeType.choices, default=FeeType.REGISTRATION_FEE) is_active = models.BooleanField(default=False) + establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='payment_modes') def __str__(self): return f"{self.get_mode_display()} - {self.get_type_display()}" diff --git a/Back-End/School/serializers.py b/Back-End/School/serializers.py index 44e1509..f91d430 100644 --- a/Back-End/School/serializers.py +++ b/Back-End/School/serializers.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES, Discount, Fee, PaymentPlan, PaymentMode +from .models import Teacher, Speciality, SchoolClass, Planning, LEVEL_CHOICES, Discount, Fee, PaymentPlan, PaymentMode, Establishment from Auth.models import Profile from N3wtSchool import settings, bdd from django.utils import timezone @@ -200,4 +200,9 @@ class PaymentPlanSerializer(serializers.ModelSerializer): class PaymentModeSerializer(serializers.ModelSerializer): class Meta: model = PaymentMode + fields = '__all__' + +class EstablishmentSerializer(serializers.ModelSerializer): + class Meta: + model = Establishment fields = '__all__' \ No newline at end of file diff --git a/Back-End/School/urls.py b/Back-End/School/urls.py index e0938e5..5757ef1 100644 --- a/Back-End/School/urls.py +++ b/Back-End/School/urls.py @@ -16,7 +16,9 @@ from School.views import ( PaymentPlansView, PaymentPlanView, PaymentModesView, - PaymentModeView + PaymentModeView, + EstablishmentsView, + EstablishmentView ) urlpatterns = [ @@ -51,4 +53,9 @@ urlpatterns = [ re_path(r'^paymentModes/(?P<_filter>[a-zA-z]+)$', PaymentModesView.as_view(), name="paymentModes"), re_path(r'^paymentMode$', PaymentModeView.as_view(), name="paymentMode"), re_path(r'^paymentMode/([0-9]+)$', PaymentModeView.as_view(), name="paymentMode"), + + re_path(r'^establishments$', EstablishmentsView.as_view(), name="establishments"), + re_path(r'^establishment$', EstablishmentView.as_view(), name='establishment'), + re_path(r'^establishment/([0-9]+)$', EstablishmentView.as_view(), name='establishment') + ] \ No newline at end of file diff --git a/Back-End/School/views.py b/Back-End/School/views.py index fdb2657..a4993cf 100644 --- a/Back-End/School/views.py +++ b/Back-End/School/views.py @@ -5,8 +5,8 @@ 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, Discount, Fee, PaymentPlan, PaymentMode -from .serializers import TeacherSerializer, SpecialitySerializer, SchoolClassSerializer, PlanningSerializer, DiscountSerializer, FeeSerializer, PaymentPlanSerializer, PaymentModeSerializer +from .models import Teacher, Speciality, SchoolClass, Planning, Discount, Fee, PaymentPlan, PaymentMode, Establishment +from .serializers import TeacherSerializer, SpecialitySerializer, SchoolClassSerializer, PlanningSerializer, DiscountSerializer, FeeSerializer, PaymentPlanSerializer, PaymentModeSerializer, EstablishmentSerializer from N3wtSchool import bdd from N3wtSchool.bdd import delete_object, getAllObjects, getObject @@ -400,4 +400,46 @@ class PaymentModeView(APIView): if payment_mode_serializer.is_valid(): payment_mode_serializer.save() return JsonResponse(payment_mode_serializer.data, safe=False) - return JsonResponse(payment_mode_serializer.errors, safe=False, status=400) \ No newline at end of file + return JsonResponse(payment_mode_serializer.errors, safe=False, status=400) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class EstablishmentsView(APIView): + def get(self, request): + establishments=getAllObjects(Establishment) + establishments_serializer=EstablishmentSerializer(establishments, many=True) + return JsonResponse(establishments_serializer.data, safe=False, status=200) + +@method_decorator(csrf_protect, name='dispatch') +@method_decorator(ensure_csrf_cookie, name='dispatch') +class EstablishmentView(APIView): + def get(self, request, _id): + try: + establishment = Establishment.objects.get(id=_id) + establishment_serializer = EstablishmentSerializer(establishment) + return JsonResponse(establishment_serializer.data, safe=False) + except Establishment.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=404) + + def post(self, request): + establishment_data = JSONParser().parse(request) + establishment_serializer = EstablishmentSerializer(data=establishment_data) + if establishment_serializer.is_valid(): + establishment_serializer.save() + return JsonResponse(establishment_serializer.data, safe=False, status=201) + return JsonResponse(establishment_serializer.errors, safe=False, status=400) + + def put(self, request, _id): + establishment_data = JSONParser().parse(request) + try: + establishment = Establishment.objects.get(id=_id) + except Establishment.DoesNotExist: + return JsonResponse({'error': 'No object found'}, status=404) + establishment_serializer = EstablishmentSerializer(establishment, data=establishment_data, partial=True) + if establishment_serializer.is_valid(): + establishment_serializer.save() + return JsonResponse(establishment_serializer.data, safe=False) + return JsonResponse(establishment_serializer.errors, safe=False, status=400) + + def delete(self, request, _id): + return delete_object(Establishment, _id) \ No newline at end of file diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index a2892c9..9272b15 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -4,7 +4,7 @@ from django.conf import settings from django.utils.translation import gettext_lazy as _ from Auth.models import Profile -from School.models import SchoolClass, Fee, Discount +from School.models import SchoolClass, Fee, Discount, Establishment from datetime import datetime @@ -209,6 +209,8 @@ class RegistrationForm(models.Model): null=True, blank=True) + establishment = models.ForeignKey(Establishment, on_delete=models.CASCADE, related_name='register_forms') + def __str__(self): return "RF_" + self.student.last_name + "_" + self.student.first_name diff --git a/Back-End/start.py b/Back-End/start.py index 7ca950e..28d60f4 100644 --- a/Back-End/start.py +++ b/Back-End/start.py @@ -20,14 +20,15 @@ commands = [ ["python", "manage.py", "makemigrations", "GestionMessagerie", "--noinput"], ["python", "manage.py", "makemigrations", "Auth", "--noinput"], ["python", "manage.py", "makemigrations", "School", "--noinput"], - ["python", "manage.py", "migrate", "--noinput"], - ["python", "manage.py", "init_payment_plans"], - ["python", "manage.py", "init_payment_modes"] + ["python", "manage.py", "migrate", "--noinput"] ] test_commands = [ + ["python", "manage.py", "init_establishment"], ["python", "manage.py", "init_school_configuration"], - ["python", "manage.py", "init_school_fees"] + ["python", "manage.py", "init_school_fees"], + ["python", "manage.py", "init_payment_plans"], + ["python", "manage.py", "init_payment_modes"] ] for command in commands: diff --git a/Front-End/src/app/[locale]/admin/layout.js b/Front-End/src/app/[locale]/admin/layout.js index fe928b4..3f86c1c 100644 --- a/Front-End/src/app/[locale]/admin/layout.js +++ b/Front-End/src/app/[locale]/admin/layout.js @@ -1,6 +1,6 @@ 'use client' // src/components/Layout.js -import React from 'react'; +import React, { useState, useEffect } from 'react'; import Sidebar from '@/components/Sidebar'; import { usePathname } from 'next/navigation'; import {useTranslations} from 'next-intl'; @@ -25,6 +25,7 @@ import { } from '@/utils/Url'; import { disconnect } from '@/app/lib/authAction'; +import { fetchEstablishment } from '@/app/lib/schoolAction'; export default function Layout({ children, @@ -40,6 +41,9 @@ export default function Layout({ "settings": { "id": "settings", "name": t('settings'), "url": FE_ADMIN_SETTINGS_URL, "icon": Settings } }; + const [establishment, setEstablishment] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const pathname = usePathname(); const currentPage = pathname.split('/').pop(); @@ -57,38 +61,50 @@ export default function Layout({ }, ]; + useEffect(() => { + setIsLoading(true); + fetchEstablishment() + .then(data => { + setEstablishment(data); + }) + .catch(error => console.error('Error fetching establishment : ', error)) + .finally(() => setIsLoading(false)); + }, []); + return ( <> -
- -
- {/* Header - h-16 = 64px */} -
-
{headerTitle}
- } - items={dropdownItems} - buttonClassName="" - menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded shadow-lg" - /> -
- {/* Main Content */} -
- {/* Content avec scroll si nécessaire */} -
- {children} -
- {/* Footer - h-16 = 64px */} -
-
- © {new Date().getFullYear()} N3WT-INNOV Tous droits réservés. -
{softwareName} - {softwareVersion}
+ {!isLoading && ( +
+ +
+ {/* Header - h-16 = 64px */} +
+
{headerTitle}
+ } + items={dropdownItems} + buttonClassName="" + menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded shadow-lg" + /> +
+ {/* Main Content */} +
+ {/* Content avec scroll si nécessaire */} +
+ {children}
- -
+ {/* Footer - h-16 = 64px */} +
+
+ © {new Date().getFullYear()} N3WT-INNOV Tous droits réservés. +
{softwareName} - {softwareVersion}
+
+ +
+
- + )} ); } diff --git a/Front-End/src/app/lib/schoolAction.js b/Front-End/src/app/lib/schoolAction.js index 0d94378..c4091bc 100644 --- a/Front-End/src/app/lib/schoolAction.js +++ b/Front-End/src/app/lib/schoolAction.js @@ -6,7 +6,9 @@ import { BE_SCHOOL_FEES_URL, BE_SCHOOL_DISCOUNTS_URL, BE_SCHOOL_PAYMENT_PLANS_URL, - BE_SCHOOL_PAYMENT_MODES_URL + BE_SCHOOL_PAYMENT_MODES_URL, + BE_SCHOOL_ESTABLISHMENT_URL, + ESTABLISHMENT_ID } from '@/utils/Url'; const requestResponseHandler = async (response) => { @@ -82,6 +84,11 @@ export const fetchTuitionPaymentModes = () => { .then(requestResponseHandler) } +export const fetchEstablishment = () => { + return fetch(`${BE_SCHOOL_ESTABLISHMENT_URL}/${ESTABLISHMENT_ID}`) + .then(requestResponseHandler) +} + export const createDatas = (url, newData, csrfToken) => { return fetch(url, { method: 'POST', diff --git a/Front-End/src/components/Sidebar.js b/Front-End/src/components/Sidebar.js index f6aaa9d..8140f76 100644 --- a/Front-End/src/components/Sidebar.js +++ b/Front-End/src/components/Sidebar.js @@ -14,7 +14,7 @@ const SidebarItem = ({ icon: Icon, text, active, url, onClick }) => ( ); -function Sidebar({ currentPage, items }) { +function Sidebar({ establishment, currentPage, items }) { const router = useRouter(); const [selectedItem, setSelectedItem] = useState(currentPage); @@ -31,7 +31,7 @@ function Sidebar({ currentPage, items }) { {/* Sidebar */}
-
Ecole NEWT
+
{establishment?.name}