# MIGRATION-PLAN.md — ICAVI GESTION
*Rédigé le 2026-10-08 — basé sur AUDIT.md + ARCHITECTURE.md + DESIGN-SYSTEM.md*

---

## 1. PHILOSOPHIE DE MIGRATION

### Principe directeur : "Strangler Fig" progressif

On ne supprime pas l'existant d'un coup. On construit le nouveau système en parallèle, module par module, jusqu'à ce que l'ancien soit entièrement remplacé.

```
Phase A : Infrastructure (Laravel 12 + API skeleton)
Phase B : Auth + Shell React
Phase C : Modules critiques (Stock, Documents, Dashboard)
Phase D : Modules secondaires (Clients, Ventes, GED)
Phase E : Modules spéciaux (Bons de caisse, Permissions, Notifs)
Phase F : Cleanup, PDF, cron, déploiement production
```

### Règles de migration

1. **Les données ne bougent pas.** La base de données `icaviss3` existante est conservée. Pas de migration destructive.
2. **L'URL de production reste `gst.icavi-secure.com`.**
3. **Zéro downtime.** L'ancienne Blade app reste en ligne jusqu'au go-live du nouveau système.
4. **Chaque phase est livrable, testable et annulable.**
5. **La logique métier reste côté serveur.** React ne valide que l'UX.

---

## 2. CE QU'ON GARDE (sans modification)

| Élément | Raison |
|---|---|
| Base de données `icaviss3` (25 tables) | Données de production — intouchables |
| `StockService` (logique de verrouillage pessimiste) | Solide, correcte, à conserver |
| Modèle `Document::STATUTS_PAR_TYPE` | Logique métier correcte |
| Migrations existantes (30 fichiers) | Base de référence pour les nouvelles |
| Assets images (`icavi-logo.png`, cachet, etc.) | Identité de marque |
| Helper `NumberToWords` | Spécifique métier, fonctionnel |
| Config `config/icavi.php` | Étendue, pas remplacée |
| Seeders existants | Utiles pour le dev local |

---

## 3. CE QU'ON CORRIGE

| Problème | Correction | Priorité |
|---|---|---|
| `DocumentService` instancie `DocumentController` | Extraire `calculerMontants()` vers `DocumentService` | P0 |
| Bug `$typeCode` undefined dans `DocumentService::genererDepuisVente()` | Fixer la référence à `$type->code` | P0 |
| Logique métier dans `DocumentController::calculerMontants()` | Déplacer vers `DocumentService` | P0 |
| Logique métier dans `DocumentController::generateReference()` | Déplacer vers `DocumentService` | P0 |
| `CheckRole` middleware — vérification simple de string | Remplacer par Laravel Policies | P1 |
| `auto_seed` dans `DocumentController` | Migrer vers un Seeder dédié | P1 |
| Pas de Form Requests | Créer `StoreDocumentRequest`, `UpdateDocumentRequest`, etc. | P1 |
| Pas d'API Resources | Créer `DocumentResource`, `ProductResource`, etc. | P1 |
| Données client dénormalisées sur Document | Conserver (archivage voulu) + documenter le choix | - |

---

## 4. CE QU'ON CONSTRUIT (nouveau)

| Élément | Description | Phase |
|---|---|---|
| Toute l'API REST (`/api/v1/*`) | 85 routes web → ~60 routes API | A-E |
| Auth Sanctum SPA | Login, logout, CSRF | B |
| React SPA complète | Toutes les pages Blade → composants React | B-E |
| Laravel Policies | Rôles granulaires par ressource | A |
| API Resources | Sérialisation JSON cohérente | A |
| Tables `categories` et `suppliers` normalisées | Remplacer les champs string dans `products` | A |
| PDF DomPDF | Remplacer Chrome headless | C |
| Design System React | Composants UI + Tailwind tokens | B |
| Zustand auth store | État auth côté React | B |
| TanStack Query | Cache et synchronisation API | B-E |

---

## 5. CE QU'ON SUPPRIME

| Élément | Raison | Timing |
|---|---|---|
| Toutes les vues Blade (sauf print/) | Remplacées par React | Phase F |
| Routes web (hors catch-all SPA) | Remplacées par routes API | Phase F |
| Chrome headless / `CASH_VOUCHER_CHROME_PATH` | Remplacé par DomPDF | Phase C |
| `app.css` (949 lignes de CSS Blade) | Remplacé par Tailwind + Design System | Phase B |
| `CheckRole` middleware | Remplacé par Policies | Phase A |

---

## 6. PHASES DÉTAILLÉES

---

### PHASE A — Infrastructure Laravel 12

**Durée estimée : 3-4 jours**

#### A1. Mise à jour Laravel 11 → Laravel 12

```bash
# 1. Mettre à jour composer.json
"require": {
    "php": "^8.2",
    "laravel/framework": "^12.0",
    "laravel/sanctum": "^4.0",
    "barryvdh/laravel-dompdf": "^3.0"
}

# 2. Mise à jour des dépendances
composer update

# 3. Vérifier les breaking changes Laravel 12
# → Principaux : Model::unguard() retiré (utiliser $guarded = [])
# → HasFactory trait importé depuis Illuminate\Database\Eloquent\Factories
# → Vérifier config/database.php (sqlite 3.35+ si tests)

# 4. Lancer les tests existants
php artisan test
```

#### A2. Installer et configurer Sanctum

```bash
composer require laravel/sanctum

php artisan install:api    # Laravel 12 : génère routes/api.php + config sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
```

```php
// app/Http/Kernel.php ou bootstrap/app.php (Laravel 12 style)
// Ajouter EnsureFrontendRequestsAreStateful dans le groupe API
```

#### A3. Structure des dossiers

```bash
# Créer la structure cible
mkdir -p app/Http/Controllers/Api/V1/{Auth,Dashboard,Clients,Products,Stock,Sales,Documents,CashVouchers,Permissions,Ged,Users}
mkdir -p app/Http/Requests/{Auth,Client,Product,Stock,Sale,Document,CashVoucher,Permission,Ged,User}
mkdir -p app/Http/Resources/Api/V1
mkdir -p app/Policies
```

#### A4. Refactoring DocumentService (corrections critiques)

```php
// AVANT (bug) :
// $line->type_code = $typeCode;   ← $typeCode n'existe pas dans ce scope

// APRÈS :
$line->type_code = $document->type_code;

// Extraire calculerMontants() et generateReference() de DocumentController vers DocumentService
// Créer DocumentService::calculerMontants(array $lines, float $remise, float $tva): array
// Créer DocumentService::generateReference(DocumentType $type): string
```

#### A5. Créer Policies de base

```bash
php artisan make:policy ClientPolicy --model=Client
php artisan make:policy ProductPolicy --model=Product
php artisan make:policy DocumentPolicy --model=Document
php artisan make:policy SalePolicy --model=Sale
php artisan make:policy CashVoucherPolicy --model=CashVoucher
php artisan make:policy PermissionPolicy --model=Permission
php artisan make:policy UserPolicy --model=User
```

#### A6. Route API skeleton

```php
// routes/api.php — squelette vide mais structuré
// Toutes les routes retournent 501 Not Implemented jusqu'à leur implémentation
Route::prefix('v1')->group(function () {
    // ... toutes les routes définies dans ARCHITECTURE.md
});
```

#### A7. Migrations de normalisation

```bash
php artisan make:migration create_categories_table
php artisan make:migration create_suppliers_table
php artisan make:migration add_category_id_to_products_table
php artisan make:migration add_supplier_id_to_products_table
# Note : garder les colonnes category/supplier texte jusqu'à data migration validée
```

**Critères de sortie Phase A :**
- [ ] Laravel 12 tourne sans erreur
- [ ] Sanctum installé et configuré
- [ ] `DocumentService` sans bug `$typeCode`
- [ ] `calculerMontants()` et `generateReference()` dans le service
- [ ] Policies créées (même si vides)
- [ ] Structure de dossiers en place
- [ ] `php artisan test` passe

---

### PHASE B — Shell React + Auth

**Durée estimée : 4-5 jours**

#### B1. Initialiser le projet React

```bash
cd frontend
npm create vite@latest . -- --template react-ts
npm install

# Dépendances principales
npm install axios @tanstack/react-query react-router-dom
npm install zustand react-hook-form zod @hookform/resolvers
npm install recharts date-fns lucide-react
npm install sonner               # Toasts
npm install clsx tailwind-merge  # cn() utility

# shadcn/ui
npx shadcn@latest init
npx shadcn@latest add button card dialog badge input label table
npx shadcn@latest add select textarea dropdown-menu tooltip
npx shadcn@latest add separator skeleton alert

# Tailwind
npm install -D tailwindcss postcss autoprefixer @tailwindcss/forms
npx tailwindcss init -p
```

#### B2. Design System (tokens + composants de base)

```bash
# Appliquer DESIGN-SYSTEM.md :
# 1. tailwind.config.ts avec couleurs brand + accent
# 2. CSS variables dans index.css
# 3. Composants ui/ : Button, Badge, Card, DataTable, PageHeader, EmptyState, ErrorState
# 4. Layout : AppLayout, Sidebar, Topbar
```

#### B3. Routing React

```tsx
// src/main.tsx
<BrowserRouter>
  <QueryClientProvider client={queryClient}>
    <RouterProvider router={router} />
  </QueryClientProvider>
</BrowserRouter>

// src/router.tsx
const router = createBrowserRouter([
  { path: '/login', element: <LoginPage /> },
  {
    path: '/',
    element: <RequireAuth><AppLayout /></RequireAuth>,
    children: [
      { index: true, element: <Navigate to="/dashboard" /> },
      { path: 'dashboard', element: <DashboardPage /> },
      { path: 'clients', element: <ClientsPage /> },
      // ...
    ]
  }
]);
```

#### B4. Auth Sanctum

```tsx
// api/authApi.ts
export const authApi = {
  getCsrfCookie: () => axiosBase.get('/sanctum/csrf-cookie'),
  login: (credentials) => api.post('/auth/login', credentials),
  logout: () => api.post('/auth/logout'),
  me: () => api.get('/auth/me'),
};

// stores/authStore.ts (Zustand)
// hooks/useAuth.ts — hook d'accès au store
// components/RequireAuth.tsx — route guard
```

#### B5. Page de login

```
Design de la page login :
- Fond split : gauche bleu marine (#1e2c54) avec logo ICAVI grand format
- Droite blanc : formulaire centré
- Formulaire : email + password + bouton "Se connecter"
- Validation Zod + React Hook Form
- Gestion erreur 422 (identifiants incorrects)
```

#### B6. Layout principal

```
Implémentation du layout AppLayout :
- Sidebar avec navigation par module et rôle
- Topbar avec breadcrumb + notifs + user menu
- Slot de contenu
- Responsive (sidebar overlay sur tablet)
```

**Critères de sortie Phase B :**
- [ ] `npm run dev` démarre sans erreur
- [ ] Login → session Sanctum → `GET /api/v1/auth/me` renvoie l'utilisateur
- [ ] Logout détruit la session
- [ ] Layout principal visible avec navigation
- [ ] Route guard redirige vers `/login` si non authentifié
- [ ] Design System appliqué (couleurs, typo, composants de base)

---

### PHASE C — Modules critiques

**Durée estimée : 7-10 jours**

#### C1. Dashboard

**Backend :**
```php
// GET /api/v1/dashboard
// Migrer DashboardController::index() → DashboardController (Api/V1)
// Retourner JSON avec : stats, monthly_chart, recent_sales, low_stock, recent_movements, follow_ups
// Adapter selon le rôle (cash vouchers section)
// Ajouter Cache::remember() avec TTL 5 min
```

**Frontend :**
```tsx
// features/dashboard/
// StatCard x4 (clients, produits, stock faible, CA)
// Graphique mensuel Recharts (LineChart + année selector)
// 3 petits tableaux (ventes récentes, stocks faibles, relances)
// CashVoucher section conditionnelle (selon abilities)
```

#### C2. Gestion du stock

**Backend :**
```php
// GET  /api/v1/stock           → Liste paginée avec filtres
// POST /api/v1/stock           → StockService::enregistrerMouvement()
// GET  /api/v1/products/search → Autocomplete (pour saisie mouvement)
```

**Frontend :**
```tsx
// Page stock : tableau + filtres (catégorie, seuil critique)
// Indicateurs visuels : barre de progression colorée
// Modal "Entrée/Sortie de stock" avec formulaire simplifié
// Badge statut : OK / FAIBLE / CRITIQUE
```

#### C3. Documents (module le plus complexe)

**Backend :**
```php
// apiResource('documents') — CRUD complet
// POST /documents/{id}/status           → changer le statut
// POST /documents/{id}/generate-bl      → DocumentService::genererBlDepuisFacture()
// POST /documents/{id}/generate-invoice → DocumentService::genererFactureDepuisProforma()
// GET  /documents/{id}/pdf             → DomPDF
// Workflow suppression : request / approve / reject
```

**DomPDF — remplacement Chrome :**
```php
// resources/views/print/document.blade.php
// Adapter le CSS existant pour DomPDF (inline styles, pas de Flexbox CSS pur, pas de JS)
// Font : DejaVu Sans (incluse DomPDF) ou charger Inter en base64
// Inclure watermark : background-image en base64
// Cachet et signature : <img> en base64 (chargés côté serveur uniquement)
```

**Frontend :**
```tsx
// DocumentsPage : tableau avec filtres type + statut + client + date
// DocumentDetailPage : vue complète + actions contextuelles selon statut
// DocumentCreatePage : formulaire avec lignes (ajout/suppression dynamique)
// DocumentStatusBadge : badge coloré par statut
// Bouton PDF → téléchargement direct via window.open()
```

#### C4. Produits + Catégories

**Backend + Frontend pour :**
```
- CRUD produits avec category_id (normalisé)
- CRUD catégories
- Indicateurs stock sur chaque produit
- Filtre par catégorie
```

**Critères de sortie Phase C :**
- [ ] Dashboard affiche les stats correctes
- [ ] Mouvements de stock enregistrés (lockForUpdate préservé)
- [ ] Documents CRUD + changements de statut fonctionnels
- [ ] Chaîne PRO → FAC → BL générée correctement
- [ ] PDF documents téléchargeable (DomPDF, pas Chrome)
- [ ] Bug `$typeCode` corrigé et vérifié
- [ ] Workflow suppression avec approbation admin

---

### PHASE D — Modules secondaires

**Durée estimée : 5-7 jours**

#### D1. Clients

```php
// CRUD complet + pagination
// Client follow-ups : liste + création (relances)
// Stats par client (orders, revenue) depuis la vue existante
// Filtre par statut (prospect / actif / inactif)
```

#### D2. Ventes (Sales)

```php
// CRUD Sales avec SoftDeletes
// Liaison Sale → Document (has one)
// Génération document depuis vente (DocumentService::genererDepuisVente)
// Changement de statut (draft → sent → accepted → refused)
// Annulation avec motif
```

#### D3. GED (Gestion Électronique des Documents)

```php
// Upload fichiers (validation type + taille)
// Création dossiers
// Téléchargement, suppression avec soft-delete
// Restauration
// Permissions : accès selon rôle
```

**Critères de sortie Phase D :**
- [ ] CRUD clients fonctionnel avec follow-ups
- [ ] Ventes créables et liées aux documents
- [ ] GED : upload, téléchargement, suppression fonctionnels

---

### PHASE E — Modules spéciaux

**Durée estimée : 5-7 jours**

#### E1. Bons de caisse (CashVouchers)

```php
// Workflow : EN_ATTENTE → PAYÉ → REÇU_CONFIRMÉ | ANNULÉ
// Création : commercial + manager
// Paiement : manager uniquement
// Signature : user dont id = config('cash_vouchers.signer_user_id')
// PDF signé : cachet + signature (images chargées côté serveur, jamais exposées)
// PDF DomPDF (ex Chrome headless)
```

```env
# .env
CASH_VOUCHER_SIGNER_USER_ID=4
# Supprimer CASH_VOUCHER_CHROME_PATH (plus utilisé)
```

#### E2. Permissions (absences)

```php
// Création : tous les employés
// Approbation/Refus : directeur (is_directeur = true)
// PDF de la permission approuvée (DomPDF)
// Notification email à l'employé sur décision
```

#### E3. Notifications

```php
// GET /api/v1/notifications       → liste non-lues
// POST /api/v1/notifications/mark-read → marquer lues
// Notifications existantes (table notifications Laravel) → exposées via API
// Cloche dans la topbar React avec badge compteur
```

#### E4. Gestion des utilisateurs (admin)

```php
// CRUD users (super_admin et admin uniquement)
// Changement de mot de passe
// Attribuer rôle + flag is_directeur
```

**Critères de sortie Phase E :**
- [ ] Bons de caisse complets avec PDF signé (DomPDF)
- [ ] Permissions avec workflow directeur
- [ ] Notifications affichées dans la topbar
- [ ] CRUD utilisateurs admin

---

### PHASE F — Finalisation et déploiement

**Durée estimée : 3-5 jours**

#### F1. Cleanup

```bash
# Supprimer les anciennes vues Blade (sauf print/)
rm -rf resources/views/{auth,clients,dashboard,documents,ged,permissions,products,sales,stock,users,cash-vouchers,layouts}
# Garder uniquement resources/views/print/ (pour DomPDF)

# Nettoyer les routes web (garder uniquement SPA catch-all)
# Supprimer CheckRole middleware
# Supprimer les anciens controllers Blade
```

#### F2. Optimisations production

```bash
# Backend
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan optimize

# Vérifier les index SQL manquants
# products.category_id, stock_movements.product_id + created_at
# documents.type_code + status + client_id + date_document
php artisan migrate

# Frontend
npm run build
```

#### F3. .htaccess et déploiement OVH

```apache
# public/.htaccess final (voir ARCHITECTURE.md section 10)
# React static → fallback index.html
# /api/* → Laravel PHP
# /sanctum/* → Laravel PHP
```

#### F4. Configuration cron OVH

```
Interface OVH → Hébergement → Tâches CRON :
Commande : php /home/icavi/backend/artisan schedule:run
Fréquence : toutes les heures (minimum mutualisé OVH)
```

#### F5. Variables d'environnement production

```env
# .env production (à mettre à jour manuellement sur OVH)
APP_ENV=production
APP_DEBUG=false
APP_KEY=[générer avec php artisan key:generate]
APP_URL=https://gst.icavi-secure.com

SESSION_DOMAIN=.icavi-secure.com
SANCTUM_STATEFUL_DOMAINS=gst.icavi-secure.com
SESSION_DRIVER=file
SESSION_SECURE_COOKIE=true
SESSION_SAME_SITE=lax

CACHE_STORE=file
QUEUE_CONNECTION=sync

MAIL_MAILER=smtp
MAIL_HOST=ssl0.ovh.net
MAIL_PORT=465
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS=gestion@icavi-secure.com
MAIL_FROM_NAME="ICAVI GESTION"
# MAIL_PASSWORD → variable secrète, ne jamais committer

CASH_VOUCHER_SIGNER_USER_ID=4
RAYMOND_EMAIL=kayliah.tsongwang@icavi-secure.com
```

#### F6. Tests de recette

```
Checklist recette finale :
[ ] Login / Logout
[ ] Dashboard stats correctes
[ ] Créer un client → créer une vente → générer un PRO → valider → générer FAC → générer BL
[ ] Entrée de stock + vérification compteur
[ ] PDF document (DomPDF) téléchargeable
[ ] Bon de caisse : créer → payer → confirmer réception → PDF signé
[ ] Permission : créer → directeur approuve → PDF
[ ] Upload GED → téléchargement
[ ] CRUD utilisateurs (admin)
[ ] Notifications : badge dans topbar
[ ] Responsive tablet (768px)
[ ] Déconnexion expirée redirige vers login
[ ] APP_DEBUG=false → pas de stack trace exposée
```

---

## 7. STRATÉGIE DE ROLLBACK

### Niveau base de données
Les migrations du nouveau projet n'effacent aucune donnée existante. En cas de problème :
```bash
php artisan migrate:rollback --step=N
```

### Niveau application
L'ancienne application Blade reste disponible sur la branche `main-legacy` (tag `v1-last-blade`).

### Niveau déploiement OVH
Conserver une copie de l'ancien `public/` dans `/home/icavi/backup_v1/`. En cas d'urgence, remplacer `www/` par `backup_v1/`.

---

## 8. RISQUES ET MITIGATIONS

| Risque | Probabilité | Impact | Mitigation |
|---|---|---|---|
| DomPDF rendu différent des PDFs actuels | Moyen | Moyen | Valider les PDFs avec le client avant mise en prod |
| CORS / Sanctum cookies mal configurés sur OVH | Moyen | Haut | Tester avec un sous-domaine de staging d'abord |
| Perte de données lors des migrations catégories/fournisseurs | Faible | Haut | Script de migration data avec vérification avant suppression des colonnes |
| Cron OVH avec chemin PHP différent | Moyen | Moyen | Vérifier le chemin PHP avec le support OVH avant déploiement |
| Performance (pas de Redis, pas de queue) | Moyen | Moyen | Cache fichier + sync queue suffisants pour le volume actuel |
| Timeout téléchargement PDF lourd | Faible | Faible | DomPDF timeout configurable + cache PDF si besoin |

---

## 9. ORDRE DES TÂCHES (RÉCAPITULATIF)

```
Semaine 1 :
├── [A1] Laravel 11 → 12 + mise à jour composer
├── [A2] Installer Sanctum + configurer
├── [A3] Structure dossiers
├── [A4] Corriger bug $typeCode + refactoring DocumentService
├── [A5] Créer Policies
├── [A6] Route API skeleton
└── [A7] Migrations catégories + fournisseurs

Semaine 2 :
├── [B1] Initialiser projet React + Vite + TypeScript
├── [B2] Design System (tokens Tailwind + composants de base)
├── [B3] Routing React
├── [B4] Auth Sanctum côté React
├── [B5] Page login
└── [B6] Layout principal (sidebar + topbar)

Semaines 3-4 :
├── [C1] Dashboard (backend + frontend)
├── [C2] Stock (backend + frontend)
├── [C3] Documents (backend + frontend + DomPDF)
└── [C4] Produits + Catégories

Semaine 5 :
├── [D1] Clients + Follow-ups
├── [D2] Ventes
└── [D3] GED

Semaine 6 :
├── [E1] Bons de caisse
├── [E2] Permissions
├── [E3] Notifications
└── [E4] Gestion utilisateurs

Semaine 7 :
├── [F1] Cleanup Blade / routes web
├── [F2] Optimisations production
├── [F3] .htaccess + déploiement OVH
├── [F4] Cron OVH
├── [F5] Variables d'environnement production
└── [F6] Tests de recette + go-live
```

---

## 10. ENVIRONNEMENT DE DÉVELOPPEMENT LOCAL

```bash
# Terminal 1 : Backend Laravel
cd backend
cp .env.example .env
php artisan key:generate
php artisan migrate --seed
php artisan serve    # http://localhost:8000

# Terminal 2 : Frontend React
cd frontend
cp .env.example .env.local
# VITE_API_URL=/api/v1  (proxy vite → port 8000)
npm install
npm run dev          # http://localhost:5173

# Base de données locale
# MySQL local avec dump de icaviss3 (données de dev/test)
# Ne jamais utiliser la base de production en développement
```

### `.env.local` (frontend)

```env
VITE_APP_NAME="ICAVI GESTION (DEV)"
# Pas de VITE_API_URL nécessaire si proxy Vite configuré
```

### `.env` (backend local)

```env
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost:8000

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=icavi_dev
DB_USERNAME=root
DB_PASSWORD=

SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost:5173,localhost:8000

QUEUE_CONNECTION=sync
CACHE_STORE=file
```
