Commit Graph

3 Commits

Author SHA1 Message Date
matthieu 54d8327fa5 feat(technique) : entités + repositories Provider* (ERP-133) (#91)
Auto Tag Develop / tag (push) Successful in 9s
PR **empilée sur ERP-132** (#90) — base = \`feature/ERP-132-migrer-schema-bdd-m3\` (ERP-132 pas encore mergé dans develop). À rebaser sur develop une fois #90 mergée.

## Périmètre (ticket Lesstime #133, M3 § 3.3/3.4/2.12/4.0)
Entités Doctrine + mapping ApiResource (squelette) + repository avec hydratation anti-N+1. Miroir des entités `Supplier*` (M2), **amputé de l'onglet Information** et **augmenté de `provider.sites`** (M2M direct, RG-3.03).

### Créé
- `Provider`, `ProviderContact`, `ProviderAddress` (simplifiée : pas de `addressType`/`bennes`/`triageProvider`), `ProviderRib` — `#[Auditable]` + Timestampable/Blamable.
- `ProviderRepositoryInterface` + `DoctrineProviderRepository` : `createListQueryBuilder` (filtres + tri seuls) + `hydrateListCollections` anti-N+1 (catégories puis **sites en relation directe**, requêtes `IN` bornées séparées — § 2.12).

### Contrat de sérialisation (RETEX M1 — 3 maillons)
Groupes posés sur l'entité (source unique) : liste = `provider:read`+`category:read`+`site:read` ; détail = +`provider:item:read`. Piège booléen `isArchived` traité (`#[Groups]`+`#[SerializedName]` sur le getter). Embed `categories[].code/name` + `sites[].name/postalCode` (objet, pas IRI).

### Consommation cross-module (§ 2.1)
- Site/Category via contrats Shared (`SiteInterface`/`CategoryInterface` + `resolve_target_entities`) — comme Supplier, conforme règle ABSOLUE n°1.
- Référentiels comptables (`TvaMode`/`PaymentDelay`/`PaymentType`/`Bank`) en relation ORM partagée directe (décision § 2.1, remontée Shared tracée HP-M4-2).

### Garde-fous / infra (requis pour le vert)
- Mapping ORM du module `Technique` dans `doctrine.yaml` (sinon les 9 tables `provider*` vues orphelines → DROP).
- Tables `provider*` ajoutées à `ColumnCommentsCatalog` + ligne `dbal:run-sql uq_provider_company_name_active` au makefile `test-db-setup`.
- 4 libellés `audit.entity.technique_*` (fr.json) ; `ProviderAddress::postalCode` whitelisté dans `EXCLUDED_LENGTH_MIRROR` (Regex CP {4,5}).

## Hors périmètre (→ ERP-134)
ApiResource **sans** `ProviderProvider`/`ProviderProcessor` ; sous-entités **sans** `#[ApiResource]`. Hydratation effective, gating accounting, cloisonnement par site, normalisation, 409 doublon, RG-3.07/3.08 → ERP-134. Sous-ressources POST/PATCH/DELETE → ticket ultérieur.

## Tests
- \`make test\` → **589/589 ✓** · \`php-cs-fixer\` → 0 correction.
- \`schema:validate\` : mapping OK ; « not in sync » résiduel strictement homologue à supplier (COMMENT via catalogue + index FK auto-Doctrine), non régressif.

---------

Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #91
2026-06-12 14:25:27 +00:00
matthieu 6a01067746 feat(commercial) : entités + repositories M2 fournisseurs (ERP-86) (#65)
Auto Tag Develop / tag (push) Successful in 7s
## ERP-86 — Entités + Repositories M2 Fournisseurs (étape 2/7)

PR **empilée sur ERP-85** (#64) : ne contient que le commit ERP-86. À merger après #64 (la base rebascule automatiquement au fil des merges de la chaîne #63#64 → develop).

Dépend de #64 (migration BDD). Bloque #87 (Provider + Processor) et suivants.

### Contenu

4 entités jumelles du M1 `Client*`, mapping ORM aligné **exactement** sur la migration ERP-85 (noms, types, longueurs, FK, M2M, index), **sans contact inline** (ERP-106) :

- **`Supplier`** — `#[Auditable]` + Timestampable/Blamable. Formulaire principal, onglet Information (+ `volumeForecast`, spécifique fournisseur), onglet Comptabilité (FK référentiels M1 partagés), archivage (`isArchived`/`archivedAt`), soft-delete préparé. Catégories M2M via `CategoryInterface` (règle n°1, pas d'import inter-module). Pas de `distributor`/`broker`.
- **`SupplierContact`** — onglet Contacts (RG-2.04 : `firstName` OU `lastName`).
- **`SupplierAddress`** — enum `addressType` (`PROSPECT`/`DEPART`/`RENDU` via `Assert\Choice`), `bennes`, `triageProvider` ; M2M sites/contacts/categories.
- **`SupplierRib`** — RIB, embed gaté comptable.
- **Repositories** : interfaces `Domain/Repository/` + impls `Infrastructure/Doctrine/`.

### Points clés

- **Contrat de sérialisation (RETEX M1, 3 maillons posés sur l'entité)** : read-groups sur les propriétés ; getters `isArchived()` / `isTriageProvider()` avec `#[Groups]` + `#[SerializedName('isX')]` (parade piège booléen n°3) ; embed `contacts`/`addresses` (`supplier:item:read`) et `ribs` (`supplier:read:accounting`). `getSites()` agrège/dédoublonne les `Site` des adresses (`name`/`postalCode`, pas de `code`).
- **Fetch-joins anti-N+1** dans le **repository de liste** : `hydrateListCollections()` en 2 passes (`categories`, puis `addresses.sites`) — évite le produit cartésien (pattern ERP-100). Filtres : recherche `companyName` + contacts liés (D1), `categoryCode`, `siteId`, archivage.
- **Pas d'`#[ApiResource]`** : Provider/Processor (gating accounting, archivage, mode strict) sont au ticket **ERP-87**. L'ajouter ici référencerait des classes inexistantes → boot/tests cassés. Les groupes de lecture/écriture sont déjà en place ; le `normalizationContext` viendra avec #87.
- **Validation FR (ERP-107)** : messages FR sur toutes les contraintes ; `Assert\Length(max)` calé sur les colonnes. Garde-fou `EntityConstraintsHaveFrenchMessageTest` étendu : `Assert\Choice` ajouté au mapping ; `addressType` et `postalCode` whitelistés du miroir Length (déjà bornés par Choice / Regex).
- Clés i18n `audit.entity.commercial_supplier*` ajoutées (garde-fou `AuditableEntitiesHaveI18nLabelTest`).

### Vérifications

- `make test` : **483/483 OK** (1965 assertions).
- `make php-cs-fixer-allow-risky` : 0 correction.
- `doctrine:schema:validate` : mapping correct (bruit d'index FK cosmétique identique au M1 `client`).

---------

Co-authored-by: admin malio <malio@yuno.malio.fr>
Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #65
Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
2026-06-08 07:18:30 +00:00
matthieu 597101262d feat(commercial) : messages de validation FR sur les contraintes back + garde-fou (ERP-107) (#59)
Auto Tag Develop / tag (push) Successful in 8s
## Contexte

Résout **ERP-107** — pendant back du mapping d'erreur par champ front (ERP-101). Le front (`useFormErrors` / `mapViolationsToRecord`) affiche sous chaque champ le `message` renvoyé par le back. Ce ticket garantit que ces messages existent, sont en FR et rattachés au bon champ.

## Changements

- **Messages FR explicites** sur toutes les contraintes `#[Assert\*]` des entités métier : `Client`, `ClientContact`, `ClientAddress`, `ClientRib`, `Category`, `Role`, `User` (Email, NotBlank, Length, Bic, Iban, PositiveOrZero, Count…).
- **Contraintes `Assert\Length` manquantes ajoutées**, calées sur le `length` de la colonne ORM (téléphones `VARCHAR(20)`, `siren`, `nTva`, `accountNumber`, `username`…). Évite une erreur Postgres 500 non rattachée au champ → 422 propre.
- **Locale FR globale** (`symfony/translation` + `default_locale: fr`) comme filet pour les messages natifs Symfony non surchargés.
- **Garde-fou** `tests/Architecture/EntityConstraintsHaveFrenchMessageTest` : échoue si une contrainte n'a pas de message FR explicite (comparaison au défaut Symfony) ou si `Assert\Length.max` diverge du `length` ORM. Whitelist justifiée pour les formats auto-bornés (Bic/Iban/Regex CP/couleur hex).
- **Test fonctionnel** du JSON 422 réel : message FR + `propertyPath` consommable par le front.
- **Convention documentée** dans `.claude/rules/backend.md`.

## Décisions

- Stratégie retenue : message FR **explicite sur toutes** les contraintes + locale FR en filet (les deux leviers du ticket).
- Garde-fou `Length == ORM length` : **test bloquant** (anti-dérive).
- RG-1.03 (distributor/broker) : pas de `Assert\Callback` ajouté — le `ClientProcessor` gère **déjà** l'exclusivité (422 + `propertyPath`). Pas de doublon.

## Hors périmètre / à suivre

- **Alignement `nullable`(DB) / `NotBlank`(back) / `required`(front)** : les champs obligatoires existants ont été confirmés, mais aucun changement de nullabilité DB n'a été fait sans arbitrage métier. À recroiser avec les astérisques front (ERP-101 / PR #58) si divergence constatée.

## Vérifications

- `make test` : **469 tests verts** (1793 assertions), 0 échec/erreur.
- `php-cs-fixer` : 0 fichier à corriger.

---------

Co-authored-by: admin malio <malio@yuno.malio.fr>
Co-authored-by: Matthieu <contact@malio.fr>
Reviewed-on: #59
Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
2026-06-04 09:27:32 +00:00