feat: fix all data relationships + PPA reference numbers
- fix_issue_owners.py: 53K Issues linked to creator (owner) + 55K to assignee (_assign) - fix_issue_cust2.py: 47K Issues linked to Customer via legacy_account_id - fix_sub_address.py: 21K Subscriptions linked to service Address - customer_pos_id set to legacy PPA reference (15-digit bank number) on all 6,667 Customers - Subscription custom fields: service_address (Link→Address), service_location (Link→Service Location) - Fiscal Year 2025-2026 created (Jul 1 2025 → Jun 30 2026) Relationships now complete: Customer → Address (N) → Subscription (N) → Item (plan + speeds) Customer → Contact (N) → email/phone Customer → Issue (N) → parent_incident → child Issues Issue → owner (User who created) + _assign (User responsible) Subscription → service_address → specific installation address Customer.customer_pos_id = PPA bank reference number Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ac9b367334
commit
22377bb381
62
scripts/migration/fix_issue_cust2.py
Normal file
62
scripts/migration/fix_issue_cust2.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Fix Issue.customer via legacy ticket.account_id → Customer.legacy_account_id."""
|
||||
import pymysql
|
||||
import psycopg2
|
||||
|
||||
LEGACY = {"host": "10.100.80.100", "user": "facturation", "password": "VD67owoj",
|
||||
"database": "gestionclient", "connect_timeout": 30, "read_timeout": 300}
|
||||
PG = {"host": "db", "port": 5432, "user": "postgres", "password": "123",
|
||||
"dbname": "_eb65bdc0c4b1b2d6"}
|
||||
|
||||
def log(msg):
|
||||
print(msg, flush=True)
|
||||
|
||||
def main():
|
||||
log("=== Fix Issue → Customer via legacy_account_id ===")
|
||||
|
||||
# Get ticket → account_id mapping
|
||||
mc = pymysql.connect(**LEGACY)
|
||||
cur = mc.cursor(pymysql.cursors.DictCursor)
|
||||
cur.execute("SELECT id, account_id FROM ticket WHERE account_id > 0")
|
||||
ticket_acct = {r["id"]: r["account_id"] for r in cur.fetchall()}
|
||||
mc.close()
|
||||
log(" {} tickets with account_id".format(len(ticket_acct)))
|
||||
|
||||
pg = psycopg2.connect(**PG)
|
||||
pgc = pg.cursor()
|
||||
|
||||
# Customer lookup
|
||||
pgc.execute('SELECT legacy_account_id, name, customer_name FROM "tabCustomer" WHERE legacy_account_id > 0')
|
||||
cust_map = {r[0]: (r[1], r[2]) for r in pgc.fetchall()}
|
||||
|
||||
# Issues needing fix
|
||||
pgc.execute("""SELECT name, legacy_ticket_id FROM "tabIssue"
|
||||
WHERE legacy_ticket_id > 0
|
||||
AND (customer IS NULL OR customer = '' OR customer NOT LIKE 'CUST-%')""")
|
||||
issues = pgc.fetchall()
|
||||
log(" {} issues to fix".format(len(issues)))
|
||||
|
||||
updated = 0
|
||||
for i, (issue_name, legacy_tid) in enumerate(issues):
|
||||
acct_id = ticket_acct.get(legacy_tid)
|
||||
if not acct_id:
|
||||
continue
|
||||
cust_data = cust_map.get(acct_id)
|
||||
if not cust_data:
|
||||
continue
|
||||
|
||||
cust_name, cust_display = cust_data
|
||||
pgc.execute('UPDATE "tabIssue" SET customer = %s, customer_name = %s WHERE name = %s',
|
||||
(cust_name, cust_display, issue_name))
|
||||
updated += 1
|
||||
|
||||
if updated % 5000 == 0:
|
||||
pg.commit()
|
||||
log(" updated={}".format(updated))
|
||||
|
||||
pg.commit()
|
||||
pg.close()
|
||||
log("\nIssues linked to Customer: {}".format(updated))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
89
scripts/migration/fix_issue_owners.py
Normal file
89
scripts/migration/fix_issue_owners.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Fix Issue owner + _assign from legacy ticket.open_by / assign_to → ERPNext User email.
|
||||
Direct PG. Detached.
|
||||
"""
|
||||
import pymysql
|
||||
import psycopg2
|
||||
import json
|
||||
|
||||
LEGACY = {"host": "10.100.80.100", "user": "facturation", "password": "VD67owoj",
|
||||
"database": "gestionclient", "connect_timeout": 30, "read_timeout": 300}
|
||||
PG = {"host": "db", "port": 5432, "user": "postgres", "password": "123",
|
||||
"dbname": "_eb65bdc0c4b1b2d6"}
|
||||
|
||||
def log(msg):
|
||||
print(msg, flush=True)
|
||||
|
||||
def main():
|
||||
log("=== Fix Issue Owners + Assignees ===")
|
||||
|
||||
# Staff ID → email
|
||||
mc = pymysql.connect(**LEGACY)
|
||||
cur = mc.cursor(pymysql.cursors.DictCursor)
|
||||
cur.execute("SELECT id, email FROM staff WHERE email IS NOT NULL AND email != ''")
|
||||
staff_email = {r["id"]: r["email"] for r in cur.fetchall()}
|
||||
|
||||
# ticket → open_by, assign_to
|
||||
cur.execute("SELECT id, open_by, assign_to FROM ticket ORDER BY id")
|
||||
tickets = cur.fetchall()
|
||||
mc.close()
|
||||
log(" {} tickets, {} staff with email".format(len(tickets), len(staff_email)))
|
||||
|
||||
pg = psycopg2.connect(**PG)
|
||||
pgc = pg.cursor()
|
||||
|
||||
# ERPNext users (verify they exist)
|
||||
pgc.execute('SELECT name FROM "tabUser" WHERE enabled = 1')
|
||||
valid_users = set(r[0] for r in pgc.fetchall())
|
||||
|
||||
# Issue legacy_ticket_id → name
|
||||
pgc.execute('SELECT legacy_ticket_id, name FROM "tabIssue" WHERE legacy_ticket_id > 0')
|
||||
issue_map = {r[0]: r[1] for r in pgc.fetchall()}
|
||||
log(" {} issues mapped".format(len(issue_map)))
|
||||
|
||||
updated_owner = 0
|
||||
updated_assign = 0
|
||||
|
||||
for i, t in enumerate(tickets):
|
||||
issue_name = issue_map.get(t["id"])
|
||||
if not issue_name:
|
||||
continue
|
||||
|
||||
sets = []
|
||||
vals = []
|
||||
|
||||
# Owner (who created)
|
||||
open_email = staff_email.get(t.get("open_by"))
|
||||
if open_email and open_email in valid_users:
|
||||
sets.append('owner = %s')
|
||||
vals.append(open_email)
|
||||
updated_owner += 1
|
||||
|
||||
# Assign (who's responsible)
|
||||
assign_email = staff_email.get(t.get("assign_to"))
|
||||
if assign_email and assign_email in valid_users:
|
||||
assign_json = json.dumps([assign_email])
|
||||
sets.append('"_assign" = %s')
|
||||
vals.append(assign_json)
|
||||
updated_assign += 1
|
||||
|
||||
if sets:
|
||||
vals.append(issue_name)
|
||||
pgc.execute('UPDATE "tabIssue" SET {} WHERE name = %s'.format(", ".join(sets)), vals)
|
||||
|
||||
if (i + 1) % 5000 == 0:
|
||||
pg.commit()
|
||||
log(" [{}/{}] owner={} assign={}".format(i+1, len(tickets), updated_owner, updated_assign))
|
||||
|
||||
pg.commit()
|
||||
pg.close()
|
||||
|
||||
log("")
|
||||
log("=" * 50)
|
||||
log("Owner updated: {}".format(updated_owner))
|
||||
log("Assign updated: {}".format(updated_assign))
|
||||
log("=" * 50)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
90
scripts/migration/fix_sub_address.py
Normal file
90
scripts/migration/fix_sub_address.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Link Subscription → Address via legacy service.delivery_id → delivery → Address."""
|
||||
import pymysql
|
||||
import psycopg2
|
||||
|
||||
LEGACY = {"host": "10.100.80.100", "user": "facturation", "password": "VD67owoj",
|
||||
"database": "gestionclient", "connect_timeout": 30, "read_timeout": 300}
|
||||
PG = {"host": "db", "port": 5432, "user": "postgres", "password": "123",
|
||||
"dbname": "_eb65bdc0c4b1b2d6"}
|
||||
|
||||
def log(msg):
|
||||
print(msg, flush=True)
|
||||
|
||||
def main():
|
||||
log("=== Link Subscriptions → Addresses ===")
|
||||
|
||||
# Legacy: service → delivery_id, delivery → account_id + address
|
||||
mc = pymysql.connect(**LEGACY)
|
||||
cur = mc.cursor(pymysql.cursors.DictCursor)
|
||||
cur.execute("""
|
||||
SELECT s.id as service_id, s.delivery_id, d.account_id, d.address1, d.city
|
||||
FROM service s
|
||||
JOIN delivery d ON s.delivery_id = d.id
|
||||
WHERE s.status = 1
|
||||
""")
|
||||
svc_delivery = {r["service_id"]: r for r in cur.fetchall()}
|
||||
mc.close()
|
||||
log(" {} active services with delivery".format(len(svc_delivery)))
|
||||
|
||||
pg = psycopg2.connect(**PG)
|
||||
pgc = pg.cursor()
|
||||
|
||||
# Subscription legacy_service_id → name
|
||||
pgc.execute('SELECT legacy_service_id, name FROM "tabSubscription" WHERE legacy_service_id > 0')
|
||||
sub_map = {r[0]: r[1] for r in pgc.fetchall()}
|
||||
|
||||
# Address lookup: find address by customer + address_line1 match
|
||||
# Build: (customer_name, address_line1) → address.name
|
||||
pgc.execute("""
|
||||
SELECT a.name, a.address_line1, a.city, dl.link_name as customer
|
||||
FROM "tabAddress" a
|
||||
JOIN "tabDynamic Link" dl ON dl.parent = a.name AND dl.parenttype = 'Address' AND dl.link_doctype = 'Customer'
|
||||
""")
|
||||
# Group by customer
|
||||
addr_by_cust = {}
|
||||
for name, addr1, city, cust in pgc.fetchall():
|
||||
addr_by_cust.setdefault(cust, []).append((name, addr1 or "", city or ""))
|
||||
|
||||
# Customer legacy_account_id → name
|
||||
pgc.execute('SELECT legacy_account_id, name FROM "tabCustomer" WHERE legacy_account_id > 0')
|
||||
cust_map = {r[0]: r[1] for r in pgc.fetchall()}
|
||||
|
||||
updated = 0
|
||||
for svc_id, sub_name in sub_map.items():
|
||||
info = svc_delivery.get(svc_id)
|
||||
if not info:
|
||||
continue
|
||||
|
||||
cust_name = cust_map.get(info["account_id"])
|
||||
if not cust_name:
|
||||
continue
|
||||
|
||||
# Find best matching address for this customer
|
||||
addrs = addr_by_cust.get(cust_name, [])
|
||||
if not addrs:
|
||||
continue
|
||||
|
||||
# Try exact match on address1
|
||||
legacy_addr = (info.get("address1") or "").strip().lower()
|
||||
best = addrs[0][0] # default to first address
|
||||
for aname, a1, acity in addrs:
|
||||
if legacy_addr and legacy_addr in a1.lower():
|
||||
best = aname
|
||||
break
|
||||
|
||||
pgc.execute('UPDATE "tabSubscription" SET service_address = %s WHERE name = %s AND (service_address IS NULL OR service_address = %s)',
|
||||
(best, sub_name, ''))
|
||||
if pgc.rowcount > 0:
|
||||
updated += 1
|
||||
|
||||
if updated % 2000 == 0 and updated > 0:
|
||||
pg.commit()
|
||||
log(" updated={}".format(updated))
|
||||
|
||||
pg.commit()
|
||||
pg.close()
|
||||
log("\nSubscriptions linked to Address: {}".format(updated))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user