@@ -15,7 +15,6 @@ import os, sys, importlib, inspect | |||
import json | |||
import semantic_version | |||
from frappe.core.doctype.print_format.print_format import get_html as get_print_html | |||
from .exceptions import * | |||
local = Local() | |||
@@ -315,8 +314,7 @@ def get_obj(dt = None, dn = None, doc=None, doclist=None, with_children = True): | |||
return get_obj(dt, dn, doc, doclist, with_children) | |||
def doc(doctype=None, name=None, fielddata=None): | |||
from frappe.model.doc import Document | |||
return Document(doctype, name, fielddata) | |||
return Document(doctype, name, fielddata) | |||
def new_doc(doctype, parent_doc=None, parentfield=None): | |||
from frappe.model.create_new import get_new_doc | |||
@@ -334,7 +332,7 @@ def bean(doctype=None, name=None, copy=None): | |||
"""return an instance of the object, wrapped as a Bean (frappe.model.bean)""" | |||
from frappe.model.bean import Bean | |||
if copy: | |||
return Bean(copy_doclist(copy)) | |||
return Bean(copy_doc(copy)) | |||
else: | |||
return Bean(doctype, name) | |||
@@ -348,10 +346,6 @@ def get_doclist(doctype, name=None): | |||
def get_doc(arg1, arg2=None): | |||
import frappe.model.document | |||
return frappe.model.document.get_doc(arg1, arg2) | |||
def get_doctype(doctype, processed=False): | |||
import frappe.model.doctype | |||
return frappe.model.doctype.get(doctype, processed) | |||
def get_meta(doctype, cached=True): | |||
import frappe.model.meta | |||
@@ -531,26 +525,21 @@ def import_doclist(path, ignore_links=False, ignore_insert=False, insert=False): | |||
from frappe.core.page.data_import_tool import data_import_tool | |||
data_import_tool.import_doclist(path, ignore_links=ignore_links, ignore_insert=ignore_insert, insert=insert) | |||
def copy_doclist(in_doclist): | |||
new_doclist = [] | |||
parent_doc = None | |||
for i, d in enumerate(in_doclist): | |||
is_dict = False | |||
if isinstance(d, dict): | |||
is_dict = True | |||
values = _dict(d.copy()) | |||
else: | |||
values = _dict(d.fields.copy()) | |||
newd = new_doc(values.doctype, parent_doc=(None if i==0 else parent_doc), parentfield=values.parentfield) | |||
newd.fields.update(values) | |||
if i==0: | |||
parent_doc = newd | |||
new_doclist.append(newd.fields if is_dict else newd) | |||
return doclist(new_doclist) | |||
def copy_doc(doc): | |||
import copy | |||
d = doc.as_dict() | |||
newdoc = get_doc(copy.deepcopy(d)) | |||
newdoc.name = None | |||
newdoc.set("__islocal", 1) | |||
newdoc.owner = None | |||
newdoc.creation = None | |||
for d in newdoc.get_all_children(): | |||
d.name = None | |||
d.parent = None | |||
d.set("__islocal", 1) | |||
d.owner = None | |||
d.creation = None | |||
return newdoc | |||
def compare(val1, condition, val2): | |||
import frappe.utils | |||
@@ -126,7 +126,7 @@ def get_user(bootinfo): | |||
"""get user info""" | |||
bootinfo['user'] = frappe.user.load_user() | |||
def add_home_page(bootinfo, doclist): | |||
def add_home_page(bootinfo, docs): | |||
"""load home page""" | |||
if frappe.session.user=="Guest": | |||
@@ -141,4 +141,4 @@ def add_home_page(bootinfo, doclist): | |||
page = frappe.widgets.page.get('desktop') | |||
bootinfo['home_page'] = page.name | |||
doclist.append(page) | |||
docs.append(page) |
@@ -55,7 +55,7 @@ def insert(doclist): | |||
# inserting a child record | |||
d = doclist[0] | |||
bean = frappe.bean(d["parenttype"], d["parent"]) | |||
bean.doclist.append(d) | |||
bean.append(d) | |||
bean.save() | |||
return [d] | |||
else: | |||
@@ -267,8 +267,7 @@ class CustomizeForm(Document): | |||
# If the above conditions are fulfilled, | |||
# create a property setter doc, but dont save it yet. | |||
from frappe.model.doc import Document | |||
d = Document('Property Setter') | |||
d = frappe.get_doc('Property Setter') | |||
d.doctype_or_field = ref_d.doctype=='DocField' and 'DocField' or 'DocType' | |||
d.doc_type = self.doc.doc_type | |||
d.field_name = ref_d.fieldname | |||
@@ -75,12 +75,12 @@ def get_html(doc, doclist, print_format=None): | |||
template = Environment().from_string(get_print_format_name(doc.doctype, | |||
print_format or frappe.form_dict.format)) | |||
doctype = frappe.get_doctype(doc.doctype) | |||
doctype = frappe.get_meta(doc.doctype) | |||
args = { | |||
"doc": doc, | |||
"doclist": doclist, | |||
"doctype": doctype, | |||
"meta": meta, | |||
"frappe": frappe, | |||
"utils": frappe.utils | |||
} | |||
@@ -46,16 +46,7 @@ class TestUser(unittest.TestCase): | |||
frappe.db.set_value("Control Panel", "Control Panel", "_test", "_test_val") | |||
self.assertEquals(frappe.db.get_value("Control Panel", None, "_test"), "_test_val") | |||
self.assertEquals(frappe.db.get_value("Control Panel", "Control Panel", "_test"), "_test_val") | |||
def test_doclist(self): | |||
p_meta = frappe.get_doctype("User") | |||
self.assertEquals(len(p_meta.get({"doctype": "DocField", "parent": "User", "fieldname": "first_name"})), 1) | |||
self.assertEquals(len(p_meta.get({"doctype": "DocField", "parent": "User", "fieldname": "^first"})), 1) | |||
self.assertEquals(len(p_meta.get({"fieldname": ["!=", "first_name"]})), len(p_meta) - 1) | |||
self.assertEquals(len(p_meta.get({"fieldname": ["in", ["first_name", "last_name"]]})), 2) | |||
self.assertEquals(len(p_meta.get({"fieldname": ["not in", ["first_name", "last_name"]]})), len(p_meta) - 2) | |||
test_records = [ | |||
[ | |||
@@ -54,9 +54,8 @@ class User(Document): | |||
if self.doc.user_type == "System User" and not self.get_other_system_managers(): | |||
msgprint("""Adding System Manager Role as there must | |||
be atleast one 'System Manager'.""") | |||
self.doclist.append({ | |||
self.append("user_roles", { | |||
"doctype": "UserRole", | |||
"parentfield": "user_roles", | |||
"role": "System Manager" | |||
}) | |||
@@ -244,11 +243,10 @@ class User(Document): | |||
def add_roles(self, *roles): | |||
for role in roles: | |||
if role in [d.role for d in self.doclist.get({"doctype":"UserRole"})]: | |||
if role in [d.role for d in self.get("user_roles")]: | |||
continue | |||
self.bean.doclist.append({ | |||
self.append("user_roles", { | |||
"doctype": "UserRole", | |||
"parentfield": "user_roles", | |||
"role": role | |||
}) | |||
@@ -18,10 +18,8 @@ class Workflow(Document): | |||
def create_custom_field_for_workflow_state(self): | |||
frappe.clear_cache(doctype=self.doc.document_type) | |||
doctypeobj = frappe.get_doctype(self.doc.document_type) | |||
if not len(doctypeobj.get({"doctype":"DocField", | |||
"fieldname":self.doc.workflow_state_field})): | |||
meta = frappe.get_meta(self.doc.document_type) | |||
if not meta.get_field(self.doc.workflow_state_field): | |||
# create custom field | |||
frappe.bean([{ | |||
"doctype":"Custom Field", | |||
@@ -32,7 +30,6 @@ class Workflow(Document): | |||
"hidden": 1, | |||
"fieldtype": "Link", | |||
"options": "Workflow State", | |||
#"insert_after": doctypeobj.get({"doctype":"DocField"})[-1].fieldname | |||
}]).save() | |||
frappe.msgprint("Created Custom Field '%s' in '%s'" % (self.doc.workflow_state_field, | |||
@@ -328,7 +328,7 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, | |||
doclist.append(d) | |||
else: | |||
break | |||
return doclist | |||
else: | |||
d = frappe._dict(zip(columns, rows[start_idx][1:])) | |||
@@ -66,8 +66,7 @@ def post(arg=None): | |||
import json | |||
arg = json.loads(arg) | |||
from frappe.model.doc import Document | |||
d = Document('Comment') | |||
d = frappe.get_doc('Comment') | |||
d.parenttype = arg.get("parenttype") | |||
d.comment = arg['txt'] | |||
d.comment_docname = arg['contact'] | |||
@@ -3,7 +3,6 @@ | |||
from __future__ import unicode_literals | |||
import memcache, frappe | |||
from frappe.model.doc import Document | |||
class MClient(memcache.Client): | |||
"""memcache client that will automatically prefix conf.db_name""" | |||
@@ -87,8 +87,8 @@ def delete_fields(args_dict, delete=0): | |||
def rename_field(doctype, old_fieldname, new_fieldname): | |||
"""This functions assumes that doctype is already synced""" | |||
doctype_list = frappe.get_doctype(doctype) | |||
new_field = doctype_list.get_field(new_fieldname) | |||
meta = frappe.get_meta(doctype) | |||
new_field = meta.get_field(new_fieldname) | |||
if not new_field: | |||
print "rename_field: " + (new_fieldname) + " not found in " + doctype | |||
return | |||
@@ -99,7 +99,7 @@ def rename_field(doctype, old_fieldname, new_fieldname): | |||
where parentfield=%s""" % (new_field.options.split("\n")[0], "%s", "%s"), | |||
(new_fieldname, old_fieldname)) | |||
elif new_field.fieldtype not in no_value_fields: | |||
if doctype_list[0].issingle: | |||
if meta.issingle: | |||
frappe.db.sql("""update `tabSingles` set field=%s | |||
where doctype=%s and field=%s""", | |||
(new_fieldname, doctype, old_fieldname)) | |||
@@ -69,7 +69,7 @@ class BaseDocument(object): | |||
def extend(self, key, value): | |||
if isinstance(value, list): | |||
for v in value: | |||
self.append(v) | |||
self.append(key, v) | |||
else: | |||
raise ValueError | |||
@@ -146,6 +146,10 @@ class BaseDocument(object): | |||
self.set("__islocal", False) | |||
def db_update(self): | |||
if self.get("__islocal") or not self.name: | |||
self.db_insert() | |||
return | |||
d = self.get_valid_dict() | |||
columns = d.keys() | |||
frappe.db.sql("""update `tab{doctype}` | |||
@@ -1,521 +0,0 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
""" | |||
Transactions are defined as collection of classes, a Bean represents collection of Document | |||
objects for a transaction with main and children. | |||
Group actions like save, etc are performed on doclists | |||
""" | |||
import frappe | |||
from frappe import _, msgprint | |||
from frappe.utils import cint, cstr, flt | |||
from frappe.model.doc import Document | |||
import frappe.permissions | |||
class DocstatusTransitionError(frappe.ValidationError): pass | |||
class BeanPermissionError(frappe.ValidationError): pass | |||
class TimestampMismatchError(frappe.ValidationError): pass | |||
class Bean: | |||
""" | |||
Collection of Documents with one parent and multiple children | |||
""" | |||
def __init__(self, dt=None, dn=None): | |||
self.obj = None | |||
self.ignore_permissions = False | |||
self.ignore_children_type = [] | |||
self.ignore_links = False | |||
self.ignore_validate = False | |||
self.ignore_fields = False | |||
self.ignore_mandatory = False | |||
self.ignore_restrictions = False | |||
if isinstance(dt, basestring) and not dn: | |||
dn = dt | |||
if dt and dn: | |||
if isinstance(dn, dict): | |||
dn = frappe.db.get_value(dt, dn, "name") | |||
if dn is None: | |||
raise frappe.DoesNotExistError | |||
self.load_from_db(dt, dn) | |||
elif isinstance(dt, list): | |||
self.set_doclist(dt) | |||
elif isinstance(dt, dict): | |||
self.set_doclist([dt]) | |||
def load_from_db(self, dt=None, dn=None): | |||
""" | |||
Load doclist from dt | |||
""" | |||
if not dt: dt = self.doc.doctype | |||
if not dn: dn = self.doc.name | |||
doc = Document(dt, dn) | |||
# get all children types | |||
tablefields = frappe.model.meta.get_table_fields(dt) | |||
# load chilren | |||
doclist = frappe.doclist([doc,]) | |||
self.set_doclist(doclist) | |||
def __iter__(self): | |||
return self.doclist.__iter__() | |||
@property | |||
def meta(self): | |||
if not hasattr(self, "_meta"): | |||
self._meta = frappe.get_doctype(self.doc.doctype) | |||
return self._meta | |||
def from_compressed(self, data, docname): | |||
from frappe.model.utils import expand | |||
self.set_doclist(expand(data)) | |||
def set_doclist(self, doclist): | |||
for i, d in enumerate(doclist): | |||
if isinstance(d, dict): | |||
doclist[i] = Document(fielddata=d) | |||
self.doclist = frappe.doclist(doclist) | |||
self.doc = self.doclist[0] | |||
if self.doc.meta.issingle: | |||
self.doc.cast_floats_and_ints() | |||
if self.obj: | |||
self.obj.doclist = self.doclist | |||
self.obj.doc = self.doc | |||
def make_controller(self): | |||
if not self.doc.doctype: | |||
raise frappe.DataError("Bean doctype not specified") | |||
if self.obj: | |||
# update doclist before running any method | |||
self.obj.doclist = self.doclist | |||
return self.obj | |||
self.obj = frappe.get_obj(doc=self.doc, doclist=self.doclist) | |||
self.obj.bean = self | |||
self.controller = self.obj | |||
return self.obj | |||
def get_controller(self): | |||
return self.make_controller() | |||
def to_dict(self): | |||
return [d.fields for d in self.doclist] | |||
def check_if_latest(self, method="save"): | |||
from frappe.model.meta import is_single | |||
conflict = False | |||
if not cint(self.doc.fields.get('__islocal')): | |||
if is_single(self.doc.doctype): | |||
modified = frappe.db.get_value(self.doc.doctype, self.doc.name, "modified") | |||
if isinstance(modified, list): | |||
modified = modified[0] | |||
if cstr(modified) and cstr(modified) != cstr(self.doc.modified): | |||
conflict = True | |||
else: | |||
tmp = frappe.db.sql("""select modified, docstatus from `tab%s` | |||
where name=%s for update""" | |||
% (self.doc.doctype, '%s'), self.doc.name, as_dict=True) | |||
if not tmp: | |||
frappe.msgprint("""This record does not exist. Please refresh.""", raise_exception=1) | |||
modified = cstr(tmp[0].modified) | |||
if modified and modified != cstr(self.doc.modified): | |||
conflict = True | |||
self.check_docstatus_transition(tmp[0].docstatus, method) | |||
if conflict: | |||
frappe.msgprint(_("Error: Document has been modified after you have opened it") \ | |||
+ (" (%s, %s). " % (modified, self.doc.modified)) \ | |||
+ _("Please refresh to get the latest document."), raise_exception=TimestampMismatchError) | |||
def check_docstatus_transition(self, db_docstatus, method): | |||
valid = { | |||
"save": [0,0], | |||
"submit": [0,1], | |||
"cancel": [1,2], | |||
"update_after_submit": [1,1] | |||
} | |||
labels = { | |||
0: _("Draft"), | |||
1: _("Submitted"), | |||
2: _("Cancelled") | |||
} | |||
if not hasattr(self, "to_docstatus"): | |||
self.to_docstatus = 0 | |||
if method != "runserverobj" and [db_docstatus, self.to_docstatus] != valid[method]: | |||
frappe.msgprint(_("Cannot change from") + ": " + labels[db_docstatus] + " > " + \ | |||
labels[self.to_docstatus], raise_exception=DocstatusTransitionError) | |||
def update_timestamps_and_docstatus(self): | |||
from frappe.utils import now | |||
ts = now() | |||
user = frappe.__dict__.get('session', {}).get('user') or 'Administrator' | |||
for d in self.doclist: | |||
if self.doc.fields.get('__islocal'): | |||
if not d.owner: | |||
d.owner = user | |||
if not d.creation: | |||
d.creation = ts | |||
d.modified_by = user | |||
d.modified = ts | |||
if d.docstatus != 2 and self.to_docstatus >= int(d.docstatus): # don't update deleted | |||
d.docstatus = self.to_docstatus | |||
def prepare_for_save(self, method): | |||
self.check_if_latest(method) | |||
self.update_timestamps_and_docstatus() | |||
self.update_parent_info() | |||
if self.doc.fields.get("__islocal"): | |||
# set name before validate | |||
self.run_method('before_set_name') | |||
self.doc.set_new_name(self) | |||
self.run_method('before_insert') | |||
if method != "cancel": | |||
self.extract_images_from_text_editor() | |||
def update_parent_info(self): | |||
idx_map = {} | |||
is_local = cint(self.doc.fields.get("__islocal")) | |||
if not frappe.flags.in_import: | |||
parentfields = [d.fieldname for d in self.meta.get({"doctype": "DocField", "fieldtype": "Table"})] | |||
for i, d in enumerate(self.doclist[1:]): | |||
if d.parentfield: | |||
if not frappe.flags.in_import: | |||
if not d.parentfield in parentfields: | |||
frappe.msgprint("Bad parentfield %s" % d.parentfield, | |||
raise_exception=True) | |||
d.parenttype = self.doc.doctype | |||
d.parent = self.doc.name | |||
if not d.idx: | |||
d.idx = idx_map.setdefault(d.parentfield, 0) + 1 | |||
else: | |||
d.idx = cint(d.idx) | |||
if is_local: | |||
# if parent is new, all children should be new | |||
d.fields["__islocal"] = 1 | |||
d.name = None | |||
idx_map[d.parentfield] = d.idx | |||
def run_method(self, method, *args, **kwargs): | |||
if not args: | |||
args = [] | |||
self.make_controller() | |||
def add_to_response(out, new_response): | |||
if isinstance(new_response, dict): | |||
out.update(new_response) | |||
if hasattr(self.controller, method): | |||
add_to_response(frappe.local.response, | |||
frappe.call(getattr(self.controller, method), *args, **kwargs)) | |||
self.set_doclist(self.controller.doclist) | |||
args = [self, method] + list(args) | |||
for handler in frappe.get_hooks("bean_event:" + self.doc.doctype + ":" + method) \ | |||
+ frappe.get_hooks("bean_event:*:" + method): | |||
add_to_response(frappe.local.response, frappe.call(frappe.get_attr(handler), *args, **kwargs)) | |||
return frappe.local.response | |||
def get_attr(self, method): | |||
self.make_controller() | |||
return getattr(self.controller, method, None) | |||
def insert(self, ignore_permissions=None): | |||
if ignore_permissions: | |||
self.ignore_permissions = True | |||
self.doc.fields["__islocal"] = 1 | |||
self.set_defaults() | |||
if frappe.flags.in_test: | |||
if self.meta.get_field("naming_series"): | |||
self.doc.naming_series = "_T-" + self.doc.doctype + "-" | |||
return self.save() | |||
def insert_or_update(self): | |||
if self.doc.name and frappe.db.exists(self.doc.doctype, self.doc.name): | |||
return self.save() | |||
else: | |||
return self.insert() | |||
def set_defaults(self): | |||
if frappe.flags.in_import: | |||
return | |||
new_docs = {} | |||
new_doclist = [] | |||
for d in self.doclist: | |||
if not d.doctype in new_docs: | |||
new_docs[d.doctype] = frappe.new_doc(d.doctype) | |||
newd = frappe.doc(new_docs[d.doctype].fields.copy()) | |||
newd.fields.update(d.fields) | |||
new_doclist.append(newd) | |||
self.set_doclist(new_doclist) | |||
def has_read_perm(self): | |||
return self.has_permission("read") | |||
def has_permission(self, permtype): | |||
if self.ignore_permissions: | |||
return True | |||
if self.doc.parent and self.doc.parenttype: | |||
return frappe.has_permission(self.doc.parenttype, permtype, | |||
frappe.doc(self.doc.parenttype, self.doc.parent)) | |||
else: | |||
return frappe.has_permission(self.doc.doctype, permtype, self.doc) | |||
def save(self, check_links=1, ignore_permissions=None): | |||
if ignore_permissions!=None: | |||
self.ignore_permissions = ignore_permissions | |||
perm_to_check = "write" | |||
if self.doc.fields.get("__islocal"): | |||
perm_to_check = "create" | |||
if not self.doc.owner: | |||
self.doc.owner = frappe.session.user | |||
self.to_docstatus = 0 | |||
self.prepare_for_save("save") | |||
# check permissions after preparing for save, since name might be required | |||
if self.has_permission(perm_to_check): | |||
if not self.ignore_validate: | |||
self.run_method('validate') | |||
self.validate_doclist() | |||
self.save_main() | |||
self.save_children() | |||
self.run_method('on_update') | |||
if perm_to_check=="create": | |||
self.run_method("after_insert") | |||
else: | |||
self.no_permission_to(_(perm_to_check.title())) | |||
return self | |||
def submit(self): | |||
if self.has_permission("submit"): | |||
self.to_docstatus = 1 | |||
self.prepare_for_save("submit") | |||
self.run_method('validate') | |||
self.validate_doclist() | |||
self.save_main() | |||
self.save_children() | |||
self.run_method('on_update') | |||
self.run_method('on_submit') | |||
else: | |||
self.no_permission_to(_("Submit")) | |||
return self | |||
def cancel(self): | |||
if self.has_permission("cancel"): | |||
self.to_docstatus = 2 | |||
self.prepare_for_save("cancel") | |||
self.run_method('before_cancel') | |||
self.save_main() | |||
self.save_children() | |||
self.run_method('on_cancel') | |||
self.check_no_back_links_exist() | |||
else: | |||
self.no_permission_to(_("Cancel")) | |||
return self | |||
def update_after_submit(self): | |||
if self.doc.docstatus != 1: | |||
frappe.msgprint("Only to called after submit", raise_exception=1) | |||
if self.has_permission("write"): | |||
self.to_docstatus = 1 | |||
self.prepare_for_save("update_after_submit") | |||
self.run_method('validate') | |||
self.run_method('before_update_after_submit') | |||
self.validate_doclist() | |||
self.save_main() | |||
self.save_children() | |||
self.run_method('on_update_after_submit') | |||
else: | |||
self.no_permission_to(_("Update")) | |||
return self | |||
def save_main(self): | |||
try: | |||
self.doc.save(check_links = False, ignore_fields = self.ignore_fields) | |||
except NameError, e: | |||
frappe.msgprint('%s "%s" already exists' % (self.doc.doctype, self.doc.name)) | |||
# prompt if cancelled | |||
if frappe.db.get_value(self.doc.doctype, self.doc.name, 'docstatus')==2: | |||
frappe.msgprint('[%s "%s" has been cancelled]' % (self.doc.doctype, self.doc.name)) | |||
frappe.errprint(frappe.utils.get_traceback()) | |||
raise | |||
def save_children(self): | |||
child_map = {} | |||
for d in self.doclist[1:]: | |||
if d.fields.get("parent") or d.fields.get("parentfield"): | |||
d.parent = self.doc.name # rename if reqd | |||
d.parenttype = self.doc.doctype | |||
d.save(check_links=False, ignore_fields = self.ignore_fields) | |||
child_map.setdefault(d.doctype, []).append(d.name) | |||
# delete all children in database that are not in the child_map | |||
# get all children types | |||
tablefields = frappe.model.meta.get_table_fields(self.doc.doctype) | |||
for dt in tablefields: | |||
if dt[0] not in self.ignore_children_type: | |||
cnames = child_map.get(dt[0]) or [] | |||
if cnames: | |||
frappe.db.sql("""delete from `tab%s` where parent=%s and parenttype=%s and | |||
name not in (%s)""" % (dt[0], '%s', '%s', ','.join(['%s'] * len(cnames))), | |||
tuple([self.doc.name, self.doc.doctype] + cnames)) | |||
else: | |||
frappe.db.sql("""delete from `tab%s` where parent=%s and parenttype=%s""" \ | |||
% (dt[0], '%s', '%s'), (self.doc.name, self.doc.doctype)) | |||
def delete(self): | |||
frappe.delete_doc(self.doc.doctype, self.doc.name) | |||
def no_permission_to(self, ptype): | |||
frappe.msgprint(("%s (%s): " % (self.doc.name, _(self.doc.doctype))) + \ | |||
_("No Permission to ") + ptype, raise_exception=BeanPermissionError) | |||
def check_no_back_links_exist(self): | |||
from frappe.model.delete_doc import check_if_doc_is_linked | |||
check_if_doc_is_linked(self.doc.doctype, self.doc.name, method="Cancel") | |||
def check_mandatory(self): | |||
if self.ignore_mandatory: | |||
return | |||
missing = [] | |||
for doc in self.doclist: | |||
for df in self.meta: | |||
if df.doctype=="DocField" and df.reqd and df.parent==doc.doctype and df.fieldname!="naming_series": | |||
msg = "" | |||
if df.fieldtype == "Table": | |||
if not self.get(df.fieldname): | |||
msg = _("Error") + ": " + _("Data missing in table") + ": " + _(df.label) | |||
elif doc.fields.get(df.fieldname) is None: | |||
msg = _("Error") + ": " | |||
if doc.parentfield: | |||
msg += _("Row") + (" # %s: " % (doc.idx,)) | |||
msg += _("Value missing for") + ": " + _(df.label) | |||
if msg: | |||
missing.append([msg, df.fieldname]) | |||
if missing: | |||
for msg, fieldname in missing: | |||
msgprint(msg) | |||
raise frappe.MandatoryError, ", ".join([fieldname for msg, fieldname in missing]) | |||
def extract_images_from_text_editor(self): | |||
from frappe.utils.file_manager import extract_images_from_html | |||
if self.doc.doctype != "DocType": | |||
for df in self.meta.get({"doctype": "DocField", "parent": self.doc.doctype, "fieldtype":"Text Editor"}): | |||
extract_images_from_html(self.doc, df.fieldname) | |||
def validate_doclist(self): | |||
self.check_mandatory() | |||
self.validate_restrictions() | |||
self.check_links() | |||
def check_links(self): | |||
if self.ignore_links: | |||
return | |||
ref, err_list = {}, [] | |||
for d in self.doclist: | |||
if not ref.get(d.doctype): | |||
ref[d.doctype] = d.make_link_list() | |||
err_list += d.validate_links(ref[d.doctype]) | |||
if err_list: | |||
frappe.msgprint("""[Link Validation] Could not find the following values: %s. | |||
Please correct and resave. Document Not Saved.""" % ', '.join(err_list), raise_exception=1) | |||
def validate_restrictions(self): | |||
if self.ignore_restrictions: | |||
return | |||
has_restricted_data = False | |||
for d in self.doclist: | |||
if not frappe.permissions.has_unrestricted_access(d): | |||
has_restricted_data = True | |||
if has_restricted_data: | |||
raise BeanPermissionError | |||
def clone(source_wrapper): | |||
""" make a clone of a document""" | |||
if isinstance(source_wrapper, list): | |||
source_wrapper = Bean(source_wrapper) | |||
new_wrapper = Bean(source_wrapper.doclist.copy()) | |||
if new_wrapper.doc.fields.get("amended_from"): | |||
new_wrapper.doc.fields["amended_from"] = None | |||
if new_wrapper.doc.fields.get("amendment_date"): | |||
new_wrapper.doc.fields["amendment_date"] = None | |||
for d in new_wrapper.doclist: | |||
d.fields.update({ | |||
"name": None, | |||
"__islocal": 1, | |||
"docstatus": 0, | |||
}) | |||
return new_wrapper | |||
# for bc | |||
def getlist(doclist, parentfield): | |||
import frappe.model.utils | |||
return frappe.model.utils.getlist(doclist, parentfield) | |||
def copy_doclist(doclist, no_copy = []): | |||
""" | |||
Make a copy of the doclist | |||
""" | |||
import frappe.model.utils | |||
return frappe.model.utils.copy_doclist(doclist, no_copy) | |||
@@ -18,7 +18,7 @@ def get_new_doc(doctype, parent_doc = None, parentfield = None): | |||
"docstatus": 0 | |||
}) | |||
meta = frappe.get_doctype(doctype) | |||
meta = frappe.get_meta(doctype) | |||
restrictions = frappe.defaults.get_restrictions() | |||
@@ -1,735 +0,0 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
""" | |||
Contains the Document class representing an object / record | |||
""" | |||
import frappe | |||
from frappe import _ | |||
import frappe.model.meta | |||
from frappe.utils import * | |||
class Document: | |||
""" | |||
The frappe(meta-data)framework equivalent of a Database Record. | |||
Stores,Retrieves,Updates the record in the corresponding table. | |||
Runs the triggers required. | |||
The `Document` class represents the basic Object-Relational Mapper (ORM). The object type is defined by | |||
`DocType` and the object ID is represented by `name`:: | |||
Please note the anamoly in the Web Notes Framework that `ID` is always called as `name` | |||
If both `doctype` and `name` are specified in the constructor, then the object is loaded from the database. | |||
If only `doctype` is given, then the object is not loaded | |||
If `fielddata` is specfied, then the object is created from the given dictionary. | |||
**Note 1:** | |||
The getter and setter of the object are overloaded to map to the fields of the object that | |||
are loaded when it is instantiated. | |||
For example: doc.name will be the `name` field and doc.owner will be the `owner` field | |||
**Note 2 - Standard Fields:** | |||
* `name`: ID / primary key | |||
* `owner`: creator of the record | |||
* `creation`: datetime of creation | |||
* `modified`: datetime of last modification | |||
* `modified_by` : last updating user | |||
* `docstatus` : Status 0 - Saved, 1 - Submitted, 2- Cancelled | |||
* `parent` : if child (table) record, this represents the parent record | |||
* `parenttype` : type of parent record (if any) | |||
* `parentfield` : table fieldname of parent record (if any) | |||
* `idx` : Index (sequence) of the child record | |||
""" | |||
def __init__(self, doctype = None, name = None, fielddata = None): | |||
self._roles = [] | |||
self._perms = [] | |||
self._user_defaults = {} | |||
self._new_name_set = False | |||
self._meta = None | |||
if isinstance(doctype, dict): | |||
fielddata = doctype | |||
doctype = None | |||
if doctype and isinstance(name, dict): | |||
name = frappe.db.get_value(doctype, name, "name") or None | |||
if fielddata: | |||
self.fields = fielddata | |||
else: | |||
self.fields = {} | |||
if not self.fields.has_key('name'): | |||
self.fields['name']='' # required on save | |||
if not self.fields.has_key('doctype'): | |||
self.fields['doctype']='' # required on save | |||
if not self.fields.has_key('owner'): | |||
self.fields['owner']='' # required on save | |||
if doctype: | |||
self.fields['doctype'] = doctype | |||
if name: | |||
self.fields['name'] = name | |||
self.__initialized = 1 | |||
if (doctype and name): | |||
self._loadfromdb(doctype, name) | |||
else: | |||
if not fielddata: | |||
self.fields['__islocal'] = 1 | |||
if not self.fields.get("docstatus"): | |||
self.fields["docstatus"] = 0 | |||
def __nonzero__(self): | |||
return True | |||
def __str__(self): | |||
return str(self.fields) | |||
def __repr__(self): | |||
return repr(self.fields) | |||
def __unicode__(self): | |||
return unicode(self.fields) | |||
def __eq__(self, other): | |||
if isinstance(other, Document): | |||
return self.fields == other.fields | |||
else: | |||
return False | |||
def __getstate__(self): | |||
return self.fields | |||
def __setstate__(self, d): | |||
self.fields = d | |||
def encode(self, encoding='utf-8'): | |||
"""convert all unicode values to utf-8""" | |||
from frappe.utils import encode_dict | |||
encode_dict(self.fields) | |||
def _loadfromdb(self, doctype = None, name = None): | |||
if name: self.name = name | |||
if doctype: self.doctype = doctype | |||
is_single = False | |||
try: | |||
is_single = frappe.model.meta.is_single(self.doctype) | |||
except Exception, e: | |||
pass | |||
if is_single: | |||
self._loadsingle() | |||
else: | |||
try: | |||
dataset = frappe.db.sql('select * from `tab%s` where name=%s' % | |||
(self.doctype, "%s"), self.name) | |||
except frappe.SQLError, e: | |||
if e.args[0]==1146: | |||
dataset = None | |||
else: | |||
raise | |||
if not dataset: | |||
raise frappe.DoesNotExistError, '[WNF] %s %s does not exist' % (self.doctype, self.name) | |||
self._load_values(dataset[0], frappe.db.get_description()) | |||
def is_new(self): | |||
return self.fields.get("__islocal") | |||
def _load_values(self, data, description): | |||
if '__islocal' in self.fields: | |||
del self.fields['__islocal'] | |||
for i in range(len(description)): | |||
v = data[i] | |||
self.fields[description[i][0]] = frappe.db.convert_to_simple_type(v) | |||
def _merge_values(self, data, description): | |||
for i in range(len(description)): | |||
v = data[i] | |||
if v: # only if value, over-write | |||
self.fields[description[i][0]] = frappe.db.convert_to_simple_type(v) | |||
def _loadsingle(self): | |||
self.name = self.doctype | |||
self.fields.update(getsingle(self.doctype)) | |||
self.cast_floats_and_ints() | |||
def cast_floats_and_ints(self): | |||
for df in frappe.get_doctype(self.doctype).get_docfields(): | |||
if df.fieldtype in ("Int", "Check"): | |||
self.fields[df.fieldname] = cint(self.fields.get(df.fieldname)) | |||
elif df.fieldtype in ("Float", "Currency"): | |||
self.fields[df.fieldname] = flt(self.fields.get(df.fieldname)) | |||
if self.docstatus is not None: | |||
self.docstatus = cint(self.docstatus) | |||
def __setattr__(self, name, value): | |||
# normal attribute | |||
if not self.__dict__.has_key('_Document__initialized'): | |||
self.__dict__[name] = value | |||
elif self.__dict__.has_key(name): | |||
self.__dict__[name] = value | |||
else: | |||
# field attribute | |||
f = self.__dict__['fields'] | |||
f[name] = value | |||
def __getattr__(self, name): | |||
if self.__dict__.has_key(name): | |||
return self.__dict__[name] | |||
elif self.fields.has_key(name): | |||
return self.fields[name] | |||
else: | |||
if name.startswith("__"): | |||
raise AttributeError() | |||
return None | |||
def get(self, name, value=None): | |||
return self.fields.get(name, value) | |||
def update(self, d): | |||
self.fields.update(d) | |||
return self | |||
def update_if_missing(self, d): | |||
fields = self.get_valid_fields() | |||
for key, value in d.iteritems(): | |||
if (key in fields) and (not self.fields.get(key)): | |||
self.fields[key] = value | |||
def insert(self): | |||
self.fields['__islocal'] = 1 | |||
self.save() | |||
return self | |||
def save(self, new=0, check_links=1, ignore_fields=0, make_autoname=1, | |||
keep_timestamps=False): | |||
if new: | |||
self.fields["__islocal"] = 1 | |||
# add missing parentinfo (if reqd) | |||
if self.parent and not (self.parenttype and self.parentfield): | |||
self.update_parentinfo() | |||
if self.parent and not self.idx: | |||
self.set_idx() | |||
# if required, make new | |||
if not self.meta.issingle: | |||
if self.is_new(): | |||
r = self._insert(make_autoname=make_autoname, keep_timestamps = keep_timestamps) | |||
if r: | |||
return r | |||
else: | |||
if not frappe.db.exists(self.doctype, self.name): | |||
frappe.msgprint(frappe._("Cannot update a non-exiting record, try inserting.") + ": " + self.doctype + " / " + self.name, | |||
raise_exception=1) | |||
# save the values | |||
self._update_values(self.meta.issingle, | |||
check_links and self.make_link_list() or {}, ignore_fields=ignore_fields, | |||
keep_timestamps=keep_timestamps) | |||
self._clear_temp_fields() | |||
def _get_amended_name(self): | |||
am_id = 1 | |||
am_prefix = self.amended_from | |||
if frappe.db.get_value(self.doctype, self.amended_from, "amended_from"): | |||
am_id = cint(self.amended_from.split('-')[-1]) + 1 | |||
am_prefix = '-'.join(self.amended_from.split('-')[:-1]) # except the last hyphen | |||
self.name = am_prefix + '-' + str(am_id) | |||
def set_new_name(self, bean=None): | |||
if self._new_name_set: | |||
# already set by bean | |||
return | |||
self._new_name_set = True | |||
autoname = self.meta.autoname | |||
self.localname = self.name | |||
# amendments | |||
if self.amended_from: | |||
return self._get_amended_name() | |||
# by method | |||
else: | |||
# get my object | |||
if not bean: | |||
bean = frappe.bean([self]) | |||
bean.run_method("autoname") | |||
if self.name and self.localname != self.name: | |||
return | |||
# based on a field | |||
if autoname and autoname.startswith('field:'): | |||
n = self.fields[autoname[6:]] | |||
if not n: | |||
raise Exception, 'Name is required' | |||
self.name = n.strip() | |||
elif autoname and autoname.startswith("naming_series:"): | |||
self.set_naming_series() | |||
if not self.naming_series: | |||
frappe.msgprint(frappe._("Naming Series mandatory"), raise_exception=True) | |||
self.name = make_autoname(self.naming_series+'.#####') | |||
# call the method! | |||
elif autoname and autoname!='Prompt': | |||
self.name = make_autoname(autoname, self.doctype) | |||
# given | |||
elif self.fields.get('__newname',''): | |||
self.name = self.fields['__newname'] | |||
# default name for table | |||
elif self.meta.istable: | |||
self.name = make_autoname('#########', self.doctype) | |||
# unable to determine a name, use global series | |||
if not self.name: | |||
self.name = make_autoname('#########', self.doctype) | |||
def set_naming_series(self): | |||
if not self.naming_series: | |||
# pick default naming series | |||
self.naming_series = get_default_naming_series(self.doctype) | |||
def append_number_if_name_exists(self): | |||
if frappe.db.exists(self.doctype, self.name): | |||
last = frappe.db.sql("""select name from `tab{}` | |||
where name regexp '{}-[[:digit:]]+' | |||
order by name desc limit 1""".format(self.doctype, self.name)) | |||
if last: | |||
count = str(cint(last[0][0].rsplit("-", 1)[1]) + 1) | |||
else: | |||
count = "1" | |||
self.name = "{0}-{1}".format(self.name, count) | |||
def set(self, key, value): | |||
self.modified = now() | |||
self.modified_by = frappe.session["user"] | |||
frappe.db.set_value(self.doctype, self.name, key, value, self.modified, self.modified_by) | |||
self.fields[key] = value | |||
def _insert(self, make_autoname=True, keep_timestamps=False): | |||
# set name | |||
if make_autoname: | |||
self.set_new_name() | |||
# validate name | |||
self.name = validate_name(self.doctype, self.name, self.meta.name_case) | |||
# insert! | |||
if not keep_timestamps: | |||
if not self.owner: | |||
self.owner = frappe.session['user'] | |||
self.modified_by = frappe.session['user'] | |||
if not self.creation: | |||
self.creation = self.modified = now() | |||
else: | |||
self.modified = now() | |||
frappe.db.sql("insert into `tab%(doctype)s`" % self.fields \ | |||
+ """ (name, owner, creation, modified, modified_by) | |||
values (%(name)s, %(owner)s, %(creation)s, %(modified)s, | |||
%(modified_by)s)""", self.fields) | |||
def _update_single(self, link_list): | |||
self.modified = now() | |||
update_str, values = [], [] | |||
frappe.db.sql("delete from tabSingles where doctype=%s", self.doctype) | |||
for f in self.fields.keys(): | |||
if not (f in ('modified', 'doctype', 'name', 'perm', 'localname', 'creation'))\ | |||
and (not f.startswith('__')): # fields not saved | |||
# validate links | |||
if link_list and link_list.get(f): | |||
self.fields[f] = self._validate_link(link_list, f) | |||
if self.fields[f]==None: | |||
update_str.append("(%s,%s,NULL)") | |||
values.append(self.doctype) | |||
values.append(f) | |||
else: | |||
update_str.append("(%s,%s,%s)") | |||
values.append(self.doctype) | |||
values.append(f) | |||
values.append(self.fields[f]) | |||
frappe.db.sql("insert into tabSingles(doctype, field, value) values %s" % (', '.join(update_str)), values) | |||
def validate_links(self, link_list): | |||
err_list = [] | |||
for f in self.fields.keys(): | |||
# validate links | |||
old_val = self.fields[f] | |||
if link_list and link_list.get(f): | |||
self.fields[f] = self._validate_link(link_list, f) | |||
if old_val and not self.fields[f]: | |||
err_list.append("{}: {}".format(link_list[f][1], old_val)) | |||
return err_list | |||
def make_link_list(self): | |||
res = frappe.model.meta.get_link_fields(self.doctype) | |||
link_list = {} | |||
for i in res: link_list[i[0]] = (i[1], i[2]) # options, label | |||
return link_list | |||
def _validate_link(self, link_list, f): | |||
dt = link_list[f][0] | |||
dn = self.fields.get(f) | |||
if not dt: | |||
frappe.throw("Options not set for link field: " + f) | |||
if not dt: return dn | |||
if not dn: return None | |||
if dt=="[Select]": return dn | |||
if dt.lower().startswith('link:'): | |||
dt = dt[5:] | |||
if '\n' in dt: | |||
dt = dt.split('\n')[0] | |||
tmp = frappe.db.sql("""SELECT name FROM `tab%s` | |||
WHERE name = %s""" % (dt, '%s'), (dn,)) | |||
return tmp and tmp[0][0] or ''# match case | |||
def _update_values(self, issingle, link_list, ignore_fields=0, keep_timestamps=False): | |||
self.validate_constants() | |||
if issingle: | |||
self._update_single(link_list) | |||
else: | |||
update_str, values = [], [] | |||
# set modified timestamp | |||
if self.modified and not keep_timestamps: | |||
self.modified = now() | |||
self.modified_by = frappe.session['user'] | |||
fields_list = ignore_fields and self.get_valid_fields() or self.fields.keys() | |||
for f in fields_list: | |||
if (not (f in ('doctype', 'name', 'perm', 'localname', | |||
'creation','_user_tags', "file_list", "_comments"))) and (not f.startswith('__')): | |||
# fields not saved | |||
# validate links | |||
if link_list and link_list.get(f): | |||
self.fields[f] = self._validate_link(link_list, f) | |||
if self.fields.get(f) is None or self.fields.get(f)=='': | |||
update_str.append("`%s`=NULL" % f) | |||
else: | |||
values.append(self.fields.get(f)) | |||
update_str.append("`%s`=%s" % (f, '%s')) | |||
if values: | |||
values.append(self.name) | |||
r = frappe.db.sql("update `tab%s` set %s where name=%s" % \ | |||
(self.doctype, ', '.join(update_str), "%s"), values) | |||
def get_valid_fields(self): | |||
import frappe.model.doctype | |||
if getattr(frappe.local, "valid_fields_map", None) is None: | |||
frappe.local.valid_fields_map = {} | |||
valid_fields_map = frappe.local.valid_fields_map | |||
if not valid_fields_map.get(self.doctype): | |||
if cint(self.meta.issingle): | |||
doctypelist = frappe.model.doctype.get(self.doctype) | |||
valid_fields_map[self.doctype] = doctypelist.get_fieldnames({ | |||
"fieldtype": ["not in", frappe.model.no_value_fields]}) | |||
else: | |||
valid_fields_map[self.doctype] = \ | |||
frappe.db.get_table_columns(self.doctype) | |||
return valid_fields_map.get(self.doctype) | |||
def validate_constants(self): | |||
if frappe.flags.in_import: | |||
return | |||
meta = frappe.get_doctype(self.doctype) | |||
constants = [d.fieldname for d in meta.get({"set_only_once": 1})] | |||
if constants: | |||
values = frappe.db.get_value(self.doctype, self.name, constants, as_dict=True) | |||
for fieldname in constants: | |||
if self.fields.get(fieldname) != values.get(fieldname): | |||
frappe.throw("{0}: {1}".format(_("Value cannot be changed for"), | |||
_(meta.get_field(fieldname).label)), frappe.CannotChangeConstantError) | |||
@property | |||
def meta(self): | |||
if not self._meta: | |||
self._meta = frappe.db.get_value("DocType", self.doctype, "*", as_dict=True) \ | |||
or frappe._dict() | |||
return self._meta | |||
def update_parentinfo(self): | |||
"""update parent type and parent field, if not explicitly specified""" | |||
tmp = frappe.db.sql("""select parent, fieldname from tabDocField | |||
where fieldtype='Table' and options=%s""", (self.doctype,)) | |||
if len(tmp)==0: | |||
raise Exception, 'Incomplete parent info in child table (%s, %s)' \ | |||
% (self.doctype, self.fields.get('name', '[new]')) | |||
elif len(tmp)>1: | |||
raise Exception, 'Ambiguous parent info (%s, %s)' \ | |||
% (self.doctype, self.fields.get('name', '[new]')) | |||
else: | |||
self.parenttype = tmp[0][0] | |||
self.parentfield = tmp[0][1] | |||
def set_idx(self): | |||
"""set idx""" | |||
self.idx = (frappe.db.sql("""select max(idx) from `tab%s` | |||
where parent=%s and parentfield=%s""" % (self.doctype, '%s', '%s'), | |||
(self.parent, self.parentfield))[0][0] or 0) + 1 | |||
def _clear_temp_fields(self): | |||
# clear temp stuff | |||
keys = self.fields.keys() | |||
for f in keys: | |||
if f.startswith('__'): | |||
del self.fields[f] | |||
def clear_table(self, doclist, tablefield, save=0): | |||
""" | |||
Clears the child records from the given `doclist` for a particular `tablefield` | |||
""" | |||
from frappe.model.utils import getlist | |||
table_list = getlist(doclist, tablefield) | |||
delete_list = [d.name for d in table_list] | |||
if delete_list: | |||
#filter doclist | |||
doclist = filter(lambda d: d.name not in delete_list, doclist) | |||
# delete from db | |||
frappe.db.sql("""\ | |||
delete from `tab%s` | |||
where parent=%s and parenttype=%s""" | |||
% (table_list[0].doctype, '%s', '%s'), | |||
(self.name, self.doctype)) | |||
self.fields['__unsaved'] = 1 | |||
return frappe.doclist(doclist) | |||
def get_values(self): | |||
"""get non-null fields dict withouth standard fields""" | |||
from frappe.model import default_fields | |||
ret = {} | |||
for key in self.fields: | |||
if key not in default_fields and self.fields[key]: | |||
ret[key] = self.fields[key] | |||
return ret | |||
def get_db_value(self, key): | |||
return frappe.db.get_value(self.doctype, self.name, key) | |||
def make_autoname(key, doctype=''): | |||
""" | |||
Creates an autoname from the given key: | |||
**Autoname rules:** | |||
* The key is separated by '.' | |||
* '####' represents a series. The string before this part becomes the prefix: | |||
Example: ABC.#### creates a series ABC0001, ABC0002 etc | |||
* 'MM' represents the current month | |||
* 'YY' and 'YYYY' represent the current year | |||
*Example:* | |||
* DE/./.YY./.MM./.##### will create a series like | |||
DE/09/01/0001 where 09 is the year, 01 is the month and 0001 is the series | |||
""" | |||
if key=="hash": | |||
return frappe.generate_hash(doctype) | |||
if not "#" in key: | |||
key = key + ".#####" | |||
n = '' | |||
l = key.split('.') | |||
series_set = False | |||
today = now_datetime() | |||
for e in l: | |||
en = '' | |||
if e.startswith('#'): | |||
if not series_set: | |||
digits = len(e) | |||
en = getseries(n, digits, doctype) | |||
series_set = True | |||
elif e=='YY': | |||
en = today.strftime('%y') | |||
elif e=='MM': | |||
en = today.strftime('%m') | |||
elif e=='DD': | |||
en = today.strftime("%d") | |||
elif e=='YYYY': | |||
en = today.strftime('%Y') | |||
else: en = e | |||
n+=en | |||
return n | |||
def getseries(key, digits, doctype=''): | |||
# series created ? | |||
current = frappe.db.sql("select `current` from `tabSeries` where name=%s for update", (key,)) | |||
if current and current[0][0] is not None: | |||
current = current[0][0] | |||
# yes, update it | |||
frappe.db.sql("update tabSeries set current = current+1 where name=%s", (key,)) | |||
current = cint(current) + 1 | |||
else: | |||
# no, create it | |||
frappe.db.sql("insert into tabSeries (name, current) values (%s, 1)", (key,)) | |||
current = 1 | |||
return ('%0'+str(digits)+'d') % current | |||
def getchildren(name, childtype, field='', parenttype='', from_doctype=0): | |||
import frappe | |||
from frappe.model.doclist import DocList | |||
condition = "" | |||
values = [] | |||
if field: | |||
condition += ' and parentfield=%s ' | |||
values.append(field) | |||
if parenttype: | |||
condition += ' and parenttype=%s ' | |||
values.append(parenttype) | |||
dataset = frappe.db.sql("""select * from `tab%s` where parent=%s %s order by idx""" \ | |||
% (childtype, "%s", condition), tuple([name]+values)) | |||
desc = frappe.db.get_description() | |||
l = DocList() | |||
for i in dataset: | |||
d = Document() | |||
d.doctype = childtype | |||
d._load_values(i, desc) | |||
l.append(d) | |||
return l | |||
def check_page_perm(doc): | |||
if doc.name=='Login Page': | |||
return | |||
if doc.publish: | |||
return | |||
if not frappe.db.sql("select name from `tabPage Role` where parent=%s and role='Guest'", (doc.name,)): | |||
frappe.response['403'] = 1 | |||
raise frappe.PermissionError, '[WNF] No read permission for %s %s' % ('Page', doc.name) | |||
def get(dt, dn='', with_children = 1, from_controller = 0): | |||
""" | |||
Returns a doclist containing the main record and all child records | |||
""" | |||
import frappe | |||
import frappe.model | |||
from frappe.model.doclist import DocList | |||
dn = dn or dt | |||
# load the main doc | |||
doc = Document(dt, dn) | |||
if dt=='Page' and frappe.session['user'] == 'Guest': | |||
check_page_perm(doc) | |||
if not with_children: | |||
# done | |||
return DocList([doc,]) | |||
# get all children types | |||
tablefields = frappe.model.meta.get_table_fields(dt) | |||
# load chilren | |||
doclist = DocList([doc,]) | |||
for t in tablefields: | |||
doclist += getchildren(doc.name, t[0], t[1], dt) | |||
return doclist | |||
def getsingle(doctype): | |||
"""get single doc as dict""" | |||
dataset = frappe.db.sql("select field, value from tabSingles where doctype=%s", (doctype,)) | |||
return dict(dataset) | |||
def copy_common_fields(from_doc, to_doc): | |||
from frappe.model import default_fields | |||
doctype_list = frappe.get_doctype(to_doc.doctype) | |||
for fieldname, value in from_doc.fields.items(): | |||
if fieldname in default_fields: | |||
continue | |||
if doctype_list.get_field(fieldname) and to_doc.fields[fieldname] != value: | |||
to_doc.fields[fieldname] = value | |||
def validate_name(doctype, name, case=None, merge=False): | |||
if not merge: | |||
if frappe.db.sql('select name from `tab%s` where name=%s' % (doctype,'%s'), (name,)): | |||
raise NameError, 'Name %s already exists' % name | |||
# no name | |||
if not name: return 'No Name Specified for %s' % doctype | |||
# new.. | |||
if name.startswith('New '+doctype): | |||
raise NameError, 'There were some errors setting the name, please contact the administrator' | |||
if case=='Title Case': name = name.title() | |||
if case=='UPPER CASE': name = name.upper() | |||
name = name.strip() # no leading and trailing blanks | |||
return name | |||
def get_default_naming_series(doctype): | |||
"""get default value for `naming_series` property""" | |||
from frappe.model.doctype import get_property | |||
naming_series = get_property(doctype, "options", "naming_series") | |||
if naming_series: | |||
naming_series = naming_series.split("\n") | |||
return naming_series[0] or naming_series[1] | |||
else: | |||
return None |
@@ -1,143 +0,0 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
import frappe | |||
import frappe.model | |||
from frappe.model.doc import Document | |||
from frappe import _ | |||
class DocList(list): | |||
"""DocList object as a wrapper around a list""" | |||
def get(self, filters, limit=0): | |||
"""pass filters as: | |||
{"key": "val", "key": ["!=", "val"], | |||
"key": ["in", "val"], "key": ["not in", "val"], "key": "^val", | |||
"key" : True (exists), "key": False (does not exist) }""" | |||
out = [] | |||
for doc in self: | |||
d = isinstance(getattr(doc, "fields", None), dict) and doc.fields or doc | |||
add = True | |||
for f in filters: | |||
fval = filters[f] | |||
if fval is True: | |||
fval = ["not None", fval] | |||
elif fval is False: | |||
fval = ["None", fval] | |||
elif not isinstance(fval, (tuple, list)): | |||
if isinstance(fval, basestring) and fval.startswith("^"): | |||
fval = ["^", fval[1:]] | |||
else: | |||
fval = ["=", fval] | |||
if not frappe.compare(d.get(f), fval[0], fval[1]): | |||
add = False | |||
break | |||
if add: | |||
out.append(doc) | |||
if limit and (len(out)-1)==limit: | |||
break | |||
return DocList(out) | |||
def get_distinct_values(self, fieldname): | |||
return filter(None, list(set(map(lambda d: d.fields.get(fieldname), self)))) | |||
def remove_items(self, filters): | |||
for d in self.get(filters): | |||
self.remove(d) | |||
def getone(self, filters): | |||
return self.get(filters, limit=1)[0] | |||
def copy(self): | |||
out = [] | |||
for d in self: | |||
if isinstance(d, dict): | |||
fielddata = d | |||
else: | |||
fielddata = d.fields | |||
fielddata.update({"name": None}) | |||
out.append(Document(fielddata=fielddata)) | |||
return DocList(out) | |||
def get_item_value(self, d, name): | |||
if isinstance(d, dict): | |||
return d.get(name) | |||
else: | |||
return d.fields.get(name) | |||
def filter_valid_fields(self): | |||
import frappe.model | |||
fieldnames = {} | |||
for d in self: | |||
remove = [] | |||
for f in d: | |||
if f not in fieldnames.setdefault(d.doctype, | |||
frappe.model.get_fieldnames(d.doctype)): | |||
remove.append(f) | |||
for f in remove: | |||
del d[f] | |||
def append(self, doc): | |||
if not isinstance(doc, Document): | |||
doc = Document(fielddata=doc) | |||
self._prepare_doc(doc) | |||
super(DocList, self).append(doc) | |||
def extend(self, doclist): | |||
doclist = objectify(doclist) | |||
for doc in doclist: | |||
self._prepare_doc(doc) | |||
super(DocList, self).extend(doclist) | |||
return self | |||
def _prepare_doc(self, doc): | |||
if not doc.name: | |||
doc.fields["__islocal"] = 1 | |||
doc.docstatus = 0 | |||
if doc.parentfield: | |||
if not doc.parenttype: | |||
doc.parenttype = self[0].doctype | |||
if not doc.parent: | |||
doc.parent = self[0].name | |||
if not doc.idx: | |||
siblings = [int(self.get_item_value(d, "idx") or 0) for d in self.get({"parentfield": doc.parentfield})] | |||
doc.idx = (max(siblings) + 1) if siblings else 1 | |||
def update(self, doclist): | |||
for i, d in enumerate(self): | |||
if d.get("parent") and d.get("name") not in [t.get("name") for t in doclist]: | |||
del self[i] | |||
for d in doclist: | |||
if not d["name"]: | |||
d["__islocal"] = 1 | |||
self.append(d) | |||
else: | |||
# child | |||
found_in_existing = False | |||
for ref in self: | |||
if d["name"] and ref.name and ref.name == d["name"]: | |||
ref.fields.update(d) | |||
found_in_existing = True | |||
break | |||
if not found_in_existing: | |||
d["__islocal"] = 1 | |||
d["name"] = None | |||
self.append(d) | |||
return self | |||
def objectify(doclist): | |||
from frappe.model.doc import Document | |||
return map(lambda d: isinstance(d, Document) and d or Document(d), doclist) |
@@ -1,425 +0,0 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
""" | |||
Get metadata (main doctype with fields and permissions with all table doctypes) | |||
- if exists in cache, get it from cache | |||
- add custom fields | |||
- override properties from PropertySetter | |||
- sort based on prev_field | |||
- optionally, post process (add js, css, select fields), or without | |||
""" | |||
from __future__ import unicode_literals | |||
# imports | |||
import json, os | |||
import frappe | |||
import frappe.model | |||
import frappe.model.doc | |||
import frappe.model.doclist | |||
from frappe.utils import cint, cstr | |||
doctype_cache = frappe.local('doctype_doctype_cache') | |||
docfield_types = frappe.local('doctype_docfield_types') | |||
# doctype_cache = {} | |||
# docfield_types = None | |||
def get(doctype, processed=False, cached=True): | |||
"""return doclist""" | |||
if cached: | |||
doclist = from_cache(doctype, processed) | |||
if doclist: | |||
if processed: | |||
update_language(doclist) | |||
return DocTypeDocList(doclist) | |||
load_docfield_types() | |||
# main doctype doclist | |||
doclist = get_doctype_doclist(doctype) | |||
# add doctypes of table fields | |||
table_types = [d.options for d in doclist \ | |||
if d.doctype=='DocField' and d.fieldtype=='Table'] | |||
for table_doctype in table_types: | |||
doclist += get_doctype_doclist(table_doctype) | |||
if processed: | |||
add_code(doctype, doclist) | |||
expand_selects(doclist) | |||
add_print_formats(doclist) | |||
add_search_fields(doclist) | |||
add_workflows(doclist) | |||
add_linked_with(doclist) | |||
to_cache(doctype, processed, doclist) | |||
if processed: | |||
update_language(doclist) | |||
return DocTypeDocList(doclist) | |||
def load_docfield_types(): | |||
frappe.local.doctype_docfield_types = dict(frappe.db.sql("""select fieldname, fieldtype from tabDocField | |||
where parent='DocField'""")) | |||
def add_workflows(doclist): | |||
from frappe.model.workflow import get_workflow_name | |||
doctype = doclist[0].name | |||
# get active workflow | |||
workflow_name = get_workflow_name(doctype) | |||
if workflow_name and frappe.db.exists("Workflow", workflow_name): | |||
doclist += frappe.get_doclist("Workflow", workflow_name) | |||
# add workflow states (for icons and style) | |||
for state in map(lambda d: d.state, doclist.get({"doctype":"Workflow Document State"})): | |||
doclist += frappe.get_doclist("Workflow State", state) | |||
def get_doctype_doclist(doctype): | |||
"""get doclist of single doctype""" | |||
doclist = frappe.get_doclist('DocType', doctype) | |||
add_custom_fields(doctype, doclist) | |||
apply_property_setters(doctype, doclist) | |||
sort_fields(doclist) | |||
return doclist | |||
def sort_fields(doclist): | |||
"""sort on basis of previous_field""" | |||
from frappe.model.doclist import DocList | |||
newlist = DocList([]) | |||
pending = doclist.get({"doctype":"DocField"}) | |||
if doclist[0].get("_idx"): | |||
for fieldname in json.loads(doclist[0].get("_idx")): | |||
d = doclist.get({"fieldname": fieldname}) | |||
if d: | |||
newlist.append(d[0]) | |||
pending.remove(d[0]) | |||
else: | |||
maxloops = 20 | |||
while (pending and maxloops>0): | |||
maxloops -= 1 | |||
for d in pending[:]: | |||
if d.previous_field: | |||
# field already added | |||
for n in newlist: | |||
if n.fieldname==d.previous_field: | |||
newlist.insert(newlist.index(n)+1, d) | |||
pending.remove(d) | |||
break | |||
else: | |||
newlist.append(d) | |||
pending.remove(d) | |||
# recurring at end | |||
if pending: | |||
newlist += pending | |||
# renum | |||
idx = 1 | |||
for d in newlist: | |||
d.idx = idx | |||
idx += 1 | |||
doclist.get({"doctype":["!=", "DocField"]}).extend(newlist) | |||
def apply_property_setters(doctype, doclist): | |||
for ps in frappe.db.sql("""select * from `tabProperty Setter` where | |||
doc_type=%s""", (doctype,), as_dict=1): | |||
if ps['doctype_or_field']=='DocType': | |||
if ps.get('property_type', None) in ('Int', 'Check'): | |||
ps['value'] = cint(ps['value']) | |||
doclist[0].fields[ps['property']] = ps['value'] | |||
else: | |||
docfield = filter(lambda d: d.doctype=="DocField" and d.fieldname==ps['field_name'], | |||
doclist) | |||
if not docfield: continue | |||
if docfield_types.get(ps['property'], None) in ('Int', 'Check'): | |||
ps['value'] = cint(ps['value']) | |||
docfield[0].fields[ps['property']] = ps['value'] | |||
def add_custom_fields(doctype, doclist): | |||
try: | |||
res = frappe.db.sql("""SELECT * FROM `tabCustom Field` | |||
WHERE dt = %s AND docstatus < 2""", (doctype,), as_dict=1) | |||
except Exception, e: | |||
if e.args[0]==1146: | |||
return doclist | |||
else: | |||
raise | |||
for r in res: | |||
custom_field = frappe.model.doc.Document(fielddata=r) | |||
# convert to DocField | |||
custom_field.fields.update({ | |||
'doctype': 'DocField', | |||
'parent': doctype, | |||
'parentfield': 'fields', | |||
'parenttype': 'DocType', | |||
'__custom_field': 1 | |||
}) | |||
doclist.append(custom_field) | |||
return doclist | |||
def add_linked_with(doclist): | |||
"""add list of doctypes this doctype is 'linked' with""" | |||
doctype = doclist[0].name | |||
links = frappe.db.sql("""select parent, fieldname from tabDocField | |||
where (fieldtype="Link" and options=%s) | |||
or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype)) | |||
links += frappe.db.sql("""select dt as parent, fieldname from `tabCustom Field` | |||
where (fieldtype="Link" and options=%s) | |||
or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype)) | |||
links = dict(links) | |||
if not links: | |||
return {} | |||
ret = {} | |||
for dt in links: | |||
ret[dt] = { "fieldname": links[dt] } | |||
for grand_parent, options in frappe.db.sql("""select parent, options from tabDocField | |||
where fieldtype="Table" | |||
and options in (select name from tabDocType | |||
where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)): | |||
ret[grand_parent] = {"child_doctype": options, "fieldname": links[options] } | |||
if options in ret: | |||
del ret[options] | |||
doclist[0].fields["__linked_with"] = ret | |||
def from_cache(doctype, processed): | |||
""" load doclist from cache. | |||
sets flag __from_cache in first doc of doclist if loaded from cache""" | |||
# from memory | |||
if doctype_cache and not processed and doctype in doctype_cache: | |||
doclist = doctype_cache[doctype] | |||
doclist[0].fields["__from_cache"] = 1 | |||
return doclist | |||
doclist = frappe.cache().get_value(cache_name(doctype, processed)) | |||
if doclist: | |||
from frappe.model.doclist import DocList | |||
doclist = DocList([frappe.model.doc.Document(fielddata=d) | |||
for d in doclist]) | |||
doclist[0].fields["__from_cache"] = 1 | |||
return doclist | |||
def to_cache(doctype, processed, doclist): | |||
if not doctype_cache: | |||
frappe.local.doctype_doctype_cache = {} | |||
frappe.cache().set_value(cache_name(doctype, processed), | |||
[d.fields for d in doclist]) | |||
if not processed: | |||
doctype_cache[doctype] = doclist | |||
def cache_name(doctype, processed): | |||
"""returns cache key""" | |||
suffix = "" | |||
if processed: | |||
suffix = ":Raw" | |||
return "doctype:" + doctype + suffix | |||
def clear_cache(doctype=None): | |||
def clear_single(dt): | |||
frappe.cache().delete_value(cache_name(dt, False)) | |||
frappe.cache().delete_value(cache_name(dt, True)) | |||
frappe.cache().delete_value("meta:" + dt) | |||
frappe.cache().delete_value("form_meta:" + dt) | |||
if doctype_cache and (dt in doctype_cache): | |||
del doctype_cache[dt] | |||
if doctype: | |||
clear_single(doctype) | |||
# clear all parent doctypes | |||
for dt in frappe.db.sql("""select parent from tabDocField | |||
where fieldtype="Table" and options=%s""", (doctype,)): | |||
clear_single(dt[0]) | |||
# clear all notifications | |||
from frappe.core.doctype.notification_count.notification_count import delete_notification_count_for | |||
delete_notification_count_for(doctype) | |||
else: | |||
# clear all | |||
for dt in frappe.db.sql("""select name from tabDocType"""): | |||
clear_single(dt[0]) | |||
frappe.cache().delete_value("is_table") | |||
def add_code(doctype, doclist): | |||
import os | |||
from frappe.modules import scrub, get_module_path | |||
doc = doclist[0] | |||
path = os.path.join(get_module_path(doc.module), 'doctype', scrub(doc.name)) | |||
def _get_path(fname): | |||
return os.path.join(path, scrub(fname)) | |||
_add_code(doc, _get_path(doc.name + '.js'), '__js') | |||
_add_code(doc, _get_path(doc.name + '.css'), "__css") | |||
_add_code(doc, _get_path(doc.name + '_list.js'), '__list_js') | |||
_add_code(doc, _get_path(doc.name + '_calendar.js'), '__calendar_js') | |||
_add_code(doc, _get_path(doc.name + '_map.js'), '__map_js') | |||
add_custom_script(doc) | |||
add_code_via_hook(doc, "doctype_js", "__js") | |||
def _add_code(doc, path, fieldname): | |||
js = frappe.read_file(path) | |||
if js: | |||
doc.fields[fieldname] = (doc.fields.get(fieldname) or "") + "\n\n" + render_jinja(js) | |||
def add_code_via_hook(doc, hook, fieldname): | |||
hook = "{}:{}".format(hook, doc.name) | |||
for app_name in frappe.get_installed_apps(): | |||
for file in frappe.get_hooks(hook, app_name=app_name): | |||
path = frappe.get_app_path(app_name, *file.strip("/").split("/")) | |||
_add_code(doc, path, fieldname) | |||
def add_custom_script(doc): | |||
"""embed all require files""" | |||
# custom script | |||
custom = frappe.db.get_value("Custom Script", {"dt": doc.name, | |||
"script_type": "Client"}, "script") or "" | |||
doc.fields["__js"] = (doc.fields.get('__js') or '') + "\n\n" + custom | |||
def render_jinja(content): | |||
if "{% include" in content: | |||
content = frappe.get_jenv().from_string(content).render() | |||
return content | |||
def expand_selects(doclist): | |||
for d in filter(lambda d: d.fieldtype=='Select' \ | |||
and (d.options or '').startswith('link:'), doclist): | |||
doctype = d.options.split("\n")[0][5:] | |||
d.link_doctype = doctype | |||
d.options = '\n'.join([''] + [o.name for o in frappe.db.sql("""select | |||
name from `tab%s` where docstatus<2 order by name asc""" % doctype, as_dict=1)]) | |||
def add_print_formats(doclist): | |||
print_formats = frappe.db.sql("""select * FROM `tabPrint Format` | |||
WHERE doc_type=%s AND docstatus<2""", (doclist[0].name,), as_dict=1) | |||
for pf in print_formats: | |||
doclist.append(frappe.model.doc.Document('Print Format', fielddata=pf)) | |||
def get_property(dt, prop, fieldname=None): | |||
"""get a doctype property""" | |||
doctypelist = get(dt) | |||
if fieldname: | |||
field = doctypelist.get_field(fieldname) | |||
return field and field.fields.get(prop) or None | |||
else: | |||
return doctypelist[0].fields.get(prop) | |||
def get_link_fields(doctype): | |||
"""get docfields of links and selects with "link:" | |||
for main doctype and child doctypes""" | |||
doctypelist = get(doctype) | |||
return doctypelist.get({"fieldtype":"Link"}).extend(doctypelist.get({"fieldtype":"Select", | |||
"options": "^link:"})) | |||
def add_validators(doctype, doclist): | |||
for validator in frappe.db.sql("""select name from `tabDocType Validator` where | |||
for_doctype=%s""", (doctype,), as_dict=1): | |||
doclist.extend(frappe.get_doclist('DocType Validator', validator.name)) | |||
def add_search_fields(doclist): | |||
"""add search fields found in the doctypes indicated by link fields' options""" | |||
for lf in doclist.get({"fieldtype": "Link", "options":["!=", "[Select]"]}): | |||
if lf.options: | |||
search_fields = get(lf.options)[0].search_fields | |||
if search_fields: | |||
lf.search_fields = map(lambda sf: sf.strip(), search_fields.split(",")) | |||
def update_language(doclist): | |||
"""update language""" | |||
if frappe.local.lang != 'en': | |||
doclist[0].fields["__messages"] = frappe.get_lang_dict("doctype", doclist[0].name) | |||
class DocTypeDocList(frappe.model.doclist.DocList): | |||
def get_field(self, fieldname, parent=None, parentfield=None): | |||
filters = {"doctype":"DocField"} | |||
if isinstance(fieldname, dict): | |||
filters.update(fieldname) | |||
else: | |||
filters["fieldname"] = fieldname | |||
# if parentfield, get the name of the parent table | |||
if parentfield: | |||
parent = self.get_options(parentfield) | |||
if parent: | |||
filters["parent"] = parent | |||
else: | |||
filters["parent"] = self[0].name | |||
fields = self.get(filters) | |||
if fields: | |||
return fields[0] | |||
def has_field(self, fieldname): | |||
return fieldname in self.get_fieldnames() | |||
def get_fieldnames(self, filters=None): | |||
return map(lambda df: df.fieldname, self.get_docfields(filters)) | |||
def get_docfields(self, filters=None): | |||
if not filters: filters = {} | |||
filters.update({"doctype": "DocField", "parent": self[0].name}) | |||
return self.get(filters) | |||
def get_options(self, fieldname, parent=None, parentfield=None): | |||
return self.get_field(fieldname, parent, parentfield).options | |||
def get_label(self, fieldname, parent=None, parentfield=None): | |||
return self.get_field(fieldname, parent, parentfield).label | |||
def get_table_fields(self): | |||
return self.get({"doctype": "DocField", "fieldtype": "Table"}) | |||
def get_parent_doclist(self): | |||
return frappe.doclist([self[0]] + self.get({"parent": self[0].name})) | |||
def get_restricted_fields(self, restricted_types): | |||
restricted_fields = self.get({ | |||
"doctype":"DocField", | |||
"fieldtype":"Link", | |||
"parent": self[0].name, | |||
"ignore_restrictions":("!=", 1), | |||
"options":("in", restricted_types) | |||
}) | |||
if self[0].name in restricted_types: | |||
restricted_fields.append(frappe._dict({"label":"Name", "fieldname":"name", "options": self[0].name})) | |||
return restricted_fields | |||
def get_permissions(self, user=None): | |||
user_roles = frappe.get_roles(user) | |||
return [p for p in self.get({"doctype": "DocPerm"}) | |||
if cint(p.permlevel)==0 and (p.role=="All" or p.role in user_roles)] | |||
def get_link_fields(self): | |||
return self.get({"fieldtype":"Link", "parent": self[0].name})\ | |||
.extend(self.get({"fieldtype":"Select", "options": "^link:", "parent": self[0].name})) |
@@ -12,7 +12,9 @@ from frappe.model.base_document import BaseDocument | |||
# methods | |||
def get_doc(arg1, arg2=None): | |||
if isinstance(arg1, basestring): | |||
if isinstance(arg1, BaseDocument): | |||
return arg1 | |||
elif isinstance(arg1, basestring): | |||
doctype = arg1 | |||
else: | |||
doctype = arg1.get("doctype") | |||
@@ -315,6 +317,14 @@ class Document(BaseDocument): | |||
if hasattr(self, method): | |||
fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs) | |||
return Document.hook(fn)(self, *args, **kwargs) | |||
def submit(self): | |||
self.docstatus = 1 | |||
self.save() | |||
def cancel(self): | |||
self.docstatus = 2 | |||
self.save() | |||
def run_before_save_methods(self): | |||
if self._action=="save": | |||
@@ -20,8 +20,8 @@ def get_mapped_doclist(from_doctype, from_docname, table_maps, target_doclist=No | |||
if not ignore_permissions and not frappe.has_permission(from_doctype, "read", source.doc): | |||
frappe.msgprint("No Permission", raise_exception=frappe.PermissionError) | |||
source_meta = frappe.get_doctype(from_doctype) | |||
target_meta = frappe.get_doctype(table_maps[from_doctype]["doctype"]) | |||
source_meta = frappe.get_meta(from_doctype) | |||
target_meta = frappe.get_meta(table_maps[from_doctype]["doctype"]) | |||
# main | |||
if target_doclist: | |||
@@ -46,7 +46,17 @@ class Meta(Document): | |||
def get_table_field_doctype(self, fieldname): | |||
return { "fields": "DocField", "permissions": "DocPerm"}.get(fieldname) | |||
def get_field(self, fieldname): | |||
fields = self.get("fields", {"fieldname":fieldname}) | |||
return fields[0] if fields else None | |||
def get_label(self, fieldname): | |||
return self.get_field(fieldname).label | |||
def get_options(self, fieldname): | |||
return self.get_field(fieldname).options | |||
def process(self): | |||
self.add_custom_fields() | |||
self.apply_property_setters() | |||
@@ -174,11 +184,7 @@ def get_table_fields(doctype): | |||
def has_field(doctype, fieldname, parent=None, parentfield=None): | |||
return get_field(doctype, fieldname, parent=None, parentfield=None) and True or False | |||
def get_field(doctype, fieldname, parent=None, parentfield=None): | |||
doclist = frappe.get_doctype(doctype) | |||
return doclist.get_field(fieldname, parent, parentfield) | |||
def get_field_currency(df, doc): | |||
"""get currency based on DocField options and fieldvalue in doc""" | |||
currency = None | |||
@@ -12,7 +12,7 @@ def set_new_name(doc): | |||
return | |||
doc._new_name_set = True | |||
autoname = frappe.get_doctype(doc.doctype)[0].autoname | |||
autoname = frappe.get_meta(doc.doctype).autoname | |||
doc.localname = doc.name # for passing back to client | |||
# amendments | |||
@@ -4,113 +4,10 @@ | |||
from __future__ import unicode_literals | |||
import frappe, json | |||
from frappe import _ | |||
from frappe.model.doc import Document | |||
""" | |||
Model utilities, unclassified functions | |||
""" | |||
def expand(docs): | |||
""" | |||
Expand a doclist sent from the client side. (Internally used by the request handler) | |||
""" | |||
def xzip(a,b): | |||
d = {} | |||
for i in range(len(a)): | |||
d[a[i]] = b[i] | |||
return d | |||
docs = json.loads(docs) | |||
clist = [] | |||
for d in docs['_vl']: | |||
doc = xzip(docs['_kl'][d[0]], d); | |||
clist.append(doc) | |||
return clist | |||
def compress(doclist): | |||
""" | |||
Compress a doclist before sending it to the client side. (Internally used by the request handler) | |||
""" | |||
docs = [isinstance(d, Document) and d.fields or d for d in doclist] | |||
kl, vl = {}, [] | |||
forbidden = ['server_code_compiled'] | |||
# scan for keys & values | |||
for d in docs: | |||
dt = d['doctype'] | |||
if not (dt in kl.keys()): | |||
kl[dt] = ['doctype','localname','__oldparent','__unsaved'] | |||
# add client script for doctype, doctype due to ambiguity | |||
if dt=='DocType' and '__client_script' not in kl[dt]: | |||
kl[dt].append('__client_script') | |||
for f in d.keys(): | |||
if not (f in kl[dt]) and not (f in forbidden): | |||
# if key missing, then append | |||
kl[dt].append(f) | |||
# build values | |||
tmp = [] | |||
for f in kl[dt]: | |||
v = d.get(f) | |||
if type(v)==long: | |||
v=int(v) | |||
tmp.append(v) | |||
vl.append(tmp) | |||
return {'_vl':vl,'_kl':kl} | |||
def getlist(doclist, field): | |||
from frappe.utils import cint | |||
l = [] | |||
for d in doclist: | |||
if d.parentfield == field: | |||
l.append(d) | |||
l.sort(lambda a, b: cint(a.idx) - cint(b.idx)) | |||
return l | |||
def copy_doclist(doclist, no_copy = []): | |||
""" | |||
Save & return a copy of the given doclist | |||
Pass fields that are not to be copied in `no_copy` | |||
""" | |||
cl = [] | |||
# main doc | |||
c = Document(fielddata = doclist[0].fields.copy()) | |||
# clear no_copy fields | |||
for f in no_copy: | |||
if c.fields.has_key(f): | |||
c.fields[f] = None | |||
c.name = None | |||
c.save(1) | |||
cl.append(c) | |||
# new parent name | |||
parent = c.name | |||
# children | |||
for d in doclist[1:]: | |||
c = Document(fielddata = d.fields.copy()) | |||
c.name = None | |||
# clear no_copy fields | |||
for f in no_copy: | |||
if c.fields.has_key(f): | |||
c.fields[f] = None | |||
c.parent = parent | |||
c.save(1) | |||
cl.append(c) | |||
return cl | |||
def set_default(doc, key): | |||
if not doc.is_default: | |||
frappe.db.set(doc, "is_default", 1) | |||
@@ -165,10 +165,10 @@ def make_test_objects(doctype, test_records, verbose=None): | |||
def print_mandatory_fields(doctype): | |||
print "Please setup make_test_records for: " + doctype | |||
print "-" * 60 | |||
doctype_obj = frappe.get_doctype(doctype) | |||
print "Autoname: " + (doctype_obj[0].autoname or "") | |||
meta = frappe.get_meta(doctype) | |||
print "Autoname: " + (meta.autoname or "") | |||
print "Mandatory Fields: " | |||
for d in doctype_obj.get({"reqd":1}): | |||
for d in meta.get({"reqd":1}): | |||
print d.parent + ":" + d.fieldname + " | " + d.fieldtype + " | " + (d.options or "") | |||
@@ -141,11 +141,11 @@ def get_messages_for_app(app): | |||
def get_messages_from_doctype(name): | |||
messages = [] | |||
meta = frappe.get_doctype(name) | |||
meta = frappe.get_meta(name) | |||
messages = [meta[0].name, meta[0].description, meta[0].module] | |||
messages = [meta.name, meta.description, meta.module] | |||
for d in meta.get({"doctype":"DocField"}): | |||
for d in meta.get("fields"): | |||
messages.extend([d.label, d.description]) | |||
if d.fieldtype=='Select' and d.options \ | |||
@@ -156,7 +156,7 @@ def get_messages_from_doctype(name): | |||
messages.extend(options) | |||
# extract from js, py files | |||
doctype_file_path = frappe.get_module_path(meta[0].module, "doctype", meta[0].name, meta[0].name) | |||
doctype_file_path = frappe.get_module_path(meta.module, "doctype", meta.name, meta.name) | |||
messages.extend(get_messages_from_file(doctype_file_path + ".js")) | |||
return clean(messages) | |||
@@ -92,17 +92,13 @@ class UnicodeWriter: | |||
def getvalue(self): | |||
return self.queue.getvalue() | |||
def check_record(d, parenttype=None, doctype_dl=None): | |||
def check_record(d): | |||
"""check for mandatory, select options, dates. these should ideally be in doclist""" | |||
from frappe.utils.dateutils import parse_date | |||
if parenttype and not d.get('parent'): | |||
frappe.msgprint(_("Parent is required."), raise_exception=1) | |||
if not doctype_dl: | |||
doctype_dl = frappe.model.doctype.get(d.doctype) | |||
d = frappe.get_doc(d) | |||
for key in d: | |||
docfield = doctype_dl.get_field(key) | |||
docfield = d.meta.get_field(key) | |||
val = d[key] | |||
if docfield: | |||
if docfield.reqd and (val=='' or val==None): | |||
@@ -163,7 +163,7 @@ class TestBlogPost(unittest.TestCase): | |||
self.assertTrue(bean.has_read_perm()) | |||
def test_set_only_once(self): | |||
blog_post = frappe.get_doctype("Blog Post") | |||
blog_post = frappe.get_meta("Blog Post") | |||
blog_post.get_field("title").set_only_once = 1 | |||
bean = frappe.bean("Blog Post", "_test-blog-post-1") | |||
bean.doc.title = "New" | |||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals | |||
import frappe | |||
from frappe.website.website_generator import WebsiteGenerator | |||
from frappe.templates.generators.website_group import clear_cache | |||
from frappe.model.doc import make_autoname | |||
from frappe.model.naming import make_autoname | |||
class WebsiteGroup(WebsiteGenerator): | |||
@@ -3,6 +3,7 @@ | |||
from __future__ import unicode_literals | |||
import frappe, json | |||
import frappe.widgets.form.meta | |||
from frappe import _ | |||
@@ -87,13 +88,13 @@ def get_next(doctype, name, prev): | |||
@frappe.whitelist() | |||
def get_linked_docs(doctype, name, metadata_loaded=None): | |||
if not metadata_loaded: metadata_loaded = [] | |||
meta = frappe.get_doctype(doctype, True) | |||
linkinfo = meta[0].get("__linked_with") | |||
meta = frappe.widgets.form.meta.get_meta(doctype) | |||
linkinfo = meta.get("__linked_with") | |||
results = {} | |||
for dt, link in linkinfo.items(): | |||
link["doctype"] = dt | |||
linkmeta = frappe.get_doctype(dt, True) | |||
if not linkmeta[0].get("issingle"): | |||
linkmeta = frappe.widgets.form.meta.get_meta(dt) | |||
if not linkmeta.get("issingle"): | |||
fields = [d.fieldname for d in linkmeta.get({"parent":dt, "in_list_view":1, | |||
"fieldtype": ["not in", ["Image", "HTML", "Button", "Table"]]})] \ | |||
+ ["name", "modified", "docstatus"] | |||
@@ -52,8 +52,7 @@ def compress(data): | |||
@frappe.whitelist() | |||
def save_report(): | |||
"""save report""" | |||
from frappe.model.doc import Document | |||
data = frappe.local.form_dict | |||
if frappe.db.exists('Report', data['name']): | |||
d = Document('Report', data['name']) | |||
@@ -22,7 +22,7 @@ def search_widget(doctype, txt, query=None, searchfield="name", start=0, | |||
import json | |||
filters = json.loads(filters) | |||
meta = frappe.get_doctype(doctype) | |||
meta = frappe.get_meta(doctype) | |||
standard_queries = frappe.get_hooks().standard_queries or [] | |||
if standard_queries: | |||
@@ -68,12 +68,12 @@ def search_widget(doctype, txt, query=None, searchfield="name", start=0, | |||
def get_std_fields_list(meta, key): | |||
# get additional search fields | |||
sflist = meta[0].search_fields and meta[0].search_fields.split(",") or [] | |||
sflist = meta.search_fields and meta.search_fields.split(",") or [] | |||
sflist = ['name'] + sflist | |||
if not key in sflist: | |||
sflist = sflist + [key] | |||
return ['`tab%s`.`%s`' % (meta[0].name, f.strip()) for f in sflist] | |||
return ['`tab%s`.`%s`' % (meta.name, f.strip()) for f in sflist] | |||
def build_for_autosuggest(res): | |||
results = [] | |||