# Prompt p07 — Structure des Prix (web)
*Contextes requis : ctx-global.md + ctx-web-nextjs.md + ctx-laravel-bff.md*

---

## Profil & Accès
- **Rôle CFO** : saisie mensuelle + soumission pour validation
- **Rôle DG** : validation (approuve ou rejette avec commentaire)
- **Rôle CEO** : lecture seule (historique + structure en vigueur)
- **Nombre d'utilisateurs** : 3 (1 CFO + 1 DG + 1 CEO)
- **Session** : 8h, OTP obligatoire à chaque connexion

---

## Fichiers à créer

```
app/(erp)/prix/page.tsx
app/(erp)/prix/[id]/page.tsx
app/(erp)/prix/[id]/valider/page.tsx
components/prix/StructurePrixForm.tsx
components/prix/NiveauxPrixTable.tsx
components/prix/ActeursVentilation.tsx
components/prix/WorkflowTimeline.tsx
components/prix/PropagationPreview.tsx
components/prix/ValidationModal.tsx
hooks/useStructurePrix.ts
```

---

## Spec UX condensée

```
┌─────────────────────────────────────────────────────────────────────┐
│ FLOWUP ERP          Structure des Prix — Mai 2026    [CFO][DG][CEO] │
├─────────────────────────────────────────────────────────────────────┤
│ [+ Nouvelle structure]   Historique ▾   Statut : EN VIGUEUR : Avr.  │
├─────────────────────────────────────────────────────────────────────┤
│ STRUCTURE MAI 2026 — BROUILLON                                      │
│ Produit : [Gasoil ▾]    Période : Mai 2026    [Enregistrer brouillon]│
├──────────────────────────────────┬──────────────────────────────────┤
│ 5 NIVEAUX DE PRIX                │ 8 ACTEURS — VENTILATION          │
│                                  │                                  │
│ ① Coût import (CIF Conakry)      │ Fournisseur     : 4 200 GNF/L   │
│    [__________] GNF/L            │ Douanes         :   480 GNF/L   │
│                                  │ Taxes État      :   320 GNF/L   │
│ ② + Droits & Taxes              │ Dépôt           :   150 GNF/L   │
│    Douanes   [______] GNF/L      │ Transport       :   280 GNF/L   │
│    Taxes État[______] GNF/L      │ Station FEBI    :   320 GNF/L   │
│                                  │ Marge FEBI      :   186 GNF/L   │
│ ③ + Coûts dépôt & transport     │ ─────────────────────────────── │
│    Dépôt    [______] GNF/L       │ TOTAL           : 5 936 GNF/L   │
│    Transport[______] GNF/L       │                                  │
│                                  │ Prix pompe public: 6 000 GNF/L  │
│ ④ + Marges                      │ Écart arrondi   :    64 GNF/L ✓ │
│    Station  [______] GNF/L       │                                  │
│    FEBI     [______] GNF/L       │                                  │
│                                  │                                  │
│ ⑤ Prix pompe public (résultat)  │                                  │
│    [  6 000 GNF/L  ] (calculé)   │                                  │
├──────────────────────────────────┴──────────────────────────────────┤
│ WORKFLOW                                                             │
│ ● BROUILLON → [SOUMETTRE AU DG] → EN_VALIDATION → VALIDÉ → APPLIQUÉ │
│                                                                      │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━                │
│ PROPAGATION VERS CATALOGUE PRODUITS (prévisualisation)              │
│ PRD-CAR-001 Gasoil : 5 800 GNF/L → 6 000 GNF/L  [+3,4%]           │
│ PRD-CAR-002 SP95   : 6 100 GNF/L → 6 300 GNF/L  [+3,3%]           │
└─────────────────────────────────────────────────────────────────────┘
```

### Modules clés
- **NiveauxPrixTable** : saisie des 5 niveaux (CFO). Calcul automatique du prix pompe = somme des acteurs. Validation en temps réel (prix pompe doit correspondre au tarif officiel ±1%).
- **ActeursVentilation** : tableau des 8 acteurs avec parts calculées et pourcentages. Mise à jour réactive sur chaque saisie.
- **WorkflowTimeline** : barre de progression 4 étapes avec dates et acteurs (qui a fait quoi). Bouton "Soumettre" pour CFO, "Valider/Rejeter" pour DG.
- **PropagationPreview** : avant validation DG, afficher l'impact sur le catalogue produits (anciens prix → nouveaux prix + % variation).
- **ValidationModal** : modal DG avec champ commentaire obligatoire si rejet, OTP requis pour validation.

---

## Endpoints BFF

```
GET /api/structure-prix
  Query : ?periode={YYYY-MM}&statut=brouillon|en_validation|valide|applique
  Réponse : [{
    id: INT,
    periode: "2026-05",
    produit: "Gasoil",
    statut: "BROUILLON"|"EN_VALIDATION"|"VALIDÉ"|"APPLIQUÉ",
    prix_pompe_public: "6000",   // VARCHAR (GNF/litre)
    created_by: INT,
    created_at: "..."
  }]

GET /api/structure-prix/{id}
  Réponse : {
    id: INT,
    periode: "2026-05",
    produit_id: INT,
    produit_nom: "Gasoil",
    statut: "BROUILLON",
    niveaux: {
      cout_import_cif: "4200",          // VARCHAR (GNF/L)
      droits_douanes: "480",            // VARCHAR
      taxes_etat: "320",                // VARCHAR
      cout_depot: "150",                // VARCHAR
      cout_transport: "280",            // VARCHAR
      marge_station: "320",             // VARCHAR
      marge_febi: "186",                // VARCHAR
      prix_pompe_public: "6000"         // VARCHAR (calculé)
    },
    acteurs: [
      { acteur: "Fournisseur", montant: "4200", pct: "70.0" },
      { acteur: "Douanes", montant: "480", pct: "8.0" },
      ...
    ],
    historique_workflow: [{
      etape: "SOUMIS",
      par: "CFO Bah",
      date: "2026-05-01T09:00:00Z",
      commentaire: null
    }]
  }

POST /api/structure-prix
  Body : {
    periode: "2026-05",
    produit_id: INT,
    cout_import_cif: "4200",        // VARCHAR
    droits_douanes: "480",          // VARCHAR
    taxes_etat: "320",              // VARCHAR
    cout_depot: "150",              // VARCHAR
    cout_transport: "280",          // VARCHAR
    marge_station: "320",           // VARCHAR
    marge_febi: "186"               // VARCHAR
  }
  Réponse : { id: INT, statut: "BROUILLON" }

PUT /api/structure-prix/{id}
  Body : { ...mêmes champs que POST... }
  Réponse : { id: INT, statut: "BROUILLON" }

PUT /api/structure-prix/{id}/soumettre
  Body : {} (le CFO soumet)
  Réponse : { id: INT, statut: "EN_VALIDATION" }

PUT /api/structure-prix/{id}/valider
  Body : { decision: "VALIDE"|"REJETE", commentaire: "...", otp: "123456" }
  Réponse : { id: INT, statut: "VALIDÉ"|"BROUILLON", propagation_ids: [INT] }

GET /api/structure-prix/{id}/propagation-preview
  Réponse : [{
    produit_code: "PRD-CAR-001",
    produit_nom: "Gasoil",
    prix_actuel: "5800",          // VARCHAR
    prix_nouveau: "6000",         // VARCHAR
    variation_pct: "+3.45"        // VARCHAR
  }]
```

---

## Règles spécifiques

- **RGPX-01** : Tous les montants (niveaux, marges, prix) = VARCHAR(255) côté BFF et DB. Calculs côté client via `Decimal.js` — jamais de `parseFloat`/`Number()` sur des montants.
- **RGPX-02** : Le prix pompe public est un champ calculé = somme des 7 acteurs. Affiché en lecture seule, jamais saisissable directement. Toute divergence >1% avec le tarif officiel affiche une alerte bloquante.
- **RGPX-03** : Un seul brouillon par produit par période — le POST rejette avec HTTP 409 si un brouillon existe déjà pour ce produit/période.
- **RGPX-04** : Propagation vers le catalogue produits est automatique et immédiate dès que la structure passe au statut "VALIDÉ". La PropagationPreview doit être affichée avant la soumission du DG.
- **RGPX-05** : La validation DG nécessite un OTP. L'OTP est envoyé par SMS au numéro du DG lors du clic sur "Valider".
- **RGPX-06** : Les structures au statut "APPLIQUÉ" sont en lecture absolue — aucun champ modifiable, pas de bouton d'action.
- **RGPX-07** : IDs = `number` (INT UNSIGNED). Soft delete = `statut: 3` (structure annulée sans validation).
- **RGPX-08** : Le workflow est linéaire et non-réversible : BROUILLON → EN_VALIDATION → VALIDÉ → APPLIQUÉ. Un rejet DG renvoie en BROUILLON (pas au statut 3).

---

## Tâche Claude

Génère le code complet Next.js 15 TypeScript pour le module Structure des Prix FlowUp ERP.

**Contraintes techniques absolues :**
- Palette navy `#1A2157`, blue `#1E5EFF`, cyan `#00C8C0` — Inter + JetBrains Mono
- Montants = VARCHAR(255) → `string` dans les types TS. Tous calculs via `Decimal.js`
- IDs = `number` (INT UNSIGNED)
- Permissions par rôle : CFO = saisie+soumission, DG = validation, CEO = lecture seule
- Masquer les boutons d'action selon le rôle du JWT courant

**Génère dans cet ordre :**
1. Types TypeScript (`types/structure-prix.ts`)
2. Utilitaire `utils/calcul-prix.ts` (Decimal.js, somme acteurs, calcul prix pompe)
3. Hook `useStructurePrix.ts` (CRUD + workflow)
4. `NiveauxPrixTable.tsx` (saisie 5 niveaux, calcul réactif)
5. `ActeursVentilation.tsx` (tableau 8 acteurs, %s)
6. `PropagationPreview.tsx` (tableau impact catalogue)
7. `WorkflowTimeline.tsx` (barre 4 étapes + boutons rôle-dépendants)
8. `ValidationModal.tsx` (DG : commentaire + OTP)
9. `StructurePrixForm.tsx` (assemblage CFO)
10. Pages : `page.tsx` (liste), `[id]/page.tsx` (détail), `[id]/valider/page.tsx` (validation DG)
