diff --git a/src/Bundle/ChillTicketBundle/src/Action/Ticket/Handler/SetPersonsCommandHandler.php b/src/Bundle/ChillTicketBundle/src/Action/Ticket/Handler/SetPersonsCommandHandler.php new file mode 100644 index 000000000..e0a7cfbd0 --- /dev/null +++ b/src/Bundle/ChillTicketBundle/src/Action/Ticket/Handler/SetPersonsCommandHandler.php @@ -0,0 +1,56 @@ +getPersonHistories() as $personHistory) { + if (null !== $personHistory->getEndDate()) { + continue; + } + + if (!in_array($personHistory->getPerson(), $command->persons, true)) { + $personHistory->setEndDate($this->clock->now()); + if (($user = $this->security->getUser()) instanceof User) { + $personHistory->setRemovedBy($user); + } + } + } + + // add new addresses + foreach ($command->persons as $person) { + if (in_array($person, $ticket->getPersons(), true)) { + continue; + } + + $history = new PersonHistory($person, $ticket, $this->clock->now()); + $this->entityManager->persist($history); + } + } +} diff --git a/src/Bundle/ChillTicketBundle/src/Action/Ticket/SetPersonsCommand.php b/src/Bundle/ChillTicketBundle/src/Action/Ticket/SetPersonsCommand.php new file mode 100644 index 000000000..6bd47a567 --- /dev/null +++ b/src/Bundle/ChillTicketBundle/src/Action/Ticket/SetPersonsCommand.php @@ -0,0 +1,28 @@ + + */ + #[GreaterThan(0)] + #[Groups(['read'])] + public array $persons + ) {} +} diff --git a/src/Bundle/ChillTicketBundle/src/Controller/SetPersonsController.php b/src/Bundle/ChillTicketBundle/src/Controller/SetPersonsController.php new file mode 100644 index 000000000..3fbe254a8 --- /dev/null +++ b/src/Bundle/ChillTicketBundle/src/Controller/SetPersonsController.php @@ -0,0 +1,72 @@ +security->isGranted('ROLE_USER')) { + throw new AccessDeniedHttpException('Only users can set addressees.'); + } + + $command = $this->serializer->deserialize($request->getContent(), SetPersonsCommand::class, 'json', [AbstractNormalizer::GROUPS => ['read']]); + + return $this->registerSetPersons($command, $ticket); + } + + private function registerSetPersons(SetPersonsCommand $command, Ticket $ticket): Response + { + if (0 < count($errors = $this->validator->validate($command))) { + return new JsonResponse( + $this->serializer->serialize($errors, 'json'), + Response::HTTP_UNPROCESSABLE_ENTITY, + [], + true + ); + } + + $this->setPersonsCommandHandler->handle($ticket, $command); + + $this->entityManager->flush(); + + return new JsonResponse( + $this->serializer->serialize($ticket, 'json', ['groups' => 'read']), + Response::HTTP_OK, + [], + true, + ); + } +} diff --git a/src/Bundle/ChillTicketBundle/src/Entity/PersonHistory.php b/src/Bundle/ChillTicketBundle/src/Entity/PersonHistory.php index 927ea978f..1fce78fa1 100644 --- a/src/Bundle/ChillTicketBundle/src/Entity/PersonHistory.php +++ b/src/Bundle/ChillTicketBundle/src/Entity/PersonHistory.php @@ -91,4 +91,9 @@ class PersonHistory implements TrackCreationInterface return $this; } + + public function setRemovedBy(?User $removedBy): void + { + $this->removedBy = $removedBy; + } } diff --git a/src/Bundle/ChillTicketBundle/tests/Action/Ticket/Handler/SetPersonsCommandHandlerTest.php b/src/Bundle/ChillTicketBundle/tests/Action/Ticket/Handler/SetPersonsCommandHandlerTest.php new file mode 100644 index 000000000..3db50e7e6 --- /dev/null +++ b/src/Bundle/ChillTicketBundle/tests/Action/Ticket/Handler/SetPersonsCommandHandlerTest.php @@ -0,0 +1,122 @@ +prophesize(EntityManagerInterface::class); + $entityManager->persist(Argument::that(function ($arg) use ($person1) { + return $arg instanceof PersonHistory && $arg->getPerson() === $person1; + }))->shouldBeCalledOnce(); + $entityManager->persist(Argument::that(function ($arg) use ($group1) { + return $arg instanceof PersonHistory && $arg->getPerson() === $group1; + }))->shouldBeCalledOnce(); + + $handler = $this->buildHandler($entityManager->reveal()); + + $handler->handle($ticket, $command); + + self::assertCount(2, $ticket->getPersons()); + } + + public function testHandleExistingPersonIsNotRemovedNorCreatingDouble(): void + { + $ticket = new Ticket(); + $person = new Person(); + $history = new PersonHistory($person, $ticket, new \DateTimeImmutable('1 month ago')); + $command = new SetPersonsCommand([$person]); + + $entityManager = $this->prophesize(EntityManagerInterface::class); + $entityManager->persist(Argument::that(function ($arg) use ($person) { + return $arg instanceof PersonHistory && $arg->getPerson() === $person; + }))->shouldNotBeCalled(); + + $handler = $this->buildHandler($entityManager->reveal()); + + $handler->handle($ticket, $command); + + self::assertNull($history->getEndDate()); + self::assertCount(1, $ticket->getPersons()); + } + + public function testHandleRemoveExistingPerson(): void + { + $ticket = new Ticket(); + $person = new Person(); + $person2 = new Person(); + $history = new PersonHistory($person, $ticket, new \DateTimeImmutable('1 month ago')); + $command = new SetPersonsCommand([$person2]); + + $entityManager = $this->prophesize(EntityManagerInterface::class); + $entityManager->persist(Argument::that(function ($arg) use ($person2) { + return $arg instanceof PersonHistory && $arg->getPerson() === $person2; + }))->shouldBeCalled(); + + $handler = $this->buildHandler($entityManager->reveal()); + + $handler->handle($ticket, $command); + + self::assertNotNull($history->getEndDate()); + self::assertContains($person2, $ticket->getPersons()); + } + + public function testAddingDoublingPersonsDoesNotCreateDoubleHistories(): void + { + $ticket = new Ticket(); + $person = new Person(); + $command = new SetPersonsCommand([$person, $person]); + + $entityManager = $this->prophesize(EntityManagerInterface::class); + $entityManager->persist(Argument::that(function ($arg) use ($person) { + return $arg instanceof PersonHistory && $arg->getPerson() === $person; + }))->shouldBeCalledOnce(); + + $handler = $this->buildHandler($entityManager->reveal()); + + $handler->handle($ticket, $command); + + self::assertCount(1, $ticket->getPersons()); + } + + private function buildHandler(EntityManagerInterface $entityManager): SetPersonsCommandHandler + { + $security = $this->prophesize(Security::class); + $security->getUser()->willReturn(new User()); + + return new SetPersonsCommandHandler(new MockClock(), $entityManager, $security->reveal()); + } +}