feat : écran bovins, refacto cases, enrichissement bovins, migrations

- Ajout page infrastructure/bovine avec CRUD
- Refacto BuildingCase (suppression Statut, simplification)
- Commande EnrichBovinesCommand pour enrichir les données bovins
- 4 migrations Doctrine
- Mise à jour composables shipment/weighing
- Mise à jour README et CHANGELOG

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-04-10 14:44:53 +02:00
parent 6eb2ee2578
commit 340aa2a3c0
30 changed files with 854 additions and 422 deletions

View File

@@ -9,6 +9,7 @@ use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use App\State\BovineProcessor;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Context;
@@ -31,12 +32,14 @@ use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
normalizationContext: ['groups' => ['bovine:read']],
denormalizationContext: ['groups' => ['bovine:write']],
security: "is_granted('ROLE_ADMIN')",
processor: BovineProcessor::class,
),
new Patch(
requirements: ['id' => '\d+'],
normalizationContext: ['groups' => ['bovine:read']],
denormalizationContext: ['groups' => ['bovine:write']],
security: "is_granted('ROLE_ADMIN')",
processor: BovineProcessor::class,
),
],
security: "is_granted('ROLE_USER')",
@@ -46,19 +49,19 @@ class Bovine
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['bovine:read'])]
#[Groups(['bovine:read', 'building_case:read'])]
private ?int $id = null;
#[ORM\Column(length: 50)]
#[Groups(['bovine:read', 'bovine:write'])]
#[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
private string $nationalNumber = '';
#[ORM\Column(nullable: true)]
#[Groups(['bovine:read', 'bovine:write'])]
#[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
private ?int $receivedWeight = null;
#[ORM\Column(type: 'date_immutable', nullable: true)]
#[Groups(['bovine:read', 'bovine:write'])]
#[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
private ?DateTimeImmutable $arrivalDate = null;
@@ -66,6 +69,23 @@ class Bovine
#[Groups(['bovine:read', 'bovine:write'])]
private ?BuildingCase $buildingCase = null;
#[ORM\ManyToOne]
#[Groups(['bovine:read', 'bovine:write', 'building_case:read'])]
private ?Supplier $supplier = null;
#[ORM\Column(length: 50, nullable: true)]
#[Groups(['bovine:read', 'building_case:read'])]
private ?string $workNumber = null;
#[ORM\Column(type: 'date_immutable', nullable: true)]
#[Groups(['bovine:read', 'building_case:read'])]
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
private ?DateTimeImmutable $birthDate = null;
#[ORM\Column(length: 20, nullable: true)]
#[Groups(['bovine:read', 'building_case:read'])]
private ?string $breedCode = null;
public function getId(): ?int
{
return $this->id;
@@ -118,4 +138,52 @@ class Bovine
return $this;
}
public function getSupplier(): ?Supplier
{
return $this->supplier;
}
public function setSupplier(?Supplier $supplier): static
{
$this->supplier = $supplier;
return $this;
}
public function getWorkNumber(): ?string
{
return $this->workNumber;
}
public function setWorkNumber(?string $workNumber): static
{
$this->workNumber = $workNumber;
return $this;
}
public function getBirthDate(): ?DateTimeImmutable
{
return $this->birthDate;
}
public function setBirthDate(?DateTimeImmutable $birthDate): static
{
$this->birthDate = $birthDate;
return $this;
}
public function getBreedCode(): ?string
{
return $this->breedCode;
}
public function setBreedCode(?string $breedCode): static
{
$this->breedCode = $breedCode;
return $this;
}
}

View File

@@ -32,15 +32,15 @@ class Building
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['building:read', 'reception:read'])]
#[Groups(['building:read', 'building:summary', 'reception:read'])]
private ?int $id = null;
#[ORM\Column(length: 120)]
#[Groups(['building:read', 'reception:read'])]
#[Groups(['building:read', 'building:summary', 'reception:read'])]
private string $label = '';
#[ORM\Column(length: 50)]
#[Groups(['building:read', 'reception:read'])]
#[Groups(['building:read', 'building:summary', 'reception:read'])]
private string $code = '';
/**

View File

@@ -19,7 +19,7 @@ use Symfony\Component\Serializer\Attribute\SerializedName;
operations: [
new Get(
requirements: ['id' => '\d+'],
normalizationContext: ['groups' => ['building:read']],
normalizationContext: ['groups' => ['building_case:read', 'building:summary']],
),
new Get(
uriTemplate: '/building_cases/{id}/weights-report',
@@ -39,20 +39,20 @@ class BuildingCase
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['building:read'])]
#[Groups(['building:read', 'building_case:read'])]
private ?int $id = null;
#[ORM\Column]
#[Groups(['building:read'])]
#[Groups(['building:read', 'building_case:read'])]
#[SerializedName('caseNumber')]
private ?int $case_number = null;
#[ORM\Column(length: 255)]
#[Groups(['building:read'])]
#[Groups(['building:read', 'building_case:read'])]
private ?string $code = null;
#[ORM\Column]
#[Groups(['building:read'])]
#[Groups(['building:read', 'building_case:read'])]
private ?int $capacity = null;
/**
@@ -62,16 +62,15 @@ class BuildingCase
private Collection $id_case_position;
#[ORM\ManyToOne(inversedBy: 'buildingCases')]
#[Groups(['building_case:read'])]
#[SerializedName('building')]
private ?Building $id_building = null;
#[ORM\ManyToOne(inversedBy: 'id_case')]
#[Groups(['building:read'])]
private ?Statut $statut = null;
/**
* @var Collection<int, Bovine>
*/
#[ORM\OneToMany(targetEntity: Bovine::class, mappedBy: 'buildingCase')]
#[Groups(['building_case:read'])]
private Collection $bovines;
public function __construct()
@@ -170,16 +169,17 @@ class BuildingCase
return $this;
}
public function getStatut(): ?Statut
/**
* @return array{label: string, couleur: string}
*/
#[Groups(['building:read', 'building_case:read'])]
public function getStatut(): array
{
return $this->statut;
}
if ($this->bovines->count() > 0) {
return ['label' => 'Occupé', 'couleur' => '#3A506B'];
}
public function setStatut(?Statut $statut): static
{
$this->statut = $statut;
return $this;
return ['label' => 'Libre', 'couleur' => '#A3B18A'];
}
/**

View File

@@ -1,138 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Serializer\Attribute\SerializedName;
#[ORM\Entity]
#[ApiResource(
operations: [
new Get(
requirements: ['id' => '\d+'],
normalizationContext: ['groups' => ['building:read']],
),
new GetCollection(
normalizationContext: ['groups' => ['building:read']],
),
],
security: "is_granted('ROLE_USER')",
)]
class Statut
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['building:read'])]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Groups(['building:read'])]
private ?string $label = null;
#[ORM\Column(length: 255)]
#[Groups(['building:read'])]
private ?string $code = null;
#[ORM\Column(length: 255)]
#[Groups(['building:read'])]
#[SerializedName('couleur')]
private ?string $color = null;
/**
* @var Collection<int, BuildingCase>
*/
#[ORM\OneToMany(targetEntity: BuildingCase::class, mappedBy: 'statut')]
private Collection $id_case;
public function __construct()
{
$this->id_case = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function setId(int $id): static
{
$this->id = $id;
return $this;
}
public function getLabel(): ?string
{
return $this->label;
}
public function setLabel(string $label): static
{
$this->label = $label;
return $this;
}
public function getCode(): ?string
{
return $this->code;
}
public function setCode(string $code): static
{
$this->code = $code;
return $this;
}
public function getColor(): ?string
{
return $this->color;
}
public function setColor(string $color): static
{
$this->color = $color;
return $this;
}
/**
* @return Collection<int, BuildingCase>
*/
public function getIdCase(): Collection
{
return $this->id_case;
}
public function addIdCase(BuildingCase $idCase): static
{
if (!$this->id_case->contains($idCase)) {
$this->id_case->add($idCase);
$idCase->setStatut($this);
}
return $this;
}
public function removeIdCase(BuildingCase $idCase): static
{
if ($this->id_case->removeElement($idCase)) {
// set the owning side to null (unless already changed)
if ($idCase->getStatut() === $this) {
$idCase->setStatut(null);
}
}
return $this;
}
}