devices: import du manquant + fix liens SS.device orphelins + catch-up quotidien

- import_devices_and_enrich.py : creds via env (secret hors repo) + SKIP_ENRICH (devices seuls).
  Rattrapage : 1160 devices importés (tous les devices F courants sont maintenant en Service Equipment).
- FIX bug d'import : rename_all_doctypes.py avait renommé Service Equipment (EQ-{md5}→EQP-{seq})
  sans cascader vers Service Subscription.device → 6980 liens orphelins ("Could not find Device: EQ-…",
  bloquait les saves pleine-doc). 5241 remappés via legacy_device_id+md5 (UPDATE PG direct).
  Résiduel 1739 = device supprimé côté F (1584 Actif / 155 Annulé) → décision NULL vs relink en attente.
- Récurrence : tickets-daily.sh importe désormais AUSSI les devices (SKIP_ENRICH=1), quotidien 04:30.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-07-06 23:09:16 -04:00
parent d8052bb17c
commit 8a51925da8
3 changed files with 46 additions and 20 deletions

View File

@ -26,9 +26,9 @@ now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
# LEGACY DB CONNECTION
# ═══════════════════════════════════════════════════════════════
conn = pymysql.connect(
host="legacy-db",
host=os.environ.get("LEGACY_HOST", "10.100.80.100"),
user="facturation",
password="*******",
password=os.environ.get("LEGACY_PW", ""), # secret via env (jamais commité)
database="gestionclient",
cursorclass=pymysql.cursors.DictCursor
)
@ -225,6 +225,11 @@ if batch:
print("Inserted {} new devices ({} already existed)".format(inserted, skipped))
if os.environ.get("SKIP_ENRICH", "0") == "1":
print("SKIP_ENRICH=1 → devices importés, on saute l'enrichissement (Phase 3) + Phase 4")
conn.close()
import sys; sys.exit(0)
# ═══════════════════════════════════════════════════════════════
# PHASE 3: Enrich Service Locations with fibre data
# ═══════════════════════════════════════════════════════════════

View File

@ -52,13 +52,23 @@ Réconciliation visible dans OPS : page **/sync-legacy** → carte « Réconcili
## Fichiers
## Tickets → Issue (cron QUOTIDIEN séparé)
## Catch-up QUOTIDIEN — tickets + appareils (cron séparé)
`tickets-daily.sh` (cron `30 4 * * *`) lance `migrate_tickets.py` (idempotent, skip par
`legacy_ticket_id`) pour n'importer que les nouveaux tickets. Séparé de `run.sh` (horaire) car
le scan de 252k tickets est trop lourd pour l'horaire. Le canonique de `migrate_tickets.py` est
`scripts/migration/` ; en déployer une copie dans `/opt/targo-sync/`. Messages des tickets *closed*
non importés (deferred ; `import_ticket_msgs.py` au besoin).
`tickets-daily.sh` (cron `30 4 * * *`) lance deux imports idempotents (skip par `legacy_*_id`,
n'importent que les nouveaux) :
- **`migrate_tickets.py`** → `tabIssue` (skip par `legacy_ticket_id`). Messages des tickets *closed*
non importés (deferred ; `import_ticket_msgs.py` au besoin).
- **`import_devices_and_enrich.py`** (avec `SKIP_ENRICH=1` = devices seulement) → `tabService Equipment`
(skip par `legacy_device_id`). Sans `SKIP_ENRICH`, enrichit aussi les Service Location (fibre/OLT/VLAN).
Séparé de `run.sh` (horaire) car les scans complets (252k tickets / 11k devices) sont trop lourds
pour l'horaire. Canoniques dans `scripts/migration/` ; en déployer une copie dans `/opt/targo-sync/`.
⚠️ **Bug d'import historique corrigé 2026-07-06** : `rename_all_doctypes.py` a renommé les
`Service Equipment` (`EQ-{md5}` → `EQP-{seq}`) SANS mettre à jour `Service Subscription.device`
→ 6980 liens orphelins (`Could not find Device: EQ-…`, bloquait les saves pleine-doc Frappe).
5241 remappés (via `legacy_device_id`+md5) ; 1739 résiduels pointaient un device supprimé côté F
(à trancher : NULL vs relink au device courant du lieu). Voir mémoire `feedback_device_hierarchy`.
## 🔐 Secrets — JAMAIS dans le repo
@ -73,5 +83,7 @@ Ne **jamais** committer une copie prod d'un script qui aurait le mot de passe en
| `run.sh` | Orchestrateur cron horaire (4 étapes DAG) |
| `sync_services_incremental.py` | Service Location (delivery) + Service Subscription (service) : création + prix + **statut (fix fantômes raffiné)** |
| `sync_invoices_incremental.py` | Sales Invoice (miroir des factures F ; statut reflétant F) |
| `tickets-daily.sh` | Wrapper cron quotidien → `migrate_tickets.py` (secret via `secrets.env`) |
| `tickets-daily.sh` | Wrapper cron quotidien → `migrate_tickets.py` + `import_devices_and_enrich.py` (secret via `secrets.env`) |
| `migrate_tickets.py` | (déployé depuis `scripts/migration/`) tickets F → Issue |
| `import_devices_and_enrich.py` | (déployé depuis `scripts/migration/`) devices F → Service Equipment |
| `secrets.env` | **Hôte seulement, non versionné** : `LEGACY_PW=…` |

View File

@ -1,25 +1,34 @@
#!/bin/bash
# Import QUOTIDIEN incrémental des tickets F → ERPNext Issue.
# migrate_tickets.py est idempotent (skip par legacy_ticket_id) : ne crée que les nouveaux.
# Complète le rattrapage 2026-07-06. Séparé du cron horaire run.sh (le scan de 252k tickets
# est trop lourd pour l'horaire ; quotidien suffit — les tickets s'accumulent lentement).
# Catch-up QUOTIDIEN legacy → ERPNext : tickets (Issue) + appareils (Service Equipment).
# Scripts idempotents (skip par legacy_*_id) : n'importent que les nouveaux.
# Séparé de run.sh (horaire) car les scans complets (252k tickets / 11k devices) sont lourds.
# (Nom de fichier conservé pour la stabilité du crontab.)
#
# Déploiement (hôte) :
# scp scripts/migration/migrate_tickets.py root@<prod>:/opt/targo-sync/migrate_tickets.py
# scp scripts/targo-sync/tickets-daily.sh root@<prod>:/opt/targo-sync/ && chmod +x /opt/targo-sync/tickets-daily.sh
# scp scripts/migration/migrate_tickets.py scripts/migration/import_devices_and_enrich.py root@<prod>:/opt/targo-sync/
# scp scripts/targo-sync/tickets-daily.sh root@<prod>:/opt/targo-sync/ && chmod +x /opt/targo-sync/tickets-daily.sh
# echo 'LEGACY_PW=<mdp F>' > /opt/targo-sync/secrets.env && chmod 600 /opt/targo-sync/secrets.env # secret HORS repo
# crontab: 30 4 * * * /opt/targo-sync/tickets-daily.sh
set -uo pipefail
DIR=/opt/targo-sync
LOG="$DIR/tickets-sync.log"
[ -f "$LOG" ] && tail -n 2000 "$LOG" > "$LOG.tmp" 2>/dev/null && mv "$LOG.tmp" "$LOG"
[ -f "$LOG" ] && tail -n 3000 "$LOG" > "$LOG.tmp" 2>/dev/null && mv "$LOG.tmp" "$LOG"
exec >> "$LOG" 2>&1
echo "===== $(date -Is) tickets-daily ====="
echo "===== $(date -Is) daily-catchup (tickets + appareils) ====="
exec 9>"$DIR/.tickets.lock"
flock -n 9 || { echo "déjà en cours, skip"; exit 0; }
# secret (LEGACY_PW) chargé depuis un fichier hôte non versionné
[ -f "$DIR/secrets.env" ] && . "$DIR/secrets.env"
[ -f "$DIR/secrets.env" ] && set -a && . "$DIR/secrets.env" && set +a
LH="${LEGACY_HOST:-10.100.80.100}"
echo "-- tickets → Issue --"
docker cp "$DIR/migrate_tickets.py" erpnext-backend-1:/tmp/migrate_tickets.py 2>/dev/null
docker exec -e LEGACY_PW="${LEGACY_PW:-}" -e LEGACY_HOST="${LEGACY_HOST:-10.100.80.100}" -e PG_HOST=db \
erpnext-backend-1 /home/frappe/frappe-bench/env/bin/python /tmp/migrate_tickets.py 2>&1 | tail -12
docker exec -e LEGACY_PW="${LEGACY_PW:-}" -e LEGACY_HOST="$LH" -e PG_HOST=db \
erpnext-backend-1 /home/frappe/frappe-bench/env/bin/python /tmp/migrate_tickets.py 2>&1 | tail -6
echo "-- appareils → Service Equipment (device-only) --"
docker cp "$DIR/import_devices_and_enrich.py" erpnext-backend-1:/tmp/import_devices.py 2>/dev/null
docker exec -e LEGACY_PW="${LEGACY_PW:-}" -e LEGACY_HOST="$LH" -e SKIP_ENRICH=1 \
erpnext-backend-1 /home/frappe/frappe-bench/env/bin/python /tmp/import_devices.py 2>&1 | tail -4
echo "done $(date -Is)"