# Service `bl_demat_service` — génération du PDF dématérialisé d'un BL

## Origine

Ce service est le portage Laravel du script procédural `public/pdftk/pdf.php`.
Il reprend **à l'identique** la logique métier de ce script (mêmes règles de
gestion, mêmes champs, mêmes coordonnées de calque) en la réécrivant avec les
outils Laravel : Eloquent / Query Builder à la place des requêtes PDO
concaténées, disque `public` à la place des constantes
`RACINE_SITE_MODELE_CLIENT` / `URL_MODELE_CLIENT`, exceptions à la place des
`echo` + `exit`.

- **Fichier** : `app/Services/bl_demat_service.php`
- **Namespace / classe** : `App\Services\bl_demat_service`
- **Script d'origine** : `public/pdftk/pdf.php`

## Ce que fait le service

Pour un bon de livraison (BL) donné :

1. Charge le BL et toutes les entités liées (centrale, client, chantier,
   commande, véhicule, chauffeur, transporteur, activité de la centrale).
2. Détermine le modèle de PDF (pdftk) à utiliser : celui de la centrale s'il
   est défini et présent sur disque, sinon celui des paramètres généraux
   (`t_parametre`).
3. Reconstitue les informations de formule béton (désignation, classes
   d'exposition/chlorure/consistance/résistance, liant, dosage...) ou de
   produit négoce, selon `t_activite.activite_type`.
4. Construit les listes « services », « ajouts » et « adjuvants » imprimées
   sur le BL, avec la même règle d'affichage des quantités (paramètre général
   ou surchargé au niveau centrale).
5. Remplit le formulaire PDF (`mikehaertl/php-pdftk`) avec l'ensemble de ces
   données puis l'aplatit (`flatten`).
6. Met à jour `t_bl.bl_lien_pdf` avec l'URL publique du PDF généré.
7. Génère, si un modèle de QR code est configuré sur le BL, l'image PNG du QR
   code (bibliothèque `phpqrcode`, non composerisée, incluse depuis
   `public/inc/phpqrcode/qrlib.php`).
8. Réimporte chaque page du PDF avec FPDI (`setasign/fpdi`) pour y incruster,
   selon les coordonnées définies au niveau centrale ou paramètres généraux :
   la signature du chauffeur, le pictogramme « NF barrée » (ajout d'eau sur
   formule normalisée) et le QR code.
9. Nettoie les fichiers temporaires (PDF intermédiaire, image QR code).

## Utilisation

```php
use App\Services\bl_demat_service;

$service = new bl_demat_service();

// À partir du code du BL (équivalent du paramètre GET "numero_bl" du script d'origine)
$resultat = $service->genererPdf($bl->bl_code);

// Ou directement à partir d'un modèle déjà chargé (évite une requête)
$resultat = $service->genererPdfPourBl($bl);

// $resultat = [
//     'success' => true,
//     'path'    => 'C:/.../storage/app/public/pdf_demat/1234.pdf',
//     'url'     => 'http://localhost/storage/pdf_demat/1234.pdf',
//     'id_bl'   => 1234,
// ]
```

En cas d'erreur (BL introuvable, échec pdftk), le service lève une
`\RuntimeException` au lieu d'afficher un message et de s'arrêter (`echo` +
`exit` dans le script d'origine). Exemple d'intégration dans un contrôleur :

```php
public function genererPdfDemat(Request $request, bl_demat_service $service)
{
    try {
        $resultat = $service->genererPdf($request->input('numero_bl'));

        return response()->json($resultat);
    } catch (\RuntimeException $e) {
        return response()->json(['success' => false, 'message' => $e->getMessage()], 422);
    }
}
```

## Correspondance des chemins de fichiers

| Script d'origine (`pdf.php`)                          | Service Laravel                                              |
|--------------------------------------------------------|----------------------------------------------------------------|
| `RACINE_SITE_MODELE_CLIENT` (`public/storage/`)         | `Storage::disk('public')->path('...')`                          |
| `URL_MODELE_CLIENT` (`http://localhost/storage/`)       | `Storage::disk('public')->url('...')`                            |
| `modele_pdf/…`                                          | `Storage::disk('public')->path('modele_pdf/…')`                  |
| `pdf_demat/{id_bl}.pdf`                                 | `Storage::disk('public')->path('pdf_demat/{id_bl}.pdf')`         |
| `image_signature/…`                                     | `Storage::disk('public')->path('image_signature/…')`             |
| `image/barrer_nf.png`                                   | `Storage::disk('public')->path('image/barrer_nf.png')`           |
| `../inc/phpqrcode/qrlib.php`                            | `base_path('public/inc/phpqrcode/qrlib.php')` (toujours inclus manuellement, non composerisé) |

Ces chemins pointent tous vers le même dossier physique
(`storage/app/public`, lié à `public/storage` par `php artisan storage:link`),
donc aucun fichier n'a besoin d'être déplacé pour que le service fonctionne.

## Dépendances

Toutes déjà présentes dans `composer.json` racine (aucune nouvelle
dépendance à installer) :

- `mikehaertl/php-pdftk` (remplissage de formulaire PDF)
- `setasign/fpdi` (+ `setasign/fpdf`, incrustation des calques)
- `public/inc/phpqrcode/qrlib.php` (génération du QR code — bibliothèque
  historique non gérée par Composer, incluse via `require_once`)

## Points de vigilance conservés du script d'origine

Le portage préserve volontairement certaines particularités du script
original plutôt que de les corriger silencieusement (un portage doit rester
fidèle au comportement existant ; ces points sont candidats à une correction
séparée, à valider avec le métier) :

- **`useTemplate()`** est appelé avec la coordonnée **X en guise de X *et*
  de Y** (`$cordonee_x_modele_pdf` passé deux fois) au lieu de X puis Y.
- **`bl_heure_dep_vidange`** est affiché alors que c'est le type
  **`bl_heure_deb_vidange_type`** qui est testé (au lieu de
  `bl_heure_deb_vidange`).
- Sur un BL de type « négoce » (`activite_type == 1`), le champ
  **`formule_code_facturation`** n'est jamais renseigné (il ne l'était pas
  non plus dans le script d'origine, où la variable correspondante restait
  non définie dans cette branche).
- Les requêtes sur `t_commune` (chantier / client) sont conservées bien
  qu'inutilisées dans le remplissage du formulaire, à l'identique du script
  d'origine.
- Le calcul de la zone du BL se base sur la ligne de BL de type « zone »
  (`ligne_bl_type_article = 4`), et **pas** sur `t_bl.zone_id` — c'est déjà
  ainsi que fonctionnait le script original.

## Différences volontaires (idiomatiques Laravel)

- Le service **retourne un tableau structuré ou lève une exception**, au
  lieu d'`echo`-er `'null'` (succès) ou un message d'erreur brut.
- Les jointures SQL brutes sont remplacées par des relations Eloquent
  (`bl->lier_centrale`, `formule->lier_designation_beton`, etc.) quand elles
  existaient déjà sur les modèles, et par le Query Builder (`DB::table()`)
  pour les jointures multi-tables ponctuelles (services, ajouts, adjuvants,
  liant) — ce qui supprime au passage les injections SQL du script d'origine
  (paramètres concaténés directement dans la chaîne SQL).
- La mise à jour de `bl_lien_pdf` se fait via une requête ciblée
  (`bl::where('id_bl', ...)->update([...])`) plutôt qu'un `save()` complet,
  pour ne modifier que cette colonne comme le faisait le `UPDATE` SQL
  d'origine.

## Limites connues / suite possible

- Le service ne gère pas l'exposition HTTP (route/contrôleur) : il est prévu
  pour être appelé depuis un contrôleur, un job en file d'attente, ou une
  commande Artisan, selon le besoin (l'exemple de contrôleur ci-dessus est
  fourni à titre indicatif, non branché dans les routes existantes).
- Le binaire `pdftk` doit rester disponible dans le `PATH` du serveur, comme
  pour le script d'origine.
