Gestion du changement de type de contrat + correction du calcule des RTT sur un contrat qui commence en milieu de semaine #19

Merged
tristan merged 55 commits from feat/contract-phase-view-selector into develop 2026-05-22 06:42:33 +00:00
2 changed files with 56 additions and 3 deletions
Showing only changes of commit 03add0d45a - Show all commits

View File

@@ -134,11 +134,18 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
[$periodFrom, $periodTo] = $this->resolvePeriodBounds($employee, $year, $phase);
// Forfait : jours à travailler sur l'exercice = jours ouvrés de la période congés acquis.
// Année pleine → 218 (252 34) ; entrée en cours d'année → prorata (ex. 168 13 ≈ 155).
// Forfait : jours à travailler sur l'exercice.
// Année pleine → cible contractuelle 218 ; les bonus week-end/férié et les jours
// fractionnés sont des congés EN PLUS, ils ne réduisent pas la cible. Entrée en cours
// d'année → jours ouvrés de la période congés acquis de l'entrée (repos proratisés +
// CP reportés), via yearSummary['acquiredDays'] (hors fractionnés/bonus). Ex. Grégory : 168 13 ≈ 155.
if (LeaveRuleCode::FORFAIT_218->value === $summary->ruleCode) {
$businessDaysInPeriod = $this->countBusinessDays($periodFrom, $periodTo, $this->buildRawPublicHolidayMap($periodFrom, $periodTo));
$summary->forfaitWorkTargetDays = $businessDaysInPeriod - $summary->acquiredDays;
$summary->forfaitWorkTargetDays = $this->computeForfaitWorkTargetDays(
$businessDaysInPeriod,
$this->isForfaitEntryYear($phase, $year),
$yearSummary['acquiredDays'],
);
}
// Forfait-only: leaves taken from N-1 stock do NOT decrement presence days.
@@ -890,6 +897,24 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
return $repoDaysYear * $businessDaysPeriod / $businessDaysYear;
}
/**
* Jours à travailler d'un forfait sur l'exercice consulté.
*
* - Année pleine : cible contractuelle 218 (bornée aux jours ouvrés de la période si
* celle-ci en compte moins). Les bonus week-end/férié et jours fractionnés sont des
* congés EN PLUS et ne réduisent pas la cible.
* - Entrée en cours d'année : jours ouvrés de la période congés acquis de l'entrée
* (repos proratisés + CP reportés, hors fractionnés/bonus). Ex. Grégory : 168 13 ≈ 155.
*/
private function computeForfaitWorkTargetDays(int $businessDaysInPeriod, bool $isEntryYear, float $entryAcquiredDays): float
{
if ($isEntryYear) {
return $businessDaysInPeriod - $entryAcquiredDays;
}
return (float) min($businessDaysInPeriod, self::FORFAIT_TARGET_WORKED_DAYS);
}
/**
* Vrai si la phase FORFAIT démarre en cours de l'année civile consultée
* (donc avec une période partielle), faux pour une année pleine ou un démarrage le 1er janvier.

View File

@@ -86,6 +86,34 @@ final class EmployeeLeaveSummaryProviderTest extends TestCase
self::assertEqualsWithDelta(25.0 / 12.0, $result, 0.0001);
}
public function testComputeForfaitWorkTargetDaysEntryYearProrates(): void
{
$provider = new ReflectionClass(EmployeeLeaveSummaryProvider::class)->newInstanceWithoutConstructor();
// Grégory : 168 jours ouvrés sur la période 12.94 congés acquis (repos + CP reportés) ≈ 155.06
$result = $this->invokePrivate($provider, 'computeForfaitWorkTargetDays', 168, true, 12.94);
self::assertEqualsWithDelta(155.06, $result, 0.001);
}
public function testComputeForfaitWorkTargetDaysFullYearIs218IgnoringBonusAndFractioned(): void
{
$provider = new ReflectionClass(EmployeeLeaveSummaryProvider::class)->newInstanceWithoutConstructor();
// Année pleine : la cible reste 218 quelle que soit la valeur des congés acquis
// (les bonus week-end/férié et jours fractionnés ne réduisent pas la cible).
self::assertSame(218.0, $this->invokePrivate($provider, 'computeForfaitWorkTargetDays', 252, false, 34.0));
self::assertSame(218.0, $this->invokePrivate($provider, 'computeForfaitWorkTargetDays', 252, false, 40.0));
}
public function testComputeForfaitWorkTargetDaysFullYearCapsAtBusinessDaysWhenFewer(): void
{
$provider = new ReflectionClass(EmployeeLeaveSummaryProvider::class)->newInstanceWithoutConstructor();
// Période avec moins de 218 jours ouvrés (phase forfait clôturée en cours d'année) → cap aux jours ouvrés.
self::assertSame(200.0, $this->invokePrivate($provider, 'computeForfaitWorkTargetDays', 200, false, 5.0));
}
public function testComputeProratedForfaitRepoDaysGregoryCase(): void
{
$provider = new ReflectionClass(EmployeeLeaveSummaryProvider::class)->newInstanceWithoutConstructor();