d42b288434
LST-58 (2.4), part 2 — Prospect (new entity). Completes the Directory backend.
- ProspectStatus enum (new/contacted/qualified/won/lost) + Prospect entity
(name, company, email, phone, address, status, source, notes,
convertedClient -> ClientInterface) with Timestampable/Blamable + #[Auditable].
- API: GetCollection/Get (ROLE_USER), Post/Patch/Delete (ROLE_ADMIN),
custom POST /prospects/{id}/convert (ConvertProspectProcessor: creates a
Client from the prospect, links convertedClient, sets status=Won; idempotent).
SearchFilter on status.
- Repository interface + Doctrine impl (bound); 6 MCP tools (list/get/create/
update/delete/convert-prospect); Serializer::prospect(). Module perms
directory.prospects.view/manage. Demo fixtures (3 prospects, one converted).
- Additive migration: CREATE TABLE prospect + FKs ON DELETE SET NULL + COMMENT.
163 tests green (incl. conversion test), mapping valid, cs-fixer clean.
78 lines
2.5 KiB
PHP
78 lines
2.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Functional\Module\Directory;
|
|
|
|
use ApiPlatform\Metadata\Post;
|
|
use App\Module\Directory\Domain\Entity\Prospect;
|
|
use App\Module\Directory\Domain\Enum\ProspectStatus;
|
|
use App\Module\Directory\Infrastructure\ApiPlatform\State\ConvertProspectProcessor;
|
|
use App\Module\Directory\Infrastructure\Doctrine\DoctrineProspectRepository;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
class ProspectConversionTest extends KernelTestCase
|
|
{
|
|
private EntityManagerInterface $em;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
self::bootKernel();
|
|
$this->em = self::getContainer()->get(EntityManagerInterface::class);
|
|
}
|
|
|
|
public function testConvertCreatesClientAndFlagsProspectWon(): void
|
|
{
|
|
$prospect = new Prospect();
|
|
$prospect->setName('Lead Test');
|
|
$prospect->setCompany('Lead Company '.uniqid());
|
|
$prospect->setEmail('lead@example.com');
|
|
$prospect->setPhone('06 00 00 00 00');
|
|
$prospect->setStreet('1 rue du Test');
|
|
$prospect->setCity('Testville');
|
|
$prospect->setPostalCode('00000');
|
|
$prospect->setStatus(ProspectStatus::Qualified);
|
|
$this->em->persist($prospect);
|
|
$this->em->flush();
|
|
|
|
$result = $this->processor()->process($prospect, new Post(), ['id' => $prospect->getId()]);
|
|
|
|
self::assertSame(ProspectStatus::Won, $result->getStatus());
|
|
$client = $result->getConvertedClient();
|
|
self::assertNotNull($client);
|
|
self::assertSame($prospect->getCompany(), $client->getName());
|
|
}
|
|
|
|
public function testConvertIsIdempotent(): void
|
|
{
|
|
$prospect = new Prospect();
|
|
$prospect->setName('Idempotent Lead');
|
|
$prospect->setStatus(ProspectStatus::New);
|
|
$this->em->persist($prospect);
|
|
$this->em->flush();
|
|
|
|
$processor = $this->processor();
|
|
$first = $processor->process($prospect, new Post(), ['id' => $prospect->getId()]);
|
|
$clientId = $first->getConvertedClient()?->getId();
|
|
|
|
$second = $processor->process($prospect, new Post(), ['id' => $prospect->getId()]);
|
|
|
|
self::assertNotNull($clientId);
|
|
self::assertSame($clientId, $second->getConvertedClient()?->getId());
|
|
}
|
|
|
|
private function processor(): ConvertProspectProcessor
|
|
{
|
|
$c = self::getContainer();
|
|
|
|
return new ConvertProspectProcessor(
|
|
$c->get(EntityManagerInterface::class),
|
|
$c->get(DoctrineProspectRepository::class),
|
|
);
|
|
}
|
|
}
|