La 1re version cascadait compte résilié ⇒ TOUS services Annulé, ce qui contredit la politique 2026-06-13 (statut SERVICE = vérité ; employés gardent internet gratuit sur comptes terminés) et annulait ~842 services légitimes (dont 185k$/mo facturés encore par F ou gratuits). Raffinement (sync_services_incremental.py, marqueur RAFFINÉ 2026-07-06) : - annul_ghost(compte, prix) : n'annule QUE le fantôme AVÉRÉ = compte résilié + service PAYANT (prix>0) + F silencieux depuis >24 mois (MAX(invoice.date_orig)). Préserve les gratuits (employés), tout ce que F facture encore, et le jamais-facturé (ambigu → revue manuelle via /rapports/resilies-actifs). - Phases B (création) + D (rafraîchissement) passent par annul_ghost. - Résultat mesuré : 3013 fantômes avérés annulés, 842 réactivés/préservés, fantômes avérés restants = 0. Scoreboard : métrique renommée « actifs sur compte résilié — à revoir » (hub note + libellé UI) puisque le résidu n'est plus un bug mais une liste de revue. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| README.md | ||
| run.sh | ||
| sync_invoices_incremental.py | ||
| sync_services_incremental.py | ||
targo-sync — miroir opérationnel F (legacy) → ERPNext
Snapshot versionné des scripts qui tournent sur l'hôte de prod dans /opt/targo-sync/
(hors conteneur). Avant 2026-07-06 ils n'étaient dans aucun repo — c'est le vrai moteur de
synchronisation « legacy F → ERPNext » pour la couche opérationnelle (ce que les équipes
voient : clients, adresses, services/abonnements, factures, paiements). F reste autoritaire
pour la facturation (scheduler ERPNext en pause) ; ERPNext = miroir + grand livre à la bascule.
Cadence
Cron horaire sur l'hôte (15 * * * *) → run.sh → 4 étapes, dans l'ordre du DAG de dépendances :
- Comptes manquants — hub
POST /legacy-payments/ensure-customers(crée les Customer absents). - Adresses + services —
sync_services_incremental.py(Python, exécuté danserpnext-backend-1). - Factures —
sync_invoices_incremental.py(idem). - Paiements + soldes — hub
POST /legacy-payments/sync-cycle.
Les scripts Python sont idempotents (ON CONFLICT DO NOTHING pour les créations, UPDATE
conditionnel pour les rafraîchissements). APPLY=0 = dry-run (n'écrit rien) ; run.sh lance
en APPLY=1. Autres env : PG_HOST (défaut db), LIMIT (taille de lot), LEGACY_HOST.
Déploiement (host, hors repo)
scp scripts/targo-sync/*.py scripts/targo-sync/run.sh root@<prod>:/opt/targo-sync/
# le cron docker cp le .py dans erpnext-backend-1 puis l'exécute — pas de restart requis
Dry-run manuel d'un script avant bascule :
docker cp /opt/targo-sync/sync_services_incremental.py erpnext-backend-1:/tmp/svc.py
docker exec -e APPLY=0 -e PG_HOST=db erpnext-backend-1 \
/home/frappe/frappe-bench/env/bin/python /tmp/svc.py
⚠️ FIX 2026-07-06 — abonnements fantômes (compte résilié affiché « Actif »)
En F, résilier un compte met account.status ∈ {3,4,5} mais laisse les services à
service.status=1 (F ne cascade pas). svc_status() dérivait le statut du seul
service.status → un compte résilié ressortait « Actif » dans ERPNext. Comme le statut client
est dérivé des abonnements, ~2 663 ex-clients apparaissaient actifs et ~3 855 abonnements
étaient des fantômes (MRR gonflé, audiences polluées).
Correctif dans sync_services_incremental.py (marqueur FIX 2026-07-06) : svc_status() charge
une fois SELECT id FROM account WHERE status IN (3,4,5) et force Annulé dès que le compte est
résilié, sur les deux chemins (Phase B création + Phase D rafraîchissement). Sans ce garde-fou,
le cron ré-activait les fantômes à chaque heure. Voir la mémoire projet feedback_ghost_active_subscriptions.
Réconciliation visible dans OPS : page /sync-legacy → carte « Réconciliation par module »
(endpoint hub GET /legacy-sync/scoreboard, métrique ghost_active_subscriptions = doit rester 0).
Fichiers
| Fichier | Rôle |
|---|---|
run.sh |
Orchestrateur cron (4 étapes DAG) |
sync_services_incremental.py |
Service Location (delivery) + Service Subscription (service) : création + prix + statut (fix fantômes) |
sync_invoices_incremental.py |
Sales Invoice (miroir des factures F ; statut reflétant F) |