feat(heures) : endpoint export PDF heures jour par sites
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ApiResource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\QueryParameter;
|
||||
use App\State\WorkHourDayExportProvider;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new Get(
|
||||
uriTemplate: '/work-hours/day-export',
|
||||
provider: WorkHourDayExportProvider::class,
|
||||
parameters: [
|
||||
new QueryParameter(key: 'workDate', required: true),
|
||||
new QueryParameter(key: 'siteIds', required: true),
|
||||
],
|
||||
security: "is_granted('ROLE_ADMIN')"
|
||||
),
|
||||
]
|
||||
)]
|
||||
final class WorkHourDayExport {}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\Repository\EmployeeRepository;
|
||||
use App\Service\WorkHours\YearlyHoursExportBuilder;
|
||||
use DateTimeImmutable;
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
use Twig\Environment;
|
||||
|
||||
class WorkHourDayExportProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private Environment $twig,
|
||||
private readonly RequestStack $requestStack,
|
||||
private EmployeeRepository $employeeRepository,
|
||||
private YearlyHoursExportBuilder $exportBuilder,
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): Response
|
||||
{
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
if (!$request) {
|
||||
return new Response('Missing request.', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$workDateRaw = (string) $request->query->get('workDate');
|
||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $workDateRaw)) {
|
||||
throw new UnprocessableEntityHttpException('workDate must use YYYY-MM-DD format.');
|
||||
}
|
||||
$date = new DateTimeImmutable($workDateRaw);
|
||||
|
||||
$siteIdsRaw = (string) $request->query->get('siteIds', '');
|
||||
$siteIds = array_values(array_filter(array_map(
|
||||
static fn (string $value): int => (int) trim($value),
|
||||
explode(',', $siteIdsRaw),
|
||||
), static fn (int $id): bool => $id > 0));
|
||||
if ([] === $siteIds) {
|
||||
throw new UnprocessableEntityHttpException('siteIds is required.');
|
||||
}
|
||||
|
||||
// Feature réservée admin : on charge tous les employés puis on filtre.
|
||||
$employees = $this->employeeRepository->findAll();
|
||||
|
||||
// Regroupement par site (ordre displayOrder), non-conducteurs uniquement.
|
||||
$bySite = [];
|
||||
$siteMeta = [];
|
||||
foreach ($employees as $employee) {
|
||||
if (true === $employee->getIsDriver()) {
|
||||
continue;
|
||||
}
|
||||
$site = $employee->getSite();
|
||||
if (null === $site || !in_array($site->getId(), $siteIds, true)) {
|
||||
continue;
|
||||
}
|
||||
$siteId = $site->getId();
|
||||
$bySite[$siteId][] = $employee;
|
||||
$siteMeta[$siteId] ??= [
|
||||
'name' => $site->getName(),
|
||||
'order' => $site->getDisplayOrder(),
|
||||
];
|
||||
}
|
||||
|
||||
uasort($siteMeta, static function (array $a, array $b): int {
|
||||
return [$a['order'], $a['name']] <=> [$b['order'], $b['name']];
|
||||
});
|
||||
|
||||
$groups = [];
|
||||
foreach ($siteMeta as $siteId => $meta) {
|
||||
$siteEmployees = $bySite[$siteId];
|
||||
usort($siteEmployees, static fn ($a, $b) => ($a->getLastName() ?? '') <=> ($b->getLastName() ?? ''));
|
||||
|
||||
$rows = $this->exportBuilder->buildDayRowsForEmployees($siteEmployees, $date);
|
||||
if ([] === $rows) {
|
||||
continue;
|
||||
}
|
||||
$groups[] = ['siteName' => $meta['name'], 'rows' => $rows];
|
||||
}
|
||||
|
||||
$options = new Options();
|
||||
$options->set('isRemoteEnabled', true);
|
||||
$dompdf = new Dompdf($options);
|
||||
|
||||
$html = $this->twig->render('work-hour-day-export/print.html.twig', [
|
||||
'groups' => $groups,
|
||||
'dateLabel' => $date->format('d/m/Y'),
|
||||
'exportedAt' => new DateTimeImmutable('now')->format('d/m/Y H:i'),
|
||||
]);
|
||||
|
||||
$dompdf->loadHtml($html);
|
||||
$dompdf->setPaper('A4', 'portrait');
|
||||
$dompdf->render();
|
||||
|
||||
$filename = sprintf('heures_jour_%s.pdf', $date->format('Y-m-d'));
|
||||
|
||||
return new Response($dompdf->output(), Response::HTTP_OK, [
|
||||
'Content-Type' => 'application/pdf',
|
||||
'Content-Disposition' => sprintf('attachment; filename="%s"', $filename),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user