feat(commercial) : validators M2 fournisseurs (RG-2.03/2.07/2.08/2.10) (ERP-89) (#68)
Auto Tag Develop / tag (push) Successful in 7s
Auto Tag Develop / tag (push) Successful in 7s
Étape 4/7 du M2 fournisseurs — stackée sur #67 (ERP-88). ## Périmètre (RG-2.03 / 2.07 / 2.08 / 2.10) Décision figée ERP-89 : les RG inter-champs passent par `Assert\Callback` + `->atPath()` sur l'entité Supplier (et non dans le Processor), pour que chaque 422 porte un `propertyPath` consommable par `extractApiViolations` (mapping inline, pas un toast — ERP-101). - **RG-2.10** — `Supplier::validateCategoryType()` → `atPath('categories')` : catégories de type FOURNISSEUR uniquement sur `supplier.categories` (miroir d'ERP-88 côté adresse). - **RG-2.07** — `Supplier::validatePaymentTypeConsistency()` → `atPath('bank')` : VIREMENT impose une banque. - **RG-2.08** — même Callback → `atPath('ribs')` : LCR impose ≥ 1 RIB (le 409 sur DELETE du dernier RIB en LCR reste porté par ERP-88). - **RG-2.03** — `SupplierInformationCompletenessValidator` (8 champs Information dont `volumeForecast`), invoqué par le `SupplierProcessor` après détection back du rôle Commerciale via `BusinessRoleAwareInterface`. Le Processor ne porte que rôle / mode strict / gating. ## Tests (16, verts) - `SupplierValidationTest` — Callbacks RG-2.07/2.08/2.10, assertion par propertyPath. - `SupplierInformationCompletenessValidatorTest` — complétude / champs manquants / zéros valides. - `SupplierProcessorTest` — détection rôle RG-2.03 (POST + PATCH main-only + non-Commerciale). `make test` : 499 tests OK. `php-cs-fixer` : clean. --------- Co-authored-by: admin malio <malio@yuno.malio.fr> Co-authored-by: Matthieu <contact@malio.fr> Reviewed-on: #68 Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
This commit was merged in pull request #68.
This commit is contained in:
@@ -25,6 +25,7 @@ use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Serializer\Attribute\SerializedName;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
/**
|
||||
* Fournisseur (M2 Commercial) — entite racine du repertoire fournisseurs,
|
||||
@@ -133,6 +134,20 @@ class Supplier implements TimestampableInterface, BlamableInterface
|
||||
{
|
||||
use TimestampableBlamableTrait;
|
||||
|
||||
/**
|
||||
* RG-2.10 : seules les categories de ce type sont autorisees sur le
|
||||
* fournisseur (entite principale). Miroir de SupplierAddress (ERP-88).
|
||||
* S'appuie sur CategoryInterface::getCategoryTypeCode() (pas d'import du
|
||||
* module Catalog — regle ABSOLUE n°1).
|
||||
*/
|
||||
private const string REQUIRED_CATEGORY_TYPE_CODE = 'FOURNISSEUR';
|
||||
|
||||
/** RG-2.07 : code du type de reglement imposant une banque. */
|
||||
private const string PAYMENT_TYPE_VIREMENT = 'VIREMENT';
|
||||
|
||||
/** RG-2.08 : code du type de reglement imposant au moins un RIB. */
|
||||
private const string PAYMENT_TYPE_LCR = 'LCR';
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
@@ -280,6 +295,65 @@ class Supplier implements TimestampableInterface, BlamableInterface
|
||||
$this->ribs = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* RG-2.10 : toute categorie posee sur le fournisseur doit etre de type
|
||||
* FOURNISSEUR -> sinon 422 avec violation sur le champ `categories`
|
||||
* (propertyPath aligne ERP-101, message FR ERP-107). Miroir de
|
||||
* SupplierAddress::validateCategoryType (ERP-88). S'appuie sur
|
||||
* CategoryInterface::getCategoryTypeCode() (pas d'import du module Catalog —
|
||||
* regle ABSOLUE n°1). Joue avant la base via la validation API Platform, sur
|
||||
* POST (categories ∈ supplier:write:main) comme sur PATCH.
|
||||
*/
|
||||
#[Assert\Callback]
|
||||
public function validateCategoryType(ExecutionContextInterface $context): void
|
||||
{
|
||||
foreach ($this->categories as $category) {
|
||||
if ($category instanceof CategoryInterface
|
||||
&& self::REQUIRED_CATEGORY_TYPE_CODE !== $category->getCategoryTypeCode()) {
|
||||
$context->buildViolation('Type de catégorie non autorisé (FOURNISSEUR attendu).')
|
||||
->atPath('categories')
|
||||
->addViolation()
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RG-2.07 / RG-2.08 : coherence du type de reglement comptable. Decision
|
||||
* figee ERP-89 : ces RG inter-champs passent par une contrainte d'entite
|
||||
* (Assert\Callback + ->atPath()) et NON par le SupplierProcessor, afin que
|
||||
* chaque 422 porte un propertyPath exploitable par extractApiViolations
|
||||
* (mapping inline sous le champ, pas un toast — convention ERP-101).
|
||||
* - RG-2.07 : paymentType = VIREMENT impose une banque -> violation sur `bank`.
|
||||
* - RG-2.08 : paymentType = LCR impose au moins un RIB -> violation sur `ribs`
|
||||
* (le 409 sur DELETE du dernier RIB en LCR est porte par ERP-88).
|
||||
*
|
||||
* Ces champs vivant dans le groupe d'ecriture comptable (absent du POST, qui
|
||||
* n'expose que supplier:write:main), la contrainte ne mord en pratique que
|
||||
* sur le PATCH de l'onglet Comptabilite.
|
||||
*/
|
||||
#[Assert\Callback]
|
||||
public function validatePaymentTypeConsistency(ExecutionContextInterface $context): void
|
||||
{
|
||||
$paymentCode = $this->paymentType?->getCode();
|
||||
|
||||
if (self::PAYMENT_TYPE_VIREMENT === $paymentCode && null === $this->bank) {
|
||||
$context->buildViolation('La banque est obligatoire pour le type de règlement Virement.')
|
||||
->atPath('bank')
|
||||
->addViolation()
|
||||
;
|
||||
}
|
||||
|
||||
if (self::PAYMENT_TYPE_LCR === $paymentCode && $this->ribs->isEmpty()) {
|
||||
$context->buildViolation('Au moins un RIB est obligatoire pour le type de règlement LCR.')
|
||||
->atPath('ribs')
|
||||
->addViolation()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
|
||||
Reference in New Issue
Block a user