feat: Provisionner automatiquement un nouvel établissement
Some checks failed
CI / Naming Conventions (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Build Check (push) Has been cancelled

Lorsqu'un super-admin crée un établissement via l'interface, le système
doit automatiquement créer la base tenant, exécuter les migrations,
créer le premier utilisateur admin et envoyer l'invitation — le tout
de manière asynchrone pour ne pas bloquer la réponse HTTP.

Ce mécanisme rend chaque établissement opérationnel dès sa création
sans intervention manuelle sur l'infrastructure.
This commit is contained in:
2026-04-08 13:55:41 +02:00
parent bec211ebf0
commit 713e408773
65 changed files with 5070 additions and 374 deletions

View File

@@ -6,22 +6,26 @@ namespace App\Scolarite\Application\Query\GetBlockedDates;
use App\Administration\Domain\Model\SchoolCalendar\SchoolCalendarRepository;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Scolarite\Application\Port\HomeworkRulesChecker;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use DateInterval;
use DateTimeImmutable;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
/**
* Retourne les dates bloquées (jours fériés, vacances, journées pédagogiques, weekends)
* pour une plage de dates donnée.
* Retourne les dates bloquées (jours fériés, vacances, journées pédagogiques, weekends,
* et dates non conformes aux règles de devoirs) pour une plage de dates donnée.
*
* Utilisé par le frontend pour griser les jours non modifiables dans la grille EDT.
* Utilisé par le frontend pour griser les jours non disponibles dans le calendrier.
*/
#[AsMessageHandler(bus: 'query.bus')]
final readonly class GetBlockedDatesHandler
{
public function __construct(
private SchoolCalendarRepository $calendarRepository,
private HomeworkRulesChecker $rulesChecker,
private Clock $clock,
) {
}
@@ -37,6 +41,7 @@ final readonly class GetBlockedDatesHandler
$endDate = new DateTimeImmutable($query->endDate);
$oneDay = new DateInterval('P1D');
$now = $this->clock->now();
$blockedDates = [];
$current = $startDate;
@@ -50,14 +55,21 @@ final readonly class GetBlockedDatesHandler
reason: $dayOfWeek === 6 ? 'Samedi' : 'Dimanche',
type: 'weekend',
);
} elseif ($calendar !== null) {
$entry = $calendar->trouverEntreePourDate($current);
} elseif ($calendar !== null && ($entry = $calendar->trouverEntreePourDate($current)) !== null) {
$blockedDates[] = new BlockedDateDto(
date: $dateStr,
reason: $entry->label,
type: $entry->type->value,
);
} else {
$dueDate = new DateTimeImmutable($dateStr);
$result = $this->rulesChecker->verifier($tenantId, $dueDate, $now);
if ($entry !== null) {
if (!$result->estValide()) {
$blockedDates[] = new BlockedDateDto(
date: $dateStr,
reason: $entry->label,
type: $entry->type->value,
reason: $result->messages()[0] ?? 'Règle de devoirs',
type: $result->estBloquant() ? 'rule_hard' : 'rule_soft',
);
}
}