*/ private array $latestApprovals = []; public function __construct( private readonly TimesheetRepository $timesheetRepository, private readonly WorkingTimeRepository $workingTimeRepository, private readonly EventDispatcherInterface $eventDispatcher ) { } public function getYearSummary(Year $year, \DateTimeInterface $until): YearPerUserSummary { $yearPerUserSummary = new YearPerUserSummary($year); $summaryEvent = new WorkingTimeYearSummaryEvent($yearPerUserSummary, $until); $this->eventDispatcher->dispatch($summaryEvent); return $yearPerUserSummary; } public function getLatestApproval(User $user): ?WorkingTime { if ($user->getId() === null) { return null; } $key = 'u_' . $user->getId(); if (!\array_key_exists($key, $this->latestApprovals)) { $this->latestApprovals[$key] = $this->workingTimeRepository->getLatestApproval($user); } return $this->latestApprovals[$key]; } public function isApproved(User $user, \DateTimeInterface $dateTime): bool { $latestApproval = $this->getLatestApproval($user); if ($latestApproval === null) { return false; } $latestApprovalDate = $latestApproval->getDate(); $begin = \DateTimeImmutable::createFromInterface($dateTime); $begin = $begin->setTime(0, 0, 0); if ($begin > $latestApprovalDate) { return false; } return true; } public function getYear(User $user, \DateTimeInterface $yearDate, \DateTimeInterface $until): Year { $yearTimes = $this->workingTimeRepository->findForYear($user, $yearDate); $existing = []; foreach ($yearTimes as $workingTime) { $existing[$workingTime->getDate()->format('Y-m-d')] = $workingTime; } $year = new Year(\DateTimeImmutable::createFromInterface($yearDate), $user); $stats = null; $firstDay = $user->getWorkStartingDay(); foreach ($year->getMonths() as $month) { foreach ($month->getDays() as $day) { $key = $day->getDay()->format('Y-m-d'); if (\array_key_exists($key, $existing)) { $day->setWorkingTime($existing[$key]); continue; } if ($stats === null) { $stats = $this->getYearStatistics($yearDate, $user); } $dayDate = $day->getDay(); $result = new WorkingTime($user, $dayDate); if ($firstDay === null || $firstDay <= $dayDate) { $result->setExpectedTime($user->getWorkHoursForDay($dayDate)); } if (\array_key_exists($key, $stats)) { $result->setActualTime($stats[$key]); } $day->setWorkingTime($result); } } $event = new WorkingTimeYearEvent($year, $until); $this->eventDispatcher->dispatch($event); return $year; } public function getMonth(User $user, \DateTimeInterface $monthDate, \DateTimeInterface $until): Month { // uses the year, because that triggers the required events to collect all different working times $year = $this->getYear($user, $monthDate, $until); return $year->getMonth($monthDate); } public function approveMonth(User $user, Month $month, \DateTimeInterface $approvalDate, User $approvedBy): void { foreach ($month->getDays() as $day) { $workingTime = $day->getWorkingTime(); if ($workingTime === null) { continue; } if ($workingTime->getId() !== null) { continue; } if ($month->isLocked() || $workingTime->isApproved()) { continue; } $workingTime->setApprovedBy($approvedBy); // FIXME see calling method $workingTime->setApprovedAt(\DateTimeImmutable::createFromInterface($approvalDate)); $this->workingTimeRepository->scheduleWorkingTimeUpdate($workingTime); } $this->workingTimeRepository->persistScheduledWorkingTimes(); $key = 'u_' . $user->getId(); if (\array_key_exists($key, $this->latestApprovals)) { unset($this->latestApprovals[$key]); } $this->eventDispatcher->dispatch(new WorkingTimeApproveMonthEvent($user, $month, $approvalDate, $approvedBy)); } /** * @param \DateTimeInterface $year * @param User $user * @return array */ private function getYearStatistics(\DateTimeInterface $year, User $user): array { $dateTimeFactory = DateTimeFactory::createByUser($user); $begin = $dateTimeFactory->createStartOfYear($year); $end = $dateTimeFactory->createEndOfYear($year); $qb = $this->timesheetRepository->createQueryBuilder('t'); $qb ->select('COALESCE(SUM(t.duration), 0) as duration') ->addSelect('DATE(t.date) as day') ->where($qb->expr()->isNotNull('t.end')) ->andWhere($qb->expr()->between('t.date', ':begin', ':end')) ->andWhere($qb->expr()->eq('t.user', ':user')) ->setParameter('begin', $begin->format('Y-m-d')) ->setParameter('end', $end->format('Y-m-d')) ->setParameter('user', $user->getId()) ->addGroupBy('day') ; $results = $qb->getQuery()->getResult(); $durations = []; foreach ($results as $row) { $durations[$row['day']] = (int) $row['duration']; } return $durations; // @phpstan-ignore-line } }