""" Create clean sidebar workspaces for DiDi ERPNext. Run: python setup-sidebar.py """ import requests import json ERPNEXT_URL = "http://localhost:8080" # Login as Administrator session = requests.Session() login = session.post(f"{ERPNEXT_URL}/api/method/login", data={"usr": "Administrator", "pwd": "admin"}) if login.status_code != 200: print(f"Login failed: {login.status_code}") exit(1) print("Logged in as Administrator") def api(method, endpoint, data=None): url = f"{ERPNEXT_URL}{endpoint}" if method == "GET": r = session.get(url, params=data) elif method == "POST": r = session.post(url, json=data) elif method == "PUT": r = session.put(url, json=data) elif method == "DELETE": r = session.delete(url) else: return None return r # ─── 1. Give API user Workspace Manager role ─── print("\n1. Adding Workspace Manager role to API user...") try: r = session.post(f"{ERPNEXT_URL}/api/method/frappe.client.get_list", json={ "doctype": "Has Role", "filters": {"parent": "website_api@didi-erp", "role": "Workspace Manager"}, "fields": ["name"], }) existing = r.json().get("message", []) if not existing: user_doc = session.get(f"{ERPNEXT_URL}/api/resource/User/website_api@didi-erp").json()["data"] roles = user_doc.get("roles", []) roles.append({"role": "Workspace Manager"}) session.put(f"{ERPNEXT_URL}/api/resource/User/website_api@didi-erp", json={"roles": roles}) print(" Added Workspace Manager role") else: print(" Already has Workspace Manager role") except Exception as e: print(f" Warning: {e}") # ─── 2. Hide old workspaces ─── print("\n2. Hiding old/unused workspaces...") to_hide = [ "DiDi Platform", "DiDi", "Accounting", "CRM", "Selling", "Assets", "Build", "Buying", "ERPNext Integrations", "ERPNext Settings", "Financial Reports", "Integrations", "Manufacturing", "Payables", "Projects", "Quality", "Receivables", "Stock", "Support", ] for ws_name in to_hide: try: r = api("PUT", f"/api/resource/Workspace/{requests.utils.quote(ws_name)}", {"is_hidden": 1}) if r.status_code == 200: print(f" Hidden: {ws_name}") else: print(f" Skip (not found or error): {ws_name}") except: pass # ─── 3. Create new sidebar workspaces ─── print("\n3. Creating sidebar workspaces...") WORKSPACES = [ { "name": "Facturi", "title": "Facturi", "icon": "file-text", "indicator_color": "green", "sequence_id": 10, "content": json.dumps([ {"id": "h1", "type": "header", "data": {"text": "Facturare", "col": 12}}, {"id": "s1", "type": "shortcut", "data": {"shortcut_name": "Toate Facturile", "col": 4}}, {"id": "s2", "type": "shortcut", "data": {"shortcut_name": "Facturi Platite", "col": 4}}, {"id": "s3", "type": "shortcut", "data": {"shortcut_name": "Payment Entry", "col": 4}}, ]), "shortcuts": [ {"type": "DocType", "link_to": "Sales Invoice", "label": "Toate Facturile", "color": "Green", "doc_view": "List"}, {"type": "DocType", "link_to": "Sales Invoice", "label": "Facturi Platite", "color": "Blue", "doc_view": "List"}, {"type": "DocType", "link_to": "Payment Entry", "label": "Payment Entry", "color": "Yellow", "doc_view": "List"}, ], "links": [ {"type": "Card Break", "label": "Facturi"}, {"type": "Link", "label": "Sales Invoice", "link_to": "Sales Invoice", "link_type": "DocType", "onboard": 1}, {"type": "Link", "label": "Payment Entry", "link_to": "Payment Entry", "link_type": "DocType"}, {"type": "Link", "label": "Taxe (TVA)", "link_to": "Sales Taxes and Charges Template", "link_type": "DocType"}, ], }, { "name": "Clienti", "title": "Clienti", "icon": "user", "indicator_color": "blue", "sequence_id": 20, "content": json.dumps([ {"id": "h1", "type": "header", "data": {"text": "Clienti", "col": 12}}, {"id": "s1", "type": "shortcut", "data": {"shortcut_name": "Toti Clientii", "col": 6}}, {"id": "s2", "type": "shortcut", "data": {"shortcut_name": "Service Agreement", "col": 6}}, ]), "shortcuts": [ {"type": "DocType", "link_to": "Customer", "label": "Toti Clientii", "color": "Blue", "doc_view": "List"}, {"type": "DocType", "link_to": "Service Agreement", "label": "Service Agreement", "color": "Purple", "doc_view": "List"}, ], "links": [ {"type": "Card Break", "label": "Clienti"}, {"type": "Link", "label": "Customer", "link_to": "Customer", "link_type": "DocType", "onboard": 1}, {"type": "Link", "label": "Service Agreement", "link_to": "Service Agreement", "link_type": "DocType"}, {"type": "Link", "label": "Subscription Plan", "link_to": "Subscription Plan", "link_type": "DocType"}, ], }, { "name": "CRM DiDi", "title": "CRM", "icon": "share", "indicator_color": "orange", "sequence_id": 30, "content": json.dumps([ {"id": "h1", "type": "header", "data": {"text": "CRM & Lead Management", "col": 12}}, {"id": "s1", "type": "shortcut", "data": {"shortcut_name": "Lead-uri", "col": 4}}, {"id": "s2", "type": "shortcut", "data": {"shortcut_name": "Sales Stage", "col": 4}}, {"id": "s3", "type": "shortcut", "data": {"shortcut_name": "Lead Source", "col": 4}}, ]), "shortcuts": [ {"type": "DocType", "link_to": "Lead", "label": "Lead-uri", "color": "Orange", "doc_view": "List"}, {"type": "DocType", "link_to": "Sales Stage", "label": "Sales Stage", "color": "Yellow"}, {"type": "DocType", "link_to": "Lead Source", "label": "Lead Source", "color": "Grey"}, ], "links": [ {"type": "Card Break", "label": "CRM"}, {"type": "Link", "label": "Lead", "link_to": "Lead", "link_type": "DocType", "onboard": 1}, {"type": "Link", "label": "Sales Stage", "link_to": "Sales Stage", "link_type": "DocType"}, {"type": "Link", "label": "Lead Source", "link_to": "Lead Source", "link_type": "DocType"}, ], }, { "name": "Servicii DiDi", "title": "Servicii", "icon": "box", "indicator_color": "purple", "sequence_id": 40, "content": json.dumps([ {"id": "h1", "type": "header", "data": {"text": "Servicii & Produse", "col": 12}}, {"id": "s1", "type": "shortcut", "data": {"shortcut_name": "Articole", "col": 6}}, {"id": "s2", "type": "shortcut", "data": {"shortcut_name": "Planuri Abonament", "col": 6}}, ]), "shortcuts": [ {"type": "DocType", "link_to": "Item", "label": "Articole", "color": "Purple", "doc_view": "List"}, {"type": "DocType", "link_to": "Subscription Plan", "label": "Planuri Abonament", "color": "Cyan"}, ], "links": [ {"type": "Card Break", "label": "Catalog"}, {"type": "Link", "label": "Item", "link_to": "Item", "link_type": "DocType", "onboard": 1}, {"type": "Link", "label": "Item Group", "link_to": "Item Group", "link_type": "DocType"}, {"type": "Link", "label": "Subscription Plan", "link_to": "Subscription Plan", "link_type": "DocType"}, ], }, { "name": "Plati DiDi", "title": "Plati", "icon": "credit-card", "indicator_color": "yellow", "sequence_id": 50, "content": json.dumps([ {"id": "h1", "type": "header", "data": {"text": "Plati Stripe & Log", "col": 12}}, {"id": "s1", "type": "shortcut", "data": {"shortcut_name": "Payment Log", "col": 6}}, {"id": "s2", "type": "shortcut", "data": {"shortcut_name": "Analysis Report", "col": 6}}, ]), "shortcuts": [ {"type": "DocType", "link_to": "Payment Log", "label": "Payment Log", "color": "Yellow", "doc_view": "List"}, {"type": "DocType", "link_to": "Analysis Report", "label": "Analysis Report", "color": "Pink", "doc_view": "List"}, ], "links": [ {"type": "Card Break", "label": "Plati"}, {"type": "Link", "label": "Payment Log", "link_to": "Payment Log", "link_type": "DocType", "onboard": 1}, {"type": "Link", "label": "Analysis Report", "link_to": "Analysis Report", "link_type": "DocType"}, ], }, { "name": "Contabilitate DiDi", "title": "Contabilitate", "icon": "calculator", "indicator_color": "grey", "sequence_id": 60, "content": json.dumps([ {"id": "h1", "type": "header", "data": {"text": "Contabilitate", "col": 12}}, {"id": "s1", "type": "shortcut", "data": {"shortcut_name": "Plan Conturi", "col": 4}}, {"id": "s2", "type": "shortcut", "data": {"shortcut_name": "Furnizori", "col": 4}}, {"id": "s3", "type": "shortcut", "data": {"shortcut_name": "Companie", "col": 4}}, ]), "shortcuts": [ {"type": "DocType", "link_to": "Account", "label": "Plan Conturi", "color": "Grey", "doc_view": "Tree"}, {"type": "DocType", "link_to": "Supplier", "label": "Furnizori", "color": "Blue"}, {"type": "DocType", "link_to": "Company", "label": "Companie", "color": "Green"}, ], "links": [ {"type": "Card Break", "label": "Contabilitate"}, {"type": "Link", "label": "Account", "link_to": "Account", "link_type": "DocType", "onboard": 1}, {"type": "Link", "label": "Company", "link_to": "Company", "link_type": "DocType"}, {"type": "Link", "label": "Supplier", "link_to": "Supplier", "link_type": "DocType"}, {"type": "Link", "label": "Purchase Invoice", "link_to": "Purchase Invoice", "link_type": "DocType"}, {"type": "Link", "label": "Sales Taxes Template", "link_to": "Sales Taxes and Charges Template", "link_type": "DocType"}, ], }, { "name": "Website CMS DiDi", "title": "Website CMS", "icon": "globe", "indicator_color": "cyan", "sequence_id": 70, "content": json.dumps([ {"id": "h1", "type": "header", "data": {"text": "Continut Website", "col": 12}}, {"id": "s1", "type": "shortcut", "data": {"shortcut_name": "Website Content", "col": 6}}, {"id": "s2", "type": "shortcut", "data": {"shortcut_name": "Email Template", "col": 6}}, ]), "shortcuts": [ {"type": "DocType", "link_to": "Website Content", "label": "Website Content", "color": "Cyan", "doc_view": "List"}, {"type": "DocType", "link_to": "Email Template", "label": "Email Template", "color": "Grey"}, ], "links": [ {"type": "Card Break", "label": "CMS"}, {"type": "Link", "label": "Website Content", "link_to": "Website Content", "link_type": "DocType", "onboard": 1}, {"type": "Link", "label": "Email Template", "link_to": "Email Template", "link_type": "DocType"}, ], }, ] for ws in WORKSPACES: name = ws["name"] # Check if exists check = session.get(f"{ERPNEXT_URL}/api/resource/Workspace/{requests.utils.quote(name)}") if check.status_code == 200: # Update it r = api("PUT", f"/api/resource/Workspace/{requests.utils.quote(name)}", { "title": ws["title"], "icon": ws["icon"], "indicator_color": ws["indicator_color"], "is_hidden": 0, "public": 1, "module": "Didi Custom", "sequence_id": ws["sequence_id"], "content": ws["content"], "shortcuts": ws["shortcuts"], "links": ws["links"], }) if r.status_code == 200: print(f" Updated: {name}") else: print(f" Error updating {name}: {r.status_code} {r.text[:200]}") else: # Create it r = api("POST", "/api/resource/Workspace", { "name": name, "label": name, "title": ws["title"], "icon": ws["icon"], "indicator_color": ws["indicator_color"], "is_hidden": 0, "public": 1, "module": "Didi Custom", "sequence_id": ws["sequence_id"], "content": ws["content"], "shortcuts": ws["shortcuts"], "links": ws["links"], }) if r.status_code in (200, 201): print(f" Created: {name}") else: print(f" Error creating {name}: {r.status_code} {r.text[:200]}") print("\n4. Verifying sidebar...") r = session.get(f"{ERPNEXT_URL}/api/resource/Workspace", params={ "filters": json.dumps([["public", "=", 1], ["is_hidden", "=", 0]]), "fields": json.dumps(["name", "title", "icon", "sequence_id"]), "order_by": "sequence_id asc", "limit_page_length": 20, }) for ws in r.json().get("data", []): print(f" [{ws.get('sequence_id', '?')}] {ws['icon'] or '?'} {ws['title']}") print("\nDone! Refresh ERPNext in browser (Ctrl+Shift+R).")