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>
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.shfait déjà unmysqldump --single-transaction(non bloquant, 1 connexion) des tablesaccount/delivery/service/product→ miroir locallegacy-db. On lit TOUJOURS le miroir, jamais F live. - Détection de changement confirmée :
account.date_lastest une vraie date de modification (86 % des comptes, bouge en temps réel) → watermark possible sans rien changer dans F.service/delivery= INSERT parid(pas de date de modif). - Écritures ERPNext→F = via le pont PHP (
ops_reassign.php, déjà utilisé pour le dispatch), idempotent parlegacy_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 dansdata/legacy-sync-preview.json.applySafeAdds({confirm})— dry-run par défaut, n'écrit QUE siconfirm==='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-adds→ dry-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)
- Feu vert pour
safe_add? Remplir les 8 119 champs vides (email/tél) est sûr et réversible (PUT partiel). → lancerapplySafeAdds({confirm:'SAFE-ADD'}). (Nuance : email/tél canoniques vivent sur le doctype Contact ; on remplit ici la copieCustomer.email_id/tel_homepour recherche/affichage — le sync Contact complet est une étape ultérieure.) - needs_review (921) : surtout
is_commercial(640) — vérifier le mapping (F.commercialvs ERPNextis_commercial) ; petit volume, révisable. - 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.
- Services : filtrer
status=1+ mapper service→Service Subscription (+Subscription) proprement. - Fraîcheur F→miroir : planifier le delta (watermark
date_last/id) — toujours pas de cron ajouté (en attente de ta décision de cadence). - 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).