Files
Starseed/docs/specs/M2-suppliers/spec-front.md
T
matthieu 8fae987e15
Auto Tag Develop / tag (push) Successful in 6s
docs(commercial) : refonte contact — suppression du contact inline (specs M1 + M2) (#54)
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>
2026-06-03 13:16:11 +00:00

22 KiB
Raw Blame History

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
RG-2.01
RG-2.02
RG-2.03
RG-2.04
RG-2.05
RG-2.06
RG-2.07
RG-2.08
RG-2.09
RG-2.10
RG-2.11
RG-2.12
RG-2.13
RG-2.14
RG-2.15
RG-2.16
RG-2.17
Admin
Bureau
Compta
Commerciale
Usine
./spec-back.md
statut date version valide_par
validee 2026-05-22 V0 Matthieu (CP MALIO)
statut date version valide_par resume trace_archivee
validee 2026-06-01 V0.1 Matthieu (CP MALIO) Module 2 — Répertoire fournisseurs. Page d'entrée Commercial. Datatable + 3 écrans (Ajouter / Consulter / Modifier). Création par onglets : Information / Contact / Adresse / Comptabilité (Transport, Statistiques, Rapports, Échanges = placeholders 'À venir'). uploads/M2-reportoire-fournisseurs.docx (V0.1) + M2-reportoire-fournisseurs-V01.pdf
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 dans spec-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(...) de usePaginatedList (retombe en page 1), qui relance GET /api/suppliers avec 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 serveur companyName ASC par 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)
Email <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} (groupe supplier: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 (ou accounting.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 » (MalioDate non couvert).
  • Modal de confirmation : <MalioModal> ou wrapper partagé dans frontend/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 consomme categories[] (libellé = name) et sites[] (libellé = name, pas de code) 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 via GET /api/suppliers/{id}, qui embarque contacts, addresses (avec sites / categories / contacts imbriqué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 de useClientForm().
  • 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 $fetch direct — règle ABSOLUE n°4).
  • Filter formatPhoneFR()réutilisé du M1 pour l'affichage XX 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)
Email 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.