diff --git a/backend/src/Administration/Application/Command/ChangeStudentClass/ChangeStudentClassHandler.php b/backend/src/Administration/Application/Command/ChangeStudentClass/ChangeStudentClassHandler.php index f881069..af5e21d 100644 --- a/backend/src/Administration/Application/Command/ChangeStudentClass/ChangeStudentClassHandler.php +++ b/backend/src/Administration/Application/Command/ChangeStudentClass/ChangeStudentClassHandler.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\Administration\Application\Command\ChangeStudentClass; -use App\Administration\Domain\Exception\AffectationEleveNonTrouveeException; use App\Administration\Domain\Exception\ClasseNotFoundException; use App\Administration\Domain\Model\ClassAssignment\ClassAssignment; use App\Administration\Domain\Model\SchoolClass\AcademicYearId; @@ -44,15 +43,23 @@ final readonly class ChangeStudentClassHandler throw ClasseNotFoundException::withId($newClassId); } - // Trouver l'affectation existante + $now = $this->clock->now(); + + // Trouver l'affectation existante ou en créer une nouvelle $assignment = $this->classAssignmentRepository->findByStudent($studentId, $academicYearId, $tenantId); - if ($assignment === null) { - throw AffectationEleveNonTrouveeException::pourEleve($studentId); + if ($assignment !== null) { + $assignment->changerClasse($newClassId, $now); + } else { + $assignment = ClassAssignment::affecter( + tenantId: $tenantId, + studentId: $studentId, + classId: $newClassId, + academicYearId: $academicYearId, + assignedAt: $now, + ); } - $assignment->changerClasse($newClassId, $this->clock->now()); - $this->classAssignmentRepository->save($assignment); return $assignment; diff --git a/backend/src/Administration/Domain/Exception/AffectationEleveNonTrouveeException.php b/backend/src/Administration/Domain/Exception/AffectationEleveNonTrouveeException.php deleted file mode 100644 index 4ff90c5..0000000 --- a/backend/src/Administration/Domain/Exception/AffectationEleveNonTrouveeException.php +++ /dev/null @@ -1,21 +0,0 @@ -classLevel = $newClass->level?->value; return $data; - } catch (AffectationEleveNonTrouveeException) { - throw new NotFoundHttpException('Élève non trouvé.'); } catch (ClasseNotFoundException $e) { throw new NotFoundHttpException($e->getMessage()); } diff --git a/backend/tests/Unit/Administration/Application/Command/ChangeStudentClass/ChangeStudentClassHandlerTest.php b/backend/tests/Unit/Administration/Application/Command/ChangeStudentClass/ChangeStudentClassHandlerTest.php index 3a7216f..509a776 100644 --- a/backend/tests/Unit/Administration/Application/Command/ChangeStudentClass/ChangeStudentClassHandlerTest.php +++ b/backend/tests/Unit/Administration/Application/Command/ChangeStudentClass/ChangeStudentClassHandlerTest.php @@ -6,7 +6,6 @@ namespace App\Tests\Unit\Administration\Application\Command\ChangeStudentClass; use App\Administration\Application\Command\ChangeStudentClass\ChangeStudentClassCommand; use App\Administration\Application\Command\ChangeStudentClass\ChangeStudentClassHandler; -use App\Administration\Domain\Exception\AffectationEleveNonTrouveeException; use App\Administration\Domain\Exception\ClasseNotFoundException; use App\Administration\Domain\Model\ClassAssignment\ClassAssignment; use App\Administration\Domain\Model\SchoolClass\AcademicYearId; @@ -74,13 +73,17 @@ final class ChangeStudentClassHandlerTest extends TestCase } #[Test] - public function itThrowsWhenAssignmentNotFound(): void + public function itCreatesAssignmentWhenNoneExists(): void { $handler = $this->createHandler(); - $command = $this->createCommand(studentId: '550e8400-e29b-41d4-a716-446655440070'); + $newStudentId = '550e8400-e29b-41d4-a716-446655440070'; + $command = $this->createCommand(studentId: $newStudentId); - $this->expectException(AffectationEleveNonTrouveeException::class); - $handler($command); + $assignment = $handler($command); + + self::assertTrue($assignment->studentId->equals(UserId::fromString($newStudentId))); + self::assertTrue($assignment->classId->equals(ClassId::fromString(self::NEW_CLASS_ID))); + self::assertTrue($assignment->academicYearId->equals(AcademicYearId::fromString(self::ACADEMIC_YEAR_ID))); } #[Test] diff --git a/frontend/src/routes/admin/students/+page.svelte b/frontend/src/routes/admin/students/+page.svelte index d31c0a2..676bccd 100644 --- a/frontend/src/routes/admin/students/+page.svelte +++ b/frontend/src/routes/admin/students/+page.svelte @@ -62,6 +62,7 @@ let changeClassTarget = $state(null); let newClassForChange = $state(''); let isChangingClass = $state(false); + let changeClassError = $state(null); // Classes grouped by level for optgroup let classesByLevel = $derived.by(() => { @@ -300,7 +301,7 @@ changeClassTarget = student; newClassForChange = ''; showChangeClassModal = true; - error = null; + changeClassError = null; } function closeChangeClassModal() { @@ -338,7 +339,7 @@ successMessage = `${changeClassTarget.firstName} ${changeClassTarget.lastName} a été transféré vers ${targetClass?.name ?? 'la nouvelle classe'}.`; closeChangeClassModal(); } catch (e) { - error = e instanceof Error ? e.message : 'Erreur lors du changement de classe'; + changeClassError = e instanceof Error ? e.message : 'Erreur lors du changement de classe'; } finally { isChangingClass = false; } @@ -720,6 +721,13 @@ + {#if changeClassError} + + {/if} + {#if newClassForChange} {@const targetClass = classes.find((c) => c.id === newClassForChange)}
@@ -1244,6 +1252,10 @@ justify-content: flex-end; } + .modal-error { + margin-bottom: 0; + } + .change-confirm-info { padding: 0.75rem 1rem; background: #eff6ff;