# Module "Demande de devis" — SenOffre Devis (application séparée)

**Architecture retenue : application Laravel indépendante, connectée à la même base
de données que SenOffre.** SenOffre est bâti sur le script commercial **JobClass**
(LaraClassifier) — ce module a été adapté à sa vraie structure de base de données
réelle (tables `companies`, `categories`, champ `is_admin`), découverte en cours de
déploiement. Le fournisseur/client garde son compte SenOffre unique (username, mot
de passe, téléphone, email) — ce module s'appuie sur la table `companies` déjà
existante (profil entreprise) et ajoute juste `company_accreditations` par-dessus
pour les infos de vérification (NINEA, RC, statut), **sans jamais modifier les
tables d'origine de JobClass**.

## ⚠️ Point à vérifier avant d'aller plus loin : format du champ `categories.name`

Le champ `name` de la table `categories` existante est de type `text` en base — sur
les scripts Laravel comme JobClass, ce genre de champ est souvent stocké en JSON
multilingue (ex: `{"fr":"BTP","en":"Construction"}`) via un package de traduction.
Le modèle `Category` fourni ici suppose un cast `array`, mais **il faut vérifier
la vraie valeur stockée** avant de faire confiance à l'affichage :

```bash
php artisan tinker --execute="print_r(\App\Models\Category::first()->getRawOriginal('name'));"
```

Si le résultat ressemble à `{"fr":"...", ...}`, le cast `array` est correct et il
faudra afficher `$category->name['fr']` dans les vues (pas juste `$category->name`).
Si c'est du texte simple, retirer le cast `array` du modèle `Category`.

## Pourquoi cette architecture

- **Code isolé** : ce module est un projet Laravel à part entière, avec son propre
  dépôt/déploiement — s'il plante, ça ne touche jamais la plateforme SenOffre principale
- **Compte unique** : même base de données MySQL que SenOffre → pas de compte à recréer
- **Connexion transparente** : en partageant le domaine de session (`SESSION_DOMAIN`),
  un utilisateur déjà connecté sur senoffre.com reste connecté en arrivant sur
  devis.senoffre.com — pas besoin de ressaisir ses identifiants
- **Bouton simple** : SenOffre n'a qu'un lien `<a href="https://devis.senoffre.com">`
  à ajouter, rien d'autre à modifier côté plateforme principale
- **Aucune modification des tables JobClass d'origine** : `companies`, `categories`,
  `users` restent intactes — le module ajoute seulement ses propres tables, reliées
  par clé étrangère

## Mise en place — récapitulatif (déjà en cours sur ton hébergement OVH)

1. ✅ Projet Laravel créé dans `~/devis` (`composer create-project laravel/laravel devis`)
2. ✅ Sous-domaine `devis.senoffre.com` pointant vers `devis/public`
3. `.env` de `~/devis` configuré avec les mêmes `DB_*` et `APP_KEY` que `~/www/.env`,
   plus `SESSION_DOMAIN=.senoffre.com` (ajouté aussi dans `~/www/.env`)
4. Copier les fichiers de ce livrable dans `~/devis` :

```bash
cp database/migrations/*.php   ~/devis/database/migrations/
cp app/Models/*.php             ~/devis/app/Models/
cp app/Http/Controllers/*.php   ~/devis/app/Http/Controllers/
cp app/Notifications/*.php      ~/devis/app/Notifications/
cp -r resources/views/devis     ~/devis/resources/views/
cp routes/devis.php             ~/devis/routes/web.php
```

Le modèle `User.php`, `Company.php` et `Category.php` fournis dans `app/Models/`
**remplacent** ceux générés par défaut par `laravel new` — c'est normal et voulu.

### 5. IMPORTANT — supprimer les migrations par défaut de Laravel

Comme la base est partagée avec SenOffre, les tables `users`, `password_reset_tokens`,
`sessions`, `cache`, `jobs` existent déjà. Il faut supprimer les migrations par
défaut générées par `laravel new` avant de migrer, pour ne garder que les migrations
spécifiques à ce module :

```bash
cd ~/devis/database/migrations
ls
```

Supprime tous les fichiers dont le nom commence par `0001_01_01_...` (migrations
par défaut de Laravel), en gardant uniquement les 4 fichiers `2026_08_04_...`
fournis dans ce livrable.

```bash
cd ~/devis
php artisan migrate
```

### 6. Déploiement sur sous-domaine

Déjà fait — `devis.senoffre.com` pointe vers `devis/public`.

### 7. Ajouter le bouton sur SenOffre

Dans la navigation de la vraie application `~/www` (fichiers de vue JobClass) :

```html
<a href="https://devis.senoffre.com">Demander un devis</a>
```

## Reste identique à avant

- La logique métier (workflow devis → BC) et les points d'intégration Phase 2
  (paiement) sont **inchangés**. Les Policies Laravel ne sont plus nécessaires —
  remplacées par des vérifications directes (`abort_unless`) dans les contrôleurs,
  plus simples et cohérentes avec le style JobClass (`is_admin` booléen direct).
- Vues encore à créer, sur le modèle de `comparaison.blade.php` :
  `devis/creer.blade.php`, `devis/mes-demandes.blade.php`, `devis/bon-de-commande.blade.php`,
  `devis/agrement/demander.blade.php`, `devis/agrement/admin-liste.blade.php`

## Logique métier — résumé

1. Client crée une demande de devis (`DevisRequest`) rattachée à une `category_id`
   (catégorie réelle du site, pas un texte libre)
2. Le système notifie tous les utilisateurs dont la société (`companies`) a une
   fiche `CompanyAccreditation.statut = 'valide'` pour cette catégorie
3. Fournisseurs agréés soumettent leurs devis (`DevisOffer`) — bloqué si la société
   du fournisseur connecté n'est pas agréée (vérifié via `Auth::user()->company`)
4. Client compare et sélectionne un devis → transaction atomique qui :
   - marque le devis retenu comme `retenu`, les autres `non_retenu`
   - ferme la demande (`statut = 'attribuee'`)
   - génère automatiquement le BC avec numéro unique (`BC-2026-000123`)
5. Fournisseur livre → marque `statut_livraison = 'livre'`
6. Client confirme → `statut_livraison = 'confirme_client'` — **c'est cet événement
   qui déclenchera la libération des fonds en Phase 2**

## Prochaine étape : Phase 2 (paiement séquestre)

Une fois la réponse de PayDunya obtenue sur leur capacité "marketplace/sous-comptes",
il faudra :
1. Ajouter le champ de paiement à `PurchaseOrder` (référence transaction, statut détaillé)
2. Appeler l'API PayIn au moment où le client valide le devis (avant génération du BC,
   ou juste après selon le flow choisi avec PayDunya)
3. Appeler l'API PayOut dans `PurchaseOrderController::confirmerReception()`
   (voir le `// TODO Phase 2` déjà en place dans le code)
