- 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>
6.0 KiB
6.0 KiB