Files
Starseed/docs/specs/M1-clients/spec-front.md
T

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
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
Admin
Bureau
Compta
Commerciale
Usine
./spec-back.md
statut date canal valide_par resume trace_archivee
validee 2026-05-22 ecrit Matthieu (CP MALIO) — validation implicite, périmètre projet Module 1 — Répertoire clients. Page d'entrée Commercial. Datatable + 3 écrans (Ajouter / Consulter / Modifier). Création par onglets : Information / Contact / Adresse / Comptabilité (Transport, Statistiques, Rapports, Échanges = placeholders blancs). uploads/4a1b026f-M1-reportoire-clients.docx (V0 d'origine .docx)
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 dans spec-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 .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).

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 serveur companyName ASC par 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.
Email <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)
Email <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. flag is_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 manage restent 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 » (composant MalioDate non couvert)
  • Modal de confirmation : composant à confirmer côté équipe front (probablement <MalioModal> ou un wrapper à créer dans frontend/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)
Email 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.