gigafibre-fsm/docs/features/legacy-dispatch-bridge.md
louispaulb 70bf25ea84 feat(dispatch): pont legacy(osTicket)→Dispatch Job pour les tickets « Tech Targo »
Tire régulièrement les tickets ouverts assignés au compte « Tech Targo » (staff 3301)
de la DB legacy MariaDB et crée/maj un Dispatch Job ERPNext (pool à répartir).

- lib/legacy-dispatch-sync.js : fetch (status=open AND assign_to=3301) + mapping
  customer (legacy_account_id) / Service Location (coords) / job_type (dept) /
  scheduled_date (epoch→America/Toronto) / start_time (am|pm|HH:MM) / priority
- Idempotent via Custom Field Dispatch Job.legacy_ticket_id (lookup avant create) ;
  ne clobbe pas le travail du répartiteur (maj date seulement si encore open+non assigné)
- SÉQUENTIEL (frappe_pg) ; endpoints GET preview (dry-run) + POST run
- Récurrence opt-in : startSync() au boot, LEGACY_DISPATCH_SYNC=on + _MIN=15
- server.js : route /dispatch/legacy-sync + startSync()
- doc docs/features/legacy-dispatch-bridge.md + index

Mise en service : 70 tickets importés (0 erreur), récurrence 15 min active.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 09:33:53 -04:00

66 lines
4.0 KiB
Markdown

# Pont legacy → Dispatch (osTicket → Dispatch Job) — Handoff dev
Tire **régulièrement** les tickets ouverts assignés au compte « Tech Targo » dans la
DB legacy (osTicket/MariaDB `gestionclient`) et crée/maj un **Dispatch Job** ERPNext
pour les répartir (grille Planification / tableau Dispatch).
## Pourquoi « Tech Targo » = staff id 3301
Dans le legacy, le travail terrain à dispatcher est assigné au compte générique
**« Tech Targo »** (`staff.id = 3301`, username `tech`) — c'est le `default_staff` des
départements techniciens (Installation/Réparation/Fibre). Filtre du pont :
`ticket.status='open' AND ticket.assign_to=3301`. (~70 tickets au démarrage.)
Override possible via `LEGACY_TARGO_STAFF_ID`.
## Surfaces
| Quoi | Où |
|---|---|
| Module | `services/targo-hub/lib/legacy-dispatch-sync.js` |
| Routage + scheduler | `services/targo-hub/server.js` (`/dispatch/legacy-sync`, `startSync()` au boot) |
| Champ idempotence | Custom Field `Dispatch Job.legacy_ticket_id` (`dispatch-app/frappe-setup/setup_dispatch_custom_fields.py`) |
| Conso côté UI | Pool « à assigner » du tableau Dispatch + panneau « Jobs à assigner » de la Planification |
## Mapping ticket legacy → Dispatch Job
| Dispatch Job | Source legacy | Notes |
|---|---|---|
| `legacy_ticket_id` | `ticket.id` | **clé d'idempotence** (lookup avant create) |
| `ticket_id` | `'LEG-' + ticket.id` | nom lisible du DJ |
| `subject` | `ticket.subject` | + adresse ajoutée si pas de Service Location |
| `customer` | `Customer``legacy_account_id = ticket.account_id` | 61/70 matchés ; sinon laissé vide |
| `service_location` + `latitude`/`longitude` | `Service Location` du customer (ville qui matche) | → pin carte |
| `job_type` | `ticket.dept_id` → {Installation, Réparation, Retrait, Autre} | valeurs valides du Select |
| `scheduled_date` | `ticket.due_date` (epoch) | converti **America/Toronto** (anti-décalage UTC) |
| `start_time` | `ticket.due_time` | `HH:MM` tel quel · `am`→08:00 · `pm`→13:00 · `day`→aucune |
| `priority` | `ticket.priority` (1/2/≥3) | → low / medium / high |
| `duration_h` | défaut par type | Install 2h · Répar 1.5h · autre 1h (le legacy n'en a pas) |
| `status` | — | toujours `open` (pool ; PAS auto-assigné à un tech précis) |
## Comportement
- **Idempotent** : 1 ticket legacy = 1 Dispatch Job (clé `legacy_ticket_id`). Re-run ⇒ 0 doublon.
- **Ne clobbe PAS le répartiteur** : un DJ déjà assigné/déplacé n'est plus touché ; maj de `scheduled_date`
seulement tant qu'il est encore `open` + non assigné.
- **SÉQUENTIEL** (frappe_pg ne supporte pas la concurrence) — pas de `Promise.all`.
## Endpoints
- `GET /dispatch/legacy-sync/preview`**dry-run, 0 écriture** : ce qui serait créé + matching client/SL + non-matchés.
- `POST /dispatch/legacy-sync/run` — exécute la synchro (création/maj). Retourne `{tickets, created, updated, skipped, errors, unmatched_customer}`.
## Récurrence
`startSync()` (server.js, au boot) — **opt-in** via env :
```
LEGACY_DISPATCH_SYNC=on # active la récurrence (sinon preview/run manuels seulement)
LEGACY_DISPATCH_SYNC_MIN=15 # période en minutes (défaut 15)
```
Posé dans `/opt/targo-hub/.env`. ⚠️ Après modif de `.env`, **recréer** le conteneur
(`cd /opt/targo-hub && docker compose up -d`) — `docker restart` ne relit pas l'env_file.
1er passage différé de 90 s après le boot, puis toutes les `MIN` minutes.
## État (mise en service 2026-06-06)
70 tickets importés (0 erreur, 9 clients non matchés = comptes post-migration + 2 tickets internes
« FORMATION EN HAUTEUR »). Récurrence active (15 min).
## TODO / idées
- Fermer/annuler le Dispatch Job quand le ticket legacy passe `closed` (v1 ne gère que open).
- Filtrer les départements non-terrain (ToDo, Support Informatique, Conception…) si bruit.
- Matcher les 9 clients manquants (créer le Customer ou enrichir `legacy_account_id`).
- Écrire en retour le tech assigné / la date vers le legacy (bidirectionnel) — non fait (lecture seule legacy).