feat(transport) : schéma + entités Carrier + contrat lecture (ERP-155/157)
Schéma BDD du répertoire transporteurs (M4) + entités + contrat de lecture (liste + détail), socle du front. - Migration Version20260615150000 : tables carrier / carrier_address / carrier_contact / carrier_price (FK cross-module, CHECK enum, index partiel uq_carrier_name_active, COMMENT ON COLUMN). uploaded_document et qualimat_carrier réutilisées (non recréées). - Entités Carrier* (#[Auditable], Timestampable/Blamable) + ApiResource LECTURE seule (GetCollection + Get via CarrierProvider, anti-N+1, exclusion archivés + ?includeArchived). Écriture (POST/PATCH + Processor) reportée WT4+. - QualimatCarrier : mapping ORM lecture seule sur la table référentielle existante (sortie du schema_filter, mapping aligné DDL ERP-39, schema:update no-op) + endpoint de recherche read-only (§ 4.7). - Relations cross-module des prix (Client/Supplier/adresses) via contrats Shared (ClientInterface, SupplierInterface, ClientAddressInterface, SupplierAddressInterface) + resolve_target_entities — sans import inter-module (règle n°1). Ajout du groupe supplier_address:read aux champs de SupplierAddress pour l'embed. - Garde-fous : ColumnCommentsCatalog (carrier* + qualimat_carrier), makefile test-db-setup (index partiel carrier), i18n audit (transport_carrier*), EntitiesAreTimestampableBlamableTest (QualimatCarrier whitelisté). - CarrierSerializationContractTest : contrat JSON liste + détail vérifié (embeds objet, booléens, enveloppe Hydra) ; JSON réel capturé dans spec-back § 4.0.bis. make db-reset OK, make test vert (731), make nuxt-test vert (480), php-cs-fixer OK.
This commit is contained in:
@@ -16,6 +16,7 @@ use App\Shared\Domain\Attribute\Auditable;
|
||||
use App\Shared\Domain\Contract\BlamableInterface;
|
||||
use App\Shared\Domain\Contract\CategoryInterface;
|
||||
use App\Shared\Domain\Contract\SiteInterface;
|
||||
use App\Shared\Domain\Contract\SupplierAddressInterface;
|
||||
use App\Shared\Domain\Contract\TimestampableInterface;
|
||||
use App\Shared\Domain\Trait\TimestampableBlamableTrait;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
@@ -96,7 +97,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
#[ORM\Table(name: 'supplier_address')]
|
||||
#[ORM\Index(name: 'idx_supplier_address_supplier', columns: ['supplier_id'])]
|
||||
#[Auditable]
|
||||
class SupplierAddress implements TimestampableInterface, BlamableInterface
|
||||
class SupplierAddress implements TimestampableInterface, BlamableInterface, SupplierAddressInterface
|
||||
{
|
||||
use TimestampableBlamableTrait;
|
||||
|
||||
@@ -117,7 +118,11 @@ class SupplierAddress implements TimestampableInterface, BlamableInterface
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['supplier:item:read'])]
|
||||
// supplier_address:read : groupe additif consomme par l'embed cross-module
|
||||
// (CarrierPrice.supplierSupplyAddress, M4 § 3.4). Inerte pour M2 (ses contextes
|
||||
// ne l'incluent pas) — expose le libelle d'adresse quand un autre module embarque
|
||||
// une SupplierAddress.
|
||||
#[Groups(['supplier:item:read', 'supplier_address:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Supplier::class, inversedBy: 'addresses')]
|
||||
@@ -130,12 +135,12 @@ class SupplierAddress implements TimestampableInterface, BlamableInterface
|
||||
#[ORM\Column(length: 20)]
|
||||
#[Assert\NotBlank(message: 'Le type d\'adresse est obligatoire.', normalizer: 'trim')]
|
||||
#[Assert\Choice(choices: self::ADDRESS_TYPES, message: 'Le type d\'adresse doit être Prospect, Départ ou Rendu.')]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses'])]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses', 'supplier_address:read'])]
|
||||
private ?string $addressType = null;
|
||||
|
||||
#[ORM\Column(length: 80, options: ['default' => 'France'])]
|
||||
#[Assert\Length(max: 80, maxMessage: 'Le pays ne peut dépasser {{ limit }} caractères.', normalizer: 'trim')]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses'])]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses', 'supplier_address:read'])]
|
||||
private string $country = 'France';
|
||||
|
||||
// RG-2.05 : code postal a 4 ou 5 chiffres (pas de controle CP/ville serveur).
|
||||
@@ -143,24 +148,24 @@ class SupplierAddress implements TimestampableInterface, BlamableInterface
|
||||
#[ORM\Column(length: 20)]
|
||||
#[Assert\NotBlank(message: 'Le code postal est obligatoire.', normalizer: 'trim')]
|
||||
#[Assert\Regex(pattern: '/^[0-9]{4,5}$/', message: 'Le code postal doit comporter 4 ou 5 chiffres.')]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses'])]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses', 'supplier_address:read'])]
|
||||
private ?string $postalCode = null;
|
||||
|
||||
#[ORM\Column(length: 120)]
|
||||
#[Assert\NotBlank(message: 'La ville est obligatoire.', normalizer: 'trim')]
|
||||
#[Assert\Length(max: 120, maxMessage: 'La ville ne peut dépasser {{ limit }} caractères.', normalizer: 'trim')]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses'])]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses', 'supplier_address:read'])]
|
||||
private ?string $city = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Assert\NotBlank(message: 'La rue est obligatoire.', normalizer: 'trim')]
|
||||
#[Assert\Length(max: 255, maxMessage: 'La rue ne peut dépasser {{ limit }} caractères.', normalizer: 'trim')]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses'])]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses', 'supplier_address:read'])]
|
||||
private ?string $street = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Assert\Length(max: 255, maxMessage: 'Le complément d\'adresse ne peut dépasser {{ limit }} caractères.', normalizer: 'trim')]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses'])]
|
||||
#[Groups(['supplier:item:read', 'supplier:write:addresses', 'supplier_address:read'])]
|
||||
private ?string $streetComplement = null;
|
||||
|
||||
// Specifique fournisseur : nombre de bennes sur le site.
|
||||
|
||||
Reference in New Issue
Block a user