seitime-frappe/frappe/desk/moduleview.py
Makarand Bauskar 17a09e4b70 [feature] domainification for frappe (#3227)
* [feature] domainify doctype, pages and roles

* [minor] added get active domain method to __init__

* [fixes] minor fixes in domainification and added test cases

* [minor] added checkbox-editor class to render the domains

* [minor] added checkbox_selector option

* [minor] added or_filters to filter restrict to domain doctype and roles

* [minor] fixed codacy errors
2017-06-13 14:36:01 +05:30

260 lines
6.9 KiB
Python

# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.boot import get_allowed_pages, get_allowed_reports
from frappe.desk.doctype.desktop_icon.desktop_icon import set_hidden, clear_desktop_icons_cache
@frappe.whitelist()
def get(module):
"""Returns data (sections, list of reports, counts) to render module view in desk:
`/desk/#Module/[name]`."""
data = get_data(module)
out = {
"data": data
}
return out
@frappe.whitelist()
def hide_module(module):
set_hidden(module, frappe.session.user, 1)
clear_desktop_icons_cache()
def get_data(module):
"""Get module data for the module view `desk/#Module/[name]`"""
doctype_info = get_doctype_info(module)
data = build_config_from_file(module)
if not data:
data = build_standard_config(module, doctype_info)
else:
add_custom_doctypes(data, doctype_info)
add_section(data, _("Custom Reports"), "fa fa-list-alt",
get_report_list(module))
data = combine_common_sections(data)
data = apply_permissions(data)
#set_last_modified(data)
return data
def build_config_from_file(module):
"""Build module info from `app/config/desktop.py` files."""
data = []
module = frappe.scrub(module)
for app in frappe.get_installed_apps():
try:
data += get_config(app, module)
except ImportError:
pass
return filter_by_restrict_to_domain(data)
def filter_by_restrict_to_domain(data):
""" filter Pages and DocType depending on the Active Module(s) """
mapper = {
"page": "Page",
"doctype": "DocType"
}
active_domains = frappe.get_active_domains()
for d in data:
_items = []
for item in d.get("items", []):
doctype = mapper.get(item.get("type"))
doctype_domain = frappe.db.get_value(doctype, item.get("name"), "restrict_to_domain") or ''
if not doctype_domain or (doctype_domain in active_domains):
_items.append(item)
d.update({ "items": _items })
return data
def build_standard_config(module, doctype_info):
"""Build standard module data from DocTypes."""
if not frappe.db.get_value("Module Def", module):
frappe.throw(_("Module Not Found"))
data = []
add_section(data, _("Documents"), "fa fa-star",
[d for d in doctype_info if d.document_type in ("Document", "Transaction")])
add_section(data, _("Setup"), "fa fa-cog",
[d for d in doctype_info if d.document_type in ("Master", "Setup", "")])
add_section(data, _("Standard Reports"), "fa fa-list",
get_report_list(module, is_standard="Yes"))
return data
def add_section(data, label, icon, items):
"""Adds a section to the module data."""
if not items: return
data.append({
"label": label,
"icon": icon,
"items": items
})
def add_custom_doctypes(data, doctype_info):
"""Adds Custom DocTypes to modules setup via `config/desktop.py`."""
add_section(data, _("Documents"), "fa fa-star",
[d for d in doctype_info if (d.custom and d.document_type in ("Document", "Transaction"))])
add_section(data, _("Setup"), "fa fa-cog",
[d for d in doctype_info if (d.custom and d.document_type in ("Setup", "Master", ""))])
def get_doctype_info(module):
"""Returns list of non child DocTypes for given module."""
active_domains = frappe.get_active_domains()
doctype_info = frappe.get_all("DocType", filters={
"module": module,
"istable": 0
}, or_filters={
"ifnull(restrict_to_domain, '')": "",
"restrict_to_domain": ("in", active_domains)
}, fields=["'doctype' as type", "name", "description", "ifnull(document_type, '') as document_type",
"custom", "issingle"], order_by="custom asc, document_type desc, name asc")
for d in doctype_info:
d.description = _(d.description or "")
return doctype_info
def combine_common_sections(data):
"""Combine sections declared in separate apps."""
sections = []
sections_dict = {}
for each in data:
if each["label"] not in sections_dict:
sections_dict[each["label"]] = each
sections.append(each)
else:
sections_dict[each["label"]]["items"] += each["items"]
return sections
def apply_permissions(data):
default_country = frappe.db.get_default("country")
user = frappe.get_user()
user.build_permissions()
allowed_pages = get_allowed_pages()
allowed_reports = get_allowed_reports()
new_data = []
for section in data:
new_items = []
for item in (section.get("items") or []):
item = frappe._dict(item)
if item.country and item.country!=default_country:
continue
if ((item.type=="doctype" and item.name in user.can_read)
or (item.type=="page" and item.name in allowed_pages)
or (item.type=="report" and item.name in allowed_reports)
or item.type=="help"):
new_items.append(item)
if new_items:
new_section = section.copy()
new_section["items"] = new_items
new_data.append(new_section)
return new_data
def get_config(app, module):
"""Load module info from `[app].config.[module]`."""
config = frappe.get_module("{app}.config.{module}".format(app=app, module=module))
config = config.get_data()
for section in config:
for item in section["items"]:
if item["type"]=="report" and frappe.db.get_value("Report", item["name"], "disabled")==1:
section["items"].remove(item)
continue
if not "label" in item:
item["label"] = _(item["name"])
return config
def add_setup_section(config, app, module, label, icon):
"""Add common sections to `/desk#Module/Setup`"""
try:
setup_section = get_setup_section(app, module, label, icon)
if setup_section:
config.append(setup_section)
except ImportError:
pass
def get_setup_section(app, module, label, icon):
"""Get the setup section from each module (for global Setup page)."""
config = get_config(app, module)
for section in config:
if section.get("label")==_("Setup"):
return {
"label": label,
"icon": icon,
"items": section["items"]
}
def set_last_modified(data):
for section in data:
for item in section["items"]:
if item["type"] == "doctype":
item["last_modified"] = get_last_modified(item["name"])
def get_last_modified(doctype):
def _get():
try:
last_modified = frappe.get_all(doctype, fields=["max(modified)"], as_list=True, limit_page_length=1)[0][0]
except Exception as e:
if e.args[0]==1146:
last_modified = None
else:
raise
# hack: save as -1 so that it is cached
if last_modified==None:
last_modified = -1
return last_modified
last_modified = frappe.cache().hget("last_modified", doctype, _get)
if last_modified==-1:
last_modified = None
return last_modified
def get_report_list(module, is_standard="No"):
"""Returns list on new style reports for modules."""
reports = frappe.get_list("Report", fields=["name", "ref_doctype", "report_type"], filters=
{"is_standard": is_standard, "disabled": 0, "module": module},
order_by="name")
out = []
for r in reports:
out.append({
"type": "report",
"doctype": r.ref_doctype,
"is_query_report": 1 if r.report_type in ("Query Report", "Script Report") else 0,
"label": _(r.name),
"name": r.name
})
return out