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.
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 |
|
|
./spec-back.md |
|
|
|
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 dansspec-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, voirspec-back.md § 2.1(nouveau moduleTechnique, 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(...)deusePaginatedList(retombe en page 1), qui relanceGET /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. Voirspec-back.md § 2.12. Clic sur une ligne → écran Consultation. Pagination : standard Starseed 10 / 25 / 50 (défaut 10). Tri serveurcompanyName ASCpar 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 |
<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}(groupeprovider: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(ouaccounting.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é dansfrontend/shared/(réutiliser celui du M1/M2).
Composables & appels API
usePaginatedList<Provider>({ url: '/providers' })— liste paginée (obligatoire). La liste consommecategories[](libellé =name) etsites[](libellé =name, pas decode) embarqués +updatedAt(cf.spec-back.md § 2.12 / § 4.0).useProvider(id)— charge le détail viaGET /api/providers/{id}, qui embarquecontacts,addresses(avecsites/categories/contactsimbriqué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 deuseSupplierForm().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$fetchdirect — règle ABSOLUE n°4). - Filter
formatPhoneFR()— réutilisé pour l'affichageXX 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) |
| 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-131 → ERP-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.