--- # === IDENTITÉ === module: M3 nom: "Répertoire prestataires" ecran: repertoire-prestataires owner_spec: Matthieu backup_spec: Tristan version: V0.2 date_redaction: 2026-06-11 # Historique : # V0.2 (2026-06-11) — Restitution Markdown du docx « M3-reportoire-prestataires.docx » (04/06/2026). # Alignement refonte-contact (comme M1/M2) : le contact principal inline du formulaire principal # du PDF V0.1 (Nom contact / Prénom contact / Téléphone + / Email) est RETIRÉ — saisie via # l'onglet Contacts uniquement (décision Matthieu, 11/06 : « oublie le contact inline, comme client »). # RG-3.01 / RG-3.02 (contact inline + max 2 tél sur le formulaire principal) supprimées en conséquence. # V0.1 (PDF) — version fonctionnelle plus ancienne, NON retenue (contact inline sur le formulaire principal). # === LIENS === maquette_figma: "https://www.figma.com/design/jRYgT0T9c03VsEbjGhCwwS/Composants---Design-System?node-id=1132-42090&p=f&m=dev" regles_metier: [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] roles: [Admin, Bureau, Compta, Commerciale, Usine] lien_spec_back: ./spec-back.md # === VALIDATION CLIENT === client_validation_1: statut: validee date: 2026-05-22 version: V0 valide_par: "Matthieu (CP MALIO)" client_validation_2: statut: validee date: 2026-06-01 version: V0.1 valide_par: "Matthieu (CP MALIO)" client_validation_3: statut: a_valider date: 2026-06-04 version: V0.2 resume: "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." trace_archivee: "uploads/M3-reportoire-prestataires.docx (V0.2) + M3-reportoire-prestataires-V01.pdf (V0.1, obsolète)" # === LIEN LESSTIME === lesstime_taskgroup_id: 29 # M3 — Répertoire prestataires (projet STARSEED #6) lesstime_project_id: 6 statut_global: 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`](./spec-back.md). Le M3 réutilise massivement le pattern et les composants posés au [M1 clients](../M1-clients/spec-front.md) et au [M2 fournisseurs](../M2-suppliers/spec-front.md). > **⚠️ 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`](./spec-back.md) (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`](./spec-back.md)). 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`](./spec-back.md)) — 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`](./spec-back.md). ### 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`](./spec-back.md)) : | Filtre | Composant | Query param back | |---|---|---| | **Recherche** (nom entreprise / contact / email) | `` | `?search=` | | **Catégorie** | `` (multi, type PRESTATAIRE) | `?categoryCode=` | | **Site** | `` (86 / 17 / 82) | `?siteId=` | | **Inclure les archivés** | `` | `?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 : `` branché sur `usePaginatedList({ 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`](./spec-back.md). > **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`](./spec-back.md) (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)** | `` | Oui | RG-3.11 (UPPERCASE serveur) ; RG-3.10 (unicité) | | **Catégorie** | `` (multi) | Oui | `Category` de **type PRESTATAIRE** via `GET /api/categories?typeCode=PRESTATAIRE` (RG-3.09). Libellé affiché = `category.name`. | | **Sélecteur de site** | `` (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 » (``) → POST `/api/providers` ([`spec-back.md § 4.3`](./spec-back.md)). 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** | `` | Conditionnel | RG-3.04 + RG-3.11 (Capitalize) | | **Prénom** | `` | Conditionnel | RG-3.04 + RG-3.11 (Capitalize) | | **Fonction** | `` | Non | — | | **Téléphone** (x1, +1 possible, **max 2**) | `` | Non | RG-3.11 (format) ; max 2 téléphones par contact | | **Email** | `` 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** | `` (86 / 17 / 82) | Oui | RG-3.05 — ≥ 1 site. Stocke des IDs de Site (M2M `provider_address_site`). | | **Adresse** | `` (saisie assistée) | Oui | RG-3.06 — autocomplete BAN | | **Adresse complémentaire** | `` | Non | — | | **Code postal** | `` (saisie assistée) | Oui | RG-3.06 — déclenche autocomplete ville (BAN) | | **Ville** | `` (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** | `` (préremplie « France ») | Oui | — | | **Catégories** | `` (multi) | Oui | Catégories de type PRESTATAIRE (RG-3.09) | | **Contact** | `` (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** | `` (masque 9 chiffres) | Oui | 9 chiffres. **Pas d'unicité** (cf. [`spec-back.md § 2.6`](./spec-back.md)) | | **Numéro de compte** | `` | Oui | — | | **Mode de TVA** | `` | Oui | Liste depuis `/api/tva_modes` (référentiel partagé M1) | | **N° de TVA** | `` | Oui | — | | **Délai de règlement** | `` | Oui | Liste depuis `/api/payment_delays` | | **Type de règlement** | `` | Oui | Liste depuis `/api/payment_types` | | **Banque** | `` | 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é** | `` | Oui (si LCR) | RG-3.08 | | **BIC** | `` | Oui (si LCR) | RG-3.08 | | **IBAN** | `` | 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** : `` (+ `usePaginatedList`) - **Input texte** : `` - **Select simple** : `` (Pays, Ville, référentiels comptables) - **Select multi (cases à cocher)** : `` (Catégorie, Sites, Contacts rattachés) - **Bouton** : ``, `` - **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 : `` ou wrapper partagé dans `frontend/shared/` (réutiliser celui du M1/M2). ## Composables & appels API - `usePaginatedList({ 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`](./spec-back.md)). - `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`](./spec-back.md)). - `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`](./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 `` 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`](./spec-back.md#-tickets-lesstime-à-découper).