[#SIRH-32] Ajouter l'exercice 2026/2027 dans les congés/RTT (#20)
Auto Tag Develop / tag (push) Successful in 9s
Auto Tag Develop / tag (push) Successful in 9s
| Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [x] Pas de régression - [x] TU/TI/TF rédigée - [x] TU/TI/TF OK - [ ] CHANGELOG modifié Reviewed-on: #20 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #20.
This commit is contained in:
@@ -20,17 +20,20 @@ use App\State\EmployeeLeaveSummaryProvider;
|
||||
)]
|
||||
final class EmployeeLeaveSummary
|
||||
{
|
||||
public int $year = 0;
|
||||
public bool $isSupported = false;
|
||||
public string $ruleCode = '';
|
||||
public float $acquiredDays = 0.0;
|
||||
public float $remainingDays = 0.0;
|
||||
public float $takenDays = 0.0;
|
||||
public float $acquiredSaturdays = 0.0;
|
||||
public float $remainingSaturdays = 0.0;
|
||||
public float $takenSaturdays = 0.0;
|
||||
public float $fractionedDays = 0.0;
|
||||
public float $accruingDays = 0.0;
|
||||
public int $year = 0;
|
||||
public bool $isSupported = false;
|
||||
public string $ruleCode = '';
|
||||
public float $acquiredDays = 0.0;
|
||||
public float $remainingDays = 0.0;
|
||||
public float $takenDays = 0.0;
|
||||
public float $acquiredSaturdays = 0.0;
|
||||
public float $remainingSaturdays = 0.0;
|
||||
public float $takenSaturdays = 0.0;
|
||||
public float $fractionedDays = 0.0;
|
||||
public float $accruingDays = 0.0;
|
||||
|
||||
/** Brut généré sur l'exercice à ce jour (= accruingDays + congés pris en anticipé). Dénominateur de l'affichage « net / brut ». */
|
||||
public float $accruingDaysTotal = 0.0;
|
||||
public float $previousYearAcquiredDays = 0.0;
|
||||
public float $previousYearTakenDays = 0.0;
|
||||
public float $previousYearRemainingDays = 0.0;
|
||||
|
||||
@@ -188,24 +188,35 @@ final class LeaveRolloverCommand extends Command
|
||||
private function resolveCarry(Employee $employee, LeaveRuleCode $ruleCode, int $targetYear): array
|
||||
{
|
||||
$previousYear = $targetYear - 1;
|
||||
$previous = $this->leaveBalanceRepository->findOneByEmployeeRuleAndYear($employee, $ruleCode, $previousYear);
|
||||
if (null !== $previous) {
|
||||
$carryDays = $previous->getClosingDays() + $previous->getFractionedDays();
|
||||
$carrySaturdays = LeaveRuleCode::CDI_CDD_NON_FORFAIT === $ruleCode
|
||||
? $previous->getClosingSaturdays()
|
||||
: 0.0;
|
||||
} else {
|
||||
[$carryDays, $carrySaturdays] = $this->leaveBalanceComputationService
|
||||
->computeDynamicClosingForYear($employee, $ruleCode, $previousYear)
|
||||
;
|
||||
}
|
||||
|
||||
[$from, $to] = $this->leaveBalanceComputationService->resolvePeriodBounds($ruleCode, $previousYear);
|
||||
$hasSettlement = $this->leaveBalanceComputationService
|
||||
->hasPaidLeaveSettledClosureBetween($employee, $from, $to)
|
||||
;
|
||||
|
||||
if ($hasSettlement) {
|
||||
return [0.0, 0.0];
|
||||
$carryDays = 0.0;
|
||||
$carrySaturdays = 0.0;
|
||||
} else {
|
||||
// Compute the REAL closing of the ending exercise. computeDynamicClosingForYear
|
||||
// is bootstrap-aware (it anchors on the persisted opening balance of each year)
|
||||
// and already folds in accrual, taken absences and fractioned days. We must NOT
|
||||
// trust the stored closing_days: it is only ever written equal to the opening at
|
||||
// row creation (placeholder), so trusting it would propagate the opening and
|
||||
// ignore the year's accrual (cas Aurore : report 0 au lieu de 31).
|
||||
[$carryDays, $carrySaturdays] = $this->leaveBalanceComputationService
|
||||
->computeDynamicClosingForYear($employee, $ruleCode, $previousYear)
|
||||
;
|
||||
}
|
||||
|
||||
// Freeze the computed closing on the ending exercise's row so the column finally
|
||||
// holds a real, auditable value. The cron is idempotent — it never reaches here for
|
||||
// an already-rolled target year (existing rows are skipped upstream) — so a row that
|
||||
// was corrected manually in the DB afterwards is never overwritten.
|
||||
$previous = $this->leaveBalanceRepository->findOneByEmployeeRuleAndYear($employee, $ruleCode, $previousYear);
|
||||
if (null !== $previous) {
|
||||
$previous->setClosingDays($carryDays);
|
||||
$previous->setClosingSaturdays($carrySaturdays);
|
||||
}
|
||||
|
||||
return [$carryDays, $carrySaturdays];
|
||||
|
||||
@@ -51,9 +51,20 @@ final readonly class LeaveBalanceComputationService
|
||||
for ($year = $firstYear; $year <= $targetYear; ++$year) {
|
||||
[$from, $to] = $this->resolvePeriodBounds($ruleCode, $year);
|
||||
|
||||
// Bootstrap anchor: a manually-entered opening balance (production data
|
||||
// bootstrap) is the source of truth for the carry of that year — exactly
|
||||
// like EmployeeLeaveSummaryProvider::computeYearSummary for the live view.
|
||||
// Without it, the closing would be recomputed from the contract start with no
|
||||
// historical absences, inflating the carry by one full year of accrual for
|
||||
// every exercise predating the software (cas Aurore : 88 au lieu de 31).
|
||||
$openingBalance = $this->leaveBalanceRepository->findOneByEmployeeRuleAndYear($employee, $ruleCode, $year);
|
||||
|
||||
$carryDays = 0.0;
|
||||
$carrySaturdays = 0.0;
|
||||
if ($year > $firstYear) {
|
||||
if (null !== $openingBalance) {
|
||||
$carryDays = $openingBalance->getOpeningDays();
|
||||
$carrySaturdays = LeaveRuleCode::CDI_CDD_NON_FORFAIT === $ruleCode ? $openingBalance->getOpeningSaturdays() : 0.0;
|
||||
} elseif ($year > $firstYear) {
|
||||
[$previousFrom, $previousTo] = $this->resolvePeriodBounds($ruleCode, $year - 1);
|
||||
$hasSettlementOnPreviousYear = $this->periodRepository->hasPaidLeaveSettledClosureBetween($employee, $previousFrom, $previousTo);
|
||||
if (!$hasSettlementOnPreviousYear) {
|
||||
@@ -63,7 +74,10 @@ final readonly class LeaveBalanceComputationService
|
||||
}
|
||||
|
||||
$effectiveFrom = $this->resolveEffectivePeriodStart($employee, $from, $to);
|
||||
if ($effectiveFrom > $from) {
|
||||
// A shifted start (new hire / settled closure) zeroes the dynamic carry, but
|
||||
// an explicit bootstrap opening balance must be preserved (it already reflects
|
||||
// the real situation at the bootstrap date).
|
||||
if ($effectiveFrom > $from && null === $openingBalance) {
|
||||
$carryDays = 0.0;
|
||||
$carrySaturdays = 0.0;
|
||||
}
|
||||
@@ -74,11 +88,14 @@ final readonly class LeaveBalanceComputationService
|
||||
// Business days for forfait must use the RAW holiday list (excluded holidays
|
||||
// like "Lundi de Pentecôte" / journée de solidarité still count as non-working
|
||||
// days for the 218-day legal target).
|
||||
$totalBusinessDays = $this->countBusinessDaysInRange($from, $to, $this->buildRawPublicHolidayMap($from, $to));
|
||||
$baseAcquiredDays = (float) max(0, $totalBusinessDays - self::FORFAIT_TARGET_WORKED_DAYS);
|
||||
$acquiredDays = $carryDays + $baseAcquiredDays + $fractionedDays;
|
||||
$absences = $this->absenceRepository->findByEmployeeAndOverlappingDateRange($employee, $effectiveFrom, $to);
|
||||
[$takenDays] = $this->computeTakenAbsences($absences, $effectiveFrom, $to, false, false);
|
||||
$totalBusinessDays = $this->countBusinessDaysInRange($from, $to, $this->buildRawPublicHolidayMap($from, $to));
|
||||
$baseAcquiredDays = (float) max(0, $totalBusinessDays - self::FORFAIT_TARGET_WORKED_DAYS);
|
||||
$acquiredDays = $carryDays + $baseAcquiredDays + $fractionedDays;
|
||||
$absences = $this->absenceRepository->findByEmployeeAndOverlappingDateRange($employee, $effectiveFrom, $to);
|
||||
[$takenDays] = $this->computeTakenAbsences($absences, $effectiveFrom, $to, false, false);
|
||||
if (null !== $openingBalance) {
|
||||
$takenDays += $openingBalance->getTakenDays();
|
||||
}
|
||||
$previousRemainingDays = max(0.0, $acquiredDays - $takenDays);
|
||||
$previousRemainingSaturdays = 0.0;
|
||||
|
||||
@@ -120,6 +137,10 @@ final readonly class LeaveBalanceComputationService
|
||||
|
||||
$absences = $this->absenceRepository->findByEmployeeAndOverlappingDateRange($employee, $effectiveFrom, $to);
|
||||
[$takenDays, $takenSaturdays] = $this->computeTakenAbsences($absences, $effectiveFrom, $to, true, true);
|
||||
if (null !== $openingBalance) {
|
||||
$takenDays += $openingBalance->getTakenDays();
|
||||
$takenSaturdays += $openingBalance->getTakenSaturdays();
|
||||
}
|
||||
|
||||
$acquiredWithFractioned = $carryDays + $fractionedDays;
|
||||
$takenFromAcquired = min(max(0.0, $acquiredWithFractioned), $takenDays);
|
||||
|
||||
@@ -123,6 +123,7 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
|
||||
$summary->acquiredSaturdays = $yearSummary['acquiredSaturdays'];
|
||||
$summary->fractionedDays = $fractionedDays;
|
||||
$summary->accruingDays = $yearSummary['accruingDays'];
|
||||
$summary->accruingDaysTotal = $yearSummary['accruingDaysTotal'];
|
||||
$summary->takenDays = $yearSummary['takenDays'];
|
||||
$summary->takenSaturdays = $yearSummary['takenSaturdays'];
|
||||
$summary->remainingDays = $yearSummary['remainingDays'] + $fractionedDays;
|
||||
@@ -186,6 +187,7 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
|
||||
* acquiredDays: float,
|
||||
* acquiredSaturdays: float,
|
||||
* accruingDays: float,
|
||||
* accruingDaysTotal: float,
|
||||
* takenDays: float,
|
||||
* takenSaturdays: float,
|
||||
* remainingDays: float,
|
||||
@@ -336,8 +338,11 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
|
||||
$remainingSaturdaysToImpute = max(0.0, $takenSaturdays - $takenFromAcquiredSaturdays);
|
||||
$remainingGeneratedSaturdays = $generatedSaturdays - $remainingSaturdaysToImpute;
|
||||
|
||||
$acquiredDays = $carryDays;
|
||||
$accruingDays = $remainingGenerated + $remainingGeneratedSaturdays;
|
||||
$acquiredDays = $carryDays;
|
||||
$accruingDays = $remainingGenerated + $remainingGeneratedSaturdays;
|
||||
// Brut généré à ce jour, AVANT imputation des congés pris en anticipé
|
||||
// (dénominateur de l'affichage « net / brut » sur l'onglet Congés).
|
||||
$accruingDaysTotal = $generatedDays + $generatedSaturdays;
|
||||
$remainingDays = $remainingAcquired;
|
||||
$acquiredSaturdays = $carrySaturdays;
|
||||
$remainingSaturdays = max(0.0, $remainingAcquiredSaturdays);
|
||||
@@ -359,6 +364,7 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
|
||||
|
||||
$acquiredDays = $leavePolicy['acquiredDays'];
|
||||
$accruingDays = 0.0;
|
||||
$accruingDaysTotal = 0.0;
|
||||
$remainingDays = max(0.0, $acquiredDays - $takenFromCurrent);
|
||||
$acquiredSaturdays = 0.0;
|
||||
$remainingSaturdays = 0.0;
|
||||
@@ -373,6 +379,7 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
|
||||
'acquiredDays' => $acquiredDays,
|
||||
'acquiredSaturdays' => $acquiredSaturdays,
|
||||
'accruingDays' => $accruingDays,
|
||||
'accruingDaysTotal' => $accruingDaysTotal,
|
||||
'takenDays' => $takenDays,
|
||||
'takenSaturdays' => $takenSaturdays,
|
||||
'remainingDays' => $remainingDays,
|
||||
|
||||
Reference in New Issue
Block a user