feat(leave) : add prorated forfait repo days helper
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,7 @@ use Throwable;
|
|||||||
final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
|
final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
|
||||||
{
|
{
|
||||||
private const int FORFAIT_TARGET_WORKED_DAYS = 218;
|
private const int FORFAIT_TARGET_WORKED_DAYS = 218;
|
||||||
|
private const int FORFAIT_STANDARD_CP_DAYS = 25;
|
||||||
private const float CDI_NON_FORFAIT_STANDARD_ACQUIRED_DAYS = 25.0;
|
private const float CDI_NON_FORFAIT_STANDARD_ACQUIRED_DAYS = 25.0;
|
||||||
private const float CDI_NON_FORFAIT_STANDARD_ACQUIRED_SATURDAYS = 5.0;
|
private const float CDI_NON_FORFAIT_STANDARD_ACQUIRED_SATURDAYS = 5.0;
|
||||||
private const float CDI_NON_FORFAIT_STANDARD_ACCRUAL_PER_MONTH = self::CDI_NON_FORFAIT_STANDARD_ACQUIRED_DAYS / 12.0;
|
private const float CDI_NON_FORFAIT_STANDARD_ACCRUAL_PER_MONTH = self::CDI_NON_FORFAIT_STANDARD_ACQUIRED_DAYS / 12.0;
|
||||||
@@ -768,6 +769,23 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jours de repos forfait proratisés sur la fraction de jours ouvrés couverte.
|
||||||
|
*
|
||||||
|
* Repos année pleine = jours_ouvrés_année − 218 (cible travaillée) − 25 (CP standard).
|
||||||
|
* Pour 2026 : 252 − 218 − 25 = 9, proratisés au ratio jours_ouvrés_période / jours_ouvrés_année.
|
||||||
|
*/
|
||||||
|
private function computeProratedForfaitRepoDays(int $businessDaysYear, int $businessDaysPeriod): float
|
||||||
|
{
|
||||||
|
if ($businessDaysYear <= 0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$repoDaysYear = max(0, $businessDaysYear - self::FORFAIT_TARGET_WORKED_DAYS - self::FORFAIT_STANDARD_CP_DAYS);
|
||||||
|
|
||||||
|
return $repoDaysYear * $businessDaysPeriod / $businessDaysYear;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param null|array<string, string> $publicHolidays pre-built map (built if null)
|
* @param null|array<string, string> $publicHolidays pre-built map (built if null)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -86,6 +86,44 @@ final class EmployeeLeaveSummaryProviderTest extends TestCase
|
|||||||
self::assertEqualsWithDelta(25.0 / 12.0, $result, 0.0001);
|
self::assertEqualsWithDelta(25.0 / 12.0, $result, 0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testComputeProratedForfaitRepoDaysGregoryCase(): void
|
||||||
|
{
|
||||||
|
$provider = new ReflectionClass(EmployeeLeaveSummaryProvider::class)->newInstanceWithoutConstructor();
|
||||||
|
|
||||||
|
// 2026 : 252 jours ouvrés/an, 168 sur la période 01/05→31/12.
|
||||||
|
// repos année = 252 - 218 - 25 = 9 ; proratisé = 9 × 168/252 = 6.0
|
||||||
|
$result = $this->invokePrivate($provider, 'computeProratedForfaitRepoDays', 252, 168);
|
||||||
|
|
||||||
|
self::assertEqualsWithDelta(6.0, $result, 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testComputeProratedForfaitRepoDaysFullYearEquals9(): void
|
||||||
|
{
|
||||||
|
$provider = new ReflectionClass(EmployeeLeaveSummaryProvider::class)->newInstanceWithoutConstructor();
|
||||||
|
|
||||||
|
// Année pleine : 9 × 252/252 = 9.0
|
||||||
|
$result = $this->invokePrivate($provider, 'computeProratedForfaitRepoDays', 252, 252);
|
||||||
|
|
||||||
|
self::assertEqualsWithDelta(9.0, $result, 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testComputeProratedForfaitRepoDaysClampsNegativeToZero(): void
|
||||||
|
{
|
||||||
|
$provider = new ReflectionClass(EmployeeLeaveSummaryProvider::class)->newInstanceWithoutConstructor();
|
||||||
|
|
||||||
|
// Année avec trop peu de jours ouvrés (240 - 218 - 25 < 0) → 0
|
||||||
|
$result = $this->invokePrivate($provider, 'computeProratedForfaitRepoDays', 240, 160);
|
||||||
|
|
||||||
|
self::assertSame(0.0, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testComputeProratedForfaitRepoDaysZeroYearGuard(): void
|
||||||
|
{
|
||||||
|
$provider = new ReflectionClass(EmployeeLeaveSummaryProvider::class)->newInstanceWithoutConstructor();
|
||||||
|
|
||||||
|
self::assertSame(0.0, $this->invokePrivate($provider, 'computeProratedForfaitRepoDays', 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Phase resolution tests (Task 3 — phaseId support).
|
// Phase resolution tests (Task 3 — phaseId support).
|
||||||
// The repository / service dependencies are typed against final classes
|
// The repository / service dependencies are typed against final classes
|
||||||
|
|||||||
Reference in New Issue
Block a user