feat: import work hours from SaaS and exchange signed time sheet

This commit is contained in:
fromme committed 2026-09-26 12:13:59 +02:00
1 parent 7cf39850e6
commit 1a7c272414
73 files changed
+10488 -72

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
<?php
namespace App\Service\Pdf;
use App\Entity\TeamerWorkMonth;
use Dompdf\Dompdf;
use Dompdf\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Twig\Environment;
/**
* Renders the Arbeitszeitnachweis a teamer signs for one month.
*
* Deliberately a sibling of AbstractPdfRenderer rather than a subclass: that base exists for
* the FPDI renderers, which stamp text at fixed millimetre coordinates onto a pre-made PDF.
* That works for the one-page contract and invoice forms but not for a table whose length
* depends on the month, so this one goes through Twig and Dompdf instead. Dompdf's bundled
* DejaVu fonts also carry umlauts natively, where the FPDI path has to convert every string
* to ISO-8859-1 first (see Pdf::encodeString()).
*/
class WorkMonthRenderer
{
private array $config;
public function __construct(
private readonly Environment $twig,
array $options,
) {
$this->config = $this->resolveOptions($options);
}
public function render(TeamerWorkMonth $workMonth): string
{
$options = new Options();
// Nothing in the document is fetched from anywhere: the stylesheet is inline and
// there are no images, so the renderer never needs to leave the project directory.
$options->setIsRemoteEnabled(false);
$options->setChroot($this->config['project_dir']);
$options->setDefaultFont('DejaVu Sans');
$options->setDefaultPaperSize('A4');
$options->setDefaultPaperOrientation('portrait');
$dompdf = new Dompdf($options);
$dompdf->loadHtml($this->twig->render('pdf/work_month.html.twig', [
'workMonth' => $workMonth,
'teamer' => $workMonth->getTeamer(),
'days' => $workMonth->getDays()->forCalendarMonth($workMonth->getYear(), $workMonth->getMonth()),
'logo' => $this->logoPath(),
]), 'UTF-8');
$dompdf->render();
return (string) $dompdf->output();
}
/**
* The letterhead logo, as an absolute path inside the chroot.
*
* Read from the filesystem rather than embedded: remote loading is off and the chroot is
* the project directory, so Dompdf can open this and nothing outside it. Null when the
* file is missing, because a working-time record is worth printing without its letterhead.
*/
private function logoPath(): ?string
{
$path = $this->config['project_dir'].'/assets/images/logo.png';
return is_readable($path) ? $path : null;
}
/**
* Arbeitszeitnachweis_Test_2026-03.pdf.
*/
public function filename(TeamerWorkMonth $workMonth): string
{
$lastName = $workMonth->getTeamer()?->getLastName() ?? 'Teamer';
return sprintf(
'Arbeitszeitnachweis_%s_%04d-%02d.pdf',
preg_replace('/[^A-Za-z0-9]+/', '_', $lastName),
$workMonth->getYear(),
$workMonth->getMonth(),
);
}
private function resolveOptions(array $options): array
{
$optionsResolver = new OptionsResolver();
$optionsResolver->setRequired(['project_dir']);
$optionsResolver->setAllowedTypes('project_dir', 'string');
return $optionsResolver->resolve($options);
}
}