18 KiB
module, nom, ecran, owner_spec, backup_spec, version, date_redaction, maquette_figma, regles_metier, roles, lien_spec_back, client_validation_1, lesstime_taskgroup_id, lesstime_project_id, statut_global
| module | nom | ecran | owner_spec | backup_spec | version | date_redaction | maquette_figma | regles_metier | roles | lien_spec_back | client_validation_1 | lesstime_taskgroup_id | lesstime_project_id | statut_global | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| M1 | Répertoire clients | repertoire-clients | Matthieu | Tristan | V0 | 2026-05-28 | https://www.figma.com/design/jRYgT0T9c03VsEbjGhCwwS/Composants---Design-System?node-id=1132-31898 |
|
|
./spec-back.md |
|
23 | 6 | en_dev |
Module 1 — Répertoire clients (V0 front)
Origine : spec front V0 livrée le 22/05/2026 (
M1-reportoire-clients.docx). Restitution Markdown pour intégration au workflow MALIO. Le contenu original n'est pas modifié — toute précision et toute décision (en particulier côté back) vit dansspec-back.md.
But
Permettre aux utilisateurs Starseed (selon rôle) de gérer le répertoire des clients de l'organisation : consultation, création, modification, archivage. Cette page est la porte d'entrée du module Commercial.
Accès
- Depuis : menu principal → section Commercial → entrée « Répertoire clients »
- Rôles autorisés :
| Rôle | Consultation | Création / Modification | Archivage |
|---|---|---|---|
| Admin | ✅ Tout | ✅ Tout | ✅ |
| Bureau | ✅ Tout | ✅ Tout sauf onglet Comptabilité | ❌ |
| Compta | ✅ Tout | ❌ (lecture seule) | ❌ |
| 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
.docxindiquait « 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 despec-back.md).
Navigation
L'écran est la page d'entrée du module Commercial. Titre : « Répertoire clients ».
- Affichage principal : un datatable listant tous les clients actifs de l'organisation (les clients archivés sont masqués par défaut — filtre UI dédié pour les voir).
- Clic sur une ligne → bascule sur l'écran Consultation client (page dédiée, pas un drawer — cf. maquette Figma).
- Bouton « + Ajouter » (en haut à droite) → bascule sur l'écran Ajouter un client.
- Bouton « Exporter » (en haut à droite) → télécharge un fichier XLSX des clients affichés (cf. filtre actif). Format détaillé dans
spec-back.md§ Export.
Datatable du Répertoire
Composant : <MalioDataTable>. Colonnes (à raffiner avec Tristan en revue maquette) :
| Colonne | Source | Tri |
|---|---|---|
| Nom entreprise | client.companyName |
ASC par défaut |
| Contact principal | firstName + lastName |
Oui |
| Téléphone principal | phonePrimary (formaté XX XX XX XX XX) |
Non |
| Email principal | email |
Oui |
| Catégories | liste des codes catégories séparés par , |
Non |
| Site(s) | sites rattachés à au moins une adresse (badges colorés) | Non |
Filtre archivés : toggle UI en haut du datatable. Désactivé par défaut. État local (pas dans l'URL — cf. règle ABSOLUE Starseed n°6).
Pagination : front via
<MalioDataTable>(volumétrie cible faible — quelques centaines). Tri serveurcompanyName ASCpar défaut.
Écran « Ajouter un client »
Création par onglets successifs avec validation incrémentale : pour pouvoir passer à l'onglet suivant, il faut avoir validé l'onglet en cours. Une fois un onglet validé, on passe automatiquement au suivant, et les champs de l'onglet validé passent en lecture seule + bouton « Valider » désactivé (disabled).
Formulaire principal (pré-onglets)
C'est le 1er bloc à remplir. Sans validation de ce formulaire, les onglets ne sont pas accessibles.
| Champ | Type composant | Obligatoire | Règle |
|---|---|---|---|
| Nom du client (Entreprise) | <MalioInputText> |
Oui | RG-1.18 (normalisation UPPERCASE serveur) |
| Nom du contact principal | <MalioInputText> |
Conditionnel | RG-1.01 + RG-1.19 (Capitalize) |
| Prénom du contact principal | <MalioInputText> |
Conditionnel | RG-1.01 + RG-1.19 (Capitalize) |
| Catégorie | <MalioSelectCheckbox> (multi) |
Oui | Liste des Category de l'API ; M2M Client ↔ Category |
| Téléphone principal | <MalioInputText> (masque tel) |
Oui | RG-1.02 + RG-1.20 (format XX XX XX XX XX) |
| Téléphone secondaire | <MalioInputText> (masque tel) |
Non | Apparaît au clic sur le bouton + (RG-1.02). Max 2 — bouton + disparaît une fois rempli. |
<MalioInputText> type email |
Oui | RG-1.21 (lowercase) | |
| Distributeur / Courtier | <MalioSelect> |
Non | Valeurs : Dépend du distributeur / Dépend du courtier / Aucun. RG-1.03 conditionne les 2 champs suivants. |
| Nom du distributeur | <MalioSelect> |
Conditionnel | Visible si « Dépend du distributeur ». Liste = clients ayant ≥ 1 catégorie de type DISTRIBUTEUR. RG-1.03. |
| Nom du courtier | <MalioSelect> |
Conditionnel | Visible si « Dépend du courtier ». Liste = clients ayant ≥ 1 catégorie de type COURTIER. RG-1.03. |
| Prestation de triage | <MalioCheckbox> |
Non | — |
Action : « Valider » (<MalioButton>) → POST /api/clients (spec-back.md § 4.3). Si succès, on passe automatiquement à l'onglet « Information ».
Onglet « Information »
Saisir les informations de l'entreprise.
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| Description | <MalioInputTextArea> |
Conditionnel | RG-1.04 (obligatoire pour rôle Commerciale) |
| Concurrents | <MalioInputText> |
Conditionnel | RG-1.04 |
| Date de création (de l'entreprise) | <input type="date"> (exception Malio — pas de composant date couvert) |
Conditionnel | RG-1.04 |
| Nombre de salariés | <MalioInputNumber> |
Conditionnel | RG-1.04 |
| CA € | <MalioInputAmount> |
Conditionnel | RG-1.04 |
| Dirigeant | <MalioInputText> |
Conditionnel | RG-1.04 |
| Résultat € | <MalioInputAmount> |
Conditionnel | RG-1.04 |
Action : « Valider » → PATCH partiel /api/clients/{id} (groupe client:write:information).
Onglet « Contact »
Saisir un ou plusieurs contacts associés au client. Le 1er bloc est pré-rempli depuis les champs du formulaire principal (Nom, Prénom, Téléphone, Email — édition autorisée).
Bloc Contact :
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| Nom | <MalioInputText> |
Conditionnel | RG-1.05 + RG-1.19 (Capitalize) |
| Prénom | <MalioInputText> |
Conditionnel | RG-1.05 + RG-1.19 (Capitalize) |
| Fonction | <MalioInputText> |
Non | — |
| Téléphone (x1, +1 possible) | <MalioInputText> |
Non | RG-1.20 (format) |
<MalioInputText> type email |
Non | RG-1.21 (lowercase) |
RG-1.14 (renforcement validée par Tristan le 28/05) : au moins 1 bloc Contact valide (au moins Nom OU Prénom rempli) est obligatoire pour valider l'onglet. Donc l'onglet Contact ne peut pas être finalisé vide.
Actions :
- « + Nouveau contact » : ajoute un bloc. Bouton désactivé tant que le bloc précédent n'a pas Prénom OU Nom rempli (RG-1.05).
- « Supprimer » (icône) sur un bloc : modal de confirmation (
<MalioButton>Annuler / Confirmer). Si Oui → suppression du bloc. - « Valider » → PATCH
/api/clients/{id}/contacts(création/mise à jour de la collection).
Onglet « Adresse »
Saisir une ou plusieurs adresses du client, rattachées à un ou plusieurs sites Starseed (Châtellerault 86 / Saint-Jean 17 / Pommevic 82) et à des contacts.
Bloc Adresse :
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| 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 |
| 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 |
| Adresse | <MalioInputText> (saisie assistée) |
Oui | RG-1.09 — autocomplete BAN |
| Adresse complémentaire | <MalioInputText> |
Non | — |
| Sites Starseed | <MalioSelectCheckbox> (multi-checkbox 86 / 17 / 82) |
Oui | RG-1.10 — ≥ 1 site obligatoire |
| Contact(s) rattaché(s) | <MalioSelectCheckbox> (multi) |
Non | Liste = blocs Contact saisis dans l'onglet Contact |
| Email (facturation) | <MalioInputText> type email |
Conditionnel | RG-1.11 — visible/obligatoire uniquement si « Facturation » coché |
Actions :
- « + Nouvelle Adresse » : ajoute un bloc identique.
- « Supprimer » : modal de confirmation puis suppression.
- « Valider » → PATCH
/api/clients/{id}/addresses.
Onglet « Transport »
🚧 Placeholder blanc au M1. Frame vide. Aucun champ. Aucun bouton de validation. L'utilisateur passe automatiquement à l'onglet suivant. Pas de mention « En cours » — c'est juste blanc (décision Tristan 28/05).
Onglet « Comptabilité »
⚠ Accessible uniquement aux rôles avec commercial.clients.accounting.manage (Admin seul au M1). Bureau et Commerciale ne voient pas l'onglet. Compta voit l'onglet en lecture seule (cf. décision Compta lecture seule).
Champs comptables :
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| SIREN | <MalioInputText> (masque 9 chiffres) |
Oui | RG-1.15 (unicité) |
| Numéro de compte | <MalioInputText> |
Oui | — |
| Mode de TVA | <MalioSelect> |
Oui | Liste depuis /api/tva_modes |
| N° de TVA | <MalioInputText> |
Oui | — |
| Délai de règlement | <MalioSelect> |
Oui | Liste depuis /api/payment_delays |
| Type de règlement | <MalioSelect> |
Oui | Liste depuis /api/payment_types |
| Banque | <MalioSelect> |
Conditionnel | RG-1.12 — visible et obligatoire si Type de règlement = VIREMENT. Liste depuis /api/banks. |
Bloc RIB (0..n blocs, présence obligatoire conditionnée par RG-1.13) :
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| Libellé | <MalioInputText> |
Oui (si LCR) | RG-1.13 |
| BIC | <MalioInputText> |
Oui (si LCR) | RG-1.13 — #[AuditIgnore] (champ sensible) |
| IBAN | <MalioInputText> |
Oui (si LCR) | RG-1.13 — #[AuditIgnore] (champ sensible) |
Actions :
- « + RIB » : ajoute un bloc.
- « Supprimer » (icône) : modal de confirmation.
- « Valider » → PATCH
/api/clients/{id}/accounting.
Onglets « Statistiques » / « Rapports » / « Échanges »
🚧 Placeholders blancs au M1. Mêmes règles que Transport (frames vides, pas de validation).
Écran « Consultation client »
Tous les champs en lecture seule. Layout identique à l'écran Ajouter mais sans bouton « Valider », sans bouton + pour ajouter des blocs Contact / Adresse / RIB.
- Flèche retour (à gauche) → revient au Répertoire.
- Bouton « Modifier » (à droite, visible si l'utilisateur a la permission
commercial.clients.manage) → bascule sur l'écran Modification. - Bouton « Archiver » (à droite, visible uniquement pour Admin via permission
commercial.clients.archive) → ouvre une modal de confirmation, puis PATCH/api/clients/{id}{ "isArchived": true }. Le client passe en archivé (cf. flagis_archived).
Le client archivé peut être restauré (
isArchived: false) — bouton « Restaurer » remplace « Archiver » dans la consultation d'un archivé. Décision validée Tristan 28/05.
Onglets affichés en consultation
Mêmes onglets qu'en création, plus les 4 placeholders blancs. L'utilisateur navigue librement entre les onglets (pas de séquence forcée en consultation).
Écran « Modification client »
Comportement identique à l'écran Ajouter sauf :
- Pas de formulaire principal (les champs principaux sont édités via les onglets correspondants).
- Les champs sont pré-remplis avec les valeurs actuelles.
- Validation par onglet : on peut modifier UN onglet sans toucher aux autres (PATCH partiel).
- Les onglets pour lesquels l'utilisateur n'a pas la permission
managerestent en lecture seule (pas de bouton Valider, pas d'icône suppression de bloc). - Les onglets placeholders restent inaccessibles à l'édition (blancs).
Composants UI à utiliser (@malio/layer-ui)
- Datatable :
<MalioDataTable>(Répertoire) - Input texte :
<MalioInputText> - Input numérique :
<MalioInputNumber> - Input montant :
<MalioInputAmount>(CA, Résultat) - TextArea :
<MalioInputTextArea>(Description) - Select simple :
<MalioSelect>(Pays, Ville, distributeur/courtier, refs comptables) - Select multi (cases à cocher) :
<MalioSelectCheckbox>(Catégorie, Sites, Contacts rattachés) - Checkbox :
<MalioCheckbox>(Prospect, Adresse livraison, Facturation, Prestation de triage) - Bouton :
<MalioButton>,<MalioButtonIcon> - Toasts : standards via
useApi()
Exceptions autorisées (à commenter // TODO migrer quand Malio couvre) :
<input type="date">pour « Date de création » (composantMalioDatenon couvert)- Modal de confirmation : composant à confirmer côté équipe front (probablement
<MalioModal>ou un wrapper à créer dansfrontend/shared/)
Règles de formatage et normalisation
Le serveur normalise systématiquement (cf. RG-1.18 à RG-1.21 dans spec-back.md) :
| Champ | Normalisation serveur | Affichage front |
|---|---|---|
Nom entreprise (companyName) |
UPPERCASE intégral | UPPERCASE |
| Nom + Prénom contact | Capitalize (1ère lettre majuscule + reste minuscule) | identique |
Téléphone (phonePrimary, phoneSecondary, contact phones) |
Chiffres uniquement en BDD | Formaté XX XX XX XX XX à l'affichage (filter Vue) |
| lowercase intégral | identique |
Le front ne fait pas la normalisation — il envoie la valeur saisie, le serveur normalise puis renvoie la valeur normalisée. L'UI affiche immédiatement la valeur normalisée renvoyée par l'API. Cohérent avec le pattern
useApi().
API adresse postale
Le composant Code postal + Ville + Adresse est branché sur api-adresse.data.gouv.fr (Base Adresse Nationale, gratuite, française).
- Composable dédié
useAddressAutocomplete()(à créer en M1). - Appel HTTP direct depuis le front (CORS OK), pas de proxy back.
- Pattern : à la saisie du code postal (5 chiffres), GET
https://api-adresse.data.gouv.fr/search/?q={cp}&type=municipality→ alimente le select Ville. Sur saisie d'adresse :?q={addr}&postcode={cp}&type=housenumber→ suggestions adresse. - Cas dégradé : si l'API ne répond pas (offline, timeout), le champ Ville devient un
<MalioInputText>libre éditable + toast d'avertissement. Validation serveur acceptera la saisie libre.
Points laissés ouverts par la V0 (résolus côté back)
| # | Zone d'ombre V0 | Résolution (cf. spec-back.md) |
|---|---|---|
| 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. |
| 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. |
| 7 | Unicité métier | SIREN, Nom entreprise, Email principal — tous uniques parmi non-archivés. Index partiels Postgres. Tentative de doublon → 409 Conflict. |
| 8 | Téléphones (max 2) | 2 colonnes plates phone_primary + phone_secondary. Pas de table séparée. |
| 9 | API code postal | api-adresse.data.gouv.fr (BAN). Appel direct front via composable dédié. Cas dégradé : saisie libre + toast. |
| 10 | Référentiels comptables | 4 entités CRUD-ables (TvaMode, PaymentDelay, PaymentType, Bank) seedées au M1, CRUD admin futur (HP-M2). |
| 11 | Format de l'export | XLSX uniquement au M1. CSV à étudier en HP. |
📦 Tickets Lesstime générés
TaskGroup Lesstime : à créer — M1 — Répertoire clients (projet ERP / Starseed, projectId=6).
Détail complet, table des tickets et action manuelle dans Lesstime → voir
spec-back.md § Tickets Lesstime générés.