employeeRepository->findForPrintBySiteIds([]); $siteGroups = []; foreach ($employees as $employee) { if (!$employee->getHasActiveContract()) { continue; } $site = $employee->getSite(); $siteId = $site ? $site->getId() : 0; if (!isset($siteGroups[$siteId])) { $siteGroups[$siteId] = [ 'name' => $site ? $site->getName() : 'Sans site', 'color' => $site?->getColor() ?? '#ffd7d7', 'employees' => [], ]; } $siteGroups[$siteId]['employees'][] = $this->buildEmployeeRow($employee, $today); $this->entityManager->clear(); } // Re-load Twig environment after clear $options = new Options(); $options->set('isRemoteEnabled', true); $dompdf = new Dompdf($options); $html = $this->twig->render('leave-recap/print.html.twig', [ 'today' => $today, 'siteGroups' => $siteGroups, ]); $dompdf->loadHtml($html); $dompdf->setPaper('A4', 'portrait'); $dompdf->render(); $filename = sprintf('recap_conges_%s.pdf', $today->format('Y-m-d')); return new Response($dompdf->output(), Response::HTTP_OK, [ 'Content-Type' => 'application/pdf', 'Content-Disposition' => 'inline; filename="'.$filename.'"', ]); } private function buildEmployeeRow(Employee $employee, DateTimeImmutable $today): array { $contract = $employee->getContract(); $contractName = $contract?->getName(); $isForfait = ContractType::FORFAIT === $contract?->getType(); $nature = ContractNature::tryFrom($employee->getCurrentContractNature()); $isInterim = ContractNature::INTERIM === $nature; $cpN1Remaining = 0.0; $cpN = '-'; $acquiredSaturdays = '-'; $rtt = '-'; if (!$isInterim) { $leaveYear = $this->leaveSummaryProvider->resolveLeaveYearForToday($employee); $yearSummary = $this->leaveSummaryProvider->computeYearSummary($employee, $leaveYear); if (null !== $yearSummary) { if ($isForfait) { $cpN1Remaining = round($yearSummary['previousYearRemainingDays'], 2); $cpN = (string) round($yearSummary['acquiredDays'], 2); $acquiredSaturdays = '-'; } else { $cpN1Remaining = round($yearSummary['remainingDays'], 2); $cpN = (string) round($yearSummary['accruingDays'], 2); $acquiredSaturdays = (string) round($yearSummary['remainingSaturdays'], 2); } } if (!$isForfait && TrackingMode::PRESENCE->value !== $contract?->getTrackingMode()) { try { $rtt = $this->formatMinutes($this->computeAvailableRttMinutes($employee, $today)); } catch (Throwable) { $rtt = '-'; } } } return [ 'lastName' => $employee->getLastName(), 'firstName' => $employee->getFirstName(), 'contractName' => $contractName, 'cpN1Remaining' => $cpN1Remaining, 'cpN' => $cpN, 'acquiredSaturdays' => $acquiredSaturdays, 'rtt' => $rtt, ]; } private function computeAvailableRttMinutes(Employee $employee, DateTimeImmutable $today): int { $month = (int) $today->format('n'); $year = (int) $today->format('Y'); $exerciseYear = $month >= 6 ? $year + 1 : $year; // Exclude incomplete current week: limit to last Sunday $isoDay = (int) $today->format('N'); $limitDate = 7 === $isoDay ? $today : $today->modify('last sunday'); // Include the current week if all existing days are admin-validated if (7 !== $isoDay) { $currentWeekStart = $today->modify('monday this week'); $currentWeekEnd = $currentWeekStart->modify('+6 days'); $checkEnd = $this->resolveWeekEndForEmployee($employee, $currentWeekStart, $currentWeekEnd, $today); if ($this->workHourRepository->isWeekFullyValidated($employee, $currentWeekStart, $checkEnd)) { $limitDate = $currentWeekEnd; } } // Carry from previous exercise $carry = 0; $balance = $this->rttBalanceRepository->findOneByEmployeeAndYear($employee, $exerciseYear); if (null !== $balance) { $carry = $balance->getTotalOpeningMinutes(); } else { $previousTotal = $this->rttRecoveryService->computeTotalRecoveryForExercise($employee, $exerciseYear - 1); $carry = $previousTotal->totalMinutes; } // Current exercise (limited to completed weeks) $current = $this->rttRecoveryService->computeTotalRecoveryForExercise($employee, $exerciseYear, $limitDate); // Paid RTT $paid = 0; $payments = $this->rttPaymentRepository->findByEmployeeAndYear($employee, $exerciseYear); foreach ($payments as $payment) { $paid += $payment->getBase25Minutes() + $payment->getBonus25Minutes() + $payment->getBase50Minutes() + $payment->getBonus50Minutes(); } return $carry + $current->totalMinutes - $paid; } private function resolveWeekEndForEmployee(Employee $employee, DateTimeImmutable $weekStart, DateTimeImmutable $weekEnd, DateTimeImmutable $today): DateTimeImmutable { foreach ($employee->getContractPeriods() as $period) { if ($period->getStartDate() > $today) { continue; } $endDate = $period->getEndDate(); if (null === $endDate) { continue; } if ($endDate >= $weekStart && $endDate <= $weekEnd) { return $endDate; } } return $weekEnd; } private function formatMinutes(int $minutes): string { if (0 === $minutes) { return '0 h'; } $sign = $minutes < 0 ? '- ' : ''; $abs = abs($minutes); $h = intdiv($abs, 60); $m = $abs % 60; return 0 === $m ? "{$sign}{$h} h" : "{$sign}{$h} h {$m} m"; } }