[#SIRH] Récap salaire: congés N-1 forfait non affichés et comptés en présence
L'export récap salaire comptait tous les congés 'C' d'un forfait et ne créditait aucune présence sur les jours de congé. Or un congé imputé sur le stock N-1 ne doit pas s'afficher et doit compter comme jour de présence (règle déjà appliquée dans la fiche employé via EmployeeLeaveSummaryProvider). - Nouvelle méthode publique resolvePreviousYearTakenDays() (mutualise le budget N-1 avec la fiche: phase courante + recalcul jours payés). - SalaryRecapPrintProvider charge les congés depuis le 1er janvier et consomme le budget N-1 chronologiquement (splitForfaitCongesByN1): jours couverts N-1 retirés de l'affichage congés et ajoutés à la présence; au-delà = congés N. - Non-forfait / budget N-1 = 0: comportement inchangé. Vérifié end-to-end sur données prod (SARAZI mai: +1 présence, 4 congés affichés; LIOT/ODUNCU budget 0 après paiement N-1 -> congés affichés). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\State;
|
||||
|
||||
use App\Entity\Absence;
|
||||
use App\Enum\HalfDay;
|
||||
use App\Service\WorkHours\AbsenceSegmentsResolver;
|
||||
use App\State\SalaryRecapPrintProvider;
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionClass;
|
||||
use ReflectionProperty;
|
||||
|
||||
/**
|
||||
* Forfait N-1 split for the salary recap. The provider's collaborators are final classes
|
||||
* PHPUnit cannot double, so the pure split helper is exercised via reflection, with a real
|
||||
* AbsenceSegmentsResolver (no deps) injected into the uninitialized property.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class SalaryRecapPrintProviderTest extends TestCase
|
||||
{
|
||||
public function testN1BudgetPartiallyCoversADayAndOverflowsToN(): void
|
||||
{
|
||||
// Budget N-1 = 2.5 j ; 3 congés pleins (1 j) lun/mar/mer de janvier.
|
||||
// 1.0 + 1.0 + 0.5 consommés en N-1 → reste 0.5 j affiché en congé (le mercredi).
|
||||
$conges = [
|
||||
$this->buildConge('2026-01-05'),
|
||||
$this->buildConge('2026-01-06'),
|
||||
$this->buildConge('2026-01-07'),
|
||||
];
|
||||
|
||||
$result = $this->split($conges, 2.5, '2026-01-01', '2026-01-31');
|
||||
|
||||
self::assertSame(2.5, $result['n1PresenceDays']);
|
||||
self::assertSame(0.5, $result['count']);
|
||||
self::assertSame('07/01', $result['dates']);
|
||||
}
|
||||
|
||||
public function testN1BudgetConsumedInPriorMonthLeavesCurrentMonthFullyDisplayed(): void
|
||||
{
|
||||
// Budget 1 j, consommé par le congé de janvier. Récap de février → le congé de février
|
||||
// est entièrement imputé N (affiché, 0 présence N-1 dans le mois).
|
||||
$conges = [
|
||||
$this->buildConge('2026-01-12'),
|
||||
$this->buildConge('2026-02-09'),
|
||||
];
|
||||
|
||||
$result = $this->split($conges, 1.0, '2026-02-01', '2026-02-28');
|
||||
|
||||
self::assertSame(0.0, $result['n1PresenceDays']);
|
||||
self::assertSame(1.0, $result['count']);
|
||||
self::assertSame('09/02', $result['dates']);
|
||||
}
|
||||
|
||||
public function testZeroBudgetDisplaysAllCongesInMonth(): void
|
||||
{
|
||||
$conges = [$this->buildConge('2026-03-03')];
|
||||
|
||||
$result = $this->split($conges, 0.0, '2026-03-01', '2026-03-31');
|
||||
|
||||
self::assertSame(0.0, $result['n1PresenceDays']);
|
||||
self::assertSame(1.0, $result['count']);
|
||||
self::assertSame('03/03', $result['dates']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Absence> $conges
|
||||
*
|
||||
* @return array{count: float, dates: string, n1PresenceDays: float}
|
||||
*/
|
||||
private function split(array $conges, float $budget, string $from, string $to): array
|
||||
{
|
||||
$provider = new ReflectionClass(SalaryRecapPrintProvider::class)->newInstanceWithoutConstructor();
|
||||
new ReflectionProperty(SalaryRecapPrintProvider::class, 'absenceSegmentsResolver')
|
||||
->setValue($provider, new AbsenceSegmentsResolver());
|
||||
|
||||
return new ReflectionClass($provider::class)
|
||||
->getMethod('splitForfaitCongesByN1')
|
||||
->invoke($provider, $conges, $budget, new DateTimeImmutable($from), new DateTimeImmutable($to));
|
||||
}
|
||||
|
||||
private function buildConge(string $date): Absence
|
||||
{
|
||||
return new Absence()
|
||||
->setStartDate(new DateTime($date))
|
||||
->setEndDate(new DateTime($date))
|
||||
->setStartHalf(HalfDay::AM)
|
||||
->setEndHalf(HalfDay::PM)
|
||||
;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user