feat : DTO et mapper de réponse pour IpBCreateSortie

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-22 14:56:36 +02:00
parent c5bb4d4603
commit 5c0c894e37
3 changed files with 171 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Malio\EdnotifBundle\Bovin\Dto;
use Malio\EdnotifBundle\Shared\Dto\StandardResponseDto;
/**
* Réponse de `IpBCreateSortie`.
*
* Si `$pendingBdniValidation === true`, EDNOTIF a accepté la requête mais
* attend la validation asynchrone de la BDNi — `identification`, `entryMovement`
* et `exitMovement` sont `null`. Sinon, EDNOTIF renvoie la **période de présence
* clôturée** : l'entrée initiale du bovin sur l'exploitation (`entryMovement`)
* **et** la sortie qui vient d'être déclarée (`exitMovement`).
*/
final readonly class CreateSortieResponseDto
{
public function __construct(
public StandardResponseDto $standardResponse,
public bool $pendingBdniValidation,
public ?BovinIdentificationDto $identification,
public ?MovementDto $entryMovement,
public ?MovementDto $exitMovement,
public ?object $rawSoapResponse,
) {}
}

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Malio\EdnotifBundle\Bovin\Mapper;
use Malio\EdnotifBundle\Bovin\Dto\CreateSortieResponseDto;
use Malio\EdnotifBundle\Shared\Mapper\StandardResponseMapper;
final class CreateSortieResponseMapper
{
use BovinNodeMappingTrait;
public function __construct(
private readonly StandardResponseMapper $standardResponseMapper,
) {}
public function map(object $soapResponse): CreateSortieResponseDto
{
$standardResponse = $this->standardResponseMapper->map($soapResponse->ReponseStandard ?? null);
$specific = $soapResponse->ReponseSpecifique ?? null;
$pending = false;
$identification = null;
$entryMovement = null;
$exitMovement = null;
if (is_object($specific)) {
$pendingFlag = $specific->AttenteValidationBDNi ?? null;
if (null !== $pendingFlag) {
$pending = (bool) $this->toNullableBool($pendingFlag);
}
$validee = $specific->SortieValidee ?? null;
if (is_object($validee)) {
$identityNode = $validee->IdentiteBovin ?? null;
if (is_object($identityNode)) {
$identification = $this->mapIdentification($identityNode);
}
$mouvementBovin = $validee->MouvementBovin ?? null;
if (is_object($mouvementBovin)) {
$entryNode = $mouvementBovin->MouvementEntreeBovin ?? null;
if (is_object($entryNode)) {
$entryMovement = $this->mapMovement($entryNode, 'entry');
}
$exitNode = $mouvementBovin->MouvementSortieBovin ?? null;
if (is_object($exitNode)) {
$exitMovement = $this->mapMovement($exitNode, 'exit');
}
}
}
}
return new CreateSortieResponseDto(
standardResponse: $standardResponse,
pendingBdniValidation: $pending,
identification: $identification,
entryMovement: $entryMovement,
exitMovement: $exitMovement,
rawSoapResponse: $soapResponse,
);
}
}

View File

@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace Malio\EdnotifBundle\Tests\Unit\Bovin\Mapper;
use DateTimeImmutable;
use Malio\EdnotifBundle\Bovin\Dto\CreateSortieResponseDto;
use Malio\EdnotifBundle\Bovin\Mapper\CreateSortieResponseMapper;
use Malio\EdnotifBundle\Shared\Mapper\StandardResponseMapper;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use stdClass;
/**
* @internal
*/
#[CoversClass(CreateSortieResponseMapper::class)]
final class CreateSortieResponseMapperTest extends TestCase
{
public function testMapPendingBdniValidation(): void
{
$soapResponse = new stdClass();
$soapResponse->ReponseStandard = new stdClass();
$soapResponse->ReponseStandard->Resultat = true;
$soapResponse->ReponseSpecifique = new stdClass();
$soapResponse->ReponseSpecifique->AttenteValidationBDNi = true;
$dto = new CreateSortieResponseMapper(new StandardResponseMapper())->map($soapResponse);
self::assertInstanceOf(CreateSortieResponseDto::class, $dto);
self::assertTrue($dto->pendingBdniValidation);
self::assertNull($dto->identification);
self::assertNull($dto->entryMovement);
self::assertNull($dto->exitMovement);
}
public function testMapValidatedExit(): void
{
$soapResponse = new stdClass();
$soapResponse->ReponseStandard = new stdClass();
$soapResponse->ReponseStandard->Resultat = true;
$soapResponse->ReponseSpecifique = new stdClass();
$validee = new stdClass();
$validee->IdentiteBovin = new stdClass();
$validee->IdentiteBovin->Sexe = 'M';
$validee->IdentiteBovin->Bovin = new stdClass();
$validee->IdentiteBovin->Bovin->NumeroNational = 'FR9999999999';
$mouvement = new stdClass();
$mouvement->MouvementEntreeBovin = new stdClass();
$mouvement->MouvementEntreeBovin->DateEntree = '2024-01-10';
$mouvement->MouvementEntreeBovin->CauseEntree = 'A';
$mouvement->MouvementSortieBovin = new stdClass();
$mouvement->MouvementSortieBovin->DateSortie = '2026-04-22';
$mouvement->MouvementSortieBovin->CauseSortie = 'B';
$validee->MouvementBovin = $mouvement;
$soapResponse->ReponseSpecifique->SortieValidee = $validee;
$dto = new CreateSortieResponseMapper(new StandardResponseMapper())->map($soapResponse);
self::assertFalse($dto->pendingBdniValidation);
self::assertNotNull($dto->identification);
self::assertSame('M', $dto->identification->sex);
self::assertSame('FR9999999999', $dto->identification->bovin?->nationalNumber);
self::assertNotNull($dto->entryMovement);
self::assertEquals(new DateTimeImmutable('2024-01-10'), $dto->entryMovement->date);
self::assertSame('A', $dto->entryMovement->cause);
self::assertNotNull($dto->exitMovement);
self::assertEquals(new DateTimeImmutable('2026-04-22'), $dto->exitMovement->date);
self::assertSame('B', $dto->exitMovement->cause);
}
}