Add ticket creation and associating by phone number functionality

This update introduces new features allowing the creation of tickets and associating them with a phone number. Specifically, relevant commands and their handlers have been created along with corresponding tests. An endpoint for ticket creation has also been set up, and the ViewTicketController has been renamed and refactored to EditTicketController to better reflect its function.
This commit is contained in:
Julien Fastré 2024-04-17 09:56:41 +02:00
parent 75fbec5489
commit 4b30d92282
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
9 changed files with 283 additions and 5 deletions

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\TicketBundle\Command\Ticket;
class AssociateByPhonenumberCommand
{
public function __construct(
public string $phonenumber,
) {}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\TicketBundle\Command\Ticket;
final readonly class CreateTicketCommand
{
public function __construct(
public string $externalReference = '',
) {}
}

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\TicketBundle\Command\Ticket\Handler;
use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface;
use Chill\TicketBundle\Command\Ticket\AssociateByPhonenumberCommand;
use Chill\TicketBundle\Entity\PersonHistory;
use Chill\TicketBundle\Entity\Ticket;
use Doctrine\ORM\EntityManagerInterface;
use libphonenumber\PhoneNumberUtil;
use Symfony\Component\Clock\ClockInterface;
class AssociateByPhonenumberCommandHandler
{
public function __construct(
private PersonACLAwareRepositoryInterface $personRepository,
private PhoneNumberUtil $phoneNumberUtil,
private ClockInterface $clock,
private EntityManagerInterface $entityManager,
) {}
public function __invoke(Ticket $ticket, AssociateByPhonenumberCommand $command): void
{
$phone = $this->phoneNumberUtil->parse($command->phonenumber);
$persons = $this->personRepository->findByPhone($phone);
foreach ($persons as $person) {
$history = new PersonHistory($person, $ticket, $this->clock->now());
$this->entityManager->persist($history);
}
}
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\TicketBundle\Command\Ticket\Handler;
use Chill\TicketBundle\Command\Ticket\CreateTicketCommand;
use Chill\TicketBundle\Entity\Ticket;
class CreateTicketCommandHandler
{
public function __invoke(CreateTicketCommand $command): Ticket
{
$ticket = new Ticket();
$ticket->setExternalRef($command->externalReference);
return $ticket;
}
}

View File

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\TicketBundle\Controller;
use Chill\TicketBundle\Command\Ticket\AssociateByPhonenumberCommand;
use Chill\TicketBundle\Command\Ticket\Handler\AssociateByPhonenumberCommandHandler;
use Chill\TicketBundle\Command\Ticket\CreateTicketCommand;
use Chill\TicketBundle\Command\Ticket\Handler\CreateTicketCommandHandler;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
final readonly class CreateTicketController
{
public function __construct(
private CreateTicketCommandHandler $createTicketCommandHandler,
private AssociateByPhonenumberCommandHandler $associateByPhonenumberCommandHandler,
private Security $security,
private UrlGeneratorInterface $urlGenerator,
private EntityManagerInterface $entityManager
) {}
#[Route('{_locale}/ticket/ticket/create')]
public function __invoke(Request $request): Response
{
if (!$this->security->isGranted('ROLE_USER')) {
throw new AccessDeniedHttpException('Only users are allowed to create tickets.');
}
$createCommand = new CreateTicketCommand($request->query->get('extId', ''));
$ticket = $this->createTicketCommandHandler->__invoke($createCommand);
$this->entityManager->persist($ticket);
if ($request->query->has('caller')) {
$associateByPhonenumberCommand = new AssociateByPhonenumberCommand($request->query->get('caller'));
$this->associateByPhonenumberCommandHandler->__invoke($ticket, $associateByPhonenumberCommand);
}
$this->entityManager->flush();
return new RedirectResponse(
$this->urlGenerator->generate('chill_ticket_ticket_edit', ['id' => $ticket->getId()])
);
}
}

View File

@ -11,14 +11,16 @@ declare(strict_types=1);
namespace Chill\TicketBundle\Controller;
use Chill\TicketBundle\Entity\Ticket;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ViewTicketController
class EditTicketController
{
#[Route('/{_locale}/ticket/ticket/{ticketId}', name: 'chill_ticket_ticket_view')]
public function __invoke(int $ticketId): Response
{
#[Route('/{_locale}/ticket/ticket/{id}/edit', name: 'chill_ticket_ticket_edit')]
public function __invoke(
Ticket $ticket
): Response {
return new Response('ok');
}
}

View File

@ -8,5 +8,6 @@ services:
tags:
- controller.service_arguments
Chill\TicketBundle\Command\Ticket\Handler\:
resource: '../Command/Ticket/Handler/'

View File

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\TicketBundle\Tests\Command\Ticket\Handler;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface;
use Chill\TicketBundle\Command\Ticket\AssociateByPhonenumberCommand;
use Chill\TicketBundle\Command\Ticket\Handler\AssociateByPhonenumberCommandHandler;
use Chill\TicketBundle\Entity\Ticket;
use Doctrine\Persistence\ObjectManager;
use libphonenumber\PhoneNumberUtil;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Clock\MockClock;
/**
* @internal
*
* @coversNothing
*/
class AssociateByPhonenumberCommandHandlerTest extends TestCase
{
use ProphecyTrait;
private function getHandler(
PersonACLAwareRepositoryInterface $personACLAwareRepository,
): AssociateByPhonenumberCommandHandler {
$objectManager = $this->prophesize(ObjectManager::class);
return new AssociateByPhonenumberCommandHandler(
$personACLAwareRepository,
PhoneNumberUtil::getInstance(),
new MockClock(),
$objectManager->reveal()
);
}
public function testHandleWithPersonFoundByPhonenumber(): void
{
$person = new Person();
$personAclAwareRepository = $this->prophesize(PersonACLAwareRepositoryInterface::class);
$personAclAwareRepository->findByPhone(Argument::any())->willReturn([$person]);
$handler = $this->getHandler($personAclAwareRepository->reveal());
$ticket = new Ticket();
$handler($ticket, new AssociateByPhonenumberCommand('+3281136917'));
self::assertSame($person, $ticket->getPersons()[0]);
}
}

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\TicketBundle\Tests\Command\Ticket\Handler;
use Chill\TicketBundle\Command\Ticket\CreateTicketCommand;
use Chill\TicketBundle\Command\Ticket\Handler\CreateTicketCommandHandler;
use Chill\TicketBundle\Entity\Ticket;
use PHPUnit\Framework\TestCase;
/**
* @internal
*
* @coversNothing
*/
class CreateTicketCommandHandlerTest extends TestCase
{
private function getHandler(): CreateTicketCommandHandler
{
return new CreateTicketCommandHandler();
}
public function testHandleWithoutReference(): void
{
$command = new CreateTicketCommand();
$actual = ($this->getHandler())($command);
self::assertInstanceOf(Ticket::class, $actual);
self::assertEquals('', $actual->getExternalRef());
}
public function testHandleWithReference(): void
{
$command = new CreateTicketCommand($ref = 'external-ref');
$actual = ($this->getHandler())($command);
self::assertInstanceOf(Ticket::class, $actual);
self::assertEquals($ref, $actual->getExternalRef());
}
}