Le callback validateBillingEmailPresence testait null === billingEmail. Une
chaine vide "" (champ vide envoye par le client) passait donc les validators ;
le ClientAddressProcessor la normalisait ensuite en null APRES la validation,
puis la persistance d'une adresse is_billing=true avec billing_email=null
violait le CHECK chk_client_address_billing_email -> 500 DBAL au lieu du 422
attendu. Symetriquement, "" sur une adresse non facturable etait rejete a tort
en 422 alors qu'un champ vide equivaut a une absence d'email.
Le callback raisonne desormais sur la presence effective (null OU chaine vide
apres trim = absent), coheremment avec la normalisation du processor. Deux cas
de test ajoutes : adresse facturable + "" -> 422, adresse non facturable +
"" -> 201.
admin / alice / bob sont desormais resolus via un lookup par username
(ensureUser) avant creation, sur le modele de ensureSystemRole (meme fichier)
et RbacDemoFixtures::ensureDemoUsers. Sans ce lookup, doctrine:fixtures:load
--append (sans purge) violait l'unicite de username en recreant ces comptes.
Resultat : tout le jeu de fixtures est rejouable en --append sans erreur ni
doublon (verifie : 2 runs consecutifs -> 7 users stables). Le flow canonical
make db-reset (purge + load) est inchange.
Donnees de demonstration dev (no-op en environnement test) couvrant les cas
metier du repertoire clients M1 :
- CategoryFixtures (Catalog) : 12 categories sur les 4 types DISTRIBUTEUR /
COURTIER / SECTEUR / AUTRE. Depend de CategoryTypeFixtures. Idempotent
(lookup name + type, hors soft-deleted).
- ClientFixtures (Commercial) : 14 clients couvrant RG-1.03 (dependant
distributeur/courtier), RG-1.13 (LCR + 2 RIB), Cheque sans RIB,
RG-1.06/07/08/11 (multi-adresses prospect/livraison/facturation), prospect
seul, RG-1.05/1.02 (3 contacts + tel secondaire), RG-1.22 (archive), onglet
Information complet, multi-categories M2M. Depend de CategoryFixtures /
SitesFixtures / CommercialReferentialFixtures. Idempotent (lookup companyName
normalise).
Resolution inter-modules via les seuls contrats Shared (CategoryInterface,
SiteProviderInterface) — regle n°1 respectee. Valeurs fournies brutes et
normalisees par ClientFieldNormalizer (companyName UPPERCASE, etc.). Donnees
conformes aux CHECK BDD et aux validators ERP-76. Garde-fou test : les deux
fixtures no-op en env test pour ne pas polluer comptages et cleanups FK.
Mirror applicatif des CHECK Postgres d'adresse via Assert\Callback sur
ClientAddress, joue avant la base pour remonter une 422 Hydra au lieu d'une
500 DBAL, et durcit RG-1.29 (categorie d'adresse limitee a SECTEUR/AUTRE) :
- validateProspectExclusivity : isProspect exclusif de isDelivery/isBilling
(RG-1.06/07/08, mirror chk_client_address_prospect_exclusive).
- validateBillingEmailPresence : billingEmail obligatoire ssi isBilling
(RG-1.11, mirror chk_client_address_billing_email).
- validateCategoryTypes : refuse une categorie DISTRIBUTEUR/COURTIER sur une
adresse (RG-1.29, violation 'categories'), via CategoryInterface.
Les CHECK BDD restent en filet de securite. Tests ClientAddressTest durcis de
>= 400 vers 422 explicite + 4 cas RG-1.29. Cahier de test M1 mis a jour.