From bc994927c82eb2702c559f4f0a50dafd614d4477 Mon Sep 17 00:00:00 2001 From: Ali <010109ali@gmail.com> Date: Mon, 4 May 2026 15:35:39 +0000 Subject: [PATCH] setup: re-add Employees /desk icon under Frappe HR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HRMS v16 dropped the old Employee Lifecycle workspace (see hrms/patches/v16_0/delete_old_workspaces.py), which removed its desktop icon from the Frappe HR folder. This adds it back as a jey_theme-owned record so HRMS upgrades stay untouched. The icon is link_type='Workspace Sidebar' pointing to a minimal Workspace Sidebar 'Employees' that has a single DocType link to Employee — Frappe's DesktopIcon.is_permitted requires a sidebar named after the icon's label or it filters the icon out of bootinfo, so an External link alone is not enough. No Workspace page is created. Click routes to /app/employee via get_route_for_icon. Idempotent setup runs on after_migrate, mirroring taxes_workspace. Co-Authored-By: Claude Opus 4.7 (1M context) --- jey_theme/hooks.py | 8 ++- jey_theme/setup/employees_icon.py | 101 ++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 jey_theme/setup/employees_icon.py diff --git a/jey_theme/hooks.py b/jey_theme/hooks.py index cca47ec..bea3f9d 100644 --- a/jey_theme/hooks.py +++ b/jey_theme/hooks.py @@ -28,8 +28,12 @@ has_permission = { "*": "jey_theme.access_control.has_permission", } -# Idempotent setup of the custom Taxes workspace (see setup/taxes_workspace.py). -after_migrate = ["jey_theme.setup.taxes_workspace.setup"] +# Idempotent setup of the custom Taxes workspace (see setup/taxes_workspace.py) +# and the Employees /desk icon under Frappe HR (see setup/employees_icon.py). +after_migrate = [ + "jey_theme.setup.taxes_workspace.setup", + "jey_theme.setup.employees_icon.setup", +] # override_doctype_class = { # "Report": "jey_theme.custom_report.CustomReport" diff --git a/jey_theme/setup/employees_icon.py b/jey_theme/setup/employees_icon.py new file mode 100644 index 0000000..5e72390 --- /dev/null +++ b/jey_theme/setup/employees_icon.py @@ -0,0 +1,101 @@ +"""Idempotent setup of an 'Employees' /desk icon under the Frappe HR folder. + +HRMS v16 dropped the old 'Employee Lifecycle' workspace (see +hrms/patches/v16_0/delete_old_workspaces.py), which removed its desktop +icon from the Frappe HR folder on /desk. This re-adds an icon that goes +straight to the Employee DocType list, owned by jey_theme so HRMS stays +untouched. + +Frappe's DesktopIcon.is_permitted (frappe/desk/doctype/desktop_icon/ +desktop_icon.py) requires a Workspace Sidebar named after the icon's +label to exist, otherwise the icon is filtered out of bootinfo even when +link_type='External'. So we also create a minimal Workspace Sidebar with +one item — a DocType Link to Employee — that get_route_for_icon picks up +and routes to /app/employee. No Workspace page is created. +""" + +import frappe + +DESKTOP_ICON_LABEL = "Employees" +DESKTOP_ICON_PARENT = "Frappe HR" +DESKTOP_ICON_ICON = "users" +DESKTOP_ICON_IDX = 0 + +SIDEBAR_NAME = "Employees" +TARGET_DOCTYPE = "Employee" + + +def ensure_workspace_sidebar() -> str: + item = { + "type": "Link", + "label": TARGET_DOCTYPE, + "link_type": "DocType", + "link_to": TARGET_DOCTYPE, + "icon": DESKTOP_ICON_ICON, + } + + if frappe.db.exists("Workspace Sidebar", SIDEBAR_NAME): + sb = frappe.get_doc("Workspace Sidebar", SIDEBAR_NAME) + sb.title = SIDEBAR_NAME + sb.header_icon = DESKTOP_ICON_ICON + sb.app = "jey_theme" + sb.standard = 1 + sb.items = [] + sb.append("items", item) + sb.save(ignore_permissions=True) + return f"updated Workspace Sidebar {SIDEBAR_NAME!r}" + + sb = frappe.get_doc({ + "doctype": "Workspace Sidebar", + "name": SIDEBAR_NAME, + "title": SIDEBAR_NAME, + "header_icon": DESKTOP_ICON_ICON, + "app": "jey_theme", + "standard": 1, + "items": [item], + }) + sb.insert(ignore_permissions=True) + return f"created Workspace Sidebar {SIDEBAR_NAME!r}" + + +def ensure_desktop_icon() -> str: + existing = frappe.db.exists("Desktop Icon", {"label": DESKTOP_ICON_LABEL}) + if existing: + icon = frappe.get_doc("Desktop Icon", existing) + icon.icon = DESKTOP_ICON_ICON + icon.icon_type = "Link" + icon.link_type = "Workspace Sidebar" + icon.link_to = SIDEBAR_NAME + icon.link = None + icon.parent_icon = DESKTOP_ICON_PARENT + icon.app = "jey_theme" + icon.standard = 1 + icon.hidden = 0 + icon.idx = DESKTOP_ICON_IDX + icon.save(ignore_permissions=True) + return f"updated Desktop Icon {DESKTOP_ICON_LABEL!r}" + + icon = frappe.get_doc({ + "doctype": "Desktop Icon", + "label": DESKTOP_ICON_LABEL, + "icon": DESKTOP_ICON_ICON, + "icon_type": "Link", + "link_type": "Workspace Sidebar", + "link_to": SIDEBAR_NAME, + "parent_icon": DESKTOP_ICON_PARENT, + "app": "jey_theme", + "standard": 1, + "hidden": 0, + "idx": DESKTOP_ICON_IDX, + }) + icon.insert(ignore_permissions=True) + return f"created Desktop Icon {DESKTOP_ICON_LABEL!r}" + + +def setup(): + """Entry point — safe to call repeatedly (e.g. from after_migrate).""" + results = [ensure_workspace_sidebar(), ensure_desktop_icon()] + frappe.db.commit() + for r in results: + if r: + print(r)