test(technique) : couvrir RG-3.x PHPUnit + capturer le contrat JSON (ERP-139) #100

Merged
matthieu merged 8 commits from feature/ERP-139-tests-phpunit-m3 into develop 2026-06-12 14:44:45 +00:00
Owner

Ticket Lesstime #139 (M3 — Répertoire prestataires, position 1.9). DoD back avant le front : suite PHPUnit consolidée sur la matrice § 8.1 + captures JSON réelles dans la spec § 4.0.bis.

Contenu

  • Fix réfs comptables : provider:read:accounting ajouté sur TvaMode/PaymentDelay/PaymentType/Bank — sans ça elles sortaient en IRI nu dans le détail prestataire (réplique du fix ERP-92 du M2, piège #1 § 4.0.bis).
  • ProviderSerializationContractTest (13 tests) : gating RIB/scalaires par omission, réfs compta en objet {id,code,label}, isArchived, embed categories/sites liste+détail, sous-collections, enveloppe AP4 ; testDodReferenceJsonShape dumpe le JSON réel (PROVIDER_DOD_DUMP=1).
  • ProviderAuditTest (5 tests) : create/update/archive (technique.Provider), iban/bic dans le diff (technique.ProviderRib, pas dAuditIgnore), trace M2M sites.
  • ProviderListTest étendu : ?pagination=false, anti-N+1, filtre ?typeCode=PRESTATAIRE.
  • ProviderRbacGatingTest étendu : restauration en conflit de nom → 409 (RG-3.14).
  • ProviderFixtures (§ 8.4) : démo idempotente (complet VIREMENT+banque+RIB, LCR+RIB, CHEQUE multi-cat, minimal, archivé) répartie sur sites 86/17/82 ; skip en env test.
  • Helper seedCompleteProvider ; spec § 4.0.bis : gabarits remplacés par les captures réelles (liste + détail avec/sans accounting.view).

Vérifications

  • make php-cs-fixer-allow-risky → 0 fichier
  • make test → OK, 677 tests, 3328 assertions (garde-fous globaux verts)

Notes

  • MR stackée sur ERP-138 (base = sa branche).
  • Fixtures démo exercées en dev via make fixtures (autowiring vérifié).
Ticket Lesstime #139 (M3 — Répertoire prestataires, position 1.9). DoD back avant le front : suite PHPUnit consolidée sur la matrice § 8.1 + captures JSON réelles dans la spec § 4.0.bis. ## Contenu - **Fix réfs comptables** : `provider:read:accounting` ajouté sur `TvaMode`/`PaymentDelay`/`PaymentType`/`Bank` — sans ça elles sortaient en IRI nu dans le détail prestataire (réplique du fix ERP-92 du M2, piège #1 § 4.0.bis). - **`ProviderSerializationContractTest`** (13 tests) : gating RIB/scalaires par omission, réfs compta en objet `{id,code,label}`, `isArchived`, embed categories/sites liste+détail, sous-collections, enveloppe AP4 ; `testDodReferenceJsonShape` dumpe le JSON réel (`PROVIDER_DOD_DUMP=1`). - **`ProviderAuditTest`** (5 tests) : create/update/archive (`technique.Provider`), iban/bic dans le diff (`technique.ProviderRib`, pas dAuditIgnore), trace M2M `sites`. - **`ProviderListTest`** étendu : `?pagination=false`, anti-N+1, filtre `?typeCode=PRESTATAIRE`. - **`ProviderRbacGatingTest`** étendu : restauration en conflit de nom → 409 (RG-3.14). - **`ProviderFixtures`** (§ 8.4) : démo idempotente (complet VIREMENT+banque+RIB, LCR+RIB, CHEQUE multi-cat, minimal, archivé) répartie sur sites 86/17/82 ; skip en env `test`. - Helper `seedCompleteProvider` ; spec § 4.0.bis : gabarits remplacés par les captures réelles (liste + détail avec/sans accounting.view). ## Vérifications - `make php-cs-fixer-allow-risky` → 0 fichier - `make test` → OK, 677 tests, 3328 assertions (garde-fous globaux verts) ## Notes - MR stackée sur ERP-138 (base = sa branche). - Fixtures démo exercées en dev via `make fixtures` (autowiring vérifié).
matthieu added the backM3-Prestatairetype/test labels 2026-06-12 13:32:49 +00:00
Author
Owner

Review consolidée — stack back M3 « Prestataires » (ERP-132 → ERP-139)

Review du propre de M3 (cloisonnement site, RBAC technique.providers.* + bypass_scope, RG-3.04/3.07/3.08). Le cœur Provider (liste/détail/écriture/export/RBAC) est solide et bien testé. Un bloquant trouvé et corrigé dans c9095ff (sur cette branche).

Fuite cross-site des sous-ressources (corrigé)

Les Get/Patch/Delete de provider_ribs|contacts|addresses/{id} passaient par le provider Doctrine par défaut (non cloisonné), et le POST résolvait le parent sans contrôle de scope → un user cloisonné pouvait lire l'IBAN/BIC d'un RIB d'un prestataire d'un autre site, l'éditer/supprimer, ou créer une sous-ressource hors périmètre. SiteScopedQueryExtension ne filtre que les SiteAwareInterface, ce que ces entités ne sont pas. La spec § 2.13 affirmait à tort que les sous-ressources « héritent du garde-fou parent ».

Fix : ProviderSiteScopeChecker (décision de scope centralisée, source unique) + provider décoré ProviderSubResourceItemProvider sur Get/Patch/Delete (→ 404 hors périmètre) + garde assertInScope au POST des 3 processors. 7 nouveaux tests (ProviderSubResourceSiteScopeTest). Spec § 2.13 corrigée.

🟠 RG-3.04 — jobTitle (Fonction) (corrigé)

Le CHECK + validateName ignoraient jobTitle, alors que la spec RG-3.04 (ligne 926) liste « Fonction » → un contact avec seulement la Fonction passait le front mais récoltait un 422 (desync). Aligné le code sur la spec. Décision à valider : si tu voulais le garde-fou minimal (jobTitle exclu), c'est l'inverse à appliquer.

Points positifs vérifiés

  • Cloisonnement Provider appliqué au SQL avant pagination (totalItems correct), détail 404, écriture 422 hors user_site.
  • Export XLSX : priority:1, scope site, gating SIREN sur accounting.view. RBAC : 3 miroirs alignés, bypass_scope correct (Usine cloisonnée).

🟡 Non bloquants (laissés tels quels)

  • Checklist DoD RG-3.07/3.08 « POST paymentType=LCR sans RIB → 422 » trompeuse : paymentType ∈ write:accounting absent du POST → le cas est structurellement un PATCH, les tests doivent cibler ça.
  • guardLastRibDeletionUnderLcr compte les RIB en mémoire — robuste, mais à border par un test « 1 seul RIB en base ».

Suite back complète verte (685 tests OK) après fix.

## Review consolidée — stack back M3 « Prestataires » (ERP-132 → ERP-139) Review du propre de M3 (cloisonnement site, RBAC `technique.providers.*` + `bypass_scope`, RG-3.04/3.07/3.08). Le cœur Provider (liste/détail/écriture/export/RBAC) est solide et bien testé. Un bloquant trouvé et **corrigé dans `c9095ff`** (sur cette branche). ### ⛔ Fuite cross-site des sous-ressources (corrigé) Les `Get/Patch/Delete` de `provider_ribs|contacts|addresses/{id}` passaient par le provider Doctrine par défaut (non cloisonné), et le `POST` résolvait le parent sans contrôle de scope → un user cloisonné pouvait **lire l'IBAN/BIC** d'un RIB d'un prestataire d'un autre site, l'éditer/supprimer, ou créer une sous-ressource hors périmètre. `SiteScopedQueryExtension` ne filtre que les `SiteAwareInterface`, ce que ces entités ne sont pas. La spec § 2.13 affirmait à tort que les sous-ressources « héritent du garde-fou parent ». **Fix** : `ProviderSiteScopeChecker` (décision de scope centralisée, source unique) + provider décoré `ProviderSubResourceItemProvider` sur Get/Patch/Delete (→ 404 hors périmètre) + garde `assertInScope` au POST des 3 processors. 7 nouveaux tests (`ProviderSubResourceSiteScopeTest`). Spec § 2.13 corrigée. ### 🟠 RG-3.04 — `jobTitle` (Fonction) (corrigé) Le CHECK + `validateName` ignoraient `jobTitle`, alors que la spec RG-3.04 (ligne 926) liste « Fonction » → un contact avec seulement la Fonction passait le front mais récoltait un 422 (desync). Aligné le code sur la spec. **Décision à valider** : si tu voulais le garde-fou minimal (jobTitle exclu), c'est l'inverse à appliquer. ### Points positifs vérifiés - Cloisonnement Provider appliqué **au SQL** avant pagination (`totalItems` correct), détail 404, écriture 422 hors `user_site`. - Export XLSX : `priority:1`, scope site, gating SIREN sur `accounting.view`. RBAC : 3 miroirs alignés, `bypass_scope` correct (Usine cloisonnée). ### 🟡 Non bloquants (laissés tels quels) - Checklist DoD RG-3.07/3.08 « POST `paymentType=LCR` sans RIB → 422 » trompeuse : `paymentType ∈ write:accounting` absent du POST → le cas est structurellement un **PATCH**, les tests doivent cibler ça. - `guardLastRibDeletionUnderLcr` compte les RIB en mémoire — robuste, mais à border par un test « 1 seul RIB en base ». Suite back complète verte (**685 tests OK**) après fix.
matthieu changed target branch from feature/ERP-138-rbac-technique-providers to develop 2026-06-12 14:36:54 +00:00
matthieu added 7 commits 2026-06-12 14:36:54 +00:00
Coeur API du repertoire prestataires (M3), jumeau du M2 fournisseurs :

- ProviderProvider : liste paginee (Paginator ORM), filtres
  search/categoryCode/siteId/includeArchived, tri companyName ASC,
  exclusion archives + soft-deletes (RG-3.16). Cloisonnement par site
  pilote par l'utilisateur (RG-3.17 / § 2.13) : liste restreinte au
  currentSite avant pagination (totalItems = perimetre), detail hors
  perimetre -> 404, bypass via sites.bypass_scope.
- ProviderProcessor : normalisation companyName (RG-3.11), POST formulaire
  principal (companyName + categories + sites), PATCH partiels par groupe
  en mode strict (RG-3.15, 403 sur tout le payload), archivage
  (RG-3.13/3.14), 409 doublon de nom (RG-3.10), garde d'ecriture cloisonnee
  des sites (RG-3.03/3.17, 422 sur sites pour les users sites.read_ref).
- ProviderReadGroupContextBuilder : gating comptabilite par AJOUT du groupe
  provider:read:accounting si accounting.view (jamais par retrait).
- ProviderFieldNormalizer : miroir SupplierFieldNormalizer.
- ApiResource cable (provider + processor) sur l'entite Provider.

Tests : ProviderApiTest, ProviderListTest, ProviderRbacGatingTest,
ProviderSiteScopeTest (26 tests). Suite complete verte (612 tests).
Expose les sous-collections du prestataire en #[ApiResource] (POST sur le
parent + PATCH/DELETE/GET unitaires), edition complete par onglet (pas de
POST-only, RETEX M1/M2) :

- ProviderContact : POST /providers/{id}/contacts, PATCH/DELETE
  /provider_contacts/{id} (security technique.providers.manage).
  ProviderContactProcessor : normalisation RG-3.11 (nom/prenom Title Case,
  telephones chiffres, email lowercase) + RG-3.04 (au moins un champ parmi
  prenom/nom/telephone/email, miroir du CHECK chk_provider_contact_name -> 422).
- ProviderAddress : POST /providers/{id}/addresses, PATCH/DELETE
  /provider_addresses/{id} (security technique.providers.manage).
  ProviderAddressProcessor : rattachement parent + cloisonnement d'ecriture des
  sites de l'adresse (RG-3.05 / § 2.13 : site hors user_site -> 422 sur sites).
- ProviderRib : POST /providers/{id}/ribs, PATCH/DELETE /provider_ribs/{id}
  (security technique.providers.accounting.manage). ProviderRibProcessor :
  RG-3.08 (DELETE du dernier RIB sous LCR -> 409).

Tests : ProviderSubResourceApiTest (19 cas) — CRUD chaque sous-ressource, 403
selon permission (Contacts/Adresses=manage, RIB=accounting.manage), 409 dernier
RIB LCR, 422 cloisonnement site adresse. Helpers addContact/addRib/paymentType
ajoutes a AbstractProviderApiTestCase.
- Provider::validatePaymentTypeConsistency (Assert\Callback, miroir Supplier ERP-89) :
  RG-3.07 VIREMENT impose une banque (violation sur bank),
  RG-3.08 LCR impose au moins un RIB (violation sur paymentType).
- ProviderProcessor : docblock realigne (RG-3.07/3.08 portees par l'entite).
- AbstractProviderApiTestCase::bank() helper referentiel.
- ProviderAccountingValidationTest : 4 cas (negatif 422 / positif 200) par RG.

Les RG-3.03/3.05/3.09 (contraintes d'entite) et l'ecriture cloisonnee (gardes
processors, RG-3.17/2.13) etaient deja posees en ERP-133/134/135 et restent couvertes.
Câble les permissions du module Technique dans toutes les sources RBAC (règle
ABSOLUE n°8, dans le même commit) :

- RbacSeeder::MATRIX : bureau/compta/commerciale reçoivent technique.providers.*
  selon la matrice § 2.9 + sites.bypass_scope (visibilité multi-site, § 2.13) ;
  usine = technique.providers.view seul, SANS bypass (cloisonnée à son site).
- config/sidebar.php : nouvelle section Technique + item Répertoire prestataires
  (/providers, module technique, permission technique.providers.view).
- personas.ts + SeedE2ECommand.php : 5 perms technique.providers.* sur le persona
  user-full (porte déjà sites.bypass_scope) — pas de nouveau persona (règle n°7).
- i18n fr.json : clés sidebar.technique.section / sidebar.technique.providers.

Test : ProviderRBACMatrixTest (miroir SupplierRBACMatrixTest) valide la matrice
rôle×verbe via app:seed-rbac, dont le cloisonnement par site de l'Usine
(détail hors site → 404). 8 tests, 65 assertions.
Ajoute provider:read:accounting sur les réfs comptables partagées (TvaMode/PaymentDelay/PaymentType/Bank) pour embarquer {id,code,label} au lieu d un IRI nu (réplique fix ERP-92). Helper seedCompleteProvider, anti-N+1 + pagination=false + filtre typeCode, restauration conflit 409, fixtures démo idempotentes. Captures JSON réelles collées dans spec § 4.0.bis.
Les operations Get/Patch/Delete des sous-ressources Contact/Adresse/RIB
passaient par le provider Doctrine par defaut (non cloisonne), et le POST
resolvait le parent sans controle de scope : un user cloisonne pouvait
lire/editer/supprimer une sous-ressource d'un prestataire hors de son site
(IBAN/BIC du RIB inclus). SiteScopedQueryExtension ne filtre que les
SiteAwareInterface, que ces entites ne sont pas.

- ProviderSiteScopeChecker : decision de cloisonnement centralisee (source
  unique), consommee par ProviderProvider (refactore), le provider decore
  et les processors.
- ProviderSubResourceItemProvider : decore le provider par defaut sur
  Get/Patch/Delete des 3 sous-ressources -> 404 si parent hors perimetre.
- Garde assertInScope au POST dans les 3 processors -> 404 si parent hors
  perimetre. ProviderOwnedInterface sur les 3 entites.

RG-3.04 : alignement code <-> spec (ligne 926). La Fonction (jobTitle) rend
desormais un contact valide a elle seule : ajout au validateName, au CHECK
chk_provider_contact_name et normalisation (normalizeText, vide -> null).

Tests : ProviderSubResourceSiteScopeTest (fuite cross-site, 7 cas) ;
RG-3.04 jobTitle reecrit. Spec § 2.13 corrigee (l'heritage n'etait pas
automatique). Suite back complete verte (685 tests).
matthieu added 1 commit 2026-06-12 14:37:51 +00:00
Merge branch 'develop' into feature/ERP-139-tests-phpunit-m3
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 4m55s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m33s
970d8560a7
matthieu merged commit 3fe0f676f6 into develop 2026-06-12 14:44:45 +00:00
matthieu deleted branch feature/ERP-139-tests-phpunit-m3 2026-06-12 14:44:46 +00:00
Sign in to join this conversation.