feat(directory) : add Prospect entity with conversion to Client (back)
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.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
<?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),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user