Files
Starseed/docs/specs/M3-prestataires/spec-front.md
T
Matthieu 6ceef62056
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 2m13s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m27s
feat(technique) : module Technique + taxonomie categories prestataires
Cree le nouveau module Technique (pole distinct du Commercial) prerequis du
M3 repertoire prestataires :
- TechniqueModule (ID=technique, REQUIRED=false) + 5 permissions RBAC
  technique.providers.* (view / manage / accounting.view / accounting.manage
  / archive), declarees pour app:sync-permissions.
- Activation dans config/modules.php + layer front frontend/modules/technique/.
- Seed taxonomie : nouveau CategoryType PRESTATAIRE + 3 categories
  (Maintenance industrielle, Nettoyage, Transport) via migration idempotente
  (ON CONFLICT / NOT EXISTS, jonction M2M category_category_type) ET fixtures
  CategoryType/Category (survivent au purger db-reset).
- Tests : structure du module (5 permissions figees) + filtre
  GET /api/categories?typeCode=PRESTATAIRE.

Inclut la spec back/front M3 et le RETEX M1.
2026-06-12 09:23:08 +02:00

24 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, client_validation_3, 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 client_validation_3 lesstime_taskgroup_id lesstime_project_id statut_global
M3 Répertoire prestataires repertoire-prestataires Matthieu Tristan V0.2 2026-06-11 https://www.figma.com/design/jRYgT0T9c03VsEbjGhCwwS/Composants---Design-System?node-id=1132-42090&p=f&m=dev
RG-3.03
RG-3.04
RG-3.05
RG-3.06
RG-3.07
RG-3.08
RG-3.09
RG-3.10
RG-3.11
RG-3.12
RG-3.13
RG-3.14
RG-3.15
RG-3.16
RG-3.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
validee 2026-06-01 V0.1 Matthieu (CP MALIO)
statut date version resume trace_archivee
a_valider 2026-06-04 V0.2 Module 3 — Répertoire prestataires. Pôle Technique (nouvelle section sidebar). Datatable + 3 écrans (Ajouter / Consulter / Modifier). Création par onglets : Contact / Adresse / Comptabilité (Rapports, Échanges = placeholders 'À venir'). PAS d'onglet Information. Sélecteur de site aussi sur le formulaire principal. uploads/M3-reportoire-prestataires.docx (V0.2) + M3-reportoire-prestataires-V01.pdf (V0.1, obsolète)
29 6 en_dev

Module 3 — Répertoire prestataires (V0.2 front)

Origine : spec fonctionnelle M3-reportoire-prestataires.docx (V0.2 du 04/06/2026 ; historique V0 22/05 → V0.1 01/06). Restitution Markdown pour intégration au workflow MALIO. Le contenu fonctionnel original n'est pas modifié, sauf l'alignement refonte-contact (cf. ci-dessous). Toute décision technique (back) vit dans spec-back.md. Le M3 réutilise massivement le pattern et les composants posés au M1 clients et au M2 fournisseurs.

⚠️ Alignement refonte-contact (décision Matthieu, 11/06/2026) : le PDF V0.1 portait un contact principal inline sur le formulaire principal (Nom du contact / Prénom du contact / Téléphone + bouton + / Email) avec RG-3.01 (Nom OU Prénom) et RG-3.02 (max 2 téléphones). Ce contact inline est retiré, exactement comme l'a fait M1/M2 (refonte-contact). Les coordonnées du contact se saisissent uniquement dans l'onglet Contacts. RG-3.01 et RG-3.02 sont donc supprimées du formulaire principal ; la garantie « au moins un contact nommé » est portée par RG-3.04 + RG-3.12, et le « maximum 2 téléphones » s'applique aux blocs Contact.

⚠️ Décision d'architecture (à confirmer) — pôle « Technique » : le docx place le répertoire prestataires dans un Module « Technique ». Confirmé par Matthieu (11/06) : c'est bien un nouveau pôle Technique, distinct du Commercial. Côté front cela se traduit par une nouvelle section sidebar « Technique » (route /providers). Côté back, voir spec-back.md § 2.1 (nouveau module Technique, entités jumelles du fournisseur, référentiels comptables consommés en relation ORM partagée).

But

Lister tous les prestataires de l'organisation et accéder rapidement à leurs fiches : consultation, création, modification, archivage. C'est la porte d'entrée du pôle Technique.

Accès

  • Depuis : menu principal → section Technique → entrée « Répertoire prestataires » (route /providers).
  • Rôles autorisés (tableau « Rôles & permissions » du docx) :
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 Son site uniquement

Notes :

  • RBAC transposée sur technique.providers.* (cf. spec-back.md § 2.9 / § 5). Compta édite uniquement l'onglet Comptabilité d'un prestataire existant ; Compta ne peut pas créer un prestataire. L'archivage est réservé à Admin.
  • Cloisonnement par site (décision 11/06 — DANS LE PÉRIMÈTRE M3) : « Tout » vs « son site uniquement » n'est pas porté par le rôle mais par l'utilisateur. Chaque user a un site courant ; par défaut il ne voit que les prestataires rattachés à son site. Les profils qui doivent voir tous les sites (Admin, et par défaut Bureau / Compta / Commerciale) ont la permission sites.bypass_scope (Admin l'a automatiquement). Usine n'a pas le bypass → cloisonnée à son site. Filtrage automatique côté back (cf. spec-back.md § 2.13) — aucun filtre à coder côté front.

Navigation

Page d'entrée du pôle Technique (route /providers). Titre : « Répertoire prestataires ».

  • Affichage principal : un datatable listant tous les prestataires actifs (les archivés sont masqués par défaut — toggle/filtre dédié).
  • Clic sur une ligne → écran Consultation prestataire (page dédiée).
  • Bouton « + Ajouter » (haut droite) → écran Ajouter un prestataire.
  • Bouton « Filtrer » (haut droite) → panneau de filtres (cf. ci-dessous).
  • Bouton « Exporter » (haut droite) → télécharge un XLSX des prestataires affichés (cf. filtres actifs). Format dans spec-back.md § 4.6.

Panneau de filtres (bouton « Filtrer »)

Réutilise le pattern M1/M2. Filtres branchés sur les query params de GET /api/providers (cf. spec-back.md § 4.1) :

Filtre Composant Query param back
Recherche (nom entreprise / contact / email) <MalioInputText> ?search=
Catégorie <MalioSelectCheckbox> (multi, type PRESTATAIRE) ?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/providers.
  • État 100 % local (jamais dans l'URL — règle ABSOLUE n°6).

Datatable du Répertoire

Composant : <MalioDataTable> branché sur usePaginatedList<Provider>({ url: '/providers' }) (règle frontend obligatoire — pagination Hydra, état 100 % local). Colonnes (alignées M2) :

Colonne Source Tri
Nom provider.companyName ASC par défaut
Catégories provider.categories[].name (embarquées en liste — cohérence M1/M2 ; libellé = name, pas label) Non
Site provider.sites[].name (sites du prestataire — cf. note ci-dessous) Non
Dernière activité provider.updatedAt (format JJ-MM-AAAA) — exposé dans provider:read Oui

Source de la colonne « Site » : le M3 porte un sélecteur de site sur le formulaire principal (RG-3.03) — donc provider.sites[] est une relation directe du prestataire (≠ M2 où les sites venaient de l'agrégat des adresses). La colonne liste affiche ces sites directs. Voir spec-back.md § 2.12. Clic sur une ligne → écran Consultation. Pagination : standard Starseed 10 / 25 / 50 (défaut 10). Tri serveur companyName ASC par défaut.

Écran « Ajouter un prestataire »

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).

Accès : bouton « + Ajouter » du Répertoire. Rôles : Admin, Bureau.

Barre d'onglets en création (3 onglets) : Contact · Adresse · Comptabilité. Les onglets Rapports et Échanges n'apparaissent PAS dans le flux de création — ils ne sont présents qu'en Consultation / Modification (placeholders « À venir »).

Différence majeure avec M2 : PAS d'onglet « Information ». Le M3 n'a aucun champ Description / Concurrent / Date création / Salariés / CA / Dirigeant / Résultat / Volume. Le formulaire principal est minimal (3 champs).

Règle « placeholder par défaut » (convention MALIO) : tout onglet ou écran que la spec ne détaille pas explicitement (ici Rapports et Échanges) est livré en placeholder « À venir » (frame vide, navigable, pas de validation ni d'API), à l'identique des autres modules (M1/M2). Aucun champ inventé hors spec.

Formulaire principal (pré-onglets)

1er bloc à remplir. Sans validation, les onglets ne sont pas accessibles. Une fois validé → POST /api/providers, puis bascule sur l'onglet Contact ; les champs passent en readonly.

Champ Type composant Obligatoire Règle
Nom du prestataire (Entreprise) <MalioInputText> Oui RG-3.11 (UPPERCASE serveur) ; RG-3.10 (unicité)
Catégorie <MalioSelectCheckbox> (multi) Oui Category de type PRESTATAIRE via GET /api/categories?typeCode=PRESTATAIRE (RG-3.09). Libellé affiché = category.name.
Sélecteur de site <MalioSelectCheckbox> (86 / 17 / 82) Oui RG-3.03 — ≥ 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 provider_site).

Action : « Valider » (<MalioButton>) → POST /api/providers (spec-back.md § 4.3). Succès → onglet « Contact ».

Onglet « Contact »

Saisir un ou plusieurs contacts. Au moins un bloc Contact valide est requis (RG-3.12). (Refonte-contact : pas de pré-remplissage depuis le formulaire principal ; les coordonnées du contact se saisissent directement ici.)

Bloc Contact :

Champ Type Obligatoire Règle
Nom <MalioInputText> Conditionnel RG-3.04 + RG-3.11 (Capitalize)
Prénom <MalioInputText> Conditionnel RG-3.04 + RG-3.11 (Capitalize)
Fonction <MalioInputText> Non
Téléphone (x1, +1 possible, max 2) <MalioInputText> Non RG-3.11 (format) ; max 2 téléphones par contact
Email <MalioInputText> type email Non RG-3.11 (lowercase)

RG-3.04 / RG-3.12 : un bloc Contact est valide dès qu'au moins 1 champ est rempli ; au moins 1 bloc Contact valide pour finaliser l'onglet — l'onglet Contact ne peut pas être validé vide.

Actions :

  • « + Nouveau contact » : ajoute un bloc. Désactivé tant que le bloc précédent n'a pas au moins 1 champ rempli (RG-3.04).
  • « Supprimer » (icône) : modal de confirmation, puis suppression du bloc.
  • « Valider » → PATCH /api/providers/{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
Sélecteur de site <MalioSelectCheckbox> (86 / 17 / 82) Oui RG-3.05 — ≥ 1 site. Stocke des IDs de Site (M2M provider_address_site).
Adresse <MalioInputText> (saisie assistée) Oui RG-3.06 — autocomplete BAN
Adresse complémentaire <MalioInputText> Non
Code postal <MalioInputText> (saisie assistée) Oui RG-3.06 — déclenche autocomplete ville (BAN)
Ville <MalioSelect> (saisie assistée) Oui RG-3.06 — alimentée par api-adresse.data.gouv.fr suivant le CP ; si plusieurs villes, choix dans le select
Pays <MalioSelect> (préremplie « France ») Oui
Catégories <MalioSelectCheckbox> (multi) Oui Catégories de type PRESTATAIRE (RG-3.09)
Contact <MalioSelectCheckbox> (multi) Non Liste = blocs Contact saisis dans l'onglet Contact

Différence avec M2 : l'adresse prestataire n'a PAS de Type d'adresse (Prospect/Départ/Rendu), PAS de Bennes, PAS de Prestation de triage. C'est une adresse « simple » (site + adresse postale + catégories + contacts).

Actions :

  • « + Nouvelle Adresse » : ajoute un bloc identique au premier.
  • « Supprimer » (icône) : modal de confirmation puis suppression.
  • « Valider » → PATCH /api/providers/{id}/addresses.

Onglet « Comptabilité »

Accessible aux rôles avec technique.providers.accounting.view (Admin + Compta). Bureau et Commerciale ne voient pas l'onglet. Compta peut éditer cet onglet (accounting.manage). Compta ne peut pas créer un prestataire (pas de manage global).

Champs comptables :

Champ Type Obligatoire Règle
SIREN <MalioInputText> (masque 9 chiffres) Oui 9 chiffres. Pas d'unicité (cf. spec-back.md § 2.6)
Numéro de compte <MalioInputText> Oui
Mode de TVA <MalioSelect> Oui Liste depuis /api/tva_modes (référentiel partagé 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-3.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-3.08) :

Champ Type Obligatoire Règle
Libellé <MalioInputText> Oui (si LCR) RG-3.08
BIC <MalioInputText> Oui (si LCR) RG-3.08
IBAN <MalioInputText> Oui (si LCR) RG-3.08

Actions :

  • « + RIB » : ajoute un bloc.
  • « Supprimer » (icône) : modal de confirmation.
  • « Valider » → PATCH /api/providers/{id} (groupe provider:write:accounting) + sous-ressource RIBs.

Écran « Consultation prestataire »

Tous les champs en lecture seule. La page s'ouvre par défaut sur l'onglet Contacts. 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 technique.providers.manage) → écran Modification.
  • Bouton « Archiver » (droite, visible uniquement Admin via technique.providers.archive) → modal de confirmation, puis PATCH /api/providers/{id} { "isArchived": true }.

Un prestataire archivé peut être restauré (isArchived: false) — bouton « Restaurer » remplace « Archiver » dans la consultation d'un archivé.

Onglets affichés en consultation

Contacts · Adresse · Rapports · Échanges · Comptabilité. Navigation libre entre onglets (pas de séquence forcée). Rapports et Échanges = placeholders « À venir ». Comptabilité selon permission.

  • Onglet Contacts : un bloc par contact, 5 champs en lecture seule (Nom / Prénom / Fonction / Téléphone / Email).
  • Onglet Adresse : un bloc par adresse, en lecture seule (Sélecteur de site / Adresse / Adresse complémentaire / Code postal / Ville / Pays / Catégorie / Contact).
  • Onglet Comptabilité : bloc principal (champs comptables) + un bloc par RIB. Le champ Banque n'apparaît que si Type de règlement = Virement (RG-3.07).

Écran « Modification prestataire »

Comportement identique à l'écran Ajouter (mêmes formulaires, mêmes RG-3.03 → RG-3.08) sauf :

  • Pas de formulaire principal réaffiché (champs principaux édités via l'onglet correspondant / pré-remplis).
  • Les champs sont pré-remplis avec les valeurs actuelles du prestataire.
  • 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).
  • Accès : Admin, Bureau (Compta pour l'onglet Comptabilité uniquement).

Composants UI à utiliser (@malio/layer-ui)

  • Datatable : <MalioDataTable> (+ usePaginatedList)
  • Input texte : <MalioInputText>
  • Select simple : <MalioSelect> (Pays, Ville, référentiels comptables)
  • Select multi (cases à cocher) : <MalioSelectCheckbox> (Catégorie, Sites, Contacts rattachés)
  • Bouton : <MalioButton>, <MalioButtonIcon>
  • Toasts : standards via useApi()
  • Validation par champ : useFormErrors (mapping 422 inline — règle frontend obligatoire)

Exceptions autorisées (commenter // TODO migrer quand Malio couvre) :

  • Modal de confirmation : <MalioModal> ou wrapper partagé dans frontend/shared/ (réutiliser celui du M1/M2).

Composables & appels API

  • usePaginatedList<Provider>({ url: '/providers' }) — liste paginée (obligatoire). La liste consomme categories[] (libellé = name) et sites[] (libellé = name, pas de code) embarqués + updatedAt (cf. spec-back.md § 2.12 / § 4.0).
  • useProvider(id) — charge le détail via GET /api/providers/{id}, qui embarque contacts, addresses (avec sites / categories / contacts imbriqués) et, si permission, ribs + scalaires compta. Écrans Consultation et Modification peuplés depuis cette seule réponse (RETEX M1 §2 : embed borné, pas de N+1). DoD avant intégration : vérifier que le JSON réel contient ces blocs (cf. spec-back.md § 4.0.bis).
  • useProviderForm() — workflow par onglet (POST principal + PATCH partiels par groupe), miroir de useSupplierForm().
  • useAddressAutocomplete()réutilisé du M1/M2 (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é pour l'affichage XX XX XX XX XX.

Règles de formatage et normalisation

Le serveur normalise systématiquement (RG-3.11 — cf. spec-back.md) :

Champ Normalisation serveur Affichage front
Nom prestataire (companyName) UPPERCASE intégral UPPERCASE
Nom + Prénom contact Capitalize identique
Téléphones (blocs ProviderContact) 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.

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/M2 (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 (RG-3.06 : si plusieurs villes, choix dans le select).
  • À 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 M2 (fournisseurs)

Zone M2 fournisseurs M3 prestataires
Onglet Information 8 champs (Description … Volume) Absent (aucun champ Information)
Sélecteur de site sur formulaire principal Non (sites uniquement via adresses) Oui (RG-3.03 — relation directe provider.sites)
Type d'adresse Radio Prospect / Départ / Rendu (RG-2.09) Absent
Bennes / Prestation de triage (adresse) Présents Absents
Onglet Transport Placeholder Absent
Onglet Statistiques Placeholder Absent
Onglets « À venir » Transport / Stats / Rapports / Échanges Rapports / Échanges uniquement
Catégories type FOURNISSEUR nouveau type PRESTATAIRE
Pôle / module Commercial Technique (nouvelle section sidebar + module back)
Cloisonnement par site aucun Visibilité par site, pilotée par l'utilisateur (bypass via sites.bypass_scope) — § 2.13

Points résolus côté back

# Zone d'ombre Résolution (cf. spec-back.md)
1 Catégorie multi-select M2M provider_category, Category de type PRESTATAIRE (RG-3.09)
2 Site sur le formulaire principal M2M provider_site (≥ 1 — RG-3.03), distinct de provider_address_site (RG-3.05)
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 » (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 prestataire uniquement (à valider — § 2.6). SIREN/email non uniques
8 Référentiels comptables Réutilisés M1/M2 (zéro duplication) ; relation ORM partagée
9 API code postal BAN via useAddressAutocomplete() du M1/M2 (RG-3.06)
10 Format export XLSX uniquement (CSV = HP)
11 Cloisonnement par site (Usine « son site ») Filtre back automatique par currentSite + bypass sites.bypass_scope (§ 2.13 / RG-3.17)

📦 Tickets Lesstime

TaskGroup Lesstime : #29 — M3 — Répertoire prestataires (projet ERP / Starseed, projectId=6) — créé le 11/06/2026, 16 tickets ERP-131ERP-146, statut « Prêt à dev », assignés à Tristan.

# Ticket Réf Tag
1.1 Créer module Technique + taxonomie PRESTATAIRE ERP-131 Backend
1.2 Migrer le schéma BDD M3 (provider + sous-collections) ERP-132 Backend
1.3 Créer entités + repositories Provider* ERP-133 Backend
1.4 ProviderProvider + ProviderProcessor + cloisonnement site ERP-134 Backend
1.5 Sous-ressources Contacts / Adresses / RIBs ERP-135 Backend
1.6 Valider les RG métier server-side (RG-3.03→3.09) ERP-136 Backend
1.7 Export XLSX des prestataires ERP-137 Backend
1.8 RBAC technique.providers.* (3 sources) ERP-138 Backend
1.9 PHPUnit RG-3.x + capture contrat JSON ERP-139 Backend
1.10 Page Répertoire (/providers) ERP-140 Frontend
1.11 Page Ajouter (/providers/new) + formulaire principal ERP-141 Frontend
1.12 Onglet Contact ERP-142 Frontend
1.13 Onglet Adresse (autocomplete BAN) ERP-143 Frontend
1.14 Onglet Comptabilité + RIB ERP-144 Frontend
1.15 Pages Consultation + Modification ERP-145 Frontend
1.16 i18n + sidebar Technique + libellés audit ERP-146 Frontend

Détail back complet → voir spec-back.md § Tickets Lesstime.