Files
Classeo/backend/src/Scolarite/Domain/Model/Evaluation/Evaluation.php
Mathias STRASSER b70d5ec2ad
Some checks failed
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Naming Conventions (push) Has been cancelled
CI / Build Check (push) Has been cancelled
feat: Permettre à l'enseignant de saisir les notes dans une grille inline
L'enseignant avait besoin d'un moyen rapide de saisir les notes après
une évaluation. La grille inline permet de compléter 30 élèves en moins
de 3 minutes grâce à la navigation clavier (Tab/Enter/Shift+Tab),
la validation temps réel, l'auto-save debounced (500ms) et les
raccourcis /abs et /disp pour marquer absents/dispensés.

Les notes restent en brouillon jusqu'à publication explicite (avec
confirmation modale). Une fois publiées, les élèves les voient
immédiatement ; les parents après un délai de 24h (VisibiliteNotesPolicy).
Le mode offline stocke les notes en IndexedDB et synchronise
automatiquement au retour de la connexion.

Chaque modification est auditée dans grade_events via un event
subscriber qui écoute NoteSaisie/NoteModifiee sur le bus d'événements.
2026-03-29 10:02:03 +02:00

198 lines
6.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Scolarite\Domain\Model\Evaluation;
use App\Administration\Domain\Model\SchoolClass\ClassId;
use App\Administration\Domain\Model\Subject\SubjectId;
use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Domain\Event\EvaluationCreee;
use App\Scolarite\Domain\Event\EvaluationModifiee;
use App\Scolarite\Domain\Event\EvaluationSupprimee;
use App\Scolarite\Domain\Event\NotesPubliees;
use App\Scolarite\Domain\Exception\BaremeNonModifiableException;
use App\Scolarite\Domain\Exception\EvaluationDejaSupprimeeException;
use App\Scolarite\Domain\Exception\NotesDejaPublieesException;
use App\Shared\Domain\AggregateRoot;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
final class Evaluation extends AggregateRoot
{
public private(set) DateTimeImmutable $updatedAt;
public private(set) ?DateTimeImmutable $gradesPublishedAt = null;
private function __construct(
public private(set) EvaluationId $id,
public private(set) TenantId $tenantId,
public private(set) ClassId $classId,
public private(set) SubjectId $subjectId,
public private(set) UserId $teacherId,
public private(set) string $title,
public private(set) ?string $description,
public private(set) DateTimeImmutable $evaluationDate,
public private(set) GradeScale $gradeScale,
public private(set) Coefficient $coefficient,
public private(set) EvaluationStatus $status,
public private(set) DateTimeImmutable $createdAt,
) {
$this->updatedAt = $createdAt;
}
public static function creer(
TenantId $tenantId,
ClassId $classId,
SubjectId $subjectId,
UserId $teacherId,
string $title,
?string $description,
DateTimeImmutable $evaluationDate,
GradeScale $gradeScale,
Coefficient $coefficient,
DateTimeImmutable $now,
): self {
$evaluation = new self(
id: EvaluationId::generate(),
tenantId: $tenantId,
classId: $classId,
subjectId: $subjectId,
teacherId: $teacherId,
title: $title,
description: $description,
evaluationDate: $evaluationDate,
gradeScale: $gradeScale,
coefficient: $coefficient,
status: EvaluationStatus::PUBLISHED,
createdAt: $now,
);
$evaluation->recordEvent(new EvaluationCreee(
evaluationId: $evaluation->id,
classId: (string) $classId,
subjectId: (string) $subjectId,
teacherId: (string) $teacherId,
title: $title,
evaluationDate: $evaluationDate,
occurredOn: $now,
));
return $evaluation;
}
public function modifier(
string $title,
?string $description,
Coefficient $coefficient,
DateTimeImmutable $evaluationDate,
?GradeScale $gradeScale,
bool $hasGrades,
DateTimeImmutable $now,
): void {
if ($this->status === EvaluationStatus::DELETED) {
throw EvaluationDejaSupprimeeException::withId($this->id);
}
if ($gradeScale !== null && !$this->gradeScale->equals($gradeScale) && $hasGrades) {
throw BaremeNonModifiableException::carNotesExistantes($this->id);
}
$this->title = $title;
$this->description = $description;
$this->coefficient = $coefficient;
$this->evaluationDate = $evaluationDate;
if ($gradeScale !== null && !$hasGrades) {
$this->gradeScale = $gradeScale;
}
$this->updatedAt = $now;
$this->recordEvent(new EvaluationModifiee(
evaluationId: $this->id,
title: $title,
evaluationDate: $evaluationDate,
occurredOn: $now,
));
}
public function publierNotes(DateTimeImmutable $now): void
{
if ($this->status === EvaluationStatus::DELETED) {
throw EvaluationDejaSupprimeeException::withId($this->id);
}
if ($this->gradesPublishedAt !== null) {
throw NotesDejaPublieesException::pourEvaluation($this->id);
}
$this->gradesPublishedAt = $now;
$this->updatedAt = $now;
$this->recordEvent(new NotesPubliees(
evaluationId: $this->id,
occurredOn: $now,
));
}
public function notesPubliees(): bool
{
return $this->gradesPublishedAt !== null;
}
public function supprimer(DateTimeImmutable $now): void
{
if ($this->status === EvaluationStatus::DELETED) {
throw EvaluationDejaSupprimeeException::withId($this->id);
}
$this->status = EvaluationStatus::DELETED;
$this->updatedAt = $now;
$this->recordEvent(new EvaluationSupprimee(
evaluationId: $this->id,
occurredOn: $now,
));
}
/**
* @internal Pour usage Infrastructure uniquement
*/
public static function reconstitute(
EvaluationId $id,
TenantId $tenantId,
ClassId $classId,
SubjectId $subjectId,
UserId $teacherId,
string $title,
?string $description,
DateTimeImmutable $evaluationDate,
GradeScale $gradeScale,
Coefficient $coefficient,
EvaluationStatus $status,
DateTimeImmutable $createdAt,
DateTimeImmutable $updatedAt,
?DateTimeImmutable $gradesPublishedAt = null,
): self {
$evaluation = new self(
id: $id,
tenantId: $tenantId,
classId: $classId,
subjectId: $subjectId,
teacherId: $teacherId,
title: $title,
description: $description,
evaluationDate: $evaluationDate,
gradeScale: $gradeScale,
coefficient: $coefficient,
status: $status,
createdAt: $createdAt,
);
$evaluation->updatedAt = $updatedAt;
$evaluation->gradesPublishedAt = $gradesPublishedAt;
return $evaluation;
}
}