docs(commercial) : address Tristan and Matthieu MR review feedback on M1 specs
- Compta peut éditer l'onglet Comptabilité (accounting.manage) — aligné docx - Retirer #[AuditIgnore] sur ClientRib.iban/bic (admin-only suffit) - Ajouter RG-1.28 (PATCH strict mix groupes -> 403) - Ajouter RG-1.29 (filtre catégorie ClientAddress = SECTEUR + AUTRE) - HP-M2-10 / HP-M2-12 supprimés (Compta édition nominal)
This commit is contained in:
@@ -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 `<MalioSelectCheckbox>` 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.
|
||||
|
||||
@@ -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** | `<MalioCheckbox>` | Non | RG-1.06 — masque Adresse de livraison + Facturation si coché |
|
||||
| **Adresse de livraison** | `<MalioCheckbox>` | Non | RG-1.07 — masque Prospect si coché |
|
||||
| **Facturation** | `<MalioCheckbox>` | Non | RG-1.08 — masque Prospect si coché ; affiche le champ Email (RG-1.11) |
|
||||
| **Catégorie** | `<MalioSelectCheckbox>` (multi) | Oui | Liste des `Category` |
|
||||
| **Catégorie** | `<MalioSelectCheckbox>` (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** | `<MalioSelect>` | Oui | Préremplie « France » |
|
||||
| **Code postal** | `<MalioInputText>` (masque numérique) | Oui | RG-1.09 — déclenche autocomplete ville via BAN |
|
||||
| **Ville** | `<MalioSelect>` | 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. |
|
||||
|
||||
Reference in New Issue
Block a user