Scoreboard (console de réconciliation F ↔ ERPNext) :
- hub: legacy-sync.js scoreboard() + GET /legacy-sync/scoreboard — comptes bruts
par module (clients/lieux/abos/appareils/tickets/factures/paiements) + métrique
ghost_active_subscriptions. Devices = tabService Equipment (pas tabDevice).
Count F « abos actifs » exclut les comptes résiliés (comparaison pomme-à-pomme).
- ops: carte « Réconciliation par module » sur LegacySyncPage.vue.
Fix fantômes à la SOURCE (scripts/targo-sync/, jusqu'ici hors repo) :
- La vraie sync des services = scripts Python hôte (/opt/targo-sync/, cron horaire),
PAS le hub. svc_status() ignorait le statut du COMPTE → un compte résilié F
(status 3/4/5) dont les services restent status=1 ressortait « Actif » (F ne
cascade pas). Résultat : ~3855 abonnements fantômes recréés à chaque heure.
- sync_services_incremental.py: svc_status() rendu conscient du compte (force
'Annulé' si compte résilié) sur Phase B (création) + Phase D (rafraîchissement).
Rollout: dry-run {Actif→Annulé: 3855} → APPLY=1 → ghost=0 stable.
- Versionne aussi run.sh + sync_invoices_incremental.py + README (snapshot fidèle
de la prod ; ces scripts écrivent en base chaque heure et n'étaient pas suivis).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
27 lines
1.9 KiB
Bash
Executable File
27 lines
1.9 KiB
Bash
Executable File
#!/bin/bash
|
|
LOG=/opt/targo-sync/billing-sync.log; [ -f "$LOG" ] && tail -n 1500 "$LOG" > "$LOG.tmp" 2>/dev/null && mv "$LOG.tmp" "$LOG"
|
|
# Sync F→ERPNext (miroir opérationnel) — idempotent, léger, gap-aware. Ordre = DAG des dépendances.
|
|
set -uo pipefail
|
|
LOG=/opt/targo-sync/billing-sync.log
|
|
exec >> "$LOG" 2>&1
|
|
echo "===== $(date -Is) ====="
|
|
echo "-- 1. comptes manquants (hub) --"
|
|
docker exec targo-hub node -e '
|
|
const T=process.env.HUB_SERVICE_TOKEN
|
|
fetch("http://localhost:3300/legacy-payments/ensure-customers",{method:"POST",headers:{Authorization:"Bearer "+T,"Content-Type":"application/json"},body:JSON.stringify({confirm:"F-WINS",limit:1000})})
|
|
.then(r=>r.json()).then(d=>console.log("comptes:",JSON.stringify({missing:d.missing,created:d.created,errors:d.errors}))).catch(e=>console.log("comptes ERR",e.message))
|
|
'
|
|
echo "-- 2. adresses + services (Python, opérationnel) --"
|
|
docker cp /opt/targo-sync/sync_services_incremental.py erpnext-backend-1:/tmp/sync_svc.py 2>/dev/null
|
|
docker exec -e APPLY=1 -e PG_HOST=db erpnext-backend-1 /home/frappe/frappe-bench/env/bin/python /tmp/sync_svc.py 2>&1 | tail -4
|
|
echo "-- 3. factures (Python, statut reflétant F) --"
|
|
docker cp /opt/targo-sync/sync_invoices_incremental.py erpnext-backend-1:/tmp/sync_inv.py 2>/dev/null
|
|
docker exec -e APPLY=1 -e PG_HOST=db erpnext-backend-1 /home/frappe/frappe-bench/env/bin/python /tmp/sync_inv.py 2>&1 | tail -2
|
|
echo "-- 4. paiements + soldes (hub) --"
|
|
docker exec targo-hub node -e '
|
|
const T=process.env.HUB_SERVICE_TOKEN
|
|
fetch("http://localhost:3300/legacy-payments/sync-cycle",{method:"POST",headers:{Authorization:"Bearer "+T,"Content-Type":"application/json"},body:JSON.stringify({confirm:"F-WINS",limit:20000})})
|
|
.then(r=>r.json()).then(d=>console.log("cycle:",JSON.stringify({pay:d.payments&&d.payments.applied,refreshed:d.open_refresh&&d.open_refresh.updated}))).catch(e=>{console.log("cycle ERR",e.message)})
|
|
'
|
|
echo "done $(date -Is)"
|