removeProcessor->process($data, $operation, $uriVariables, $context); } if (!$data instanceof Employee) { return $this->persistProcessor->process($data, $operation, $uriVariables, $context); } $isNew = null === $data->getId(); $previousContract = $this->resolvePreviousContract($data); $result = $this->persistProcessor->process($data, $operation, $uriVariables, $context); $currentContract = $data->getContract(); if (!$currentContract instanceof Contract) { return $result; } $today = new DateTimeImmutable('today'); $requestedContractNature = $this->resolveContractNature($data->getContractNature()); $requestedStartDate = $this->parseOptionalYmd($data->getContractStartDate(), 'contractStartDate'); $requestedEndDate = $this->parseOptionalYmd($data->getContractEndDate(), 'contractEndDate'); if ($isNew) { $startDate = $requestedStartDate ?? new DateTimeImmutable('1970-01-01'); $nature = $requestedContractNature ?? ContractNature::CDI; $this->assertPeriodDates($startDate, $requestedEndDate, $nature); $this->ensureContractPeriodExists($data, $currentContract, $startDate, $requestedEndDate, $nature); return $result; } $hasPeriodChangeRequest = null !== $requestedContractNature || null !== $requestedStartDate || null !== $requestedEndDate; if ($this->isSameContract($previousContract, $currentContract) && !$hasPeriodChangeRequest) { return $result; } $startDate = $requestedStartDate ?? $today; $todayPeriod = $this->periodRepository->findOneCoveringDate($data, $today); $nature = $requestedContractNature ?? $todayPeriod?->getContractNatureEnum() ?? ContractNature::CDI; $endDate = $requestedEndDate; $this->assertPeriodDates($startDate, $endDate, $nature); if ( null !== $todayPeriod && null === $todayPeriod->getEndDate() && $todayPeriod->getStartDate()->format('Y-m-d') === $startDate->format('Y-m-d') ) { $todayPeriod->setContract($currentContract); $todayPeriod->setContractNature($nature); $todayPeriod->setEndDate($endDate); $this->entityManager->flush(); return $result; } $this->periodRepository->closeOpenPeriods($data, $startDate->modify('-1 day')); $this->createPeriod($data, $currentContract, $startDate, $endDate, $nature); $this->entityManager->flush(); return $result; } private function resolvePreviousContract(Employee $employee): ?Contract { if (null === $employee->getId()) { return null; } $originalData = $this->entityManager->getUnitOfWork()->getOriginalEntityData($employee); $original = $originalData['contract'] ?? null; return $original instanceof Contract ? $original : null; } private function isSameContract(?Contract $first, ?Contract $second): bool { if (null === $first || null === $second) { return $first === $second; } return $first->getId() === $second->getId(); } private function ensureContractPeriodExists( Employee $employee, Contract $contract, DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ContractNature $nature, ): void { $covered = $this->periodRepository->findOneCoveringDate($employee, $startDate); if (null !== $covered) { return; } $this->createPeriod($employee, $contract, $startDate, $endDate, $nature); $this->entityManager->flush(); } private function createPeriod( Employee $employee, Contract $contract, DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ContractNature $nature, ): void { $period = new EmployeeContractPeriod() ->setEmployee($employee) ->setContract($contract) ->setStartDate($startDate) ->setEndDate($endDate) ->setContractNature($nature) ; $this->entityManager->persist($period); } private function resolveContractNature(?string $raw): ?ContractNature { if (null === $raw || '' === trim($raw)) { return null; } return ContractNature::tryFrom(trim($raw)) ?? throw new UnprocessableEntityHttpException('contractNature must be one of CDI, CDD, INTERIM.'); } private function parseOptionalYmd(?string $raw, string $field): ?DateTimeImmutable { if (null === $raw || '' === trim($raw)) { return null; } $value = trim($raw); $date = DateTimeImmutable::createFromFormat('Y-m-d', $value); if (!$date || $date->format('Y-m-d') !== $value) { throw new UnprocessableEntityHttpException(sprintf('%s must use Y-m-d format.', $field)); } return $date; } private function assertPeriodDates( DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ContractNature $nature ): void { if (null !== $endDate && $endDate < $startDate) { throw new UnprocessableEntityHttpException('contractEndDate cannot be before contractStartDate.'); } if ($nature->requiresEndDate() && null === $endDate) { throw new UnprocessableEntityHttpException('contractEndDate is required for CDD and INTERIM.'); } if (ContractNature::CDI === $nature && null !== $endDate) { throw new UnprocessableEntityHttpException('contractEndDate must be empty for CDI.'); } } }