[ERP-76 + ERP-68] Validations d'adresse client (RG → 422) + fixtures démo Catalog/Commercial (#41)
Auto Tag Develop / tag (push) Successful in 11s

Stack de 2 tickets sur une branche (squash sur `develop`).

## ERP-76 (#500) — Validations d'adresse Client → 422

Les règles d'intégrité de l'onglet Adresse étaient soit non implémentées (RG-1.29), soit rejetées en 500 par les CHECK Postgres (RG-1.06/07/08/11). Elles sont désormais portées par des `Assert\Callback` applicatifs sur `ClientAddress`, qui remontent une **422 Hydra avant la base** ; les CHECK BDD restent en filet de sécurité.

- `validateProspectExclusivity` — `isProspect` exclusif de `isDelivery`/`isBilling` (RG-1.06/07/08).
- `validateBillingEmailPresence` — `billingEmail` obligatoire ssi `isBilling` (RG-1.11).
- `validateCategoryTypes` — refuse une catégorie de type DISTRIBUTEUR/COURTIER sur une adresse (RG-1.29, violation `categories`), via `CategoryInterface` (règle n°1 respectée).

Tests `ClientAddressTest` durcis (≥400 → **422 explicite**) + 4 cas RG-1.29. Cahier de test M1 mis à jour.

## ERP-68 (#486) — Fixtures démo Catalog + Commercial (dev only)

- `CategoryFixtures` (Catalog) : 12 catégories sur les 4 types.
- `ClientFixtures` (Commercial) : 14 clients couvrant les cas RG (dépendant distributeur/courtier RG-1.03, LCR + 2 RIB RG-1.13, Chèque sans RIB, multi-adresses Prospect/Livraison/Facturation RG-1.06/07/08/11, prospect seul, 3 contacts + tél. secondaire RG-1.05/1.02, archivé RG-1.22, onglet Information complet, multi-catégories M2M).

Résolution inter-modules via les seuls contrats Shared (`CategoryInterface`, `SiteProviderInterface`). Valeurs brutes normalisées par `ClientFieldNormalizer`. Données conformes aux CHECK BDD **et** aux validators ERP-76. Idempotentes (lookup `companyName`/`name`). **Garde-fou** : les deux fixtures sont no-op en environnement `test` (la base de test reste un socle minimal ; pas de pollution des comptages ni des cleanups FK).

## Bonus — idempotence fixtures

`AppFixtures` (admin/alice/bob) rendu idempotent via lookup par username : `doctrine:fixtures:load --append` est désormais rejouable sans erreur sur tout le jeu de fixtures.

## Vérifications

- `make test` : **436/436 vert** (0 échec/erreur).
- `make php-cs-fixer-allow-risky` OK.
- `make db-reset` charge sans erreur ; 2 runs `--append` consécutifs = idempotent (0 doublon ; 7 users / 14 clients / 12 catégories stables).
- `admin/admin` intact.

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #41
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 #41.
This commit is contained in:
2026-06-01 22:14:23 +00:00
committed by admin malio
parent 865180e648
commit a52e3bec34
6 changed files with 1005 additions and 57 deletions
+15 -14
View File
@@ -23,10 +23,10 @@ merge de la stack.
| RG-1.03 | distributor/broker exclusifs + type catégorie | `ClientApiTest::testPostWithDistributorAndBrokerReturns422` ; `::testPostDistributorReferencingNonDistributorReturns422` ; `::testPostValidDistributorReturns201` ; `ClientProcessorTest` (unit) | ERP-55 |
| RG-1.04 | Onglet Information obligatoire pour rôle Commerciale | `ClientProcessorTest::testCommercialeIncompleteInformationIsUnprocessable` ; `::testNonCommercialeSkipsInformationCompleteness` (unit, dormant). **Test fonctionnel + durcissement → ERP-74** | ERP-55 / **ERP-74** |
| RG-1.05 | Contact : prénom OU nom → 422 (CHECK) | `ClientSubResourceApiTest::testPostContactWithoutNameReturns422` | ERP-57 |
| RG-1.06/07/08 | Adresse prospect exclusive de livraison/facturation (CHECK) | `ClientAddressTest::testProspectAddressCannotBeDelivery` ; `::testProspectAddressCannotBeBilling` | **ERP-60** |
| RG-1.06/07/08 | Adresse prospect exclusive de livraison/facturation → 422 (Assert\Callback + CHECK filet) | `ClientAddressTest::testProspectAddressCannotBeDelivery` ; `::testProspectAddressCannotBeBilling` | ERP-60 / **ERP-76** |
| RG-1.09 | Code postal `^[0-9]{4,5}$` → 422 | `ClientSubResourceApiTest::testPostAddressWithInvalidPostalCodeReturns422` | ERP-57 |
| RG-1.10 | ≥ 1 site sur adresse → 422 | `ClientSubResourceApiTest::testPostAddressWithoutSiteReturns422` | ERP-57 |
| RG-1.11 | billingEmail obligatoire ssi isBilling (CHECK) | `ClientAddressTest::testBillingAddressRequiresBillingEmail` ; `::testNonBillingAddressRejectsBillingEmail` | **ERP-60** |
| RG-1.11 | billingEmail obligatoire ssi isBilling → 422 (Assert\Callback + CHECK filet) | `ClientAddressTest::testBillingAddressRequiresBillingEmail` ; `::testNonBillingAddressRejectsBillingEmail` | ERP-60 / **ERP-76** |
| RG-1.12 | Virement → banque obligatoire → 422 | `ClientProcessorTest::testVirementWithoutBankIsUnprocessable` ; `::testVirementWithBankPasses` (unit) | ERP-55 |
| RG-1.13 | LCR → ≥ 1 RIB ; DELETE dernier RIB en LCR → 409 | `ClientProcessorTest::testLcrWithoutRibIsUnprocessable` / `::testLcrWithRibPasses` (unit) ; `ClientSubResourceApiTest::testDeleteLastRibUnderLcrReturns409` / `::testDeleteRibNonLcrReturns204` | ERP-55 / ERP-57 |
| RG-1.14 | ≥ 1 bloc Contact pour finaliser l'onglet | **Front-driven (pas de state machine back).** Back voisin : `ClientSubResourceApiTest::testDeleteLastContactReturns409` | ERP-57 |
@@ -44,7 +44,7 @@ merge de la stack.
| RG-1.26 | Tri par défaut companyName ASC | `ClientApiTest::testListSortedByCompanyNameAscAndExcludesArchived` | ERP-55 |
| RG-1.27 | Timestampable/Blamable : created* figés, updated* mis à jour | `ClientAuditTest::testCreatedFrozenAndUpdatedByReflectsModifier` | **ERP-60** |
| RG-1.28 | PATCH multi-groupes sans permission → 403 strict (tout le payload) | `ClientProcessorTest::testStrictMixWithAccountingFieldIsForbidden` / `::testAccountingFieldWithoutPermissionIsForbidden` (unit) ; **`ClientPatchStrictTest::testMixedGroupsPatchWithoutAccountingPermissionIsForbidden`** (fonctionnel) | ERP-55 / **ERP-60** |
| RG-1.29 | Catégorie d'adresse limitée aux types SECTEUR/AUTRE | **Filtrage LECTURE = front-driven** (SearchFilter `GET /api/categories?categoryType.code[]=…`). **Validation ÉCRITURE (POST/PATPH catégorie DISTRIBUTEUR/COURTIER 422) NON IMPLÉMENTÉE côté back au M1** (absente du `ClientAddressProcessor` et de la liste § 8.1). → voir « Gaps & suivi » | — (gap) |
| RG-1.29 | Catégorie d'adresse limitée aux types SECTEUR/AUTRE | **Filtrage LECTURE = front-driven** (SearchFilter `GET /api/categories?categoryType.code[]=…`). **Validation ÉCRITURE** : `ClientAddress::validateCategoryTypes` (Assert\Callback) rejette une catégorie DISTRIBUTEUR/COURTIER en 422 (violation `categories`). Tests : `ClientAddressTest::testAddressRejectsDistributorCategory` / `::testAddressRejectsBrokerCategory` / `::testAddressAcceptsSectorCategory` / `::testAddressAcceptsOtherCategory` | **ERP-76** |
## Couvertures transverses
@@ -66,14 +66,15 @@ merge de la stack.
## Gaps & suivi
- **RG-1.29 (validation écriture)** : refuser une catégorie de type
`DISTRIBUTEUR`/`COURTIER` sur une `ClientAddress` (→ 422, violation
`categories`) n'est pas implémenté au M1. La spec § 8.1 ne le liste pas comme
cas de test back ; le filtrage de lecture est front-driven. **Suggestion** :
ouvrir un follow-up (durcissement `ClientAddressProcessor`) ou l'intégrer à
ERP-74. Aucune invention de feature dans ERP-60 (ticket test-only).
- **Violations CHECK → statut HTTP** : les CHECK d'adresse (RG-1.06/07/08/11)
sont aujourd'hui rejetées par la base (statut ≥ 400) mais sans mapping fin
vers 422 (pas d'`exception_to_status` ni de listener DBAL→HTTP). Les tests
ERP-60 assertent donc le **rejet** (≥ 400). Un mapping explicite vers 422
serait une amélioration UX d'API (follow-up possible).
- ~~**RG-1.29 (validation écriture)**~~ — **résolu en ERP-76**. La validation
d'écriture refuse désormais une catégorie de type `DISTRIBUTEUR`/`COURTIER` sur
une `ClientAddress` (→ 422, violation `categories`) via l'Assert\Callback
`ClientAddress::validateCategoryTypes`. Le filtrage de lecture reste
front-driven (SearchFilter). Couvert par `ClientAddressTest`.
- ~~**Violations CHECK → statut HTTP**~~ — **résolu en ERP-76**. Les règles
d'adresse RG-1.06/07/08/11 sont désormais rejetées en **422** par des
Assert\Callback applicatifs (`validateProspectExclusivity` /
`validateBillingEmailPresence`) qui s'exécutent AVANT la base. Les CHECK
Postgres (`chk_client_address_prospect_exclusive` /
`chk_client_address_billing_email`) restent en filet de sécurité. Les tests
`ClientAddressTest` assertent maintenant le 422 explicite (et non plus ≥ 400).