refactor(leave) : address Task 3 review (helper, dead param, phase nature, regression test)

- Extract private helper `exerciseYearForDate(date, isForfait)` to dedupe
  the date->leave-exercise-year expression duplicated across `clampYearToPhase`
  and `resolveFirstComputationYear` (4 copies collapsed into 1 helper + 4
  call sites).

- Remove the unused `ContractPhase $phase` parameter from
  `resolveLeavePeriodBounds`: the body never reads it (the phase cap is
  applied later by `resolvePeriodBounds`).

- Add `ContractNature $contractNature` to `ContractPhase` DTO, populated
  from the first period of the group by `EmployeeContractPhaseResolver`.
  Drop the `resolveNatureForPhase` lookup in `EmployeeLeaveSummaryProvider`
  in favor of `$phase->contractNature`. Expose `contractNature` in
  `Employee::getContractPhases()` array shape for frontend use.

- Fix regression for terminated employees calling `computeYearSummary`
  without an explicit phase (LeaveRecapRowBuilder,
  DumpVerificationSnapshotCommand). Before the refactor the period bounds,
  accrual end and taken end were NOT capped at the contract end for
  terminated employees, because `Employee::getCurrentContractEndDate()`
  returns null when no period covers "today". The new fallback phase
  (`isCurrent=false`, real `endDate`) was silently capping `to`. Add an
  internal `applyPhaseEndCap` flag, true when phase is explicit, false
  for legacy callers, threaded through `resolvePeriodBounds`,
  `resolveAccrualCalculationEndDate` and `resolveTakenCalculationEndDate`.

- Add regression test
  `testTerminatedEmployeeWithoutExplicitPhaseSkipsPhaseEndCap` proving
  that legacy callers keep the natural exercise upper bound while explicit
  phase callers get the cap.

- Add `contractNature` assertion in `EmployeeContractPhaseResolverTest`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-19 11:07:38 +02:00
parent 9efe0e81a0
commit 5a2a43bf51
6 changed files with 131 additions and 63 deletions

View File

@@ -439,7 +439,8 @@ class Employee
* startDate: string,
* endDate: ?string,
* periodIds: list<int>,
* isCurrent: bool
* isCurrent: bool,
* contractNature: string
* }>
*/
#[Groups(['employee:read'])]
@@ -449,14 +450,15 @@ class Employee
return array_map(
static fn (ContractPhase $phase): array => [
'id' => $phase->id,
'contractType' => $phase->contractType->value,
'weeklyHours' => $phase->weeklyHours,
'isDriver' => $phase->isDriver,
'startDate' => $phase->startDate->format('Y-m-d'),
'endDate' => $phase->endDate?->format('Y-m-d'),
'periodIds' => $phase->periodIds,
'isCurrent' => $phase->isCurrent,
'id' => $phase->id,
'contractType' => $phase->contractType->value,
'weeklyHours' => $phase->weeklyHours,
'isDriver' => $phase->isDriver,
'startDate' => $phase->startDate->format('Y-m-d'),
'endDate' => $phase->endDate?->format('Y-m-d'),
'periodIds' => $phase->periodIds,
'isCurrent' => $phase->isCurrent,
'contractNature' => $phase->contractNature->value,
],
$resolver->resolvePhases($this),
);