Acte la décision refonte-contact dans les specs : le contact principal inline (firstName/lastName/phonePrimary/phoneSecondary/email) est retiré des entités tiers (Client, Supplier). Les contacts vivent uniquement dans ClientContact / SupplierContact (onglet Contacts). Garantie « >=1 contact nommé » préservée par RG-1.05/1.14 (M1) et RG-2.04/2.13 (M2). - M1 (spec-back/spec-front/cahier) : modèle Client sans contact inline ; RG-1.01/1.02 supprimées ; D1 (recherche) / D2 (export) décrites ; version V1. - M2 (spec-back/spec-front) : FICHIERS NOUVEAUX (non versionnés sur develop), introduits déjà corrigés (Supplier sans contact inline, RG-2.01/2.02 supprimées) ; version V0.2. - docs/specs/M1-clients/refonte-contact/ : décision (README) + tickets (M1 back/front/specs, M2 specs) + prompts + amendement des tickets M2. Lesstime : tâches #103 (M1 back), #104 (M1 front), #105 (M1 specs), #106 (M2 specs) ; tickets M2 #85-#97 amendés. --------- Co-authored-by: Matthieu <contact@malio.fr> Reviewed-on: #54 Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
22 KiB
module, nom, ecran, owner_spec, backup_spec, version, date_redaction, maquette_figma, regles_metier, roles, lien_spec_back, client_validation_1, client_validation_2, 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 | client_validation_2 | lesstime_taskgroup_id | lesstime_project_id | statut_global | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| M2 | Répertoire fournisseurs | repertoire-fournisseurs | Matthieu | Tristan | V0.2 | 2026-06-02 | https://www.figma.com/design/jRYgT0T9c03VsEbjGhCwwS/Composants---Design-System?node-id=1132-36987&p=f&m=dev |
|
|
./spec-back.md |
|
|
26 | 6 | a_dev |
Module 2 — Répertoire fournisseurs (V0.1 front)
Origine : spec front livrée le 22/05/2026 (V0), amendée le 01/06/2026 (V0.1) —
M2-reportoire-fournisseurs.docx. Restitution Markdown pour intégration au workflow MALIO. Le contenu fonctionnel original n'est pas modifié ; toute décision technique (back) vit dansspec-back.md. Le M2 réutilise massivement le pattern et les composants posés au M1 clients.
But
Permettre aux utilisateurs Starseed (selon rôle) de gérer le répertoire des fournisseurs de l'organisation : consultation, création, modification, archivage. C'est la deuxième porte d'entrée du module Commercial (aux côtés des Clients).
Accès
- Depuis : menu principal → section Commercial → entrée « Répertoire fournisseurs » (route
/suppliers). - Rôles autorisés :
| Rôle | Consultation | Création / Modification | Archivage |
|---|---|---|---|
| Admin | ✅ Tout | ✅ Tout | ✅ |
| Bureau | ✅ Tout | ✅ Tout sauf onglet Comptabilité | ❌ |
| Compta | ✅ Tout | ✅ Onglet Comptabilité uniquement | ❌ |
| Commerciale | ✅ Tout sauf Comptabilité | ✅ Tout sauf Comptabilité | ❌ |
| Usine | ❌ (pas d'accès) | ❌ | ❌ |
Note
: RBAC identique au M1, transposée sur
commercial.suppliers.*. Compta édite uniquement l'onglet Comptabilité (SIREN / N° compte / TVA / Délai / Type de règlement / Banque / RIBs) d'un fournisseur existant ; Compta ne peut pas créer un fournisseur. L'archivage est réservé à Admin (cf. tableau du docx).
Navigation
Page d'entrée du module Commercial (route /suppliers). Titre : « Répertoire fournisseurs ».
- Affichage principal : un datatable listant tous les fournisseurs actifs (les archivés sont masqués par défaut — toggle UI dédié).
- Clic sur une ligne → écran Consultation fournisseur (page dédiée).
- Bouton « + Ajouter » (haut droite) → écran Ajouter un fournisseur.
- Bouton « Filtrer » (haut droite, à côté de « + Ajouter ») → ouvre le panneau de filtres (cf. ci-dessous). Un badge/compteur indique le nombre de filtres actifs ; un bouton « Réinitialiser » les vide.
- Bouton « Exporter » (haut droite) → télécharge un XLSX des fournisseurs affichés (cf. filtres actifs). Format dans
spec-back.md § 4.6.
Panneau de filtres (bouton « Filtrer »)
Ouvre un drawer/popover (composant à confirmer côté équipe front — réutiliser le pattern M1 s'il existe). Filtres proposés, branchés sur les query params de GET /api/suppliers (cf. spec-back.md § 4.1) :
| Filtre | Composant | Query param back |
|---|---|---|
Recherche (nom entreprise / contact / email — recherche contact via supplier_contact, décision D1) |
<MalioInputText> |
?search= |
| Catégorie | <MalioSelectCheckbox> (multi, type FOURNISSEUR) |
?categoryCode= |
| Site | <MalioSelectCheckbox> (86 / 17 / 82) |
?siteId= |
| Inclure les archivés | <MalioCheckbox> |
?includeArchived=true |
- À l'application des filtres →
setFilters(...)deusePaginatedList(retombe en page 1), qui relanceGET /api/suppliersavec les params. - État 100 % local (jamais dans l'URL — règle ABSOLUE n°6). Le bouton « Filtrer » + son panneau remplacent/regroupent l'ancien toggle « archivés » isolé.
Datatable du Répertoire
Composant : <MalioDataTable> branché sur usePaginatedList<Supplier>({ url: '/suppliers' }) (règle frontend obligatoire — pagination Hydra, état 100 % local). Colonnes conformes à la maquette Figma (4 colonnes) :
| Colonne | Source | Tri |
|---|---|---|
| Nom | supplier.companyName |
ASC par défaut |
| Catégories | supplier.categories[].name (embarquées en liste — cohérence M1/ERP-62 ; libellé = name, pas label) |
Non |
| Site | supplier.sites[].name (agrégat des adresses via getSites() ; Site n'a pas de code) |
Non |
| Dernière activité | supplier.updatedAt (format JJ-MM-AAAA) — exposé dans supplier:read |
Oui |
Clic sur une ligne (texte en bleu lien) → écran Consultation. Filtres : regroupés dans le panneau du bouton « Filtrer » (cf. section précédente), dont l'inclusion des archivés (désactivée par défaut). État local (jamais dans l'URL — règle ABSOLUE n°6). Pagination :
<MalioDataTable>+usePaginatedList, options standard Starseed 10 / 25 / 50 (défaut 10) — on n'applique pas le « Ligne : 20 » de la maquette (décision Matthieu : on reste sur le standard). Tri serveurcompanyName ASCpar défaut.
Écran « Ajouter un fournisseur »
Création par onglets successifs avec validation incrémentale : pour passer à l'onglet suivant, il faut avoir validé l'onglet en cours. Une fois un onglet validé, on passe automatiquement au suivant ; les champs validés passent en lecture seule + bouton « Valider » désactivé (disabled). Cf. spec-back.md § 2.10 (PATCH partiels par groupe de sérialisation).
Barre d'onglets en création (5 onglets, conforme maquette) : Information · Contacts · Adresses · Transport · Comptabilité. L'onglet Information est actif par défaut juste après validation du formulaire principal. Les onglets Statistiques, Rapports et Échanges n'apparaissent PAS dans le flux de création — ils ne sont présents qu'en Consultation / Modification.
Formulaire principal (pré-onglets)
1er bloc à remplir. Sans validation, les onglets ne sont pas accessibles. Une fois validé → POST /api/suppliers, puis bascule sur l'onglet Information ; les champs passent en readonly.
V0.2 — refonte-contact : le contact principal (Nom / Prénom / Téléphone / Téléphone 2 / Email) a été retiré du formulaire principal. Les coordonnées se saisissent dans l'onglet Contacts (RG-2.04 / RG-2.13). Le formulaire principal ne contient plus que Entreprise + Catégorie.
| Champ | Type composant | Obligatoire | Règle |
|---|---|---|---|
| Nom du fournisseur (Entreprise) | <MalioInputText> |
Oui | RG-2.12 (UPPERCASE serveur) |
| Catégorie | <MalioSelectCheckbox> (multi) |
Oui | Category de type FOURNISSEUR via GET /api/categories?typeCode=FOURNISSEUR (RG-2.10). Libellé affiché = category.name. ⚠️ Le type + le filtre ?typeCode= sont à créer côté back (n'existent pas en prod — cf. spec-back § 2.4). |
Action : « Valider » (<MalioButton>) → POST /api/suppliers (spec-back.md § 4.3). Succès → onglet « Information ».
Onglet « Information »
Saisir les informations du fournisseur.
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| Description | <MalioInputTextArea> |
Conditionnel | RG-2.03 (obligatoire rôle Commerciale) |
| Concurrent | <MalioInputText> |
Conditionnel | RG-2.03 |
| Date création (entreprise) | <input type="date"> (exception Malio — // TODO migrer) |
Conditionnel | RG-2.03 |
| Nombre de salariés | <MalioInputNumber> |
Conditionnel | RG-2.03 |
| CA € | <MalioInputAmount> |
Conditionnel | RG-2.03 |
| Dirigeant | <MalioInputText> |
Conditionnel | RG-2.03 |
| Résultat € | <MalioInputAmount> |
Conditionnel | RG-2.03 |
| Volume Prévisionnel | <MalioInputNumber> |
Conditionnel | RG-2.03 (champ spécifique fournisseur) |
Disposition maquette : 3 colonnes — ligne 1 (Description / Concurrent / Date création), ligne 2 (Nombre de salariés / CA / Dirigeant), ligne 3 (Résultat / Volume Prévisionnel).
Action : « Valider » → PATCH /api/suppliers/{id} (groupe supplier:write:information).
Onglet « Contact »
Saisir un ou plusieurs contacts. (V0.2 — refonte-contact : plus de pré-remplissage depuis le formulaire principal ; les coordonnées du contact se saisissent directement ici.) Au moins un bloc Contact valide est requis (RG-2.13).
Bloc Contact :
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| Nom | <MalioInputText> |
Conditionnel | RG-2.04 + RG-2.12 (Capitalize) |
| Prénom | <MalioInputText> |
Conditionnel | RG-2.04 + RG-2.12 (Capitalize) |
| Fonction | <MalioInputText> |
Non | — |
| Téléphone (x1, +1 possible) | <MalioInputText> |
Non | RG-2.12 (format) |
<MalioInputText> type email |
Non | RG-2.12 (lowercase) |
RG-2.04 / RG-2.13 : au moins 1 bloc Contact valide (Nom OU Prénom rempli) pour valider l'onglet — l'onglet Contact ne peut pas être finalisé vide.
Actions :
- « + Nouveau contact » : ajoute un bloc. Désactivé tant que le bloc précédent n'a pas Prénom OU Nom (RG-2.04).
- « Supprimer » (icône) : modal de confirmation, puis suppression du bloc.
- « Valider » → PATCH
/api/suppliers/{id}/contacts.
Onglet « Adresse »
Saisir une ou plusieurs adresses, rattachées à un ou plusieurs sites (86 / 17 / 82) et à des contacts.
Bloc Adresse :
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| Type d'adresse | <MalioRadioButton> — Prospect / Départ / Rendu |
Oui | RG-2.09 (exclusif, enum PROSPECT/DEPART/RENDU) |
| Pays | <MalioSelect> (saisie assistée — préremplie « France ») |
Oui | — |
| Code postal | <MalioInputText> (saisie assistée) |
Oui | RG-2.05 — déclenche autocomplete ville (BAN) |
| Ville | <MalioSelect> (saisie assistée) |
Oui | RG-2.05 — alimentée par api-adresse.data.gouv.fr suivant le CP |
| Adresse | <MalioInputText> (saisie assistée) |
Oui | RG-2.05 — autocomplete BAN |
| Adresse complémentaire | <MalioInputText> |
Non | — |
| Sélecteur de site | <MalioSelectCheckbox> (86 / 17 / 82) |
Oui | RG-2.06 — ≥ 1 site. Les 3 cases = les 3 Site fixes ; libellés « 86/17/82 » = préfixe du postalCode (86100/17400/82400), pas un Site.code (qui n'existe pas). La sélection stocke des IDs de Site (M2M). |
| Catégories | <MalioSelectCheckbox> (multi) |
Oui | Catégories de type FOURNISSEUR (RG-2.10), liées aux catégories du fournisseur |
| Contact | <MalioSelectCheckbox> (multi) |
Non | Liste = blocs Contact saisis dans l'onglet Contact |
| Benne(s) | <MalioInputNumber> (stepper −/+ , défaut 0) |
Non | Champ spécifique fournisseur |
| Prestation de triage | <MalioCheckbox> |
Non | Champ spécifique fournisseur (porté par l'adresse — colonne back triage_provider) |
Disposition maquette par bloc : ligne 1 = radio (Prospect / Départ / Rendu) + Pays + Code postal ; ligne 2 = Ville + Adresse + Adresse complémentaire ; ligne 3 = sites (86 / 17 / 82) + Catégories + Contact ; ligne 4 = Benne(s) + Prestation de triage. Icône corbeille en haut à droite de chaque bloc pour le supprimer.
Actions :
- « + Nouvelle Adresse » : ajoute un bloc identique au premier.
- « Supprimer » : modal de confirmation puis suppression.
- « Valider » → PATCH
/api/suppliers/{id}/addresses.
Onglet « Transport »
🚧 Onglet placeholder minimal au M2. Conforme à la maquette : la frame est vide (aucun champ, aucun bouton de validation, aucune API back). L'onglet reste navigable. Un libellé discret « À venir » est toléré mais non requis (la maquette ne l'affiche pas). Cet onglet fait partie de la barre de création (entre Adresses et Comptabilité).
Onglet « Comptabilité »
⚠ Accessible aux rôles avec commercial.suppliers.accounting.view (Admin + Compta au M2). Bureau et Commerciale ne voient pas l'onglet. Compta peut éditer cet onglet (accounting.manage). Compta ne peut pas créer un fournisseur (pas de manage global).
Champs comptables :
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| SIREN | <MalioInputText> (masque 9 chiffres) |
Oui | 9 chiffres. Pas d'unicité (cf. § 2.6) |
| Numéro de compte | <MalioInputText> |
Oui | — |
| Mode de TVA | <MalioSelect> |
Oui | Liste depuis /api/tva_modes (référentiel M1) |
| 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-2.07 — visible et obligatoire si Type de règlement = VIREMENT. Liste depuis /api/banks (SG / CIC / CA). |
Bloc RIB (0..n, présence obligatoire conditionnée par RG-2.08) :
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| Libellé | <MalioInputText> |
Oui (si LCR) | RG-2.08 |
| BIC | <MalioInputText> |
Oui (si LCR) | RG-2.08 |
| IBAN | <MalioInputText> |
Oui (si LCR) | RG-2.08 |
Actions :
- « + RIB » : ajoute un bloc.
- « Supprimer » (icône) : modal de confirmation.
- « Valider » → PATCH
/api/suppliers/{id}(groupesupplier:write:accounting) + sous-ressource RIBs.
Onglets « Statistiques » / « Rapports » / « Échanges »
🚧 Placeholders minimaux au M2 — uniquement en Consultation / Modification (ils n'apparaissent pas dans le flux de création, cf. maquette). Frames vides, pas de validation, pas d'API.
Écran « Consultation fournisseur »
Tous les champs en lecture seule. Layout identique à l'écran Ajouter mais sans bouton « Valider », sans + pour ajouter des blocs.
- Flèche retour (gauche) → revient au Répertoire.
- Bouton « Modifier » (droite, visible si
commercial.suppliers.manage) → écran Modification. - Bouton « Archiver » (droite, visible uniquement Admin via
commercial.suppliers.archive) → modal de confirmation, puis PATCH/api/suppliers/{id}{ "isArchived": true }.
Un fournisseur archivé peut être restauré (
isArchived: false) — bouton « Restaurer » remplace « Archiver » dans la consultation d'un archivé.
Onglets affichés en consultation
Information / Contacts / Adresses / Transport / Statistiques / Rapports / Échanges / Comptabilité (les 4 derniers métiers en placeholder « À venir », Comptabilité selon permission). L'utilisateur navigue librement entre les onglets (pas de séquence forcée en consultation).
Écran « Modification fournisseur »
Comportement identique à l'écran Ajouter sauf :
- Pas de formulaire principal réaffiché (champs principaux é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(ouaccounting.manage) restent en lecture seule (pas de bouton Valider, pas d'icône suppression). - Les onglets placeholders « À venir » restent non éditables.
Composants UI à utiliser (@malio/layer-ui)
- Datatable :
<MalioDataTable>(+usePaginatedList) - Input texte :
<MalioInputText> - Input numérique :
<MalioInputNumber>(Nombre de salariés, Volume prévisionnel, Bennes) - Input montant :
<MalioInputAmount>(CA, Résultat) - TextArea :
<MalioInputTextArea>(Description) - Select simple :
<MalioSelect>(Pays, Ville, référentiels comptables) - Select multi (cases à cocher) :
<MalioSelectCheckbox>(Catégorie, Sites, Contacts rattachés) - Radio :
<MalioRadioButton>(Type d'adresse Prospect / Départ / Rendu — RG-2.09) - Checkbox :
<MalioCheckbox>(Prestataire de triage) - Bouton :
<MalioButton>,<MalioButtonIcon> - Toasts : standards via
useApi()
Exceptions autorisées (commenter // TODO migrer quand Malio couvre) :
<input type="date">pour « Date Création » (MalioDatenon couvert).- Modal de confirmation :
<MalioModal>ou wrapper partagé dansfrontend/shared/(réutiliser celui du M1 si présent).
Composables & appels API
usePaginatedList<Supplier>({ url: '/suppliers' })— liste paginée (obligatoire, règle frontend). La liste consommecategories[](libellé =name) etsites[](libellé =name, pas decode) embarqués +updatedAt(cohérence M1/ERP-62, cf.spec-back.md § 2.12 / § 4.0). Côté back, fetch-joins anti-N+1.useSupplier(id)— charge le détail viaGET /api/suppliers/{id}, qui embarquecontacts,addresses(avecsites/categories/contactsimbriqués) et, si permission,ribs+ scalaires compta. Les écrans Consultation et Modification se peuplent depuis cette seule réponse (RETEX M1 §2 : embed borné, pas de N+1 d'appels). DoD avant intégration : vérifier que le JSON réel contient bien ces blocs (cf.spec-back.md § 4.0.bis).useSupplierForm()— workflow par onglet (POST principal + PATCH partiels par groupe), miroir deuseClientForm().useAddressAutocomplete()— réutilisé du M1 (BAN), pas de réécriture.usePermissions()— masque l'onglet Comptabilité et le bouton Archiver.- Tous les appels passent par
useApi()(jamais$fetchdirect — règle ABSOLUE n°4). - Filter
formatPhoneFR()— réutilisé du M1 pour l'affichageXX XX XX XX XX.
Règles de formatage et normalisation
Le serveur normalise systématiquement (RG-2.12 — cf. spec-back.md) :
| Champ | Normalisation serveur | Affichage front |
|---|---|---|
Nom fournisseur (companyName) |
UPPERCASE intégral | UPPERCASE |
| Nom + Prénom contact | Capitalize | identique |
Téléphones (blocs SupplierContact) |
Chiffres uniquement en BDD | Formaté XX XX XX XX XX (filter Vue) |
| lowercase intégral | identique |
Le front ne normalise pas : il envoie la valeur saisie, le serveur normalise et renvoie la valeur normalisée que l'UI affiche. Cohérent avec
useApi().
API adresse postale
Code postal + Ville + Adresse branchés sur api-adresse.data.gouv.fr (BAN) via le composable useAddressAutocomplete() déjà créé au M1 (réutilisé tel quel) :
- À la saisie du CP (5 chiffres) :
GET https://api-adresse.data.gouv.fr/search/?q={cp}&type=municipality→ alimente le select Ville. - À la saisie d'adresse :
?q={addr}&postcode={cp}&type=housenumber→ suggestions. - Cas dégradé (timeout / offline) : Ville en
<MalioInputText>libre + toast d'avertissement.
Différences notables avec le M1 (clients)
| Zone | M1 clients | M2 fournisseurs |
|---|---|---|
| Distributeur / Courtier | Auto-référence Client (RG-1.03) | Absent |
| Prestation de triage | Booléen sur le client (formulaire principal) | Booléen sur l'adresse (triage_provider) |
| Type d'adresse | 3 checkboxes Prospect / Livraison / Facturation | Radio exclusif Prospect / Départ / Rendu (RG-2.09) |
| Email facturation sur adresse | Oui (conditionnel) | Absent |
| Champ adresse « Bennes » | — | Présent (nombre) |
| Onglet Information | 7 champs | 8 champs (ajout « Volume prévisionnel ») |
| Catégories | type unique CLIENT (codes ERP-78) |
nouveau type FOURNISSEUR |
| Archivage | Admin | Admin uniquement (idem) |
| Onglets « À venir » | frames blanches | placeholder « À venir » (minimal) |
Points résolus côté back
| # | Zone d'ombre | Résolution (cf. spec-back.md) |
|---|---|---|
| 1 | Catégorie multi-select | M2M supplier_category, Category de type FOURNISSEUR (RG-2.10) |
| 2 | Type d'adresse Prospect/Départ/Rendu | Enum exclusif address_type (RG-2.09) |
| 3 | Onglet Comptabilité : qui édite ? | Admin + Compta (accounting.manage) ; Bureau/Commerciale ne le voient pas |
| 4 | Workflow par onglet | Sauvegarde incrémentale (POST principal + PATCH partiels) — pas d'état « draft » |
| 5 | Onglets « À venir » | Placeholder minimal « À venir » (Transport / Stats / Rapports / Échanges) |
| 6 | Archive vs delete | Flag is_archived séparé de deleted_at ; archivage Admin seul ; soft delete = HP |
| 7 | Unicité métier | Nom de fournisseur uniquement (à valider — § 2.6). SIREN/email non uniques |
| 8 | Référentiels comptables | Réutilisés du M1 (zéro duplication) |
| 9 | API code postal | BAN via useAddressAutocomplete() du M1 |
| 10 | Format export | XLSX uniquement (CSV = HP) |
📦 Tickets Lesstime
TaskGroup Lesstime : à créer — M2 — Répertoire fournisseurs (projet ERP / Starseed, projectId=6).
Détail complet et action manuelle → voir
spec-back.md § Tickets Lesstime.