feat: Permettre l'import d'enseignants via fichier CSV ou XLSX
L'établissement a besoin d'importer en masse ses enseignants depuis les exports des logiciels de vie scolaire (Pronote, EDT, etc.), comme c'est déjà possible pour les élèves. Le wizard en 4 étapes (upload → mapping → aperçu → import) réutilise l'architecture de l'import élèves tout en ajoutant la gestion des matières et des classes enseignées. Corrections de la review #2 intégrées : - La commande ImportTeachersCommand est routée en async via Messenger pour ne pas bloquer la requête HTTP sur les gros fichiers. - Le handler est protégé par un try/catch Throwable pour marquer le batch en échec si une erreur inattendue survient, évitant qu'il reste bloqué en statut "processing". - Les domain events (UtilisateurInvite) sont dispatchés sur l'event bus après chaque création d'utilisateur, déclenchant l'envoi des emails d'invitation. - L'option "mettre à jour les enseignants existants" (AC5) permet de choisir entre ignorer ou mettre à jour nom/prénom et ajouter les affectations manquantes pour les doublons détectés par email.
This commit is contained in:
@@ -0,0 +1,603 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Administration\Application\Command\ImportTeachers;
|
||||
|
||||
use App\Administration\Application\Command\ImportTeachers\ImportTeachersCommand;
|
||||
use App\Administration\Application\Command\ImportTeachers\ImportTeachersHandler;
|
||||
use App\Administration\Domain\Event\UtilisateurInvite;
|
||||
use App\Administration\Domain\Model\Import\ImportRow;
|
||||
use App\Administration\Domain\Model\Import\ImportStatus;
|
||||
use App\Administration\Domain\Model\Import\KnownImportFormat;
|
||||
use App\Administration\Domain\Model\Import\TeacherColumnMapping;
|
||||
use App\Administration\Domain\Model\Import\TeacherImportBatch;
|
||||
use App\Administration\Domain\Model\Import\TeacherImportField;
|
||||
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassName;
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolClass;
|
||||
use App\Administration\Domain\Model\SchoolClass\SchoolId;
|
||||
use App\Administration\Domain\Model\Subject\Subject;
|
||||
use App\Administration\Domain\Model\Subject\SubjectCode;
|
||||
use App\Administration\Domain\Model\Subject\SubjectName;
|
||||
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\Infrastructure\Persistence\InMemory\InMemoryClassRepository;
|
||||
use App\Administration\Infrastructure\Persistence\InMemory\InMemorySubjectRepository;
|
||||
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryTeacherAssignmentRepository;
|
||||
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryTeacherImportBatchRepository;
|
||||
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryUserRepository;
|
||||
use App\Administration\Infrastructure\School\SchoolIdResolver;
|
||||
use App\Shared\Domain\Clock;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
use function count;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Component\Messenger\Envelope;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
|
||||
final class ImportTeachersHandlerTest extends TestCase
|
||||
{
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string ACADEMIC_YEAR_ID = '550e8400-e29b-41d4-a716-446655440003';
|
||||
|
||||
private InMemoryTeacherImportBatchRepository $importBatchRepository;
|
||||
private InMemoryUserRepository $userRepository;
|
||||
private InMemorySubjectRepository $subjectRepository;
|
||||
private InMemoryClassRepository $classRepository;
|
||||
private InMemoryTeacherAssignmentRepository $assignmentRepository;
|
||||
private ImportTeachersHandler $handler;
|
||||
private TenantId $tenantId;
|
||||
private SchoolId $schoolId;
|
||||
private MessageBusInterface $eventBus;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-02-25 10:00:00');
|
||||
}
|
||||
};
|
||||
|
||||
$this->tenantId = TenantId::fromString(self::TENANT_ID);
|
||||
$schoolIdResolver = new SchoolIdResolver();
|
||||
$this->schoolId = SchoolId::fromString($schoolIdResolver->resolveForTenant(self::TENANT_ID));
|
||||
|
||||
$this->importBatchRepository = new InMemoryTeacherImportBatchRepository();
|
||||
$this->userRepository = new InMemoryUserRepository();
|
||||
$this->subjectRepository = new InMemorySubjectRepository();
|
||||
$this->classRepository = new InMemoryClassRepository();
|
||||
$this->assignmentRepository = new InMemoryTeacherAssignmentRepository();
|
||||
|
||||
$connection = $this->createMock(Connection::class);
|
||||
|
||||
$this->eventBus = $this->createMock(MessageBusInterface::class);
|
||||
$this->eventBus->method('dispatch')->willReturnCallback(
|
||||
static fn (object $message) => new Envelope($message),
|
||||
);
|
||||
|
||||
$this->handler = new ImportTeachersHandler(
|
||||
$this->importBatchRepository,
|
||||
$this->userRepository,
|
||||
$this->subjectRepository,
|
||||
$this->classRepository,
|
||||
$this->assignmentRepository,
|
||||
$schoolIdResolver,
|
||||
$connection,
|
||||
$clock,
|
||||
new NullLogger(),
|
||||
$this->eventBus,
|
||||
);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function importsTeachersWithEmailOnly(): void
|
||||
{
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr'),
|
||||
$this->createMappedRow(2, 'Martin', 'Marie', 'marie@ecole.fr'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
));
|
||||
|
||||
$updatedBatch = $this->importBatchRepository->get($batch->id);
|
||||
|
||||
self::assertSame(ImportStatus::COMPLETED, $updatedBatch->status);
|
||||
self::assertSame(2, $updatedBatch->importedCount);
|
||||
self::assertSame(0, $updatedBatch->errorCount);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function createsTeachersWithProfRole(): void
|
||||
{
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
));
|
||||
|
||||
$users = $this->userRepository->findAllByTenant($this->tenantId);
|
||||
|
||||
self::assertCount(1, $users);
|
||||
self::assertTrue($users[0]->aLeRole(Role::PROF));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function assignsTeacherToSubjectsAndClasses(): void
|
||||
{
|
||||
$subject = $this->createSubject('Mathématiques', 'MATH');
|
||||
$class = $this->createClass('6A');
|
||||
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr', 'Mathématiques', '6A'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
));
|
||||
|
||||
$users = $this->userRepository->findAllByTenant($this->tenantId);
|
||||
self::assertCount(1, $users);
|
||||
|
||||
$assignments = $this->assignmentRepository->findActiveByTeacher($users[0]->id, $this->tenantId);
|
||||
self::assertCount(1, $assignments);
|
||||
self::assertTrue($assignments[0]->subjectId->equals($subject->id));
|
||||
self::assertTrue($assignments[0]->classId->equals($class->id));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function assignsMultipleSubjectsAndClasses(): void
|
||||
{
|
||||
$this->createSubject('Mathématiques', 'MATH');
|
||||
$this->createSubject('Physique', 'PHYS');
|
||||
$this->createClass('6A');
|
||||
$this->createClass('6B');
|
||||
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr', 'Mathématiques, Physique', '6A, 6B'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
));
|
||||
|
||||
$users = $this->userRepository->findAllByTenant($this->tenantId);
|
||||
$assignments = $this->assignmentRepository->findActiveByTeacher($users[0]->id, $this->tenantId);
|
||||
|
||||
// 2 subjects × 2 classes = 4 assignments
|
||||
self::assertCount(4, $assignments);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function createsMissingSubjectsWhenEnabled(): void
|
||||
{
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr', 'Chimie'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
createMissingSubjects: true,
|
||||
));
|
||||
|
||||
$updatedBatch = $this->importBatchRepository->get($batch->id);
|
||||
|
||||
self::assertSame(ImportStatus::COMPLETED, $updatedBatch->status);
|
||||
self::assertSame(1, $updatedBatch->importedCount);
|
||||
|
||||
$subjects = $this->subjectRepository->findAllActiveByTenant($this->tenantId);
|
||||
self::assertCount(1, $subjects);
|
||||
self::assertSame('Chimie', (string) $subjects[0]->name);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function rejectsDuplicateEmails(): void
|
||||
{
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr'),
|
||||
$this->createMappedRow(2, 'Martin', 'Marie', 'jean@ecole.fr'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
));
|
||||
|
||||
$updatedBatch = $this->importBatchRepository->get($batch->id);
|
||||
|
||||
self::assertSame(ImportStatus::COMPLETED, $updatedBatch->status);
|
||||
self::assertSame(1, $updatedBatch->importedCount);
|
||||
self::assertSame(1, $updatedBatch->errorCount);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function importedTeachersHaveEnAttenteStatus(): void
|
||||
{
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
));
|
||||
|
||||
$users = $this->userRepository->findAllByTenant($this->tenantId);
|
||||
|
||||
self::assertCount(1, $users);
|
||||
self::assertSame(StatutCompte::EN_ATTENTE, $users[0]->statut);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function importedTeachersDispatchUtilisateurInviteEvent(): void
|
||||
{
|
||||
$eventBus = $this->createMock(MessageBusInterface::class);
|
||||
$eventBus->expects(self::once())
|
||||
->method('dispatch')
|
||||
->with(self::isInstanceOf(UtilisateurInvite::class))
|
||||
->willReturnCallback(static fn (object $message) => new Envelope($message));
|
||||
|
||||
$clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-02-25 10:00:00');
|
||||
}
|
||||
};
|
||||
|
||||
$schoolIdResolver = new SchoolIdResolver();
|
||||
$connection = $this->createMock(Connection::class);
|
||||
|
||||
$handler = new ImportTeachersHandler(
|
||||
$this->importBatchRepository,
|
||||
$this->userRepository,
|
||||
$this->subjectRepository,
|
||||
$this->classRepository,
|
||||
$this->assignmentRepository,
|
||||
$schoolIdResolver,
|
||||
$connection,
|
||||
$clock,
|
||||
new NullLogger(),
|
||||
$eventBus,
|
||||
);
|
||||
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr'),
|
||||
]);
|
||||
|
||||
($handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function rejectsPreExistingEmailFromDatabase(): void
|
||||
{
|
||||
$existingUser = User::inviter(
|
||||
email: new Email('jean@ecole.fr'),
|
||||
role: Role::PROF,
|
||||
tenantId: $this->tenantId,
|
||||
schoolName: 'École Test',
|
||||
firstName: 'Existing',
|
||||
lastName: 'Teacher',
|
||||
invitedAt: new DateTimeImmutable('2026-02-20 10:00:00'),
|
||||
);
|
||||
$this->userRepository->save($existingUser);
|
||||
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
));
|
||||
|
||||
$updatedBatch = $this->importBatchRepository->get($batch->id);
|
||||
|
||||
self::assertSame(ImportStatus::COMPLETED, $updatedBatch->status);
|
||||
self::assertSame(0, $updatedBatch->importedCount);
|
||||
self::assertSame(1, $updatedBatch->errorCount);
|
||||
|
||||
// Verify no new user was created (still only the pre-existing one)
|
||||
$users = $this->userRepository->findAllByTenant($this->tenantId);
|
||||
self::assertCount(1, $users);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updatesExistingTeacherWhenUpdateExistingEnabled(): void
|
||||
{
|
||||
$existingUser = User::inviter(
|
||||
email: new Email('jean@ecole.fr'),
|
||||
role: Role::PROF,
|
||||
tenantId: $this->tenantId,
|
||||
schoolName: 'École Test',
|
||||
firstName: 'Ancien',
|
||||
lastName: 'Nom',
|
||||
invitedAt: new DateTimeImmutable('2026-02-20 10:00:00'),
|
||||
);
|
||||
$existingUser->pullDomainEvents();
|
||||
$this->userRepository->save($existingUser);
|
||||
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
updateExisting: true,
|
||||
));
|
||||
|
||||
$updatedBatch = $this->importBatchRepository->get($batch->id);
|
||||
|
||||
self::assertSame(ImportStatus::COMPLETED, $updatedBatch->status);
|
||||
self::assertSame(1, $updatedBatch->importedCount);
|
||||
self::assertSame(0, $updatedBatch->errorCount);
|
||||
|
||||
$users = $this->userRepository->findAllByTenant($this->tenantId);
|
||||
self::assertCount(1, $users);
|
||||
self::assertSame('Jean', $users[0]->firstName);
|
||||
self::assertSame('Dupont', $users[0]->lastName);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updateExistingAddsMissingAssignments(): void
|
||||
{
|
||||
$subject = $this->createSubject('Mathématiques', 'MATH');
|
||||
$class = $this->createClass('6A');
|
||||
|
||||
$existingUser = User::inviter(
|
||||
email: new Email('jean@ecole.fr'),
|
||||
role: Role::PROF,
|
||||
tenantId: $this->tenantId,
|
||||
schoolName: 'École Test',
|
||||
firstName: 'Jean',
|
||||
lastName: 'Dupont',
|
||||
invitedAt: new DateTimeImmutable('2026-02-20 10:00:00'),
|
||||
);
|
||||
$existingUser->pullDomainEvents();
|
||||
$this->userRepository->save($existingUser);
|
||||
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr', 'Mathématiques', '6A'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
updateExisting: true,
|
||||
));
|
||||
|
||||
$assignments = $this->assignmentRepository->findActiveByTeacher($existingUser->id, $this->tenantId);
|
||||
self::assertCount(1, $assignments);
|
||||
self::assertTrue($assignments[0]->subjectId->equals($subject->id));
|
||||
self::assertTrue($assignments[0]->classId->equals($class->id));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updateExistingDoesNotDuplicateAssignments(): void
|
||||
{
|
||||
$subject = $this->createSubject('Mathématiques', 'MATH');
|
||||
$class = $this->createClass('6A');
|
||||
|
||||
$existingUser = User::inviter(
|
||||
email: new Email('jean@ecole.fr'),
|
||||
role: Role::PROF,
|
||||
tenantId: $this->tenantId,
|
||||
schoolName: 'École Test',
|
||||
firstName: 'Jean',
|
||||
lastName: 'Dupont',
|
||||
invitedAt: new DateTimeImmutable('2026-02-20 10:00:00'),
|
||||
);
|
||||
$existingUser->pullDomainEvents();
|
||||
$this->userRepository->save($existingUser);
|
||||
|
||||
// Pre-create the assignment
|
||||
$assignment = \App\Administration\Domain\Model\TeacherAssignment\TeacherAssignment::creer(
|
||||
tenantId: $this->tenantId,
|
||||
teacherId: $existingUser->id,
|
||||
classId: $class->id,
|
||||
subjectId: $subject->id,
|
||||
academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
|
||||
createdAt: new DateTimeImmutable('2026-02-20 10:00:00'),
|
||||
);
|
||||
$this->assignmentRepository->save($assignment);
|
||||
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'Dupont', 'Jean', 'jean@ecole.fr', 'Mathématiques', '6A'),
|
||||
]);
|
||||
|
||||
($this->handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
updateExisting: true,
|
||||
));
|
||||
|
||||
$assignments = $this->assignmentRepository->findActiveByTeacher($existingUser->id, $this->tenantId);
|
||||
self::assertCount(1, $assignments);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updateExistingDoesNotDispatchInvitationEvent(): void
|
||||
{
|
||||
$eventBus = $this->createMock(MessageBusInterface::class);
|
||||
$eventBus->expects(self::never())->method('dispatch');
|
||||
|
||||
$clock = new class implements Clock {
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('2026-02-25 10:00:00');
|
||||
}
|
||||
};
|
||||
|
||||
$schoolIdResolver = new SchoolIdResolver();
|
||||
$connection = $this->createMock(Connection::class);
|
||||
|
||||
$handler = new ImportTeachersHandler(
|
||||
$this->importBatchRepository,
|
||||
$this->userRepository,
|
||||
$this->subjectRepository,
|
||||
$this->classRepository,
|
||||
$this->assignmentRepository,
|
||||
$schoolIdResolver,
|
||||
$connection,
|
||||
$clock,
|
||||
new NullLogger(),
|
||||
$eventBus,
|
||||
);
|
||||
|
||||
$existingUser = User::inviter(
|
||||
email: new Email('jean@ecole.fr'),
|
||||
role: Role::PROF,
|
||||
tenantId: $this->tenantId,
|
||||
schoolName: 'École Test',
|
||||
firstName: 'Jean',
|
||||
lastName: 'Dupont',
|
||||
invitedAt: new DateTimeImmutable('2026-02-20 10:00:00'),
|
||||
);
|
||||
$existingUser->pullDomainEvents();
|
||||
$this->userRepository->save($existingUser);
|
||||
|
||||
$batch = $this->createBatchWithRows([
|
||||
$this->createMappedRow(1, 'NouveauNom', 'NouveauPrenom', 'jean@ecole.fr'),
|
||||
]);
|
||||
|
||||
($handler)(new ImportTeachersCommand(
|
||||
batchId: (string) $batch->id,
|
||||
tenantId: self::TENANT_ID,
|
||||
schoolName: 'École Test',
|
||||
academicYearId: self::ACADEMIC_YEAR_ID,
|
||||
updateExisting: true,
|
||||
));
|
||||
}
|
||||
|
||||
private function createSubject(string $name, string $code): Subject
|
||||
{
|
||||
$subject = Subject::creer(
|
||||
tenantId: $this->tenantId,
|
||||
schoolId: $this->schoolId,
|
||||
name: new SubjectName($name),
|
||||
code: new SubjectCode($code),
|
||||
color: null,
|
||||
createdAt: new DateTimeImmutable('2026-01-01'),
|
||||
);
|
||||
|
||||
$this->subjectRepository->save($subject);
|
||||
|
||||
return $subject;
|
||||
}
|
||||
|
||||
private function createClass(string $name): SchoolClass
|
||||
{
|
||||
$class = SchoolClass::creer(
|
||||
tenantId: $this->tenantId,
|
||||
schoolId: $this->schoolId,
|
||||
academicYearId: AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
|
||||
name: new ClassName($name),
|
||||
level: null,
|
||||
capacity: null,
|
||||
createdAt: new DateTimeImmutable('2026-01-01'),
|
||||
);
|
||||
|
||||
$this->classRepository->save($class);
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<ImportRow> $rows
|
||||
*/
|
||||
private function createBatchWithRows(array $rows): TeacherImportBatch
|
||||
{
|
||||
$batch = TeacherImportBatch::creer(
|
||||
tenantId: $this->tenantId,
|
||||
originalFilename: 'enseignants.csv',
|
||||
totalRows: count($rows),
|
||||
detectedColumns: ['Nom', 'Prénom', 'Email', 'Matières', 'Classes'],
|
||||
detectedFormat: KnownImportFormat::CUSTOM,
|
||||
createdAt: new DateTimeImmutable('2026-02-25 09:00:00'),
|
||||
);
|
||||
|
||||
$mapping = TeacherColumnMapping::creer(
|
||||
[
|
||||
'Nom' => TeacherImportField::LAST_NAME,
|
||||
'Prénom' => TeacherImportField::FIRST_NAME,
|
||||
'Email' => TeacherImportField::EMAIL,
|
||||
'Matières' => TeacherImportField::SUBJECTS,
|
||||
'Classes' => TeacherImportField::CLASSES,
|
||||
],
|
||||
KnownImportFormat::CUSTOM,
|
||||
);
|
||||
|
||||
$batch->appliquerMapping($mapping);
|
||||
$batch->enregistrerLignes($rows);
|
||||
$this->importBatchRepository->save($batch);
|
||||
|
||||
return $batch;
|
||||
}
|
||||
|
||||
private function createMappedRow(
|
||||
int $line,
|
||||
string $lastName,
|
||||
string $firstName,
|
||||
string $email,
|
||||
string $subjects = '',
|
||||
string $classes = '',
|
||||
): ImportRow {
|
||||
$mappedData = [
|
||||
'lastName' => $lastName,
|
||||
'firstName' => $firstName,
|
||||
'email' => $email,
|
||||
'subjects' => $subjects,
|
||||
'classes' => $classes,
|
||||
];
|
||||
|
||||
return new ImportRow(
|
||||
lineNumber: $line,
|
||||
rawData: $mappedData,
|
||||
mappedData: $mappedData,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user