pour prod

This commit is contained in:
Loic Masi
2026-08-07 16:13:43 +02:00
commit 5fbf76868f
157 changed files with 24085 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
<?php
declare(strict_types=1);
namespace App\Services;
use DateTimeImmutable;
use DateTimeZone;
/**
* Génère le document annuel de validation du quota horaire d'un agent.
* Le PDF est autonome et ne nécessite aucune dépendance Composer.
*/
final class AgentHoursContractPdfService
{
private const PAGE_WIDTH = 595.28;
private const PAGE_HEIGHT = 841.89;
private const MARGIN = 42.0;
public function annualContract(array $agent, array $quota): string
{
$pdf = new SimplePdf(self::PAGE_WIDTH, self::PAGE_HEIGHT);
$periodLabel = (string) ($quota['periode_libelle'] ?? ('année ' . ($quota['annee'] ?? date('Y'))));
$generatedAt = new DateTimeImmutable('now', new DateTimeZone('Europe/Paris'));
$this->drawHeader($pdf, $agent, $periodLabel, $generatedAt);
$this->drawAgentIdentity($pdf, $agent);
$this->drawQuotaSummary($pdf, $quota);
$this->drawValidationClause($pdf, $periodLabel, $generatedAt);
$this->drawSignatures($pdf);
$pdf->text(
self::MARGIN,
813,
sprintf('Document généré par PTA le %s - Référence agent : %s', $generatedAt->format('d/m/Y H:i'), (string) ($agent['matricule'] ?? '-')),
7
);
return $pdf->output();
}
private function drawHeader(SimplePdf $pdf, array $agent, string $periodLabel, DateTimeImmutable $generatedAt): void
{
$pdf->text(self::MARGIN, 48, 'CONTRAT HORAIRE ANNUEL', 18, true);
$pdf->text(self::MARGIN, 71, 'Validation du quota annuel de travail', 12, true);
$pdf->text(self::MARGIN, 92, 'Période de référence : ' . $periodLabel, 10);
$pdf->text(self::MARGIN + 315, 92, 'Édité le : ' . $generatedAt->format('d/m/Y'), 10);
$pdf->line(self::MARGIN, 108, self::PAGE_WIDTH - self::MARGIN, 108, 0.35);
$pdf->text(
self::MARGIN,
130,
sprintf('%s %s', (string) ($agent['prenom'] ?? ''), (string) ($agent['nom'] ?? '')),
14,
true
);
}
private function drawAgentIdentity(SimplePdf $pdf, array $agent): void
{
$x = self::MARGIN;
$y = 150.0;
$w = self::PAGE_WIDTH - (self::MARGIN * 2);
$h = 140.0;
$pdf->fillRect($x, $y, $w, 28, 0.93);
$pdf->rect($x, $y, $w, $h, 0.65);
$pdf->text($x + 12, $y + 19, 'Informations de l\'agent', 11, true);
$leftX = $x + 14;
$rightX = $x + 278;
$row1 = $y + 52;
$row2 = $y + 78;
$row3 = $y + 102;
$row4 = $y + 128;
$qualification = !empty($agent['est_diplome'])
? 'Diplômé' . (!empty($agent['diplome_libelle']) ? ' - ' . $agent['diplome_libelle'] : '')
: 'Non diplômé';
$pdf->text($leftX, $row1, 'Matricule : ' . (($agent['matricule'] ?? '') ?: '-'), 9);
$pdf->text($rightX, $row1, 'E-mail : ' . (($agent['email'] ?? '') ?: 'Non renseigné'), 9);
$pdf->text($leftX, $row2, 'Téléphone : ' . (($agent['telephone'] ?? '') ?: 'Non renseigné'), 9);
$pdf->text($rightX, $row2, 'Qualification : ' . $qualification, 9);
$pdf->text($leftX, $row3, 'Lieu principal : ' . (($agent['structure_principale_nom'] ?? '') ?: 'Non renseigné'), 9);
$pdf->text($rightX, $row3, 'Début du contrat : ' . (!empty($agent['date_debut_contrat']) ? (new DateTimeImmutable((string) $agent['date_debut_contrat']))->format('d/m/Y') : 'Non renseigné'), 9);
$address = (string) (($agent['adresse'] ?? '') ?: 'Non renseignée');
$addressLines = $pdf->wrap('Adresse : ' . $address, $w - 28, 8.5);
$pdf->text($leftX, $row4, (string) ($addressLines[0] ?? 'Adresse : Non renseignée'), 8.5);
$pdf->text($rightX, $row4, 'Type du lieu : ' . $this->typeLabel((string) ($agent['structure_principale_type_affectation'] ?? '')), 8.5);
}
private function drawQuotaSummary(SimplePdf $pdf, array $quota): void
{
$x = self::MARGIN;
$y = 318.0;
$w = self::PAGE_WIDTH - (self::MARGIN * 2);
$headerH = 30.0;
$rowH = 31.0;
$rows = [
['Quota annuel de référence à 100 %', $this->minutesLabel((int) ($quota['quota_reference_minutes'] ?? 0))],
['Quotité de travail applicable', $this->percentLabel((float) ($quota['quotite_travail'] ?? 100))],
['Quota annuel cible de l\'agent', (string) ($quota['quota_cible']['libelle'] ?? $this->minutesLabel((int) ($quota['quota_cible_minutes'] ?? 0)))],
['Heures validées dans PTA à la date d\'édition', (string) ($quota['valides']['libelle'] ?? $this->minutesLabel((int) ($quota['minutes_valides'] ?? 0)))],
['Heures encore en brouillon', (string) ($quota['brouillons']['libelle'] ?? $this->minutesLabel((int) ($quota['minutes_brouillon'] ?? 0)))],
['Solde restant à planifier / affecter', (string) ($quota['restantes']['libelle'] ?? $this->minutesLabel((int) ($quota['minutes_restantes'] ?? 0)))],
];
$pdf->fillRect($x, $y, $w, $headerH, 0.90);
$pdf->rect($x, $y, $w, $headerH + (count($rows) * $rowH), 0.60);
$pdf->text($x + 12, $y + 20, 'Synthèse du quota horaire', 11, true);
$labelWidth = 360.0;
$valueWidth = $w - $labelWidth;
$cursorY = $y + $headerH;
foreach ($rows as $index => [$label, $value]) {
if ($index % 2 === 1) {
$pdf->fillRect($x, $cursorY, $w, $rowH, 0.975);
}
$pdf->line($x, $cursorY, $x + $w, $cursorY, 0.82);
$pdf->line($x + $labelWidth, $cursorY, $x + $labelWidth, $cursorY + $rowH, 0.82);
$pdf->text($x + 12, $cursorY + 20, (string) $label, 8.6, $index === 2);
$pdf->text($x + $labelWidth + 12, $cursorY + 20, (string) $value, 9.2, $index === 2);
$cursorY += $rowH;
}
}
private function drawValidationClause(SimplePdf $pdf, string $periodLabel, DateTimeImmutable $generatedAt): void
{
$x = self::MARGIN;
$y = 557.0;
$w = self::PAGE_WIDTH - (self::MARGIN * 2);
$pdf->text($x, $y, 'Validation', 11, true);
$paragraphs = [
sprintf(
'Le présent document fixe et récapitule le quota annuel de travail applicable à l\'agent pour la période %s. La signature de l\'agent et la contre-signature du responsable valent validation du quota annuel indiqué ci-dessus.',
$periodLabel
),
'Les heures validées et les heures en brouillon constituent un état de suivi à la date d\'édition. Toute modification ultérieure du planning doit faire l\'objet d\'une nouvelle validation dans PTA lorsque cela est nécessaire.',
];
$cursorY = $y + 22;
foreach ($paragraphs as $paragraph) {
foreach ($pdf->wrap($paragraph, $w, 9) as $line) {
$pdf->text($x, $cursorY, $line, 9);
$cursorY += 13;
}
$cursorY += 8;
}
$pdf->text($x, 638, 'Fait à : ____________________________________', 9);
$pdf->text($x + 286, 638, 'Le : ____ / ____ / ________', 9);
$pdf->text($x, 658, 'Document établi le ' . $generatedAt->format('d/m/Y') . '.', 8);
}
private function drawSignatures(SimplePdf $pdf): void
{
$x = self::MARGIN;
$y = 680.0;
$gap = 18.0;
$w = (self::PAGE_WIDTH - (self::MARGIN * 2) - $gap) / 2;
$h = 120.0;
$pdf->rect($x, $y, $w, $h, 0.55);
$pdf->rect($x + $w + $gap, $y, $w, $h, 0.55);
$pdf->fillRect($x, $y, $w, 28, 0.94);
$pdf->fillRect($x + $w + $gap, $y, $w, 28, 0.94);
$pdf->text($x + 10, $y + 19, 'Signature de l\'agent', 10, true);
$pdf->text($x + $w + $gap + 10, $y + 19, 'Contre-signature du responsable', 10, true);
$pdf->text($x + 10, $y + 48, 'Nom : ______________________________', 8.5);
$pdf->text($x + 10, $y + 68, 'Mention « Lu et approuvé » :', 8.5);
$pdf->text($x + 10, $y + 101, 'Signature :', 8.5);
$rightX = $x + $w + $gap + 10;
$pdf->text($rightX, $y + 48, 'Nom / qualité : _____________________', 8.5);
$pdf->text($rightX, $y + 68, 'Mention « Bon pour validation » :', 8.5);
$pdf->text($rightX, $y + 101, 'Signature et cachet :', 8.5);
}
private function typeLabel(string $type): string
{
return match (strtoupper($type)) {
'EXTRASCOLAIRE' => 'Extrascolaire',
'PERISCOLAIRE' => 'Périscolaire',
default => 'Non renseigné',
};
}
private function percentLabel(float $rate): string
{
$formatted = rtrim(rtrim(number_format($rate, 2, ',', ' '), '0'), ',');
return $formatted . ' %';
}
private function minutesLabel(int $minutes): string
{
$sign = $minutes < 0 ? '-' : '';
$absolute = abs($minutes);
return sprintf('%s%d h %02d', $sign, intdiv($absolute, 60), $absolute % 60);
}
}