gigafibre-fsm/services/targo-hub/docs/legacy-sync.md
louispaulb 5ab14cac44 feat(fsm): platform build — comms UI, F→ERPNext sync/billing, roster, campaigns, network, reports
Accumulated work on the dispatch/legacy-writeback branch:
- Communications UI: CommunicationsPage, ConversationFullPage, DepartmentBoard,
  PipelineBoard, ReaderStack, Orchestrator/NewTicket/ServiceStatus/Outbox dialogs;
  hub gmail.js, ticket-collab.js, outbox.js, coupon-triage.js, client-diag.js.
- Billing/sync mirror (F→ERPNext): legacy-payments.js, legacy-sync.js,
  sync-orchestrator.js, supplier-invoices.js, municipality.js + incremental
  migration scripts; LegacySyncPage, SupplierInvoices + negative-billing /
  terminated-active reports.
- Roster/campaigns/network/voice: roster + roster-assistant, campaigns, giftbit,
  olt-snmp, traccar, twilio, vision, tech-absence-sms, ai/agent/config/helpers,
  legacy-dispatch-sync; ops PlanificationPage, RapportsPage, Settings, Tickets,
  ClientDetail updates.
- docs/ PLATFORM_GUIDE + UI_AND_OPTIMIZATION; .gitignore __pycache__.

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

6.0 KiB

Sync F → ERPNext — état au 2026-06-11 (session nocturne)

TL;DR — Moteur de sync delta construit + testé en lecture seule sur tes vraies données. AUCUNE écriture n'a été faite (ni F, ni ERPNext). Aucun cron ajouté. Les déploiements sont inertes (nouveau module lib/legacy-sync.js + routes gated, qui ne tournent que sur appel). Décision attendue de toi avant toute écriture (voir §6).

1. Méthode de sync retenue (rappel)

  • Pas de proxy live vers F (prod fragile). Pas de réplication binlog (F n'a pas log-bin → exigerait un redémarrage du MySQL de facturation).
  • Snapshot/delta pull : le script existant refresh-report-tables.sh fait déjà un mysqldump --single-transaction (non bloquant, 1 connexion) des tables account/delivery/service/product → miroir local legacy-db. On lit TOUJOURS le miroir, jamais F live.
  • Détection de changement confirmée : account.date_last est une vraie date de modification (86 % des comptes, bouge en temps réel) → watermark possible sans rien changer dans F. service/delivery = INSERT par id (pas de date de modif).
  • Écritures ERPNext→F = via le pont PHP (ops_reassign.php, déjà utilisé pour le dispatch), idempotent par legacy_account_id. F jamais écrit en double.
  • Propriété par domaine : F = facturation (tarif/facture/statut billing), ERPNext = ops (dispatch/RDV/notes). Aucun champ disputé.

2. Ce qui a été construit

  • services/targo-hub/lib/legacy-sync.js (déployé, inerte) :
    • previewCustomers() — lit le miroir + ERPNext, classe chaque écart.
    • runPreview() — orchestre customers + locations + services, écrit le rapport complet dans data/legacy-sync-preview.json.
    • applySafeAdds({confirm})dry-run par défaut, n'écrit QUE si confirm==='SAFE-ADD' (non exécuté).
  • Routes (gated par le token hub, donc OPS-only) :
    • GET /legacy-sync/preview → recalcule + renvoie le rapport.
    • GET /legacy-sync/report → relit le dernier rapport.
    • POST /legacy-sync/apply-safe-addsdry-run sauf { "confirm": "SAFE-ADD" }.

3. Politique de sécurité (le cœur de « preview d'abord »)

Chaque écart de champ est classé :

  • ADD — ERPNext vide + F a une valeur → sûr (on remplit).
  • CHANGE — les deux ont une valeur différente → révision humaine (jamais auto-appliqué).
  • NEVER-CLOBBER — F vide + ERPNext a une valeur → jamais écrasé (protège ex. les stripe_id).
  • disabled — ERPNext le DÉRIVE des abonnements actifs (≠ account.status) → jamais synchronisé, juste rapporté comme « divergence de statut ».

4. Résultats du preview (réels, 2026-06-11)

Customers — F 15 771 / ERPNext 15 303 :

Bucket Nombre Action
À créer (nouveaux comptes F) 471 nouveaux clients depuis la migration (id 15 429+)
safe_add (enrichir champs vides) 8 119 sûr — email 7 073, tél 8 786, cell 2 310, stripe 138
needs_review (valeurs divergentes) 921 is_commercial 640, is_bad_payer 161, ppa 47, email 34, nom 21, langue 9
inchangés 6 260
never-clobber (protégés) 2 1 stripe_id + 1 cell (F vide)

Divergence de statut (NON auto-synchronisée) : 713 actifs-F/désactivés-ERP + 3 502 inactifs-F/actifs-ERP (probables churns qu'ERPNext montre encore actifs → à réviser).

Locations delivery→Service Location : 537 à créer (sur 17 645). Services ACTIFS (status=1) service→Service Subscription : 3 580 à créer (F 41 685 / ERPNext 39 628). (Avec l'historique inactif c'était 30 469 — désormais filtré sur status=1.)

Écran de revue OPS (déployé)

Page /sync-legacy (« Sync F↔ERPNext » dans le menu, requires: view_settings) — src/pages/LegacySyncPage.vue. Lecture seule : appelle GET /legacy-sync/preview et affiche les buckets (à créer / à enrichir / à réviser), les ventilations par champ (ADD/CHANGE/never-clobber), la divergence de statut, et des échantillons. Aucun bouton d'écriture (l'apply reste CLI/gated tant que tu n'as pas donné le feu vert).

5. Comment l'utiliser

# Preview (lecture seule) :
docker exec targo-hub node -e "require('/app/lib/legacy-sync').runPreview().then(r=>console.log(JSON.stringify(r.customers.summary)))"
# Rapport complet :
cat /opt/targo-hub/data/legacy-sync-preview.json   # (dans le conteneur : /app/data/…)
# Dry-run de l'apply (n'écrit RIEN) :
docker exec targo-hub node -e "require('/app/lib/legacy-sync').applySafeAdds({}).then(r=>console.log(JSON.stringify(r)))"

6. Décisions / prochaines étapes (À TON RÉVEIL)

  1. Feu vert pour safe_add ? Remplir les 8 119 champs vides (email/tél) est sûr et réversible (PUT partiel). → lancer applySafeAdds({confirm:'SAFE-ADD'}). (Nuance : email/tél canoniques vivent sur le doctype Contact ; on remplit ici la copie Customer.email_id/tel_home pour recherche/affichage — le sync Contact complet est une étape ultérieure.)
  2. needs_review (921) : surtout is_commercial (640) — vérifier le mapping (F.commercial vs ERPNext is_commercial) ; petit volume, révisable.
  3. Divergence de statut (3 502 inactifs-F/actifs-ERP) : décider la règle (réconcilier ERPNext.disabled depuis F.status pour les terminés ?). C'est un signal de churn, pas un bug.
  4. Services : filtrer status=1 + mapper service→Service Subscription (+Subscription) proprement.
  5. Fraîcheur F→miroir : planifier le delta (watermark date_last/id) — toujours pas de cron ajouté (en attente de ta décision de cadence).
  6. Suite du funnel : Lead (doctype ERPNext) + wizard rep, puis push-to-F (#65), puis onboarding (flow), puis RDV (booking déjà construit).

7. Garanties de cette session

  • Zéro écriture dans F. Zéro écriture dans ERPNext. Aucun cron/scheduler ajouté.
  • Scheduler ERPNext reste en pause ; facturation F reste autoritaire.
  • Déploiements = nouveau module + routes gated (n'altèrent aucun comportement existant).