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:
+50
-7
@@ -7,7 +7,10 @@ namespace App\Module\Commercial\Infrastructure\ApiPlatform\State\Processor;
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Module\Commercial\Application\Service\SupplierFieldNormalizer;
|
||||
use App\Module\Commercial\Application\Validator\SupplierInformationCompletenessValidator;
|
||||
use App\Module\Commercial\Domain\Entity\Supplier;
|
||||
use App\Shared\Domain\Contract\BusinessRoleAwareInterface;
|
||||
use App\Shared\Domain\Security\BusinessRoles;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -40,14 +43,19 @@ use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
* collisions d'unicite en 409 (RG-2.11 doublon de nom ; RG-2.15 conflit de
|
||||
* restauration).
|
||||
*
|
||||
* Hors perimetre ERP-87 (ticket #5 « Validators ») : RG-2.03 (completude
|
||||
* Information pour la Commerciale), RG-2.07 (Virement -> banque), RG-2.08 (LCR ->
|
||||
* RIB), RG-2.10 (categorie de type FOURNISSEUR). Ces regles metier seront
|
||||
* branchees ici via des validators dedies au ticket suivant.
|
||||
* Validators metier (ERP-89). Decision figee : ce processor ne porte QUE
|
||||
* RG-2.03 (completude Information exigee pour le role Commerciale — detection du
|
||||
* role cote back, non exprimable en contrainte d'entite). Les RG inter-champs
|
||||
* RG-2.07 (Virement -> banque), RG-2.08 (LCR -> >= 1 RIB) et RG-2.10 (categorie
|
||||
* de type FOURNISSEUR) sont portees par des Assert\Callback + ->atPath() sur
|
||||
* l'entite Supplier (jouees par API Platform AVANT ce processor), pour que
|
||||
* chaque 422 porte un propertyPath consommable par extractApiViolations
|
||||
* (mapping inline, pas un toast — convention ERP-101).
|
||||
*
|
||||
* Note : la validation Symfony (Assert\NotBlank, Assert\Count sur categories...)
|
||||
* est jouee par API Platform AVANT ce processor ; on n'y traite donc que les
|
||||
* regles non exprimables en simples contraintes d'attribut.
|
||||
* Note : la validation Symfony (Assert\NotBlank, Assert\Count sur categories,
|
||||
* les Callback RG-2.07/2.08/2.10...) est jouee par API Platform AVANT ce
|
||||
* processor ; on n'y traite donc que les regles non exprimables en simples
|
||||
* contraintes d'entite (RG-2.03, qui depend du role de l'utilisateur courant).
|
||||
*
|
||||
* @implements ProcessorInterface<Supplier, Supplier>
|
||||
*/
|
||||
@@ -94,6 +102,7 @@ final class SupplierProcessor implements ProcessorInterface
|
||||
#[Autowire(service: 'api_platform.doctrine.orm.state.persist_processor')]
|
||||
private readonly ProcessorInterface $persistProcessor,
|
||||
private readonly SupplierFieldNormalizer $normalizer,
|
||||
private readonly SupplierInformationCompletenessValidator $informationValidator,
|
||||
private readonly Security $security,
|
||||
private readonly RequestStack $requestStack,
|
||||
private readonly EntityManagerInterface $em,
|
||||
@@ -117,6 +126,8 @@ final class SupplierProcessor implements ProcessorInterface
|
||||
// normalisees des deux cotes (l'etat persiste l'a deja ete).
|
||||
$this->guardManage($data);
|
||||
|
||||
$this->validateInformationCompleteness($data);
|
||||
|
||||
try {
|
||||
return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
|
||||
} catch (UniqueConstraintViolationException $e) {
|
||||
@@ -244,6 +255,38 @@ final class SupplierProcessor implements ProcessorInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RG-2.03 : si l'utilisateur porte le role metier Commerciale, TOUS les
|
||||
* champs de l'onglet Information sont obligatoires sur POST comme sur TOUT
|
||||
* PATCH — independamment des champs reellement envoyes. Garantit qu'un
|
||||
* fournisseur cree/edite par une Commerciale ne reste jamais avec un onglet
|
||||
* Information incomplet. Pour les autres roles, ces champs restent optionnels.
|
||||
*
|
||||
* Consequence (cf. spec § 7, miroir RG-1.04) : le POST n'exposant que
|
||||
* supplier:write:main, une Commerciale obtient 422 sur tout POST tant que
|
||||
* l'Information n'est pas complete -> la completude se fait via les PATCH
|
||||
* supplier:write:information.
|
||||
*/
|
||||
private function validateInformationCompleteness(Supplier $data): void
|
||||
{
|
||||
if ($this->currentUserIsCommerciale()) {
|
||||
$this->informationValidator->validate($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detection du role metier Commerciale cote back (jamais front), via le
|
||||
* contrat BusinessRoleAwareInterface (pas d'import de User — regle ABSOLUE
|
||||
* n°1). Identique au ClientProcessor (M1).
|
||||
*/
|
||||
private function currentUserIsCommerciale(): bool
|
||||
{
|
||||
$user = $this->security->getUser();
|
||||
|
||||
return $user instanceof BusinessRoleAwareInterface
|
||||
&& $user->hasBusinessRole(BusinessRoles::COMMERCIALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Champs « metier » (onglets principal + Information, hors comptabilite et
|
||||
* archivage) dont la valeur courante differe de l'etat persiste. Memes
|
||||
|
||||
Reference in New Issue
Block a user