Hi all,
Until Frappe team adds or finds a solution for extending current workspace i have come with the following work solution …
First you need to add under hooks.py override_module_workspace = { ‘Payroll’: 'customapp.workspace_overrides.salary_payout.salary_payout.json"}
After copy original salary_payout.json to you customapp/workspace_overrides
Inside your copied salary_payout paste from your Custom app the Cards you want to add or just create on the file…
My case i need to add Custom Reports - Report1, Report 2, Report 3
Also if you copied from your Custom app the list of reports from the context copy your and append to the salary_payout …
Mine custom report content had {"id":"MzU3sonXYo","type":"card","data":{"card_name":"Recibos de Sal\u00e1rio","col":4}
On the Override file i just added as the last record of the Content
“[{"id":"jvTZ8RvO42","type":"header","data":{"text":"<span class=\"h4\">Your Shortcuts","col":12}},{"id":"hNVIisuaFR","type":"shortcut","data":{"shortcut_name":"Salary Slip","col":3}},{"id":"6XUIWHJ3jI","type":"shortcut","data":{"shortcut_name":"Payroll Entry","col":3}},{"id":"yjIBi3GMoo","type":"shortcut","data":{"shortcut_name":"Salary Register","col":3}},{"id":"ORKhwX-uqw","type":"spacer","data":{"col":12}},{"id":"gGURwviUAZ","type":"header","data":{"text":"<span class=\"h4\">Transactions & Reports","col":12}},{"id":"m7ibJXxzpl","type":"card","data":{"card_name":"Masters","col":4}},{"id":"U-jv2v4nCv","type":"card","data":{"card_name":"Payroll","col":4}},{"id":"LG69O3ku4y","type":"card","data":{"card_name":"Incentives","col":4}},{"id":"kOuItimoNm","type":"card","data":{"card_name":"Accounting","col":4}},{"id":"UJqBhPqNZd","type":"card","data":{"card_name":"Accounting Reports","col":4}},{"id":"eNZuk6i-jy","type":"card","data":{"card_name":"Payroll Reports","col":4}},{"id":"ll91Zs2cbx","type":"card","data":{"card_name":"Deduction Reports","col":4}},{"id":"Rtbe3KmbBf","type":"card","data":{"card_name":"Relat\u00f3rios RH","col":4}},{"id":"MzU3sonXYo","type":"card","data":{"card_name":"Recibos de Sal\u00e1rio","col":4}}]”
Now that the files are ready… lets hack desktop.py
Create your customdesktop.py under Customapp.overrides and paste this…
from functools import wraps
from json import dumps, loads
import frappe
from frappe import DoesNotExistError, ValidationError, _, _dict
from frappe.boot import get_allowed_pages, get_allowed_reports
from frappe.cache_manager import (
build_domain_restriced_doctype_cache,
build_domain_restriced_page_cache,
build_table_count_cache,
)
from frappe.core.doctype.custom_role.custom_role import get_custom_allowed_roles
from frappe.desk.desktop import Workspace
@frappe.whitelist()
@frappe.read_only()
def get_desktop_page(page):
“”"Applies permissions, customizations and returns the configruration for a page
on desk.
Args:
page (json): page data
Returns:
dict: dictionary of cards, charts and shortcuts to be displayed on website
"""
try:
#HELKYDS 10-02-2024
workspace_overrides = frappe.get_hooks("override_module_workspace")
if workspace_overrides and workspace_overrides.get(loads(page)['parent_page']):
import_file = workspace_overrides[loads(page)['parent_page']][-1]
module_path, classname, filetype = import_file.rsplit(".", 2)
module = frappe.get_module(module_path)
module_name = import_file.split(".", 1)[0]
if not filetype == "json":
raise ImportError(f"{loads(page)['parent_page']}: {import_file} is not a JSON file")
if module:
tmp_mpath, tmp = frappe.modules.get_module_path(module_name).rsplit("/",1)
mpath = tmp_mpath + import_file.replace(module_name,'').replace('.json','').replace('.','/') + ".json"
with open(mpath) as ws_file:
workspace_contents = ws_file.read()
parsed_json = loads(workspace_contents)
|
|
|
|
|
#FIX 13-02-2024; To avoid load on other pages just bcs parent is the same |
|
|
|
|
|
if parsed_json[‘name’] == loads(page)[‘name’]: |
#Replace page
page = workspace_contents
#TODO:Needs to rewrite the content based on the Card break from the JSON
for pp,valor in enumerate(parsed_json['links']):
existe_workspacelink = False
for d in frappe.get_all("Workspace Link", fields=["*"], filters={"parent": parsed_json['name']}):
if d.label == valor['label']:
existe_workspacelink = True
break
if not existe_workspacelink:
print ('Adds to...... WORKSPACE')
wwlink = frappe.get_doc('Workspace',parsed_json['name'])
links = wwlink.get("links")
if valor['type'] == "Card Break":
wwlink.append(
"links",
{
"label": valor['label'],
"type": valor['type'],
"link_count": valor['link_count'],
"onboard": valor['onboard'],
"hidden": valor['hidden'],
"is_query_report": valor['is_query_report'],
"idx": 1 if not wwlink.links else wwlink.links[-1].idx + 1,
},
)
else:
wwlink.append(
"links",
{
"label": valor['label'],
"type": valor['type'],
"link_type": valor['link_type'],
"link_to": valor['link_to'],
"link_count": valor['link_count'],
"onboard": valor['onboard'],
"hidden": valor['hidden'],
"is_query_report": valor['is_query_report'],
"idx": wwlink.links[-1].idx + 1,
},
)
wwlink.save(ignore_permissions=True)
frappe.db.commit()
workspace = Workspace(loads(page))
workspace.build_workspace()
return {
"charts": workspace.charts,
"shortcuts": workspace.shortcuts,
"cards": workspace.cards,
"onboardings": workspace.onboardings,
"quick_lists": workspace.quick_lists,
"number_cards": workspace.number_cards,
"custom_blocks": workspace.custom_blocks,
}
else:
workspace = Workspace(loads(page))
workspace.build_workspace()
return {
"charts": workspace.charts,
"shortcuts": workspace.shortcuts,
"cards": workspace.cards,
"onboardings": workspace.onboardings,
"quick_lists": workspace.quick_lists,
"number_cards": workspace.number_cards,
"custom_blocks": workspace.custom_blocks,
}
except DoesNotExistError:
frappe.log_error("Workspace Missing")
return {}
@frappe.whitelist()
def get_workspace_sidebar_items():
“”“Get list of sidebar items for desk”“”
has_access = “Workspace Manager” in frappe.get_roles()
# don't get domain restricted pages
blocked_modules = frappe.get_doc("User", frappe.session.user).get_blocked_modules()
blocked_modules.append("Dummy Module")
# adding None to allowed_domains to include pages without domain restriction
allowed_domains = [None] + frappe.get_active_domains()
filters = {
"restrict_to_domain": ["in", allowed_domains],
"module": ["not in", blocked_modules],
}
if has_access:
filters = []
# pages sorted based on sequence id
order_by = "sequence_id asc"
fields = [
"name",
"title",
"for_user",
"parent_page",
"content",
"public",
"module",
"icon",
"is_hidden",
]
all_pages = frappe.get_all(
"Workspace", fields=fields, filters=filters, order_by=order_by, ignore_permissions=True
)
#HELKYDS 12-02-2024; To load CUSTOM workspace...
for page in all_pages:
workspace_overrides = frappe.get_hooks("override_module_workspace")
if workspace_overrides and workspace_overrides.get(page.parent_page):
import_file = workspace_overrides[page.parent_page][-1]
module_path, classname, filetype = import_file.rsplit(".", 2)
module = frappe.get_module(module_path)
module_name = import_file.split(".", 1)[0]
if not filetype == "json":
raise ImportError(f"{page.parent_page}: {import_file} is not a JSON file")
if module:
tmp_mpath, tmp = frappe.modules.get_module_path(module_name).rsplit("/",1)
mpath = tmp_mpath + import_file.replace(module_name,'').replace('.json','').replace('.','/') + ".json"
with open(mpath) as ws_file:
workspace_contents = ws_file.read()
parsed_json = loads(workspace_contents)
|
|
|
|
|
#FIX 13-02-2024; To avoid load on other pages just bcs parent is the same |
|
|
|
|
|
if parsed_json[‘name’] == page.name: |
print ('UPDATE CONTENT....')
page.content = parsed_json['content']
pages = []
private_pages = []
# Filter Page based on Permission
for page in all_pages:
try:
workspace = Workspace(page, True)
if has_access or workspace.is_permitted():
if page.public and (has_access or not page.is_hidden) and page.title != "Welcome Workspace":
pages.append(page)
elif page.for_user == frappe.session.user:
private_pages.append(page)
page["label"] = _(page.get("name"))
except frappe.PermissionError:
pass
if private_pages:
pages.extend(private_pages)
if len(pages) == 0:
pages = [frappe.get_doc("Workspace", "Welcome Workspace").as_dict()]
pages[0]["label"] = _("Welcome Workspace")
return {"pages": pages, "has_access": has_access}
Don’t forget to under hooks add this
override_whitelisted_methods = {
“frappe.desk.desktop.get_desktop_page”: “customapp.overrides.customdesktop.get_desktop_page”,
“frappe.desk.desktop.get_workspace_sidebar_items”: “customapp.overrides.customdesktop.get_workspace_sidebar_items”
}
So basically it will load your Cards …
Few more tweaks might be needed but for my needs now it is ok.
Good luck