# Intégration GPS Trakkeo (Bon de livraison XML)

Échange de fichiers XML "Bon de livraison" avec le prestataire GPS **Trakkeo**, dans les deux sens :

- **Envoi** : à la création d'un BL, on génère un XML et on le dépose sur le FTP Trakkeo (dossier `/IN`), puis on notifie leur webhook.
- **Réception** : Trakkeo dépose en retour des XML enrichis des heures réelles (GPS) dans un dossier `/OUT/<societe_code_demat>` par société ; une tâche planifiée les récupère régulièrement et met à jour nos données.

Ce document couvre le port Laravel de l'ancien flux PHP procédural (`public/commande/planification_commande/bon_livraison_xml_gps.php` et `public/integration_fichier_xml/*.php`).

## Vue d'ensemble

```
                          ENVOI
┌─────────────────┐   génère XML    ┌──────────────┐   FTP /IN    ┌──────────┐
│ Création d'un BL │ ─────────────> │ bon_livraison_ │ ──────────> │ Trakkeo  │
│ (BL controller,   │                │ xml_gps_service│             │  FTP     │
│  à câbler)         │                │                │  webhook   │          │
└─────────────────┘                 └──────────────┘ ──────────> │ (nouveau-bl)│
                                                                    └──────────┘

                          RÉCEPTION
┌──────────┐  liste  ┌────────────────────────────┐  télécharge+  ┌─────────────┐
│ Trakkeo  │ <────── │ tache:tache_recuperer_      │  supprime     │ integration_ │
│  FTP     │ ──────> │ fichier_xml_ftp (planifiée) │ ────────────> │ fichier_xml_ │
│ /OUT/<sc>│ fichiers│  boucle sur chaque société   │                │ bl_gps_service│
└──────────┘          └────────────────────────────┘                └─────────────┘
                                                                            │
                                                          met à jour t_bl, t_planning,
                                                          t_chantier (position GPS)
```

## Envoi — `bon_livraison_xml_gps_service`

**Fichier** : [app/Services/bon_livraison_xml_gps_service.php](../app/Services/bon_livraison_xml_gps_service.php)
**Source portée** : `public/commande/planification_commande/bon_livraison_xml_gps.php`

### API

```php
$service = app(\App\Services\bon_livraison_xml_gps_service::class);

// Construit le XML sans effet de bord (pas d'écriture disque, pas de réseau) — pratique pour tester.
$document = $service->construireXml($numero_bl); // ?array{nom_fichier, xml, id_trakkeo}

// Construit + écrit le fichier local + envoie sur le FTP Trakkeo + notifie le webhook.
$succes = $service->envoyer($numero_bl); // bool
```

### Ce qui n'est PAS encore fait

- **Aucune route/contrôleur n'appelle `envoyer()` pour l'instant.** L'ancien `bl_xml()` JS (dans `planification_commande_manuel.blade.php`) pointe encore vers l'ancien endpoint `public/commande/planification_commande/bon_livraison_xml.php` (un flux XML différent, non-GPS, non traité ici). Il reste à décider où/quand appeler `bon_livraison_xml_gps_service::envoyer()` (probablement à la création du BL, une fois que la génération automatique de BL sera réimplémentée côté planification — voir plus bas).
- La génération automatique de BL a été volontairement retirée de `planning_controller::of_manuel_valider()` / `of_auto_valider()` ("sera faite ailleurs" — cf. historique du projet). Tant qu'elle n'est pas réimplémentée, il n'y a pas de nouveau BL à envoyer automatiquement.

### Bugs corrigés lors du portage

| Bug dans le PHP legacy | Effet | Correction |
|---|---|---|
| `$formule['Designation_Beton_Libelle']` (casse incorrecte, vraie colonne `designation_beton_libelle`) | Le nœud XML `Designation_Normalisee` était **toujours vide** | Corrigé pour lire la vraie colonne |
| Boucle "adjuvant" lisait `$ajout['ligne_bl_facturable']` (variable de la boucle précédente) au lieu de `$adjuvant[...]` | Attribut `Facture` potentiellement faux sur les lignes adjuvant | Corrigé pour utiliser la bonne ligne |
| Identifiants FTP + token webhook en clair dans le code | Secrets versionnés | Déplacés dans `.env` / `config/services.php` (clé `trakkeo`) |

## Réception — tâche planifiée + service

**Commande** : [app/Console/Commands/tache_recuperer_fichier_xml_ftp.php](../app/Console/Commands/tache_recuperer_fichier_xml_ftp.php) (`tache:tache_recuperer_fichier_xml_ftp`)
**Service** : [app/Services/integration_fichier_xml_bl_gps_service.php](../app/Services/integration_fichier_xml_bl_gps_service.php)
**Sources portées** : `public/integration_fichier_xml/chercher_fichier_ftp_gps.php` (→ commande) et `public/integration_fichier_xml/integration_fichier_xml_bl_gps.php` (→ service)

### Fonctionnement

1. La commande boucle sur **toutes les sociétés** (`App\Models\societe::all()`) — voir "Bug corrigé" ci-dessous.
2. Pour chaque société, elle liste les fichiers du dossier `OUT/<societe_code_demat>` via `integration_fichier_xml_bl_gps_service::listerFichiers()`.
3. Pour chaque fichier trouvé, elle appelle `integrer()`, qui :
   - télécharge le fichier depuis le FTP Trakkeo et le supprime du serveur distant,
   - sauvegarde une copie locale dans `storage/app/fichier_xml_recuperer/`,
   - parse le XML et retrouve le BL/planning via `Numero` (= code producteur + `bl_code`),
   - recalcule les 6 heures du trajet (départ centrale, arrivée chantier, début/fin vidange, départ chantier, retour centrale), avec **priorité GPS optionnelle** (paramètre `parametre_integration_heure_gps_prioritaire`) sinon repli sur l'heure déjà saisie manuellement (`type == 2`) sinon calcul via les temps d'accès/déchargement de la commande — logique strictement identique à l'original,
   - met à jour `t_bl` et `t_planning` (jamais `t_planning_prev`, comme l'original),
   - trace l'événement dans `t_tracabilite_envoi` (table `T_BLHR`) si au moins une heure GPS a été utilisée,
   - met à jour la position GPS du chantier (`t_chantier.chantier_latitude/longitude`).

### Ce que j'ai changé par rapport à l'original

- **Boucle sur les sociétés** (demande explicite) : l'ancien PHP ne récupérait que la **première** ligne de `t_societe` (`_fetch()` sans boucle), ratant les dossiers `OUT/<code>` de toute société supplémentaire. Vérifié en base : il y a **4 sociétés** dans cette installation, donc c'était un vrai bug, pas une hypothèse. La commande boucle maintenant sur `societe::all()`.
- **Suppression de l'aller-retour HTTP interne** : l'ancien `chercher_fichier_ftp_gps.php` s'auto-appelait en HTTP (`file_get_contents` vers `integration_fichier_xml_bl_gps.php`) pour traiter chaque fichier — un artefact du style procédural PHP. La commande appelle directement le service en process, plus de requête HTTP superflue.
- **Bug de traçabilité multi-centrale corrigé** : voir l'encart ci-dessous, commun avec l'envoi côté planification.
- **Nettoyage** : le fetch de `t_parametre` ne récupère plus les colonnes `service_retour_beton_id`, `service_fibre_id`, `service_pompe_id`, `service_tapis_id` — présentes dans le SELECT d'origine mais jamais utilisées ensuite (code mort). La variable `$societe` (première société, elle aussi jamais utilisée dans le fichier d'intégration original) a été supprimée de même.
- Colonne `t_bl.bl__heure_arr_centrale_type` (double underscore) : **conservée telle quelle**, ce n'est pas une coquille de portage mais le vrai nom de colonne en base.

### ⚠️ Bug de traçabilité multi-centrale (découvert et corrigé pendant ce chantier)

`App\fonctions\tracabilite_envoi_fonction::ecrire_tracabilite_envoi()` est conçue pour synchroniser un **ensemble** centrale ↔ entité (ex : les centrales rattachées à un chantier ou un véhicule) : si on lui passe une collection d'UNE seule centrale, elle **marque automatiquement toutes les autres centrales actives en "Suppression"** pour cette entité — logique correcte pour une relation many-to-many, mais fausse pour une simple clé étrangère 1-vers-1 comme `t_planning.centrale_id` ou le BL concerné par une mise à jour GPS.

Impact : les premières versions de `planning_controller::of_manuel_valider()` et `of_auto_valider()` (et la version initiale de ce service) utilisaient cette fonction partagée avec une seule centrale, ce qui aurait créé une entrée `Suppression` parasite pour **chaque autre centrale active** à chaque tournée créée. Corrigé partout par un `tracabilite_envoi::create([...])` direct et ciblé, comme le faisait le SQL brut d'origine (un seul `INSERT`).

À retenir : ne jamais passer une collection à une seule centrale à `ecrire_tracabilite_envoi()`. Soit ne pas passer `$tab_centrale_select` du tout (broadcast à toutes les centrales actives, comportement par défaut voulu pour chantier/véhicule/formule), soit faire un insert direct comme ci-dessus pour un événement 1-vers-1.

## Configuration

Toutes les clés sous `config('services.trakkeo')`, alimentées par `.env` :

| Clé `.env` | Rôle | Défaut |
|---|---|---|
| `TRAKKEO_FTP_HOST` | Hôte FTP Trakkeo | `go.trakkeo.com` |
| `TRAKKEO_FTP_USERNAME` | Utilisateur FTP | `ftp_alfi` |
| `TRAKKEO_FTP_PASSWORD` | Mot de passe FTP | *(à renseigner)* |
| `TRAKKEO_FTP_ROOT_IN` | Dossier de dépôt pour l'envoi | `/IN` |
| `TRAKKEO_WEBHOOK_URL` | URL du webhook "nouveau BL" | `http://go.trakkeo.com:9000/hooks/nouveau-bl` |
| `TRAKKEO_WEBHOOK_TOKEN` | Token du webhook | *(à renseigner)* |

Le dossier de réception (`OUT/<societe_code_demat>`) est construit dynamiquement par société, pas configuré en dur.

**Note** : un disk Laravel `ftp_trakkeo` existe déjà dans `config/filesystems.php` (utilisé par les traits expérimentaux `App\Traits\copier_fichier_ftp_trakkeo` / `lire_fichier_xml`). Il n'est **pas réutilisé** ici : son mot de passe partage la variable d'environnement `FTP_PASSWORD` avec le disk `ftp` (mybodi), et sa racine par défaut (`/OUT/`) ne correspond pas à l'usage qu'on en fait ici (racine dynamique par société en réception, `/IN` en envoi). D'où les clés `TRAKKEO_*` dédiées.

## Activer la tâche planifiée

La commande existe et fonctionne (`php artisan tache:tache_recuperer_fichier_xml_ftp`), mais n'est **pas encore programmée** dans `app/Console/Kernel.php` — comme les autres tâches du projet (`tache:envoyer_facture_comptant_externe`, etc.), elle reste commentée par défaut :

```php
// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void
{
    // $schedule->command('tache:tache_recuperer_fichier_xml_ftp')->everyFiveMinutes();
}
```

À décommenter (et ajuster la fréquence) quand l'équipe est prête à activer la réception automatique.

## Tester sans effet de bord

- **Envoi** : `bon_livraison_xml_gps_service::construireXml($numero_bl)` ne touche ni au disque ni au réseau — utile pour vérifier le XML généré avant d'appeler `envoyer()`.
- **Réception** : `integration_fichier_xml_bl_gps_service::listerFichiers($dossier)` est en lecture seule (liste juste les fichiers). **`integrer()` en revanche télécharge, supprime le fichier du FTP distant et modifie `t_bl`/`t_planning` en base — irréversible côté FTP.** Ne pas l'appeler sur un fichier réel sans être certain de vouloir l'intégrer.

Au moment de la rédaction, le FTP Trakkeo contient **1198 fichiers en attente** dans `OUT/` (connexion vérifiée en lecture seule) — un vrai retard à traiter le jour où la tâche sera activée.

## Fichiers legacy correspondants

| Ancien fichier PHP | Nouveau code Laravel |
|---|---|
| `public/commande/planification_commande/bon_livraison_xml_gps.php` | `app/Services/bon_livraison_xml_gps_service.php` |
| `public/integration_fichier_xml/chercher_fichier_ftp_gps.php` | `app/Console/Commands/tache_recuperer_fichier_xml_ftp.php` |
| `public/integration_fichier_xml/integration_fichier_xml_bl_gps.php` | `app/Services/integration_fichier_xml_bl_gps_service.php` |
