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.
81 lines
7.1 KiB
Markdown
81 lines
7.1 KiB
Markdown
# RETEX M1 (Clients) → à appliquer pour M2 (Fournisseurs)
|
||
|
||
> But : éviter de reproduire en M2 les erreurs de **contrat de sérialisation** qui ont bloqué M1.
|
||
> ~80 % des frictions M1 venaient du contrat API (sérialisation / groupes / sous-ressources), **pas** du métier.
|
||
> À lire AVANT de rédiger `spec-back.md` et `spec-front.md` du M2, et à garder ouvert pendant la rédaction.
|
||
|
||
---
|
||
|
||
## 0. TL;DR (les 3 erreurs à ne jamais refaire)
|
||
|
||
1. **Affirmer qu'un champ est « embarqué » sans vérifier les 3 maillons de sérialisation.** En M1 : `Category.code` annoncé dans `client:read`, détail annoncé embarquant contacts/adresses/ribs → **faux dans le code**. Résultat : colonnes liste vides, onglets détail impossibles à peupler.
|
||
2. **Livrer des sous-ressources en POST-only** (pas de `GetCollection`, pas d'embed) → le front ne peut pas lister les enfants de l'agrégat.
|
||
3. **Écrire la spec/les tickets sur une intention, pas sur le contrat réel.** Le docblock `Client` décrivait un embed jamais implémenté.
|
||
|
||
---
|
||
|
||
## 1. Contrat de sérialisation : les 3 maillons obligatoires
|
||
|
||
Pour **chaque champ affiché** (liste OU détail), la spec back doit prouver les trois maillons. Si un seul manque → le champ sort en quasi-IRI (`@id`/`@type` seulement) ou pas du tout.
|
||
|
||
| Maillon | Question | Exemple M1 raté |
|
||
|---|---|---|
|
||
| (a) Groupe sur la **propriété** | `#[Groups([...])]` contient-il un read-group ? | `Supplier::$addresses` sans groupe → jamais sérialisé |
|
||
| (b) Groupe dans le **`normalizationContext` de l'opération** | l'opération (`GetCollection`/`Get`) liste-t-elle ce groupe ? | `GetCollection` en `['client:read','default:read']` |
|
||
| (c) Read-group de l'**entité imbriquée** dans le contexte parent | pour embarquer les champs d'une relation (catégorie, site…), le contexte parent inclut-il `category:read` / `site:read` ? | `Category.code` ∈ `category:read`, absent du contexte client → pas de `code` |
|
||
|
||
**Règle de rédaction** : dans `spec-back.md`, faire un tableau « champ → groupe propriété → groupe(s) à ajouter au contexte de chaque opération » pour la liste ET le détail. Inclure explicitement les **relations imbriquées** (ex. catégories d'une adresse, sites d'une adresse).
|
||
|
||
## 2. Collections enfant d'un agrégat : décider embed vs GetCollection, et câbler en ENTIER
|
||
|
||
Décision à acter dès la spec back pour chaque sous-collection (contacts, adresses, RIB, lignes…) :
|
||
|
||
- **Embed dans le détail (recommandé pour un agrégat DDD)** : poser `#[Groups(['<root>:item:read'])]` sur la propriété + ajouter au `normalizationContext` du `Get` racine les read-groups des entités enfant **et** de leurs relations imbriquées. 1 requête, cohérent avec un composable `useX(id)`. Réservé aux ensembles **bornés** (ne viole pas la règle n°13 : elle vise les collections exposées, pas un embed borné d'item).
|
||
- **GetCollection sous-ressource** : `/<root>/{id}/children` paginé. À réserver aux collections potentiellement volumineuses. Si choisi, **créer l'opération** (pas seulement POST).
|
||
|
||
❌ Anti-pattern M1 : sous-ressources avec `POST` + `Get` unitaire seulement → **aucun moyen de lister** (ids non découvrables). Interdit.
|
||
|
||
## 3. Vérifier le contrat sur l'API RÉELLE avant d'écrire les tickets front
|
||
|
||
Le blocage M1 (codes/sites/sous-collections) aurait été vu en 5 min. À mettre dans la **definition of done de la spec back** :
|
||
|
||
> Créer un enregistrement de test, appeler `GET /api/<resource>` (liste) ET `GET /api/<resource>/{id}` (détail), **coller la réponse JSON réelle** dans la spec. Toute donnée affichée par le front doit apparaître dans ce JSON collé.
|
||
|
||
## 4. La spec décrit le RÉEL, pas l'intention
|
||
|
||
- Bannir les « devrait être embarqué », « est exposé » non vérifiés. Décrire ce qui existe (ou ce qui sera livré dans le ticket, en le marquant clairement « à livrer »).
|
||
- Si un docblock/commentaire existant contredit le code, le **corriger**, pas le recopier.
|
||
|
||
## 5. Réutiliser les acquis M1 (ne pas réinventer)
|
||
|
||
- **Taxonomie ERP-78** : si M2 catégorise les fournisseurs, repartir du modèle **type unique + `code` stable** (slug MAJUSCULE auto-généré, NOT NULL, figé, **lecture seule** `category:read`), filtrage métier via `?categoryCode=`. Réutiliser le contrat partagé `CategoryInterface` (pas d'import inter-module).
|
||
- **Front** : `usePaginatedList` (listes), composants `Malio*`, `useApi()`, `formatPhoneFR`, blocs réutilisables (Contact/Adresse), pattern de blocs dynamiques + modal de confirmation.
|
||
- **Archive** : flag `is_archived` **distinct** de `deleted_at` (soft delete). Restauration → gérer le 409 homonyme.
|
||
- **Normalisation = serveur** (UPPERCASE nom société, Capitalize noms, lowercase email, téléphone en chiffres). Le front envoie la saisie, réaffiche la valeur normalisée renvoyée. À documenter dans la spec.
|
||
- **Gating fin + mode strict PATCH** : PATCH par groupe de sérialisation ; tout champ hors-permission dans le payload = **403 sur l'intégralité** (pas de filtrage silencieux). Spécifier la matrice rôle × onglet.
|
||
|
||
## 6. Règles ABSOLUES transverses à rappeler dans la spec M2
|
||
|
||
- **Pagination obligatoire** (règle n°13) sur toute `GetCollection` ; échappatoire `?pagination=false` réservée aux selects de référentiels bornés.
|
||
- **`COMMENT ON COLUMN`** (règle n°12) sur chaque colonne créée/modifiée (sinon `make test` casse). Helper standard pour les colonnes Timestampable/Blamable.
|
||
- **Timestampable + Blamable** sur toute nouvelle entité métier (4 colonnes + trait) ; garde-fou archi.
|
||
- **`#[Auditable]`** sur les entités métier ; **`#[AuditIgnore]`** sur les champs sensibles (équivalents BIC/IBAN/secret).
|
||
- **`declare(strict_types=1);`** partout ; commentaires FR, code EN.
|
||
- **Routes front à plat** (pas de préfixe module), état tableau **jamais** dans l'URL.
|
||
- **3 miroirs RBAC** à toucher ensemble : `config/sidebar.php`, `frontend/tests/e2e/_fixtures/personas.ts`, `SeedE2ECommand.php`.
|
||
- **Communication inter-module** uniquement via `Shared/Domain/Contract/` ou domain events — jamais d'import direct.
|
||
|
||
## 7. Fixtures & seed dès le départ
|
||
|
||
M1 a subi un aller-retour (ERP-68) faute de fixtures alignées. Pour M2 : prévoir dès la spec un seed de fournisseurs démo **couvrant tous les cas des règles métier** (relations, catégories codées, archivés, cas comptables) + comptes de rôles démo, pour vérifier le gating et le golden path sans bricolage.
|
||
|
||
## 8. Mini-checklist de relecture de la spec M2 (avant de la déclarer prête)
|
||
|
||
- [ ] Chaque champ affiché (liste + détail) a ses 3 maillons de sérialisation documentés (propriété, contexte opération, relations imbriquées).
|
||
- [ ] Chaque sous-collection a une décision **embed vs GetCollection** explicite et **complètement câblée** (pas de POST-only).
|
||
- [ ] Réponses JSON réelles (liste + détail) collées dans la spec back.
|
||
- [ ] Matrice RBAC rôle × écran × onglet + mode strict PATCH spécifiés.
|
||
- [ ] Pagination, COMMENT ON COLUMN, Timestampable/Blamable, Audit, routes à plat : rappelés.
|
||
- [ ] Réutilisations M1 identifiées (taxonomie code, usePaginatedList, blocs, archive, normalisation).
|
||
- [ ] Seed/fixtures démo planifiés.
|