getEmployee(); if (null === $employee) { return $data; } if ($operation instanceof DeleteOperationInterface) { if ($this->workHourRepository->hasValidatedInRange($employee, $data->getStartDate(), $data->getEndDate())) { throw new ConflictHttpException('Impossible de modifier une absence sur une période validée.'); } $this->entityManager->remove($data); $this->entityManager->flush(); return null; } $segments = $this->expandAbsenceRange($data); if ([] === $segments) { throw new UnprocessableEntityHttpException('La période de l\'absence est invalide.'); } $from = DateTimeImmutable::createFromInterface($segments[0]['date']); $to = DateTimeImmutable::createFromInterface($segments[count($segments) - 1]['date']); if ($this->workHourRepository->hasValidatedInRange($employee, $from, $to)) { throw new ConflictHttpException('Impossible de modifier une absence sur une période validée.'); } $existing = $this->absenceRepository->findByEmployeeAndDateRange($employee, $from, $to); foreach ($existing as $existingAbsence) { if ($existingAbsence->getId() === $data->getId()) { continue; } throw new ConflictHttpException('Cette période chevauche déjà une absence existante.'); } $first = array_shift($segments); if (null === $first) { throw new UnprocessableEntityHttpException('La période de l\'absence est invalide.'); } $data ->setStartDate($this->toMutableDate($first['date'])) ->setEndDate($this->toMutableDate($first['date'])) ->setStartHalf($first['startHalf']) ->setEndHalf($first['endHalf']) ; $this->clearWorkHoursForSegment($employee, $first); $this->entityManager->persist($data); foreach ($segments as $segment) { $absence = new Absence() ->setEmployee($employee) ->setType($data->getType()) ->setComment($data->getComment()) ->setStartDate($this->toMutableDate($segment['date'])) ->setEndDate($this->toMutableDate($segment['date'])) ->setStartHalf($segment['startHalf']) ->setEndHalf($segment['endHalf']) ; $this->clearWorkHoursForSegment($employee, $segment); $this->entityManager->persist($absence); } $this->entityManager->flush(); return $data; } /** * @return list */ private function expandAbsenceRange(Absence $absence): array { $start = DateTimeImmutable::createFromInterface($absence->getStartDate()); $end = DateTimeImmutable::createFromInterface($absence->getEndDate()); if ($start > $end) { throw new UnprocessableEntityHttpException('La date de fin ne peut pas être avant la date de début.'); } if ( $start->format('Y-m-d') === $end->format('Y-m-d') && HalfDay::PM === $absence->getStartHalf() && HalfDay::AM === $absence->getEndHalf() ) { throw new UnprocessableEntityHttpException('La demi-journée de fin ne peut pas être avant la demi-journée de début.'); } $days = new DatePeriod($start, new DateInterval('P1D'), $end->modify('+1 day')); $segments = []; foreach ($days as $day) { $isFirst = $day->format('Y-m-d') === $start->format('Y-m-d'); $isLast = $day->format('Y-m-d') === $end->format('Y-m-d'); $isSame = $isFirst && $isLast; if ($isSame) { $segments[] = [ 'date' => $day, 'startHalf' => $absence->getStartHalf(), 'endHalf' => $absence->getEndHalf(), ]; continue; } if ($isFirst && HalfDay::PM === $absence->getStartHalf()) { $segments[] = [ 'date' => $day, 'startHalf' => HalfDay::PM, 'endHalf' => HalfDay::PM, ]; continue; } if ($isLast && HalfDay::AM === $absence->getEndHalf()) { $segments[] = [ 'date' => $day, 'startHalf' => HalfDay::AM, 'endHalf' => HalfDay::AM, ]; continue; } $segments[] = [ 'date' => $day, 'startHalf' => HalfDay::AM, 'endHalf' => HalfDay::PM, ]; } return $segments; } private function toMutableDate(DateTimeImmutable $date): DateTime { return DateTime::createFromImmutable($date); } /** * @param array{date: DateTimeImmutable, startHalf: HalfDay, endHalf: HalfDay} $segment */ private function clearWorkHoursForSegment(Employee $employee, array $segment): void { $workHour = $this->workHourRepository->findOneByEmployeeAndDate($employee, $segment['date']); if (null === $workHour) { return; } // Demi-journée matin: on efface uniquement la plage du matin. if (HalfDay::AM === $segment['startHalf'] && HalfDay::AM === $segment['endHalf']) { $workHour ->setMorningFrom(null) ->setMorningTo(null) ; return; } // Demi-journée après-midi: on efface après-midi + soirée. if (HalfDay::PM === $segment['startHalf'] && HalfDay::PM === $segment['endHalf']) { $workHour ->setAfternoonFrom(null) ->setAfternoonTo(null) ->setEveningFrom(null) ->setEveningTo(null) ; return; } // Journée complète: on efface toutes les plages horaires. $workHour ->setMorningFrom(null) ->setMorningTo(null) ->setAfternoonFrom(null) ->setAfternoonTo(null) ->setEveningFrom(null) ->setEveningTo(null) ; } }