feat : extend export endpoint for multi-user, multi-project, client filters

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-24 21:41:53 +01:00
parent 8f8eeddd91
commit 755c39a0f6
2 changed files with 63 additions and 23 deletions

View File

@@ -47,27 +47,65 @@ class TimeEntryExportController extends AbstractController
throw new BadRequestHttpException('Format de date invalide. Utilisez YYYY-MM-DD.'); throw new BadRequestHttpException('Format de date invalide. Utilisez YYYY-MM-DD.');
} }
// Max range: 12 months
if ($after->modify('+12 months') < $before) { if ($after->modify('+12 months') < $before) {
throw new BadRequestHttpException('La plage de dates ne peut pas dépasser 12 mois.'); throw new BadRequestHttpException('La plage de dates ne peut pas dépasser 12 mois.');
} }
// Authorization: non-admin users can only export their own data // --- Users ---
$user = null; $users = null;
if (!$this->security->isGranted('ROLE_ADMIN')) { if (!$this->security->isGranted('ROLE_ADMIN')) {
/** @var User $user */ /** @var User $currentUser */
$user = $this->security->getUser(); $currentUser = $this->security->getUser();
$users = [$currentUser];
} else { } else {
$userId = $request->query->getInt('user'); /** @var int[] $userIds */
if ($userId > 0) { $userIds = array_filter(
$user = $this->entityManager->getRepository(User::class)->find($userId); array_map('intval', (array) $request->query->all('users')),
fn (int $id) => $id > 0,
);
if ([] !== $userIds) {
$users = $this->entityManager->getRepository(User::class)->findBy(['id' => $userIds]);
} }
} }
$project = null; // --- Client (filter projects by client) ---
$projectId = $request->query->getInt('project'); $clientId = $request->query->getInt('client');
if ($projectId > 0) { $clientProjects = null;
$project = $this->entityManager->getRepository(Project::class)->find($projectId); if ($clientId > 0) {
$clientProjects = $this->entityManager->getRepository(Project::class)->findBy(['client' => $clientId]);
}
// --- Projects ---
$projects = null;
/** @var int[] $projectIds */
$projectIds = array_filter(
array_map('intval', (array) $request->query->all('projects')),
fn (int $id) => $id > 0,
);
if ([] !== $projectIds) {
$projects = $this->entityManager->getRepository(Project::class)->findBy(['id' => $projectIds]);
}
// Merge: if both client and projects are set, intersect; if only client, use client projects
if (null !== $clientProjects && null !== $projects) {
$clientProjectIds = array_map(fn (Project $p) => $p->getId(), $clientProjects);
$projects = array_values(array_filter($projects, fn (Project $p) => in_array($p->getId(), $clientProjectIds, true)));
if ([] === $projects) {
$projects = null;
// No matching projects — force empty result by using a dummy condition
$entries = [];
$tempFile = $this->exportService->generate($entries, $after, $before);
$filename = sprintf('export-temps-%s_%s.xlsx', $after->format('Y-m-d'), $before->format('Y-m-d'));
$response = new BinaryFileResponse($tempFile);
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename);
$response->headers->set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
$response->deleteFileAfterSend(true);
return $response;
}
} elseif (null !== $clientProjects) {
$projects = $clientProjects;
} }
/** @var int[] $tagIds */ /** @var int[] $tagIds */
@@ -79,8 +117,8 @@ class TimeEntryExportController extends AbstractController
$entries = $this->timeEntryRepository->findForExport( $entries = $this->timeEntryRepository->findForExport(
$after, $after,
$before, $before,
$user, $users ?: null,
$project, $projects ?: null,
$tagIds ?: null, $tagIds ?: null,
); );

View File

@@ -30,15 +30,17 @@ class TimeEntryRepository extends ServiceEntityRepository
} }
/** /**
* @param null|int[] $tagIds * @param null|User[] $users
* @param null|Project[] $projects
* @param null|int[] $tagIds
* *
* @return TimeEntry[] * @return TimeEntry[]
*/ */
public function findForExport( public function findForExport(
DateTimeImmutable $after, DateTimeImmutable $after,
DateTimeImmutable $before, DateTimeImmutable $before,
?User $user = null, ?array $users = null,
?Project $project = null, ?array $projects = null,
?array $tagIds = null, ?array $tagIds = null,
): array { ): array {
$qb = $this->createQueryBuilder('te') $qb = $this->createQueryBuilder('te')
@@ -49,15 +51,15 @@ class TimeEntryRepository extends ServiceEntityRepository
->orderBy('te.startedAt', 'ASC') ->orderBy('te.startedAt', 'ASC')
; ;
if (null !== $user) { if (null !== $users && [] !== $users) {
$qb->andWhere('te.user = :user') $qb->andWhere('te.user IN (:users)')
->setParameter('user', $user) ->setParameter('users', $users)
; ;
} }
if (null !== $project) { if (null !== $projects && [] !== $projects) {
$qb->andWhere('te.project = :project') $qb->andWhere('te.project IN (:projects)')
->setParameter('project', $project) ->setParameter('projects', $projects)
; ;
} }