diff --git a/docs/specs/M1-clients/spec-back.md b/docs/specs/M1-clients/spec-back.md index dc9597d..82477c1 100644 --- a/docs/specs/M1-clients/spec-back.md +++ b/docs/specs/M1-clients/spec-back.md @@ -122,7 +122,7 @@ Tentative de doublon → `409 Conflict` géré par le `ClientProcessor` qui attr Pattern Starseed standard : - `#[Auditable]` sur `Client`, `ClientContact`, `ClientAddress`, `ClientRib` -- `#[AuditIgnore]` sur les champs sensibles : `ClientRib.iban`, `ClientRib.bic` +- **Tous les champs sont auditables** (pas d'`#[AuditIgnore]`) — y compris `ClientRib.iban` et `ClientRib.bic`. Décision validée par Matthieu en revue de MR (29/05/2026) : l'audit étant **admin-only** côté Starseed, l'équipe a besoin de tracer les modifications RIB pour le suivi comptable et la conformité. - Audit Many-to-Many automatique pour la collection `client.categories` (cf. doc audit-log) ### 2.6 Timestampable + Blamable @@ -138,10 +138,12 @@ Toutes les entités métier nouvelles implémentent `TimestampableInterface` + ` | `commercial.clients.view` | ✅ | ✅ | ✅ | ✅ | ❌ | | `commercial.clients.manage` | ✅ | ✅ | ❌ | ✅ | ❌ | | `commercial.clients.accounting.view` | ✅ | ❌ | ✅ | ❌ | ❌ | -| `commercial.clients.accounting.manage` | ✅ | ❌ | ❌ | ❌ | ❌ | +| `commercial.clients.accounting.manage` | ✅ | ❌ | ✅ | ❌ | ❌ | | `commercial.clients.archive` | ✅ | ❌ | ❌ | ❌ | ❌ | -**Note** : Commerciale a `view` global mais **n'a pas** `accounting.view` → l'onglet Comptabilité est masqué pour ce rôle. Le filtre se fait à 2 niveaux : (a) API Platform `security` sur les opérations qui exposent les groupes `client:accounting:*` ; (b) front masque l'onglet via `usePermissions().has('commercial.clients.accounting.view')`. +**Notes** : +- **Compta peut éditer l'onglet Comptabilité** (`accounting.manage`) d'un client existant — décision revue MR Matthieu 29/05, aligné avec le docx d'origine. Compta **ne peut pas créer** un client (pas de `manage` global) ni modifier les autres onglets. +- Commerciale a `view` global mais **n'a pas** `accounting.view` → l'onglet Comptabilité est masqué pour ce rôle. Le filtre se fait à 2 niveaux : (a) API Platform `security` sur les opérations qui exposent les groupes `client:accounting:*` ; (b) front masque l'onglet via `usePermissions().has('commercial.clients.accounting.view')`. ### 2.8 Validation incrémentale par onglet (workflow front-driven) @@ -842,7 +844,7 @@ Cf. § 2.7 (matrice détaillée). 2. **`frontend/tests/e2e/_fixtures/personas.ts`** — attribuer les permissions : - Admin : `view` + `manage` + `accounting.view` + `accounting.manage` + `archive` - Bureau : `view` + `manage` - - Compta : `view` + `accounting.view` + - Compta : `view` + `accounting.view` + `accounting.manage` - Commerciale : `view` + `manage` - Usine : aucune @@ -859,12 +861,10 @@ Synchronisation finale : `php bin/console app:sync-permissions`. ### 6.1 Audit complet via `#[Auditable]` -- `Client` : `#[Auditable]` (tous les champs sauf ceux marqués `#[AuditIgnore]`) +- `Client` : `#[Auditable]` (tous les champs) - `ClientContact` : `#[Auditable]` - `ClientAddress` : `#[Auditable]` -- `ClientRib` : - - `#[Auditable]` sur l'entité - - `#[AuditIgnore]` sur `iban` et `bic` (RGPD / sécurité) +- `ClientRib` : `#[Auditable]` sur l'entité, **tous les champs auditables y compris `iban` et `bic`** (décision Matthieu en revue MR 29/05 : l'audit étant admin-only, l'équipe a besoin de ces infos pour le suivi comptable et la conformité). L'audit M2M sur `client.categories` est automatique (cf. doc audit-log) — produit `{categories: {added: [3], removed: [7]}}`. @@ -938,6 +938,14 @@ Cf. § 2.6. Pattern Shared standard. - **RG-1.27** : Pattern Shared standard (cf. RG-1.15 à RG-1.17 du M0). Tous les actes (POST/PATCH/archive/RIB/contact/adresse) tracent `updatedAt` + `updatedBy`. `createdAt` + `createdBy` posés au POST initial. +### PATCH mix de groupes (mode strict) + +- **RG-1.28** : Si un PATCH contient des champs de **plusieurs groupes** de sérialisation et que l'utilisateur **n'a pas toutes les permissions** correspondantes, le `ClientProcessor` renvoie **403 Forbidden sur l'ensemble du payload** (mode strict — pas de filtrage silencieux). Le front est responsable de ne JAMAIS envoyer de champs hors-permission (les onglets masqués via `usePermissions()` ne génèrent pas de payload). Cette règle protège contre les appels API directs malveillants. Exemple : un Bureau qui envoie `{ "companyName": "...", "siren": "..." }` → 403, le message d'erreur précise « Champ `siren` requiert la permission `commercial.clients.accounting.manage` ». + +### Catégorie sur ClientAddress (filtrage par type) + +- **RG-1.29** : Le `` Catégorie de l'onglet Adresse n'expose **que** les `Category` dont `categoryType.code IN ('SECTEUR', 'AUTRE')`. Les types `DISTRIBUTEUR` et `COURTIER` qualifient une **relation entre clients** (cf. RG-1.03) et n'ont pas de sens sur une adresse physique. Implémentation : `ClientAddressProvider` filtre côté serveur via paramètre de requête à l'endpoint `GET /api/categories?categoryType.code[]=SECTEUR&categoryType.code[]=AUTRE` (SearchFilter API Platform). Côté validation du POST/PATCH : si l'utilisateur tente de poster une catégorie de type DISTRIBUTEUR ou COURTIER sur une adresse → **422** avec violation `categories: "Type de catégorie non autorisé sur une adresse."`. + ## 8. Tests à automatiser ### 8.1 Cas à couvrir (back — PHPUnit) @@ -965,8 +973,10 @@ Cf. § 2.6. Pattern Shared standard. - [ ] **RG-1.26** : GET liste → tri companyName ASC - [ ] **RG-1.27** : POST + PATCH → createdAt/createdBy figés, updatedAt/updatedBy mis à jour - [ ] **RBAC** : Bureau, Commerciale, Compta sur chaque permission (matrice § 2.7) — 200/403 selon le verbe -- [ ] **Compta accounting.view** : GET client retourne les champs accounting ; PATCH accounting par Compta → 403 -- [ ] **Audit** : POST + PATCH + archive → audit_log avec entity_type='Client', `changes` correct ; iban/bic absents du diff (AuditIgnore) +- [ ] **Compta accounting.view + accounting.manage** : GET client retourne les champs accounting ; PATCH onglet accounting par Compta → 200 ; PATCH onglet info / contacts / adresses par Compta → 403 +- [ ] **Compta POST création** : Compta → 403 (pas de `manage` global) +- [ ] **PATCH mix groupes** : Bureau envoie payload avec `companyName` (write:main) + `siren` (write:accounting) → **403 sur tout le payload** (strict, RG-1.28) +- [ ] **Audit** : POST + PATCH + archive → audit_log avec entity_type='Client', `changes` correct ; **iban/bic présents dans le diff** (pas d'AuditIgnore, cf. § 6.1) - [ ] **Migration** : `make db-reset` → schéma OK, seed des 4 référentiels + CategoryType (DISTRIBUTEUR/COURTIER/SECTEUR/AUTRE) présent ; index partiels présents ### 8.2 Cas à couvrir (front — Vitest) @@ -994,9 +1004,9 @@ Cf. § 2.6. Pattern Shared standard. - **HP-M2-7** : **Onglet Échanges** (timeline d'emails / appels). Placeholder blanc. - **HP-M2-8** : **Restauration d'un client soft-deleted** (post-DELETE M2). Pas pertinent au M1. - **HP-M2-9** : **Export CSV** (en plus du XLSX). À étudier si besoin métier. -- **HP-M2-10** : **Compta en édition de l'onglet Comptabilité** (réintroduction de la ligne du tableau du `.docx` invalidée par décision Tristan 28/05). Demande explicite + décision archi à formaliser. +- **HP-M2-10** : ~~Compta en édition de l'onglet Comptabilité~~ — **devenu nominal au M1** suite à revue MR Matthieu 29/05 (cf. § 2.7 et § 5.2). HP supprimé. - **HP-M2-11** : **Périmètre Commerciale** (« consultation selon périmètre » — formulation floue du doc). Au M1, Commerciale voit **tous** les clients en consultation (sauf Comptabilité). Si besoin de cloisonner par portefeuille (un commercial ne voit que SES clients), à spec dédiée. -- **HP-M2-12** : **Création par Compta limitée à l'onglet Comptabilité** (idem HP-M2-10 — invalidée au M1). +- **HP-M2-12** : ~~Création par Compta limitée à l'onglet Comptabilité~~ — Compta n'a toujours pas le droit de **créer** un client au M1 (pas de `manage` global). Si demande métier future, à spec dédiée. - **HP-M2-13** : **Référencement entrant** (autres modules ajoutent une FK `client_id`). Les modules Commandes, Factures, etc. ajouteront leurs FK quand ils arriveront. Aucun changement côté Client. - **HP-M2-14** : **Validation IBAN/BIC stricte côté serveur**. Au M1, validation Symfony standard `Assert\Iban` et `Assert\Bic` côté entité `ClientRib`. Pas de check externe (banque réelle, etc.). - **HP-M2-15** : **Validation SIREN stricte** (algorithme Luhn). Au M1, validation `Assert\Length(min: 9, max: 9)` + `Assert\Regex('/^\d{9}$/')` + algorithme Luhn dans un validator custom. diff --git a/docs/specs/M1-clients/spec-front.md b/docs/specs/M1-clients/spec-front.md index 07483f0..b6384b9 100644 --- a/docs/specs/M1-clients/spec-front.md +++ b/docs/specs/M1-clients/spec-front.md @@ -10,7 +10,7 @@ date_redaction: 2026-05-28 # === LIENS === maquette_figma: "https://www.figma.com/design/jRYgT0T9c03VsEbjGhCwwS/Composants---Design-System?node-id=1132-31898" -regles_metier: [RG-1.01, RG-1.02, RG-1.03, RG-1.04, RG-1.05, RG-1.06, RG-1.07, RG-1.08, RG-1.09, RG-1.10, RG-1.11, RG-1.12, RG-1.13, RG-1.14, RG-1.15, RG-1.16, RG-1.17, RG-1.18, RG-1.19, RG-1.20, RG-1.21] +regles_metier: [RG-1.01, RG-1.02, RG-1.03, RG-1.04, RG-1.05, RG-1.06, RG-1.07, RG-1.08, RG-1.09, RG-1.10, RG-1.11, RG-1.12, RG-1.13, RG-1.14, RG-1.15, RG-1.16, RG-1.17, RG-1.18, RG-1.19, RG-1.20, RG-1.21, RG-1.22, RG-1.23, RG-1.24, RG-1.25, RG-1.26, RG-1.27, RG-1.28, RG-1.29] roles: [Admin, Bureau, Compta, Commerciale, Usine] lien_spec_back: ./spec-back.md @@ -46,11 +46,11 @@ Permettre aux utilisateurs Starseed (selon rôle) de gérer le **répertoire des |---|---|---|---| | **Admin** | ✅ Tout | ✅ Tout | ✅ | | **Bureau** | ✅ Tout | ✅ Tout sauf onglet Comptabilité | ❌ | -| **Compta** | ✅ Tout | ❌ (lecture seule) | ❌ | +| **Compta** | ✅ Tout | ✅ Onglet Comptabilité uniquement | ❌ | | **Commerciale** | ✅ Tout sauf Comptabilité | ✅ Tout sauf Comptabilité | ❌ | | **Usine** | ❌ | ❌ | ❌ | -> **⚠ Décision validée par Tristan (28/05/2026)** : le rôle **Compta est en lecture seule** sur l'ensemble du module clients, y compris l'onglet Comptabilité. Le tableau d'origine du `.docx` indiquait « Compta = Ajout / Modification : Onglet Comptabilité uniquement » — cette ligne est **invalidée** par cette spec. Si un besoin métier d'édition apparaît plus tard, une décision archi dédiée sera prise (cf. HP-X de [`spec-back.md`](./spec-back.md)). +> **Note** : aligné sur le docx d'origine — Compta édite uniquement l'onglet Comptabilité (champs SIREN / TVA / Délai de règlement / Type de règlement / Banque / RIBs). Compta ne peut pas **créer** un client (pas de droit `manage` général), mais peut éditer la partie comptable d'un client existant créé par Admin ou Bureau. ## Navigation @@ -150,7 +150,7 @@ Saisir une ou plusieurs adresses du client, rattachées à un ou plusieurs sites | **Prospect** | `` | Non | RG-1.06 — masque Adresse de livraison + Facturation si coché | | **Adresse de livraison** | `` | Non | RG-1.07 — masque Prospect si coché | | **Facturation** | `` | Non | RG-1.08 — masque Prospect si coché ; affiche le champ Email (RG-1.11) | -| **Catégorie** | `` (multi) | Oui | Liste des `Category` | +| **Catégorie** | `` (multi) | Oui | Liste des `Category` de **type SECTEUR + AUTRE** uniquement (cf. décision Q5 — DISTRIBUTEUR et COURTIER qualifient une relation entre clients, pas un lieu) | | **Pays** | `` | Oui | Préremplie « France » | | **Code postal** | `` (masque numérique) | Oui | RG-1.09 — déclenche autocomplete ville via BAN | | **Ville** | `` | Oui | RG-1.09 — alimentée par api-adresse.data.gouv.fr suivant le CP | @@ -270,7 +270,7 @@ Le composant `Code postal` + `Ville` + `Adresse` est branché sur **api-adresse. |---|---|---| | 1 | Catégorie en multi-select non clarifiée (1 ou n par client) | **M2M `client_category`** validée. CategoryType seedé avec `DISTRIBUTEUR`, `COURTIER`, `SECTEUR`, `AUTRE` (HP-3 du M0 levé). | | 2 | Distributeur / Courtier : liste de quoi ? | **Auto-référence Client** via 2 FK nullables `distributor_id` et `broker_id` (cf. RG-1.03). Une seule des deux est remplie à la fois. | -| 3 | Onglet « Comptabilité » : qui édite ? | **Admin uniquement au M1.** Compta lecture seule (décision validée par Tristan 28/05). Bureau / Commerciale ne voient pas l'onglet. | +| 3 | Onglet « Comptabilité » : qui édite ? | **Admin et Compta** peuvent éditer l'onglet Comptabilité (`commercial.clients.accounting.manage`). Bureau / Commerciale ne voient pas l'onglet. Compta ne peut pas créer un client (pas de `manage` global), mais peut éditer la partie comptable d'un client existant. | | 4 | Workflow par onglet | **Sauvegarde incrémentale**. POST formulaire principal crée le `Client` (status implicite « actif »). Chaque onglet validé = PATCH partiel par groupe de sérialisation dédié. Pas d'état « draft ». | | 5 | Onglets « À venir » | **Placeholders blancs** (frames vides, pas de message). Ré-activables sans rebuild quand les modules associés arriveront. | | 6 | Archive vs soft delete | **Flag `is_archived` séparé de `deleted_at`**. Archive ≠ delete : un client archivé est masqué par défaut mais reste en BDD éditable (Admin seul). Filtres UI distincts. Soft delete = HP M2. |