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,206 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Query\GetHomeworkExceptionsReport;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Administration\Domain\Model\Subject\SubjectId;
|
||||
use App\Administration\Domain\Model\User\Email;
|
||||
use App\Administration\Domain\Model\User\Role;
|
||||
use App\Administration\Domain\Model\User\StatutCompte;
|
||||
use App\Administration\Domain\Model\User\User;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Administration\Domain\Repository\UserRepository;
|
||||
use App\Scolarite\Application\Query\GetHomeworkExceptionsReport\GetHomeworkExceptionsReportHandler;
|
||||
use App\Scolarite\Application\Query\GetHomeworkExceptionsReport\GetHomeworkExceptionsReportQuery;
|
||||
use App\Scolarite\Domain\Model\Homework\Homework;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkId;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkRuleException;
|
||||
use App\Scolarite\Domain\Model\Homework\HomeworkStatus;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkRepository;
|
||||
use App\Scolarite\Infrastructure\Persistence\InMemory\InMemoryHomeworkRuleExceptionRepository;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class GetHomeworkExceptionsReportHandlerTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
|
||||
private const string TEACHER2_ID = '550e8400-e29b-41d4-a716-446655440011';
|
||||
|
||||
private InMemoryHomeworkRepository $homeworkRepository;
|
||||
private InMemoryHomeworkRuleExceptionRepository $exceptionRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->homeworkRepository = new InMemoryHomeworkRepository();
|
||||
$this->exceptionRepository = new InMemoryHomeworkRuleExceptionRepository();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itReturnsAllExceptionsForTenant(): void
|
||||
{
|
||||
$this->seedData();
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$results = $handler(new GetHomeworkExceptionsReportQuery(tenantId: self::TENANT_ID));
|
||||
|
||||
self::assertCount(2, $results);
|
||||
self::assertSame('Exercices chapitre 5', $results[0]->homeworkTitle);
|
||||
self::assertSame('Jean Dupont', $results[0]->teacherName);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itFiltersByDateRange(): void
|
||||
{
|
||||
$this->seedData();
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$results = $handler(new GetHomeworkExceptionsReportQuery(
|
||||
tenantId: self::TENANT_ID,
|
||||
startDate: '2026-03-20',
|
||||
endDate: '2026-03-25',
|
||||
));
|
||||
|
||||
self::assertCount(1, $results);
|
||||
self::assertSame('Devoir histoire', $results[0]->homeworkTitle);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itFiltersByTeacher(): void
|
||||
{
|
||||
$this->seedData();
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$results = $handler(new GetHomeworkExceptionsReportQuery(
|
||||
tenantId: self::TENANT_ID,
|
||||
teacherId: self::TEACHER2_ID,
|
||||
));
|
||||
|
||||
self::assertCount(1, $results);
|
||||
self::assertSame(self::TEACHER2_ID, $results[0]->teacherId);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itFiltersByRuleType(): void
|
||||
{
|
||||
$this->seedData();
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$results = $handler(new GetHomeworkExceptionsReportQuery(
|
||||
tenantId: self::TENANT_ID,
|
||||
ruleType: 'no_monday_after',
|
||||
));
|
||||
|
||||
self::assertCount(1, $results);
|
||||
self::assertStringContainsString('no_monday_after', $results[0]->ruleType);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itReturnsEmptyWhenNoExceptions(): void
|
||||
{
|
||||
$handler = $this->createHandler();
|
||||
|
||||
$results = $handler(new GetHomeworkExceptionsReportQuery(tenantId: self::TENANT_ID));
|
||||
|
||||
self::assertCount(0, $results);
|
||||
}
|
||||
|
||||
private function seedData(): void
|
||||
{
|
||||
$tenantId = TenantId::fromString(self::TENANT_ID);
|
||||
|
||||
$hw1 = Homework::reconstitute(
|
||||
id: HomeworkId::fromString('550e8400-e29b-41d4-a716-446655440050'),
|
||||
tenantId: $tenantId,
|
||||
classId: ClassId::fromString('550e8400-e29b-41d4-a716-446655440020'),
|
||||
subjectId: SubjectId::fromString('550e8400-e29b-41d4-a716-446655440030'),
|
||||
teacherId: UserId::fromString(self::TEACHER_ID),
|
||||
title: 'Exercices chapitre 5',
|
||||
description: null,
|
||||
dueDate: new DateTimeImmutable('2026-04-15'),
|
||||
status: HomeworkStatus::PUBLISHED,
|
||||
createdAt: new DateTimeImmutable('2026-03-19 10:00:00'),
|
||||
updatedAt: new DateTimeImmutable('2026-03-19 10:00:00'),
|
||||
);
|
||||
$this->homeworkRepository->save($hw1);
|
||||
|
||||
$hw2 = Homework::reconstitute(
|
||||
id: HomeworkId::fromString('550e8400-e29b-41d4-a716-446655440051'),
|
||||
tenantId: $tenantId,
|
||||
classId: ClassId::fromString('550e8400-e29b-41d4-a716-446655440020'),
|
||||
subjectId: SubjectId::fromString('550e8400-e29b-41d4-a716-446655440030'),
|
||||
teacherId: UserId::fromString(self::TEACHER2_ID),
|
||||
title: 'Devoir histoire',
|
||||
description: null,
|
||||
dueDate: new DateTimeImmutable('2026-04-20'),
|
||||
status: HomeworkStatus::PUBLISHED,
|
||||
createdAt: new DateTimeImmutable('2026-03-21 10:00:00'),
|
||||
updatedAt: new DateTimeImmutable('2026-03-21 10:00:00'),
|
||||
);
|
||||
$this->homeworkRepository->save($hw2);
|
||||
|
||||
$ex1 = HomeworkRuleException::demander(
|
||||
tenantId: $tenantId,
|
||||
homeworkId: $hw1->id,
|
||||
ruleTypes: ['minimum_delay'],
|
||||
justification: 'Sortie scolaire prévue, devoir urgent pour les élèves.',
|
||||
createdBy: UserId::fromString(self::TEACHER_ID),
|
||||
now: new DateTimeImmutable('2026-03-19 10:00:00'),
|
||||
);
|
||||
$this->exceptionRepository->save($ex1);
|
||||
|
||||
$ex2 = HomeworkRuleException::demander(
|
||||
tenantId: $tenantId,
|
||||
homeworkId: $hw2->id,
|
||||
ruleTypes: ['no_monday_after'],
|
||||
justification: 'Rentrée après les vacances, devoir préparatoire.',
|
||||
createdBy: UserId::fromString(self::TEACHER2_ID),
|
||||
now: new DateTimeImmutable('2026-03-21 10:00:00'),
|
||||
);
|
||||
$this->exceptionRepository->save($ex2);
|
||||
}
|
||||
|
||||
private function createHandler(): GetHomeworkExceptionsReportHandler
|
||||
{
|
||||
$userRepository = $this->createMock(UserRepository::class);
|
||||
$userRepository->method('findById')
|
||||
->willReturnCallback(static function (UserId $id): ?User {
|
||||
$map = [
|
||||
self::TEACHER_ID => ['Jean', 'Dupont'],
|
||||
self::TEACHER2_ID => ['Marie', 'Martin'],
|
||||
];
|
||||
|
||||
$data = $map[(string) $id] ?? null;
|
||||
|
||||
if ($data === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return User::reconstitute(
|
||||
id: $id,
|
||||
email: new Email('test@test.fr'),
|
||||
roles: [Role::PROF],
|
||||
tenantId: TenantId::fromString(self::TENANT_ID),
|
||||
schoolName: 'École Test',
|
||||
statut: StatutCompte::ACTIF,
|
||||
dateNaissance: null,
|
||||
createdAt: new DateTimeImmutable('2026-01-01'),
|
||||
hashedPassword: 'hashed',
|
||||
activatedAt: new DateTimeImmutable('2026-01-02'),
|
||||
consentementParental: null,
|
||||
firstName: $data[0],
|
||||
lastName: $data[1],
|
||||
);
|
||||
});
|
||||
|
||||
return new GetHomeworkExceptionsReportHandler(
|
||||
$this->exceptionRepository,
|
||||
$this->homeworkRepository,
|
||||
$userRepository,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user