feat: Permettre la création et modification de l'emploi du temps des classes
L'administration a besoin de construire et maintenir les emplois du temps hebdomadaires pour chaque classe, en s'assurant que les enseignants ne sont pas en conflit (même créneau, classes différentes) et que les affectations enseignant-matière-classe sont respectées. Cette implémentation couvre le CRUD complet des créneaux (ScheduleSlot), la détection de conflits (classe, enseignant, salle) avec possibilité de forcer, la validation des affectations côté serveur (AC2), l'intégration calendrier pour les jours bloqués, une vue mobile-first avec onglets jour par jour, et le drag-and-drop pour réorganiser les créneaux sur desktop.
This commit is contained in:
23
backend/src/Scolarite/Domain/Service/ScheduleConflict.php
Normal file
23
backend/src/Scolarite/Domain/Service/ScheduleConflict.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Service;
|
||||
|
||||
use App\Scolarite\Domain\Model\Schedule\ScheduleSlot;
|
||||
|
||||
/**
|
||||
* Représente un conflit détecté entre deux créneaux.
|
||||
*/
|
||||
final readonly class ScheduleConflict
|
||||
{
|
||||
/**
|
||||
* @param 'class'|'teacher'|'room' $type
|
||||
*/
|
||||
public function __construct(
|
||||
public string $type,
|
||||
public ScheduleSlot $conflictingSlot,
|
||||
public string $description,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Service;
|
||||
|
||||
use App\Scolarite\Domain\Model\Schedule\ScheduleSlot;
|
||||
use App\Scolarite\Domain\Model\Schedule\ScheduleSlotId;
|
||||
use App\Scolarite\Domain\Repository\ScheduleSlotRepository;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
/**
|
||||
* Détecte les conflits de créneaux dans l'emploi du temps.
|
||||
*
|
||||
* Vérifie les conflits enseignant (même enseignant, même créneau horaire)
|
||||
* et les conflits de salle (même salle, même créneau horaire).
|
||||
*/
|
||||
final readonly class ScheduleConflictDetector
|
||||
{
|
||||
public function __construct(
|
||||
private ScheduleSlotRepository $repository,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<ScheduleConflict>
|
||||
*/
|
||||
public function detectConflicts(
|
||||
ScheduleSlot $slot,
|
||||
TenantId $tenantId,
|
||||
?ScheduleSlotId $excludeId = null,
|
||||
): array {
|
||||
$conflicts = [];
|
||||
|
||||
$classConflicts = $this->repository->findOverlappingForClass(
|
||||
$slot->classId,
|
||||
$slot->dayOfWeek,
|
||||
$slot->timeSlot->startTime,
|
||||
$slot->timeSlot->endTime,
|
||||
$tenantId,
|
||||
$excludeId,
|
||||
);
|
||||
|
||||
foreach ($classConflicts as $conflicting) {
|
||||
$conflicts[] = new ScheduleConflict(
|
||||
type: 'class',
|
||||
conflictingSlot: $conflicting,
|
||||
description: "La classe a déjà un cours le {$conflicting->dayOfWeek->label()} de {$conflicting->timeSlot->startTime} à {$conflicting->timeSlot->endTime}.",
|
||||
);
|
||||
}
|
||||
|
||||
$teacherConflicts = $this->repository->findOverlappingForTeacher(
|
||||
$slot->teacherId,
|
||||
$slot->dayOfWeek,
|
||||
$slot->timeSlot->startTime,
|
||||
$slot->timeSlot->endTime,
|
||||
$tenantId,
|
||||
$excludeId,
|
||||
);
|
||||
|
||||
foreach ($teacherConflicts as $conflicting) {
|
||||
$conflicts[] = new ScheduleConflict(
|
||||
type: 'teacher',
|
||||
conflictingSlot: $conflicting,
|
||||
description: "L'enseignant est déjà occupé le {$conflicting->dayOfWeek->label()} de {$conflicting->timeSlot->startTime} à {$conflicting->timeSlot->endTime}.",
|
||||
);
|
||||
}
|
||||
|
||||
if ($slot->room !== null) {
|
||||
$roomConflicts = $this->repository->findOverlappingForRoom(
|
||||
$slot->room,
|
||||
$slot->dayOfWeek,
|
||||
$slot->timeSlot->startTime,
|
||||
$slot->timeSlot->endTime,
|
||||
$tenantId,
|
||||
$excludeId,
|
||||
);
|
||||
|
||||
foreach ($roomConflicts as $conflicting) {
|
||||
$conflicts[] = new ScheduleConflict(
|
||||
type: 'room',
|
||||
conflictingSlot: $conflicting,
|
||||
description: "La salle {$slot->room} est déjà occupée le {$conflicting->dayOfWeek->label()} de {$conflicting->timeSlot->startTime} à {$conflicting->timeSlot->endTime}.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $conflicts;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user