Compare commits
10 Commits
dd87ea9216
...
db2503379f
| Author | SHA1 | Date | |
|---|---|---|---|
| db2503379f | |||
| c939190987 | |||
| 6493c8a1a7 | |||
| d2839f91b5 | |||
| 0dd0e58ec1 | |||
| 87f61ae7f1 | |||
| c7709de753 | |||
| 56c852ed59 | |||
| 9feb32c3c2 | |||
| b4d763dc0e |
33
README.md
33
README.md
@@ -33,3 +33,36 @@ Dans le docker-composer.yaml
|
|||||||
volumes:
|
volumes:
|
||||||
- ../ednotif-bundle:/var/www/html/ednotif-bundle
|
- ../ednotif-bundle:/var/www/html/ednotif-bundle
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Utilisation
|
||||||
|
|
||||||
|
Le bundle expose `Malio\EdnotifBundle\Bovin\Api\BovinApiInterface`. Injection standard par autowiring.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Malio\EdnotifBundle\Bovin\Api\BovinApiInterface;
|
||||||
|
|
||||||
|
final class MyController
|
||||||
|
{
|
||||||
|
public function __construct(private BovinApiInterface $ednotif) {}
|
||||||
|
|
||||||
|
public function example(): void
|
||||||
|
{
|
||||||
|
// Dossier d'un bovin
|
||||||
|
$file = $this->ednotif->getAnimalFile('FR1234567890');
|
||||||
|
|
||||||
|
// Inventaire du cheptel à une date
|
||||||
|
$inventory = $this->ednotif->getInventory(
|
||||||
|
startDate: new \DateTimeImmutable('2026-01-01'),
|
||||||
|
includeEarTagStock: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Retours de notifications depuis une date
|
||||||
|
$returns = $this->ednotif->getReturnedDossiers(new \DateTimeImmutable('2026-03-01'));
|
||||||
|
|
||||||
|
// Sorties présumées par l'IPG (flux de rapprochement)
|
||||||
|
$presumed = $this->ednotif->getPresumedExits();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Toutes les méthodes lèvent `Malio\EdnotifBundle\Shared\Exception\EdnotifException` en cas de `Resultat=false` côté EDNOTIF.
|
||||||
|
|||||||
@@ -6,7 +6,13 @@ use Malio\EdnotifBundle\Auth\TokenProvider;
|
|||||||
use Malio\EdnotifBundle\Bovin\Api\BovinApi;
|
use Malio\EdnotifBundle\Bovin\Api\BovinApi;
|
||||||
use Malio\EdnotifBundle\Bovin\Api\BovinApiInterface;
|
use Malio\EdnotifBundle\Bovin\Api\BovinApiInterface;
|
||||||
use Malio\EdnotifBundle\Bovin\Mapper\AnimalFileMapper;
|
use Malio\EdnotifBundle\Bovin\Mapper\AnimalFileMapper;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\AnimalSummaryMapper;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\InventoryMapper;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\PresumedExitsMapper;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\ReturnedDossiersMapper;
|
||||||
|
use Malio\EdnotifBundle\Shared\Mapper\StandardResponseMapper;
|
||||||
use Malio\EdnotifBundle\Shared\Soap\SoapClientFactory;
|
use Malio\EdnotifBundle\Shared\Soap\SoapClientFactory;
|
||||||
|
use Malio\EdnotifBundle\Shared\Soap\ZipMessageDecoder;
|
||||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||||
|
|
||||||
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
|
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
|
||||||
@@ -22,6 +28,8 @@ return static function (ContainerConfigurator $container): void {
|
|||||||
->arg('$soapOptions', '%ednotif.soap_options%')
|
->arg('$soapOptions', '%ednotif.soap_options%')
|
||||||
;
|
;
|
||||||
|
|
||||||
|
$services->set(StandardResponseMapper::class);
|
||||||
|
|
||||||
$services->set('ednotif.soap.guichet', SoapClient::class)
|
$services->set('ednotif.soap.guichet', SoapClient::class)
|
||||||
->factory([service(SoapClientFactory::class), 'create'])
|
->factory([service(SoapClientFactory::class), 'create'])
|
||||||
->args(['%ednotif.guichet_wsdl%'])
|
->args(['%ednotif.guichet_wsdl%'])
|
||||||
@@ -32,7 +40,29 @@ return static function (ContainerConfigurator $container): void {
|
|||||||
->args(['%ednotif.metier_wsdl%'])
|
->args(['%ednotif.metier_wsdl%'])
|
||||||
;
|
;
|
||||||
|
|
||||||
$services->set(AnimalFileMapper::class);
|
$services->set(AnimalFileMapper::class)->args([service(StandardResponseMapper::class)]);
|
||||||
|
|
||||||
|
$services->set(ZipMessageDecoder::class);
|
||||||
|
$services->set(AnimalSummaryMapper::class);
|
||||||
|
$services->set(InventoryMapper::class)
|
||||||
|
->args([
|
||||||
|
service(AnimalSummaryMapper::class),
|
||||||
|
service(StandardResponseMapper::class),
|
||||||
|
])
|
||||||
|
;
|
||||||
|
|
||||||
|
$services->set(ReturnedDossiersMapper::class)
|
||||||
|
->args([
|
||||||
|
service(AnimalSummaryMapper::class),
|
||||||
|
service(StandardResponseMapper::class),
|
||||||
|
])
|
||||||
|
;
|
||||||
|
|
||||||
|
$services->set(PresumedExitsMapper::class)
|
||||||
|
->args([
|
||||||
|
service(StandardResponseMapper::class),
|
||||||
|
])
|
||||||
|
;
|
||||||
|
|
||||||
$services->set(TokenProvider::class)
|
$services->set(TokenProvider::class)
|
||||||
->args([
|
->args([
|
||||||
@@ -52,6 +82,10 @@ return static function (ContainerConfigurator $container): void {
|
|||||||
service(TokenProvider::class),
|
service(TokenProvider::class),
|
||||||
service('ednotif.soap.business'),
|
service('ednotif.soap.business'),
|
||||||
service(AnimalFileMapper::class),
|
service(AnimalFileMapper::class),
|
||||||
|
service(InventoryMapper::class),
|
||||||
|
service(ReturnedDossiersMapper::class),
|
||||||
|
service(PresumedExitsMapper::class),
|
||||||
|
service(ZipMessageDecoder::class),
|
||||||
'%ednotif.exploitation_country_code%',
|
'%ednotif.exploitation_country_code%',
|
||||||
'%ednotif.exploitation_number%',
|
'%ednotif.exploitation_number%',
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ WS métier principal : déclarations réglementaires d'un cheptel bovin auprès
|
|||||||
| Opération | Statut | Description probable |
|
| Opération | Statut | Description probable |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `IpBGetDossierAnimal` | Implémenté | Dossier complet d'un bovin (identifications, mouvements, parents…) |
|
| `IpBGetDossierAnimal` | Implémenté | Dossier complet d'un bovin (identifications, mouvements, parents…) |
|
||||||
| `IpBGetInventaire` | À faire | Inventaire des animaux présents sur l'exploitation |
|
| `IpBGetInventaire` | Implémenté | Inventaire des animaux présents sur l'exploitation |
|
||||||
| `IpBGetRetourDossiers` | À faire | Retours de traitement des notifications envoyées |
|
| `IpBGetRetourDossiers` | Implémenté | Retours de traitement des notifications envoyées |
|
||||||
| `IpBGetSortiesPresumees` | À faire | Animaux sortis selon l'IPG mais non déclarés par l'éleveur |
|
| `IpBGetSortiesPresumees` | Implémenté | Animaux sortis selon l'IPG mais non déclarés par l'éleveur |
|
||||||
|
|
||||||
### Écriture
|
### Écriture
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,18 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Malio\EdnotifBundle\Bovin\Api;
|
namespace Malio\EdnotifBundle\Bovin\Api;
|
||||||
|
|
||||||
|
use DateTimeInterface;
|
||||||
use Malio\EdnotifBundle\Auth\TokenProvider;
|
use Malio\EdnotifBundle\Auth\TokenProvider;
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\AnimalFileDto;
|
use Malio\EdnotifBundle\Bovin\Dto\AnimalFileDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\InventoryDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\PresumedExitsDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\ReturnedDossiersDto;
|
||||||
use Malio\EdnotifBundle\Bovin\Mapper\AnimalFileMapper;
|
use Malio\EdnotifBundle\Bovin\Mapper\AnimalFileMapper;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\InventoryMapper;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\PresumedExitsMapper;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\ReturnedDossiersMapper;
|
||||||
use Malio\EdnotifBundle\Shared\Exception\EdnotifException;
|
use Malio\EdnotifBundle\Shared\Exception\EdnotifException;
|
||||||
|
use Malio\EdnotifBundle\Shared\Soap\ZipMessageDecoder;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use SoapClient;
|
use SoapClient;
|
||||||
use SoapFault;
|
use SoapFault;
|
||||||
@@ -18,6 +26,10 @@ final readonly class BovinApi implements BovinApiInterface
|
|||||||
private TokenProvider $tokenProvider,
|
private TokenProvider $tokenProvider,
|
||||||
private SoapClient $businessClient,
|
private SoapClient $businessClient,
|
||||||
private AnimalFileMapper $bovinDossierMapper,
|
private AnimalFileMapper $bovinDossierMapper,
|
||||||
|
private InventoryMapper $inventoryMapper,
|
||||||
|
private ReturnedDossiersMapper $returnedDossiersMapper,
|
||||||
|
private PresumedExitsMapper $presumedExitsMapper,
|
||||||
|
private ZipMessageDecoder $zipMessageDecoder,
|
||||||
private string $exploitationCountryCode,
|
private string $exploitationCountryCode,
|
||||||
private string $exploitationNumber,
|
private string $exploitationNumber,
|
||||||
) {}
|
) {}
|
||||||
@@ -45,20 +57,122 @@ final readonly class BovinApi implements BovinApiInterface
|
|||||||
throw new RuntimeException('SOAP Fault on IpBGetDossierAnimal: '.$soapFault->getMessage(), 0, $soapFault);
|
throw new RuntimeException('SOAP Fault on IpBGetDossierAnimal: '.$soapFault->getMessage(), 0, $soapFault);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throw uniquement si Resultat=false (erreur métier)
|
$this->assertSuccessfulResponse($soapResponse, 'IpBGetDossierAnimal');
|
||||||
$standardResponseNode = $soapResponse->ReponseStandard ?? null;
|
|
||||||
$isOk = is_object($standardResponseNode) && (($standardResponseNode->Resultat ?? false) === true);
|
|
||||||
|
|
||||||
if (!$isOk) {
|
|
||||||
$anomalyNode = is_object($standardResponseNode) ? ($standardResponseNode->Anomalie ?? null) : null;
|
|
||||||
|
|
||||||
throw new EdnotifException(
|
|
||||||
codeAnomalie: (string) ($anomalyNode->Code ?? 'UNKNOWN'),
|
|
||||||
severite: (int) ($anomalyNode->Severite ?? 1),
|
|
||||||
message: (string) ($anomalyNode->Message ?? 'EDNOTIF error')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->bovinDossierMapper->map($soapResponse);
|
return $this->bovinDossierMapper->map($soapResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getInventory(
|
||||||
|
DateTimeInterface $startDate,
|
||||||
|
?DateTimeInterface $endDate = null,
|
||||||
|
bool $includeEarTagStock = false,
|
||||||
|
): InventoryDto {
|
||||||
|
$token = $this->tokenProvider->getToken();
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'JetonAuthentification' => $token,
|
||||||
|
'Exploitation' => [
|
||||||
|
'CodePays' => $this->exploitationCountryCode,
|
||||||
|
'NumeroExploitation' => $this->exploitationNumber,
|
||||||
|
],
|
||||||
|
'DateDebut' => $startDate->format('Y-m-d'),
|
||||||
|
'StockBoucles' => $includeEarTagStock,
|
||||||
|
];
|
||||||
|
if (null !== $endDate) {
|
||||||
|
$payload['DateFin'] = $endDate->format('Y-m-d');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
/** @var object $soapResponse */
|
||||||
|
$soapResponse = $this->businessClient->__soapCall('IpBGetInventaire', [$payload]);
|
||||||
|
} catch (SoapFault $soapFault) {
|
||||||
|
throw new RuntimeException('SOAP Fault on IpBGetInventaire: '.$soapFault->getMessage(), 0, $soapFault);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSuccessfulResponse($soapResponse, 'IpBGetInventaire');
|
||||||
|
|
||||||
|
$messageZip = $soapResponse->ReponseSpecifique->MessageZip ?? null;
|
||||||
|
$unzippedMessage = is_string($messageZip) && '' !== $messageZip
|
||||||
|
? $this->zipMessageDecoder->decode($messageZip)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return $this->inventoryMapper->map($soapResponse, $unzippedMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReturnedDossiers(DateTimeInterface $startDate): ReturnedDossiersDto
|
||||||
|
{
|
||||||
|
$token = $this->tokenProvider->getToken();
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'JetonAuthentification' => $token,
|
||||||
|
'Exploitation' => [
|
||||||
|
'CodePays' => $this->exploitationCountryCode,
|
||||||
|
'NumeroExploitation' => $this->exploitationNumber,
|
||||||
|
],
|
||||||
|
'DateDebut' => $startDate->format('Y-m-d'),
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
/** @var object $soapResponse */
|
||||||
|
$soapResponse = $this->businessClient->__soapCall('IpBGetRetourDossiers', [$payload]);
|
||||||
|
} catch (SoapFault $soapFault) {
|
||||||
|
throw new RuntimeException('SOAP Fault on IpBGetRetourDossiers: '.$soapFault->getMessage(), 0, $soapFault);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSuccessfulResponse($soapResponse, 'IpBGetRetourDossiers');
|
||||||
|
|
||||||
|
$messageZip = $soapResponse->ReponseSpecifique->MessageZip ?? null;
|
||||||
|
$unzippedMessage = is_string($messageZip) && '' !== $messageZip
|
||||||
|
? $this->zipMessageDecoder->decode($messageZip)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return $this->returnedDossiersMapper->map($soapResponse, $unzippedMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPresumedExits(): PresumedExitsDto
|
||||||
|
{
|
||||||
|
$token = $this->tokenProvider->getToken();
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'JetonAuthentification' => $token,
|
||||||
|
'Exploitation' => [
|
||||||
|
'CodePays' => $this->exploitationCountryCode,
|
||||||
|
'NumeroExploitation' => $this->exploitationNumber,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
/** @var object $soapResponse */
|
||||||
|
$soapResponse = $this->businessClient->__soapCall('IpBGetSortiesPresumees', [$payload]);
|
||||||
|
} catch (SoapFault $soapFault) {
|
||||||
|
throw new RuntimeException('SOAP Fault on IpBGetSortiesPresumees: '.$soapFault->getMessage(), 0, $soapFault);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSuccessfulResponse($soapResponse, 'IpBGetSortiesPresumees');
|
||||||
|
|
||||||
|
$messageZip = $soapResponse->ReponseSpecifique->MessageZip ?? null;
|
||||||
|
$unzippedMessage = is_string($messageZip) && '' !== $messageZip
|
||||||
|
? $this->zipMessageDecoder->decode($messageZip)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return $this->presumedExitsMapper->map($soapResponse, $unzippedMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertSuccessfulResponse(object $soapResponse, string $operation): void
|
||||||
|
{
|
||||||
|
$standardResponseNode = $soapResponse->ReponseStandard ?? null;
|
||||||
|
$isOk = is_object($standardResponseNode) && (($standardResponseNode->Resultat ?? false) === true);
|
||||||
|
|
||||||
|
if ($isOk) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$anomalyNode = is_object($standardResponseNode) ? ($standardResponseNode->Anomalie ?? null) : null;
|
||||||
|
|
||||||
|
throw new EdnotifException(
|
||||||
|
codeAnomalie: (string) ($anomalyNode->Code ?? 'UNKNOWN'),
|
||||||
|
severite: (int) ($anomalyNode->Severite ?? 1),
|
||||||
|
message: (string) ($anomalyNode->Message ?? $operation.' : EDNOTIF error')
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,23 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Malio\EdnotifBundle\Bovin\Api;
|
namespace Malio\EdnotifBundle\Bovin\Api;
|
||||||
|
|
||||||
|
use DateTimeInterface;
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\AnimalFileDto;
|
use Malio\EdnotifBundle\Bovin\Dto\AnimalFileDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\InventoryDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\PresumedExitsDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\ReturnedDossiersDto;
|
||||||
|
|
||||||
interface BovinApiInterface
|
interface BovinApiInterface
|
||||||
{
|
{
|
||||||
public function getAnimalFile(string $nationalNumber, string $countryCode = 'FR'): AnimalFileDto;
|
public function getAnimalFile(string $nationalNumber, string $countryCode = 'FR'): AnimalFileDto;
|
||||||
|
|
||||||
|
public function getInventory(
|
||||||
|
DateTimeInterface $startDate,
|
||||||
|
?DateTimeInterface $endDate = null,
|
||||||
|
bool $includeEarTagStock = false,
|
||||||
|
): InventoryDto;
|
||||||
|
|
||||||
|
public function getReturnedDossiers(DateTimeInterface $startDate): ReturnedDossiersDto;
|
||||||
|
|
||||||
|
public function getPresumedExits(): PresumedExitsDto;
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/Bovin/Dto/AnimalSummaryDto.php
Normal file
16
src/Bovin/Dto/AnimalSummaryDto.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Dto;
|
||||||
|
|
||||||
|
final readonly class AnimalSummaryDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param list<PresencePeriodDto> $presencePeriods
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public ?BovinIdentificationDto $identification,
|
||||||
|
public array $presencePeriods,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
12
src/Bovin/Dto/EarTagSeriesDto.php
Normal file
12
src/Bovin/Dto/EarTagSeriesDto.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Dto;
|
||||||
|
|
||||||
|
final readonly class EarTagSeriesDto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public object $rawNode,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
26
src/Bovin/Dto/InventoryDto.php
Normal file
26
src/Bovin/Dto/InventoryDto.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Dto;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Malio\EdnotifBundle\Shared\Dto\StandardResponseDto;
|
||||||
|
|
||||||
|
final readonly class InventoryDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param list<AnimalSummaryDto> $animals
|
||||||
|
* @param list<EarTagSeriesDto> $earTagSeries
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public StandardResponseDto $standardResponse,
|
||||||
|
public int $nbBovins,
|
||||||
|
public ?DateTimeImmutable $startDate,
|
||||||
|
public ?DateTimeImmutable $endDate,
|
||||||
|
public ?bool $includesEarTagStock,
|
||||||
|
public array $animals,
|
||||||
|
public array $earTagSeries,
|
||||||
|
public ?object $rawSoapResponse,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
15
src/Bovin/Dto/PresumedExitDto.php
Normal file
15
src/Bovin/Dto/PresumedExitDto.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Dto;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
|
||||||
|
final readonly class PresumedExitDto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ?BovinRef $bovin,
|
||||||
|
public ?DateTimeImmutable $exitDate,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
20
src/Bovin/Dto/PresumedExitsDto.php
Normal file
20
src/Bovin/Dto/PresumedExitsDto.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Dto;
|
||||||
|
|
||||||
|
use Malio\EdnotifBundle\Shared\Dto\StandardResponseDto;
|
||||||
|
|
||||||
|
final readonly class PresumedExitsDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param list<PresumedExitDto> $presumedExits
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public StandardResponseDto $standardResponse,
|
||||||
|
public int $nbBovins,
|
||||||
|
public array $presumedExits,
|
||||||
|
public ?object $rawSoapResponse,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
22
src/Bovin/Dto/ReturnedDossiersDto.php
Normal file
22
src/Bovin/Dto/ReturnedDossiersDto.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Dto;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Malio\EdnotifBundle\Shared\Dto\StandardResponseDto;
|
||||||
|
|
||||||
|
final readonly class ReturnedDossiersDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param list<AnimalSummaryDto> $animals
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public StandardResponseDto $standardResponse,
|
||||||
|
public int $nbBovins,
|
||||||
|
public ?DateTimeImmutable $startDate,
|
||||||
|
public array $animals,
|
||||||
|
public ?object $rawSoapResponse,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@@ -4,24 +4,20 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Malio\EdnotifBundle\Bovin\Mapper;
|
namespace Malio\EdnotifBundle\Bovin\Mapper;
|
||||||
|
|
||||||
use DateTimeImmutable;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\AnimalFileDto;
|
use Malio\EdnotifBundle\Bovin\Dto\AnimalFileDto;
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\BovinIdentificationDto;
|
use Malio\EdnotifBundle\Shared\Mapper\StandardResponseMapper;
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\BovinRef;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\DateValueDto;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\ExploitationRef;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\MovementDto;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\ParentInfoDto;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\PresencePeriodDto;
|
|
||||||
use Malio\EdnotifBundle\Shared\Dto\AnomalyDto;
|
|
||||||
use Malio\EdnotifBundle\Shared\Dto\StandardResponseDto;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
final class AnimalFileMapper
|
final class AnimalFileMapper
|
||||||
{
|
{
|
||||||
|
use BovinNodeMappingTrait;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly StandardResponseMapper $standardResponseMapper,
|
||||||
|
) {}
|
||||||
|
|
||||||
public function map(object $soapResponse): AnimalFileDto
|
public function map(object $soapResponse): AnimalFileDto
|
||||||
{
|
{
|
||||||
$standardResponse = $this->mapStandardResponse($soapResponse->ReponseStandard ?? null);
|
$standardResponse = $this->standardResponseMapper->map($soapResponse->ReponseStandard ?? null);
|
||||||
|
|
||||||
$specificResponseNode = $soapResponse->ReponseSpecifique ?? null;
|
$specificResponseNode = $soapResponse->ReponseSpecifique ?? null;
|
||||||
$bovinNode = is_object($specificResponseNode) ? ($specificResponseNode->Bovin ?? null) : null;
|
$bovinNode = is_object($specificResponseNode) ? ($specificResponseNode->Bovin ?? null) : null;
|
||||||
@@ -51,185 +47,4 @@ final class AnimalFileMapper
|
|||||||
rawSoapResponse: $soapResponse
|
rawSoapResponse: $soapResponse
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function mapStandardResponse(mixed $standardResponseNode): StandardResponseDto
|
|
||||||
{
|
|
||||||
$result = (bool) ($standardResponseNode->Resultat ?? false);
|
|
||||||
|
|
||||||
$anomalyNode = $standardResponseNode->Anomalie ?? null;
|
|
||||||
$anomaly = null;
|
|
||||||
|
|
||||||
if (is_object($anomalyNode)) {
|
|
||||||
$anomaly = new AnomalyDto(
|
|
||||||
code: $this->toNullableString($anomalyNode->Code ?? null),
|
|
||||||
severity: $this->toNullableInt($anomalyNode->Severite ?? null),
|
|
||||||
message: $this->toNullableString($anomalyNode->Message ?? null),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new StandardResponseDto($result, $anomaly);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function mapIdentification(object $identificationNode): BovinIdentificationDto
|
|
||||||
{
|
|
||||||
$bovinRef = $this->mapBovinRef($identificationNode->Bovin ?? null);
|
|
||||||
|
|
||||||
$birthDate = null;
|
|
||||||
$birthDateNode = $identificationNode->DateNaissance ?? null;
|
|
||||||
if (is_object($birthDateNode)) {
|
|
||||||
$birthDate = new DateValueDto(
|
|
||||||
date: $this->toNullableDate($birthDateNode->Date ?? null),
|
|
||||||
completenessFlag: $this->toNullableString($birthDateNode->TemoinCompletude ?? null),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$motherCarrier = $this->mapParentInfo($identificationNode->MerePorteuse ?? null);
|
|
||||||
$fatherIpg = $this->mapParentInfo($identificationNode->PereIPG ?? null);
|
|
||||||
$birthExploitation = $this->mapExploitationRef($identificationNode->ExploitationNaissance ?? null);
|
|
||||||
|
|
||||||
return new BovinIdentificationDto(
|
|
||||||
bovin: $bovinRef,
|
|
||||||
sex: $this->toNullableString($identificationNode->Sexe ?? null),
|
|
||||||
breedType: $this->toNullableString($identificationNode->TypeRacial ?? null),
|
|
||||||
birthDate: $birthDate,
|
|
||||||
workNumber: $this->toNullableString($identificationNode->NumeroTravail ?? null),
|
|
||||||
isFilie: $this->toNullableBool($identificationNode->StatutFilie ?? null),
|
|
||||||
motherCarrier: $motherCarrier,
|
|
||||||
fatherIpg: $fatherIpg,
|
|
||||||
birthExploitation: $birthExploitation,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function mapPresencePeriod(object $presencePeriodNode): PresencePeriodDto
|
|
||||||
{
|
|
||||||
$entryNode = $presencePeriodNode->Entree ?? null;
|
|
||||||
$exitNode = $presencePeriodNode->Sortie ?? null;
|
|
||||||
|
|
||||||
$entryMovement = is_object($entryNode) ? $this->mapMovement($entryNode, 'entry') : null;
|
|
||||||
$exitMovement = is_object($exitNode) ? $this->mapMovement($exitNode, 'exit') : null;
|
|
||||||
|
|
||||||
return new PresencePeriodDto(
|
|
||||||
entry: $entryMovement,
|
|
||||||
exit: $exitMovement,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function mapMovement(object $movementNode, string $direction): MovementDto
|
|
||||||
{
|
|
||||||
$dateValue = null;
|
|
||||||
$causeValue = null;
|
|
||||||
|
|
||||||
if ('entry' === $direction) {
|
|
||||||
// SOAP: DateEntree / CauseEntree
|
|
||||||
$dateValue = $movementNode->DateEntree ?? ($movementNode->Date ?? ($movementNode->DateMouvement ?? null));
|
|
||||||
$causeValue = $movementNode->CauseEntree ?? null;
|
|
||||||
} else {
|
|
||||||
// SOAP (souvent): DateSortie / CauseSortie
|
|
||||||
$dateValue = $movementNode->DateSortie ?? ($movementNode->Date ?? ($movementNode->DateMouvement ?? null));
|
|
||||||
$causeValue = $movementNode->CauseSortie ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$exploitationRef = $this->mapExploitationRef($movementNode->Exploitation ?? null);
|
|
||||||
|
|
||||||
return new MovementDto(
|
|
||||||
date: $this->toNullableDate($dateValue),
|
|
||||||
cause: $this->toNullableString($causeValue),
|
|
||||||
exploitation: $exploitationRef,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function mapParentInfo(mixed $parentNode): ?ParentInfoDto
|
|
||||||
{
|
|
||||||
if (!is_object($parentNode)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$bovinRef = $this->mapBovinRef($parentNode->Bovin ?? null);
|
|
||||||
|
|
||||||
return new ParentInfoDto(
|
|
||||||
bovin: $bovinRef,
|
|
||||||
breedType: $this->toNullableString($parentNode->TypeRacial ?? null),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function mapBovinRef(mixed $bovinNode): ?BovinRef
|
|
||||||
{
|
|
||||||
if (!is_object($bovinNode)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BovinRef(
|
|
||||||
countryCode: $this->toNullableString($bovinNode->CodePays ?? null),
|
|
||||||
nationalNumber: $this->toNullableString($bovinNode->NumeroNational ?? null),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function mapExploitationRef(mixed $exploitationNode): ?ExploitationRef
|
|
||||||
{
|
|
||||||
if (!is_object($exploitationNode)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ExploitationRef(
|
|
||||||
countryCode: $this->toNullableString($exploitationNode->CodePays ?? null),
|
|
||||||
exploitationNumber: $this->toNullableString($exploitationNode->NumeroExploitation ?? null),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return list<mixed> */
|
|
||||||
private function normalizeToList(mixed $value): array
|
|
||||||
{
|
|
||||||
if (null === $value) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return is_array($value) ? $value : [$value];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function toNullableString(mixed $value): ?string
|
|
||||||
{
|
|
||||||
if (null === $value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$stringValue = trim((string) $value);
|
|
||||||
|
|
||||||
return '' === $stringValue ? null : $stringValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function toNullableInt(mixed $value): ?int
|
|
||||||
{
|
|
||||||
if (null === $value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (is_int($value)) {
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
if (is_numeric($value)) {
|
|
||||||
return (int) $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function toNullableBool(mixed $value): ?bool
|
|
||||||
{
|
|
||||||
if (null === $value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (bool) $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function toNullableDate(mixed $value): ?DateTimeImmutable
|
|
||||||
{
|
|
||||||
if (!is_string($value) || '' === trim($value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return new DateTimeImmutable($value);
|
|
||||||
} catch (Throwable) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/Bovin/Mapper/AnimalSummaryMapper.php
Normal file
32
src/Bovin/Mapper/AnimalSummaryMapper.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Mapper;
|
||||||
|
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\AnimalSummaryDto;
|
||||||
|
|
||||||
|
final class AnimalSummaryMapper
|
||||||
|
{
|
||||||
|
use BovinNodeMappingTrait;
|
||||||
|
|
||||||
|
public function map(object $bovinNode): AnimalSummaryDto
|
||||||
|
{
|
||||||
|
$identificationNode = $bovinNode->IdentiteBovin ?? null;
|
||||||
|
$identification = is_object($identificationNode) ? $this->mapIdentification($identificationNode) : null;
|
||||||
|
|
||||||
|
$presencePeriods = [];
|
||||||
|
$presencePeriodsNode = $bovinNode->PeriodesPresences->PeriodePresence ?? null;
|
||||||
|
foreach ($this->normalizeToList($presencePeriodsNode) as $presencePeriodNode) {
|
||||||
|
if (!is_object($presencePeriodNode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$presencePeriods[] = $this->mapPresencePeriod($presencePeriodNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AnimalSummaryDto(
|
||||||
|
identification: $identification,
|
||||||
|
presencePeriods: $presencePeriods,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
168
src/Bovin/Mapper/BovinNodeMappingTrait.php
Normal file
168
src/Bovin/Mapper/BovinNodeMappingTrait.php
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Mapper;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\BovinIdentificationDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\BovinRef;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\DateValueDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\ExploitationRef;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\MovementDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\ParentInfoDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\PresencePeriodDto;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpers partagés par les mappers travaillant sur des noeuds `Bovin` issus
|
||||||
|
* d'EDNOTIF, que la source soit une réponse SOAP directe (stdClass via ext-soap)
|
||||||
|
* ou un message XML zippé (stdClass via ZipMessageDecoder).
|
||||||
|
*/
|
||||||
|
trait BovinNodeMappingTrait
|
||||||
|
{
|
||||||
|
protected function mapIdentification(object $identificationNode): BovinIdentificationDto
|
||||||
|
{
|
||||||
|
$birthDate = null;
|
||||||
|
$birthDateNode = $identificationNode->DateNaissance ?? null;
|
||||||
|
if (is_object($birthDateNode)) {
|
||||||
|
$birthDate = new DateValueDto(
|
||||||
|
date: $this->toNullableDate($birthDateNode->Date ?? null),
|
||||||
|
completenessFlag: $this->toNullableString($birthDateNode->TemoinCompletude ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BovinIdentificationDto(
|
||||||
|
bovin: $this->mapBovinRef($identificationNode->Bovin ?? null),
|
||||||
|
sex: $this->toNullableString($identificationNode->Sexe ?? null),
|
||||||
|
breedType: $this->toNullableString($identificationNode->TypeRacial ?? null),
|
||||||
|
birthDate: $birthDate,
|
||||||
|
workNumber: $this->toNullableString($identificationNode->NumeroTravail ?? null),
|
||||||
|
isFilie: $this->toNullableBool($identificationNode->StatutFilie ?? null),
|
||||||
|
motherCarrier: $this->mapParentInfo($identificationNode->MerePorteuse ?? null),
|
||||||
|
fatherIpg: $this->mapParentInfo($identificationNode->PereIPG ?? null),
|
||||||
|
birthExploitation: $this->mapExploitationRef($identificationNode->ExploitationNaissance ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function mapPresencePeriod(object $presencePeriodNode): PresencePeriodDto
|
||||||
|
{
|
||||||
|
$entryNode = $presencePeriodNode->Entree ?? null;
|
||||||
|
$exitNode = $presencePeriodNode->Sortie ?? null;
|
||||||
|
|
||||||
|
return new PresencePeriodDto(
|
||||||
|
entry: is_object($entryNode) ? $this->mapMovement($entryNode, 'entry') : null,
|
||||||
|
exit: is_object($exitNode) ? $this->mapMovement($exitNode, 'exit') : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function mapMovement(object $movementNode, string $direction): MovementDto
|
||||||
|
{
|
||||||
|
if ('entry' === $direction) {
|
||||||
|
$dateValue = $movementNode->DateEntree ?? ($movementNode->Date ?? ($movementNode->DateMouvement ?? null));
|
||||||
|
$causeValue = $movementNode->CauseEntree ?? null;
|
||||||
|
} else {
|
||||||
|
$dateValue = $movementNode->DateSortie ?? ($movementNode->Date ?? ($movementNode->DateMouvement ?? null));
|
||||||
|
$causeValue = $movementNode->CauseSortie ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MovementDto(
|
||||||
|
date: $this->toNullableDate($dateValue),
|
||||||
|
cause: $this->toNullableString($causeValue),
|
||||||
|
exploitation: $this->mapExploitationRef($movementNode->Exploitation ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function mapParentInfo(mixed $parentNode): ?ParentInfoDto
|
||||||
|
{
|
||||||
|
if (!is_object($parentNode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ParentInfoDto(
|
||||||
|
bovin: $this->mapBovinRef($parentNode->Bovin ?? null),
|
||||||
|
breedType: $this->toNullableString($parentNode->TypeRacial ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function mapBovinRef(mixed $bovinNode): ?BovinRef
|
||||||
|
{
|
||||||
|
if (!is_object($bovinNode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BovinRef(
|
||||||
|
countryCode: $this->toNullableString($bovinNode->CodePays ?? null),
|
||||||
|
nationalNumber: $this->toNullableString($bovinNode->NumeroNational ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function mapExploitationRef(mixed $exploitationNode): ?ExploitationRef
|
||||||
|
{
|
||||||
|
if (!is_object($exploitationNode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExploitationRef(
|
||||||
|
countryCode: $this->toNullableString($exploitationNode->CodePays ?? null),
|
||||||
|
exploitationNumber: $this->toNullableString($exploitationNode->NumeroExploitation ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return list<mixed> */
|
||||||
|
protected function normalizeToList(mixed $value): array
|
||||||
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_array($value) ? array_values($value) : [$value];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function toNullableString(mixed $value): ?string
|
||||||
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$stringValue = trim((string) $value);
|
||||||
|
|
||||||
|
return '' === $stringValue ? null : $stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function toNullableInt(mixed $value): ?int
|
||||||
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (is_int($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
if (is_numeric($value)) {
|
||||||
|
return (int) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function toNullableBool(mixed $value): ?bool
|
||||||
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function toNullableDate(mixed $value): ?DateTimeImmutable
|
||||||
|
{
|
||||||
|
if (!is_string($value) || '' === trim($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new DateTimeImmutable($value);
|
||||||
|
} catch (Throwable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/Bovin/Mapper/InventoryMapper.php
Normal file
68
src/Bovin/Mapper/InventoryMapper.php
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Mapper;
|
||||||
|
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\EarTagSeriesDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\InventoryDto;
|
||||||
|
use Malio\EdnotifBundle\Shared\Mapper\StandardResponseMapper;
|
||||||
|
|
||||||
|
final class InventoryMapper
|
||||||
|
{
|
||||||
|
use BovinNodeMappingTrait;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly AnimalSummaryMapper $animalSummaryMapper,
|
||||||
|
private readonly StandardResponseMapper $standardResponseMapper,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function map(object $soapResponse, ?object $unzippedMessage): InventoryDto
|
||||||
|
{
|
||||||
|
$standardResponse = $this->standardResponseMapper->map($soapResponse->ReponseStandard ?? null);
|
||||||
|
|
||||||
|
$nbBovins = $this->toNullableInt($soapResponse->ReponseSpecifique->NbBovins ?? null) ?? 0;
|
||||||
|
|
||||||
|
$startDate = null;
|
||||||
|
$endDate = null;
|
||||||
|
$includesEarTagStock = null;
|
||||||
|
$animals = [];
|
||||||
|
$earTagSeries = [];
|
||||||
|
|
||||||
|
if (is_object($unzippedMessage)) {
|
||||||
|
$infoNode = $unzippedMessage->InformationsMessage ?? null;
|
||||||
|
if (is_object($infoNode)) {
|
||||||
|
$startDate = $this->toNullableDate($infoNode->DateDebut ?? null);
|
||||||
|
$endDate = $this->toNullableDate($infoNode->DateFin ?? null);
|
||||||
|
$includesEarTagStock = $this->toNullableBool($infoNode->StockBoucles ?? null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bovinsNode = $unzippedMessage->Bovins->Bovin ?? null;
|
||||||
|
foreach ($this->normalizeToList($bovinsNode) as $bovinNode) {
|
||||||
|
if (!is_object($bovinNode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$animals[] = $this->animalSummaryMapper->map($bovinNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
$seriesNode = $unzippedMessage->Boucles->SerieBoucles ?? null;
|
||||||
|
foreach ($this->normalizeToList($seriesNode) as $serieNode) {
|
||||||
|
if (!is_object($serieNode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$earTagSeries[] = new EarTagSeriesDto(rawNode: $serieNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InventoryDto(
|
||||||
|
standardResponse: $standardResponse,
|
||||||
|
nbBovins: $nbBovins,
|
||||||
|
startDate: $startDate,
|
||||||
|
endDate: $endDate,
|
||||||
|
includesEarTagStock: $includesEarTagStock,
|
||||||
|
animals: $animals,
|
||||||
|
earTagSeries: $earTagSeries,
|
||||||
|
rawSoapResponse: $soapResponse,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/Bovin/Mapper/PresumedExitsMapper.php
Normal file
46
src/Bovin/Mapper/PresumedExitsMapper.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Mapper;
|
||||||
|
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\PresumedExitDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\PresumedExitsDto;
|
||||||
|
use Malio\EdnotifBundle\Shared\Mapper\StandardResponseMapper;
|
||||||
|
|
||||||
|
final class PresumedExitsMapper
|
||||||
|
{
|
||||||
|
use BovinNodeMappingTrait;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly StandardResponseMapper $standardResponseMapper,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function map(object $soapResponse, ?object $unzippedMessage): PresumedExitsDto
|
||||||
|
{
|
||||||
|
$standardResponse = $this->standardResponseMapper->map($soapResponse->ReponseStandard ?? null);
|
||||||
|
$nbBovins = $this->toNullableInt($soapResponse->ReponseSpecifique->NbBovins ?? null) ?? 0;
|
||||||
|
|
||||||
|
$presumedExits = [];
|
||||||
|
|
||||||
|
if (is_object($unzippedMessage)) {
|
||||||
|
$exitsNode = $unzippedMessage->SortiesPresumees->SortiePresumee ?? null;
|
||||||
|
foreach ($this->normalizeToList($exitsNode) as $exitNode) {
|
||||||
|
if (!is_object($exitNode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$presumedExits[] = new PresumedExitDto(
|
||||||
|
bovin: $this->mapBovinRef($exitNode->Bovin ?? null),
|
||||||
|
exitDate: $this->toNullableDate($exitNode->DateSortie ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PresumedExitsDto(
|
||||||
|
standardResponse: $standardResponse,
|
||||||
|
nbBovins: $nbBovins,
|
||||||
|
presumedExits: $presumedExits,
|
||||||
|
rawSoapResponse: $soapResponse,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/Bovin/Mapper/ReturnedDossiersMapper.php
Normal file
50
src/Bovin/Mapper/ReturnedDossiersMapper.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Bovin\Mapper;
|
||||||
|
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\ReturnedDossiersDto;
|
||||||
|
use Malio\EdnotifBundle\Shared\Mapper\StandardResponseMapper;
|
||||||
|
|
||||||
|
final class ReturnedDossiersMapper
|
||||||
|
{
|
||||||
|
use BovinNodeMappingTrait;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly AnimalSummaryMapper $animalSummaryMapper,
|
||||||
|
private readonly StandardResponseMapper $standardResponseMapper,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function map(object $soapResponse, ?object $unzippedMessage): ReturnedDossiersDto
|
||||||
|
{
|
||||||
|
$standardResponse = $this->standardResponseMapper->map($soapResponse->ReponseStandard ?? null);
|
||||||
|
$nbBovins = $this->toNullableInt($soapResponse->ReponseSpecifique->NbBovins ?? null) ?? 0;
|
||||||
|
|
||||||
|
$startDate = null;
|
||||||
|
$animals = [];
|
||||||
|
|
||||||
|
if (is_object($unzippedMessage)) {
|
||||||
|
$infoNode = $unzippedMessage->InformationsMessage ?? null;
|
||||||
|
if (is_object($infoNode)) {
|
||||||
|
$startDate = $this->toNullableDate($infoNode->DateDebut ?? null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bovinsNode = $unzippedMessage->Bovins->Bovin ?? null;
|
||||||
|
foreach ($this->normalizeToList($bovinsNode) as $bovinNode) {
|
||||||
|
if (!is_object($bovinNode)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$animals[] = $this->animalSummaryMapper->map($bovinNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ReturnedDossiersDto(
|
||||||
|
standardResponse: $standardResponse,
|
||||||
|
nbBovins: $nbBovins,
|
||||||
|
startDate: $startDate,
|
||||||
|
animals: $animals,
|
||||||
|
rawSoapResponse: $soapResponse,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/Shared/Mapper/StandardResponseMapper.php
Normal file
53
src/Shared/Mapper/StandardResponseMapper.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Shared\Mapper;
|
||||||
|
|
||||||
|
use Malio\EdnotifBundle\Shared\Dto\AnomalyDto;
|
||||||
|
use Malio\EdnotifBundle\Shared\Dto\StandardResponseDto;
|
||||||
|
|
||||||
|
final class StandardResponseMapper
|
||||||
|
{
|
||||||
|
public function map(mixed $standardResponseNode): StandardResponseDto
|
||||||
|
{
|
||||||
|
$result = (bool) ($standardResponseNode->Resultat ?? false);
|
||||||
|
$anomalyNode = is_object($standardResponseNode) ? ($standardResponseNode->Anomalie ?? null) : null;
|
||||||
|
|
||||||
|
$anomaly = null;
|
||||||
|
if (is_object($anomalyNode)) {
|
||||||
|
$anomaly = new AnomalyDto(
|
||||||
|
code: $this->toNullableString($anomalyNode->Code ?? null),
|
||||||
|
severity: $this->toNullableInt($anomalyNode->Severite ?? null),
|
||||||
|
message: $this->toNullableString($anomalyNode->Message ?? null),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StandardResponseDto($result, $anomaly);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function toNullableString(mixed $value): ?string
|
||||||
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$stringValue = trim((string) $value);
|
||||||
|
|
||||||
|
return '' === $stringValue ? null : $stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function toNullableInt(mixed $value): ?int
|
||||||
|
{
|
||||||
|
if (null === $value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (is_int($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
if (is_numeric($value)) {
|
||||||
|
return (int) $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
70
tests/Unit/Bovin/Mapper/AnimalSummaryMapperTest.php
Normal file
70
tests/Unit/Bovin/Mapper/AnimalSummaryMapperTest.php
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Tests\Unit\Bovin\Mapper;
|
||||||
|
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\AnimalSummaryDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\AnimalSummaryMapper;
|
||||||
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
#[CoversClass(AnimalSummaryMapper::class)]
|
||||||
|
final class AnimalSummaryMapperTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testMapReturnsIdentificationAndPresencePeriods(): void
|
||||||
|
{
|
||||||
|
$node = $this->makeBovinNode();
|
||||||
|
|
||||||
|
$summary = new AnimalSummaryMapper()->map($node);
|
||||||
|
|
||||||
|
self::assertInstanceOf(AnimalSummaryDto::class, $summary);
|
||||||
|
self::assertNotNull($summary->identification);
|
||||||
|
self::assertSame('FR1234567890', $summary->identification->bovin?->nationalNumber);
|
||||||
|
self::assertSame('F', $summary->identification->sex);
|
||||||
|
self::assertCount(2, $summary->presencePeriods);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMapHandlesMissingOptionalNodes(): void
|
||||||
|
{
|
||||||
|
$summary = new AnimalSummaryMapper()->map(new stdClass());
|
||||||
|
|
||||||
|
self::assertNull($summary->identification);
|
||||||
|
self::assertSame([], $summary->presencePeriods);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeBovinNode(): object
|
||||||
|
{
|
||||||
|
$node = new stdClass();
|
||||||
|
$node->IdentiteBovin = new stdClass();
|
||||||
|
$node->IdentiteBovin->Sexe = 'F';
|
||||||
|
$node->IdentiteBovin->Bovin = new stdClass();
|
||||||
|
$node->IdentiteBovin->Bovin->CodePays = 'FR';
|
||||||
|
$node->IdentiteBovin->Bovin->NumeroNational = 'FR1234567890';
|
||||||
|
|
||||||
|
$node->PeriodesPresences = new stdClass();
|
||||||
|
$node->PeriodesPresences->PeriodePresence = [
|
||||||
|
$this->makePresencePeriod('2024-01-10', '2024-06-01'),
|
||||||
|
$this->makePresencePeriod('2024-06-02', null),
|
||||||
|
];
|
||||||
|
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makePresencePeriod(string $entryDate, ?string $exitDate): object
|
||||||
|
{
|
||||||
|
$period = new stdClass();
|
||||||
|
$period->Entree = new stdClass();
|
||||||
|
$period->Entree->DateEntree = $entryDate;
|
||||||
|
if (null !== $exitDate) {
|
||||||
|
$period->Sortie = new stdClass();
|
||||||
|
$period->Sortie->DateSortie = $exitDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $period;
|
||||||
|
}
|
||||||
|
}
|
||||||
100
tests/Unit/Bovin/Mapper/InventoryMapperTest.php
Normal file
100
tests/Unit/Bovin/Mapper/InventoryMapperTest.php
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Tests\Unit\Bovin\Mapper;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\InventoryDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\AnimalSummaryMapper;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\InventoryMapper;
|
||||||
|
use Malio\EdnotifBundle\Shared\Mapper\StandardResponseMapper;
|
||||||
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
#[CoversClass(InventoryMapper::class)]
|
||||||
|
final class InventoryMapperTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testMapFullInventory(): void
|
||||||
|
{
|
||||||
|
$mapper = new InventoryMapper(new AnimalSummaryMapper(), new StandardResponseMapper());
|
||||||
|
|
||||||
|
$inventory = $mapper->map($this->makeSoapResponse(), $this->makeUnzippedMessage());
|
||||||
|
|
||||||
|
self::assertInstanceOf(InventoryDto::class, $inventory);
|
||||||
|
self::assertTrue($inventory->standardResponse->result);
|
||||||
|
self::assertSame(2, $inventory->nbBovins);
|
||||||
|
self::assertEquals(new DateTimeImmutable('2026-01-01'), $inventory->startDate);
|
||||||
|
self::assertEquals(new DateTimeImmutable('2026-01-31'), $inventory->endDate);
|
||||||
|
self::assertTrue($inventory->includesEarTagStock);
|
||||||
|
self::assertCount(2, $inventory->animals);
|
||||||
|
self::assertCount(1, $inventory->earTagSeries);
|
||||||
|
self::assertSame('FR123', $inventory->animals[0]->identification?->bovin?->nationalNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMapInventoryWithoutMessageZipReturnsEmptyLists(): void
|
||||||
|
{
|
||||||
|
$mapper = new InventoryMapper(new AnimalSummaryMapper(), new StandardResponseMapper());
|
||||||
|
|
||||||
|
$soapResponse = new stdClass();
|
||||||
|
$soapResponse->ReponseStandard = new stdClass();
|
||||||
|
$soapResponse->ReponseStandard->Resultat = true;
|
||||||
|
$soapResponse->ReponseSpecifique = new stdClass();
|
||||||
|
$soapResponse->ReponseSpecifique->NbBovins = 0;
|
||||||
|
|
||||||
|
$inventory = $mapper->map($soapResponse, null);
|
||||||
|
|
||||||
|
self::assertSame(0, $inventory->nbBovins);
|
||||||
|
self::assertSame([], $inventory->animals);
|
||||||
|
self::assertSame([], $inventory->earTagSeries);
|
||||||
|
self::assertNull($inventory->startDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeSoapResponse(): object
|
||||||
|
{
|
||||||
|
$response = new stdClass();
|
||||||
|
$response->ReponseStandard = new stdClass();
|
||||||
|
$response->ReponseStandard->Resultat = true;
|
||||||
|
$response->ReponseSpecifique = new stdClass();
|
||||||
|
$response->ReponseSpecifique->NbBovins = 2;
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeUnzippedMessage(): object
|
||||||
|
{
|
||||||
|
$message = new stdClass();
|
||||||
|
$message->InformationsMessage = new stdClass();
|
||||||
|
$message->InformationsMessage->DateDebut = '2026-01-01';
|
||||||
|
$message->InformationsMessage->DateFin = '2026-01-31';
|
||||||
|
$message->InformationsMessage->StockBoucles = '1';
|
||||||
|
$message->Bovins = new stdClass();
|
||||||
|
$message->Bovins->Bovin = [
|
||||||
|
$this->makeAnimalNode('FR123'),
|
||||||
|
$this->makeAnimalNode('FR456'),
|
||||||
|
];
|
||||||
|
$message->Boucles = new stdClass();
|
||||||
|
$message->Boucles->SerieBoucles = new stdClass();
|
||||||
|
$message->Boucles->SerieBoucles->NumeroSerieDebut = 'A0001';
|
||||||
|
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeAnimalNode(string $nationalNumber): object
|
||||||
|
{
|
||||||
|
$node = new stdClass();
|
||||||
|
$node->IdentiteBovin = new stdClass();
|
||||||
|
$node->IdentiteBovin->Bovin = new stdClass();
|
||||||
|
$node->IdentiteBovin->Bovin->NumeroNational = $nationalNumber;
|
||||||
|
$node->PeriodesPresences = new stdClass();
|
||||||
|
$node->PeriodesPresences->PeriodePresence = new stdClass();
|
||||||
|
$node->PeriodesPresences->PeriodePresence->Entree = new stdClass();
|
||||||
|
$node->PeriodesPresences->PeriodePresence->Entree->DateEntree = '2025-05-01';
|
||||||
|
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
}
|
||||||
72
tests/Unit/Bovin/Mapper/PresumedExitsMapperTest.php
Normal file
72
tests/Unit/Bovin/Mapper/PresumedExitsMapperTest.php
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Tests\Unit\Bovin\Mapper;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\PresumedExitsDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\PresumedExitsMapper;
|
||||||
|
use Malio\EdnotifBundle\Shared\Mapper\StandardResponseMapper;
|
||||||
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
#[CoversClass(PresumedExitsMapper::class)]
|
||||||
|
final class PresumedExitsMapperTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testMapWithExits(): void
|
||||||
|
{
|
||||||
|
$soapResponse = new stdClass();
|
||||||
|
$soapResponse->ReponseStandard = new stdClass();
|
||||||
|
$soapResponse->ReponseStandard->Resultat = true;
|
||||||
|
$soapResponse->ReponseSpecifique = new stdClass();
|
||||||
|
$soapResponse->ReponseSpecifique->NbBovins = 2;
|
||||||
|
|
||||||
|
$message = new stdClass();
|
||||||
|
$message->SortiesPresumees = new stdClass();
|
||||||
|
$message->SortiesPresumees->SortiePresumee = [
|
||||||
|
$this->makeExit('FR111', '2026-02-15'),
|
||||||
|
$this->makeExit('FR222', null),
|
||||||
|
];
|
||||||
|
|
||||||
|
$dto = new PresumedExitsMapper(new StandardResponseMapper())->map($soapResponse, $message);
|
||||||
|
|
||||||
|
self::assertInstanceOf(PresumedExitsDto::class, $dto);
|
||||||
|
self::assertSame(2, $dto->nbBovins);
|
||||||
|
self::assertCount(2, $dto->presumedExits);
|
||||||
|
self::assertSame('FR111', $dto->presumedExits[0]->bovin?->nationalNumber);
|
||||||
|
self::assertEquals(new DateTimeImmutable('2026-02-15'), $dto->presumedExits[0]->exitDate);
|
||||||
|
self::assertNull($dto->presumedExits[1]->exitDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMapWithoutMessageReturnsEmpty(): void
|
||||||
|
{
|
||||||
|
$soapResponse = new stdClass();
|
||||||
|
$soapResponse->ReponseStandard = new stdClass();
|
||||||
|
$soapResponse->ReponseStandard->Resultat = true;
|
||||||
|
$soapResponse->ReponseSpecifique = new stdClass();
|
||||||
|
$soapResponse->ReponseSpecifique->NbBovins = 0;
|
||||||
|
|
||||||
|
$dto = new PresumedExitsMapper(new StandardResponseMapper())->map($soapResponse, null);
|
||||||
|
|
||||||
|
self::assertSame(0, $dto->nbBovins);
|
||||||
|
self::assertSame([], $dto->presumedExits);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeExit(string $nationalNumber, ?string $exitDate): object
|
||||||
|
{
|
||||||
|
$node = new stdClass();
|
||||||
|
$node->Bovin = new stdClass();
|
||||||
|
$node->Bovin->CodePays = 'FR';
|
||||||
|
$node->Bovin->NumeroNational = $nationalNumber;
|
||||||
|
if (null !== $exitDate) {
|
||||||
|
$node->DateSortie = $exitDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
tests/Unit/Bovin/Mapper/ReturnedDossiersMapperTest.php
Normal file
67
tests/Unit/Bovin/Mapper/ReturnedDossiersMapperTest.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Malio\EdnotifBundle\Tests\Unit\Bovin\Mapper;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\ReturnedDossiersDto;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\AnimalSummaryMapper;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Mapper\ReturnedDossiersMapper;
|
||||||
|
use Malio\EdnotifBundle\Shared\Mapper\StandardResponseMapper;
|
||||||
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
#[CoversClass(ReturnedDossiersMapper::class)]
|
||||||
|
final class ReturnedDossiersMapperTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testMapReturnsAnimalsAndStartDate(): void
|
||||||
|
{
|
||||||
|
$mapper = new ReturnedDossiersMapper(new AnimalSummaryMapper(), new StandardResponseMapper());
|
||||||
|
|
||||||
|
$soapResponse = new stdClass();
|
||||||
|
$soapResponse->ReponseStandard = new stdClass();
|
||||||
|
$soapResponse->ReponseStandard->Resultat = true;
|
||||||
|
$soapResponse->ReponseSpecifique = new stdClass();
|
||||||
|
$soapResponse->ReponseSpecifique->NbBovins = 1;
|
||||||
|
|
||||||
|
$message = new stdClass();
|
||||||
|
$message->InformationsMessage = new stdClass();
|
||||||
|
$message->InformationsMessage->DateDebut = '2026-03-01';
|
||||||
|
$message->Bovins = new stdClass();
|
||||||
|
$message->Bovins->Bovin = new stdClass();
|
||||||
|
$message->Bovins->Bovin->IdentiteBovin = new stdClass();
|
||||||
|
$message->Bovins->Bovin->IdentiteBovin->Bovin = new stdClass();
|
||||||
|
$message->Bovins->Bovin->IdentiteBovin->Bovin->NumeroNational = 'FR789';
|
||||||
|
$message->Bovins->Bovin->PeriodesPresences = new stdClass();
|
||||||
|
$message->Bovins->Bovin->PeriodesPresences->PeriodePresence = new stdClass();
|
||||||
|
|
||||||
|
$dto = $mapper->map($soapResponse, $message);
|
||||||
|
|
||||||
|
self::assertInstanceOf(ReturnedDossiersDto::class, $dto);
|
||||||
|
self::assertEquals(new DateTimeImmutable('2026-03-01'), $dto->startDate);
|
||||||
|
self::assertSame(1, $dto->nbBovins);
|
||||||
|
self::assertCount(1, $dto->animals);
|
||||||
|
self::assertSame('FR789', $dto->animals[0]->identification?->bovin?->nationalNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMapWithoutMessageReturnsEmptyAnimals(): void
|
||||||
|
{
|
||||||
|
$mapper = new ReturnedDossiersMapper(new AnimalSummaryMapper(), new StandardResponseMapper());
|
||||||
|
|
||||||
|
$soapResponse = new stdClass();
|
||||||
|
$soapResponse->ReponseStandard = new stdClass();
|
||||||
|
$soapResponse->ReponseStandard->Resultat = true;
|
||||||
|
$soapResponse->ReponseSpecifique = new stdClass();
|
||||||
|
$soapResponse->ReponseSpecifique->NbBovins = 0;
|
||||||
|
|
||||||
|
$dto = $mapper->map($soapResponse, null);
|
||||||
|
|
||||||
|
self::assertSame(0, $dto->nbBovins);
|
||||||
|
self::assertSame([], $dto->animals);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user