"""Fix sidebar workspaces — use simple names without spaces.""" import requests import json ERPNEXT_URL = "http://localhost:8080" 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"); exit(1) print("Logged in") def api_post(endpoint, data): return session.post(f"{ERPNEXT_URL}{endpoint}", json=data) def api_put(endpoint, data): return session.put(f"{ERPNEXT_URL}{endpoint}", json=data) def api_get(endpoint): return session.get(f"{ERPNEXT_URL}{endpoint}") def api_delete(endpoint): return session.delete(f"{ERPNEXT_URL}{endpoint}") # Delete broken workspaces first print("\n1. Cleaning broken workspaces...") for name in ["Clienti", "CRM DiDi", "Servicii DiDi", "Plati DiDi", "Contabilitate DiDi", "Website CMS DiDi"]: r = api_delete(f"/api/resource/Workspace/{requests.utils.quote(name)}") print(f" Delete {name}: {r.status_code}") # Workspace definitions — simple slugified names WORKSPACES = [ { "label": "Facturi", "title": "Facturi", "icon": "file-text", "indicator_color": "green", "sequence_id": 10, "shortcuts": [ {"type": "DocType", "link_to": "Sales Invoice", "label": "Toate Facturile", "color": "Green", "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"}, ], }, { "label": "Clienti", "title": "Clienti", "icon": "users", "indicator_color": "blue", "sequence_id": 20, "shortcuts": [ {"type": "DocType", "link_to": "Customer", "label": "Toti Clientii", "color": "Blue", "doc_view": "List"}, {"type": "DocType", "link_to": "Service Agreement", "label": "Acorduri Servicii", "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"}, ], }, { "label": "CRM-DiDi", "title": "CRM", "icon": "share", "indicator_color": "orange", "sequence_id": 30, "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"}, ], }, { "label": "Servicii", "title": "Servicii", "icon": "box", "indicator_color": "purple", "sequence_id": 40, "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"}, ], }, { "label": "Plati", "title": "Plati", "icon": "credit-card", "indicator_color": "yellow", "sequence_id": 50, "shortcuts": [ {"type": "DocType", "link_to": "Payment Log", "label": "Payment Log", "color": "Yellow", "doc_view": "List"}, {"type": "DocType", "link_to": "Analysis Report", "label": "Rapoarte Analiza", "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"}, ], }, { "label": "Contabilitate", "title": "Contabilitate", "icon": "calculator", "indicator_color": "", "sequence_id": 60, "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"}, ], }, { "label": "Website-CMS", "title": "Website CMS", "icon": "globe", "indicator_color": "cyan", "sequence_id": 70, "shortcuts": [ {"type": "DocType", "link_to": "Website Content", "label": "Continut Website", "color": "Cyan", "doc_view": "List"}, {"type": "DocType", "link_to": "Email Template", "label": "Template Email", "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"}, ], }, ] print("\n2. Creating workspaces...") for ws in WORKSPACES: label = ws["label"] # Check if already exists check = api_get(f"/api/resource/Workspace/{requests.utils.quote(label)}") payload = { "label": label, "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": json.dumps([ {"id": "h1", "type": "header", "data": {"text": f"{ws['title']}", "col": 12}}, ] + [ {"id": f"s{i}", "type": "shortcut", "data": {"shortcut_name": s["label"], "col": 4}} for i, s in enumerate(ws["shortcuts"]) ]), "shortcuts": ws["shortcuts"], "links": ws["links"], } if check.status_code == 200: r = api_put(f"/api/resource/Workspace/{requests.utils.quote(label)}", payload) status = "Updated" if r.status_code == 200 else f"Error {r.status_code}" else: r = api_post("/api/resource/Workspace", payload) status = "Created" if r.status_code in (200, 201) else f"Error {r.status_code}" if r.status_code not in (200, 201): print(f" {label}: {status} — {r.text[:200]}") continue print(f" {label}: {status}") # Verify print("\n3. Final sidebar:") r = session.post(f"{ERPNEXT_URL}/api/method/frappe.client.get_list", json={ "doctype": "Workspace", "filters": [["public", "=", 1], ["is_hidden", "=", 0]], "fields": ["name", "title", "icon", "sequence_id"], "order_by": "sequence_id asc", "limit_page_length": 20, }) for ws in r.json().get("message", []): print(f" [{ws.get('sequence_id', '?'):>3}] {ws.get('icon', '?'):15s} {ws['title']:20s} → /app/{ws['name'].lower().replace(' ', '-')}") print("\nDone! Ctrl+Shift+R in browser.")