Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de39207102 | ||
| 5da0003c4d |
@@ -67,6 +67,7 @@ Ajouter dans le fichier .env du frontend
|
|||||||
* [#FER-18] Mise à jour du tableau d'arrivage
|
* [#FER-18] Mise à jour du tableau d'arrivage
|
||||||
* [#FER-26] Passeport du bovin
|
* [#FER-26] Passeport du bovin
|
||||||
* [#FER-27] Fix export inventaire bovin
|
* [#FER-27] Fix export inventaire bovin
|
||||||
|
* [#FER-25] Ajout un cron pour la synchro de l'inventaire bovin
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
parameters:
|
parameters:
|
||||||
app.version: '0.0.102'
|
app.version: '0.0.103'
|
||||||
|
|||||||
49
src/Command/SyncBovineInventoryCommand.php
Normal file
49
src/Command/SyncBovineInventoryCommand.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Command;
|
||||||
|
|
||||||
|
use App\Service\BovineInventorySyncer;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
#[AsCommand(
|
||||||
|
name: 'app:sync-bovine-inventory',
|
||||||
|
description: "Synchronise l'inventaire bovin avec EDNOTIF (équivalent du bouton Rafraîchir de l'interface)."
|
||||||
|
)]
|
||||||
|
final class SyncBovineInventoryCommand extends Command
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly BovineInventorySyncer $syncer,
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $this->syncer->sync();
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$io->error(sprintf('Échec de la synchronisation : %s', $e->getMessage()));
|
||||||
|
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->success(sprintf(
|
||||||
|
'Inventaire synchronisé · Créés : %d · Mis à jour : %d · Sortis : %d · Total EDNOTIF : %d',
|
||||||
|
$result->created,
|
||||||
|
$result->updated,
|
||||||
|
$result->exited,
|
||||||
|
$result->total,
|
||||||
|
));
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
138
src/Service/BovineInventorySyncer.php
Normal file
138
src/Service/BovineInventorySyncer.php
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\ApiResource\BovineSyncInventoryResult;
|
||||||
|
use App\Entity\Bovine;
|
||||||
|
use App\Entity\BovineType;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Api\BovinApiInterface;
|
||||||
|
use Malio\EdnotifBundle\Bovin\Dto\AnimalSummaryDto;
|
||||||
|
|
||||||
|
final class BovineInventorySyncer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array<string, BovineType>
|
||||||
|
*/
|
||||||
|
private array $bovineTypeCache = [];
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly BovinApiInterface $bovinApi,
|
||||||
|
private readonly EntityManagerInterface $em,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function sync(): BovineSyncInventoryResult
|
||||||
|
{
|
||||||
|
$inventory = $this->bovinApi->getInventory(new DateTimeImmutable('today'));
|
||||||
|
|
||||||
|
$result = new BovineSyncInventoryResult();
|
||||||
|
$result->total = count($inventory->animals);
|
||||||
|
|
||||||
|
$this->bovineTypeCache = [];
|
||||||
|
foreach ($this->em->getRepository(BovineType::class)->findAll() as $bovineType) {
|
||||||
|
if (null !== $bovineType->getCode()) {
|
||||||
|
$this->bovineTypeCache[$bovineType->getCode()] = $bovineType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingByNationalNumber = [];
|
||||||
|
foreach ($this->em->getRepository(Bovine::class)->findAll() as $bovine) {
|
||||||
|
$existingByNationalNumber[$bovine->getNationalNumber()] = $bovine;
|
||||||
|
}
|
||||||
|
|
||||||
|
$seen = [];
|
||||||
|
foreach ($inventory->animals as $animal) {
|
||||||
|
$nationalNumber = $animal->identification?->bovin?->nationalNumber;
|
||||||
|
if (null === $nationalNumber || '' === $nationalNumber) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$seen[$nationalNumber] = true;
|
||||||
|
|
||||||
|
if (isset($existingByNationalNumber[$nationalNumber])) {
|
||||||
|
$bovine = $existingByNationalNumber[$nationalNumber];
|
||||||
|
++$result->updated;
|
||||||
|
} else {
|
||||||
|
$bovine = new Bovine();
|
||||||
|
$bovine->setNationalNumber($nationalNumber);
|
||||||
|
$this->em->persist($bovine);
|
||||||
|
++$result->created;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->applyEdnotifData($bovine, $animal);
|
||||||
|
$bovine->setExitedAt(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$now = new DateTimeImmutable();
|
||||||
|
foreach ($existingByNationalNumber as $nationalNumber => $bovine) {
|
||||||
|
if (isset($seen[$nationalNumber])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (null !== $bovine->getExitedAt()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$bovine->setExitedAt($now);
|
||||||
|
++$result->exited;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->em->flush();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyEdnotifData(Bovine $bovine, AnimalSummaryDto $animal): void
|
||||||
|
{
|
||||||
|
$identification = $animal->identification;
|
||||||
|
if (null !== $identification) {
|
||||||
|
$bovine->setSex($identification->sex);
|
||||||
|
$bovine->setBovineType($this->resolveBovineType($identification->breedType));
|
||||||
|
$bovine->setWorkNumber($identification->workNumber);
|
||||||
|
$bovine->setBirthDate($identification->birthDate?->date);
|
||||||
|
|
||||||
|
$bovine->setMotherNationalNumber($identification->motherCarrier?->bovin?->nationalNumber);
|
||||||
|
$bovine->setMotherBovineType($this->resolveBovineType($identification->motherCarrier?->breedType));
|
||||||
|
$bovine->setFatherNationalNumber($identification->fatherIpg?->bovin?->nationalNumber);
|
||||||
|
$bovine->setFatherBovineType($this->resolveBovineType($identification->fatherIpg?->breedType));
|
||||||
|
}
|
||||||
|
|
||||||
|
$latestEntry = null;
|
||||||
|
$latestExit = null;
|
||||||
|
foreach ($animal->presencePeriods as $period) {
|
||||||
|
if (null !== $period->entry?->date && (null === $latestEntry || $period->entry->date > $latestEntry)) {
|
||||||
|
$latestEntry = $period->entry->date;
|
||||||
|
}
|
||||||
|
if (null !== $period->exit?->date && (null === $latestExit || $period->exit->date > $latestExit)) {
|
||||||
|
$latestExit = $period->exit->date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$bovine->setArrivalDate($latestEntry);
|
||||||
|
$bovine->setExitDate($latestExit);
|
||||||
|
$bovine->refreshAgeMonths();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trouve un BovineType existant par code, sinon en crée un placeholder
|
||||||
|
* que l'admin pourra renommer dans /admin/bovin/bovin-list.
|
||||||
|
*/
|
||||||
|
private function resolveBovineType(?string $code): ?BovineType
|
||||||
|
{
|
||||||
|
if (null === $code || '' === $code) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->bovineTypeCache[$code])) {
|
||||||
|
return $this->bovineTypeCache[$code];
|
||||||
|
}
|
||||||
|
|
||||||
|
$bovineType = new BovineType();
|
||||||
|
$bovineType->setCode($code);
|
||||||
|
$bovineType->setLabel(sprintf('À renommer (%s)', $code));
|
||||||
|
$this->em->persist($bovineType);
|
||||||
|
|
||||||
|
$this->bovineTypeCache[$code] = $bovineType;
|
||||||
|
|
||||||
|
return $bovineType;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,26 +7,15 @@ namespace App\State\Bovin;
|
|||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProcessorInterface;
|
use ApiPlatform\State\ProcessorInterface;
|
||||||
use App\ApiResource\BovineSyncInventoryResult;
|
use App\ApiResource\BovineSyncInventoryResult;
|
||||||
use App\Entity\Bovine;
|
use App\Service\BovineInventorySyncer;
|
||||||
use App\Entity\BovineType;
|
|
||||||
use DateTimeImmutable;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Api\BovinApiInterface;
|
|
||||||
use Malio\EdnotifBundle\Bovin\Dto\AnimalSummaryDto;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @implements ProcessorInterface<mixed, BovineSyncInventoryResult>
|
* @implements ProcessorInterface<mixed, BovineSyncInventoryResult>
|
||||||
*/
|
*/
|
||||||
final class BovineSyncInventoryProcessor implements ProcessorInterface
|
final readonly class BovineSyncInventoryProcessor implements ProcessorInterface
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var array<string, BovineType>
|
|
||||||
*/
|
|
||||||
private array $bovineTypeCache = [];
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private BovinApiInterface $bovinApi,
|
private BovineInventorySyncer $syncer,
|
||||||
private EntityManagerInterface $em,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function process(
|
public function process(
|
||||||
@@ -35,113 +24,6 @@ final class BovineSyncInventoryProcessor implements ProcessorInterface
|
|||||||
array $uriVariables = [],
|
array $uriVariables = [],
|
||||||
array $context = [],
|
array $context = [],
|
||||||
): BovineSyncInventoryResult {
|
): BovineSyncInventoryResult {
|
||||||
$inventory = $this->bovinApi->getInventory(new DateTimeImmutable('today'));
|
return $this->syncer->sync();
|
||||||
|
|
||||||
$result = new BovineSyncInventoryResult();
|
|
||||||
$result->total = count($inventory->animals);
|
|
||||||
|
|
||||||
$this->bovineTypeCache = [];
|
|
||||||
foreach ($this->em->getRepository(BovineType::class)->findAll() as $bovineType) {
|
|
||||||
if (null !== $bovineType->getCode()) {
|
|
||||||
$this->bovineTypeCache[$bovineType->getCode()] = $bovineType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$existingByNationalNumber = [];
|
|
||||||
foreach ($this->em->getRepository(Bovine::class)->findAll() as $bovine) {
|
|
||||||
$existingByNationalNumber[$bovine->getNationalNumber()] = $bovine;
|
|
||||||
}
|
|
||||||
|
|
||||||
$seen = [];
|
|
||||||
foreach ($inventory->animals as $animal) {
|
|
||||||
$nationalNumber = $animal->identification?->bovin?->nationalNumber;
|
|
||||||
if (null === $nationalNumber || '' === $nationalNumber) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$seen[$nationalNumber] = true;
|
|
||||||
|
|
||||||
if (isset($existingByNationalNumber[$nationalNumber])) {
|
|
||||||
$bovine = $existingByNationalNumber[$nationalNumber];
|
|
||||||
++$result->updated;
|
|
||||||
} else {
|
|
||||||
$bovine = new Bovine();
|
|
||||||
$bovine->setNationalNumber($nationalNumber);
|
|
||||||
$this->em->persist($bovine);
|
|
||||||
++$result->created;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->applyEdnotifData($bovine, $animal);
|
|
||||||
$bovine->setExitedAt(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
$now = new DateTimeImmutable();
|
|
||||||
foreach ($existingByNationalNumber as $nationalNumber => $bovine) {
|
|
||||||
if (isset($seen[$nationalNumber])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (null !== $bovine->getExitedAt()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$bovine->setExitedAt($now);
|
|
||||||
++$result->exited;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->em->flush();
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function applyEdnotifData(Bovine $bovine, AnimalSummaryDto $animal): void
|
|
||||||
{
|
|
||||||
$identification = $animal->identification;
|
|
||||||
if (null !== $identification) {
|
|
||||||
$bovine->setSex($identification->sex);
|
|
||||||
$bovine->setBovineType($this->resolveBovineType($identification->breedType));
|
|
||||||
$bovine->setWorkNumber($identification->workNumber);
|
|
||||||
$bovine->setBirthDate($identification->birthDate?->date);
|
|
||||||
|
|
||||||
$bovine->setMotherNationalNumber($identification->motherCarrier?->bovin?->nationalNumber);
|
|
||||||
$bovine->setMotherBovineType($this->resolveBovineType($identification->motherCarrier?->breedType));
|
|
||||||
$bovine->setFatherNationalNumber($identification->fatherIpg?->bovin?->nationalNumber);
|
|
||||||
$bovine->setFatherBovineType($this->resolveBovineType($identification->fatherIpg?->breedType));
|
|
||||||
}
|
|
||||||
|
|
||||||
$latestEntry = null;
|
|
||||||
$latestExit = null;
|
|
||||||
foreach ($animal->presencePeriods as $period) {
|
|
||||||
if (null !== $period->entry?->date && (null === $latestEntry || $period->entry->date > $latestEntry)) {
|
|
||||||
$latestEntry = $period->entry->date;
|
|
||||||
}
|
|
||||||
if (null !== $period->exit?->date && (null === $latestExit || $period->exit->date > $latestExit)) {
|
|
||||||
$latestExit = $period->exit->date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$bovine->setArrivalDate($latestEntry);
|
|
||||||
$bovine->setExitDate($latestExit);
|
|
||||||
$bovine->refreshAgeMonths();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trouve un BovineType existant par code, sinon en crée un placeholder
|
|
||||||
* que l'admin pourra renommer dans /admin/bovin/bovin-list.
|
|
||||||
*/
|
|
||||||
private function resolveBovineType(?string $code): ?BovineType
|
|
||||||
{
|
|
||||||
if (null === $code || '' === $code) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->bovineTypeCache[$code])) {
|
|
||||||
return $this->bovineTypeCache[$code];
|
|
||||||
}
|
|
||||||
|
|
||||||
$bovineType = new BovineType();
|
|
||||||
$bovineType->setCode($code);
|
|
||||||
$bovineType->setLabel(sprintf('À renommer (%s)', $code));
|
|
||||||
$this->em->persist($bovineType);
|
|
||||||
|
|
||||||
$this->bovineTypeCache[$code] = $bovineType;
|
|
||||||
|
|
||||||
return $bovineType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user