fix(contracts) : hide contract phases entirely before RTT_START_DATE

EmployeeContractPhaseResolver now accepts the data-start date and filters
out phases whose endDate is strictly before it. No work-hour or absence
data exists before the application launch date, so legacy contract
periods that ended before that date would surface meaningless RTT/CP
figures in the phase picker.

For employees who joined long before the software (typical legacy 35h
contracts, in production since 2014), only the current phase remains
visible — which also collapses the picker (threshold ≥ 2 phases).

The Employee entity reads RTT_START_DATE from $_SERVER/$_ENV directly
since it has no DI. The resolver service is wired via services.yaml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-19 14:17:54 +02:00
parent 3da1cab2c8
commit f48f1d2f3a
6 changed files with 100 additions and 1 deletions

View File

@@ -112,6 +112,58 @@ final class EmployeeContractPhaseResolverTest extends TestCase
self::assertFalse($phases[1]->isDriver);
}
public function testPhasesEntirelyBeforeDataStartDateAreFilteredOut(): void
{
// H35 phase ends before 2026-02-23 → must be hidden; H39 phase spans the date → kept.
$employee = $this->buildEmployee([
['type' => ContractType::H35, 'hours' => 35, 'driver' => false, 'start' => '2014-07-01', 'end' => '2025-10-31'],
['type' => ContractType::H39, 'hours' => 39, 'driver' => false, 'start' => '2025-11-01', 'end' => null],
]);
$phases = new EmployeeContractPhaseResolver('2026-02-23')->resolvePhases($employee);
self::assertCount(1, $phases);
self::assertSame(ContractType::H39, $phases[0]->contractType);
}
public function testPhaseEndingExactlyOnDataStartDateIsKept(): void
{
// Edge case: a phase whose endDate equals the data start date is kept
// (the inequality is `>= $dataStart`, not strict).
$employee = $this->buildEmployee([
['type' => ContractType::H35, 'hours' => 35, 'driver' => false, 'start' => '2020-01-01', 'end' => '2026-02-23'],
['type' => ContractType::H39, 'hours' => 39, 'driver' => false, 'start' => '2026-02-24', 'end' => null],
]);
$phases = new EmployeeContractPhaseResolver('2026-02-23')->resolvePhases($employee);
self::assertCount(2, $phases);
}
public function testNoFilteringWhenDataStartDateIsEmpty(): void
{
$employee = $this->buildEmployee([
['type' => ContractType::H35, 'hours' => 35, 'driver' => false, 'start' => '2014-07-01', 'end' => '2020-12-31'],
['type' => ContractType::H39, 'hours' => 39, 'driver' => false, 'start' => '2021-01-01', 'end' => null],
]);
$phases = new EmployeeContractPhaseResolver()->resolvePhases($employee);
self::assertCount(2, $phases);
}
public function testInvalidDataStartDateStringIsTreatedAsNull(): void
{
$employee = $this->buildEmployee([
['type' => ContractType::H35, 'hours' => 35, 'driver' => false, 'start' => '2014-07-01', 'end' => '2020-12-31'],
['type' => ContractType::H39, 'hours' => 39, 'driver' => false, 'start' => '2021-01-01', 'end' => null],
]);
$phases = new EmployeeContractPhaseResolver('not-a-date')->resolvePhases($employee);
self::assertCount(2, $phases);
}
/**
* @param list<array{type: ContractType, hours: ?int, driver: bool, start: string, end: ?string}> $periodsSpec
*/