feat: Permettre aux enseignants de contourner les règles de devoirs avec justification
Akeneo permet de configurer des règles de devoirs en mode Hard qui bloquent totalement la création. Or certains cas légitimes (sorties scolaires, événements exceptionnels) nécessitent de passer outre ces règles. Sans mécanisme d'exception, l'enseignant est bloqué et doit contacter manuellement la direction. Cette implémentation ajoute un flux complet d'exception : l'enseignant justifie sa demande (min 20 caractères), le devoir est créé immédiatement, et la direction est notifiée par email. Le handler vérifie côté serveur que les règles sont réellement bloquantes avant d'accepter l'exception, empêchant toute fabrication de fausses exceptions via l'API. La direction dispose d'un rapport filtrable par période, enseignant et type de règle.
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetHomeworkExceptionsReport;
|
||||
|
||||
use App\Administration\Domain\Model\User\User;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Administration\Domain\Repository\UserRepository;
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkRuleException;
|
||||
use App\Scolarite\Domain\Repository\HomeworkRepository;
|
||||
use App\Scolarite\Domain\Repository\HomeworkRuleExceptionRepository;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
use function array_filter;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
use function str_contains;
|
||||
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
|
||||
#[AsMessageHandler(bus: 'query.bus')]
|
||||
final readonly class GetHomeworkExceptionsReportHandler
|
||||
{
|
||||
public function __construct(
|
||||
private HomeworkRuleExceptionRepository $exceptionRepository,
|
||||
private HomeworkRepository $homeworkRepository,
|
||||
private UserRepository $userRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
/** @return array<HomeworkExceptionDto> */
|
||||
public function __invoke(GetHomeworkExceptionsReportQuery $query): array
|
||||
{
|
||||
$tenantId = TenantId::fromString($query->tenantId);
|
||||
$exceptions = $this->exceptionRepository->findByTenant($tenantId);
|
||||
|
||||
$startDate = $query->startDate !== null ? new DateTimeImmutable($query->startDate) : null;
|
||||
$endDate = $query->endDate !== null ? new DateTimeImmutable($query->endDate . ' 23:59:59') : null;
|
||||
|
||||
$filtered = array_filter(
|
||||
$exceptions,
|
||||
fn (HomeworkRuleException $e): bool => $this->matchesFilters(
|
||||
$e,
|
||||
$startDate,
|
||||
$endDate,
|
||||
$query->teacherId,
|
||||
$query->ruleType,
|
||||
),
|
||||
);
|
||||
|
||||
if ($filtered === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$homeworkCache = $this->preloadHomeworks($filtered, $tenantId);
|
||||
$teacherCache = $this->preloadTeachers($filtered);
|
||||
|
||||
$results = [];
|
||||
|
||||
foreach ($filtered as $exception) {
|
||||
$hwId = (string) $exception->homeworkId;
|
||||
$teacherId = (string) $exception->createdBy;
|
||||
|
||||
$homework = $homeworkCache[$hwId] ?? null;
|
||||
$teacher = $teacherCache[$teacherId] ?? null;
|
||||
|
||||
$results[] = new HomeworkExceptionDto(
|
||||
id: (string) $exception->id,
|
||||
homeworkId: $hwId,
|
||||
homeworkTitle: $homework !== null ? $homework->title : '(supprimé)',
|
||||
ruleType: $exception->ruleType,
|
||||
justification: $exception->justification,
|
||||
teacherId: $teacherId,
|
||||
teacherName: $teacher !== null ? $teacher->firstName . ' ' . $teacher->lastName : 'Inconnu',
|
||||
createdAt: $exception->createdAt->format(DateTimeImmutable::ATOM),
|
||||
);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<HomeworkRuleException> $exceptions
|
||||
*
|
||||
* @return array<string, Homework|null>
|
||||
*/
|
||||
private function preloadHomeworks(array $exceptions, TenantId $tenantId): array
|
||||
{
|
||||
$cache = [];
|
||||
|
||||
foreach ($exceptions as $exception) {
|
||||
$hwId = (string) $exception->homeworkId;
|
||||
|
||||
if (!isset($cache[$hwId])) {
|
||||
$cache[$hwId] = $this->homeworkRepository->findById($exception->homeworkId, $tenantId);
|
||||
}
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<HomeworkRuleException> $exceptions
|
||||
*
|
||||
* @return array<string, User|null>
|
||||
*/
|
||||
private function preloadTeachers(array $exceptions): array
|
||||
{
|
||||
$cache = [];
|
||||
|
||||
foreach ($exceptions as $exception) {
|
||||
$teacherId = (string) $exception->createdBy;
|
||||
|
||||
if (!isset($cache[$teacherId])) {
|
||||
$cache[$teacherId] = $this->userRepository->findById(UserId::fromString($teacherId));
|
||||
}
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
private function matchesFilters(
|
||||
HomeworkRuleException $exception,
|
||||
?DateTimeImmutable $startDate,
|
||||
?DateTimeImmutable $endDate,
|
||||
?string $teacherId,
|
||||
?string $ruleType,
|
||||
): bool {
|
||||
if ($startDate !== null && $exception->createdAt < $startDate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($endDate !== null && $exception->createdAt > $endDate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($teacherId !== null && (string) $exception->createdBy !== $teacherId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($ruleType !== null && !str_contains($exception->ruleType, $ruleType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetHomeworkExceptionsReport;
|
||||
|
||||
final readonly class GetHomeworkExceptionsReportQuery
|
||||
{
|
||||
public function __construct(
|
||||
public string $tenantId,
|
||||
public ?string $startDate = null,
|
||||
public ?string $endDate = null,
|
||||
public ?string $teacherId = null,
|
||||
public ?string $ruleType = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Application\Query\GetHomeworkExceptionsReport;
|
||||
|
||||
final readonly class HomeworkExceptionDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public string $homeworkId,
|
||||
public string $homeworkTitle,
|
||||
public string $ruleType,
|
||||
public string $justification,
|
||||
public string $teacherId,
|
||||
public string $teacherName,
|
||||
public string $createdAt,
|
||||
) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user