# Contrôleur `impression_pdf_controller` — impression d'un lot de fiches (BL / pesées)

## Objectif

Exposer une route API qui reçoit un **tableau JSON de fiches à imprimer**
(format imposé par l'appelant externe, ex. automate de centrale) et, pour
chacune :

1. retrouve la fiche correspondante (BL ou pesées d'un BL) ;
2. (re)génère son PDF ;
3. lance l'impression de ce PDF via `impression_pdf_service` (SumatraPDF).

- **Fichier** : `app/Http/Controllers/impression_pdf_controller.php`
- **Route** : `routes/api.php`
- **Services** : `App\Services\bl_demat_service`, `App\Services\pesee_pdf_service`, `App\Services\impression_pdf_service`

## Route

```
POST /impression_pdf/imprimer   (nom de route : impression_pdf.imprimer)
```

Pas de middleware d'authentification sur cette route (retiré du groupe dans
`routes/api.php`).

## Format de la requête

Le corps de la requête est **un tableau JSON**, chaque élément décrivant une
fiche à imprimer :

```json
[
    {
        "centrale_code": "01",
        "fichier_id": 3,
        "fichier_no_exec": "123456",
        "fichier_type": 0
    }
]
```

| Champ              | Type          | Requis | Description                                                                 |
|---------------------|---------------|--------|-------------------------------------------------------------------------------|
| `fichier_type`       | integer       | oui    | `0` = BL dématérialisé, `1` = pesées du BL.                                   |
| `fichier_no_exec`    | int ou string | oui    | Valeur de `t_bl.bl_no_exec` (converti en entier). Pour `fichier_type=1`, sert à retrouver l'`id_bl` (même BL) dont on imprime les pesées — **pas** `t_pesee.pesee_no_exec`. |
| `fichier_id`         | —             | non    | Reçu et renvoyé tel quel dans la réponse, non utilisé dans la logique.        |
| `centrale_code`      | string        | non    | Reçu et renvoyé tel quel dans la réponse ; **pas encore utilisé** pour filtrer la recherche (mis de côté pour l'instant, cf. Limites). |

Le tableau peut contenir une ou plusieurs fiches ; chacune est traitée
indépendamment (une fiche en erreur n'empêche pas le traitement des autres).

## Déroulé

1. Décodage du corps brut de la requête (`json_decode($request->getContent(), true)`,
   indépendant du header `Content-Type`). Si ce n'est pas un tableau : réponse
   `422`.
2. Validation de chaque élément (`fichier_type` entier dans `[0, 1]`,
   `fichier_no_exec` requis). En cas d'échec : réponse `422` avec le détail
   des erreurs.
3. Pour chaque fiche, selon `fichier_type` :
   - **`0` (bl)** :
     1. Recherche du BL : `bl::where('bl_no_exec', $no_exec)->first()`. Si
        introuvable : résultat en erreur pour cette fiche (`success: false`).
     2. Génération du PDF dématérialisé : `bl_demat_service::genererPdfPourBl($bl)`
        (§ [`bl_demat_service`](bl_demat_service.md)). Régénère systématiquement
        le PDF (données à jour) et met à jour `bl_lien_pdf`.
     3. Impression du PDF généré : `impression_pdf_service::imprimerPdf($chemin, env('IMPRIMANTE'))`.
   - **`1` (pesee)** :
     1. Résolution de l'`id_bl` : `bl::where('bl_no_exec', $no_exec)->value('id_bl')`.
     2. Génération du PDF des pesées et enregistrement sur disque :
        `pesee_pdf_service::genererPdfPourBl($idBl)` (§ [service partagé](#service-pesee_pdf_service)
        ci-dessous).
     3. Impression du PDF généré : `impression_pdf_service::imprimerPdf($chemin, env('IMPRIMANTE'))`.
4. Réponse JSON : **tableau de résultats**, un par fiche reçue, dans le même
   ordre. Statut HTTP toujours `200` au niveau de la requête globale ; le
   succès/échec de chaque fiche est porté par son propre champ `success`.

## Réponse

```json
[
    {
        "fichier_id": 3,
        "centrale_code": "01",
        "success": true,
        "type": "bl",
        "bl_code": "000123",
        "no_exec": 123456,
        "pdf": {
            "success": true,
            "path": "C:/.../storage/app/public/pdf_demat/123.pdf",
            "url": "http://localhost/storage/pdf_demat/123.pdf",
            "id_bl": 123
        },
        "impression": {
            "success": true,
            "code": 0,
            "output": []
        },
        "errors": "",
        "message": "Impression lancée avec succès."
    }
]
```

Pour `fichier_type: 1`, `pdf` est celui de `pesee_pdf_service::genererPdfPourBl()`
(chemin `pdf_pesee/{id_bl}.pdf`).

Chaque élément porte systématiquement les champs **`errors`** puis
**`message`** (dans cet ordre) ; le sous-objet `impression` garde `success`,
`code` et `output` (`output` = sortie brute de SumatraPDF) mais ne porte
**plus** de champ `message` propre, pour ne pas dupliquer l'information :
- `errors` :
  - `success: true` → chaîne vide `""` ;
  - `success: false` → **tableau** : la sortie de SumatraPDF
    (`impression.output`) quand l'échec vient de l'impression elle-même
    (`impression.success = false`), sinon (sortie vide, ou échec avant
    l'impression : BL introuvable, échec de génération du PDF) un tableau à
    un seul élément contenant le message d'erreur.
- `message` : toujours présent (succès ou échec), reprend le message
  d'origine (celui de `impression_pdf_service::imprimerPdf()` une fois
  l'impression tentée, ou le message d'erreur avant impression).

En cas d'erreur sur une fiche (BL introuvable, échec de génération ou
d'impression), son élément du tableau a `success: false`, un champ `message`
précisant la cause et `errors` renseigné comme ci-dessus (`pdf`/`impression`
absents si l'échec survient avant l'impression).

## Exemple d'appel

```bash
curl -X POST https://<host>/impression_pdf/imprimer \
  -H "Content-Type: application/json" \
  -d '[{"centrale_code":"01","fichier_id":3,"fichier_no_exec":"123456","fichier_type":0}]'
```

## Service `pesee_pdf_service`

- **Fichier** : `app/Services/pesee_pdf_service.php`.
- Extrait de `pesee_controller::imprimer_pdf()` (qui construisait un
  `Spipu\Html2Pdf\Html2Pdf` puis l'envoyait directement en téléchargement
  navigateur, mode `'D'`) pour pouvoir être **partagé entre les deux
  contrôleurs** qui en ont besoin :
  - `pesee_controller::imprimer_pdf($blId, pesee_pdf_service $service)` —
    appelle `construireHtml2Pdf($blId)` et garde le comportement d'origine
    (téléchargement navigateur, mode `'D'`).
  - `impression_pdf_controller::imprimerPesee()` — appelle
    `genererPdfPourBl($idBl)`, qui réutilise `construireHtml2Pdf()` puis
    enregistre le PDF sur le disque `public` (`pdf_pesee/{id_bl}.pdf`, mode
    `'F'`) et retourne son chemin, pour transmission à
    `impression_pdf_service::imprimerPdf()`.
- Sans cette extraction, la requête complexe (5 sous-requêtes sur
  `t_pesee`/`t_bl`/`t_produit`/...) et la construction du PDF auraient été
  dupliquées entre les deux contrôleurs.

## Dépendances

- `App\Services\bl_demat_service` (génération du PDF BL, cf.
  [`docs/bl_demat_service.md`](bl_demat_service.md)).
- `App\Services\pesee_pdf_service` (génération du PDF des pesées d'un BL,
  partagé avec `pesee_controller`, voir ci-dessus).
- `App\Services\impression_pdf_service` (impression via SumatraPDF, variable
  d'environnement `SUMATRA_PATH` requise sur le serveur ; `IMPRIMANTE` pour
  l'imprimante par défaut ; appel borné à 30 s, voir ci-dessous).

## Timeout d'impression (`impression_pdf_service`)

L'appel à SumatraPDF (`Process::timeout(30)->run($commande)`, Laravel
Process/Symfony Process — plus de `exec()` brut) est borné à **30 secondes**.
Si SumatraPDF ne rend pas la main dans ce délai (imprimante introuvable ou
hors ligne, boîte de dialogue Windows bloquante...), le process est tué et
`imprimerPdf()` retourne `success: false` avec un message dédié (« Délai
d'impression dépassé... ») au lieu de bloquer la requête indéfiniment.
`output` reste alimenté avec ce qui a pu être capturé (stdout + stderr) avant
l'arrêt forcé.

## Points de vigilance

- `bl_no_exec` est incrémenté globalement (`bl::max('bl_no_exec') + 1`, voir
  `helpers/Modele_helpers.php`) : la colonne est donc considérée unique en
  pratique, mais aucune contrainte d'unicité n'existe en base — en cas de
  doublon, `first()`/`value()` retourne le premier BL trouvé.
- Le PDF (BL ou pesées) est régénéré à chaque appel (pas de vérification d'un
  fichier déjà présent sur disque), afin de garantir l'impression des données
  les plus récentes.
- L'imprimante utilisée est toujours celle par défaut du serveur
  (`env('IMPRIMANTE')`) — le format de requête actuel ne transporte pas
  d'imprimante par fiche.

## Limites connues

- `centrale_code` n'est **pas utilisé** pour filtrer/sécuriser la recherche
  du BL (mis de côté volontairement pour l'instant) : seule `fichier_no_exec`
  (→ `bl_no_exec`) identifie le BL. À ajouter si `bl_no_exec` s'avère un jour
  ambigu entre centrales.
- `fichier_id` n'a aucun rôle métier actuellement : simplement reçu et
  renvoyé dans la réponse.
