feat: Phase 7 — 45 ERPNext Users from legacy staff

- 45 users created with Authentik SSO (no password)
- Roles assigned: System Manager, Support Team, Sales/Accounts
- Service accounts skipped (admin, tech, dev, inventaire, agent)
- Email = Authentik identity link

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-03-28 15:13:31 -04:00
parent 7a15bfd600
commit ac9b367334

View File

@ -0,0 +1,148 @@
#!/usr/bin/env python3
"""
Create ERPNext Users from legacy staff + link to Authentik via email.
Authentik forwardAuth sends X-authentik-email header ERPNext matches by email.
Direct PG. Detached.
"""
import pymysql
import psycopg2
import uuid
from datetime import datetime, timezone
from html import unescape
LEGACY = {"host": "10.100.80.100", "user": "facturation", "password": "VD67owoj",
"database": "gestionclient", "connect_timeout": 30, "read_timeout": 120}
PG = {"host": "db", "port": 5432, "user": "postgres", "password": "123",
"dbname": "_eb65bdc0c4b1b2d6"}
ADMIN = "Administrator"
# Roles for ISP staff
ROLES_ALL = ["System Manager", "Support Team", "Sales User", "Accounts User"]
ROLES_TECH = ["Support Team"]
ROLES_ADMIN = ["System Manager", "Support Team", "Sales User", "Accounts User", "Sales Manager", "Accounts Manager"]
# Staff IDs that are admin/sysadmin
ADMIN_IDS = {2, 4, 11, 12, 3293, 4661, 4664, 4671, 4749}
# Staff IDs that are service accounts (skip)
SKIP_IDS = {1, 3301, 3393, 4663, 4682}
def uid(prefix=""):
return prefix + uuid.uuid4().hex[:10]
def now():
return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S.%f")
def clean(val):
if not val: return ""
return unescape(str(val)).strip()
def log(msg):
print(msg, flush=True)
def main():
ts = now()
log("=== User Migration ===")
mc = pymysql.connect(**LEGACY)
cur = mc.cursor(pymysql.cursors.DictCursor)
cur.execute("SELECT * FROM staff WHERE status = 1 ORDER BY id")
staff = cur.fetchall()
mc.close()
log("{} active staff loaded".format(len(staff)))
pg = psycopg2.connect(**PG)
pgc = pg.cursor()
# Existing users
pgc.execute('SELECT name FROM "tabUser"')
existing = set(r[0] for r in pgc.fetchall())
created = skipped = errors = 0
for s in staff:
sid = s["id"]
if sid in SKIP_IDS:
skipped += 1
continue
email = clean(s.get("email"))
if not email or "@" not in email:
skipped += 1
continue
if email in existing or email.lower() in {e.lower() for e in existing}:
skipped += 1
continue
first = clean(s["first_name"])
last = clean(s["last_name"])
full = "{} {}".format(first, last).strip()
username = clean(s.get("username"))
cell = clean(s.get("cell"))
# Determine roles
if sid in ADMIN_IDS:
roles = ROLES_ADMIN
else:
roles = ROLES_ALL
try:
# Create User
pgc.execute("""
INSERT INTO "tabUser" (
name, creation, modified, modified_by, owner, docstatus, idx,
email, first_name, last_name, full_name, username,
mobile_no, language, time_zone, enabled,
user_type, role_profile_name,
new_password
) VALUES (
%s, %s, %s, %s, %s, 0, 0,
%s, %s, %s, %s, %s,
%s, 'fr', 'America/Toronto', 1,
'System User', NULL,
''
)
""", (email, ts, ts, ADMIN, ADMIN,
email, first, last or None, full, username,
cell or None))
# Create Role assignments
for i, role in enumerate(roles):
pgc.execute("""
INSERT INTO "tabHas Role" (
name, creation, modified, modified_by, owner, docstatus, idx,
role, parent, parentfield, parenttype
) VALUES (%s, %s, %s, %s, %s, 0, %s, %s, %s, 'roles', 'User')
""", (uid("HR-"), ts, ts, ADMIN, ADMIN, i+1, role, email))
# Create UserRole for "All"
pgc.execute("""
INSERT INTO "tabHas Role" (
name, creation, modified, modified_by, owner, docstatus, idx,
role, parent, parentfield, parenttype
) VALUES (%s, %s, %s, %s, %s, 0, %s, 'All', %s, 'roles', 'User')
""", (uid("HR-"), ts, ts, ADMIN, ADMIN, len(roles)+1, email))
created += 1
log(" OK {} ({})".format(full, email))
except Exception as e:
errors += 1
pg.rollback()
log(" ERR {} -> {}".format(email, str(e)[:80]))
continue
pg.commit()
pg.close()
log("")
log("=" * 50)
log("Users: {} created, {} skipped, {} errors".format(created, skipped, errors))
log("=" * 50)
log("")
log("Users are created with NO password (Authentik SSO handles login).")
log("Next: bench --site erp.gigafibre.ca clear-cache")
if __name__ == "__main__":
main()