mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-07-03 07:26:12 +00:00
Créer un point d'api de suggestion des usagers pour un ticket
This commit is contained in:
parent
0566ab0910
commit
4f93150874
@ -18,7 +18,6 @@ use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Command to get the report with curl:
|
||||
|
@ -17,7 +17,6 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function count;
|
||||
|
||||
// command to get the report with curl : curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff
|
||||
|
||||
|
@ -21,7 +21,6 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use function count;
|
||||
|
||||
// command to get the report with curl : curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff
|
||||
|
||||
|
@ -18,12 +18,15 @@ use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\Persistence\ObjectRepository;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use libphonenumber\PhoneNumberFormat;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
|
||||
class ThirdPartyRepository implements ObjectRepository
|
||||
{
|
||||
private readonly EntityRepository $repository;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, private readonly Connection $connection)
|
||||
public function __construct(EntityManagerInterface $em, private readonly Connection $connection, private readonly PhoneNumberUtil $phonenumberUtil)
|
||||
{
|
||||
$this->repository = $em->getRepository(ThirdParty::class);
|
||||
}
|
||||
@ -122,6 +125,43 @@ class ThirdPartyRepository implements ObjectRepository
|
||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds third-party records by phone number.
|
||||
*
|
||||
* The search is performed agains every phonenumber field (there are two phonenumber on a thirdParty).
|
||||
*
|
||||
* @param string|PhoneNumber $phonenumber The phone number to search for. Can be a string or a PhoneNumber object.
|
||||
* @param int $firstResult the index of the first result to retrieve (pagination start)
|
||||
* @param int $maxResults the maximum number of results to retrieve (pagination limit)
|
||||
*
|
||||
* @return list<ThirdParty> the result set containing matching third-party records
|
||||
*/
|
||||
public function findByPhonenumber(string|PhoneNumber $phonenumber, int $firstResult = 0, int $maxResults = 20): array
|
||||
{
|
||||
if ('' === $phonenumber) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$qb = $this->createQueryBuilder('tp');
|
||||
$qb->select('tp');
|
||||
|
||||
$qb->where(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->eq('t.telephone', ':phonenumber'),
|
||||
$qb->expr()->eq('t.telephone2', ':phonenumber')
|
||||
)
|
||||
);
|
||||
|
||||
$qb->setParameter(
|
||||
'phonenumber',
|
||||
is_string($phonenumber) ? $phonenumber : $this->phonenumberUtil->format($phonenumber, PhoneNumberFormat::E164)
|
||||
);
|
||||
|
||||
$qb->setFirstResult($firstResult)->setMaxResults($maxResults);
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search amongst parties associated to $centers, with $terms parameters.
|
||||
*
|
||||
|
@ -164,8 +164,8 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/UserGroupById'
|
||||
- $ref: '#/components/schemas/UserById'
|
||||
- $ref: '#/components/schemas/UserGroupById'
|
||||
- $ref: '#/components/schemas/UserById'
|
||||
|
||||
|
||||
responses:
|
||||
@ -197,8 +197,8 @@ paths:
|
||||
properties:
|
||||
addressee:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/UserGroupById'
|
||||
- $ref: '#/components/schemas/UserById'
|
||||
- $ref: '#/components/schemas/UserGroupById'
|
||||
- $ref: '#/components/schemas/UserById'
|
||||
|
||||
|
||||
responses:
|
||||
@ -288,3 +288,82 @@ paths:
|
||||
description: "OK"
|
||||
401:
|
||||
description: "UNAUTHORIZED"
|
||||
|
||||
/1.0/ticket/ticket/{id}/set-caller:
|
||||
post:
|
||||
tags:
|
||||
- ticket
|
||||
summary: Set a caller for this ticket
|
||||
description: |
|
||||
Set a caller to the ticket.
|
||||
|
||||
To remove the caller, set the caller field to null
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The ticket id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
caller:
|
||||
nullable: true
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/PersonById'
|
||||
- $ref: '#/components/schemas/ThirdPartyById'
|
||||
examples:
|
||||
add_user:
|
||||
value:
|
||||
caller:
|
||||
type: person
|
||||
id: 8
|
||||
summary: Set the person with id 8
|
||||
add_third_party:
|
||||
value:
|
||||
caller:
|
||||
type: thirdparty
|
||||
id: 10
|
||||
summary: Set the third party with id 10
|
||||
remove:
|
||||
value:
|
||||
caller: null
|
||||
summary: Remove the caller (set the caller to null)
|
||||
|
||||
responses:
|
||||
200:
|
||||
description: "OK"
|
||||
401:
|
||||
description: "UNAUTHORIZED"
|
||||
/1.0/ticket/ticket/{id}/suggest-person:
|
||||
get:
|
||||
tags:
|
||||
- ticket
|
||||
summary: Get a list of person suggested for the given ticket
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
description: The ticket id
|
||||
schema:
|
||||
type: integer
|
||||
format: integer
|
||||
minimum: 1
|
||||
responses:
|
||||
401:
|
||||
description: "UNAUTHORIZED"
|
||||
200:
|
||||
description: "OK"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Person'
|
||||
|
@ -13,8 +13,9 @@ namespace Chill\TicketBundle\Action\Ticket\Handler;
|
||||
|
||||
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
|
||||
use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface;
|
||||
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
|
||||
use Chill\TicketBundle\Action\Ticket\AssociateByPhonenumberCommand;
|
||||
use Chill\TicketBundle\Entity\PersonHistory;
|
||||
use Chill\TicketBundle\Entity\CallerHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
@ -23,6 +24,7 @@ class AssociateByPhonenumberCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly PersonACLAwareRepositoryInterface $personRepository,
|
||||
private readonly ThirdPartyRepository $thirdPartyRepository,
|
||||
private readonly PhonenumberHelper $phonenumberHelper,
|
||||
private readonly ClockInterface $clock,
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
@ -31,10 +33,13 @@ class AssociateByPhonenumberCommandHandler
|
||||
public function __invoke(Ticket $ticket, AssociateByPhonenumberCommand $command): void
|
||||
{
|
||||
$phone = $this->phonenumberHelper->parse($command->phonenumber);
|
||||
$persons = $this->personRepository->findByPhone($phone);
|
||||
$callers = [
|
||||
...$this->personRepository->findByPhone($phone),
|
||||
...$this->thirdPartyRepository->findByPhonenumber($phone),
|
||||
];
|
||||
|
||||
foreach ($persons as $person) {
|
||||
$history = new PersonHistory($person, $ticket, $this->clock->now());
|
||||
if (count($callers) > 0) {
|
||||
$history = new CallerHistory($callers[0], $ticket, $this->clock->now());
|
||||
$this->entityManager->persist($history);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
<?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\Action\Ticket\Handler;
|
||||
|
||||
use Chill\TicketBundle\Action\Ticket\SetCallerCommand;
|
||||
use Chill\TicketBundle\Entity\CallerHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
|
||||
/**
|
||||
* Handler for setting the caller of a ticket.
|
||||
*/
|
||||
class SetCallerCommandHandler
|
||||
{
|
||||
public function __construct(private readonly ClockInterface $clock) {}
|
||||
|
||||
public function __invoke(Ticket $ticket, SetCallerCommand $command): Ticket
|
||||
{
|
||||
// If the ticket already has the requested caller, return it without changes
|
||||
$currentCaller = $ticket->getCaller();
|
||||
if ($currentCaller === $command->caller) {
|
||||
return $ticket;
|
||||
}
|
||||
|
||||
// End the current caller history (if any)
|
||||
foreach ($ticket->getCallerHistories() as $callerHistory) {
|
||||
if (null === $callerHistory->getEndDate()) {
|
||||
$callerHistory->setEndDate($this->clock->now());
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new caller history with the new caller
|
||||
new CallerHistory(
|
||||
$command->caller,
|
||||
$ticket,
|
||||
$this->clock->now(),
|
||||
);
|
||||
|
||||
return $ticket;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<?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\Action\Ticket;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
|
||||
/**
|
||||
* Command to set the caller of a ticket.
|
||||
* The caller can be either a Person or a ThirdParty.
|
||||
*/
|
||||
final readonly class SetCallerCommand
|
||||
{
|
||||
/**
|
||||
* @param Person|ThirdParty|null $caller The caller to associate with the ticket
|
||||
*/
|
||||
public function __construct(
|
||||
public Person|ThirdParty|null $caller,
|
||||
) {}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
<?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\Action\Ticket\SetCallerCommand;
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\SetCallerCommandHandler;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
/**
|
||||
* Controller for setting the caller of a ticket.
|
||||
*/
|
||||
final readonly class SetCallerApiController
|
||||
{
|
||||
public function __construct(
|
||||
private SetCallerCommandHandler $setCallerCommandHandler,
|
||||
private Security $security,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private SerializerInterface $serializer,
|
||||
) {}
|
||||
|
||||
#[Route('/api/1.0/ticket/ticket/{id}/set-caller', name: 'chill_ticket_ticket_set_caller_api', requirements: ['id' => '\d+'], methods: ['POST'])]
|
||||
public function setCaller(Ticket $ticket, Request $request): Response
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_USER')) {
|
||||
throw new AccessDeniedHttpException('Only users are allowed to set ticket callers.');
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var SetCallerCommand $command */
|
||||
$command = $this->serializer->deserialize(
|
||||
$request->getContent(),
|
||||
SetCallerCommand::class,
|
||||
'json'
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
throw new BadRequestHttpException('Invalid request body: '.$e->getMessage(), $e);
|
||||
}
|
||||
|
||||
$this->setCallerCommandHandler->__invoke($ticket, $command);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($ticket, 'json', ['groups' => ['read']]),
|
||||
json: true
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?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\Entity\Ticket;
|
||||
use Chill\TicketBundle\Service\Ticket\SuggestPersonForTicketInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
final readonly class SuggestPersonForTicketApiController
|
||||
{
|
||||
public function __construct(
|
||||
private SuggestPersonForTicketInterface $suggestPersonForTicket,
|
||||
private SerializerInterface $serializer,
|
||||
private Security $security,
|
||||
) {}
|
||||
|
||||
#[Route('/api/1.0/ticket/ticket/{id}/suggest-person', name: 'chill_ticket_ticket_suggest_person_api', requirements: ['id' => '\d+'], methods: ['GET'])]
|
||||
public function __invoke(Ticket $ticket): Response
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_USER')) {
|
||||
throw new AccessDeniedHttpException('Only users are allowed to suggest persons for tickets.');
|
||||
}
|
||||
|
||||
$persons = $this->suggestPersonForTicket->suggestPerson($ticket, 0, 10);
|
||||
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($persons, 'json', ['groups' => ['read']]),
|
||||
json: true,
|
||||
);
|
||||
}
|
||||
}
|
148
src/Bundle/ChillTicketBundle/src/Entity/CallerHistory.php
Normal file
148
src/Bundle/ChillTicketBundle/src/Entity/CallerHistory.php
Normal file
@ -0,0 +1,148 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
|
||||
/**
|
||||
* Represents the history of a caller associated with a ticket.
|
||||
*
|
||||
* This entity is used to track the changes in a ticket's caller over time.
|
||||
* The caller can be either a Person or a ThirdParty.
|
||||
* Implements the TrackCreationInterface for tracking entity lifecycle creation.
|
||||
*/
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'caller_history', schema: 'chill_ticket')]
|
||||
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['ticket_caller_history' => CallerHistory::class])]
|
||||
class CallerHistory implements TrackCreationInterface
|
||||
{
|
||||
use TrackCreationTrait;
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?\DateTimeImmutable $endDate = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Person::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?Person $person = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: ThirdParty::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private ?ThirdParty $thirdParty = null;
|
||||
|
||||
public function __construct(
|
||||
ThirdParty|Person|null $caller,
|
||||
#[ORM\ManyToOne(targetEntity: Ticket::class)]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private Ticket $ticket,
|
||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: false)]
|
||||
#[Serializer\Groups(['read'])]
|
||||
private \DateTimeImmutable $startDate = new \DateTimeImmutable('now'),
|
||||
) {
|
||||
$this->setCaller($caller);
|
||||
$ticket->addCallerHistory($this);
|
||||
}
|
||||
|
||||
public function getEndDate(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPerson(): ?Person
|
||||
{
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
public function getThirdParty(): ?ThirdParty
|
||||
{
|
||||
return $this->thirdParty;
|
||||
}
|
||||
|
||||
public function getStartDate(): \DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function getTicket(): Ticket
|
||||
{
|
||||
return $this->ticket;
|
||||
}
|
||||
|
||||
public function setEndDate(?\DateTimeImmutable $endDate): void
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
}
|
||||
|
||||
public function setPerson(?Person $person): self
|
||||
{
|
||||
$this->person = $person;
|
||||
|
||||
// If setting a person, ensure thirdParty is null
|
||||
if (null !== $person) {
|
||||
$this->thirdParty = null;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setThirdParty(?ThirdParty $thirdParty): self
|
||||
{
|
||||
$this->thirdParty = $thirdParty;
|
||||
|
||||
// If setting a thirdParty, ensure person is null
|
||||
if (null !== $thirdParty) {
|
||||
$this->person = null;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the caller.
|
||||
*
|
||||
* This is a private method and should be only called while instance creation
|
||||
*/
|
||||
private function setCaller(Person|ThirdParty|null $caller): void
|
||||
{
|
||||
if ($caller instanceof Person) {
|
||||
$this->setPerson($caller);
|
||||
} elseif ($caller instanceof ThirdParty) {
|
||||
$this->setThirdParty($caller);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the caller, which can be either a Person or a ThirdParty.
|
||||
*/
|
||||
public function getCaller(): Person|ThirdParty|null
|
||||
{
|
||||
return $this->person ?? $this->thirdParty;
|
||||
}
|
||||
}
|
@ -96,6 +96,12 @@ class Ticket implements TrackCreationInterface, TrackUpdateInterface
|
||||
#[ORM\OneToMany(targetEntity: EmergencyStatusHistory::class, mappedBy: 'ticket', cascade: ['persist', 'remove'])]
|
||||
private Collection $emergencyStatusHistories;
|
||||
|
||||
/**
|
||||
* @var Collection<int, CallerHistory>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: CallerHistory::class, mappedBy: 'ticket', cascade: ['persist', 'remove'])]
|
||||
private Collection $callerHistories;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->addresseeHistory = new ArrayCollection();
|
||||
@ -105,6 +111,7 @@ class Ticket implements TrackCreationInterface, TrackUpdateInterface
|
||||
$this->inputHistories = new ArrayCollection();
|
||||
$this->stateHistories = new ArrayCollection();
|
||||
$this->emergencyStatusHistories = new ArrayCollection();
|
||||
$this->callerHistories = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
@ -294,4 +301,36 @@ class Ticket implements TrackCreationInterface, TrackUpdateInterface
|
||||
{
|
||||
return $this->emergencyStatusHistories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal use @see{CallerHistory::__construct} instead
|
||||
*/
|
||||
public function addCallerHistory(CallerHistory $callerHistory): void
|
||||
{
|
||||
$this->callerHistories->add($callerHistory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current caller (Person or ThirdParty) associated with this ticket.
|
||||
*
|
||||
* @return Person|ThirdParty|null
|
||||
*/
|
||||
public function getCaller()
|
||||
{
|
||||
foreach ($this->callerHistories as $callerHistory) {
|
||||
if (null === $callerHistory->getEndDate()) {
|
||||
return $callerHistory->getCaller();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ReadableCollection<int, CallerHistory>
|
||||
*/
|
||||
public function getCallerHistories(): ReadableCollection
|
||||
{
|
||||
return $this->callerHistories;
|
||||
}
|
||||
}
|
||||
|
@ -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\Repository;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
|
||||
final readonly class PersonTicketACLAwareRepository implements PersonTicketACLAwareRepositoryInterface
|
||||
{
|
||||
public function __construct(private EntityManagerInterface $em) {}
|
||||
|
||||
public function findPersonPreviouslyAssociatedWithCaller(Person|ThirdParty $caller, int $start = 0, int $limit = 100): array
|
||||
{
|
||||
$resultSetMappingBuilder = new ResultSetMappingBuilder($this->em);
|
||||
$resultSetMappingBuilder->addRootEntityFromClassMetadata(Person::class, 'p');
|
||||
|
||||
$callerClause = match ($caller instanceof Person) {
|
||||
true => 'caller_history.person_id = :callerId',
|
||||
false => 'caller_history.thirdparty_id = :callerId',
|
||||
};
|
||||
|
||||
$query = <<<SQL
|
||||
SELECT DISTINCT {$resultSetMappingBuilder->generateSelectClause()} FROM chill_person_person p
|
||||
WHERE p.id IN (
|
||||
SELECT person_id
|
||||
FROM chill_ticket.person_history person_history
|
||||
WHERE person_history.endDate IS NULL
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM chill_ticket.caller_history
|
||||
WHERE
|
||||
caller_history.ticket_id = person_history.ticket_id
|
||||
AND caller_history.endDate IS NULL
|
||||
AND {$callerClause}
|
||||
)
|
||||
ORDER BY person_history.startDate DESC, p.id ASC
|
||||
)
|
||||
OFFSET :start LIMIT :limit;
|
||||
SQL;
|
||||
|
||||
$nql = $this->em->createNativeQuery($query, $resultSetMappingBuilder);
|
||||
$nql
|
||||
->setParameter('callerId', $caller->getId())
|
||||
->setParameter('start', $start)
|
||||
->setParameter('limit', $limit);
|
||||
|
||||
|
||||
return $nql->getResult();
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<?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\Repository;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
|
||||
/**
|
||||
* Interface for repository operations related to accessing and managing
|
||||
* person tickets within an ACL-aware context.
|
||||
*/
|
||||
interface PersonTicketACLAwareRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Find all the Person entity that were previously associated with a ticket with the same caller.
|
||||
*
|
||||
* @return list<Person>
|
||||
*/
|
||||
public function findPersonPreviouslyAssociatedWithCaller(Person|ThirdParty $caller, int $start = 0, int $limit = 100): array;
|
||||
}
|
@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\TicketBundle\Entity\AddresseeHistory;
|
||||
use Chill\TicketBundle\Entity\CallerHistory;
|
||||
use Chill\TicketBundle\Entity\Comment;
|
||||
use Chill\TicketBundle\Entity\EmergencyStatusHistory;
|
||||
use Chill\TicketBundle\Entity\MotiveHistory;
|
||||
@ -53,6 +54,7 @@ final class TicketNormalizer implements NormalizerInterface, NormalizerAwareInte
|
||||
'createdBy' => $this->normalizer->normalize($object->getCreatedBy(), $format, $context),
|
||||
'currentState' => $object->getState()?->value ?? 'open',
|
||||
'emergency' => $object->getEmergencyStatus()?->value ?? 'no',
|
||||
'caller' => $this->normalizer->normalize($object->getCaller(), $format, ['groups' => 'read']),
|
||||
];
|
||||
}
|
||||
|
||||
@ -115,6 +117,17 @@ final class TicketNormalizer implements NormalizerInterface, NormalizerAwareInte
|
||||
],
|
||||
$ticket->getEmergencyStatusHistories()->toArray(),
|
||||
),
|
||||
...array_map(
|
||||
fn (CallerHistory $stateHistory) => [
|
||||
'event_type' => 'set_caller',
|
||||
'at' => $stateHistory->getStartDate(),
|
||||
'by' => $stateHistory->getCreatedBy(),
|
||||
'data' => [
|
||||
'new_caller' => $this->normalizer->normalize($ticket->getCaller(), $format, ['groups' => ['read']]),
|
||||
],
|
||||
],
|
||||
$ticket->getCallerHistories()->toArray(),
|
||||
),
|
||||
];
|
||||
|
||||
if (null !== $ticket->getCreatedBy() && null !== $ticket->getCreatedAt()) {
|
||||
|
@ -0,0 +1,30 @@
|
||||
<?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\Service\Ticket;
|
||||
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Chill\TicketBundle\Repository\PersonTicketACLAwareRepositoryInterface;
|
||||
|
||||
final readonly class SuggestPersonForTicket implements SuggestPersonForTicketInterface
|
||||
{
|
||||
public function __construct(private PersonTicketACLAwareRepositoryInterface $personTicketACLAwareRepository) {}
|
||||
|
||||
public function suggestPerson(Ticket $ticket, int $start = 0, int $limit = 20): array
|
||||
{
|
||||
$caller = $ticket->getCaller();
|
||||
if (null === $caller) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->personTicketACLAwareRepository->findPersonPreviouslyAssociatedWithCaller($caller, $start, $limit);
|
||||
}
|
||||
}
|
@ -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\Service\Ticket;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
|
||||
/**
|
||||
* Suggest Person entities for a ticket.
|
||||
*/
|
||||
interface SuggestPersonForTicketInterface
|
||||
{
|
||||
/**
|
||||
* @return list<Person>
|
||||
*/
|
||||
public function suggestPerson(Ticket $ticket, int $start = 0, int $limit = 20): array;
|
||||
}
|
@ -17,6 +17,9 @@ services:
|
||||
Chill\TicketBundle\Serializer\:
|
||||
resource: '../Serializer/'
|
||||
|
||||
Chill\TicketBundle\Service\:
|
||||
resource: '../Service/'
|
||||
|
||||
Chill\TicketBundle\Menu\:
|
||||
resource: '../Menu/'
|
||||
|
||||
|
@ -0,0 +1,93 @@
|
||||
<?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\Migrations\Ticket;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20250624105842 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add CallerHistory entity to associate a ticket with either a Person or a ThirdParty entity';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE SEQUENCE chill_ticket.caller_history_id_seq INCREMENT BY 1 MINVALUE 1 START 1
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE chill_ticket.caller_history (id INT NOT NULL, person_id INT DEFAULT NULL, ticket_id INT NOT NULL, endDate TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, startDate TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, thirdParty_id INT DEFAULT NULL, createdBy_id INT DEFAULT NULL, PRIMARY KEY(id))
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_AD0DCE24217BBB47 ON chill_ticket.caller_history (person_id)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_AD0DCE243EA5CAB0 ON chill_ticket.caller_history (thirdParty_id)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_AD0DCE24700047D2 ON chill_ticket.caller_history (ticket_id)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_AD0DCE243174800F ON chill_ticket.caller_history (createdBy_id)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
COMMENT ON COLUMN chill_ticket.caller_history.endDate IS '(DC2Type:datetime_immutable)'
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
COMMENT ON COLUMN chill_ticket.caller_history.startDate IS '(DC2Type:datetime_immutable)'
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
COMMENT ON COLUMN chill_ticket.caller_history.createdAt IS '(DC2Type:datetime_immutable)'
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE chill_ticket.caller_history ADD CONSTRAINT FK_AD0DCE24217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE chill_ticket.caller_history ADD CONSTRAINT FK_AD0DCE243EA5CAB0 FOREIGN KEY (thirdParty_id) REFERENCES chill_3party.third_party (id) NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE chill_ticket.caller_history ADD CONSTRAINT FK_AD0DCE24700047D2 FOREIGN KEY (ticket_id) REFERENCES chill_ticket.ticket (id) NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE chill_ticket.caller_history ADD CONSTRAINT FK_AD0DCE243174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE chill_ticket.caller_history ADD CONSTRAINT caller_history_not_overlaps
|
||||
exclude using gist (ticket_id with =, tsrange(startdate, enddate) with &&)
|
||||
deferrable initially deferred
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP SEQUENCE chill_ticket.caller_history_id_seq CASCADE
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE chill_ticket.caller_history DROP CONSTRAINT FK_AD0DCE24217BBB47
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE chill_ticket.caller_history DROP CONSTRAINT FK_AD0DCE243EA5CAB0
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE chill_ticket.caller_history DROP CONSTRAINT FK_AD0DCE24700047D2
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE chill_ticket.caller_history DROP CONSTRAINT FK_AD0DCE243174800F
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP TABLE chill_ticket.caller_history
|
||||
SQL);
|
||||
}
|
||||
}
|
@ -14,10 +14,13 @@ namespace Chill\TicketBundle\Tests\Action\Ticket\Handler;
|
||||
use Chill\MainBundle\Phonenumber\PhonenumberHelper;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
|
||||
use Chill\TicketBundle\Action\Ticket\AssociateByPhonenumberCommand;
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\AssociateByPhonenumberCommandHandler;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
@ -37,6 +40,7 @@ class AssociateByPhonenumberCommandHandlerTest extends TestCase
|
||||
|
||||
private function getHandler(
|
||||
PersonACLAwareRepositoryInterface $personACLAwareRepository,
|
||||
ThirdPartyRepository $thirdPartyRepository,
|
||||
): AssociateByPhonenumberCommandHandler {
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
$phonenumberHelper = new PhonenumberHelper(
|
||||
@ -51,6 +55,7 @@ class AssociateByPhonenumberCommandHandlerTest extends TestCase
|
||||
|
||||
return new AssociateByPhonenumberCommandHandler(
|
||||
$personACLAwareRepository,
|
||||
$thirdPartyRepository,
|
||||
$phonenumberHelper,
|
||||
new MockClock(),
|
||||
$entityManager->reveal()
|
||||
@ -63,12 +68,29 @@ class AssociateByPhonenumberCommandHandlerTest extends TestCase
|
||||
|
||||
$personAclAwareRepository = $this->prophesize(PersonACLAwareRepositoryInterface::class);
|
||||
$personAclAwareRepository->findByPhone(Argument::any())->willReturn([$person]);
|
||||
$thirdPartyRepository = $this->prophesize(ThirdPartyRepository::class);
|
||||
$thirdPartyRepository->findByPhonenumber(Argument::type(PhoneNumber::class))->willReturn([]);
|
||||
|
||||
$handler = $this->getHandler($personAclAwareRepository->reveal());
|
||||
$handler = $this->getHandler($personAclAwareRepository->reveal(), $thirdPartyRepository->reveal());
|
||||
|
||||
$ticket = new Ticket();
|
||||
$handler($ticket, new AssociateByPhonenumberCommand('+3281136917'));
|
||||
|
||||
self::assertSame($person, $ticket->getPersons()[0]);
|
||||
self::assertSame($person, $ticket->getCaller());
|
||||
}
|
||||
|
||||
public function testHandleWithThirdPartyFoundByPhonenumber(): void
|
||||
{
|
||||
$personAclAwareRepository = $this->prophesize(PersonACLAwareRepositoryInterface::class);
|
||||
$personAclAwareRepository->findByPhone(Argument::any())->willReturn([]);
|
||||
$thirdPartyRepository = $this->prophesize(ThirdPartyRepository::class);
|
||||
$thirdPartyRepository->findByPhonenumber(Argument::type(PhoneNumber::class))->willReturn([$thirdParty = new ThirdParty()]);
|
||||
|
||||
$handler = $this->getHandler($personAclAwareRepository->reveal(), $thirdPartyRepository->reveal());
|
||||
|
||||
$ticket = new Ticket();
|
||||
$handler($ticket, new AssociateByPhonenumberCommand('081136917'));
|
||||
|
||||
self::assertSame($thirdParty, $ticket->getCaller());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,178 @@
|
||||
<?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\Action\Ticket\Handler;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\SetCallerCommandHandler;
|
||||
use Chill\TicketBundle\Action\Ticket\SetCallerCommand;
|
||||
use Chill\TicketBundle\Entity\CallerHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
use Symfony\Component\Clock\MockClock;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class SetCallerCommandHandlerTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
private \DateTimeImmutable $now;
|
||||
private ClockInterface $clock;
|
||||
private SetCallerCommandHandler $handler;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->now = new \DateTimeImmutable('2023-01-01 12:00:00');
|
||||
$this->clock = new MockClock($this->now);
|
||||
$this->handler = new SetCallerCommandHandler($this->clock);
|
||||
}
|
||||
|
||||
public function testSetPersonAsCaller(): void
|
||||
{
|
||||
// Arrange
|
||||
$ticket = new Ticket();
|
||||
$person = new Person();
|
||||
$command = new SetCallerCommand($person);
|
||||
|
||||
// Act
|
||||
$result = ($this->handler)($ticket, $command);
|
||||
|
||||
// Assert
|
||||
self::assertSame($ticket, $result);
|
||||
self::assertSame($person, $ticket->getCaller());
|
||||
self::assertCount(1, $ticket->getCallerHistories());
|
||||
|
||||
$callerHistory = $ticket->getCallerHistories()->first();
|
||||
self::assertInstanceOf(CallerHistory::class, $callerHistory);
|
||||
self::assertSame($person, $callerHistory->getPerson());
|
||||
self::assertNull($callerHistory->getThirdParty());
|
||||
self::assertEquals($this->now->getTimestamp(), $callerHistory->getStartDate()->getTimestamp());
|
||||
self::assertNull($callerHistory->getEndDate());
|
||||
}
|
||||
|
||||
public function testSetThirdPartyAsCaller(): void
|
||||
{
|
||||
// Arrange
|
||||
$ticket = new Ticket();
|
||||
$thirdParty = new ThirdParty();
|
||||
$command = new SetCallerCommand($thirdParty);
|
||||
|
||||
// Act
|
||||
$result = ($this->handler)($ticket, $command);
|
||||
|
||||
// Assert
|
||||
self::assertSame($ticket, $result);
|
||||
self::assertSame($thirdParty, $ticket->getCaller());
|
||||
self::assertCount(1, $ticket->getCallerHistories());
|
||||
|
||||
$callerHistory = $ticket->getCallerHistories()->first();
|
||||
self::assertInstanceOf(CallerHistory::class, $callerHistory);
|
||||
self::assertNull($callerHistory->getPerson());
|
||||
self::assertSame($thirdParty, $callerHistory->getThirdParty());
|
||||
self::assertEquals($this->now->getTimestamp(), $callerHistory->getStartDate()->getTimestamp());
|
||||
self::assertNull($callerHistory->getEndDate());
|
||||
}
|
||||
|
||||
public function testChangeCallerFromPersonToThirdParty(): void
|
||||
{
|
||||
// Arrange
|
||||
$ticket = new Ticket();
|
||||
$person = new Person();
|
||||
$thirdParty = new ThirdParty();
|
||||
|
||||
// Set initial person caller
|
||||
$initialCallerHistory = new CallerHistory($person, $ticket, $this->now);
|
||||
$initialCallerHistory->setPerson($person);
|
||||
|
||||
// Create command to change to third party
|
||||
$command = new SetCallerCommand($thirdParty);
|
||||
|
||||
// Act
|
||||
$this->clock->modify('+ 10 minutes');
|
||||
$result = ($this->handler)($ticket, $command);
|
||||
|
||||
// Assert
|
||||
self::assertSame($ticket, $result);
|
||||
self::assertSame($thirdParty, $ticket->getCaller());
|
||||
self::assertCount(2, $ticket->getCallerHistories());
|
||||
|
||||
// Check that the first history is ended
|
||||
$firstCallerHistory = $ticket->getCallerHistories()->first();
|
||||
self::assertSame($person, $firstCallerHistory->getPerson());
|
||||
self::assertEquals($this->clock->now()->getTimestamp(), $firstCallerHistory->getEndDate()->getTimestamp());
|
||||
|
||||
// Check that the new history is created correctly
|
||||
$lastCallerHistory = $ticket->getCallerHistories()->last();
|
||||
self::assertNull($lastCallerHistory->getPerson());
|
||||
self::assertSame($thirdParty, $lastCallerHistory->getThirdParty());
|
||||
self::assertSame($this->clock->now()->getTimestamp(), $lastCallerHistory->getStartDate()->getTimestamp());
|
||||
self::assertNull($lastCallerHistory->getEndDate());
|
||||
}
|
||||
|
||||
public function testRemoveCaller(): void
|
||||
{
|
||||
// Arrange
|
||||
$ticket = new Ticket();
|
||||
$person = new Person();
|
||||
|
||||
// Set initial person caller
|
||||
$initialCallerHistory = new CallerHistory($person, $ticket, $this->now);
|
||||
|
||||
// Create command to remove caller
|
||||
$command = new SetCallerCommand(null);
|
||||
|
||||
// Act
|
||||
$result = ($this->handler)($ticket, $command);
|
||||
|
||||
// Assert
|
||||
self::assertSame($ticket, $result);
|
||||
self::assertNull($ticket->getCaller());
|
||||
self::assertCount(2, $ticket->getCallerHistories());
|
||||
|
||||
// Check that the history is ended
|
||||
$callerHistory = $ticket->getCallerHistories()->first();
|
||||
self::assertSame($person, $callerHistory->getPerson());
|
||||
self::assertEquals($this->now->getTimestamp(), $callerHistory->getEndDate()->getTimestamp());
|
||||
}
|
||||
|
||||
public function testNoChangeWhenCallerIsAlreadySet(): void
|
||||
{
|
||||
// Arrange
|
||||
$ticket = new Ticket();
|
||||
$person = new Person();
|
||||
|
||||
// Set initial person caller
|
||||
$initialCallerHistory = new CallerHistory($person, $ticket, $this->now);
|
||||
|
||||
// Create command with the same person
|
||||
$command = new SetCallerCommand($person);
|
||||
|
||||
// Act
|
||||
$result = ($this->handler)($ticket, $command);
|
||||
|
||||
// Assert
|
||||
self::assertSame($ticket, $result);
|
||||
self::assertSame($person, $ticket->getCaller());
|
||||
self::assertCount(1, $ticket->getCallerHistories());
|
||||
|
||||
// Check that the history is unchanged
|
||||
$callerHistory = $ticket->getCallerHistories()->first();
|
||||
self::assertSame($person, $callerHistory->getPerson());
|
||||
self::assertNull($callerHistory->getEndDate());
|
||||
}
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
<?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\Controller;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\SetCallerCommandHandler;
|
||||
use Chill\TicketBundle\Action\Ticket\SetCallerCommand;
|
||||
use Chill\TicketBundle\Controller\SetCallerApiController;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
final class SetCallerApiControllerTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
public function testSetCallerWithoutPermission(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
$request = new Request();
|
||||
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->isGranted('ROLE_USER')->willReturn(false);
|
||||
|
||||
$setCallerCommandHandler = $this->prophesize(SetCallerCommandHandler::class);
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
$serializer = $this->prophesize(SerializerInterface::class);
|
||||
|
||||
$controller = new SetCallerApiController(
|
||||
$setCallerCommandHandler->reveal(),
|
||||
$security->reveal(),
|
||||
$entityManager->reveal(),
|
||||
$serializer->reveal(),
|
||||
);
|
||||
|
||||
$this->expectException(AccessDeniedHttpException::class);
|
||||
$controller->setCaller($ticket, $request);
|
||||
}
|
||||
|
||||
public function testSetCallerWithInvalidRequestBody(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
$request = new Request([], [], [], [], [], [], 'invalid json');
|
||||
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->isGranted('ROLE_USER')->willReturn(true);
|
||||
|
||||
$setCallerCommandHandler = $this->prophesize(SetCallerCommandHandler::class);
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
|
||||
$serializer = $this->prophesize(SerializerInterface::class);
|
||||
$serializer->deserialize('invalid json', SetCallerCommand::class, 'json')
|
||||
->willThrow(new \Exception('Invalid JSON'));
|
||||
|
||||
$controller = new SetCallerApiController(
|
||||
$setCallerCommandHandler->reveal(),
|
||||
$security->reveal(),
|
||||
$entityManager->reveal(),
|
||||
$serializer->reveal(),
|
||||
);
|
||||
|
||||
$this->expectException(BadRequestHttpException::class);
|
||||
$controller->setCaller($ticket, $request);
|
||||
}
|
||||
|
||||
public function testSetCallerWithValidRequest(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
$request = new Request([], [], [], [], [], [], '{"caller": {"id": 123, "type": "person"}}');
|
||||
|
||||
$person = new Person();
|
||||
$command = new SetCallerCommand($person);
|
||||
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->isGranted('ROLE_USER')->willReturn(true);
|
||||
|
||||
$serializer = $this->prophesize(SerializerInterface::class);
|
||||
$serializer->deserialize('{"caller": {"id": 123, "type": "person"}}', SetCallerCommand::class, 'json')
|
||||
->willReturn($command);
|
||||
$serializer->serialize($ticket, 'json', ['groups' => ['read']])
|
||||
->willReturn('{}')
|
||||
->shouldBeCalled();
|
||||
|
||||
$setCallerCommandHandler = $this->prophesize(SetCallerCommandHandler::class);
|
||||
$setCallerCommandHandler->__invoke($ticket, $command)
|
||||
->willReturn($ticket)
|
||||
->shouldBeCalled();
|
||||
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
$entityManager->flush()->shouldBeCalled();
|
||||
|
||||
$controller = new SetCallerApiController(
|
||||
$setCallerCommandHandler->reveal(),
|
||||
$security->reveal(),
|
||||
$entityManager->reveal(),
|
||||
$serializer->reveal(),
|
||||
);
|
||||
|
||||
$response = $controller->setCaller($ticket, $request);
|
||||
|
||||
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||
}
|
||||
|
||||
public function testSetCallerWithThirdParty(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
$request = new Request([], [], [], [], [], [], '{"caller": {"id": 456, "type": "thirdParty"}}');
|
||||
|
||||
$thirdParty = $this->prophesize(ThirdParty::class)->reveal();
|
||||
$command = new SetCallerCommand($thirdParty);
|
||||
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->isGranted('ROLE_USER')->willReturn(true);
|
||||
|
||||
$serializer = $this->prophesize(SerializerInterface::class);
|
||||
$serializer->deserialize('{"caller": {"id": 456, "type": "thirdParty"}}', SetCallerCommand::class, 'json')
|
||||
->willReturn($command);
|
||||
$serializer->serialize($ticket, 'json', ['groups' => ['read']])
|
||||
->willReturn('{}')
|
||||
->shouldBeCalled();
|
||||
|
||||
$setCallerCommandHandler = $this->prophesize(SetCallerCommandHandler::class);
|
||||
$setCallerCommandHandler->__invoke($ticket, $command)
|
||||
->willReturn($ticket)
|
||||
->shouldBeCalled();
|
||||
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
$entityManager->flush()->shouldBeCalled();
|
||||
|
||||
$controller = new SetCallerApiController(
|
||||
$setCallerCommandHandler->reveal(),
|
||||
$security->reveal(),
|
||||
$entityManager->reveal(),
|
||||
$serializer->reveal(),
|
||||
);
|
||||
|
||||
$response = $controller->setCaller($ticket, $request);
|
||||
|
||||
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||
}
|
||||
|
||||
public function testSetCallerToNull(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
$request = new Request([], [], [], [], [], [], '{"caller": null}');
|
||||
|
||||
$command = new SetCallerCommand(null);
|
||||
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->isGranted('ROLE_USER')->willReturn(true);
|
||||
|
||||
$serializer = $this->prophesize(SerializerInterface::class);
|
||||
$serializer->deserialize('{"caller": null}', SetCallerCommand::class, 'json')
|
||||
->willReturn($command);
|
||||
$serializer->serialize($ticket, 'json', ['groups' => ['read']])
|
||||
->willReturn('{}')
|
||||
->shouldBeCalled();
|
||||
|
||||
$setCallerCommandHandler = $this->prophesize(SetCallerCommandHandler::class);
|
||||
$setCallerCommandHandler->__invoke($ticket, $command)
|
||||
->willReturn($ticket)
|
||||
->shouldBeCalled();
|
||||
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
$entityManager->flush()->shouldBeCalled();
|
||||
|
||||
$controller = new SetCallerApiController(
|
||||
$setCallerCommandHandler->reveal(),
|
||||
$security->reveal(),
|
||||
$entityManager->reveal(),
|
||||
$serializer->reveal(),
|
||||
);
|
||||
|
||||
$response = $controller->setCaller($ticket, $request);
|
||||
|
||||
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\TicketBundle\Entity\CallerHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class CallerHistoryTest extends TestCase
|
||||
{
|
||||
public function testConstructorWithPerson(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
|
||||
$callerHistory = new CallerHistory($person = new Person(), $ticket);
|
||||
|
||||
self::assertSame($ticket, $callerHistory->getTicket());
|
||||
self::assertNull($callerHistory->getEndDate());
|
||||
self::assertSame($person, $callerHistory->getPerson());
|
||||
self::assertNull($callerHistory->getThirdParty());
|
||||
self::assertSame($person, $callerHistory->getCaller());
|
||||
}
|
||||
|
||||
public function testConstructorWithThirdParty(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
|
||||
$callerHistory = new CallerHistory($thirdParty = new ThirdParty(), $ticket);
|
||||
|
||||
self::assertSame($ticket, $callerHistory->getTicket());
|
||||
self::assertNull($callerHistory->getEndDate());
|
||||
self::assertNull($callerHistory->getPerson());
|
||||
self::assertSame($thirdParty, $callerHistory->getThirdParty());
|
||||
self::assertSame($thirdParty, $callerHistory->getCaller());
|
||||
}
|
||||
|
||||
public function testSetEndDate(): void
|
||||
{
|
||||
$ticket = $this->createMock(Ticket::class);
|
||||
$callerHistory = new CallerHistory(new ThirdParty(), $ticket);
|
||||
|
||||
$endDate = new \DateTimeImmutable('2023-01-01');
|
||||
$callerHistory->setEndDate($endDate);
|
||||
|
||||
self::assertSame($endDate, $callerHistory->getEndDate());
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\TicketBundle\Entity\CallerHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class TicketCallerTest extends TestCase
|
||||
{
|
||||
public function testGetCaller(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
|
||||
// Initially, there should be no caller
|
||||
self::assertNull($ticket->getCaller());
|
||||
|
||||
// Create a person
|
||||
$person = new Person();
|
||||
|
||||
// Create a caller history with the person
|
||||
$callerHistory = new CallerHistory($person, $ticket);
|
||||
|
||||
// The ticket should now return the person as the caller
|
||||
self::assertSame($person, $ticket->getCaller());
|
||||
|
||||
// Create a third party
|
||||
$thirdParty = new ThirdParty();
|
||||
|
||||
// Create a new caller history with the third party
|
||||
$callerHistory2 = new CallerHistory($thirdParty, $ticket);
|
||||
|
||||
// End the first caller history
|
||||
$callerHistory->setEndDate(new \DateTimeImmutable());
|
||||
|
||||
// The ticket should now return the third party as the caller
|
||||
self::assertSame($thirdParty, $ticket->getCaller());
|
||||
|
||||
// End the second caller history
|
||||
$callerHistory2->setEndDate(new \DateTimeImmutable());
|
||||
|
||||
// The ticket should now return null as there is no active caller
|
||||
self::assertNull($ticket->getCaller());
|
||||
}
|
||||
|
||||
public function testGetCallerHistories(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
|
||||
// Initially, there should be no caller histories
|
||||
self::assertCount(0, $ticket->getCallerHistories());
|
||||
|
||||
// Create a caller history
|
||||
$callerHistory = new CallerHistory(new Person(), $ticket);
|
||||
|
||||
// The ticket should now have one caller history
|
||||
self::assertCount(1, $ticket->getCallerHistories());
|
||||
self::assertSame($callerHistory, $ticket->getCallerHistories()->first());
|
||||
|
||||
// Create another caller history
|
||||
$callerHistory2 = new CallerHistory(new ThirdParty(), $ticket);
|
||||
|
||||
// The ticket should now have two caller histories
|
||||
self::assertCount(2, $ticket->getCallerHistories());
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
<?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 Repository;
|
||||
|
||||
use Chill\PersonBundle\DataFixtures\Helper\RandomPersonHelperTrait;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\TicketBundle\Repository\PersonTicketACLAwareRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class PersonTicketACLAwareRepositoryTest extends KernelTestCase
|
||||
{
|
||||
use RandomPersonHelperTrait;
|
||||
|
||||
private PersonTicketACLAwareRepository $repository;
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->repository = self::getContainer()->get(PersonTicketACLAwareRepository::class);
|
||||
$this->entityManager = self::getContainer()->get(EntityManagerInterface::class);
|
||||
}
|
||||
|
||||
public function testFindPersonPreviouslyAssociatedWithCallerWithPerson()
|
||||
{
|
||||
$person = $this->getRandomPerson(self::getContainer()->get(EntityManagerInterface::class));
|
||||
|
||||
$actual = $this->repository->findPersonPreviouslyAssociatedWithCaller($person);
|
||||
|
||||
self::assertIsList($actual);
|
||||
}
|
||||
|
||||
public function testFindPersonPreviouslyAssociatedWithCallerWithThirdParty()
|
||||
{
|
||||
$thirdParty = $this->entityManager->createQuery(
|
||||
sprintf('SELECT t FROM %s t', ThirdParty::class)
|
||||
)
|
||||
->setMaxResults(1)
|
||||
->getSingleResult();
|
||||
|
||||
if (null === $thirdParty) {
|
||||
throw new \RuntimeException('the third party table seems to be empty');
|
||||
}
|
||||
|
||||
$actual = $this->repository->findPersonPreviouslyAssociatedWithCaller($thirdParty);
|
||||
|
||||
self::assertIsList($actual);
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserGroup;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\TicketBundle\Entity\AddresseeHistory;
|
||||
use Chill\TicketBundle\Entity\CallerHistory;
|
||||
use Chill\TicketBundle\Entity\Comment;
|
||||
use Chill\TicketBundle\Entity\EmergencyStatusEnum;
|
||||
use Chill\TicketBundle\Entity\EmergencyStatusHistory;
|
||||
@ -91,27 +92,16 @@ class TicketNormalizerTest extends KernelTestCase
|
||||
'history' => [
|
||||
[
|
||||
'event_type' => 'create_ticket',
|
||||
'at' => 1718495999,
|
||||
'by' => [
|
||||
0 => 'user',
|
||||
],
|
||||
'data' => [],
|
||||
],
|
||||
[
|
||||
'event_type' => 'state_change',
|
||||
'at' => 1718495999,
|
||||
'by' => [
|
||||
0 => 'user',
|
||||
],
|
||||
'data' => [
|
||||
'new_state' => 'open',
|
||||
],
|
||||
],
|
||||
],
|
||||
'currentState' => 'open',
|
||||
'updatedAt' => $t->getUpdatedAt()->getTimestamp(),
|
||||
'updatedBy' => ['user'],
|
||||
'emergency' => 'no',
|
||||
'caller' => null,
|
||||
],
|
||||
];
|
||||
|
||||
@ -165,6 +155,36 @@ class TicketNormalizerTest extends KernelTestCase
|
||||
'updatedAt' => $ticket->getUpdatedAt()->getTimestamp(),
|
||||
'updatedBy' => ['user'],
|
||||
'emergency' => 'yes',
|
||||
'caller' => null,
|
||||
],
|
||||
];
|
||||
|
||||
// ticket with caller
|
||||
$ticket = new Ticket();
|
||||
$ticket->setCreatedAt(new \DateTimeImmutable('2024-06-16T00:00:00'));
|
||||
$ticket->setUpdatedAt(new \DateTimeImmutable('2024-06-16T00:00:00'));
|
||||
new CallerHistory(new Person(), $ticket, new \DateTimeImmutable('2024-04-01T12:00:00'));
|
||||
|
||||
yield [
|
||||
$ticket,
|
||||
[
|
||||
'type' => 'ticket_ticket',
|
||||
'createdAt' => $ticket->getCreatedAt()?->getTimestamp(),
|
||||
'createdBy' => null,
|
||||
'id' => null,
|
||||
'externalRef' => '',
|
||||
'currentPersons' => [],
|
||||
'currentAddressees' => [],
|
||||
'currentInputs' => [],
|
||||
'currentMotive' => null,
|
||||
'history' => [
|
||||
['event_type' => 'set_caller'],
|
||||
],
|
||||
'currentState' => 'open',
|
||||
'updatedAt' => $ticket->getUpdatedAt()->getTimestamp(),
|
||||
'updatedBy' => null,
|
||||
'emergency' => 'no',
|
||||
'caller' => ['person'],
|
||||
],
|
||||
];
|
||||
}
|
||||
@ -224,6 +244,11 @@ class TicketNormalizerTest extends KernelTestCase
|
||||
'json',
|
||||
['groups' => 'read']
|
||||
)->will(fn ($args): array => $args[0]);
|
||||
$normalizer->normalize(
|
||||
Argument::that(fn ($arg) => is_array($arg) && 1 === count($arg) && array_key_exists('new_caller', $arg)),
|
||||
'json',
|
||||
['groups' => 'read']
|
||||
)->will(fn ($args): array => ['new_caller' => ['dummy']]);
|
||||
|
||||
// datetime
|
||||
$normalizer->normalize(Argument::type(\DateTimeImmutable::class), 'json', Argument::type('array'))
|
||||
@ -231,6 +256,9 @@ class TicketNormalizerTest extends KernelTestCase
|
||||
// user
|
||||
$normalizer->normalize(Argument::type(User::class), 'json', Argument::type('array'))
|
||||
->willReturn(['user']);
|
||||
// person
|
||||
$normalizer->normalize(Argument::type(Person::class), 'json', Argument::type('array'))
|
||||
->willReturn(['person']);
|
||||
// motive
|
||||
$normalizer->normalize(Argument::type(Motive::class), 'json', Argument::type('array'))->willReturn(['type' => 'motive', 'id' => 0]);
|
||||
// person history
|
||||
|
@ -0,0 +1,70 @@
|
||||
<?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 Service\Ticket;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\TicketBundle\Entity\CallerHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Chill\TicketBundle\Repository\PersonTicketACLAwareRepositoryInterface;
|
||||
use Chill\TicketBundle\Service\Ticket\SuggestPersonForTicket;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class SuggestPersonForTicketTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
private \Prophecy\Prophecy\ObjectProphecy $repository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->repository = $this->prophesize(PersonTicketACLAwareRepositoryInterface::class);
|
||||
}
|
||||
|
||||
private function buildSuggestPersonForTicket(): SuggestPersonForTicket
|
||||
{
|
||||
return new SuggestPersonForTicket($this->repository->reveal());
|
||||
}
|
||||
|
||||
public function testSuggestPersonNoCaller()
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
|
||||
assert(null === $ticket->getCaller());
|
||||
|
||||
$this->repository->findPersonPreviouslyAssociatedWithCaller(Argument::any())->shouldNotBeCalled();
|
||||
$suggester = $this->buildSuggestPersonForTicket();
|
||||
|
||||
self::assertEquals([], $suggester->suggestPerson($ticket));
|
||||
}
|
||||
|
||||
public function testSuggestPersonCaller()
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
new CallerHistory($person = new Person(), $ticket);
|
||||
|
||||
assert($person === $ticket->getCaller());
|
||||
|
||||
$this->repository->findPersonPreviouslyAssociatedWithCaller($person, 0, 20)
|
||||
->willReturn($result = [new Person(), new Person()])
|
||||
->shouldBeCalledOnce();
|
||||
$suggester = $this->buildSuggestPersonForTicket();
|
||||
|
||||
self::assertEquals($result, $suggester->suggestPerson($ticket, 0, 20));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user