setName('35h'); $contract->setTrackingMode(Contract::TRACKING_TIME); $contract->setWeeklyHours(35); $withContract = new Employee(); $withContract->setFirstName('Jean')->setLastName('Dupont'); $this->setEmployeeId($withContract, 1); $noContract = new Employee(); $noContract->setFirstName('Paul')->setLastName('Martin'); $this->setEmployeeId($noContract, 2); $workHour = new WorkHour(); $workHour->setEmployee($withContract) ->setWorkDate($date) ->setMorningFrom('08:00')->setMorningTo('12:00') ->setAfternoonFrom('13:00')->setAfternoonTo('17:00') ; $workHourRepo = $this->createStub(WorkHourReadRepositoryInterface::class); $workHourRepo->method('findByDateRangeAndEmployees')->willReturn([$workHour]); $absenceRepo = $this->createStub(AbsenceReadRepositoryInterface::class); $absenceRepo->method('findForPrint')->willReturn([]); $contractResolver = $this->createStub(EmployeeContractResolver::class); $contractResolver->method('resolveForEmployeesAndDays')->willReturn([ 1 => ['2026-06-08' => $contract], 2 => ['2026-06-08' => null], ]); $contractResolver->method('resolveIsDriverForEmployeesAndDays')->willReturn([ 1 => ['2026-06-08' => false], 2 => ['2026-06-08' => false], ]); $contractResolver->method('resolveWorkDaysMinutesForEmployeesAndDays')->willReturn([ 1 => ['2026-06-08' => null], 2 => ['2026-06-08' => null], ]); $holidayService = $this->createStub(PublicHolidayServiceInterface::class); $holidayService->method('getHolidaysDayByYears')->willReturn([]); // No holiday on this Monday → virtual credit resolves to 0 via the real resolver. $virtualResolver = new HolidayVirtualHoursResolver( new DailyReferenceMinutesResolver(), $holidayService, $contractResolver, ); $builder = new YearlyHoursExportBuilder( $workHourRepo, $absenceRepo, $contractResolver, new AbsenceSegmentsResolver(), new WorkedHoursCreditPolicy($contractResolver, new DailyReferenceMinutesResolver()), $holidayService, $virtualResolver, ); $rows = $builder->buildDayRowsForEmployees([$withContract, $noContract], $date); self::assertCount(1, $rows); self::assertSame(1, $rows[0]['employeeId']); self::assertSame('Dupont Jean', $rows[0]['employeeName']); self::assertSame('08:00', $rows[0]['morningFrom']); self::assertSame('17:00', $rows[0]['afternoonTo']); self::assertSame('8h', $rows[0]['total']); self::assertSame('8h', $rows[0]['dayHours']); self::assertSame('', $rows[0]['nightHours']); self::assertNull($rows[0]['statut']); self::assertNull($rows[0]['statutColor']); self::assertFalse($rows[0]['isWeekend']); } private function setEmployeeId(Employee $employee, int $id): void { $ref = new ReflectionProperty(Employee::class, 'id'); $ref->setAccessible(true); $ref->setValue($employee, $id); } }