|
|
@@ -37,7 +37,7 @@ class _dict(dict): |
|
|
|
return _dict(dict(self).copy()) |
|
|
|
|
|
|
|
def _(msg): |
|
|
|
"""translate object in current lang, if exists""" |
|
|
|
"""Returns translated string in current lang, if exists.""" |
|
|
|
if local.lang == "en": |
|
|
|
return msg |
|
|
|
|
|
|
@@ -45,12 +45,17 @@ def _(msg): |
|
|
|
return get_full_dict(local.lang).get(msg, msg) |
|
|
|
|
|
|
|
def get_lang_dict(fortype, name=None): |
|
|
|
"""Returns the translated language dict for the given type and name. |
|
|
|
|
|
|
|
:param fortype: must be one of `doctype`, `page`, `report`, `include`, `jsfile`, `boot` |
|
|
|
:param name: name of the document for which assets are to be returned.""" |
|
|
|
if local.lang=="en": |
|
|
|
return {} |
|
|
|
from frappe.translate import get_dict |
|
|
|
return get_dict(fortype, name) |
|
|
|
|
|
|
|
def set_user_lang(user, user_language=None): |
|
|
|
"""Guess and set user language for the session. `frappe.local.lang`""" |
|
|
|
from frappe.translate import get_user_lang |
|
|
|
local.lang = get_user_lang(user) |
|
|
|
|
|
|
@@ -72,6 +77,7 @@ message_log = local("message_log") |
|
|
|
lang = local("lang") |
|
|
|
|
|
|
|
def init(site, sites_path=None): |
|
|
|
"""Initialize frappe for the current site. Reset thread locals `frappe.local`""" |
|
|
|
if getattr(local, "initialised", None): |
|
|
|
return |
|
|
|
|
|
|
@@ -111,6 +117,10 @@ def init(site, sites_path=None): |
|
|
|
local.initialised = True |
|
|
|
|
|
|
|
def connect(site=None, db_name=None): |
|
|
|
"""Connect to site database instance. |
|
|
|
|
|
|
|
:param site: If site is given, calls `frappe.init`. |
|
|
|
:param db_name: Optional. Will use from `site_config.json`.""" |
|
|
|
from database import Database |
|
|
|
if site: |
|
|
|
init(site) |
|
|
@@ -120,6 +130,8 @@ def connect(site=None, db_name=None): |
|
|
|
set_user("Administrator") |
|
|
|
|
|
|
|
def get_site_config(sites_path=None, site_path=None): |
|
|
|
"""Returns `site_config.json` combined with `sites/common_site_config.json`. |
|
|
|
`site_config` is a set of site wide settings like database name, password, email etc.""" |
|
|
|
config = {} |
|
|
|
|
|
|
|
sites_path = sites_path or getattr(local, "sites_path", None) |
|
|
@@ -138,7 +150,7 @@ def get_site_config(sites_path=None, site_path=None): |
|
|
|
return _dict(config) |
|
|
|
|
|
|
|
def destroy(): |
|
|
|
"""closes connection and releases werkzeug local""" |
|
|
|
"""Closes connection and releases werkzeug local.""" |
|
|
|
if db: |
|
|
|
db.close() |
|
|
|
|
|
|
@@ -148,6 +160,7 @@ _memc = None |
|
|
|
|
|
|
|
# memcache |
|
|
|
def cache(): |
|
|
|
"""Returns memcache connection.""" |
|
|
|
global _memc |
|
|
|
if not _memc: |
|
|
|
from frappe.memc import MClient |
|
|
@@ -155,10 +168,14 @@ def cache(): |
|
|
|
return _memc |
|
|
|
|
|
|
|
def get_traceback(): |
|
|
|
"""Returns error traceback.""" |
|
|
|
import utils |
|
|
|
return utils.get_traceback() |
|
|
|
|
|
|
|
def errprint(msg): |
|
|
|
"""Log error. This is sent back as `exc` in response. |
|
|
|
|
|
|
|
:param msg: Message.""" |
|
|
|
from utils import cstr |
|
|
|
if not request or (not "cmd" in local.form_dict): |
|
|
|
print cstr(msg) |
|
|
@@ -166,6 +183,9 @@ def errprint(msg): |
|
|
|
error_log.append(cstr(msg)) |
|
|
|
|
|
|
|
def log(msg): |
|
|
|
"""Add to `debug_log`. |
|
|
|
|
|
|
|
:param msg: Message.""" |
|
|
|
if not request: |
|
|
|
if conf.get("logging") or False: |
|
|
|
print repr(msg) |
|
|
@@ -174,6 +194,15 @@ def log(msg): |
|
|
|
debug_log.append(cstr(msg)) |
|
|
|
|
|
|
|
def msgprint(msg, small=0, raise_exception=0, as_table=False): |
|
|
|
"""Print a message to the user (via HTTP response). |
|
|
|
Messages are sent in the `__server_messages` property in the |
|
|
|
response JSON and shown in a pop-up / modal. |
|
|
|
|
|
|
|
:param msg: Message. |
|
|
|
:param small: [optional] Show as a floating message in the footer. |
|
|
|
:param raise_exception: [optional] Raise given exception and show message. |
|
|
|
:param as_table: [optional] If `msg` is a list of lists, render as HTML table. |
|
|
|
""" |
|
|
|
def _raise_exception(): |
|
|
|
if raise_exception: |
|
|
|
if flags.rollback_on_exception: |
|
|
@@ -199,9 +228,17 @@ def msgprint(msg, small=0, raise_exception=0, as_table=False): |
|
|
|
_raise_exception() |
|
|
|
|
|
|
|
def throw(msg, exc=ValidationError): |
|
|
|
"""Throw execption and show message (`msgprint`). |
|
|
|
|
|
|
|
:param msg: Message. |
|
|
|
:param exc: Exception class. Default `frappe.ValidationError`""" |
|
|
|
msgprint(msg, raise_exception=exc) |
|
|
|
|
|
|
|
def create_folder(path, with_init=False): |
|
|
|
"""Create a folder in the given path and add an `__init__.py` file (optional). |
|
|
|
|
|
|
|
:param path: Folder path. |
|
|
|
:param with_init: Create `__init__.py` in the new folder.""" |
|
|
|
from frappe.utils import touch_file |
|
|
|
if not os.path.exists(path): |
|
|
|
os.makedirs(path) |
|
|
@@ -210,6 +247,9 @@ def create_folder(path, with_init=False): |
|
|
|
touch_file(os.path.join(path, "__init__.py")) |
|
|
|
|
|
|
|
def set_user(username): |
|
|
|
"""Set current user. |
|
|
|
|
|
|
|
:param username: **User** name to set as current user.""" |
|
|
|
from frappe.utils.user import User |
|
|
|
local.session.user = username |
|
|
|
local.session.sid = username |
|
|
@@ -221,11 +261,29 @@ def set_user(username): |
|
|
|
local.role_permissions = {} |
|
|
|
|
|
|
|
def get_request_header(key, default=None): |
|
|
|
"""Return HTTP request header. |
|
|
|
|
|
|
|
:param key: HTTP header key. |
|
|
|
:param default: Default value.""" |
|
|
|
return request.headers.get(key, default) |
|
|
|
|
|
|
|
def sendmail(recipients=(), sender="", subject="No Subject", message="No Message", |
|
|
|
as_markdown=False, bulk=False, ref_doctype=None, ref_docname=None, |
|
|
|
add_unsubscribe_link=False, attachments=None, content=None, doctype=None, name=None): |
|
|
|
"""Send email using user's default **Email Account** or global default **Email Account**. |
|
|
|
|
|
|
|
|
|
|
|
:param recipients: List of recipients. |
|
|
|
:param sender: Email sender. Default is current user. |
|
|
|
:param subject: Email Subject. |
|
|
|
:param message: (or `content`) Email Content. |
|
|
|
:param as_markdown: Convert content markdown to HTML. |
|
|
|
:param bulk: Send via scheduled email sender **Bulk Email**. Don't send immediately. |
|
|
|
:param ref_doctype: (or `doctype`) Append as communication to this DocType. |
|
|
|
:param ref_docname: (or `name`) Append as communication to this document name. |
|
|
|
:param add_unsubscribe_link: Allow user to unsubscribe from these emails. |
|
|
|
:param attachments: List of attachments. |
|
|
|
""" |
|
|
|
|
|
|
|
if bulk: |
|
|
|
import frappe.email.bulk |
|
|
@@ -247,12 +305,16 @@ whitelisted = [] |
|
|
|
guest_methods = [] |
|
|
|
def whitelist(allow_guest=False): |
|
|
|
""" |
|
|
|
decorator for whitelisting a function |
|
|
|
Decorator for whitelisting a function and making it accessible via HTTP. |
|
|
|
Standard request will be `/api/method/[path.to.method]` |
|
|
|
|
|
|
|
Note: if the function is allowed to be accessed by a guest user, |
|
|
|
it must explicitly be marked as allow_guest=True |
|
|
|
:param allow_guest: Allow non logged-in user to access this method. |
|
|
|
|
|
|
|
for specific roles, set allow_roles = ['Administrator'] etc. |
|
|
|
Use as: |
|
|
|
|
|
|
|
@frappe.whitelist() |
|
|
|
def myfunc(param1, param2): |
|
|
|
pass |
|
|
|
""" |
|
|
|
def innerfn(fn): |
|
|
|
global whitelisted, guest_methods |
|
|
@@ -266,6 +328,9 @@ def whitelist(allow_guest=False): |
|
|
|
return innerfn |
|
|
|
|
|
|
|
def only_for(roles): |
|
|
|
"""Raise `frappe.PermissionError` if the user does not have any of the given **Roles**. |
|
|
|
|
|
|
|
:param roles: List of roles to check.""" |
|
|
|
if not isinstance(roles, (tuple, list)): |
|
|
|
roles = (roles,) |
|
|
|
roles = set(roles) |
|
|
@@ -274,7 +339,10 @@ def only_for(roles): |
|
|
|
raise PermissionError |
|
|
|
|
|
|
|
def clear_cache(user=None, doctype=None): |
|
|
|
"""clear cache""" |
|
|
|
"""Clear **User**, **DocType** or global cache. |
|
|
|
|
|
|
|
:param user: If user is given, only user cache is cleared. |
|
|
|
:param doctype: If doctype is given, only DocType cache is cleared.""" |
|
|
|
import frappe.sessions |
|
|
|
if doctype: |
|
|
|
import frappe.model.meta |
|
|
@@ -294,12 +362,14 @@ def clear_cache(user=None, doctype=None): |
|
|
|
frappe.local.role_permissions = {} |
|
|
|
|
|
|
|
def get_roles(username=None): |
|
|
|
"""Returns roles of current user.""" |
|
|
|
if not local.session: |
|
|
|
return ["Guest"] |
|
|
|
|
|
|
|
return get_user(username).get_roles() |
|
|
|
|
|
|
|
def get_user(username): |
|
|
|
"""Returns `frappe.utils.user.User` instance of given user.""" |
|
|
|
from frappe.utils.user import User |
|
|
|
if not username or username == local.session.user: |
|
|
|
return local.user |
|
|
@@ -307,101 +377,153 @@ def get_user(username): |
|
|
|
return User(username) |
|
|
|
|
|
|
|
def has_permission(doctype, ptype="read", doc=None, user=None): |
|
|
|
"""Raises `frappe.PermissionError` if not permitted. |
|
|
|
|
|
|
|
:param doctype: DocType for which permission is to be check. |
|
|
|
:param ptype: Permission type (`read`, `write`, `create`, `submit`, `cancel`, `amend`). Default: `read`. |
|
|
|
:param doc: [optional] Checks User permissions for given doc. |
|
|
|
:param user: [optional] Check for given user. Default: current user.""" |
|
|
|
import frappe.permissions |
|
|
|
return frappe.permissions.has_permission(doctype, ptype, doc, user=user) |
|
|
|
|
|
|
|
def is_table(doctype): |
|
|
|
"""Returns True if `istable` property (indicating child Table) is set for given DocType.""" |
|
|
|
tables = cache().get_value("is_table") |
|
|
|
if tables==None: |
|
|
|
tables = db.sql_list("select name from tabDocType where ifnull(istable,0)=1") |
|
|
|
cache().set_value("is_table", tables) |
|
|
|
return doctype in tables |
|
|
|
|
|
|
|
def clear_perms(doctype): |
|
|
|
db.sql("""delete from tabDocPerm where parent=%s""", doctype) |
|
|
|
|
|
|
|
def reset_perms(doctype): |
|
|
|
from frappe.core.doctype.notification_count.notification_count import delete_notification_count_for |
|
|
|
delete_notification_count_for(doctype) |
|
|
|
|
|
|
|
clear_perms(doctype) |
|
|
|
reload_doc(db.get_value("DocType", doctype, "module"), |
|
|
|
"DocType", doctype, force=True) |
|
|
|
|
|
|
|
def generate_hash(txt=None): |
|
|
|
"""Generates random hash for session id""" |
|
|
|
"""Generates random hash for given text + current timestamp + random string.""" |
|
|
|
import hashlib, time |
|
|
|
from .utils import random_string |
|
|
|
return hashlib.sha224((txt or "") + repr(time.time()) + repr(random_string(8))).hexdigest() |
|
|
|
|
|
|
|
def reset_metadata_version(): |
|
|
|
"""Reset `metadata_version` (Client (Javascript) build ID) hash.""" |
|
|
|
v = generate_hash() |
|
|
|
cache().set_value("metadata_version", v) |
|
|
|
return v |
|
|
|
|
|
|
|
def new_doc(doctype, parent_doc=None, parentfield=None): |
|
|
|
"""Returns a new document of the given DocType with defaults set. |
|
|
|
|
|
|
|
:param doctype: DocType of the new document. |
|
|
|
:param parent_doc: [optional] add to parent document. |
|
|
|
:param parentfield: [optional] add against this `parentfield`.""" |
|
|
|
from frappe.model.create_new import get_new_doc |
|
|
|
return get_new_doc(doctype, parent_doc, parentfield) |
|
|
|
|
|
|
|
def set_value(doctype, docname, fieldname, value): |
|
|
|
"""Set document value. Calls `frappe.client.set_value`""" |
|
|
|
import frappe.client |
|
|
|
return frappe.client.set_value(doctype, docname, fieldname, value) |
|
|
|
|
|
|
|
def get_doc(arg1, arg2=None): |
|
|
|
"""Return a `frappe.model.document.Document` object of the given type and name. |
|
|
|
|
|
|
|
:param arg1: DocType name as string **or** document JSON. |
|
|
|
:param arg2: [optional] Document name as string. |
|
|
|
|
|
|
|
Examples: |
|
|
|
|
|
|
|
# insert a new document |
|
|
|
todo = frappe.get_doc({"doctype":"ToDo", "description": "test"}) |
|
|
|
tood.insert() |
|
|
|
|
|
|
|
# open an existing document |
|
|
|
todo = frappe.get_doc("ToDo", "TD0001") |
|
|
|
|
|
|
|
""" |
|
|
|
import frappe.model.document |
|
|
|
return frappe.model.document.get_doc(arg1, arg2) |
|
|
|
|
|
|
|
def get_meta(doctype, cached=True): |
|
|
|
"""Get `frappe.model.meta.Meta` instance of given doctype name.""" |
|
|
|
import frappe.model.meta |
|
|
|
return frappe.model.meta.get_meta(doctype, cached=cached) |
|
|
|
|
|
|
|
def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, ignore_permissions=False): |
|
|
|
"""Delete a document. Calls `frappe.model.delete_doc.delete_doc`. |
|
|
|
|
|
|
|
:param doctype: DocType of document to be delete. |
|
|
|
:param name: Name of document to be delete. |
|
|
|
:param force: Allow even if document is linked. Warning: This may lead to data integrity errors. |
|
|
|
:param ignore_doctypes: Ignore if child table is one of these. |
|
|
|
:param for_reload: Call `before_reload` trigger before deleting. |
|
|
|
:param ignore_permissions: Ignore user permissions.""" |
|
|
|
import frappe.model.delete_doc |
|
|
|
frappe.model.delete_doc.delete_doc(doctype, name, force, ignore_doctypes, for_reload, ignore_permissions) |
|
|
|
|
|
|
|
def delete_doc_if_exists(doctype, name): |
|
|
|
"""Delete document if exists.""" |
|
|
|
if db.exists(doctype, name): |
|
|
|
delete_doc(doctype, name) |
|
|
|
|
|
|
|
def reload_doc(module, dt=None, dn=None, force=False): |
|
|
|
"""Reload Document from model (`[module]/[doctype]/[name]/[name].json`) files. |
|
|
|
|
|
|
|
:param module: Module name. |
|
|
|
:param dt: DocType name. |
|
|
|
:param dn: Document name. |
|
|
|
:param force: Reload even if `modified` timestamp matches. |
|
|
|
""" |
|
|
|
import frappe.modules |
|
|
|
return frappe.modules.reload_doc(module, dt, dn, force=force) |
|
|
|
|
|
|
|
def rename_doc(doctype, old, new, debug=0, force=False, merge=False, ignore_permissions=False): |
|
|
|
"""Rename a document. Calls `frappe.model.rename_doc.rename_doc`""" |
|
|
|
from frappe.model.rename_doc import rename_doc |
|
|
|
return rename_doc(doctype, old, new, force=force, merge=merge, ignore_permissions=ignore_permissions) |
|
|
|
|
|
|
|
def insert(doclist): |
|
|
|
import frappe.model |
|
|
|
return frappe.model.insert(doclist) |
|
|
|
|
|
|
|
def get_module(modulename): |
|
|
|
"""Returns a module object for given Python module name using `importlib.import_module`.""" |
|
|
|
return importlib.import_module(modulename) |
|
|
|
|
|
|
|
def scrub(txt): |
|
|
|
"""Returns sluggified string. e.g. `Sales Order` becomes `sales_order`.""" |
|
|
|
return txt.replace(' ','_').replace('-', '_').lower() |
|
|
|
|
|
|
|
def unscrub(txt): |
|
|
|
"""Returns titlified string. e.g. `sales_order` becomes `Sales Order`.""" |
|
|
|
return txt.replace('_',' ').replace('-', ' ').title() |
|
|
|
|
|
|
|
def get_module_path(module, *joins): |
|
|
|
"""Get the path of the given module name. |
|
|
|
|
|
|
|
:param module: Module name. |
|
|
|
:param *joins: Join additional path elements using `os.path.join`.""" |
|
|
|
module = scrub(module) |
|
|
|
return get_pymodule_path(local.module_app[module] + "." + module, *joins) |
|
|
|
|
|
|
|
def get_app_path(app_name, *joins): |
|
|
|
"""Return path of given app. |
|
|
|
|
|
|
|
:param app: App name. |
|
|
|
:param *joins: Join additional path elements using `os.path.join`.""" |
|
|
|
return get_pymodule_path(app_name, *joins) |
|
|
|
|
|
|
|
def get_site_path(*joins): |
|
|
|
"""Return path of current site. |
|
|
|
|
|
|
|
:param *joins: Join additional path elements using `os.path.join`.""" |
|
|
|
return os.path.join(local.site_path, *joins) |
|
|
|
|
|
|
|
def get_pymodule_path(modulename, *joins): |
|
|
|
"""Return path of given Python module name. |
|
|
|
|
|
|
|
:param modulename: Python module name. |
|
|
|
:param *joins: Join additional path elements using `os.path.join`.""" |
|
|
|
joins = [scrub(part) for part in joins] |
|
|
|
return os.path.join(os.path.dirname(get_module(scrub(modulename)).__file__), *joins) |
|
|
|
|
|
|
|
def get_module_list(app_name): |
|
|
|
"""Get list of modules for given all via `app/modules.txt`.""" |
|
|
|
return get_file_items(os.path.join(os.path.dirname(get_module(app_name).__file__), "modules.txt")) |
|
|
|
|
|
|
|
def get_all_apps(with_frappe=False, with_internal_apps=True, sites_path=None): |
|
|
|
"""Get list of all apps via `sites/apps.txt`.""" |
|
|
|
if not sites_path: |
|
|
|
sites_path = local.sites_path |
|
|
|
|
|
|
@@ -413,6 +535,7 @@ def get_all_apps(with_frappe=False, with_internal_apps=True, sites_path=None): |
|
|
|
return apps |
|
|
|
|
|
|
|
def get_installed_apps(): |
|
|
|
"""Get list of installed apps in current site.""" |
|
|
|
if getattr(flags, "in_install_db", True): |
|
|
|
return [] |
|
|
|
installed = json.loads(db.get_global("installed_apps") or "[]") |
|
|
@@ -420,6 +543,16 @@ def get_installed_apps(): |
|
|
|
|
|
|
|
@whitelist() |
|
|
|
def get_versions(): |
|
|
|
"""Get versions of all installed apps. |
|
|
|
|
|
|
|
Example: |
|
|
|
|
|
|
|
{ |
|
|
|
"frappe": { |
|
|
|
"title": "Frappe Framework", |
|
|
|
"version": "5.0.0" |
|
|
|
} |
|
|
|
}""" |
|
|
|
versions = {} |
|
|
|
for app in get_installed_apps(): |
|
|
|
versions[app] = { |
|
|
@@ -434,6 +567,11 @@ def get_versions(): |
|
|
|
return versions |
|
|
|
|
|
|
|
def get_hooks(hook=None, default=None, app_name=None): |
|
|
|
"""Get hooks via `app/hooks.py` |
|
|
|
|
|
|
|
:param hook: Name of the hook. Will gather all hooks for this name and return as a list. |
|
|
|
:param default: Default if no hook found. |
|
|
|
:param app_name: Filter by app.""" |
|
|
|
def load_app_hooks(app_name=None): |
|
|
|
hooks = {} |
|
|
|
for app in [app_name] if app_name else get_installed_apps(): |
|
|
@@ -476,6 +614,7 @@ def get_hooks(hook=None, default=None, app_name=None): |
|
|
|
return hooks |
|
|
|
|
|
|
|
def setup_module_map(): |
|
|
|
"""Rebuild map of all modules (internal).""" |
|
|
|
_cache = cache() |
|
|
|
|
|
|
|
if conf.db_name: |
|
|
@@ -497,6 +636,7 @@ def setup_module_map(): |
|
|
|
_cache.set_value("module_app", local.module_app) |
|
|
|
|
|
|
|
def get_file_items(path, raise_not_found=False, ignore_empty_lines=True): |
|
|
|
"""Returns items from text file as a list. Ignores empty lines.""" |
|
|
|
content = read_file(path, raise_not_found=raise_not_found) |
|
|
|
if content: |
|
|
|
# \ufeff is no-width-break, \u200b is no-width-space |
|
|
@@ -507,10 +647,12 @@ def get_file_items(path, raise_not_found=False, ignore_empty_lines=True): |
|
|
|
return [] |
|
|
|
|
|
|
|
def get_file_json(path): |
|
|
|
"""Read a file and return parsed JSON object.""" |
|
|
|
with open(path, 'r') as f: |
|
|
|
return json.load(f) |
|
|
|
|
|
|
|
def read_file(path, raise_not_found=False): |
|
|
|
"""Open a file and return its content as Unicode.""" |
|
|
|
from frappe.utils import cstr |
|
|
|
if os.path.exists(path): |
|
|
|
with open(path, "r") as f: |
|
|
@@ -521,11 +663,13 @@ def read_file(path, raise_not_found=False): |
|
|
|
return None |
|
|
|
|
|
|
|
def get_attr(method_string): |
|
|
|
"""Get python method object from its name.""" |
|
|
|
modulename = '.'.join(method_string.split('.')[:-1]) |
|
|
|
methodname = method_string.split('.')[-1] |
|
|
|
return getattr(get_module(modulename), methodname) |
|
|
|
|
|
|
|
def call(fn, *args, **kwargs): |
|
|
|
"""Call a function and match arguments.""" |
|
|
|
if hasattr(fn, 'fnargs'): |
|
|
|
fnargs = fn.fnargs |
|
|
|
else: |
|
|
@@ -538,6 +682,7 @@ def call(fn, *args, **kwargs): |
|
|
|
return fn(*args, **newargs) |
|
|
|
|
|
|
|
def make_property_setter(args, ignore_validate=False, validate_fields_for_doctype=True): |
|
|
|
"""Create a new **Property Setter** (for overriding DocType and DocField properties).""" |
|
|
|
args = _dict(args) |
|
|
|
ps = get_doc({ |
|
|
|
'doctype': "Property Setter", |
|
|
@@ -554,6 +699,7 @@ def make_property_setter(args, ignore_validate=False, validate_fields_for_doctyp |
|
|
|
ps.insert() |
|
|
|
|
|
|
|
def import_doc(path, ignore_links=False, ignore_insert=False, insert=False): |
|
|
|
"""Import a file using Data Import Tool.""" |
|
|
|
from frappe.core.page.data_import_tool import data_import_tool |
|
|
|
data_import_tool.import_doc(path, ignore_links=ignore_links, ignore_insert=ignore_insert, insert=insert) |
|
|
|
|
|
|
@@ -593,10 +739,31 @@ def copy_doc(doc, ignore_no_copy=True): |
|
|
|
return newdoc |
|
|
|
|
|
|
|
def compare(val1, condition, val2): |
|
|
|
"""Compare two values using `frappe.utils.compare` |
|
|
|
|
|
|
|
`condition` could be: |
|
|
|
- "^" |
|
|
|
- "in" |
|
|
|
- "not in" |
|
|
|
- "=" |
|
|
|
- "!=" |
|
|
|
- ">" |
|
|
|
- "<" |
|
|
|
- ">=" |
|
|
|
- "<=" |
|
|
|
- "not None" |
|
|
|
- "None" |
|
|
|
""" |
|
|
|
import frappe.utils |
|
|
|
return frappe.utils.compare(val1, condition, val2) |
|
|
|
|
|
|
|
def respond_as_web_page(title, html, success=None, http_status_code=None): |
|
|
|
"""Send response as a web page with a message rather than JSON. Used to show permission errors etc. |
|
|
|
|
|
|
|
:param title: Page title and heading. |
|
|
|
:param message: Message to be shown. |
|
|
|
:param success: Alert message. |
|
|
|
:param http_status_code: HTTP status code.""" |
|
|
|
local.message_title = title |
|
|
|
local.message = html |
|
|
|
local.message_success = success |
|
|
@@ -606,18 +773,62 @@ def respond_as_web_page(title, html, success=None, http_status_code=None): |
|
|
|
local.response['http_status_code'] = http_status_code |
|
|
|
|
|
|
|
def build_match_conditions(doctype, as_condition=True): |
|
|
|
"""Return match (User permissions) for given doctype as list or SQL.""" |
|
|
|
import frappe.desk.reportview |
|
|
|
return frappe.desk.reportview.build_match_conditions(doctype, as_condition) |
|
|
|
|
|
|
|
def get_list(doctype, *args, **kwargs): |
|
|
|
"""List database query via `frappe.model.db_query`. Will also check for permissions. |
|
|
|
|
|
|
|
:param doctype: DocType on which query is to be made. |
|
|
|
:param fields: List of fields or `*`. |
|
|
|
:param filters: List of filters (see example). |
|
|
|
:param order_by: Order By e.g. `modified desc`. |
|
|
|
:param limit_page_start: Start results at record #. Default 0. |
|
|
|
:param limit_poge_length: No of records in the page. Default 20. |
|
|
|
|
|
|
|
Example usage: |
|
|
|
|
|
|
|
# simple dict filter |
|
|
|
frappe.get_list("ToDo", fields=["name", "description"], filters = {"owner":"test@example.com"}) |
|
|
|
|
|
|
|
# filter as a list of lists |
|
|
|
frappe.get_list("ToDo", fields="*", filters = [["modified", ">", "2014-01-01"]]) |
|
|
|
|
|
|
|
# filter as a list of dicts |
|
|
|
frappe.get_list("ToDo", fields="*", filters = {"description": ("like", "test%")}) |
|
|
|
""" |
|
|
|
import frappe.model.db_query |
|
|
|
return frappe.model.db_query.DatabaseQuery(doctype).execute(None, *args, **kwargs) |
|
|
|
|
|
|
|
def get_all(doctype, *args, **kwargs): |
|
|
|
"""List database query via `frappe.model.db_query`. Will **not** check for conditions. |
|
|
|
Parameters are same as `frappe.get_list` |
|
|
|
|
|
|
|
:param doctype: DocType on which query is to be made. |
|
|
|
:param fields: List of fields or `*`. Default is: `["name"]`. |
|
|
|
:param filters: List of filters (see example). |
|
|
|
:param order_by: Order By e.g. `modified desc`. |
|
|
|
:param limit_page_start: Start results at record #. Default 0. |
|
|
|
:param limit_poge_length: No of records in the page. Default 20. |
|
|
|
|
|
|
|
Example usage: |
|
|
|
|
|
|
|
# simple dict filter |
|
|
|
frappe.get_all("ToDo", fields=["name", "description"], filters = {"owner":"test@example.com"}) |
|
|
|
|
|
|
|
# filter as a list of lists |
|
|
|
frappe.get_all("ToDo", fields=["*"], filters = [["modified", ">", "2014-01-01"]]) |
|
|
|
|
|
|
|
# filter as a list of dicts |
|
|
|
frappe.get_all("ToDo", fields=["*"], filters = {"description": ("like", "test%")}) |
|
|
|
""" |
|
|
|
kwargs["ignore_permissions"] = True |
|
|
|
return get_list(doctype, *args, **kwargs) |
|
|
|
|
|
|
|
def add_version(doc): |
|
|
|
"""Insert a new **Version** of the given document. |
|
|
|
A **Version** is a JSON dump of the current document state.""" |
|
|
|
get_doc({ |
|
|
|
"doctype": "Version", |
|
|
|
"ref_doctype": doc.doctype, |
|
|
@@ -626,6 +837,7 @@ def add_version(doc): |
|
|
|
}).insert(ignore_permissions=True) |
|
|
|
|
|
|
|
def get_test_records(doctype): |
|
|
|
"""Returns list of objects from `test_records.json` in the given doctype's folder.""" |
|
|
|
from frappe.modules import get_doctype_module, get_module_path |
|
|
|
path = os.path.join(get_module_path(get_doctype_module(doctype)), "doctype", scrub(doctype), "test_records.json") |
|
|
|
if os.path.exists(path): |
|
|
@@ -635,10 +847,21 @@ def get_test_records(doctype): |
|
|
|
return [] |
|
|
|
|
|
|
|
def format_value(value, df, doc=None, currency=None): |
|
|
|
"""Format value with given field properties. |
|
|
|
|
|
|
|
:param value: Value to be formatted. |
|
|
|
:param df: DocField object with properties `fieldtype`, `options` etc.""" |
|
|
|
import frappe.utils.formatters |
|
|
|
return frappe.utils.formatters.format_value(value, df, doc, currency=currency) |
|
|
|
|
|
|
|
def get_print_format(doctype, name, print_format=None, style=None, as_pdf=False): |
|
|
|
"""Get Print Format for given document. |
|
|
|
|
|
|
|
:param doctype: DocType of document. |
|
|
|
:param name: Name of document. |
|
|
|
:param print_format: Print Format name. Default 'Standard', |
|
|
|
:param style: Print Format style. |
|
|
|
:param as_pdf: Return as PDF. Default False.""" |
|
|
|
from frappe.website.render import build_page |
|
|
|
local.form_dict.doctype = doctype |
|
|
|
local.form_dict.name = name |
|
|
|