Backend services: - targo-hub: extract deepGetValue to helpers.js, DRY disconnect reasons lookup map, compact CAPABILITIES, consolidate vision.js prompts/schemas, extract dispatch scoring weights, trim section dividers across 9 files - modem-bridge: extract getSession() helper (6 occurrences), resetIdleTimer(), consolidate DM query factory, fix duplicate username fill bug, trim headers (server.js -36%, tplink-session.js -47%, docker-compose.yml -57%) Frontend: - useWifiDiagnostic: extract THRESHOLDS const, split processDiagnostic into 6 focused helpers (processOnlineStatus, processWanIPs, processRadios, processMeshNodes, processClients, checkRadioIssues) - EquipmentDetail: merge duplicate ROLE_LABELS, remove verbose comments Documentation (17 → 13 files, -1,400 lines): - New consolidated README.md (architecture, services, dependencies, auth) - Merge ECOSYSTEM-OVERVIEW into ARCHITECTURE.md - Merge MIGRATION-PLAN + ARCHITECTURE-COMPARE + FIELD-GAP + CHANGELOG → MIGRATION.md - Merge COMPETITIVE-ANALYSIS into PLATFORM-STRATEGY.md - Update ROADMAP.md with current phase status - Delete CONTEXT.md (absorbed into README) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
157 lines
5.8 KiB
Python
157 lines
5.8 KiB
Python
"""
|
|
Import account suspensions as flags on Customer.
|
|
Also imports bon_travail as Comments on Customer (work order history).
|
|
|
|
Run inside erpnext-backend-1:
|
|
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/import_suspensions.py
|
|
"""
|
|
import frappe
|
|
import pymysql
|
|
import os, sys, time
|
|
from datetime import datetime, timezone
|
|
from html import unescape
|
|
|
|
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
|
|
os.chdir("/home/frappe/frappe-bench/sites")
|
|
frappe.init(site="erp.gigafibre.ca", sites_path=".")
|
|
frappe.connect()
|
|
frappe.local.flags.ignore_permissions = True
|
|
print("Connected:", frappe.local.site)
|
|
|
|
legacy = pymysql.connect(
|
|
host="legacy-db", user="facturation", password="VD67owoj",
|
|
database="gestionclient", cursorclass=pymysql.cursors.DictCursor
|
|
)
|
|
|
|
cust_map = {}
|
|
for r in frappe.db.sql('SELECT name, legacy_account_id FROM "tabCustomer" WHERE legacy_account_id > 0', as_dict=True):
|
|
cust_map[int(r['legacy_account_id'])] = r['name']
|
|
print(f" {len(cust_map)} customers mapped")
|
|
|
|
staff_map = {}
|
|
# No legacy_staff_id on Employee — build from legacy DB
|
|
with legacy.cursor() as cur:
|
|
cur.execute("SELECT id, CONCAT(first_name, ' ', last_name) as name FROM staff WHERE first_name IS NOT NULL")
|
|
for r in cur.fetchall():
|
|
staff_map[r['id']] = r['name'].strip()
|
|
|
|
|
|
def ts_to_date(unix_ts):
|
|
if not unix_ts or unix_ts <= 0:
|
|
return None
|
|
try:
|
|
return datetime.fromtimestamp(int(unix_ts), tz=timezone.utc).strftime("%Y-%m-%d")
|
|
except (ValueError, OSError):
|
|
return None
|
|
|
|
|
|
def ts_to_dt(unix_ts):
|
|
if not unix_ts or unix_ts <= 0:
|
|
return None
|
|
try:
|
|
return datetime.fromtimestamp(int(unix_ts), tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
|
|
except (ValueError, OSError):
|
|
return None
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════
|
|
# 1. Account Suspensions → Customer.is_suspended
|
|
# ═══════════════════════════════════════════════════════════
|
|
print("\n=== Importing suspensions ===")
|
|
with legacy.cursor() as cur:
|
|
cur.execute("SELECT account_id, note FROM account_suspension")
|
|
susp_rows = cur.fetchall()
|
|
|
|
updated = 0
|
|
for r in susp_rows:
|
|
cust = cust_map.get(r['account_id'])
|
|
if not cust:
|
|
continue
|
|
frappe.db.sql(
|
|
'UPDATE "tabCustomer" SET is_suspended = 1, suspension_note = %s WHERE name = %s',
|
|
((r['note'] or '').strip(), cust)
|
|
)
|
|
updated += 1
|
|
|
|
frappe.db.commit()
|
|
print(f" {updated} customers marked as suspended")
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════
|
|
# 2. Bons de travail → Comments on Customer
|
|
# ═══════════════════════════════════════════════════════════
|
|
print("\n=== Importing bons de travail as Comments ===")
|
|
|
|
# Check if already imported (by content prefix)
|
|
existing = frappe.db.sql(
|
|
"SELECT COUNT(*) FROM \"tabComment\" WHERE comment_type='Comment' AND content LIKE %s",
|
|
("🔧 Bon de travail%",)
|
|
)[0][0]
|
|
if existing:
|
|
print(f" {existing} work order comments already exist — skipping")
|
|
else:
|
|
with legacy.cursor() as cur:
|
|
cur.execute("""
|
|
SELECT bt.id, bt.account_id, bt.date, bt.tech1, bt.tech2,
|
|
bt.heure_arrive_t1, bt.heure_depart_t1,
|
|
bt.heure_arrive_t2, bt.heure_depart_t2,
|
|
bt.note, bt.subtotal, bt.tps, bt.tvq, bt.total
|
|
FROM bon_travail bt
|
|
ORDER BY bt.id
|
|
""")
|
|
bt_rows = cur.fetchall()
|
|
print(f" {len(bt_rows)} bons de travail to import")
|
|
|
|
T0 = time.time()
|
|
created = skipped = 0
|
|
|
|
for bt in bt_rows:
|
|
cust = cust_map.get(bt['account_id'])
|
|
if not cust:
|
|
skipped += 1
|
|
continue
|
|
|
|
dt = ts_to_dt(bt['date'])
|
|
date_str = ts_to_date(bt['date']) or '?'
|
|
|
|
tech1_name = staff_map.get(bt['tech1'], f"Tech #{bt['tech1']}") if bt['tech1'] else ''
|
|
tech2_name = staff_map.get(bt['tech2'], f"Tech #{bt['tech2']}") if bt['tech2'] else ''
|
|
|
|
lines = [f"<strong>Bon de travail #{bt['id']}</strong> — {date_str}"]
|
|
if tech1_name:
|
|
time_range = f" ({bt['heure_arrive_t1'] or '?'} - {bt['heure_depart_t1'] or '?'})" if bt.get('heure_arrive_t1') else ''
|
|
lines.append(f"Tech 1: {tech1_name}{time_range}")
|
|
if tech2_name:
|
|
time_range = f" ({bt['heure_arrive_t2'] or '?'} - {bt['heure_depart_t2'] or '?'})" if bt.get('heure_arrive_t2') else ''
|
|
lines.append(f"Tech 2: {tech2_name}{time_range}")
|
|
if bt['note']:
|
|
lines.append(f"Note: {unescape(bt['note']).strip()}")
|
|
if bt['total'] and float(bt['total']) > 0:
|
|
lines.append(f"Total: ${float(bt['total']):.2f} (TPS: ${float(bt['tps'] or 0):.2f}, TVQ: ${float(bt['tvq'] or 0):.2f})")
|
|
|
|
content = "<br>".join(lines)
|
|
|
|
doc = frappe.get_doc({
|
|
"doctype": "Comment",
|
|
"comment_type": "Comment",
|
|
"reference_doctype": "Customer",
|
|
"reference_name": cust,
|
|
"content": content,
|
|
"comment_email": "migration@targo.ca",
|
|
"creation": dt or "2020-01-01 00:00:00",
|
|
"modified": dt or "2020-01-01 00:00:00",
|
|
})
|
|
doc.insert(ignore_permissions=True)
|
|
created += 1
|
|
|
|
if created % 1000 == 0:
|
|
frappe.db.commit()
|
|
print(f" [{created}/{len(bt_rows)}] [{time.time()-T0:.0f}s]")
|
|
|
|
frappe.db.commit()
|
|
print(f" Created: {created}, Skipped: {skipped} [{time.time()-T0:.0f}s]")
|
|
|
|
legacy.close()
|
|
frappe.destroy()
|
|
print("\nDone!")
|