Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
| Numéro du ticket | Titre du ticket | |------------------|-----------------| | #278 | Plan du site | ## Description de la PR [#278] Plan du site ## Modification du .env ## Check list - [ ] Pas de régression - [x] TU/TI/TF rédigée - [x] TU/TI/TF OK - [ ] CHANGELOG modifié Co-authored-by: Matteo <matteo@yuno.malio.fr> Reviewed-on: #33 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
215 lines
7.0 KiB
PHP
215 lines
7.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\State;
|
|
|
|
use ApiPlatform\Metadata\Operation;
|
|
use ApiPlatform\State\ProviderInterface;
|
|
use App\Entity\Bovine;
|
|
use App\Entity\BuildingCase;
|
|
use DateTimeImmutable;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Dompdf\Dompdf;
|
|
use Malio\EdnotifBundle\Bovin\Api\BovinApiInterface;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
use Throwable;
|
|
use Twig\Environment;
|
|
use Twig\Error\LoaderError;
|
|
use Twig\Error\RuntimeError;
|
|
use Twig\Error\SyntaxError;
|
|
|
|
final readonly class BuildingCaseWeightsReportProvider implements ProviderInterface
|
|
{
|
|
private const FRENCH_MONTHS = [
|
|
1 => 'Janvier',
|
|
2 => 'Février',
|
|
3 => 'Mars',
|
|
4 => 'Avril',
|
|
5 => 'Mai',
|
|
6 => 'Juin',
|
|
7 => 'Juillet',
|
|
8 => 'Août',
|
|
9 => 'Septembre',
|
|
10 => 'Octobre',
|
|
11 => 'Novembre',
|
|
12 => 'Décembre',
|
|
];
|
|
|
|
public function __construct(
|
|
private Environment $twig,
|
|
private EntityManagerInterface $entityManager,
|
|
private BovinApiInterface $bovinApi,
|
|
) {}
|
|
|
|
/**
|
|
* @throws RuntimeError
|
|
* @throws SyntaxError
|
|
* @throws LoaderError
|
|
*/
|
|
public function provide(Operation $operation, array $uriVariables = [], array $context = []): Response
|
|
{
|
|
$id = $uriVariables['id'] ?? null;
|
|
if (null === $id) {
|
|
throw new NotFoundHttpException('Case not found.');
|
|
}
|
|
|
|
$buildingCase = $this->entityManager->getRepository(BuildingCase::class)->find($id);
|
|
if (!$buildingCase instanceof BuildingCase) {
|
|
throw new NotFoundHttpException('Case not found.');
|
|
}
|
|
|
|
$rows = [];
|
|
$firstArrivalDate = null;
|
|
$headerBreedCode = null;
|
|
foreach ($buildingCase->getBovines() as $bovine) {
|
|
if (!$bovine instanceof Bovine) {
|
|
continue;
|
|
}
|
|
|
|
$workNumber = null;
|
|
$birthDate = null;
|
|
$breedCode = null;
|
|
|
|
try {
|
|
$animalFileDto = $this->bovinApi->getAnimalFile(
|
|
nationalNumber: $bovine->getNationalNumber(),
|
|
countryCode: 'FR',
|
|
);
|
|
|
|
$workNumber = $animalFileDto->identification?->workNumber;
|
|
$birthDate = $animalFileDto->identification?->birthDate?->date?->format('d/m/y');
|
|
$breedCode = $this->normalizeBreedCode($animalFileDto->identification?->breedType);
|
|
if (null === $headerBreedCode && null !== $breedCode) {
|
|
$headerBreedCode = $breedCode;
|
|
}
|
|
} catch (Throwable) {
|
|
// Keep row data even if external identification service is unavailable.
|
|
}
|
|
|
|
$arrivalDate = $bovine->getArrivalDate();
|
|
if ($arrivalDate instanceof DateTimeImmutable && null === $firstArrivalDate) {
|
|
$firstArrivalDate = $arrivalDate;
|
|
}
|
|
|
|
$projectedWeights = $this->buildProjectedWeights(
|
|
$bovine->getReceivedWeight(),
|
|
$arrivalDate,
|
|
$breedCode,
|
|
);
|
|
|
|
$rows[] = [
|
|
'nationalNumber' => $bovine->getNationalNumber(),
|
|
'workNumber' => $workNumber,
|
|
'birthDate' => $birthDate,
|
|
'receivedWeight' => $bovine->getReceivedWeight(),
|
|
'arrivalDate' => $bovine->getArrivalDate()?->format('d/m/Y'),
|
|
'projectedWeights' => $projectedWeights,
|
|
];
|
|
}
|
|
|
|
$monthHeaders = $this->buildMonthHeaders($firstArrivalDate, $headerBreedCode);
|
|
|
|
$dompdf = new Dompdf();
|
|
$html = $this->twig->render('case_weights_report.html.twig', [
|
|
'buildingCase' => $buildingCase,
|
|
'rows' => $rows,
|
|
'monthHeaders' => $monthHeaders,
|
|
'printedAt' => new DateTimeImmutable(),
|
|
]);
|
|
|
|
$dompdf->loadHtml($html);
|
|
$dompdf->setPaper('A4', 'landscape');
|
|
$dompdf->render();
|
|
|
|
$filename = sprintf('tableau-poids-case-%d.pdf', $buildingCase->getId());
|
|
|
|
return new Response($dompdf->output(), Response::HTTP_OK, [
|
|
'Content-Type' => 'application/pdf',
|
|
'Content-Disposition' => 'inline; filename="'.$filename.'"',
|
|
]);
|
|
}
|
|
|
|
private function normalizeBreedCode(mixed $breedType): ?string
|
|
{
|
|
if (null === $breedType) {
|
|
return null;
|
|
}
|
|
|
|
if (is_numeric($breedType)) {
|
|
return (string) $breedType;
|
|
}
|
|
|
|
if (is_string($breedType) && preg_match('/\d+/', $breedType, $matches)) {
|
|
return $matches[0];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function resolveDailyGainKg(?string $breedCode): float
|
|
{
|
|
return match ($breedCode) {
|
|
'34' => 1.3, // Limousin
|
|
'38' => 1.5, // Charolais
|
|
default => 1.4, // Other breeds
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @return array<int, null|float>
|
|
*/
|
|
private function buildProjectedWeights(?int $receivedWeight, ?DateTimeImmutable $arrivalDate, ?string $breedCode): array
|
|
{
|
|
$result = array_fill(0, 12, null);
|
|
if (null === $receivedWeight || !$arrivalDate instanceof DateTimeImmutable) {
|
|
return $result;
|
|
}
|
|
|
|
$currentWeight = (float) $receivedWeight;
|
|
$dailyGainKg = $this->resolveDailyGainKg($breedCode);
|
|
|
|
for ($i = 0; $i < 12; ++$i) {
|
|
$monthDate = $arrivalDate->modify('first day of this month')->modify(sprintf('+%d month', $i));
|
|
$daysInMonth = (int) $monthDate->format('t');
|
|
|
|
$daysToApply = 0 === $i
|
|
? max($daysInMonth - (int) $arrivalDate->format('j'), 0)
|
|
: $daysInMonth;
|
|
|
|
$currentWeight += $daysToApply * $dailyGainKg;
|
|
$result[$i] = $currentWeight;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @return array<int, array{name:string,days:string,base:string}>
|
|
*/
|
|
private function buildMonthHeaders(?DateTimeImmutable $arrivalDate, ?string $breedCode): array
|
|
{
|
|
$referenceDate = $arrivalDate ?? new DateTimeImmutable('first day of january this year');
|
|
$dailyGainKg = $this->resolveDailyGainKg($breedCode);
|
|
$headers = [];
|
|
|
|
for ($i = 0; $i < 12; ++$i) {
|
|
$monthDate = $referenceDate->modify('first day of this month')->modify(sprintf('+%d month', $i));
|
|
$monthIndex = (int) $monthDate->format('n');
|
|
$daysInMonth = (int) $monthDate->format('t');
|
|
$daysToApply = 0 === $i
|
|
? max($daysInMonth - (int) $referenceDate->format('j'), 0)
|
|
: $daysInMonth;
|
|
|
|
$headers[] = [
|
|
'name' => self::FRENCH_MONTHS[$monthIndex],
|
|
'days' => sprintf('%d jours', $daysToApply),
|
|
'baseValue' => $daysToApply * $dailyGainKg,
|
|
];
|
|
}
|
|
|
|
return $headers;
|
|
}
|
|
}
|