""" Map legacy group_ad to ERPNext Role Profiles and assign roles to users. Legacy groups: admin → Full admin access (System Manager + all modules) sysadmin → Technical admin (System Manager, HR, all operations) tech → Field technicians (Dispatch, limited Accounts read) support → Customer support (Support Team, Sales read, Dispatch) comptabilite → Accounting (Accounts Manager, HR User) facturation → Billing (Accounts User, Sales User) Run inside erpnext-backend-1: /home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/setup_user_roles.py """ import frappe import pymysql import os import time import hashlib from datetime import datetime 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) now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") # ═══════════════════════════════════════════════════════════════ # ROLE PROFILE DEFINITIONS # ═══════════════════════════════════════════════════════════════ # Roles each group should have ROLE_MAP = { "admin": [ "System Manager", "Accounts Manager", "Accounts User", "Sales Manager", "Sales User", "HR Manager", "HR User", "Support Team", "Dispatch Technician", "Employee", "Projects Manager", "Stock Manager", "Stock User", "Purchase Manager", "Purchase User", "Website Manager", "Report Manager", "Dashboard Manager", ], "sysadmin": [ "System Manager", "Accounts User", "Sales Manager", "Sales User", "HR User", "Support Team", "Dispatch Technician", "Employee", "Projects Manager", "Stock Manager", "Stock User", "Purchase User", "Website Manager", "Report Manager", "Dashboard Manager", ], "tech": [ "Dispatch Technician", "Employee", "Support Team", "Stock User", ], "support": [ "Support Team", "Employee", "Sales User", "Dispatch Technician", "Accounts User", ], "comptabilite": [ "Accounts Manager", "Accounts User", "Employee", "HR User", "Sales User", "Report Manager", ], "facturation": [ "Accounts User", "Employee", "Sales User", "Report Manager", ], } # Profile display names PROFILE_NAMES = { "admin": "Admin - Full Access", "sysadmin": "SysAdmin - Technical", "tech": "Technician - Field", "support": "Support - Customer Service", "comptabilite": "Comptabilité - Accounting", "facturation": "Facturation - Billing", } # ═══════════════════════════════════════════════════════════════ # PHASE 1: Create/update Role Profiles # ═══════════════════════════════════════════════════════════════ print("\n" + "="*60) print("PHASE 1: CREATE ROLE PROFILES") print("="*60) for group_key, roles in ROLE_MAP.items(): profile_name = PROFILE_NAMES[group_key] # Delete existing profile and its roles frappe.db.sql('DELETE FROM "tabHas Role" WHERE parent = %s AND parenttype = %s', (profile_name, "Role Profile")) if frappe.db.exists("Role Profile", profile_name): frappe.db.sql('DELETE FROM "tabRole Profile" WHERE name = %s', (profile_name,)) # Create profile frappe.db.sql(""" INSERT INTO "tabRole Profile" (name, creation, modified, modified_by, owner, docstatus, idx, role_profile) VALUES (%(name)s, %(now)s, %(now)s, 'Administrator', 'Administrator', 0, 0, %(name)s) """, {"name": profile_name, "now": now_str}) # Add roles for i, role in enumerate(roles): rname = "rp-{}-{}-{}".format(group_key, i, int(time.time())) frappe.db.sql(""" INSERT INTO "tabHas Role" ( name, creation, modified, modified_by, owner, docstatus, idx, parent, parentfield, parenttype, role ) VALUES ( %(name)s, %(now)s, %(now)s, 'Administrator', 'Administrator', 0, %(idx)s, %(parent)s, 'roles', 'Role Profile', %(role)s ) """, {"name": rname, "now": now_str, "idx": i + 1, "parent": profile_name, "role": role}) frappe.db.commit() print(" Created profile '{}' with {} roles: {}".format( profile_name, len(roles), ", ".join(roles[:5]) + ("..." if len(roles) > 5 else ""))) # ═══════════════════════════════════════════════════════════════ # PHASE 2: Get employee → user → group mapping # ═══════════════════════════════════════════════════════════════ print("\n" + "="*60) print("PHASE 2: MAP EMPLOYEES TO ROLES") print("="*60) conn = pymysql.connect( host="10.100.80.100", user="facturation", password="*******", database="gestionclient", cursorclass=pymysql.cursors.DictCursor ) with conn.cursor() as cur: cur.execute(""" SELECT id, username, first_name, last_name, email, group_ad, status FROM staff WHERE status = 1 AND email IS NOT NULL AND email != '' """) active_staff = cur.fetchall() conn.close() # Build email → group_ad map email_to_group = {} for s in active_staff: email = s["email"].strip().lower() if email: email_to_group[email] = (s["group_ad"] or "").strip().lower() print("Active staff with email: {}".format(len(email_to_group))) # Get all ERPNext users erp_users = frappe.db.sql(""" SELECT name FROM "tabUser" WHERE name NOT IN ('Administrator', 'Guest', 'admin@example.com') AND enabled = 1 """, as_dict=True) print("ERPNext users: {}".format(len(erp_users))) # ═══════════════════════════════════════════════════════════════ # PHASE 3: Assign roles to users # ═══════════════════════════════════════════════════════════════ print("\n" + "="*60) print("PHASE 3: ASSIGN ROLES") print("="*60) assigned = 0 skipped = 0 for user in erp_users: email = user["name"].lower() group = email_to_group.get(email) if not group or group not in ROLE_MAP: skipped += 1 continue profile_name = PROFILE_NAMES[group] target_roles = set(ROLE_MAP[group]) # Always add "All" and "Desk User" which are standard target_roles.add("All") target_roles.add("Desk User") # Get current roles current_roles = set() current = frappe.db.sql(""" SELECT role FROM "tabHas Role" WHERE parent = %s AND parenttype = 'User' """, (user["name"],)) for r in current: current_roles.add(r[0]) # Remove roles not in target (except a few we should keep) keep_always = {"All", "Desk User"} to_remove = current_roles - target_roles - keep_always to_add = target_roles - current_roles if to_remove: for role in to_remove: frappe.db.sql(""" DELETE FROM "tabHas Role" WHERE parent = %s AND parenttype = 'User' AND role = %s """, (user["name"], role)) if to_add: for role in to_add: rname = "ur-{}-{}".format(hashlib.md5("{}{}".format(user["name"], role).encode()).hexdigest()[:10], int(time.time())) frappe.db.sql(""" INSERT INTO "tabHas Role" ( name, creation, modified, modified_by, owner, docstatus, idx, parent, parentfield, parenttype, role ) VALUES ( %(name)s, %(now)s, %(now)s, 'Administrator', 'Administrator', 0, 0, %(parent)s, 'roles', 'User', %(role)s ) """, {"name": rname, "now": now_str, "parent": user["name"], "role": role}) # Set role profile on user frappe.db.sql(""" UPDATE "tabUser" SET role_profile_name = %s WHERE name = %s """, (profile_name, user["name"])) assigned += 1 if to_add or to_remove: print(" {} → {} (+{} -{})".format( user["name"], profile_name, len(to_add), len(to_remove))) frappe.db.commit() print("\nAssigned roles to {} users ({} skipped - no legacy group match)".format(assigned, skipped)) # Also link Employee → User print("\n--- Linking Employee → User ---") linked = 0 employees = frappe.db.sql(""" SELECT name, company_email FROM "tabEmployee" WHERE status = 'Active' AND company_email IS NOT NULL """, as_dict=True) for emp in employees: email = emp["company_email"] # Check if user exists user_exists = frappe.db.exists("User", email) if user_exists: frappe.db.sql(""" UPDATE "tabEmployee" SET user_id = %s WHERE name = %s """, (email, emp["name"])) linked += 1 frappe.db.commit() print("Linked {} employees to their User accounts".format(linked)) # ═══════════════════════════════════════════════════════════════ # PHASE 4: VERIFY # ═══════════════════════════════════════════════════════════════ print("\n" + "="*60) print("PHASE 4: VERIFY") print("="*60) # Count users per role profile by_profile = frappe.db.sql(""" SELECT role_profile_name, COUNT(*) as cnt FROM "tabUser" WHERE role_profile_name IS NOT NULL AND role_profile_name != '' GROUP BY role_profile_name ORDER BY cnt DESC """, as_dict=True) print("Users by Role Profile:") for p in by_profile: print(" {}: {}".format(p["role_profile_name"], p["cnt"])) # Sample users with their roles sample_users = frappe.db.sql(""" SELECT u.name, u.full_name, u.role_profile_name, STRING_AGG(hr.role, ', ' ORDER BY hr.role) as roles FROM "tabUser" u LEFT JOIN "tabHas Role" hr ON hr.parent = u.name AND hr.parenttype = 'User' WHERE u.role_profile_name IS NOT NULL AND u.role_profile_name != '' GROUP BY u.name, u.full_name, u.role_profile_name ORDER BY u.role_profile_name, u.name """, as_dict=True) print("\nUsers and their roles:") for u in sample_users: print(" {} ({}) → {} roles: {}".format( u["name"], u["full_name"] or "", u["role_profile_name"], u["roles"][:80] if u["roles"] else "(none)")) frappe.clear_cache() print("\nDone — cache cleared")