Files
Starseed/docs/specs/M4-transporteurs/spec-front.md
T
Matthieu d9313dbec8 feat(transport) : schéma + entités Carrier + contrat lecture (ERP-155/157)
Schéma BDD du répertoire transporteurs (M4) + entités + contrat de lecture
(liste + détail), socle du front.

- Migration Version20260615150000 : tables carrier / carrier_address /
  carrier_contact / carrier_price (FK cross-module, CHECK enum, index partiel
  uq_carrier_name_active, COMMENT ON COLUMN). uploaded_document et
  qualimat_carrier réutilisées (non recréées).
- Entités Carrier* (#[Auditable], Timestampable/Blamable) + ApiResource
  LECTURE seule (GetCollection + Get via CarrierProvider, anti-N+1, exclusion
  archivés + ?includeArchived). Écriture (POST/PATCH + Processor) reportée WT4+.
- QualimatCarrier : mapping ORM lecture seule sur la table référentielle
  existante (sortie du schema_filter, mapping aligné DDL ERP-39, schema:update
  no-op) + endpoint de recherche read-only (§ 4.7).
- Relations cross-module des prix (Client/Supplier/adresses) via contrats
  Shared (ClientInterface, SupplierInterface, ClientAddressInterface,
  SupplierAddressInterface) + resolve_target_entities — sans import inter-module
  (règle n°1). Ajout du groupe supplier_address:read aux champs de
  SupplierAddress pour l'embed.
- Garde-fous : ColumnCommentsCatalog (carrier* + qualimat_carrier), makefile
  test-db-setup (index partiel carrier), i18n audit (transport_carrier*),
  EntitiesAreTimestampableBlamableTest (QualimatCarrier whitelisté).
- CarrierSerializationContractTest : contrat JSON liste + détail vérifié
  (embeds objet, booléens, enveloppe Hydra) ; JSON réel capturé dans
  spec-back § 4.0.bis.

make db-reset OK, make test vert (731), make nuxt-test vert (480),
php-cs-fixer OK.
2026-06-15 19:15:12 +02:00

355 lines
23 KiB
Markdown

---
# === IDENTITÉ ===
module: M4
nom: "Répertoire transporteurs"
ecran: repertoire-transporteurs
owner_spec: Matthieu
backup_spec: Tristan
version: V0.1
date_redaction: 2026-06-15
# Historique :
# V0.1 (2026-06-15) — Restitution Markdown du docx « M4-repertoire-transporteurs-V0 »
# (validé 27/05/2026) + maquette Figma (node 1132-45376). Précisions techniques (back)
# dans spec-back.md. Réutilise le pattern et les composants M1/M2/M3.
# === LIENS ===
maquette_figma: "https://www.figma.com/design/jRYgT0T9c03VsEbjGhCwwS/Composants---Design-System?node-id=1132-45376&p=f&m=dev"
regles_metier: [RG-4.01, RG-4.02, RG-4.03, RG-4.04, RG-4.05, RG-4.06, RG-4.07, RG-4.08, RG-4.09, RG-4.10, RG-4.11]
roles: [Admin, Bureau, Compta, Commerciale, Usine]
lien_spec_back: ./spec-back.md
# === VALIDATION CLIENT ===
client_validation_1:
statut: validee
date: 2026-05-27
version: V0
valide_par: "Matthieu (CP MALIO)"
# === LIEN LESSTIME ===
lesstime_project_id: 6
lesstime_taskgroup_id: 31 # M4 — Répertoire transporteurs (tickets ERP-153 → ERP-171)
statut_global: pret_a_dev
---
# Module 4 — Répertoire transporteurs (V0.1 front)
> **Origine** : spec fonctionnelle `M4-repertoire-transporteurs-V0` (validée le 27/05/2026) + maquette Figma. Restitution Markdown pour intégration au workflow MALIO. Toute décision technique (back) vit dans [`spec-back.md`](./spec-back.md). Le M4 réutilise le pattern et les composants posés aux [M1 clients](../M1-clients/spec-front.md), [M2 fournisseurs](../M2-suppliers/spec-front.md) et [M3 prestataires](../M3-prestataires/spec-front.md).
> **Socle déjà en place** : le module back `Transport` existe (ERP-150) et porte deux référentiels **synchronisés par commandes console** : transporteurs **QUALIMAT** (`qualimat_carrier`, ERP-39) et codes **IDTF** (`idtf_product`, ERP-149). Le M4 ajoute le **répertoire éditable** (`Carrier`) **par-dessus** ces référentiels — la saisie assistée du nom interroge le référentiel QUALIMAT (RG-4.01). L'IDTF n'est **pas** utilisé par ces écrans.
> **Décisions Matthieu (15/06/2026)** : (1) lien QUALIMAT = FK + **copie éditable** des champs (nom / certification / adresse) ; (2) **pas de cloisonnement par site** (référentiel global) ; (3) le champ « Décharge » s'appuie sur une **infra d'upload réutilisable** (`Shared`), car d'autres uploads suivront. Détails : [`spec-back.md § 2.5 / § 2.3 / § 2.7`](./spec-back.md).
## But
Lister tous les transporteurs de l'organisation et accéder rapidement à leurs fiches : consultation, création, modification, archivage. Le nom est **relié à QUALIMAT** (saisie assistée) ; les transporteurs hors QUALIMAT (GMP+, OVOCOM, compte-propre, LIOT, autre) sont saisis manuellement.
## Accès
- **Depuis** : menu principal → section **Transport** (route `/carriers`). *(Section « Transport » dédiée ou rattachement à une section « Logistique » — à confirmer, cf. [`spec-back.md § 5.3`](./spec-back.md).)*
- **Rôles autorisés** (tableau « Rôles & permissions » du docx) :
| Rôle | Consultation | Ajout / Modification | Archive |
|---|---|---|---|
| **Admin** | ✅ Tout | ✅ Tout | ✅ |
| **Bureau** | ✅ Tout | ✅ Tout | ❌ |
| **Compta** | ❌ | ❌ | ❌ |
| **Commerciale** | ✅ Tout | ❌ | ❌ |
| **Usine** | ❌ | ❌ | ❌ |
> **Notes** :
> - RBAC transposée sur `transport.carriers.*` (cf. [`spec-back.md § 5`](./spec-back.md)). **Commerciale** = consultation seule (pas de « + Ajouter » ni « Modifier »). **Compta** et **Usine** n'ont **aucun** accès au module (item sidebar masqué).
> - **Pas de cloisonnement par site** (≠ M3) : tout rôle autorisé voit tous les transporteurs.
## Navigation
Page d'entrée du module **Transport** (route `/carriers`). Titre : « **Répertoire transporteurs** ».
- Affichage principal : un **datatable** listant tous les transporteurs **actifs** (les archivés sont masqués par défaut — filtre dédié).
- **Clic sur une ligne** → écran **Consultation transporteur** (page dédiée).
- **Bouton « + Ajouter »** (haut droite, si `manage`) → écran **Ajouter un transporteur**.
- **Bouton « Filtrer »** (haut droite) → panneau de filtres.
- **Bouton « Exporter »** (haut droite) → télécharge un **XLSX** des transporteurs **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/M3. Filtres branchés sur les query params de `GET /api/carriers` (cf. [`spec-back.md § 4.1`](./spec-back.md)) :
| Filtre | Composant | Query param back |
|---|---|---|
| **Recherche** (nom) | `<MalioInputText>` | `?search=` |
| **Certification** | `<MalioSelectCheckbox>` (QUALIMAT / GMP+ / OVOCOM / Compte-propre / Autre) | `?certificationType=` |
| **Inclure les archivés** | `<MalioCheckbox>` | `?includeArchived=true` |
- À l'application des filtres → `setFilters(...)` de `usePaginatedList` (retombe en **page 1**).
- **État 100 % local** (jamais dans l'URL — règle ABSOLUE n°6).
## Datatable du Répertoire
Composant : `<MalioDataTable>` branché sur `usePaginatedList<Carrier>({ url: '/carriers' })` (règle frontend obligatoire — pagination Hydra, état 100 % local). Colonnes :
| Colonne | Source | Tri |
|---|---|---|
| **Nom** | `carrier.name` | ASC par défaut |
| **Certification** | `carrier.certificationType` (libellé i18n) | Non |
| **Date de validité** | `carrier.qualimatCarrier.validityDate` (format `JJ-MM-AAAA`) — **fond rouge si < aujourd'hui** (RG-4.04) | Non |
| **Dernière activité** | `carrier.updatedAt` (format `JJ-MM-AAAA`) | Oui |
> **Clic sur une ligne** → écran Consultation. **Pagination** : standard Starseed 10 / 25 / 50 (défaut 10). Tri serveur `name ASC` par défaut.
## Écran « Ajouter un transporteur »
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. **L'onglet Adresses n'est accessible qu'une fois le formulaire principal validé.** Cf. [`spec-back.md § 2.9`](./spec-back.md) (PATCH partiels par groupe de sérialisation).
**Accès** : bouton « + Ajouter » du Répertoire. **Rôles** : Admin, Bureau.
**Barre d'onglets** : `Qualimat` · `Adresses` · `Contacts` · `Prix`.
### Formulaire principal (pré-onglets)
1er bloc à remplir. Sans validation, les onglets ne sont pas accessibles. Une fois validé → POST `/api/carriers`, puis bascule sur l'onglet Qualimat/Adresses ; les champs passent en readonly.
| Champ | Type composant | Obligatoire | Règle |
|---|---|---|---|
| **Nom** (saisie assistée reliée à QUALIMAT) | `<MalioInputText>` (autocomplete) | Oui | RG-4.01 ; RG-4.13 (UPPERCASE serveur) ; RG-4.12 (unicité) |
| **Liste certification transport** | `<MalioSelect>` (GMP+ / OVOCOM / Compte-propre / Autre) | Oui | RG-4.02 ; auto = `QUALIMAT` (lecture seule) si transporteur QUALIMAT sélectionné |
| **Affréter** | `<MalioCheckbox>` | Non | RG-4.03 |
| **Indexation %** | `<MalioInputNumber>` | Conditionnel | RG-4.03 — visible + obligatoire si « Affréter » coché |
| **Benne / Fond mouvant** | `<MalioRadioButton>` | Conditionnel | RG-4.03 — visible + obligatoire si « Affréter » coché |
| **Volume m³** | `<MalioInputNumber>` | Conditionnel | RG-4.03 — visible + obligatoire si « Affréter » coché |
| **Décharge** | `<MalioInputUpload>` *(cf. note)* | Conditionnel (**obligatoire si AUTRE**) | RG-4.02 — visible **et obligatoire** si certification = `AUTRE`. Upload via infra Shared ([`spec-back.md § 2.7`](./spec-back.md)) |
| **Liste immatriculation LIOT** | `<MalioInputText>` (ou TextArea) | Cas LIOT | RG-4.01 — visible **uniquement** si nom = `LIOT` ; les autres champs disparaissent. Immatriculations séparées par `;` |
> **Comportement RG-4.01 (saisie assistée)** : à la saisie du nom, recherche dans le référentiel QUALIMAT via `GET /api/qualimat_carriers?search=`. Sélection d'un résultat → **modal de confirmation** « Êtes-vous sûr de vouloir intégrer ce transporteur ? ». Si confirmé : le **Nom** et la **certification** (= `QUALIMAT`, lecture seule) se remplissent automatiquement, **ainsi que l'onglet Adresse** (copie pays/CP/ville/voie depuis le référentiel). La FK QUALIMAT est conservée (traçabilité + date de validité RG-4.04).
> - **Cas transporteur non trouvé** (pas QUALIMAT) : l'utilisateur choisit une autre certification (RG-4.02) → affichage des champs associés.
> - **Cas LIOT** : si le nom saisi est exactement `LIOT`, seul le champ « Liste immatriculation LIOT » s'affiche, les autres champs sont masqués.
> **Note `<MalioInputUpload>`** : si le composant ne couvre pas le drag & drop / type fichier requis, exception autorisée documentée (`// TODO migrer quand Malio couvre`) — cf. exceptions @.claude/rules/frontend.md.
**Action** : « Valider » (`<MalioButton>`) → POST `/api/carriers` ([`spec-back.md § 4.3`](./spec-back.md)). Succès → onglet « Qualimat » / « Adresses ».
### Onglet « Qualimat »
Sélectionner un transporteur de la liste QUALIMAT afin de mettre à jour les informations du transporteur (saisie assistée — voir RG-4.01).
**Colonnes du tableau de sélection** :
| Colonne | Règle |
|---|---|
| **Sélection** (bouton / clic ligne) | RG-4.03 *(docx)* — clic → modal « Êtes-vous sûr de vouloir intégrer ce transporteur ? » → remplit Nom + certification + onglet adresse |
| **Nom** | — |
| **Adresse** | — |
| **Date de validité** | RG-4.04 — **fond rouge si < date du jour** |
> Cet onglet alimente le formulaire principal et l'onglet Adresse par copie (RG-4.01 / RG-4.05). Source : `GET /api/qualimat_carriers?search=` (lecture seule, lignes actives uniquement).
### Onglet « Adresses »
Saisir l'adresse du transporteur (un bloc par adresse).
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| **Pays** | `<MalioSelect>` (préremplie « France ») | Conditionnel | RG-4.05 |
| **Code postal** | `<MalioInputText>` (saisie assistée) | Conditionnel | RG-4.06, RG-4.05 — déclenche autocomplete ville (BAN) |
| **Ville** | `<MalioSelect>` (saisie assistée) | Conditionnel | RG-4.06, RG-4.05 — alimentée par api-adresse.data.gouv.fr |
| **Adresse** | `<MalioInputText>` (saisie assistée) | Conditionnel | RG-4.05 |
| **Adresse complémentaire** | `<MalioInputText>` | Non | — |
> **RG-4.05** : les champs sont **déjà remplis** si le transporteur est QUALIMAT (copie). Si « Affréter » est coché, l'adresse devient **obligatoire** (Pays, Code postal, Ville, Adresse).
> **RG-4.06** : la ville est préremplie automatiquement à partir du code postal via l'API BAN (`useAddressAutocomplete()`, réutilisé M1/M2/M3). Si plusieurs villes → choix dans le select. L'adresse est une saisie assistée basée sur le CP et la ville.
> **RG-4.07** : le bouton « Valider » **n'apparaît pas** pour un transporteur QUALIMAT (adresse remplie automatiquement).
**Actions** : « Valider » → PATCH `/api/carriers/{id}/addresses` (sauf QUALIMAT, RG-4.07).
### Onglet « Contacts »
Saisir un ou plusieurs contacts associés au transporteur.
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| **Nom** | `<MalioInputText>` | Non | RG-4.08 + RG-4.13 (Capitalize) |
| **Prénom** | `<MalioInputText>` | Non | RG-4.08 + RG-4.13 (Capitalize) |
| **Fonction** | `<MalioInputText>` | Non | RG-4.08 |
| **Téléphone** (x1, +1 possible, **max 2**) | `<MalioInputText>` | Non | RG-4.08 + RG-4.13 (format) |
| **Email** | `<MalioInputText>` type email | Non | RG-4.08 + RG-4.13 (lowercase) |
**RG-4.08** : un bloc Contact est valide dès qu'au moins 1 champ est rempli. Impossible d'ajouter un nouveau bloc tant que le précédent n'est pas valide.
**Actions** :
- « + Nouveau contact » : ajoute un bloc. **Désactivé tant que le bloc précédent n'a aucun champ rempli** (RG-4.08).
- « Supprimer » (icône) : modal de confirmation, puis suppression du bloc.
- « Valider » → PATCH `/api/carriers/{id}/contacts`.
### Onglet « Prix »
Saisir un suivi de prix du transporteur (un bloc par prix). Tous les champs sont masqués par défaut sauf le radio « Client / Fournisseur » (RG-4.09).
**Bloc Prix** :
| Champ | Type | Obligatoire | Règle |
|---|---|---|---|
| **Client / Fournisseur** | `<MalioRadioButton>` | Oui | RG-4.09 |
| **Client** | `<MalioSelect>` (liste des clients) | Conditionnel | RG-4.10 — si Client |
| **Adresse de livraison** | `<MalioSelect>` (adresses du client sélectionné) | Conditionnel | RG-4.10 — si Client |
| **Adresse de départ** | `<MalioSelect>` (86 / 17 / 82) | Conditionnel | RG-4.10 — si Client ; = un des 3 sites |
| **Fournisseur** | `<MalioSelect>` (liste des fournisseurs) | Conditionnel | RG-4.11 — si Fournisseur |
| **Adresse d'approvisionnement** | `<MalioSelect>` (adresses du fournisseur) | Conditionnel | RG-4.11 — si Fournisseur |
| **Adresse de livraison** | `<MalioSelect>` (86 / 17 / 82) | Conditionnel | RG-4.11 — si Fournisseur ; = un des 3 sites |
| **Benne / Fond mouvant (FM)** | `<MalioRadioButton>` | Oui | — |
| **Forfait / Tonne** | `<MalioRadioButton>` | Oui | — |
| **Prix** | `<MalioInputAmount>` (monnaie) | Oui | — |
| **État du prix** | `<MalioSelect>` (En cours / Validé / Non validé) | Oui | — |
> **RG-4.10** : si **Client** sélectionné → champs liés au client affichés et obligatoires ; champs fournisseur masqués et non obligatoires.
> **RG-4.11** : si **Fournisseur** sélectionné → champs liés au fournisseur affichés et obligatoires ; champs client masqués et non obligatoires.
> **Adresse de départ / livraison « 86 / 17 / 82 »** = les 3 `Site` fixes (cf. switcher de site Châtellerault / Saint-Jean / Pommevic en haut de l'app). La sélection stocke un **ID de Site** ([`spec-back.md § 3.2`](./spec-back.md)).
**Actions** :
- « + Nouveau prix » : ajoute un bloc. Bloqué tant que le précédent n'est pas valide.
- « Supprimer » (icône) : modal de confirmation puis suppression.
- « Valider » → PATCH `/api/carriers/{id}/prices`.
## Écran « Consultation d'un transporteur »
Consulter en **lecture seule** la fiche complète. Affiche en haut du bloc les infos principales du transporteur (comme l'écran d'ajout) ainsi que les onglets Adresses, Contacts, Prix. **Tous les champs sont en lecture seule.**
**Accès** : clic sur une ligne du Répertoire. La page s'ouvre par défaut sur l'onglet **Adresses**. Icône « flèche » à gauche pour revenir au répertoire. Deux boutons à droite :
- **« Modifier »** (visible si `transport.carriers.manage` → Admin, Bureau).
- **« Archiver »** (visible **uniquement Admin** via `transport.carriers.archive`) → modal de confirmation, puis PATCH `/api/carriers/{id}` `{ "isArchived": true }`.
> Un transporteur archivé peut être restauré (`isArchived: false`) — bouton « Restaurer » remplace « Archiver » dans la consultation d'un archivé.
### Onglet Adresses (consultation)
Un bloc par adresse du transporteur. Chaque bloc, 5 champs en lecture seule : Pays / Code postal / Ville / Adresse / Adresse complémentaire.
### Onglet Contacts (consultation)
Un bloc par contact. 5 champs en lecture seule : Nom / Prénom / Fonction / Téléphone (x1 ou x2) / Email.
### Onglet Prix (consultation)
Un tableau regroupant les prix par type (**Fond Mouvant / Benne**) :
| Colonne | Description |
|---|---|
| **Colonne de regroupement** | « Fond Mouvant » / « Benne » |
| **Transporteurs** | Nom du transporteur |
| **Adresse APRO ou Adresse Sites** | Si prix « Client » → Adresse APRO sinon Adresse Sites |
| **Adresse livraisons** | — |
| **Forfait €** | Prix |
| **Tonne €** | Prix |
| **Indexation** | Pourcentage d'indexation (vide si non rempli) |
| **État du prix** | Validé / Non Validé / En cours |
**Action** : « Exporter » → exporte le tableau au **format Excel** (`GET /api/carriers/{id}/prices/export.xlsx`).
## Écran « Modification d'un transporteur »
Modifier les informations d'un transporteur existant. **Identique à l'écran « Ajouter un transporteur »** — mêmes formulaires, mêmes règles métier (RG-4.01 à RG-4.11) — sauf :
- Les champs sont **pré-remplis** avec les valeurs actuelles.
- **Validation par onglet** : on peut modifier UN onglet sans toucher aux autres (PATCH partiel).
- **Accès** : depuis l'écran Consultation, bouton « Modifier » (Admin, Bureau).
## Composants UI à utiliser (`@malio/layer-ui`)
- **Datatable** : `<MalioDataTable>` (+ `usePaginatedList`)
- **Input texte** : `<MalioInputText>`
- **Input nombre / montant** : `<MalioInputNumber>` (indexation, volume), `<MalioInputAmount>` (prix)
- **Select simple** : `<MalioSelect>` (certification, pays, ville, client, fournisseur, adresses, sites, état du prix)
- **Select multi (cases à cocher)** : `<MalioSelectCheckbox>` (filtres certification)
- **Radio** : `<MalioRadioButton>` (Benne/Fond mouvant, Forfait/Tonne, Client/Fournisseur)
- **Checkbox** : `<MalioCheckbox>` (Affréter, inclure archivés)
- **Upload** : `<MalioInputUpload>` (Décharge — exception documentée si type non couvert)
- **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 : wrapper partagé dans `frontend/shared/` (réutiliser celui du M1/M2/M3).
- `<MalioInputUpload>` si le type fichier / drag & drop n'est pas couvert.
## Composables & appels API
- `usePaginatedList<Carrier>({ url: '/carriers' })` — liste paginée (obligatoire). Consomme `name`, `certificationType`, `qualimatCarrier.validityDate` (RG-4.04), `updatedAt` (cf. [`spec-back.md § 2.11 / § 4.0`](./spec-back.md)).
- `useCarrier(id)` — charge le détail via `GET /api/carriers/{id}`, qui **embarque** `addresses`, `contacts`, `prices` (avec `client`/`supplier`/sites imbriqués) + `qualimatCarrier`. Écrans Consultation et Modification peuplés depuis cette seule réponse. **DoD avant intégration** : vérifier le JSON réel (cf. [`spec-back.md § 4.0.bis`](./spec-back.md)).
- `useCarrierForm()` — workflow par onglet (POST principal + PATCH partiels par groupe), miroir de `useSupplierForm()`/`useProviderForm()` + gestion des **champs conditionnels** (Affréter, AUTRE→Décharge, cas LIOT).
- `useQualimatSearch()` — saisie assistée du nom : `GET /api/qualimat_carriers?search=`, modal de confirmation, copie des champs + FK (RG-4.01).
- `useAddressAutocomplete()`**réutilisé** du M1/M2/M3 (BAN), pas de réécriture (RG-4.06).
- `useUpload()` (NOUVEAU, infra Shared) — POST multipart `/api/uploaded_documents` → renvoie l'IRI à poser sur `carrier.dischargeDocument` (RG-4.02).
- `usePermissions()` — masque l'item sidebar et les boutons selon les permissions.
- 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-4.13 — cf. [`spec-back.md`](./spec-back.md)) :
| Champ | Normalisation serveur | Affichage front |
|---|---|---|
| Nom transporteur (`name`) | UPPERCASE intégral | UPPERCASE |
| Nom + Prénom contact | Capitalize | identique |
| Téléphones (`CarrierContact`) | Chiffres uniquement en BDD | Formaté `XX XX XX XX XX` (filter Vue) |
| Email | lowercase intégral | identique |
| Immatriculations LIOT | `;`-split, trim, UPPER | listées |
> 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/M3** (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-4.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 les modules précédents
| Zone | M2/M3 | M4 transporteurs |
|---|---|---|
| Source du nom | saisie libre | **saisie assistée reliée à QUALIMAT** (référentiel synchronisé) |
| Onglet Comptabilité / RIB | présent (M2/M3) | **Absent** |
| Cloisonnement par site | M3 : oui | **Non** (référentiel global) |
| Champs conditionnels formulaire principal | peu | **Nombreux** (Affréter, AUTRE→Décharge, cas LIOT) |
| Onglet Prix | absent | **Présent** (Client/Fournisseur, sites départ/livraison) |
| Upload de fichier | aucun | **Décharge** (infra upload Shared, réutilisable) |
| Module | Commercial / Technique | **Transport** (existant, ERP-150) |
## Points résolus côté back
| # | Zone d'ombre | Résolution (cf. `spec-back.md`) |
|---|---|---|
| 1 | Lien QUALIMAT | FK `qualimatCarrier` + **copie éditable** des champs (§ 2.5) |
| 2 | Cas LIOT | Champ `liotPlates` (`;`-séparé), autres champs masqués (RG-4.01) |
| 3 | Certification QUALIMAT | Valeur `QUALIMAT` lecture seule si lié (§ 2.5) |
| 4 | Décharge (upload) | Infra upload générique `Shared` réutilisable (§ 2.7) |
| 5 | Onglet Prix — branches | M2M absentes : FK Client/Supplier + adresses + sites (RG-4.10/4.11, § 3.2) |
| 6 | Adresse de départ/livraison 86/17/82 | = les 3 `Site` fixes (FK Site) |
| 7 | Workflow par onglet | Sauvegarde incrémentale (POST principal + PATCH partiels) — pas d'état « draft » |
| 8 | Archive vs delete | Flag `is_archived` séparé ; archivage Admin seul ; soft delete = HP |
| 9 | Unicité métier | Nom seul (§ 2.6) |
| 10 | Référentiel QUALIMAT | Endpoint lecture seule `GET /api/qualimat_carriers?search=` (§ 4.7) |
| 11 | Format export | XLSX (répertoire + onglet Prix regroupé Benne/FM) |
| 12 | RBAC | `transport.carriers.view/manage/archive` ; Compta + Usine sans accès (§ 5.2) |
---
## 📦 Tickets Lesstime
**TaskGroup Lesstime** : à créer — `M4 — Répertoire transporteurs` (projet `ERP / Starseed`, projectId=6). Découpe détaillée (back en tête) → [`spec-back.md § Tickets Lesstime`](./spec-back.md#-tickets-lesstime-à-découper).
| Ordre | Sujet | Tag |
|---|---|---|
| 0 | Permissions `transport.carriers.*` + sidebar + 3 sources RBAC | Backend |
| 1 | Infra upload générique `Shared` (uploaded_document + FileUploader + endpoint) | Backend |
| 2 | Migration BDD M4 (carrier + sous-collections + index + COMMENT) | Backend |
| 3 | Entité `QualimatCarrier` (lecture seule) + endpoint recherche | Backend |
| 4 | Entités + Repositories Carrier* | Backend |
| 5 | CarrierProvider + CarrierProcessor (champs conditionnels, archive, LIOT) | Backend |
| 6 | Sous-ressources Adresses / Contacts / Prix (RG-4.10/4.11) | Backend |
| 7 | Export XLSX (répertoire + onglet Prix) | Backend |
| 8 | Tests PHPUnit RG-4.01→4.14 + capture contrat JSON | Backend |
| 9 | Page Répertoire (`/carriers`) + usePaginatedList | Frontend |
| 10 | Page Ajouter + formulaire principal + saisie assistée QUALIMAT | Frontend |
| 11 | Onglets Adresses (BAN) / Contacts / Prix | Frontend |
| 12 | Pages Consultation + Modification | Frontend |
| 13 | i18n + libellés audit + upload front (useUpload) | Frontend |