Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
- Nouveau endpoint GET /yearly-hours/print-all (admin, par mois uniquement) - Service YearlyHoursExportBuilder extrait du provider existant (logique partagée) - EmployeeYearlyHoursPrintProvider refactorisé pour utiliser le builder - Template print-all.html.twig avec saut de page entre chaque employé - Drawer BulkYearlyHoursDrawer avec loader "Génération en cours..." - Bouton "Export heures" ajouté sur la page liste employés - PHP memory_limit passé de 128M à 256M dans php.ini (nécessaire pour Dompdf multi-employés) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
119 lines
4.2 KiB
PHP
119 lines
4.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\State;
|
|
|
|
use ApiPlatform\Metadata\Operation;
|
|
use ApiPlatform\State\ProviderInterface;
|
|
use App\Entity\Employee;
|
|
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\NotFoundHttpException;
|
|
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
|
use Twig\Environment;
|
|
|
|
class EmployeeYearlyHoursPrintProvider 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);
|
|
}
|
|
|
|
$employeeId = (int) $request->query->get('employeeId', '0');
|
|
if ($employeeId <= 0) {
|
|
throw new UnprocessableEntityHttpException('employeeId must be a positive integer.');
|
|
}
|
|
|
|
$employee = $this->employeeRepository->find($employeeId);
|
|
if (!$employee instanceof Employee) {
|
|
throw new NotFoundHttpException('Employee not found.');
|
|
}
|
|
|
|
$yearRaw = (string) $request->query->get('year');
|
|
if (!preg_match('/^\d{4}$/', $yearRaw)) {
|
|
throw new UnprocessableEntityHttpException('year must use YYYY format.');
|
|
}
|
|
$year = (int) $yearRaw;
|
|
|
|
$monthRaw = (string) $request->query->get('month', '');
|
|
$month = null;
|
|
if ('' !== $monthRaw) {
|
|
if (!preg_match('/^(?:0?[1-9]|1[0-2])$/', $monthRaw)) {
|
|
throw new UnprocessableEntityHttpException('month must be between 1 and 12.');
|
|
}
|
|
$month = (int) $monthRaw;
|
|
}
|
|
|
|
if (null !== $month) {
|
|
$from = new DateTimeImmutable(sprintf('%d-%02d-01', $year, $month));
|
|
$to = $from->modify('last day of this month');
|
|
} else {
|
|
$from = new DateTimeImmutable("{$year}-01-01");
|
|
$to = new DateTimeImmutable("{$year}-12-31");
|
|
}
|
|
|
|
$entries = $this->exportBuilder->buildForEmployee($employee, $from, $to);
|
|
|
|
$employeeName = trim(($employee->getLastName() ?? '').' '.($employee->getFirstName() ?? ''));
|
|
$contractLabel = $this->exportBuilder->buildContractLabel($employee);
|
|
|
|
$options = new Options();
|
|
$options->set('isRemoteEnabled', true);
|
|
|
|
$dompdf = new Dompdf($options);
|
|
$html = $this->twig->render('employee-yearly-hours/print.html.twig', [
|
|
'employeeName' => $employeeName,
|
|
'contractLabel' => $contractLabel,
|
|
'year' => $year,
|
|
'month' => $month,
|
|
'segments' => $entries[0]['segments'] ?? [],
|
|
]);
|
|
|
|
$dompdf->loadHtml($html);
|
|
$dompdf->setPaper('A4', 'portrait');
|
|
$dompdf->render();
|
|
|
|
$filename = null !== $month
|
|
? sprintf(
|
|
'%s_%s_%d-%02d.pdf',
|
|
$this->sanitizeFilename($employee->getLastName() ?? ''),
|
|
$this->sanitizeFilename($employee->getFirstName() ?? ''),
|
|
$year,
|
|
$month,
|
|
)
|
|
: sprintf(
|
|
'%s_%s_%d.pdf',
|
|
$this->sanitizeFilename($employee->getLastName() ?? ''),
|
|
$this->sanitizeFilename($employee->getFirstName() ?? ''),
|
|
$year,
|
|
);
|
|
|
|
return new Response($dompdf->output(), Response::HTTP_OK, [
|
|
'Content-Type' => 'application/pdf',
|
|
'Content-Disposition' => 'inline; filename="'.$filename.'"',
|
|
]);
|
|
}
|
|
|
|
private function sanitizeFilename(string $name): string
|
|
{
|
|
$name = str_replace(' ', '_', $name);
|
|
|
|
return preg_replace('/[^a-zA-Z0-9_\-]/', '', $name) ?? $name;
|
|
}
|
|
}
|