feat(technique) : ProviderProvider + ProviderProcessor + cloisonnement site (ERP-134) #93

Closed
matthieu wants to merge 4 commits from feature/ERP-134-provider-processor-cloisonnement-site into develop
Owner

Cœur API du répertoire prestataires (M3 Technique), jumeau du M2 fournisseurs. Stackée sur ERP-133 (base = feature/ERP-133-creer-entites-provider) ; à rebaser sur develop une fois ERP-133 mergé.

Ticket : #134

Livré

  • ProviderProvider : liste paginée (ApiPlatform\Doctrine\Orm\Paginator), filtres ?search/categoryCode/siteId/includeArchived, tri companyName ASC, exclusion archives + soft-deletes (RG-3.16). Cloisonnement par site piloté par l'utilisateur (RG-3.17 / § 2.13) : liste restreinte au currentSite avant pagination (totalItems = périmètre), détail hors périmètre → 404, bypass via sites.bypass_scope.
  • ProviderProcessor : normalisation companyName (RG-3.11), POST formulaire principal, 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'écriture cloisonnée des sites (RG-3.03/3.17).
  • ProviderReadGroupContextBuilder : gating comptabilité par ajout du groupe provider:read:accounting si accounting.view (jamais par retrait — parade fuite IBAN/BIC bug #4 M1).
  • ProviderFieldNormalizer (miroir M2) ; #[ApiResource] câblé sur l'entité Provider.

Tests

26 tests Technique (ProviderApiTest, ProviderListTest, ProviderRbacGatingTest, ProviderSiteScopeTest). Suite complète verte : 612 tests / 2832 assertions.

Notes de revue

  1. Cloisonnement écriture : SiteCollectionScopedExtension (module Sites) scope déjà la résolution d'IRI Site → un user sans bypass_scope/read_ref reçoit 400 (anti-énumération) avant le Processor. La garde guardSiteScope (422 sur sites) reste l'enforcement autoritaire pour les users sites.read_ref. Les deux couches sont testées.
  2. Hors scope (tickets dédiés) : RG-3.07 (Virement→banque), RG-3.08 (LCR→RIB), sous-ressources contacts/adresses/RIB, cloisonnement écriture sur provider_address.sites, export XLSX.
Cœur API du répertoire prestataires (M3 Technique), jumeau du M2 fournisseurs. **Stackée sur ERP-133** (base = `feature/ERP-133-creer-entites-provider`) ; à rebaser sur `develop` une fois ERP-133 mergé. Ticket : #134 ## Livré - **ProviderProvider** : liste paginée (`ApiPlatform\Doctrine\Orm\Paginator`), filtres `?search/categoryCode/siteId/includeArchived`, tri `companyName ASC`, exclusion archives + soft-deletes (RG-3.16). **Cloisonnement par site piloté par l'utilisateur** (RG-3.17 / § 2.13) : liste restreinte au `currentSite` **avant** pagination (`totalItems` = périmètre), détail hors périmètre → 404, bypass via `sites.bypass_scope`. - **ProviderProcessor** : normalisation `companyName` (RG-3.11), POST formulaire principal, 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'écriture cloisonnée des sites (RG-3.03/3.17). - **ProviderReadGroupContextBuilder** : gating comptabilité par **ajout** du groupe `provider:read:accounting` si `accounting.view` (jamais par retrait — parade fuite IBAN/BIC bug #4 M1). - **ProviderFieldNormalizer** (miroir M2) ; `#[ApiResource]` câblé sur l'entité `Provider`. ## Tests 26 tests Technique (ProviderApiTest, ProviderListTest, ProviderRbacGatingTest, ProviderSiteScopeTest). **Suite complète verte : 612 tests / 2832 assertions.** ## Notes de revue 1. **Cloisonnement écriture** : `SiteCollectionScopedExtension` (module Sites) scope déjà la résolution d'IRI `Site` → un user sans `bypass_scope`/`read_ref` reçoit **400 (anti-énumération)** avant le Processor. La garde `guardSiteScope` (422 sur `sites`) reste l'enforcement autoritaire pour les users `sites.read_ref`. Les deux couches sont testées. 2. **Hors scope** (tickets dédiés) : RG-3.07 (Virement→banque), RG-3.08 (LCR→RIB), sous-ressources contacts/adresses/RIB, cloisonnement écriture sur `provider_address.sites`, export XLSX.
matthieu added the backM3-Prestatairetype/feat labels 2026-06-12 09:05:32 +00:00
matthieu changed target branch from feature/ERP-133-creer-entites-provider to develop 2026-06-12 14:25:36 +00:00
matthieu added 4 commits 2026-06-12 14:25:36 +00:00
feat(technique) : module Technique + taxonomie categories prestataires
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 2m13s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m27s
6ceef62056
Cree le nouveau module Technique (pole distinct du Commercial) prerequis du
M3 repertoire prestataires :
- TechniqueModule (ID=technique, REQUIRED=false) + 5 permissions RBAC
  technique.providers.* (view / manage / accounting.view / accounting.manage
  / archive), declarees pour app:sync-permissions.
- Activation dans config/modules.php + layer front frontend/modules/technique/.
- Seed taxonomie : nouveau CategoryType PRESTATAIRE + 3 categories
  (Maintenance industrielle, Nettoyage, Transport) via migration idempotente
  (ON CONFLICT / NOT EXISTS, jonction M2M category_category_type) ET fixtures
  CategoryType/Category (survivent au purger db-reset).
- Tests : structure du module (5 permissions figees) + filtre
  GET /api/categories?typeCode=PRESTATAIRE.

Inclut la spec back/front M3 et le RETEX M1.
Cree tout le schema BDD M3 du prestataire (jumeau du M2 fournisseur), sous
le namespace racine DoctrineMigrations (FK cross-module user/category/site +
referentiels comptables M1) :

- provider : company_name + bloc Comptabilite (siren/account_number/n_tva +
  FK tva_mode/payment_delay/payment_type/bank ON DELETE RESTRICT) +
  is_archived/archived_at/deleted_at + Timestampable/Blamable. Pas d onglet
  Information (contrairement a supplier).
- M2M formulaire principal : provider_category (RG-3.09), provider_site
  (sites du prestataire, RG-3.03 — nouveau vs supplier, + idx_provider_site_site).
- Sous-collections : provider_contact (CHECK chk_provider_contact_name :
  >=1 champ parmi first_name/last_name/phone_primary/email), provider_address
  (sans address_type/bennes/triage), provider_rib.
- Jointures adresse : provider_address_site (RG-3.05), provider_address_contact,
  provider_address_category.
- Index partiel unique uq_provider_company_name_active (LOWER(company_name)
  WHERE non archive/non supprime — RG-3.10) + index FK.
- COMMENT ON COLUMN/TABLE inline sur toutes les colonnes (regle n°12).

CategoryType PRESTATAIRE non re-seede (deja cree par ERP-131). Catalogue
ColumnCommentsCatalog et ligne dbal:run-sql differes au ticket entites (ERP-133),
comme supplier : tant que les entites Provider* n existent pas, schema:update du
setup test droppe ces tables non mappees et app:apply-column-comments planterait.
- 4 entités Provider / ProviderContact / ProviderAddress / ProviderRib
  (#[Auditable] + Timestampable/Blamable), miroir Supplier* amputé de
  l'onglet Information et augmenté de provider.sites (M2M direct, RG-3.03).
- Contrat de sérialisation à 3 maillons (groupes liste/détail, getter
  isArchived + SerializedName) ; référentiels comptables consommés en
  relation ORM partagée, Site/Category via contrats Shared.
- DoctrineProviderRepository : createListQueryBuilder (filtres + tri) +
  hydratation anti-N+1 categories puis sites (relation directe) en requêtes
  IN bornées séparées.
- Mapping ORM du module Technique (doctrine.yaml), catalogue COMMENT des
  tables provider*, index partiel uq_provider_company_name_active
  (test-db-setup), libellés audit i18n technique_*, whitelist Length du CP
  ProviderAddress.

ApiResource posé en squelette : ProviderProvider / ProviderProcessor
(hydratation effective, gating accounting, cloisonnement site, normalisation,
409, RG-3.07/3.08) relèvent d'ERP-134.
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).
Author
Owner

Consolidée dans #100 : toute la pile M3-Prestataire (ERP-134 à ERP-139) a été rebasée sur develop et regroupée dans la MR #100 (mergeable, tests verts). Cette MR intermédiaire est fermée pour ne garder qu'une seule MR ouverte. Les commits de ce ticket restent présents dans #100.

Consolidée dans #100 : toute la pile M3-Prestataire (ERP-134 à ERP-139) a été rebasée sur develop et regroupée dans la MR #100 (mergeable, tests verts). Cette MR intermédiaire est fermée pour ne garder qu'une seule MR ouverte. Les commits de ce ticket restent présents dans #100.
matthieu closed this pull request 2026-06-12 14:37:09 +00:00

Pull request closed

Sign in to join this conversation.